All checks were successful
Build and Deploy Backend / build-and-deploy (push) Successful in 6m7s
133 lines
4.8 KiB
Python
133 lines
4.8 KiB
Python
"""
|
||
批量为故事和音乐生成引导语 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}'
|
||
))
|