Feat whisper opts (#1368)

This commit is contained in:
Markos Gogoulos 2025-09-04 13:39:41 +03:00 committed by GitHub
parent 6cee02085c
commit 8d982ace92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 91 additions and 46 deletions

View File

@ -222,12 +222,12 @@ class WhisperSubtitlesForm(forms.ModelForm):
"allow_whisper_transcribe_and_translate", "allow_whisper_transcribe_and_translate",
) )
labels = { labels = {
"allow_whisper_transcribe": "automatic transcription", "allow_whisper_transcribe": "Transcription",
"allow_whisper_transcribe_and_translate": "automatic transcription and translation", "allow_whisper_transcribe_and_translate": "English Translation",
} }
help_texts = { help_texts = {
"allow_whisper_transcribe": "Request automatic transcription for this media.", "allow_whisper_transcribe": "",
"allow_whisper_transcribe_and_translate": "Request automatic transcription and translation for this media.", "allow_whisper_transcribe_and_translate": "",
} }
def __init__(self, user, *args, **kwargs): def __init__(self, user, *args, **kwargs):
@ -281,7 +281,7 @@ class SubtitleForm(forms.ModelForm):
fields = ["language", "subtitle_file"] fields = ["language", "subtitle_file"]
labels = { labels = {
"subtitle_file": "Subtitle or Closed Caption File", "subtitle_file": "Upload Caption File",
} }
help_texts = { help_texts = {
"subtitle_file": "SubRip (.srt) and WebVTT (.vtt) are supported file formats.", "subtitle_file": "SubRip (.srt) and WebVTT (.vtt) are supported file formats.",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "ترجمات", "Subtitles": "ترجمات",
"Tags": "العلامات", "Tags": "العلامات",
"Terms": "الشروط", "Terms": "الشروط",
"This works in Chrome, Safari and Edge browsers.": "هذا يعمل في متصفحات Chrome و Safari و Edge.",
"Trim": "قص", "Trim": "قص",
"UPLOAD": "رفع", "UPLOAD": "رفع",
"Up next": "التالي", "Up next": "التالي",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "সাবটাইটেল", "Subtitles": "সাবটাইটেল",
"Tags": "ট্যাগ", "Tags": "ট্যাগ",
"Terms": "শর্তাবলী", "Terms": "শর্তাবলী",
"This works in Chrome, Safari and Edge browsers.": "এটি ক্রোম, সাফারি এবং এজ ব্রাউজারে কাজ করে।",
"Trim": "ছাঁটাই", "Trim": "ছাঁটাই",
"UPLOAD": "আপলোড করুন", "UPLOAD": "আপলোড করুন",
"Up next": "পরবর্তী", "Up next": "পরবর্তী",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "Undertekster", "Subtitles": "Undertekster",
"Tags": "Tags", "Tags": "Tags",
"Terms": "Vilkår", "Terms": "Vilkår",
"This works in Chrome, Safari and Edge browsers.": "Dette virker i Chrome, Safari og Edge browsere.",
"Trim": "Beskær", "Trim": "Beskær",
"UPLOAD": "UPLOAD", "UPLOAD": "UPLOAD",
"Up next": "Næste", "Up next": "Næste",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "Untertitel", "Subtitles": "Untertitel",
"Tags": "Tags", "Tags": "Tags",
"Terms": "Bedingungen", "Terms": "Bedingungen",
"This works in Chrome, Safari and Edge browsers.": "Dies funktioniert in den Browsern Chrome, Safari und Edge.",
"Trim": "Trimmen", "Trim": "Trimmen",
"UPLOAD": "HOCHLADEN", "UPLOAD": "HOCHLADEN",
"Up next": "Als nächstes", "Up next": "Als nächstes",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "Υπότιτλοι", "Subtitles": "Υπότιτλοι",
"Tags": "Ετικέτες", "Tags": "Ετικέτες",
"Terms": "Όροι", "Terms": "Όροι",
"This works in Chrome, Safari and Edge browsers.": "Αυτό λειτουργεί σε προγράμματα περιήγησης Chrome, Safari και Edge.",
"Trim": "Περικοπή", "Trim": "Περικοπή",
"UPLOAD": "ΑΝΕΒΑΣΜΑ", "UPLOAD": "ΑΝΕΒΑΣΜΑ",
"Up next": "Επόμενο", "Up next": "Επόμενο",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitle was added": "", "Subtitle was added": "",
"Tags": "", "Tags": "",
"Terms": "", "Terms": "",
"This works in Chrome, Safari and Edge browsers.": "",
"Trim": "", "Trim": "",
"UPLOAD": "", "UPLOAD": "",
"Up next": "", "Up next": "",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "Subtítulos", "Subtitles": "Subtítulos",
"Tags": "Etiquetas", "Tags": "Etiquetas",
"Terms": "Términos", "Terms": "Términos",
"This works in Chrome, Safari and Edge browsers.": "Esto funciona en los navegadores Chrome, Safari y Edge.",
"Trim": "Recortar", "Trim": "Recortar",
"UPLOAD": "SUBIR", "UPLOAD": "SUBIR",
"Up next": "A continuación", "Up next": "A continuación",

View File

@ -66,6 +66,7 @@ translation_strings = {
"Subtitles": "Sous-titres", "Subtitles": "Sous-titres",
"Tags": "Tags", "Tags": "Tags",
"Terms": "Conditions", "Terms": "Conditions",
"This works in Chrome, Safari and Edge browsers.": "Cela fonctionne dans les navigateurs Chrome, Safari et Edge.",
"Trim": "Couper", "Trim": "Couper",
"UPLOAD": "TÉLÉCHARGER", "UPLOAD": "TÉLÉCHARGER",
"Up next": "À suivre", "Up next": "À suivre",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "כתוביות", "Subtitles": "כתוביות",
"Tags": "תגיות", "Tags": "תגיות",
"Terms": "תנאים", "Terms": "תנאים",
"This works in Chrome, Safari and Edge browsers.": "זה עובד בדפדפני Chrome, Safari ו-Edge.",
"Trim": "גזירה", "Trim": "גזירה",
"UPLOAD": "העלה", "UPLOAD": "העלה",
"Up next": "הבא בתור", "Up next": "הבא בתור",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "उपशीर्षक", "Subtitles": "उपशीर्षक",
"Tags": "टैग", "Tags": "टैग",
"Terms": "शर्तें", "Terms": "शर्तें",
"This works in Chrome, Safari and Edge browsers.": "यह क्रोम, सफारी और एज ब्राउज़र में काम करता है।",
"Trim": "छांटें", "Trim": "छांटें",
"UPLOAD": "अपलोड करें", "UPLOAD": "अपलोड करें",
"Up next": "अगला", "Up next": "अगला",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "Subtitel", "Subtitles": "Subtitel",
"Tags": "Tag", "Tags": "Tag",
"Terms": "Ketentuan", "Terms": "Ketentuan",
"This works in Chrome, Safari and Edge browsers.": "Ini berfungsi di browser Chrome, Safari, dan Edge.",
"Trim": "Potong", "Trim": "Potong",
"UPLOAD": "UNGGAH", "UPLOAD": "UNGGAH",
"Up next": "Selanjutnya", "Up next": "Selanjutnya",

View File

@ -66,6 +66,7 @@ translation_strings = {
"Subtitles": "Sottotitoli", "Subtitles": "Sottotitoli",
"Tags": "Tag", "Tags": "Tag",
"Terms": "Termini e condizioni", "Terms": "Termini e condizioni",
"This works in Chrome, Safari and Edge browsers.": "Questo funziona nei browser Chrome, Safari e Edge.",
"Trim": "Taglia", "Trim": "Taglia",
"UPLOAD": "CARICA", "UPLOAD": "CARICA",
"Up next": "A seguire", "Up next": "A seguire",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "字幕", "Subtitles": "字幕",
"Tags": "タグ", "Tags": "タグ",
"Terms": "利用規約", "Terms": "利用規約",
"This works in Chrome, Safari and Edge browsers.": "これはChrome、Safari、Edgeブラウザで動作します。",
"Trim": "トリム", "Trim": "トリム",
"UPLOAD": "アップロード", "UPLOAD": "アップロード",
"Up next": "次に再生", "Up next": "次に再生",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "자막", "Subtitles": "자막",
"Tags": "태그", "Tags": "태그",
"Terms": "약관", "Terms": "약관",
"This works in Chrome, Safari and Edge browsers.": "이 기능은 Chrome, Safari 및 Edge 브라우저에서 작동합니다.",
"Trim": "자르기", "Trim": "자르기",
"UPLOAD": "업로드", "UPLOAD": "업로드",
"Up next": "다음", "Up next": "다음",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "Ondertitels", "Subtitles": "Ondertitels",
"Tags": "Tags", "Tags": "Tags",
"Terms": "Voorwaarden", "Terms": "Voorwaarden",
"This works in Chrome, Safari and Edge browsers.": "Dit werkt in Chrome, Safari en Edge browsers.",
"Trim": "Bijsnijden", "Trim": "Bijsnijden",
"UPLOAD": "UPLOADEN", "UPLOAD": "UPLOADEN",
"Up next": "Hierna", "Up next": "Hierna",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "Legendas", "Subtitles": "Legendas",
"Tags": "Tags", "Tags": "Tags",
"Terms": "Termos", "Terms": "Termos",
"This works in Chrome, Safari and Edge browsers.": "Isso funciona nos navegadores Chrome, Safari e Edge.",
"Trim": "Cortar", "Trim": "Cortar",
"UPLOAD": "CARREGAR", "UPLOAD": "CARREGAR",
"Up next": "A seguir", "Up next": "A seguir",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "Субтитры", "Subtitles": "Субтитры",
"Tags": "Теги", "Tags": "Теги",
"Terms": "Условия", "Terms": "Условия",
"This works in Chrome, Safari and Edge browsers.": "Это работает в браузерах Chrome, Safari и Edge.",
"Trim": "Обрезать", "Trim": "Обрезать",
"UPLOAD": "ЗАГРУЗИТЬ", "UPLOAD": "ЗАГРУЗИТЬ",
"Up next": "Далее", "Up next": "Далее",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "Podnapisi", "Subtitles": "Podnapisi",
"Tags": "Oznake", "Tags": "Oznake",
"Terms": "Pogoji", "Terms": "Pogoji",
"This works in Chrome, Safari and Edge browsers.": "To deluje v brskalnikih Chrome, Safari in Edge.",
"Trim": "Obreži", "Trim": "Obreži",
"UPLOAD": "NALOŽI", "UPLOAD": "NALOŽI",
"Up next": "Naslednji", "Up next": "Naslednji",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "Altyazılar", "Subtitles": "Altyazılar",
"Tags": "Etiketler", "Tags": "Etiketler",
"Terms": "Şartlar", "Terms": "Şartlar",
"This works in Chrome, Safari and Edge browsers.": "Bu, Chrome, Safari ve Edge tarayıcılarında çalışır.",
"Trim": "Kırp", "Trim": "Kırp",
"UPLOAD": "YÜKLE", "UPLOAD": "YÜKLE",
"Up next": "Sıradaki", "Up next": "Sıradaki",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "سب ٹائٹلز", "Subtitles": "سب ٹائٹلز",
"Tags": "ٹیگز", "Tags": "ٹیگز",
"Terms": "شرائط", "Terms": "شرائط",
"This works in Chrome, Safari and Edge browsers.": "یہ کروم، سفاری اور ایج براؤزرز میں کام کرتا ہے۔",
"Trim": "تراشیں", "Trim": "تراشیں",
"UPLOAD": "اپ لوڈ کریں", "UPLOAD": "اپ لوڈ کریں",
"Up next": "اگلا", "Up next": "اگلا",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "字幕", "Subtitles": "字幕",
"Tags": "标签", "Tags": "标签",
"Terms": "条款", "Terms": "条款",
"This works in Chrome, Safari and Edge browsers.": "此功能适用于 Chrome、Safari 和 Edge 浏览器。",
"Trim": "修剪", "Trim": "修剪",
"UPLOAD": "上传", "UPLOAD": "上传",
"Up next": "接下来", "Up next": "接下来",

View File

@ -65,6 +65,7 @@ translation_strings = {
"Subtitles": "字幕", "Subtitles": "字幕",
"Tags": "標籤", "Tags": "標籤",
"Terms": "使用條款", "Terms": "使用條款",
"This works in Chrome, Safari and Edge browsers.": "此功能適用於 Chrome、Safari 和 Edge 瀏覽器。",
"Trim": "修剪", "Trim": "修剪",
"UPLOAD": "上傳", "UPLOAD": "上傳",
"Up next": "即將播放", "Up next": "即將播放",

View File

@ -484,11 +484,11 @@ def whisper_transcribe(friendly_token, translate_to_english=False):
if translate_to_english: if translate_to_english:
language = Language.objects.filter(code="whisper-translation").first() language = Language.objects.filter(code="whisper-translation").first()
if not language: if not language:
language = Language.objects.create(code="whisper-translation", title="Automatic Transcription and Translation") language = Language.objects.create(code="whisper-translation", title="English Translation")
else: else:
language = Language.objects.filter(code="whisper").first() language = Language.objects.filter(code="whisper").first()
if not language: if not language:
language = Language.objects.create(code="whisper", title="Automatic Transcription") language = Language.objects.create(code="whisper", title="Transcription")
cwd = os.path.dirname(os.path.realpath(media.media_file.path)) cwd = os.path.dirname(os.path.realpath(media.media_file.path))
request.status = "running" request.status = "running"

View File

@ -88,7 +88,7 @@ def add_subtitle(request):
subtitle = form.save() subtitle = form.save()
try: try:
subtitle.convert_to_srt() subtitle.convert_to_srt()
messages.add_message(request, messages.INFO, "Subtitle was added!") messages.add_message(request, messages.INFO, "Caption was added!")
return HttpResponseRedirect(subtitle.media.get_absolute_url()) return HttpResponseRedirect(subtitle.media.get_absolute_url())
except Exception as e: # noqa except Exception as e: # noqa
subtitle.delete() subtitle.delete()
@ -147,7 +147,7 @@ def edit_subtitle(request):
elif request.method == "POST": elif request.method == "POST":
confirm = request.GET.get("confirm", "").strip() confirm = request.GET.get("confirm", "").strip()
if confirm == "true": if confirm == "true":
messages.add_message(request, messages.INFO, "Subtitle was deleted") messages.add_message(request, messages.INFO, "Caption was deleted")
redirect_url = subtitle.media.get_absolute_url() redirect_url = subtitle.media.get_absolute_url()
subtitle.delete() subtitle.delete()
return HttpResponseRedirect(redirect_url) return HttpResponseRedirect(redirect_url)
@ -156,7 +156,7 @@ def edit_subtitle(request):
with open(subtitle.subtitle_file.path, "w") as ff: with open(subtitle.subtitle_file.path, "w") as ff:
ff.write(subtitle_text) ff.write(subtitle_text)
messages.add_message(request, messages.INFO, "Subtitle was edited") messages.add_message(request, messages.INFO, "Caption was edited")
return HttpResponseRedirect(subtitle.media.get_absolute_url()) return HttpResponseRedirect(subtitle.media.get_absolute_url())
return render(request, "cms/edit_subtitle.html", context) return render(request, "cms/edit_subtitle.html", context)

View File

@ -7,6 +7,23 @@
{% block innercontent %} {% block innercontent %}
{% include "cms/media_nav.html" with active_tab="subtitles" %} {% include "cms/media_nav.html" with active_tab="subtitles" %}
{% if whisper_form %}
<div class="user-action-form-wrap">
<div class="user-action-form-inner">
<h3 style="display: flex; align-items: center; gap: 0.25rem;">Request Automatic Transcription and Translation
<span title="This is Automatic Transcription using a Whisper model that is loaded locally" style="cursor: help;">
<i class="material-icons">info_outline</i>
</span>
</h3>
<form enctype="multipart/form-data" action="" method="post" class="post-form">
{% csrf_token %}
{% crispy whisper_form %}
</form>
</div>
</div>
{% endif %}
<div class="user-action-form-wrap"> <div class="user-action-form-wrap">
<div class="user-action-form-inner"> <div class="user-action-form-inner">
<form enctype="multipart/form-data" action="" method="post" class="post-form"> <form enctype="multipart/form-data" action="" method="post" class="post-form">
@ -20,7 +37,7 @@
{% if subtitles %} {% if subtitles %}
<div class="user-action-form-wrap"> <div class="user-action-form-wrap">
<div class="user-action-form-inner"> <div class="user-action-form-inner">
<h3>Existing Subtitles</h3> <h3>Edit existing Captions</h3>
<ul> <ul>
{% for subtitle in subtitles %} {% for subtitle in subtitles %}
<li><a href="{{subtitle.url}}">{{subtitle.language.title}}</a></li> <li><a href="{{subtitle.url}}">{{subtitle.language.title}}</a></li>
@ -30,20 +47,5 @@
</div> </div>
{% endif %} {% endif %}
{% if whisper_form %}
<div class="user-action-form-wrap">
<div class="user-action-form-inner">
<h3 style="display: flex; align-items: center; gap: 0.25rem;">Request Automatic Tranascription
<span title="This is Automatic Transcription using a Whisper model that is loaded locally" style="cursor: help;">
<i class="material-icons">info_outline</i>
</span>
</h3>
<form enctype="multipart/form-data" action="" method="post" class="post-form">
{% csrf_token %}
{% crispy whisper_form %}
</form>
</div>
</div>
{% endif %}
{% endblock innercontent %} {% endblock innercontent %}

View File

@ -59,6 +59,24 @@
</style> </style>
<div class="form-group{% if field.errors %} has-error{% endif %}"> <div class="form-group{% if field.errors %} has-error{% endif %}">
{% if field.field.widget.input_type == 'checkbox' and field.field.choices|length <= 1 %}
<div class="controls">
<label for="{{ field.id_for_label }}" class="control-label" style="font-weight: normal;">
{% crispy_field field %}
<span style="margin-left: 5px;">{{ field.label }}</span>
{% if field.help_text %}
<span class="help-text-inline">- {{ field.help_text|safe }}</span>
{% endif %}
</label>
{% if field.errors %}
<div class="error-container">
{% for error in field.errors %}
<p class="invalid-feedback">{{ error }}</p>
{% endfor %}
</div>
{% endif %}
</div>
{% else %}
<div class="control-label-container"> <div class="control-label-container">
{% if field.label %} {% if field.label %}
<label for="{{ field.id_for_label }}" class="control-label"> <label for="{{ field.id_for_label }}" class="control-label">
@ -79,4 +97,5 @@
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endif %}
</div> </div>

View File

@ -28,7 +28,7 @@
{% else %} {% else %}
<div class="user-action-form-wrap"> <div class="user-action-form-wrap">
<h1>Edit {{subtitle.language.title}} subtitle</h1> <h1>{{subtitle.language.title}}</h1>
<div class="user-action-form-inner"> <div class="user-action-form-inner">
Media: <a href="{{subtitle.media.get_absolute_url}}">{{subtitle.media.title}}</a> Media: <a href="{{subtitle.media.get_absolute_url}}">{{subtitle.media.title}}</a>
<form action="" method="post" class="post-form"> <form action="" method="post" class="post-form">

View File

@ -15,7 +15,7 @@
<li style="display: inline-block;"> <li style="display: inline-block;">
<a href="{% url 'add_subtitle' %}?m={{media_object.friendly_token}}" <a href="{% url 'add_subtitle' %}?m={{media_object.friendly_token}}"
style="text-decoration: none; {% if active_tab == 'subtitles' %}font-weight: bold; color: #333; padding-bottom: 3px; border-bottom: 2px solid #333;{% else %}color: #666;{% endif %}"> style="text-decoration: none; {% if active_tab == 'subtitles' %}font-weight: bold; color: #333; padding-bottom: 3px; border-bottom: 2px solid #333;{% else %}color: #666;{% endif %}">
{{ "Subtitles" | custom_translate:LANGUAGE_CODE}} {{ "Captions" | custom_translate:LANGUAGE_CODE}}
</a> </a>
</li> </li>
<li style="display: inline-block;"> <li style="display: inline-block;">

View File

@ -16,6 +16,7 @@
<div style="text-align: center; padding: 40px 0;"> <div style="text-align: center; padding: 40px 0;">
<p style="margin-bottom: 20px;">{{ "Click 'Start Recording' and select the screen or tab to record. Once recording is finished, click 'Stop Recording,' and the recording will be uploaded." | custom_translate:LANGUAGE_CODE}}</p> <p style="margin-bottom: 20px;">{{ "Click 'Start Recording' and select the screen or tab to record. Once recording is finished, click 'Stop Recording,' and the recording will be uploaded." | custom_translate:LANGUAGE_CODE}}</p>
<p style="margin-bottom: 20px;">{{ "This works in Chrome, Safari and Edge browsers." | custom_translate:LANGUAGE_CODE}}</p>
<button id="startBtn" class="qq-upload-button-selector" style="padding: 10px 20px; font-size: 16px; margin-right: 10px; cursor: pointer;">{{ "Start Recording" | custom_translate:LANGUAGE_CODE}}</button> <button id="startBtn" class="qq-upload-button-selector" style="padding: 10px 20px; font-size: 16px; margin-right: 10px; cursor: pointer;">{{ "Start Recording" | custom_translate:LANGUAGE_CODE}}</button>
<button id="stopBtn" class="qq-upload-button-selector" disabled style="padding: 10px 20px; font-size: 16px; cursor: pointer;">{{ "Stop Recording" | custom_translate:LANGUAGE_CODE}}</button> <button id="stopBtn" class="qq-upload-button-selector" disabled style="padding: 10px 20px; font-size: 16px; cursor: pointer;">{{ "Stop Recording" | custom_translate:LANGUAGE_CODE}}</button>
<div id="spinner" style="display: none; margin-top: 20px;"> <div id="spinner" style="display: none; margin-top: 20px;">
@ -81,7 +82,8 @@ document.addEventListener("DOMContentLoaded", function(event) {
const displayStream = await navigator.mediaDevices.getDisplayMedia({ video: true }); const displayStream = await navigator.mediaDevices.getDisplayMedia({ video: true });
const audioStream = await navigator.mediaDevices.getUserMedia({ audio: true }); const audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });
stream = new MediaStream([...displayStream.getTracks(), ...audioStream.getTracks()]); audioStream.getAudioTracks().forEach(track => displayStream.addTrack(track));
stream = displayStream;
} }
// When user stops sharing screen via browser UI // When user stops sharing screen via browser UI