Понятные имена для аудиофайлов
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,
|
||||
user_id: int,
|
||||
scenario_id: str,
|
||||
speaker_id: int,
|
||||
replica_index: int,
|
||||
duration: int,
|
||||
) -> None:
|
||||
@@ -21,7 +22,7 @@ async def save_voice_message(
|
||||
|
||||
# Скачиваем файл
|
||||
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
|
||||
|
||||
await file.download_to_drive(filepath)
|
||||
|
||||
@@ -30,7 +30,7 @@ class UserState(Enum):
|
||||
class User:
|
||||
"""Пользователь бота (диктор в датасете)."""
|
||||
|
||||
id: int # dataset_speaker_id
|
||||
id: int
|
||||
telegram_id: int
|
||||
created_at: datetime
|
||||
gender: str | None # "male" или "female"
|
||||
@@ -61,7 +61,7 @@ class Recording:
|
||||
"""Запись озвучки реплики."""
|
||||
|
||||
id: int
|
||||
user_id: int # dataset_speaker_id
|
||||
user_id: int
|
||||
scenario_id: str
|
||||
replica_index: int
|
||||
duration: float # длительность в секундах
|
||||
@@ -190,7 +190,7 @@ def get_or_create_user(telegram_id: int) -> User:
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
conn.commit()
|
||||
logger.info(f"Создан новый пользователь: dataset_speaker_id={row['id']}")
|
||||
logger.info(f"Создан новый пользователь: user_id={row['id']}")
|
||||
return User(
|
||||
id=row["id"],
|
||||
telegram_id=row["telegram_id"],
|
||||
|
||||
@@ -878,6 +878,7 @@ async def handle_voice_message(
|
||||
voice.file_id,
|
||||
user.id,
|
||||
session.scenario_id,
|
||||
session.speaker_id,
|
||||
real_replica_index,
|
||||
voice.duration,
|
||||
)
|
||||
|
||||
@@ -222,9 +222,9 @@ def get_data_dir(scenario_id: str) -> Path:
|
||||
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:
|
||||
@@ -302,7 +302,7 @@ def move_track_to_data(user_id: int, scenario_id: str, speaker_id: int) -> None:
|
||||
moved_count = 0
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
if filepath.exists():
|
||||
filepath.unlink()
|
||||
|
||||
Reference in New Issue
Block a user