diff --git a/gui/images/logo.png b/gui/images/logo.png new file mode 100644 index 0000000..1757ed1 Binary files /dev/null and b/gui/images/logo.png differ diff --git a/gui/karaoke.py b/gui/karaoke.py new file mode 100644 index 0000000..4b44fbf --- /dev/null +++ b/gui/karaoke.py @@ -0,0 +1,178 @@ +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() diff --git a/gui/kv_templates/karaoke.kv b/gui/kv_templates/karaoke.kv new file mode 100644 index 0000000..b7d80c8 --- /dev/null +++ b/gui/kv_templates/karaoke.kv @@ -0,0 +1,69 @@ +KaraokeGUI: + +: + size_hint: None, None + size: 200, 200 + orientation: 'vertical' + padding: 0 + spacing: 0 + + canvas.before: + Color: + rgb: 0.9, 0.9, 0.9 if self.active else 0.3, 0.3, 0.3 + Line: + rectangle: self.pos[0], self.pos[1], self.size[0], self.size[1] + + Image: + source: self.parent.thumbnail + size_hint: 1, 0.8 + Label: + canvas.before: + Color: + rgb: 0.3, 0.3, 0.3 + Rectangle: + pos: self.pos + size: self.size + size_hint: 1, 0.1 + text: self.parent.name + +: + orientation: 'vertical' + Image: + id: current_song_image + source: '' + Button: + text: '>' + on_press: root.play() + size_hint: 1, 0.1 + + BoxLayout: + orientation: 'horizontal' + height: 220 + size_hint: 1, None + + Button: + size_hint: None, None + size: 20, 220 + text: '<' + on_press: root.previous() + + ScrollView: + id: songs_scroll + do_scroll_x: True + do_scroll_y: False + size_hint: 1, None + height: 220 + + GridLayout: + id: song_container + rows: 1 + width: self.minimum_width + padding: 10 + spacing: 10 + size_hint: None, 1 + + Button: + size_hint: None, None + size: 20, 220 + text: '>' + on_press: root.next()