Get karaoke ass generator from negromate.web, add date to songs
This commit is contained in:
		
							parent
							
								
									ed5990900b
								
							
						
					
					
						commit
						2f135fed6b
					
				| 
						 | 
					@ -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"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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):
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue