rtc_backend/apps/stories/management/commands/generate_intro_opus.py
repair-agent 51a673e814
All checks were successful
Build and Deploy Backend / build-and-deploy (push) Successful in 6m7s
Add story music
2026-03-04 13:11:10 +08:00

133 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
批量为故事和音乐生成引导语 Opus 数据并写入数据库。
使用方法:
python manage.py generate_intro_opus # 处理所有
python manage.py generate_intro_opus --type story # 仅故事
python manage.py generate_intro_opus --type music # 仅音乐
python manage.py generate_intro_opus --dry-run # 仅统计
python manage.py generate_intro_opus --limit 10 # 只处理前 10 个
python manage.py generate_intro_opus --force # 重新生成已有引导语的记录
"""
import logging
from django.core.management.base import BaseCommand
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = '批量为故事和音乐生成引导语 Opus 数据'
def add_arguments(self, parser):
parser.add_argument(
'--type', choices=['story', 'music', 'all'], default='all',
help='处理类型story / music / all默认 all',
)
parser.add_argument(
'--dry-run', action='store_true',
help='仅统计需要处理的数量,不实际执行',
)
parser.add_argument(
'--limit', type=int, default=0,
help='最多处理的数量0=不限)',
)
parser.add_argument(
'--force', action='store_true',
help='重新生成已有引导语的记录',
)
def handle(self, *args, **options):
content_type = options['type']
dry_run = options['dry_run']
limit = options['limit']
force = options['force']
if content_type in ('story', 'all'):
self._process_stories(dry_run, limit, force)
if content_type in ('music', 'all'):
self._process_tracks(dry_run, limit, force)
def _process_stories(self, dry_run, limit, force):
from apps.stories.models import Story
from apps.stories.services.intro_service import generate_intro_opus
self.stdout.write(self.style.MIGRATE_HEADING('\n=== 故事引导语 ==='))
qs = Story.objects.exclude(audio_url='')
if not force:
qs = qs.filter(intro_opus_data='')
qs = qs.order_by('id')
total = qs.count()
self.stdout.write(f'需要处理的故事: {total}')
if dry_run or total == 0:
return
items = qs[:limit] if limit > 0 else qs
success_count = 0
fail_count = 0
for i, story in enumerate(items.iterator(), 1):
self.stdout.write(f'[{i}/{total}] Story#{story.id} "{story.title}"')
try:
opus_json = generate_intro_opus(story.title, content_type='story')
story.intro_opus_data = opus_json
story.save(update_fields=['intro_opus_data'])
success_count += 1
self.stdout.write(self.style.SUCCESS(
f' OK ({len(opus_json) / 1024:.1f} KB)'
))
except Exception as e:
fail_count += 1
self.stdout.write(self.style.ERROR(f' FAIL: {e}'))
logger.error(f'Story#{story.id} intro generate failed: {e}')
self.stdout.write(self.style.SUCCESS(
f'故事完成: 成功 {success_count}, 失败 {fail_count}'
))
def _process_tracks(self, dry_run, limit, force):
from apps.music.models import Track
from apps.stories.services.intro_service import generate_intro_opus
self.stdout.write(self.style.MIGRATE_HEADING('\n=== 音乐引导语 ==='))
qs = Track.objects.filter(
generation_status='completed',
).exclude(audio_url='')
if not force:
qs = qs.filter(intro_opus_data='')
qs = qs.order_by('id')
total = qs.count()
self.stdout.write(f'需要处理的曲目: {total}')
if dry_run or total == 0:
return
items = qs[:limit] if limit > 0 else qs
success_count = 0
fail_count = 0
for i, track in enumerate(items.iterator(), 1):
self.stdout.write(f'[{i}/{total}] Track#{track.id} "{track.title}"')
try:
opus_json = generate_intro_opus(track.title, content_type='music')
track.intro_opus_data = opus_json
track.save(update_fields=['intro_opus_data'])
success_count += 1
self.stdout.write(self.style.SUCCESS(
f' OK ({len(opus_json) / 1024:.1f} KB)'
))
except Exception as e:
fail_count += 1
self.stdout.write(self.style.ERROR(f' FAIL: {e}'))
logger.error(f'Track#{track.id} intro generate failed: {e}')
self.stdout.write(self.style.SUCCESS(
f'音乐完成: 成功 {success_count}, 失败 {fail_count}'
))