2023-10-08 11:06:24 +00:00
|
|
|
import json
|
2023-10-08 11:07:57 +00:00
|
|
|
import os
|
2023-10-28 09:57:44 +00:00
|
|
|
import re
|
2023-10-16 22:26:09 +00:00
|
|
|
from collections import defaultdict
|
2023-10-08 11:06:24 +00:00
|
|
|
|
|
|
|
from django.core.management.base import BaseCommand
|
|
|
|
from django.conf import settings
|
|
|
|
|
|
|
|
import item.models
|
|
|
|
import itemlist.models
|
|
|
|
|
2023-11-16 08:12:53 +00:00
|
|
|
from ...render import get_srt
|
|
|
|
|
2023-10-08 11:06:24 +00:00
|
|
|
|
2023-10-28 13:03:35 +00:00
|
|
|
def resolve_roman(s):
|
|
|
|
extra = re.compile('^\d+(.*?)$').findall(s)
|
|
|
|
if extra:
|
|
|
|
extra = extra[0].lower()
|
|
|
|
new = {
|
|
|
|
'i': '1', 'ii': '2', 'iii': '3', 'iv': '4', 'v': '5',
|
|
|
|
'vi': '6', 'vii': 7, 'viii': '8', 'ix': '9', 'x': '10'
|
|
|
|
}.get(extra, extra)
|
|
|
|
return s.replace(extra, new)
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
2023-10-08 11:06:24 +00:00
|
|
|
class Command(BaseCommand):
|
|
|
|
help = 'generate symlinks to clips and clips.json'
|
|
|
|
|
|
|
|
def add_arguments(self, parser):
|
2024-03-22 09:56:50 +00:00
|
|
|
parser.add_argument('--lang', action='store', dest='lang', default=None, help='subtitle language')
|
2023-10-08 11:07:57 +00:00
|
|
|
parser.add_argument('--prefix', action='store', dest='prefix', default="/srv/t_for_time", help='prefix to build clips in')
|
2023-10-08 11:06:24 +00:00
|
|
|
|
|
|
|
def handle(self, **options):
|
|
|
|
prefix = options['prefix']
|
|
|
|
clips = []
|
2023-10-28 09:57:44 +00:00
|
|
|
for i in item.models.Item.objects.filter(sort__type='original'):
|
2023-10-08 11:06:24 +00:00
|
|
|
qs = item.models.Item.objects.filter(data__title=i.data['title']).exclude(id=i.id)
|
2023-10-28 09:57:44 +00:00
|
|
|
if qs.count() >= 1:
|
2023-10-08 11:06:24 +00:00
|
|
|
clip = {}
|
|
|
|
durations = []
|
|
|
|
for e in item.models.Item.objects.filter(data__title=i.data['title']):
|
2023-10-09 19:29:11 +00:00
|
|
|
if 'type' not in e.data:
|
2023-11-08 08:08:38 +00:00
|
|
|
print("ignoring invalid video %s (no type)" % e)
|
|
|
|
continue
|
2023-10-21 14:36:08 +00:00
|
|
|
if not e.files.filter(selected=True).exists():
|
|
|
|
continue
|
2023-10-21 14:32:55 +00:00
|
|
|
source = e.files.filter(selected=True)[0].data.path
|
2023-10-08 11:06:24 +00:00
|
|
|
ext = os.path.splitext(source)[1]
|
|
|
|
type_ = e.data['type'][0].lower()
|
|
|
|
target = os.path.join(prefix, type_, i.data['title'] + ext)
|
|
|
|
os.makedirs(os.path.dirname(target), exist_ok=True)
|
2023-10-28 13:12:45 +00:00
|
|
|
if os.path.islink(target):
|
2023-10-08 11:06:24 +00:00
|
|
|
os.unlink(target)
|
|
|
|
os.symlink(source, target)
|
|
|
|
clip[type_] = target
|
2023-10-21 14:32:55 +00:00
|
|
|
durations.append(e.files.filter(selected=True)[0].duration)
|
2023-10-08 11:06:24 +00:00
|
|
|
clip["duration"] = min(durations)
|
2023-10-29 18:57:39 +00:00
|
|
|
if not clip["duration"]:
|
|
|
|
print('!!', durations, clip)
|
|
|
|
continue
|
2023-10-08 11:06:24 +00:00
|
|
|
clip['tags'] = i.data.get('tags', [])
|
2023-10-19 14:52:35 +00:00
|
|
|
clip['editingtags'] = i.data.get('editingtags', [])
|
2023-10-28 09:57:44 +00:00
|
|
|
name = os.path.basename(clip['original'])
|
|
|
|
|
2023-10-28 13:03:35 +00:00
|
|
|
seqid = re.sub("Hotel Aporia_(\d+)", "S\\1_", name)
|
|
|
|
seqid = re.sub("Night March_(\d+)", "S\\1_", seqid)
|
|
|
|
seqid = re.sub("_(\d+)H_(\d+)", "_S\\1\\2_", seqid)
|
|
|
|
seqid = seqid.split('_')[:2]
|
2023-10-28 09:57:44 +00:00
|
|
|
seqid = [b[1:] if b[0] in ('B', 'S') else '0' for b in seqid]
|
2023-10-28 13:03:35 +00:00
|
|
|
seqid[1] = resolve_roman(seqid[1])
|
2023-10-28 09:57:44 +00:00
|
|
|
seqid[1] = ''.join([b for b in seqid[1] if b.isdigit()])
|
|
|
|
if not seqid[1]:
|
|
|
|
seqid[1] = '0'
|
|
|
|
try:
|
|
|
|
clip['seqid'] = int(''.join(['%06d' % int(b) for b in seqid]))
|
|
|
|
except:
|
|
|
|
print(name, seqid, 'failed')
|
|
|
|
raise
|
2023-10-09 19:29:11 +00:00
|
|
|
if "original" in clip and "foreground" in clip and "background" in clip:
|
|
|
|
clips.append(clip)
|
2023-10-28 09:25:45 +00:00
|
|
|
elif "original" in clip and "animation" in clip:
|
|
|
|
clips.append(clip)
|
2023-10-09 19:29:11 +00:00
|
|
|
else:
|
|
|
|
print("ignoring incomplete video", i)
|
2023-10-08 11:06:24 +00:00
|
|
|
|
|
|
|
with open(os.path.join(prefix, 'clips.json'), 'w') as fd:
|
|
|
|
json.dump(clips, fd, indent=2, ensure_ascii=False)
|
2023-10-16 22:26:09 +00:00
|
|
|
|
2023-10-28 09:57:44 +00:00
|
|
|
print("using", len(clips), "clips")
|
|
|
|
|
2023-10-16 22:26:09 +00:00
|
|
|
voice_over = defaultdict(dict)
|
|
|
|
for vo in item.models.Item.objects.filter(
|
|
|
|
data__type__contains="Voice Over",
|
|
|
|
):
|
|
|
|
fragment_id = int(vo.get('title').split('_')[0])
|
2023-10-21 14:32:55 +00:00
|
|
|
source = vo.files.filter(selected=True)[0]
|
2023-10-16 22:26:09 +00:00
|
|
|
batch = vo.get('batch')[0].replace('Text-', '')
|
|
|
|
src = source.data.path
|
2023-10-28 09:24:51 +00:00
|
|
|
target = os.path.join(prefix, 'voice_over', batch, '%s.wav' % fragment_id)
|
2023-10-16 22:26:09 +00:00
|
|
|
os.makedirs(os.path.dirname(target), exist_ok=True)
|
2023-10-21 14:46:03 +00:00
|
|
|
if os.path.islink(target):
|
2023-10-16 22:26:09 +00:00
|
|
|
os.unlink(target)
|
|
|
|
os.symlink(src, target)
|
2023-11-14 16:48:55 +00:00
|
|
|
subs = []
|
2024-03-22 09:56:50 +00:00
|
|
|
for sub in vo.annotations.filter(layer="subtitles", languages=options["lang"]).exclude(value="").order_by("start"):
|
2023-11-16 08:12:53 +00:00
|
|
|
sdata = get_srt(sub)
|
2023-11-14 18:52:12 +00:00
|
|
|
subs.append(sdata)
|
2023-10-16 22:26:09 +00:00
|
|
|
voice_over[fragment_id][batch] = {
|
|
|
|
"src": target,
|
2023-11-14 16:48:55 +00:00
|
|
|
"duration": source.duration,
|
|
|
|
"subs": subs
|
2023-10-16 22:26:09 +00:00
|
|
|
}
|
|
|
|
with open(os.path.join(prefix, 'voice_over.json'), 'w') as fd:
|
|
|
|
json.dump(voice_over, fd, indent=2, ensure_ascii=False)
|