pandora_transcribe
This commit is contained in:
commit
1b5de1882a
6 changed files with 175 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.pyc
|
31
README.md
Normal file
31
README.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# pandora_transcribe
|
||||||
|
|
||||||
|
use whisper_timestamped to add automatic transcriptions to pan.do/ra
|
||||||
|
|
||||||
|
## installation
|
||||||
|
|
||||||
|
cd /srv/pandora/pandora
|
||||||
|
git clone https://code.0x2620.org/0x2620/pandora_transcribe transcribe
|
||||||
|
|
||||||
|
add "transcribe" to LOCAL_APPS in local_setttings.py
|
||||||
|
|
||||||
|
|
||||||
|
## configuration
|
||||||
|
|
||||||
|
add a user called subtitles and create 2 lists for that user: Queue and Transcribed
|
||||||
|
alternatively see options for `./manage.py transcribe` to use another user or list name
|
||||||
|
|
||||||
|
## run
|
||||||
|
|
||||||
|
in a terminal run
|
||||||
|
./manage.py transcribe
|
||||||
|
|
||||||
|
|
||||||
|
## install a service
|
||||||
|
|
||||||
|
copy systemd/service/pandora-transcribe.service to /etc/systemd/system/pandora-transcribe.service and
|
||||||
|
|
||||||
|
systemctl enable --now andora-transcribe.service
|
||||||
|
|
||||||
|
|
||||||
|
|
0
__init__.py
Normal file
0
__init__.py
Normal file
6
apps.py
Normal file
6
apps.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class WhisperConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = 'transcribe'
|
28
management/commands/transcribe.py
Normal file
28
management/commands/transcribe.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
|
import app.monkey_patch
|
||||||
|
from ... import transcribe
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'transcribe items with whisper_timestamped'
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument('--user', type=str, dest='user',
|
||||||
|
default='subtitles', help='user for subtitles (default: subtitles)')
|
||||||
|
parser.add_argument('--queue', type=str, dest='queue',
|
||||||
|
default='Queue', help='name of incoming list (default: Queue)')
|
||||||
|
parser.add_argument('--done', type=str, dest='done',
|
||||||
|
default='Transcribed', help='name of incoming list (default: Transcribed)')
|
||||||
|
parser.add_argument('--layer', type=str, dest='layer',
|
||||||
|
default=None, help='import into layer (default subtitle layer)')
|
||||||
|
parser.add_argument('--translate', type=str, dest='translate',
|
||||||
|
default='', help='list of languages to translate: (i.e. hi:en,de:en')
|
||||||
|
parser.add_argument('--gpu', action='store_true', dest='gpu',
|
||||||
|
default=False, help='user GPU (default: disabled)')
|
||||||
|
|
||||||
|
def handle(self, **kwargs):
|
||||||
|
transcribe.main(**kwargs)
|
109
transcribe.py
Normal file
109
transcribe.py
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import ox
|
||||||
|
import ox.iso
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from annotation import tasks
|
||||||
|
from item.models import Item
|
||||||
|
from itemlist.models import List
|
||||||
|
from item import utils
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def extract_subtitles(item, user, layer, translate, gpu=False):
|
||||||
|
if "language" not in item.data:
|
||||||
|
logger.error("skip item without language %s", item.public_id)
|
||||||
|
return False
|
||||||
|
language = ox.iso.langTo2Code(item.data["language"][0])
|
||||||
|
if not language:
|
||||||
|
logger.error("skip item with unknown language %s: %s", item.public_id, item.data["language"])
|
||||||
|
return False
|
||||||
|
if not item.streams():
|
||||||
|
logger.error("skip item without media %s: %s", item.public_id)
|
||||||
|
return False
|
||||||
|
src = item.streams()[0].media.path
|
||||||
|
|
||||||
|
tmp = tempfile.mkdtemp()
|
||||||
|
cmd = [
|
||||||
|
"/opt/whisper-timestamped/bin/whisper_timestamped",
|
||||||
|
"--language", language,
|
||||||
|
]
|
||||||
|
if translate and language in translate:
|
||||||
|
cmd += [
|
||||||
|
'--task', 'translate'
|
||||||
|
]
|
||||||
|
if not gpu:
|
||||||
|
cmd += [
|
||||||
|
"--fp16", "False",
|
||||||
|
]
|
||||||
|
|
||||||
|
cmd += [
|
||||||
|
"-f", "srt",
|
||||||
|
"--accurate",
|
||||||
|
"--output_dir", tmp,
|
||||||
|
src,
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
subprocess.check_output(cmd)
|
||||||
|
except:
|
||||||
|
logger.error("failed to extract subtitles from item %s\n%s", item.public_id, cmd)
|
||||||
|
shutil.rmtree(tmp)
|
||||||
|
return False
|
||||||
|
annotations = []
|
||||||
|
for f in os.listdir(tmp):
|
||||||
|
if f.endswith(".srt") and "words.srt" not in f:
|
||||||
|
srt = os.path.join(tmp, f)
|
||||||
|
annotations = ox.srt.load(srt)
|
||||||
|
if not annotations:
|
||||||
|
logger.error("no subtitles detected %s", item.public_id)
|
||||||
|
return True
|
||||||
|
if language != "en":
|
||||||
|
for annotation in annotations:
|
||||||
|
annotation["value"] = '<span lang="%s">%s</span>' % (language, annotation["value"])
|
||||||
|
|
||||||
|
tasks.add_annotations.delay({
|
||||||
|
'item': item.public_id,
|
||||||
|
'layer': layer,
|
||||||
|
'user': user.username,
|
||||||
|
'annotations': annotations
|
||||||
|
})
|
||||||
|
shutil.rmtree(tmp)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def main(**kwargs):
|
||||||
|
queue = List.objects.get(kwargs['queue'])
|
||||||
|
done = List.objects.get(kwargs['done'])
|
||||||
|
user = User.objects.get(kwargs['user'])
|
||||||
|
layer = kwargs.get("layer")
|
||||||
|
translate = kwargs.get("translate")
|
||||||
|
if translate:
|
||||||
|
translate = dict([tt.split(':') for tt in translate.split(',')])
|
||||||
|
if not layer:
|
||||||
|
layer = utils.get_by_key(settings.CONFIG['layers'], 'isSubtitles', True)
|
||||||
|
if layer:
|
||||||
|
layer = layer["id"]
|
||||||
|
else:
|
||||||
|
logger.error("no layer defined and config has no subtitle layer")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
wait = True
|
||||||
|
for item in queue.get_items(queue.user):
|
||||||
|
if extract_subtitles(item, user, layer, translate, kwargs.get("gpu")):
|
||||||
|
queue.items.remove(item)
|
||||||
|
done.items.remove(item)
|
||||||
|
wait = False
|
||||||
|
if wait:
|
||||||
|
time.sleep(5*60)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
|
Loading…
Reference in a new issue