@@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-04-02 11:06+0300\n" +"POT-Creation-Date: 2026-04-03 16:06+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -190,7 +190,7 @@ msgstr "Дочерние Бизнес Аккаунты" #: authentication/models/business_group.py:8 ml_model/models.py:17 #: ml_model/models.py:37 ml_model/models.py:61 ml_model/models.py:268 -#: tools/chats/models.py:9 tools/media/models.py:54 tools/media/models.py:81 +#: tools/chats/models.py:9 tools/media/models.py:59 tools/media/models.py:93 msgid "Title" msgstr "Название" @@ -208,7 +208,7 @@ msgstr "Бизнес Группы" #: authentication/models/user_vk.py:12 payments/admin.py:37 #: payments/admin.py:97 payments/models/invoice.py:15 #: payments/models/payment.py:26 payments/models/payment_plan.py:62 -#: tools/media/models.py:94 +#: tools/media/models.py:106 msgid "User" msgstr "Пользователь" @@ -730,7 +730,7 @@ msgid "Available only in paid plan" msgstr "Доступно только в платном тарифе" #: ml_model/models.py:18 ml_model/models.py:38 ml_model/models.py:70 -#: ml_model/models.py:182 tools/media/models.py:56 +#: ml_model/models.py:182 tools/media/models.py:67 msgid "Slug" msgstr "Ярлык" @@ -1327,7 +1327,7 @@ msgid "" "Temporary issues with the service, we are already working on a solution." msgstr "Временные неполадки с сервисом, мы уже работаем над их решением." -#: tools/media/apis.py:252 +#: tools/media/apis.py:255 msgid "Voice not found." msgstr "Голос не найден." @@ -1339,39 +1339,51 @@ msgstr "Хранилище клонирования голоса" msgid "Voice clone stores" msgstr "Хранилища клонирования голоса" -#: tools/media/models.py:61 tools/media/models.py:91 +#: tools/media/models.py:54 tools/media/models.py:131 +msgid "Voice" +msgstr "Голос" + +#: tools/media/models.py:55 +msgid "Instrumental" +msgstr "Инструментал" + +#: tools/media/models.py:62 +msgid "Kind" +msgstr "Тип" + +#: tools/media/models.py:72 tools/media/models.py:103 msgid "File" msgstr "Файл" -#: tools/media/models.py:72 +#: tools/media/models.py:78 +msgid "Meta" +msgstr "Метаданные" + +#: tools/media/models.py:84 msgid "Preset" msgstr "Пресет" -#: tools/media/models.py:73 +#: tools/media/models.py:85 msgid "Presets" msgstr "Пресеты" -#: tools/media/models.py:0 -msgid "Instrumental" -msgstr "Инструментал" - -#: tools/media/models.py:88 +#: tools/media/models.py:100 msgid "Only MP3, OGG, and WAV audio files are allowed." msgstr "Разрешены только аудиофайлы MP3, OGG и WAV." -#: tools/media/models.py:99 +#: tools/media/models.py:108 +msgid "Transcription" +msgstr "Транскрипция" + +#: tools/media/models.py:112 msgid "Unknown file" msgstr "Неизвестный файл" -#: tools/media/models.py:118 -msgid "Voice" -msgstr "Голос" - -#: tools/media/models.py:119 +#: tools/media/models.py:132 msgid "Voices" msgstr "Голоса" -#: tools/media/routes/v1.py:71 +#: tools/media/routes/v1.py:74 #, fuzzy #| msgid "Voice not found." msgid "Voice not found" @@ -50,7 +50,8 @@ class Qwen_3_Tts(SimpleService): 'mode': 'voice_clone', 'reference_audio': input_message.file.url, } - + if transcription := input_message.info.get('transcription', ''): + callback_data.update({'reference_text': transcription}) start_time = time.time() try: result = replicate_run('qwen/qwen3-tts', callback_data) @@ -0,0 +1,23 @@ +# Generated by Django 5.0.11 on 2026-04-03 13:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('media', '0006_preset_kind'), + ] + + operations = [ + migrations.AddField( + model_name='preset', + name='metadata', + field=models.JSONField(blank=True, default=dict, verbose_name='Meta'), + ), + migrations.AddField( + model_name='voice', + name='transcription', + field=models.TextField(blank=True, null=True, verbose_name='Transcription'), + ), + ] @@ -33,8 +33,10 @@ def get_links(request): @router.post('voices/', tags=['media/voices'], auth=AsyncAuthBearer(), response={201: None, 400: str}) -async def upload_voice(request, file: File[UploadedFile], title: str | None = None): - voice = Voice(user=request.auth, title=title, file=file) +async def upload_voice( + request, file: File[UploadedFile], title: str | None = None, transcription: str | None = None +): + voice = Voice(user=request.auth, title=title, file=file, transcription=transcription) try: await sync_to_async(voice.full_clean)() except ValidationError as exc: @@ -65,12 +67,13 @@ async def delete_voice(request, voice_id: UUID): auth=AsyncAuthBearer(), response={200: VoiceSchema, 400: str, 404: str}, ) -async def update_voice_title(request, voice_id: UUID, title: str): +async def update_voice_title(request, voice_id: UUID, title: str, transcription: str | None = None): try: voice = await Voice.objects.aget(uid=voice_id, user=request.auth) except Voice.DoesNotExist: raise HttpError(404, _('Voice not found')) voice.title = title + voice.transcription = transcription await voice.asave() return voice @@ -243,14 +243,19 @@ class ModelVoiceCloneAPIView(MediaAPIView): try: if voice_id := request.query_params.get('voice_id'): voice = Voice.objects.get(uid=voice_id, user=request.user) + transcription = voice.transcription elif preset_id := request.query_params.get('preset_id'): voice = Preset.objects.get(uid=preset_id) + transcription = voice.metadata.get('transcription', '') else: voice = Preset.objects.get(slug='russian_1') + transcription = voice.metadata.get('transcription', '') except Voice.DoesNotExist: return Response( {'detail': _('Voice not found.')}, status=HTTP_400_BAD_REQUEST, ) - request.data.update({'file': voice.file}) + request.data.update( + {'file': voice.file, 'info': {'transcription': transcription, **request.data['info']}} + ) return super().post(request, model, *args, **kwargs) @@ -75,6 +75,7 @@ class Preset(BaseModel): null=True, blank=True, ) + metadata = models.JSONField(default=dict, blank=True, verbose_name=_('Meta')) def __str__(self) -> str: return self.title @@ -104,6 +105,7 @@ class Voice(BaseModel): user = models.ForeignKey( get_user_model(), on_delete=models.CASCADE, related_name='uploaded_voices', verbose_name=_('User') ) + transcription = models.TextField(blank=True, null=True, verbose_name=_('Transcription')) def save(self, *args, **kwargs): if not self.title: @@ -6,10 +6,10 @@ from tools.media.models import Preset, Voice class VoiceSchema(ModelSchema): class Meta: model = Voice - fields = ('uid', 'title', 'file') + fields = ('uid', 'title', 'file', 'transcription') class PresetSchema(ModelSchema): class Meta: model = Preset - fields = ('uid', 'title', 'file') \ No newline at end of file + fields = ('uid', 'title', 'file', 'metadata') \ No newline at end of file