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):
|
||||||
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue