@@ -258,6 +258,7 @@ MINIO_PRIVATE_BUCKETS = [ 'air-stories', 'air-profiles', 'air-models', + 'air-media-presets' ] MINIO_STATIC_FILES_BUCKET = 'air-static' MINIO_PRIVATE_BUCKETS.append(MINIO_STATIC_FILES_BUCKET) @@ -2,13 +2,13 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-12-09 18:57+0300\n" +"POT-Creation-Date: 2025-12-12 17:36+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -46,8 +46,8 @@ msgid "" "cancellation of his current tariff plan (via the \"Report an error\" " "button), after which you will be able to invite him" msgstr "" -"У приглашенного уже активен тарифный план. Приглашенному необходимо запросить " -"аннулирование текущего тарифного плана (через кнопку \"Сообщить об " +"У приглашенного уже активен тарифный план. Приглашенному необходимо " +"запросить аннулирование текущего тарифного плана (через кнопку \"Сообщить об " "ошибке\"), после чего у вас появится возможность его пригласить" #: authentication/exceptions/business_host_exceptions/not_allowed_ip.py:6 @@ -147,6 +147,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 #: payments/models/payment_plan.py:27 tools/chats/models.py:9 +#: tools/media/models.py:41 msgid "Title" msgstr "Название" @@ -512,11 +513,11 @@ msgstr "" msgid "Error occured when proceed email sending" msgstr "Случилась ошибка во время отправки email" -#: authentication/services/email_service.py:107 +#: authentication/services/email_service.py:117 msgid "Regular users cannot send introductory letters" msgstr "Обычные пользователи не могут отсылать письма" -#: authentication/services/email_service.py:131 +#: authentication/services/email_service.py:143 msgid "Regular users cannot send invitation letters" msgstr "Обычные пользователи не могут отправлять письма для приглашений" @@ -655,7 +656,7 @@ msgid "Use style type AUTO or GENERAL when a style preset is selected" msgstr "При выбранном стиле используйте тип стиля AUTO или GENERAL" #: ml_model/models.py:18 ml_model/models.py:38 ml_model/models.py:70 -#: ml_model/models.py:182 +#: ml_model/models.py:182 tools/media/models.py:43 msgid "Slug" msgstr "Ярлык" @@ -937,12 +938,12 @@ msgstr "Инструкции Моделей" msgid "no model by this id" msgstr "Не найдено моделей по этому ID" -#: ml_model/services/chatgpt.py:225 +#: ml_model/services/chatgpt.py:161 msgid "No matching version found" msgstr "Соответствующая версия не найдена" -#: ml_model/services/minio_service.py:35 ml_model/services/minio_service.py:53 -#: ml_model/services/minio_service.py:61 ml_model/services/minio_service.py:70 +#: ml_model/services/minio_service.py:36 ml_model/services/minio_service.py:54 +#: ml_model/services/minio_service.py:62 ml_model/services/minio_service.py:71 msgid "Unknown bucket destination" msgstr "Неизвестный бакет для загрузки" @@ -1203,6 +1204,18 @@ msgstr "Чат %(id)s" msgid "Chat" msgstr "Чат" +#: tools/media/models.py:48 +msgid "File" +msgstr "Файл" + +#: tools/media/models.py:59 +msgid "Preset" +msgstr "Пресет" + +#: tools/media/models.py:60 +msgid "Presets" +msgstr "Пресеты" + #: tools/public_api/exceptions.py:7 msgid "Upgrade token limit on your api-key" msgstr "Необходимо повысить лимит токенов у API-ключа" @@ -30,6 +30,7 @@ from ml_model.services.logoai import Logoai from ml_model.services.lyria import Lyria from ml_model.services.midjourney import Midjourney from ml_model.services.minimaxvideo import Minimaxvideo +from ml_model.services.minimaxmusic import Minimaxmusic from ml_model.services.mistral import Mistral from ml_model.services.musicgen import Musicgen from ml_model.services.nanobanana import Nanobanana @@ -0,0 +1,54 @@ +import time +from datetime import timedelta +from decimal import Decimal +from io import BytesIO + +import requests +from django.core.files import File + +from messages.models import Message +from ml_model.services.base import SimpleService +from ml_model.tasks import replicate_run + +from payments.exceptions.insufficient_balance import InsufficientBalance +from payments.selectors.payment_plan_selector import PaymentPlanSelector +from tools.media.models import Preset + + +class Minimaxmusic(SimpleService): + TOKENS_COST = Decimal('10.5') + + def calculate_price(self) -> Decimal: + return self.TOKENS_COST + + def save_results(self, content: str, t: timedelta, video: str, save: bool = True) -> list[Message]: + msg = Message( + content=content, + content_object=self.store, + elapsed_time=t, + file=File(BytesIO(requests.get(video).content), '.mp3'), + ) + if save: + return Message.objects.bulk_create([msg]) + return [msg] + + def make(self, input_message: Message, save: bool = True) -> list[Message]: + speaker = input_message.info.get('speaker', 'russian_1') + instrumental = input_message.info.get('instrumental', 'classical') + if (balance := PaymentPlanSelector(self.store.user).get_current_balance()) < self.TOKENS_COST: + raise InsufficientBalance(balance, self.TOKENS_COST) + callback_data = {'lyrics': input_message.content, **input_message.info} + if speaker: + file_url = Preset.objects.get(slug=speaker).file.url + callback_data.update({'voice_file': file_url}) + if input_message.file: + callback_data.update({'instrumental_file': input_message.file.url}) + else: + file_url = Preset.objects.get(slug=instrumental).file.url + callback_data.update({'instrumental_file': file_url}) + start_time = time.time() + video = replicate_run(f'minimax/music-01', callback_data) + process_time = timedelta(seconds=(time.time() - start_time)) + self.handle_invoice(input_message.content_object.model) + msgs = self.save_results(input_message.content, process_time, video, save) + return msgs @@ -20,6 +20,7 @@ class MinIOService: 'air-errors', 'air-welcome-pic', 'air-messages', + 'air-media-presets' ] def __init__(self): @@ -1,7 +1,6 @@ from decimal import Decimal +from ml_model.services import Minimaxmusic -from ml_model.services.stablemusic import Stablemusic - -class Suno(Stablemusic): - PRICE = Decimal('0.057') +class Suno(Minimaxmusic): + TOKENS_COST = Decimal('17.5') @@ -0,0 +1,28 @@ +# Generated by Django 5.0.11 on 2025-12-12 14:31 + +import django_minio_backend.models +import tools.media.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('media', '0003_alter_audio_options_alter_image_options_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Preset', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=50, verbose_name='Title')), + ('slug', models.SlugField(unique=True, verbose_name='Slug')), + ('file', models.FileField(blank=True, null=True, storage=django_minio_backend.models.MinioBackend(bucket_name='air-media-presets'), upload_to=tools.media.models.message_file_upload, verbose_name='File')), + ], + options={ + 'verbose_name': 'Preset', + 'verbose_name_plural': 'Presets', + }, + ), + ] @@ -2,7 +2,7 @@ from django.contrib import admin from messages.inlines import MessageInline -from .models import Audio, Image, Video +from .models import Audio, Image, Video, Preset @admin.register(Image) @@ -33,3 +33,9 @@ class AudioAdmin(admin.ModelAdmin): MessageInline, ] list_per_page = 10 + + +@admin.register(Preset) +class PresetAdmin(admin.ModelAdmin): + list_display = ('title', 'slug') + search_fields = ('title', 'slug') @@ -1,4 +1,7 @@ +from django.db import models from django.db.models import QuerySet +from django_minio_backend import MinioBackend +from django.utils.translation import gettext_lazy as _ from messages.models import Message, MultipleStore @@ -28,3 +31,30 @@ class Audio(Gallery): class Meta: verbose_name = 'Хранилище аудио' verbose_name_plural = 'Хранилища аудио' + + +def message_file_upload(instance: 'Preset', filename: str): + return f'{filename}' + + +class Preset(models.Model): + title = models.CharField(max_length=50, verbose_name=_('Title')) + slug = models.SlugField( + verbose_name=_('Slug'), + unique=True, + max_length=50, + ) + file = models.FileField( + verbose_name=_('File'), + storage=MinioBackend(bucket_name='air-media-presets'), + upload_to=message_file_upload, + null=True, + blank=True, + ) + + def __str__(self) -> str: + return self.title + + class Meta: + verbose_name = _('Preset') + verbose_name_plural = _('Presets')