Get karaoke ass generator from negromate.web, add date to songs

This commit is contained in:
Ales (Shagi) Zabala Alava 2021-09-26 20:53:48 +02:00
parent ed5990900b
commit 2f135fed6b
4 changed files with 168 additions and 13 deletions

View File

@ -1,10 +1,14 @@
import sys
from pathlib import Path from pathlib import Path
from ..loader import load_songs from ..loader import load_songs
name = 'songs' name = 'songs'
help_text = 'Update song database' help_text = 'Update song database'
initial_config = {
'generate': 'yes',
'regenerate': 'no',
'karaoke_template_file': '~/negro_mate/karaoke_templates/karaoke.ass',
}
def options(parser, config, **kwargs): def options(parser, config, **kwargs):
@ -13,10 +17,31 @@ def options(parser, config, **kwargs):
default=config['global']['song_folder'], default=config['global']['song_folder'],
help="Folder with the song database, defaults to {}".format( help="Folder with the song database, defaults to {}".format(
config['global']['song_folder'])) config['global']['song_folder']))
parser.add_argument(
'-g', '--generate', action='store_const', const='yes',
default=config['songs']['generate'],
help="Generate missing files, defaults to {}".format(
config['songs']['generate']))
parser.add_argument(
'-r', '--regenerate', action='store_const', const='yes',
default=config['songs']['regenerate'],
help="Regenerate missing files, defaults to {}".format(
config['songs']['regenerate']))
parser.add_argument(
'-k', '--karaoke-template', type=Path,
default=config['songs']['karaoke_template_file'],
help="Ass file with the karaoke template, defaults to {}".format(
config['songs']['karaoke_template_file']))
def run(args, **kwargs): def run(args, **kwargs):
songs, pending_songs = load_songs(args.song_folder.expanduser()) generate = args.generate == 'yes'
regenerate = args.regenerate == 'yes'
songs, pending_songs = load_songs(
root_folder=args.song_folder.expanduser(),
generate=generate, regenerate=regenerate,
karaoke_template_file=args.karaoke_template.expanduser())
print( print(
"#######\n" "#######\n"
" Songs\n" " Songs\n"

View File

@ -0,0 +1,92 @@
import os
import subprocess
import time
from contextlib import contextmanager
import ass
@contextmanager
def Xephyr_env(display=":2", *args, **kwargs):
env = os.environ.copy()
xephyr = subprocess.Popen(["Xephyr", display], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
env['DISPLAY'] = display
try:
yield env
finally:
xephyr.kill()
def set_template(template_subtitles, orig_file, target_file=None):
if target_file is None:
target_file = orig_file
with open(orig_file, 'r') as orig:
subtitles = ass.parse(orig)
new_events = []
for dialogue in template_subtitles.events:
new_events.append(dialogue)
for dialogue in subtitles.events:
if dialogue.effect.startswith('code'):
continue
if dialogue.effect.startswith('template'):
continue
new_events.append(dialogue)
subtitles.events = new_events
with open(target_file, 'w', encoding='utf-8-sig') as target:
subtitles.dump_file(target)
def run(command, env, wait=None):
subprocess.Popen(
command,
env=env,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
if wait is not None:
time.sleep(wait)
def apply_template(subtitles, env):
run(["aegisub-3.2", subtitles], env=env, wait=2)
# Si pide confirmación para cargar video ignorar el popup
run(["xdotool", "key", "Escape"], env=env, wait=0.1)
# abrir el menú de automatización, bajar dos y darle a aplicar template
run(["xdotool", "key", "alt+u"], env=env, wait=0.1)
run(["xdotool", "key", "Down"], env=env, wait=0.1)
run(["xdotool", "key", "Down"], env=env, wait=0.1)
run(["xdotool", "key", "Return"], env=env, wait=2)
# guardar
run(["xdotool", "key", "ctrl+s"], env=env)
# cerrar programa
run(["xdotool", "key", "ctrl+q"], env=env)
def update_karaoke_songs(songs, template_file, regenerate=False):
from negromate.songs.utils import needs_change
with open(template_file, 'r') as template:
template_subtitles = ass.parse(template)
with Xephyr_env() as env:
for song in songs:
if song.metadata.get('karaoke'):
target = song.path / "{}.karaoke.ass".format(song.path.name)
if regenerate or needs_change(target, (song.ass, template_file)):
set_template(
template_subtitles=template_subtitles,
orig_file=str(song.ass),
target_file=str(target)
)
time.sleep(2)
apply_template(str(target), env)
time.sleep(2)

View File

@ -1,10 +1,9 @@
import json import json
import asstosrt import asstosrt
import srt
import webvtt import webvtt
from .utils import needs_change, generate_cover, generate_thumbnail from .utils import needs_change, generate_cover, generate_thumbnail, generate_karaoke_ass
from . import logger from . import logger
@ -14,6 +13,7 @@ class Song:
self.metadata = None self.metadata = None
self.original = None self.original = None
self.author = None self.author = None
self.date = None
self.path = path self.path = path
self.root = root self.root = root
self.video = None self.video = None
@ -38,6 +38,8 @@ class Song:
self.original = self.metadata['original'] self.original = self.metadata['original']
if 'author' in self.metadata: if 'author' in self.metadata:
self.author = self.metadata['author'] self.author = self.metadata['author']
if 'date' in self.metadata:
self.date = self.metadata['date']
elif entry.name.endswith('mp4'): elif entry.name.endswith('mp4'):
self.video = entry self.video = entry
self.video_type = 'video/mp4' self.video_type = 'video/mp4'
@ -70,28 +72,43 @@ class Song:
else: else:
self.files.append(entry) self.files.append(entry)
srt = self.path / "{}.srt".format(self.path.name) def generate_missing(self, regenerate=False, karaoke_template_file=None):
if needs_change(srt, (self.ass,)): srt_ = self.path / "{}.srt".format(self.path.name)
self.srt = srt if regenerate or needs_change(srt_, (self.ass,)):
logger.info("generating {}".format(srt_))
self.srt = srt_
with self.ass.open('r') as assfile, self.srt.open('w') as srtfile: with self.ass.open('r') as assfile, self.srt.open('w') as srtfile:
srtfile.write(asstosrt.convert(assfile)) srtfile.write(asstosrt.convert(assfile))
self.files.append(self.srt) self.files.append(self.srt)
vtt = self.path / "{}.vtt".format(self.path.name) vtt = self.path / "{}.vtt".format(self.path.name)
if needs_change(vtt, (self.srt,)): if regenerate or needs_change(vtt, (self.srt,)):
logger.info("generating {}".format(vtt))
self.vtt = vtt self.vtt = vtt
webvtt.from_srt(str(self.srt.absolute())).save(str(self.vtt.absolute())) webvtt.from_srt(str(self.srt.absolute())).save(str(self.vtt.absolute()))
cover = self.path / "cover.jpg" cover = self.path / "cover.jpg"
if needs_change(cover, (self.video,)): if regenerate or needs_change(cover, (self.video,)):
logger.info("generating {}".format(cover))
self.cover = cover self.cover = cover
generate_cover(self.video, self.cover) generate_cover(self.video, self.cover)
thumbnail = self.path / "thumb.jpg" thumbnail = self.path / "thumb.jpg"
if needs_change(thumbnail, (self.cover,)): if regenerate or needs_change(thumbnail, (self.cover,)):
logger.info("generating {}".format(thumbnail))
self.thumbnail = thumbnail self.thumbnail = thumbnail
generate_thumbnail(self.cover, self.thumbnail) generate_thumbnail(self.cover, self.thumbnail)
karaoke_ass = self.path / "{}.karaoke.ass".format(self.path.name)
karaoke_requirements = (
self.metadata.get('karaoke', False),
regenerate or needs_change(karaoke_ass, (self.ass, karaoke_template_file)),
)
if all(karaoke_requirements):
logger.info("generating {}".format(karaoke_ass))
self.karaoke_ass = karaoke_ass
generate_karaoke_ass(str(karaoke_template_file), str(self.ass), str(karaoke_ass))
@property @property
def publish(self): def publish(self):
has_subtitles = self.ass or self.srt or self.vtt has_subtitles = self.ass or self.srt or self.vtt
@ -99,7 +116,7 @@ class Song:
return has_video and has_subtitles return has_video and has_subtitles
def load_songs(root_folder): def load_songs(root_folder, generate=True, regenerate=False, karaoke_template_file=None):
songs = [] songs = []
pending_songs = [] pending_songs = []
for entry in root_folder.iterdir(): for entry in root_folder.iterdir():
@ -109,6 +126,8 @@ def load_songs(root_folder):
logger.info("building {}".format(entry.name)) logger.info("building {}".format(entry.name))
try: try:
song = Song(entry, root_folder) song = Song(entry, root_folder)
if generate:
song.generate_missing(regenerate, karaoke_template_file)
except Exception as e: except Exception as e:
logger.error("Error: %s", e) logger.error("Error: %s", e)
continue continue

View File

@ -1,4 +1,8 @@
import subprocess import subprocess
import ass
import time
from . import karaoke_templates
def needs_change(destination, dependencies): def needs_change(destination, dependencies):
@ -7,7 +11,7 @@ def needs_change(destination, dependencies):
if dependency is None: if dependency is None:
return False return False
last_dependency_change = max( last_dependency_change = max(
last_dependency_change, last_dependency_change,
dependency.lstat().st_mtime dependency.lstat().st_mtime
) )
@ -41,3 +45,18 @@ def generate_thumbnail(cover, thumbnail, geometry="200x200"):
str(thumbnail.absolute()), str(thumbnail.absolute()),
] ]
subprocess.check_call(command) subprocess.check_call(command)
def generate_karaoke_ass(template_file, orig_file, target_file):
with open(template_file, 'r') as template:
template_subtitles = ass.parse(template)
with karaoke_templates.Xephyr_env() as env:
karaoke_templates.set_template(
template_subtitles=template_subtitles,
orig_file=orig_file,
target_file=target_file,
)
time.sleep(2)
karaoke_templates.apply_template(target_file, env)
time.sleep(2)