Понятные имена для аудиофайлов
This commit is contained in:
@@ -41,13 +41,13 @@ docker compose up -d --build
|
|||||||
|
|
||||||
### Результат работы бота
|
### Результат работы бота
|
||||||
|
|
||||||
Бот сохраняет аудиофайлы с озвученными репликами в файлы `data/<scenario_id>/<replica_number>_<dataset_speaker_id>.wav`, где `<replica_number>` - это номер реплики в сценарии (0-indexed), `<dataset_speaker_id>` - это идентификатор диктора во всём датасете, по-сути, соответствует уникальному пользователю бота. Один диктор может озвучить несколько дорожек из разных совещаний. При этом он не может озвучивать две разные дорожки в рамках одного совещания, таким образом гарантируется, что разным `speaker_id` соответствуют разные дикторы в рамках одного совещания.
|
Бот сохраняет аудиофайлы с озвученными репликами в файлы `data/<scenario_id>/r<replica_idx>_s<scenario_speaker_id>_u<user_id>.wav`, где `<replica_idx>` — номер реплики в сценарии (0-indexed, с ведущими нулями), `<scenario_speaker_id>` — идентификатор диктора в рамках сценария (с ведущими нулями), `<user_id>` — уникальный идентификатор пользователя бота (с ведущими нулями). Пример: `r003_s01_u002.wav`. Один диктор может озвучить несколько дорожек из разных совещаний. При этом он не может озвучивать две разные дорожки в рамках одного совещания, таким образом гарантируется, что разным `speaker_id` соответствуют разные дикторы в рамках одного совещания.
|
||||||
|
|
||||||
В `data` попадают только полностью озвученные дорожки. Под дорожкой подразумевается набор всех реплик одного диктора в рамках сценария. Дорожки, озвученные частично, хранятся в `data_partial/<scenario_id>/<replica_number>_<dataset_speaker_id>.wav`. Они автоматически переносятся в `data` после завершения озвучивания.
|
В `data` попадают только полностью озвученные дорожки. Под дорожкой подразумевается набор всех реплик одного диктора в рамках сценария. Дорожки, озвученные частично, хранятся в `data_partial/<scenario_id>/r<replica_idx>_s<scenario_speaker_id>_u<user_id>.wav`. Они автоматически переносятся в `data` после завершения озвучивания.
|
||||||
|
|
||||||
### База данных
|
### База данных
|
||||||
|
|
||||||
Служебные данные, такие как состояния пользовательских сессий, соответствие `dataset_speaker_id` и `telegram_user_id` и т. п., сохраняются в базе данных SQLite. После перезагрузки бота его состояние полностью восстанавливается из базы данных.
|
Служебные данные, такие как состояния пользовательских сессий, соответствие `user_id` и `telegram_user_id` и т. п., сохраняются в базе данных SQLite. После перезагрузки бота его состояние полностью восстанавливается из базы данных.
|
||||||
|
|
||||||
## Интерфейс бота
|
## Интерфейс бота
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ async def save_voice_message(
|
|||||||
file_id: str,
|
file_id: str,
|
||||||
user_id: int,
|
user_id: int,
|
||||||
scenario_id: str,
|
scenario_id: str,
|
||||||
|
speaker_id: int,
|
||||||
replica_index: int,
|
replica_index: int,
|
||||||
duration: int,
|
duration: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -21,7 +22,7 @@ async def save_voice_message(
|
|||||||
|
|
||||||
# Скачиваем файл
|
# Скачиваем файл
|
||||||
file = await bot.get_file(file_id)
|
file = await bot.get_file(file_id)
|
||||||
filename = get_audio_filename(replica_index, user_id)
|
filename = get_audio_filename(replica_index, speaker_id, user_id)
|
||||||
filepath = scenario_dir / filename
|
filepath = scenario_dir / filename
|
||||||
|
|
||||||
await file.download_to_drive(filepath)
|
await file.download_to_drive(filepath)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class UserState(Enum):
|
|||||||
class User:
|
class User:
|
||||||
"""Пользователь бота (диктор в датасете)."""
|
"""Пользователь бота (диктор в датасете)."""
|
||||||
|
|
||||||
id: int # dataset_speaker_id
|
id: int
|
||||||
telegram_id: int
|
telegram_id: int
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
gender: str | None # "male" или "female"
|
gender: str | None # "male" или "female"
|
||||||
@@ -61,7 +61,7 @@ class Recording:
|
|||||||
"""Запись озвучки реплики."""
|
"""Запись озвучки реплики."""
|
||||||
|
|
||||||
id: int
|
id: int
|
||||||
user_id: int # dataset_speaker_id
|
user_id: int
|
||||||
scenario_id: str
|
scenario_id: str
|
||||||
replica_index: int
|
replica_index: int
|
||||||
duration: float # длительность в секундах
|
duration: float # длительность в секундах
|
||||||
@@ -190,7 +190,7 @@ def get_or_create_user(telegram_id: int) -> User:
|
|||||||
)
|
)
|
||||||
row = cursor.fetchone()
|
row = cursor.fetchone()
|
||||||
conn.commit()
|
conn.commit()
|
||||||
logger.info(f"Создан новый пользователь: dataset_speaker_id={row['id']}")
|
logger.info(f"Создан новый пользователь: user_id={row['id']}")
|
||||||
return User(
|
return User(
|
||||||
id=row["id"],
|
id=row["id"],
|
||||||
telegram_id=row["telegram_id"],
|
telegram_id=row["telegram_id"],
|
||||||
|
|||||||
@@ -878,6 +878,7 @@ async def handle_voice_message(
|
|||||||
voice.file_id,
|
voice.file_id,
|
||||||
user.id,
|
user.id,
|
||||||
session.scenario_id,
|
session.scenario_id,
|
||||||
|
session.speaker_id,
|
||||||
real_replica_index,
|
real_replica_index,
|
||||||
voice.duration,
|
voice.duration,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -222,9 +222,9 @@ def get_data_dir(scenario_id: str) -> Path:
|
|||||||
return DATA_DIR / scenario_id
|
return DATA_DIR / scenario_id
|
||||||
|
|
||||||
|
|
||||||
def get_audio_filename(replica_index: int, user_id: int) -> str:
|
def get_audio_filename(replica_index: int, speaker_id: int, user_id: int) -> str:
|
||||||
"""Формирует имя файла для аудиозаписи."""
|
"""Формирует имя файла для аудиозаписи."""
|
||||||
return f"{replica_index}_{user_id}.wav"
|
return f"r{replica_index:03d}_s{speaker_id:02d}_u{user_id:03d}.wav"
|
||||||
|
|
||||||
|
|
||||||
def is_scenario_complete(scenario_id: str) -> bool:
|
def is_scenario_complete(scenario_id: str) -> bool:
|
||||||
@@ -302,7 +302,7 @@ def move_track_to_data(user_id: int, scenario_id: str, speaker_id: int) -> None:
|
|||||||
moved_count = 0
|
moved_count = 0
|
||||||
|
|
||||||
for replica in track_replicas:
|
for replica in track_replicas:
|
||||||
filename = get_audio_filename(replica.replica_index, user_id)
|
filename = get_audio_filename(replica.replica_index, speaker_id, user_id)
|
||||||
src = partial_dir / filename
|
src = partial_dir / filename
|
||||||
dst = data_dir / filename
|
dst = data_dir / filename
|
||||||
|
|
||||||
@@ -323,7 +323,7 @@ def delete_partial_track(user_id: int, scenario_id: str, speaker_id: int) -> Non
|
|||||||
deleted_count = 0
|
deleted_count = 0
|
||||||
|
|
||||||
for replica in track_replicas:
|
for replica in track_replicas:
|
||||||
filename = get_audio_filename(replica.replica_index, user_id)
|
filename = get_audio_filename(replica.replica_index, speaker_id, user_id)
|
||||||
filepath = partial_dir / filename
|
filepath = partial_dir / filename
|
||||||
if filepath.exists():
|
if filepath.exists():
|
||||||
filepath.unlink()
|
filepath.unlink()
|
||||||
|
|||||||
Reference in New Issue
Block a user