Spaces:
Sleeping
Sleeping
| from django.db import models | |
| from django.conf import settings | |
| class Project(models.Model): | |
| """ | |
| 翻译项目模型 | |
| """ | |
| STATUS_CHOICES = [ | |
| ('draft', '草稿'), | |
| ('processing', '处理中'), | |
| ('completed', '已完成'), | |
| ('failed', '失败'), | |
| ] | |
| LANGUAGE_CHOICES = [ | |
| ('Chinese', '中文'), | |
| ('Chinese,Yue', '粤语'), | |
| ('English', '英语'), | |
| ('Spanish', '西班牙语'), | |
| ('French', '法语'), | |
| ('Russian', '俄语'), | |
| ('German', '德语'), | |
| ('Portuguese', '葡萄牙语'), | |
| ('Arabic', '阿拉伯语'), | |
| ('Italian', '意大利语'), | |
| ('Japanese', '日语'), | |
| ('Korean', '韩语'), | |
| ('Indonesian', '印尼语'), | |
| ('Vietnamese', '越南语'), | |
| ('Turkish', '土耳其语'), | |
| ('Dutch', '荷兰语'), | |
| ('Ukrainian', '乌克兰语'), | |
| ('Thai', '泰语'), | |
| ('Polish', '波兰语'), | |
| ('Romanian', '罗马尼亚语'), | |
| ('Greek', '希腊语'), | |
| ('Czech', '捷克语'), | |
| ('Finnish', '芬兰语'), | |
| ('Hindi', '印地语'), | |
| ('Bulgarian', '保加利亚语'), | |
| ('Danish', '丹麦语'), | |
| ('Hebrew', '希伯来语'), | |
| ('Malay', '马来语'), | |
| ('Persian', '波斯语'), | |
| ('Slovak', '斯洛伐克语'), | |
| ('Swedish', '瑞典语'), | |
| ('Croatian', '克罗地亚语'), | |
| ('Filipino', '菲律宾语'), | |
| ('Hungarian', '匈牙利语'), | |
| ('Norwegian', '挪威语'), | |
| ('Slovenian', '斯洛文尼亚语'), | |
| ('Catalan', '加泰罗尼亚语'), | |
| ('Nynorsk', '尼诺斯克语'), | |
| ('Tamil', '泰米尔语'), | |
| ('Afrikaans', '阿非利卡语'), | |
| ] | |
| user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='projects') | |
| name = models.CharField(max_length=200, help_text="项目名称") | |
| description = models.TextField(blank=True, help_text="项目描述") | |
| source_lang = models.CharField(max_length=20, choices=LANGUAGE_CHOICES, default='Chinese', help_text="源语言") | |
| target_lang = models.CharField(max_length=20, choices=LANGUAGE_CHOICES, default='English', help_text="目标语言") | |
| # 文件路径 | |
| srt_file_path = models.FileField(upload_to='srt/', blank=True, null=True, help_text="SRT文件路径") | |
| video_file_path = models.FileField(upload_to='videos/', blank=True, null=True, help_text="视频文件路径") | |
| concatenated_audio_url = models.CharField(max_length=500, blank=True, help_text="拼接后的完整音频URL") | |
| # 项目级配置 | |
| tts_model = models.CharField(max_length=50, default="speech-2.5-hd-preview", help_text="TTS模型") | |
| voice_mappings = models.JSONField(default=list, help_text="角色音色映射表") | |
| custom_vocabulary = models.JSONField(default=list, help_text="专有词汇表") | |
| max_speed = models.FloatField( | |
| default=2.0, | |
| help_text="TTS时间戳对齐允许的最大speed参数,范围1.2-2.0" | |
| ) | |
| num_speakers = models.IntegerField(default=2, help_text="对话中的角色数量,用于LLM自动分配说话人") | |
| background_info = models.TextField(blank=True, default='', help_text="对话背景信息,用于辅助LLM分配说话人") | |
| status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft') | |
| created_at = models.DateTimeField(auto_now_add=True) | |
| updated_at = models.DateTimeField(auto_now=True) | |
| class Meta: | |
| verbose_name = "项目" | |
| verbose_name_plural = "项目" | |
| ordering = ['-updated_at'] | |
| def __str__(self): | |
| return f"{self.name} ({self.get_source_lang_display()} → {self.get_target_lang_display()})" | |
| def segment_count(self): | |
| """返回段落总数""" | |
| return self.segments.count() | |
| def completed_segment_count(self): | |
| """返回已完成的段落数(ratio <= 1的段落)""" | |
| return self.segments.filter( | |
| ratio__lte=1.0, | |
| ratio__isnull=False | |
| ).count() | |
| def progress_percentage(self): | |
| """返回项目进度百分比""" | |
| total = self.segment_count | |
| if total == 0: | |
| return 0 | |
| completed = self.completed_segment_count | |
| return int((completed / total) * 100) | |
| def progress_stats(self): | |
| """返回基于时长比例的进度统计信息""" | |
| segments = self.segments.all() | |
| total = segments.count() | |
| if total == 0: | |
| return { | |
| 'total': 0, | |
| 'aligned_count': 0, | |
| 'unaligned_count': 0, | |
| 'no_ratio_count': 0, | |
| 'percentage': 0 | |
| } | |
| # 统计有ratio值的段落 | |
| with_ratio = segments.filter(ratio__isnull=False) | |
| aligned_count = with_ratio.filter(ratio__lte=1.0).count() # ratio <= 1的段落 | |
| unaligned_count = with_ratio.filter(ratio__gt=1.0).count() # ratio > 1的段落 | |
| no_ratio_count = segments.filter(ratio__isnull=True).count() # 没有ratio的段落 | |
| # 计算进度百分比(ratio <= 1的段落) | |
| percentage = int((aligned_count / total) * 100) if total > 0 else 0 | |
| return { | |
| 'total': total, | |
| 'aligned_count': aligned_count, | |
| 'unaligned_count': unaligned_count, | |
| 'no_ratio_count': no_ratio_count, | |
| 'percentage': percentage | |
| } | |
| def audio_url(self): | |
| """返回音频文件URL - 从视频中提取的音频或单独的音频文件""" | |
| # 如果项目有单独的音频文件,优先返回音频文件 | |
| # 否则可以从视频文件中提取音频,但这里暂时返回None | |
| # 因为我们不应该直接把视频URL当作音频URL使用 | |
| return None | |
| def video_url(self): | |
| """返回视频文件URL""" | |
| if self.video_file_path: | |
| return self.video_file_path.url | |
| return None | |