From 2f135fed6bc5b3975094cfe4e1ff2d3baf606039 Mon Sep 17 00:00:00 2001 From: shagi Date: Sun, 26 Sep 2021 20:53:48 +0200 Subject: [PATCH] Get karaoke ass generator from negromate.web, add date to songs --- negromate/songs/commands/songs.py | 29 ++++++++- negromate/songs/karaoke_templates.py | 92 ++++++++++++++++++++++++++++ negromate/songs/loader.py | 39 +++++++++--- negromate/songs/utils.py | 21 ++++++- 4 files changed, 168 insertions(+), 13 deletions(-) create mode 100644 negromate/songs/karaoke_templates.py diff --git a/negromate/songs/commands/songs.py b/negromate/songs/commands/songs.py index a4d3964..f852dab 100644 --- a/negromate/songs/commands/songs.py +++ b/negromate/songs/commands/songs.py @@ -1,10 +1,14 @@ -import sys from pathlib import Path from ..loader import load_songs name = 'songs' 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): @@ -13,10 +17,31 @@ def options(parser, config, **kwargs): default=config['global']['song_folder'], help="Folder with the song database, defaults to {}".format( 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): - 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( "#######\n" " Songs\n" diff --git a/negromate/songs/karaoke_templates.py b/negromate/songs/karaoke_templates.py new file mode 100644 index 0000000..a3a9e17 --- /dev/null +++ b/negromate/songs/karaoke_templates.py @@ -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) diff --git a/negromate/songs/loader.py b/negromate/songs/loader.py index b7c1ca8..6dbf19d 100644 --- a/negromate/songs/loader.py +++ b/negromate/songs/loader.py @@ -1,10 +1,9 @@ import json import asstosrt -import srt 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 @@ -14,6 +13,7 @@ class Song: self.metadata = None self.original = None self.author = None + self.date = None self.path = path self.root = root self.video = None @@ -38,6 +38,8 @@ class Song: self.original = self.metadata['original'] if 'author' in self.metadata: self.author = self.metadata['author'] + if 'date' in self.metadata: + self.date = self.metadata['date'] elif entry.name.endswith('mp4'): self.video = entry self.video_type = 'video/mp4' @@ -70,28 +72,43 @@ class Song: else: self.files.append(entry) - srt = self.path / "{}.srt".format(self.path.name) - if needs_change(srt, (self.ass,)): - self.srt = srt + def generate_missing(self, regenerate=False, karaoke_template_file=None): + srt_ = self.path / "{}.srt".format(self.path.name) + 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: srtfile.write(asstosrt.convert(assfile)) self.files.append(self.srt) 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 webvtt.from_srt(str(self.srt.absolute())).save(str(self.vtt.absolute())) 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 - generate_cover(self.video, self.cover) + generate_cover(self.video, self.cover) 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 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 def publish(self): has_subtitles = self.ass or self.srt or self.vtt @@ -99,7 +116,7 @@ class Song: 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 = [] pending_songs = [] for entry in root_folder.iterdir(): @@ -109,6 +126,8 @@ def load_songs(root_folder): logger.info("building {}".format(entry.name)) try: song = Song(entry, root_folder) + if generate: + song.generate_missing(regenerate, karaoke_template_file) except Exception as e: logger.error("Error: %s", e) continue diff --git a/negromate/songs/utils.py b/negromate/songs/utils.py index 2bcd76e..97a7c65 100644 --- a/negromate/songs/utils.py +++ b/negromate/songs/utils.py @@ -1,4 +1,8 @@ import subprocess +import ass +import time + +from . import karaoke_templates def needs_change(destination, dependencies): @@ -7,7 +11,7 @@ def needs_change(destination, dependencies): if dependency is None: return False last_dependency_change = max( - last_dependency_change, + last_dependency_change, dependency.lstat().st_mtime ) @@ -41,3 +45,18 @@ def generate_thumbnail(cover, thumbnail, geometry="200x200"): str(thumbnail.absolute()), ] 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)