179 lines
5.0 KiB
Python
179 lines
5.0 KiB
Python
|
import json
|
||
|
import subprocess
|
||
|
import sys
|
||
|
from pathlib import Path
|
||
|
|
||
|
import kivy
|
||
|
kivy.require('1.11.1')
|
||
|
|
||
|
from kivy.app import App
|
||
|
from kivy.core.window import Window
|
||
|
from kivy.properties import StringProperty, ObjectProperty, ListProperty, BooleanProperty
|
||
|
from kivy.uix.boxlayout import BoxLayout
|
||
|
from kivy.uix.button import Button
|
||
|
from kivy.uix.widget import Widget
|
||
|
|
||
|
|
||
|
class Song(BoxLayout):
|
||
|
active = BooleanProperty(False)
|
||
|
path = ObjectProperty('')
|
||
|
|
||
|
def on_path(self, instance, value):
|
||
|
self.search_media()
|
||
|
|
||
|
def search_media(self):
|
||
|
self.name = self.path.name
|
||
|
self.original = None
|
||
|
self.author = None
|
||
|
self.video = None
|
||
|
self.video_type = None
|
||
|
self.vtt = None
|
||
|
self.srt = None
|
||
|
self.ass = None
|
||
|
self.cover = None
|
||
|
self.thumbnail = None
|
||
|
self.files = []
|
||
|
for entry in self.path.iterdir():
|
||
|
if entry.name == 'metadata.json':
|
||
|
with entry.open('r') as metadatafile:
|
||
|
self.metadata = json.load(metadatafile)
|
||
|
if 'name' in self.metadata:
|
||
|
self.name = self.metadata['name']
|
||
|
if 'original' in self.metadata:
|
||
|
self.original = self.metadata['original']
|
||
|
if 'author' in self.metadata:
|
||
|
self.author = self.metadata['author']
|
||
|
elif entry.name.endswith('mp4'):
|
||
|
self.video = str(entry)
|
||
|
self.video_type = 'video/mp4'
|
||
|
elif entry.name.endswith('webm'):
|
||
|
self.video = str(entry)
|
||
|
self.video_type = 'video/webm'
|
||
|
elif entry.name.endswith('ogv'):
|
||
|
self.video = str(entry)
|
||
|
self.video_type = 'video/ogg'
|
||
|
elif entry.name.endswith('vtt'):
|
||
|
self.vtt = str(entry)
|
||
|
elif entry.name == "{}.srt".format(self.path.name):
|
||
|
self.srt = str(entry)
|
||
|
elif entry.name.endswith('ass'):
|
||
|
self.ass = str(entry)
|
||
|
elif entry.name == 'thumb.jpg':
|
||
|
self.thumbnail = str(entry)
|
||
|
elif entry.name.endswith('jpg'):
|
||
|
self.cover = str(entry)
|
||
|
elif entry.name == 'index.html':
|
||
|
continue
|
||
|
|
||
|
@property
|
||
|
def publish(self):
|
||
|
return self.video and self.subtitle
|
||
|
|
||
|
@property
|
||
|
def subtitle(self):
|
||
|
return self.ass or self.srt or self.vtt
|
||
|
|
||
|
|
||
|
class KaraokeGUI(BoxLayout):
|
||
|
active_song = ObjectProperty(None)
|
||
|
songs = ListProperty([])
|
||
|
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
super().__init__(*args, **kwargs)
|
||
|
self.keyboard = Window.request_keyboard(
|
||
|
self.keyboard_closed,
|
||
|
self,
|
||
|
'text',
|
||
|
)
|
||
|
self.keyboard.bind(on_key_down=self.on_key_down)
|
||
|
|
||
|
def on_key_down(self, keyboard, keycode, text, modifiers):
|
||
|
if text == 'a':
|
||
|
self.previous()
|
||
|
return True
|
||
|
elif text == 'd':
|
||
|
self.next()
|
||
|
return True
|
||
|
elif text == 's':
|
||
|
self.play()
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def keyboard_closed(self):
|
||
|
pass
|
||
|
|
||
|
def on_songs(self, instance, value):
|
||
|
container = self.ids['song_container']
|
||
|
container.clear_widgets()
|
||
|
for song in self.songs:
|
||
|
container.add_widget(song)
|
||
|
self.active_song = self.songs[0]
|
||
|
|
||
|
def on_active_song(self, instance, value):
|
||
|
for song in self.songs:
|
||
|
song.active = False
|
||
|
value.active = True
|
||
|
current_song_image = self.ids['current_song_image']
|
||
|
current_song_image.source = value.cover
|
||
|
|
||
|
scrollview = self.ids['songs_scroll']
|
||
|
scrollview.scroll_to(value)
|
||
|
|
||
|
def previous(self):
|
||
|
idx = self.songs.index(self.active_song)
|
||
|
|
||
|
if idx > 0:
|
||
|
idx -= 1
|
||
|
else:
|
||
|
idx = len(self.songs) - 1
|
||
|
|
||
|
self.active_song = self.songs[idx]
|
||
|
|
||
|
def next(self):
|
||
|
idx = self.songs.index(self.active_song)
|
||
|
|
||
|
if idx < len(self.songs) - 1:
|
||
|
idx += 1
|
||
|
else:
|
||
|
idx = 0
|
||
|
|
||
|
self.active_song = self.songs[idx]
|
||
|
|
||
|
def play(self):
|
||
|
subprocess.call([
|
||
|
'vlc',
|
||
|
'--fullscreen',
|
||
|
'--sub-file',
|
||
|
self.active_song.subtitle,
|
||
|
self.active_song.video,
|
||
|
'vlc://quit'
|
||
|
])
|
||
|
|
||
|
|
||
|
class KaraokeApp(App):
|
||
|
kv_directory = 'kv_templates'
|
||
|
|
||
|
def __init__(self, root_folder, **kwargs):
|
||
|
super().__init__(**kwargs)
|
||
|
self.root_folder = root_folder
|
||
|
|
||
|
def build(self):
|
||
|
super().build()
|
||
|
|
||
|
songs = []
|
||
|
container = self.root.ids['song_container']
|
||
|
for entry in self.root_folder.iterdir():
|
||
|
if entry.is_dir():
|
||
|
song = Song(
|
||
|
path=entry,
|
||
|
)
|
||
|
if song.publish:
|
||
|
songs.append(song)
|
||
|
self.root.songs = songs
|
||
|
return self.root
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
Window.fullscreen = True
|
||
|
KaraokeApp(Path(sys.argv[1])).run()
|