From 28184b31cef18c4049bd53535c4862230da9c8fd Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Tue, 22 Dec 2020 20:42:04 -0800 Subject: [PATCH 01/17] #69 basic uncompressed cdg / mp3 support --- README.md | 9 +++++++++ karaoke.py | 21 +++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f2e50b1d..1c5506ca 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ If you want to support this project with a little monetary tip, it's much apprec - Searching song library via autocomplete - Adding new tracks from Youtube - Offline storage of video files +- mp3 + cdg file support (vlc only, must be uncompressed and added to download directory manually) - Pause/Skip/Restart and volume control - Now playing and Up Next display - Basic editing of downloaded file names @@ -302,6 +303,14 @@ First of all, you must be running pikaraoke with the --use-vlc option. While a song is playing, the home screen of the web interface will show a transpose slider. Slide it up or down based on your preference and press the "ok" button to restart the song in the given key. +### How do I add cdg files? + +You'll need to add them manually by copying them to the root of your download folder. Run `python app.py --help` and look under DOWNLOAD_PATH to find out what the default folder is, or specify your own. CDG files must have an mp3 file with a matching file name. They must also be uncompressed and not stashed away in sub-directories. + +### I'm only hearing audio and not seeing video + +If this is a mp3 + cdg file, the filename of the cdg file might not match the mp3 file exactly or be missing. You'll have to fix this manually by supplying a matching cdg file. Also, cdg files are only supported in vlc. + ### I'm on a laptop, how do I output just pikaraoke to an external monitor/screen? You might be able to just drag the windows to the target screen (press 'f' to toggle fullscreen). But in my experience there can be issues figuring out which monitor to use once videos start playing. For now you'd probably have the most consistent experience using single-screen mirrored mode. diff --git a/karaoke.py b/karaoke.py index 49f64be8..9fe8c82b 100644 --- a/karaoke.py +++ b/karaoke.py @@ -440,20 +440,33 @@ def download_video(self, video_url, enqueue=False): def get_available_songs(self): logging.debug("Fetching available songs in: " + self.download_path) - self.available_songs = sorted(glob.glob(u"%s/*" % self.download_path)) + types = ('*.mp4', '*.mp3') + files_grabbed = [] + for files in types: + files_grabbed.extend(glob.glob(u"%s/%s" % (self.download_path, files))) + self.available_songs = sorted(files_grabbed) def delete(self, song_path): logging.info("Deleting song: " + song_path) os.remove(song_path) + ext = os.path.splitext(song_path) + # if we have an associated cdg file, delete that too + cdg_file = song_path.replace(ext[1],".cdg") + if (os.path.exists(cdg_file)): + os.remove(cdg_file) + self.get_available_songs() def rename(self, song_path, new_name): logging.info("Renaming song: '" + song_path + "' to: " + new_name) ext = os.path.splitext(song_path) if len(ext) == 2: - new_name = new_name + ext[1] - os.rename(song_path, self.download_path + new_name) - + new_file_name = new_name + ext[1] + os.rename(song_path, self.download_path + new_file_name) + # if we have an associated cdg file, rename that too + cdg_file = song_path.replace(ext[1],".cdg") + if (os.path.exists(cdg_file)): + os.rename(cdg_file, self.download_path + new_name + ".cdg") self.get_available_songs() def filename_from_path(self, file_path): From f55cc45412f204dfc3da44291a357512e67bf80e Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Sat, 26 Dec 2020 17:51:39 -0800 Subject: [PATCH 02/17] #61 change default download dir to ~/pikaraoke/songs --- app.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/app.py b/app.py index f85bda93..a52c21e0 100644 --- a/app.py +++ b/app.py @@ -9,16 +9,8 @@ import cherrypy import psutil -from flask import ( - Flask, - flash, - redirect, - render_template, - request, - send_file, - send_from_directory, - url_for, -) +from flask import (Flask, flash, redirect, render_template, request, send_file, + send_from_directory, url_for) import karaoke from get_platform import get_platform @@ -448,7 +440,13 @@ def get_default_vlc_path(platform): def get_default_dl_dir(platform): if platform == "raspberry_pi": - return "/usr/lib/pikaraoke/songs" + legacy_directory = "/usr/lib/pikaraoke/songs" + if os.path.isfile(legacy_directory): + # preserve old dl location for previous users + return legacy_directory + else: + # homedir is preferred because it doesn't require root #61 + return "~/pikaraoke/songs" elif platform == "windows": return "~\pikaraoke\songs" else: From 0fcf2d989aaf919ad368450ac0921fbf72f5361c Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Sat, 26 Dec 2020 21:10:28 -0800 Subject: [PATCH 03/17] #69 support zipped cdg's #61 - change default dl dir to ~/pikaraoke-songs to prevent accidental deletion when deleting app dir --- app.py | 16 ++++++++--- karaoke.py | 2 +- vlcclient.py | 81 ++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 79 insertions(+), 20 deletions(-) diff --git a/app.py b/app.py index a52c21e0..be12a171 100644 --- a/app.py +++ b/app.py @@ -441,16 +441,24 @@ def get_default_vlc_path(platform): def get_default_dl_dir(platform): if platform == "raspberry_pi": legacy_directory = "/usr/lib/pikaraoke/songs" - if os.path.isfile(legacy_directory): + if os.path.exists(legacy_directory): # preserve old dl location for previous users return legacy_directory else: # homedir is preferred because it doesn't require root #61 - return "~/pikaraoke/songs" + return "~/pikaraoke-songs" elif platform == "windows": - return "~\pikaraoke\songs" + legacy_directory = "~\pikaraoke\songs" + if os.path.exists(legacy_directory): + return legacy_directory + else: + return "~\pikaraoke-songs" else: - return os.path.expanduser("~/pikaraoke/songs") + legacy_directory = "~/pikaraoke/songs" + if os.path.exists(legacy_directory): + return legacy_directory + else: + return "~/pikaraoke-songs" if __name__ == "__main__": diff --git a/karaoke.py b/karaoke.py index 9fe8c82b..85ae76e9 100644 --- a/karaoke.py +++ b/karaoke.py @@ -440,7 +440,7 @@ def download_video(self, video_url, enqueue=False): def get_available_songs(self): logging.debug("Fetching available songs in: " + self.download_path) - types = ('*.mp4', '*.mp3') + types = ('*.mp4', '*.mp3', '*.zip') files_grabbed = [] for files in types: files_grabbed.extend(glob.glob(u"%s/%s" % (self.download_path, files))) diff --git a/vlcclient.py b/vlcclient.py index 9d1459c0..0c3ff4cf 100644 --- a/vlcclient.py +++ b/vlcclient.py @@ -1,11 +1,13 @@ import logging import os import random +import shutil import string import subprocess import sys import time import xml.etree.ElementTree as ET +import zipfile from threading import Timer import requests @@ -41,6 +43,12 @@ def __init__(self, port=5002, path=None): else: self.path = path + # Determine tmp directories (for things like extracted cdg files) + if self.platform == "windows": + self.tmp_dir = r"~\\AppData\\Local\\Temp\\pikaraoke\\" + else: + self.tmp_dir = "/tmp/pikaraoke/" + # Set up command line args self.cmd_base = [ self.path, @@ -77,22 +85,65 @@ def __init__(self, port=5002, path=None): self.volume_offset = 10 self.process = None - def play_file(self, file_path, additional_parameters=None): - if self.is_playing() or self.is_paused(): - logging.debug("VLC is currently playing, stopping track...") - self.stop() - # this pause prevents vlc http server from being borked after transpose - time.sleep(0.2) - if self.platform == "windows": - file_path = r"{}".format(file_path) - if additional_parameters == None: - command = self.cmd_base + [file_path] + def handle_zipped_cdg(self, file_path): + extracted_dir = os.path.join(self.tmp_dir, "extracted") + if (os.path.exists(extracted_dir)): + shutil.rmtree(extracted_dir) + with zipfile.ZipFile(file_path, 'r') as zip_ref: + zip_ref.extractall(extracted_dir) + + mp3_file = None + cdg_file = None + files = os.listdir(extracted_dir) + for file in files: + if os.path.splitext(file)[1] == ".mp3": + mp3_file = file + elif os.path.splitext(file)[1] == ".cdg": + cdg_file = file + + if (mp3_file is not None) and (cdg_file is not None): + if (os.path.splitext(mp3_file)[0] == os.path.splitext(cdg_file)[0] ): + return os.path.join(extracted_dir, mp3_file) + else: + raise Exception("Zipped .mp3 file did not have a matching .cdg file: " + files) + else: + raise Exception("No .mp3 or .cdg was found in the zip file: " + file_path) + + def handle_mp3_cdg(self, file_path): + if (os.path.isfile(os.path.splitext(file_path)[0] + ".cdg")): + return file_path else: - command = self.cmd_base + additional_parameters + [file_path] - print("Command: %s" % command) - self.process = subprocess.Popen( - command, shell=(self.platform == "windows"), stdin=subprocess.PIPE - ) + raise Exception("No matching .cdg file found for: " + file_path) + + def process_file(self, file_path): + file_extension = os.path.splitext(file_path)[1] + if (file_extension == ".zip"): + return self.handle_zipped_cdg(file_path) + elif (file_extension == ".mp3"): + return self.handle_mp3_cdg(file_path) + else: + return file_path + + def play_file(self, file_path, additional_parameters=None): + try: + file_path = self.process_file(file_path) + if self.is_playing() or self.is_paused(): + logging.debug("VLC is currently playing, stopping track...") + self.stop() + # this pause prevents vlc http server from being borked after transpose + time.sleep(0.2) + if self.platform == "windows": + file_path = r"{}".format(file_path) + if additional_parameters == None: + command = self.cmd_base + [file_path] + else: + command = self.cmd_base + additional_parameters + [file_path] + print("Command: %s" % command) + self.process = subprocess.Popen( + command, shell=(self.platform == "windows"), stdin=subprocess.PIPE + ) + except Exception as e: + logging.error("Playing file failed: " + str(e)) def play_file_transpose(self, file_path, semitones): # --speex-resampler-quality= From a8352496a4cd034eb1aed6fa8b37257e90bbb3f6 Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Sat, 26 Dec 2020 21:20:37 -0800 Subject: [PATCH 04/17] #69 CDG support documentation --- README.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 59dc1f38..ffc3f159 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,10 @@ PiKaraoke is a "KTV"-style karaoke song search and queueing system. It connects If you want to support this project with a little monetary tip, it's much appreciated: Buy Me A Coffee -## What's new (September 2020) +## What's new (January 2021) -- VLC support! Omxplayer is still default on raspberry pi, which can use both. -- Thanks to VLC and a lot of hacking, we now have cross platform support: Linux, OSX, and Windows! -- **Key Change / Pitch shifting!!** Also thanks to VLC. Not supported on omxplayer. -- python3 support! python 2.7 will still work for now, but it has gone the way of Latin. Python3 will become required in a near future revision. -- Basic hotkeys: "esc" exits app. "f" toggles fullscreen. This became necessary when working with windowed OS's +- CDG file support! Also supports zipped cdg + mp3, just add the files to the root of the download directory +- Default download directories are now ~/pikaraoke-songs ## Features @@ -19,7 +16,7 @@ If you want to support this project with a little monetary tip, it's much apprec - Searching song library via autocomplete - Adding new tracks from Youtube - Offline storage of video files -- mp3 + cdg file support (vlc only, must be uncompressed and added to download directory manually) +- mp3 + cdg file support (not supported on omxplayer, must be copied to download directory manually) - Pause/Skip/Restart and volume control - Now playing and Up Next display - Basic editing of downloaded file names @@ -306,13 +303,13 @@ First of all, you must be running pikaraoke with the --use-vlc option. While a song is playing, the home screen of the web interface will show a transpose slider. Slide it up or down based on your preference and press the "ok" button to restart the song in the given key. -### How do I add cdg files? +### How do I add cdg or mp3+cdg zip files? -You'll need to add them manually by copying them to the root of your download folder. Run `python app.py --help` and look under DOWNLOAD_PATH to find out what the default folder is, or specify your own. CDG files must have an mp3 file with a matching file name. They must also be uncompressed and not stashed away in sub-directories. +You'll need to add them manually by copying them to the root of your download folder. Run `python app.py --help` and look under DOWNLOAD_PATH to find out what the default folder is, or specify your own. Only cdg/mp3 pairs and .zip files are supported. -### I'm only hearing audio and not seeing video +### My mp3/cdg file is not playing -If this is a mp3 + cdg file, the filename of the cdg file might not match the mp3 file exactly or be missing. You'll have to fix this manually by supplying a matching cdg file. Also, cdg files are only supported in vlc. +CDG files must have an mp3 file with a exact matching file name. They can also be bundled together in a single zip file, but the filenames in the zip must still match. They must also be placed in the root of the download directory and not stashed away in sub-directories. Also, if you're running omxplayer instead of vlc, there will only be audio. ### I'm on a laptop, how do I output just pikaraoke to an external monitor/screen? From 07e112698a0fe3273506b9b22a5701733e9a1a55 Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Sat, 26 Dec 2020 21:40:15 -0800 Subject: [PATCH 05/17] Exceptions thrown on start up if browser window open to /nowplaying #58 --- app.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/app.py b/app.py index be12a171..5a02f941 100644 --- a/app.py +++ b/app.py @@ -51,21 +51,29 @@ def home(): show_transpose=k.use_vlc, transpose_value=k.now_playing_transpose, ) + # except (Exception) as e: + # logging.error("Problem loading /, pikaraoke may still be starting up: " + str(e)) + # return "" + @app.route("/nowplaying") def nowplaying(): - if len(k.queue) >= 1: - next_song = filename_from_path(k.queue[0]) - else: - next_song = None - rc = { - "now_playing": k.now_playing, - "up_next": next_song, - "is_paused": k.is_paused, - "transpose_value": k.now_playing_transpose, - } - return json.dumps(rc) + try: + if len(k.queue) >= 1: + next_song = filename_from_path(k.queue[0]) + else: + next_song = None + rc = { + "now_playing": k.now_playing, + "up_next": next_song, + "is_paused": k.is_paused, + "transpose_value": k.now_playing_transpose, + } + return json.dumps(rc) + except (Exception) as e: + logging.error("Problem loading /nowplaying, pikaraoke may still be starting up: " + str(e)) + return "" @app.route("/queue") From a5cde52127434b279332406d8f6734bdbeddfe1a Mon Sep 17 00:00:00 2001 From: vicwomg Date: Mon, 28 Dec 2020 20:16:08 -0800 Subject: [PATCH 06/17] #69 fix issue with tilde directories in windows --- app.py | 2 +- vlcclient.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index 5a02f941..2f4b0ac6 100644 --- a/app.py +++ b/app.py @@ -456,7 +456,7 @@ def get_default_dl_dir(platform): # homedir is preferred because it doesn't require root #61 return "~/pikaraoke-songs" elif platform == "windows": - legacy_directory = "~\pikaraoke\songs" + legacy_directory = os.path.expanduser("~\pikaraoke\songs") if os.path.exists(legacy_directory): return legacy_directory else: diff --git a/vlcclient.py b/vlcclient.py index 0c3ff4cf..2a15b598 100644 --- a/vlcclient.py +++ b/vlcclient.py @@ -45,7 +45,7 @@ def __init__(self, port=5002, path=None): # Determine tmp directories (for things like extracted cdg files) if self.platform == "windows": - self.tmp_dir = r"~\\AppData\\Local\\Temp\\pikaraoke\\" + self.tmp_dir = os.path.expanduser(r"~\\AppData\\Local\\Temp\\pikaraoke\\") else: self.tmp_dir = "/tmp/pikaraoke/" From c2d97e61a30e15d779d5aaf3cd42d3176c160be5 Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Mon, 28 Dec 2020 20:17:31 -0800 Subject: [PATCH 07/17] #62 first pass at omxclient separated library --- omxclient.py | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++ vlcclient.py | 2 +- 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 omxclient.py diff --git a/omxclient.py b/omxclient.py new file mode 100644 index 00000000..f848bedd --- /dev/null +++ b/omxclient.py @@ -0,0 +1,127 @@ +import logging +import subprocess + + +class OMXClient: + def __init__(self, path=None, adev=None, dual_screen=False): + # Handle omxplayer paths + if path == None: + self.path = "/usr/bin/omxplayer" + else: + self.path = path + + if adev == None: + self.adev = "both" + else: + self.adev = adev + + if dual_screen: + self.dual_screen = True + else: + self.dual_screen = False + + self.paused = False + + self.volume_offset = 0 + self.process = None + + def play_file(self, file_path, additional_parameters=None): + logging.info("Playing video in omxplayer: " + file_path) + self.kill() + cmd = [ + self.path, + file_path, + "--blank", + "-o", + self.adev, + "--vol", + str(self.volume_offset), + "--font-size", + str(25), + ] + if self.dual_screen: + cmd += ["--display", "7"] + + logging.debug("Player command: " + " ".join(cmd)) + self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE) + self.paused = False + + def pause(self): + if (not self.paused): + self.process.stdin.write("p".encode("utf-8")) + self.process.stdin.flush() + self.paused = True + + def play(self): + if (self.paused): + self.process.stdin.write("p".encode("utf-8")) + self.process.stdin.flush() + self.paused = False + + def stop(self): + self.process.stdin.write("q".encode("utf-8")) + self.process.stdin.flush() + self.paused = False + + def restart(self): + self.process.stdin.write("i".encode("utf-8")) + self.process.stdin.flush() + self.paused = False + + def vol_up(self): + logging.info("Volume up") + self.process.stdin.write("=".encode("utf-8")) + self.process.stdin.flush() + self.volume_offset += 300 + + def vol_down(self): + logging.info("Volume down") + self.process.stdin.write("-".encode("utf-8")) + self.volume_offset -= 300 + + def kill(self): + try: + self.process.kill() + self.paused = False + except (OSError, AttributeError) as e: + logging.error(e) + return + + def is_running(self): + return ( + self.process != None and self.process.poll() == None + ) + + def is_playing(self): + return ( + self.process != None and self.process.poll() == None and self.paused == False + ) + + def is_paused(self): + return self.paused + + def get_volume(self): + return self.volume_offset + + def run(self): + try: + while True: + pass + except KeyboardInterrupt: + self.kill() + + +# if __name__ == "__main__": +# k = VLCClient() +# k.play_file("/path/to/file.mp4") +# time.sleep(2) +# k.pause() +# k.vol_up() +# k.vol_up() +# time.sleep(2) +# k.vol_down() +# k.vol_down() +# time.sleep(2) +# k.play() +# time.sleep(2) +# k.stop() diff --git a/vlcclient.py b/vlcclient.py index 2a15b598..4fa9b54e 100644 --- a/vlcclient.py +++ b/vlcclient.py @@ -138,7 +138,7 @@ def play_file(self, file_path, additional_parameters=None): command = self.cmd_base + [file_path] else: command = self.cmd_base + additional_parameters + [file_path] - print("Command: %s" % command) + logging.debug("VLC Command: %s" % command) self.process = subprocess.Popen( command, shell=(self.platform == "windows"), stdin=subprocess.PIPE ) From 0d798e0ba558f890ee33481a1b77d973e6a5a3ec Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Mon, 28 Dec 2020 20:18:32 -0800 Subject: [PATCH 08/17] #69 documentation update to make more clear that vlc require for cdgs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ffc3f159..f128ebbc 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ If you want to support this project with a little monetary tip, it's much apprec ## What's new (January 2021) -- CDG file support! Also supports zipped cdg + mp3, just add the files to the root of the download directory +- CDG file support! Also supports zipped cdg + mp3, just add the files to the root of the download directory (must be using vlc) - Default download directories are now ~/pikaraoke-songs ## Features @@ -16,7 +16,7 @@ If you want to support this project with a little monetary tip, it's much apprec - Searching song library via autocomplete - Adding new tracks from Youtube - Offline storage of video files -- mp3 + cdg file support (not supported on omxplayer, must be copied to download directory manually) +- mp3 + cdg file support (vlc-only. Not supported on omxplayer, must be copied to download directory manually) - Pause/Skip/Restart and volume control - Now playing and Up Next display - Basic editing of downloaded file names From 654ac7ace212dbdf80723a159d234e756e35116b Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Tue, 29 Dec 2020 22:07:49 -0800 Subject: [PATCH 09/17] #74 add information to troubleshooting readme on how to get headphone jack working on vlc/pi --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f128ebbc..4c18d6cc 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ Also works on macs, PCs, and linux! ## Installation -Install git, if you haven't already. -Install python3/pip3: https://www.python.org/downloads/ (python 2.7 may work, but is not officially supported) +Install git, if you haven't already. (on raspberry pi: `sudo apt-get update; sudo apt-get install git`) +Install python3/pip3 (usually raspberry pis already have it, run `python3 --version` to check): https://www.python.org/downloads/ (python 2.7 may work, but is not officially supported) Clone this repo: @@ -247,7 +247,18 @@ If you're hearing distorted audio out, try '--adev alsa' If you're using an external USB sound card or hifi audio hat like the hifiberry, you'll need to add the argument '--adev alsa:hw:0,0' when you launch pikaraoke -You can also try vlc with the --use-vlc option. It sometimes handles audio more consistently. +You can also try vlc with the --use-vlc option. There have been reports that HDMI audio works fine with vlc, but to use the headphone jack you need to edit some also conf files: + +`sudo nano /usr/share/alsa/alsa.conf` + +Scroll down and change defaults.ctl.card and defaults.pcm.card to "1" + +``` +defaults.ctl.card 1 +defaults.pcm.card 1 +``` + +Note this value might be diffent in older versions of Raspbian. See source article for details: https://raspberrypi.stackexchange.com/a/39942 ### Songs aren't downloading! From 1f15f66b139dd0cba05e37eb77b51f526627b80d Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Tue, 29 Dec 2020 22:21:25 -0800 Subject: [PATCH 10/17] Implement omxclient --- karaoke.py | 64 +++++++++++++++------------------------------------- omxclient.py | 7 ++++++ 2 files changed, 25 insertions(+), 46 deletions(-) diff --git a/karaoke.py b/karaoke.py index 85ae76e9..4d8f0044 100644 --- a/karaoke.py +++ b/karaoke.py @@ -15,6 +15,7 @@ import qrcode from unidecode import unidecode +import omxclient import vlcclient from get_platform import get_platform @@ -75,7 +76,7 @@ def __init__( self.hide_overlay = hide_overlay self.volume_offset = volume self.youtubedl_path = youtubedl_path - self.player_path = omxplayer_path + self.omxplayer_path = omxplayer_path self.use_vlc = use_vlc self.vlc_path = vlc_path self.vlc_port = vlc_port @@ -84,6 +85,7 @@ def __init__( # other initializations self.platform = get_platform() self.vlcclient = None + self.omxclient = None self.screen = None logging.basicConfig( @@ -123,7 +125,7 @@ def __init__( self.download_path, self.volume_offset, self.youtubedl_path, - self.player_path, + self.omxplayer_path, self.logo_path, self.use_vlc, self.vlc_path, @@ -164,6 +166,8 @@ def __init__( if self.use_vlc: self.vlcclient = vlcclient.VLCClient(port=self.vlc_port, path=self.vlc_path) + else: + self.omxclient = omxclient.OMXClient(path=self.omxplayer_path, adev=self.omxplayer_adev, dual_screen=self.dual_screen) if not self.hide_splash_screen: self.generate_qr_code() @@ -496,12 +500,8 @@ def kill_player(self): if self.vlcclient != None: self.vlcclient.kill() else: - logging.debug("Killing old omxplayer processes") - player_kill = ["killall", "omxplayer.bin"] - FNULL = open(os.devnull, "w") - subprocess.Popen( - player_kill, stdin=subprocess.PIPE, stdout=FNULL, stderr=FNULL - ) + if self.omxclient != None: + self.omxclient.kill() def play_file(self, file_path, semitones=0): self.now_playing = self.filename_from_path(file_path) @@ -517,25 +517,7 @@ def play_file(self, file_path, semitones=0): self.vlcclient.play_file_transpose(file_path, semitones) else: logging.info("Playing video in omxplayer: " + self.now_playing) - self.kill_player() - cmd = [ - self.player_path, - file_path, - "--blank", - "-o", - self.omxplayer_adev, - "--vol", - str(self.volume_offset), - "--font-size", - str(25), - ] - if self.dual_screen: - cmd += ["--display", "7"] - - if not self.hide_overlay: - cmd += ["--subtitles", self.overlay_file_path] - logging.debug("Player command: " + " ".join(cmd)) - self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE) + self.omxclient.play_file(file_path) self.is_paused = False self.render_splash_screen() # remove old previous track @@ -556,10 +538,7 @@ def is_file_playing(self): self.now_playing = None return False else: - if self.process == None: - self.now_playing = None - return False - elif self.process.poll() == None: + if self.omxclient != None and self.omxclient.is_running(): return True else: self.now_playing = None @@ -645,8 +624,7 @@ def skip(self): if self.use_vlc: self.vlcclient.stop() else: - self.process.stdin.write("q".encode("utf-8")) - self.process.stdin.flush() + self.omxclient.stop() self.reset_now_playing() return True else: @@ -662,8 +640,10 @@ def pause(self): else: self.vlcclient.play() else: - self.process.stdin.write("p".encode("utf-8")) - self.process.stdin.flush() + if self.omxclient.is_playing(): + self.omxclient.pause() + else: + self.omxclient.play() self.is_paused = not self.is_paused return True else: @@ -676,10 +656,7 @@ def vol_up(self): self.vlcclient.vol_up() self.volume_offset = self.vlcclient.get_volume() else: - logging.info("Volume up: " + self.now_playing) - self.process.stdin.write("=".encode("utf-8")) - self.process.stdin.flush() - self.volume_offset += 300 + self.omxclient.vol_up() return True else: logging.warning("Tried to volume up, but no file is playing!") @@ -691,9 +668,7 @@ def vol_down(self): self.vlcclient.vol_down() self.volume_offset = self.vlcclient.get_volume() else: - logging.info("Volume down: " + self.now_playing) - self.process.stdin.write("-".encode("utf-8")) - self.volume_offset -= 300 + self.omxclient.vol_down() return True else: logging.warning("Tried to volume down, but no file is playing!") @@ -704,10 +679,7 @@ def restart(self): if self.use_vlc: self.vlcclient.restart() else: - logging.info("Restarting: " + self.now_playing) - self.process.stdin.write("i".encode("utf-8")) - self.process.stdin.flush() - self.is_paused = False + self.omxclient.restart() return True else: diff --git a/omxclient.py b/omxclient.py index f848bedd..84002e69 100644 --- a/omxclient.py +++ b/omxclient.py @@ -1,4 +1,5 @@ import logging +import os import subprocess @@ -82,6 +83,12 @@ def vol_down(self): def kill(self): try: self.process.kill() + logging.debug("Killing old omxplayer processes") + player_kill = ["killall", "omxplayer.bin"] + FNULL = open(os.devnull, "w") + subprocess.Popen( + player_kill, stdin=subprocess.PIPE, stdout=FNULL, stderr=FNULL + ) self.paused = False except (OSError, AttributeError) as e: logging.error(e) From f581cee82dd4c04e13566ca0fb67a49b7482e2ee Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Tue, 29 Dec 2020 22:31:54 -0800 Subject: [PATCH 11/17] Add some debug logging --- omxclient.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/omxclient.py b/omxclient.py index 84002e69..b96a514a 100644 --- a/omxclient.py +++ b/omxclient.py @@ -48,12 +48,14 @@ def play_file(self, file_path, additional_parameters=None): self.paused = False def pause(self): + logging.debug("Pause pressed. Current paused state: " + self.paused) if (not self.paused): self.process.stdin.write("p".encode("utf-8")) self.process.stdin.flush() self.paused = True def play(self): + logging.debug("Play pressed. Current paused state: " + self.paused) if (self.paused): self.process.stdin.write("p".encode("utf-8")) self.process.stdin.flush() @@ -74,11 +76,14 @@ def vol_up(self): self.process.stdin.write("=".encode("utf-8")) self.process.stdin.flush() self.volume_offset += 300 + logging.debug("Volume offset: " + self.volume_offset) def vol_down(self): logging.info("Volume down") self.process.stdin.write("-".encode("utf-8")) + self.process.stdin.flush() self.volume_offset -= 300 + logging.debug("Volume offset: " + self.volume_offset) def kill(self): try: @@ -100,9 +105,9 @@ def is_running(self): ) def is_playing(self): - return ( - self.process != None and self.process.poll() == None and self.paused == False - ) + is_playing = self.process != None and self.process.poll() == None and self.paused == False + logging.debug("Is playing? " + is_playing) + return is_playing def is_paused(self): return self.paused From 1f6b92395eb914c7fa02b50b276c5d6ac855492d Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Tue, 29 Dec 2020 22:34:08 -0800 Subject: [PATCH 12/17] fix bad string concatenation --- omxclient.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/omxclient.py b/omxclient.py index b96a514a..e65d2cb9 100644 --- a/omxclient.py +++ b/omxclient.py @@ -48,14 +48,14 @@ def play_file(self, file_path, additional_parameters=None): self.paused = False def pause(self): - logging.debug("Pause pressed. Current paused state: " + self.paused) + logging.debug("Pause pressed. Current paused state: %b" % self.paused) if (not self.paused): self.process.stdin.write("p".encode("utf-8")) self.process.stdin.flush() self.paused = True def play(self): - logging.debug("Play pressed. Current paused state: " + self.paused) + logging.debug("Play pressed. Current paused state: %b" % self.paused) if (self.paused): self.process.stdin.write("p".encode("utf-8")) self.process.stdin.flush() @@ -76,14 +76,12 @@ def vol_up(self): self.process.stdin.write("=".encode("utf-8")) self.process.stdin.flush() self.volume_offset += 300 - logging.debug("Volume offset: " + self.volume_offset) def vol_down(self): logging.info("Volume down") self.process.stdin.write("-".encode("utf-8")) self.process.stdin.flush() self.volume_offset -= 300 - logging.debug("Volume offset: " + self.volume_offset) def kill(self): try: @@ -106,7 +104,7 @@ def is_running(self): def is_playing(self): is_playing = self.process != None and self.process.poll() == None and self.paused == False - logging.debug("Is playing? " + is_playing) + logging.debug("Is playing? %b" % is_playing) return is_playing def is_paused(self): From eb012fb984fb18e1261cb245f23374325ba6cadb Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Tue, 29 Dec 2020 23:23:52 -0800 Subject: [PATCH 13/17] Fix issues with restarting while in paused state --- README.md | 4 ++-- karaoke.py | 2 +- omxclient.py | 22 ++++------------------ vlcclient.py | 1 + 4 files changed, 8 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 4c18d6cc..b526dd6f 100644 --- a/README.md +++ b/README.md @@ -125,14 +125,14 @@ This is optional, but you may want to make your raspberry pi a dedicated karaoke ``` # start pikaraoke on startup -/usr/bin/python /home/pi/pikaraoke/app.py & +/usr/bin/python3 /home/pi/pikaraoke/app.py & ``` Or if you're like me and want some logging for aiding debugging, the following stores output at: /var/log/pikaraoke.log: ``` # start pikaraoke on startup / logging -/usr/bin/python /home/pi/pikaraoke/app.py >> /var/log/pikaraoke.log 2>&1 & +/usr/bin/python3 /home/pi/pikaraoke/app.py >> /var/log/pikaraoke.log 2>&1 & ``` If you want to kill the pikaraoke process, you can do so from the PiKaraoke Web UI under: `Info > Quit pikaraoke`. Or you can ssh in and run `sudo killall python` or something similar. diff --git a/karaoke.py b/karaoke.py index 4d8f0044..797439e9 100644 --- a/karaoke.py +++ b/karaoke.py @@ -680,8 +680,8 @@ def restart(self): self.vlcclient.restart() else: self.omxclient.restart() + self.is_paused = False return True - else: logging.warning("Tried to restart, but no file is playing!") return False diff --git a/omxclient.py b/omxclient.py index e65d2cb9..b2fec204 100644 --- a/omxclient.py +++ b/omxclient.py @@ -1,6 +1,7 @@ import logging import os import subprocess +import time class OMXClient: @@ -48,14 +49,12 @@ def play_file(self, file_path, additional_parameters=None): self.paused = False def pause(self): - logging.debug("Pause pressed. Current paused state: %b" % self.paused) if (not self.paused): self.process.stdin.write("p".encode("utf-8")) self.process.stdin.flush() self.paused = True def play(self): - logging.debug("Play pressed. Current paused state: %b" % self.paused) if (self.paused): self.process.stdin.write("p".encode("utf-8")) self.process.stdin.flush() @@ -69,6 +68,9 @@ def stop(self): def restart(self): self.process.stdin.write("i".encode("utf-8")) self.process.stdin.flush() + if (self.paused): + time.sleep(0.2) + self.play() self.paused = False def vol_up(self): @@ -104,7 +106,6 @@ def is_running(self): def is_playing(self): is_playing = self.process != None and self.process.poll() == None and self.paused == False - logging.debug("Is playing? %b" % is_playing) return is_playing def is_paused(self): @@ -120,18 +121,3 @@ def run(self): except KeyboardInterrupt: self.kill() - -# if __name__ == "__main__": -# k = VLCClient() -# k.play_file("/path/to/file.mp4") -# time.sleep(2) -# k.pause() -# k.vol_up() -# k.vol_up() -# time.sleep(2) -# k.vol_down() -# k.vol_down() -# time.sleep(2) -# k.play() -# time.sleep(2) -# k.stop() diff --git a/vlcclient.py b/vlcclient.py index 4fa9b54e..59ef7cda 100644 --- a/vlcclient.py +++ b/vlcclient.py @@ -213,6 +213,7 @@ def stop(self): def restart(self): logging.info(self.command("seek&val=0")) + self.play() return self.command("seek&val=0") def vol_up(self): From 7b304dabc3391d671ffe8514f938427d36301772 Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Wed, 30 Dec 2020 00:02:23 -0800 Subject: [PATCH 14/17] #62 change default player to vlc on pi restore old /usr/lib/pikaraoke default download dir deprecate omxplayer overlay feature handle vlc paths exclusively in vlcclient.py --- app.py | 55 +++++++++++++--------------------------------------- karaoke.py | 36 ++++++---------------------------- omxclient.py | 7 +++++-- vlcclient.py | 23 ++++++++++++---------- 4 files changed, 38 insertions(+), 83 deletions(-) diff --git a/app.py b/app.py index 2f4b0ac6..8c2faf53 100644 --- a/app.py +++ b/app.py @@ -14,6 +14,7 @@ import karaoke from get_platform import get_platform +from vlcclient import get_default_vlc_path try: from urllib.parse import quote, unquote @@ -51,11 +52,6 @@ def home(): show_transpose=k.use_vlc, transpose_value=k.now_playing_transpose, ) - # except (Exception) as e: - # logging.error("Problem loading /, pikaraoke may still be starting up: " + str(e)) - # return "" - - @app.route("/nowplaying") def nowplaying(): @@ -432,29 +428,9 @@ def get_default_youtube_dl_path(platform): else: return "/usr/local/bin/youtube-dl" - -def get_default_vlc_path(platform): - if platform == "osx": - return "/Applications/VLC.app/Contents/MacOS/VLC" - elif platform == "windows": - alt_vlc_path = r"C:\\Program Files (x86)\\VideoLAN\VLC\\vlc.exe" - if os.path.isfile(alt_vlc_path): - return alt_vlc_path - else: - return r"C:\Program Files\VideoLAN\VLC\vlc.exe" - else: - return "/usr/bin/vlc" - - def get_default_dl_dir(platform): if platform == "raspberry_pi": - legacy_directory = "/usr/lib/pikaraoke/songs" - if os.path.exists(legacy_directory): - # preserve old dl location for previous users - return legacy_directory - else: - # homedir is preferred because it doesn't require root #61 - return "~/pikaraoke-songs" + return "/usr/lib/pikaraoke/songs" elif platform == "windows": legacy_directory = os.path.expanduser("~\pikaraoke\songs") if os.path.exists(legacy_directory): @@ -540,12 +516,6 @@ def get_default_dl_dir(platform): default=default_log_level, required=False, ) - parser.add_argument( - "--show-overlay", - action="store_true", - help="Show text overlay in omxplayer with song title and IP. (feature is broken on Pi 4 omxplayer 12/24/2019)", - required=False, - ) parser.add_argument( "--hide-ip", action="store_true", @@ -577,10 +547,16 @@ def get_default_dl_dir(platform): help="Download higher quality video. Note: requires ffmpeg and may cause CPU, download speed, and other performance issues", required=False, ) + parser.add_argument( + "--use-omxplayer", + action="store_true", + help="Use OMX Player to play video instead of the default VLC Player. This may be better-performing on older raspberry pi devices. Certain features like key change and cdg support wont be available. Note: if you want to play audio to the headphone jack on a rpi, you'll need to configure this in raspi-config: 'Advanced Options > Audio > Force 3.5mm (headphone)'", + required=False, + ), parser.add_argument( "--use-vlc", action="store_true", - help="Use VLC Player instead of the default OMX Player. Enabled by default on non-pi hardware. Note: if you want to play audio to the headphone jack on a rpi, you'll need to configure this in raspi-config: 'Advanced Options > Audio > Force 3.5mm (headphone)'", + help="Use VLC Player to play video. Enabled by default. Note: if you want to play audio to the headphone jack on a rpi, see troubleshooting steps in README.md", required=False, ), parser.add_argument( @@ -617,14 +593,11 @@ def get_default_dl_dir(platform): } ) - # force VLC on non-pi hardware - if not platform == "raspberry_pi" and not args.use_vlc: - print("Defaulting to VLC player") + # Handle OMX player if specified + if platform == "raspberry_pi" and args.use_omxplayer: + args.use_vlc = False + else: args.use_vlc = True - # disallow overlay on VLC - if args.use_vlc and args.show_overlay: - print("Overlay not supported VLC. Disabling it.") - args.show_overlay = False # check if required binaries exist if not os.path.isfile(args.youtubedl_path): @@ -662,12 +635,12 @@ def get_default_dl_dir(platform): splash_delay=args.splash_delay, log_level=args.log_level, volume=args.volume, - hide_overlay=not args.show_overlay, hide_ip=args.hide_ip, hide_splash_screen=args.hide_splash_screen, omxplayer_adev=args.adev, dual_screen=args.dual_screen, high_quality=args.high_quality, + use_omxplayer=args.use_omxplayer, use_vlc=args.use_vlc, vlc_path=args.vlc_path, vlc_port=args.vlc_port, diff --git a/karaoke.py b/karaoke.py index 797439e9..914da454 100644 --- a/karaoke.py +++ b/karaoke.py @@ -25,7 +25,6 @@ class Karaoke: - overlay_file_path = "/tmp/pikaraoke-overlay.srt" raspi_wifi_config_ip = "10.0.0.1" raspi_wifi_conf_file = "/etc/raspiwifi/raspiwifi.conf" raspi_wifi_config_installed = os.path.exists(raspi_wifi_conf_file) @@ -49,7 +48,6 @@ def __init__( download_path="/usr/lib/pikaraoke/songs", hide_ip=False, hide_splash_screen=False, - hide_overlay=True, omxplayer_adev="both", dual_screen=False, high_quality=False, @@ -58,7 +56,8 @@ def __init__( splash_delay=2, youtubedl_path="/usr/local/bin/youtube-dl", omxplayer_path="/usr/bin/omxplayer", - use_vlc=False, + use_omxplayer=False, + use_vlc=True, vlc_path=None, vlc_port=None, logo_path=None, @@ -73,10 +72,10 @@ def __init__( self.dual_screen = dual_screen self.high_quality = high_quality self.splash_delay = int(splash_delay) - self.hide_overlay = hide_overlay self.volume_offset = volume self.youtubedl_path = youtubedl_path self.omxplayer_path = omxplayer_path + self.use_omxplayer = use_omxplayer self.use_vlc = use_vlc self.vlc_path = vlc_path self.vlc_port = vlc_port @@ -100,7 +99,6 @@ def __init__( hide IP: %s hide splash: %s splash_delay: %s - hide overlay: %s omx audio device: %s dual screen: %s high quality video: %s @@ -109,6 +107,7 @@ def __init__( youtube-dl path: %s omxplayer path: %s logo path: %s + Use OMXPlayer: %s Use VLC: %s VLC path: %s VLC port: %s @@ -118,7 +117,6 @@ def __init__( self.hide_ip, self.hide_splash_screen, self.splash_delay, - self.hide_overlay, self.omxplayer_adev, self.dual_screen, self.high_quality, @@ -127,6 +125,7 @@ def __init__( self.youtubedl_path, self.omxplayer_path, self.logo_path, + self.use_omxplayer, self.use_vlc, self.vlc_path, self.vlc_port, @@ -161,13 +160,11 @@ def __init__( # clean up old sessions self.kill_player() - if os.path.isfile(self.overlay_file_path): - os.remove(self.overlay_file_path) if self.use_vlc: self.vlcclient = vlcclient.VLCClient(port=self.vlc_port, path=self.vlc_path) else: - self.omxclient = omxclient.OMXClient(path=self.omxplayer_path, adev=self.omxplayer_adev, dual_screen=self.dual_screen) + self.omxclient = omxclient.OMXClient(path=self.omxplayer_path, adev=self.omxplayer_adev, dual_screen=self.dual_screen, volume_offset=self.volume_offset) if not self.hide_splash_screen: self.generate_qr_code() @@ -225,23 +222,6 @@ def upgrade_youtubedl(self): def is_network_connected(self): return not len(self.ip) < 7 - def generate_overlay_file(self, file_path): - if not self.hide_overlay: - current_song = self.filename_from_path(file_path) - logging.debug("Generating overlay file") - if not self.hide_ip: - msg = "PiKaraoke IP: %s" % self.url - else: - msg = "" - output = "00:00:00,00 --> 00:00:30,00 \n%s\n%s" % (current_song, msg) - f = open(self.overlay_file_path, "w") - try: - f.write(output.encode("utf-8")) - except TypeError: - # python 3 hack - f.write(output) - logging.debug("Done generating overlay file: " + output) - def generate_qr_code(self): logging.debug("Generating URL QR code") img = qrcode.make(self.url) @@ -506,8 +486,6 @@ def kill_player(self): def play_file(self, file_path, semitones=0): self.now_playing = self.filename_from_path(file_path) self.now_playing_filename = file_path - if (not self.hide_overlay) and (not self.use_vlc): - self.generate_overlay_file(file_path) if self.use_vlc: logging.info("Playing video in VLC: " + self.now_playing) @@ -654,7 +632,6 @@ def vol_up(self): if self.is_file_playing(): if self.use_vlc: self.vlcclient.vol_up() - self.volume_offset = self.vlcclient.get_volume() else: self.omxclient.vol_up() return True @@ -666,7 +643,6 @@ def vol_down(self): if self.is_file_playing(): if self.use_vlc: self.vlcclient.vol_down() - self.volume_offset = self.vlcclient.get_volume() else: self.omxclient.vol_down() return True diff --git a/omxclient.py b/omxclient.py index b2fec204..e52b5a4d 100644 --- a/omxclient.py +++ b/omxclient.py @@ -5,7 +5,7 @@ class OMXClient: - def __init__(self, path=None, adev=None, dual_screen=False): + def __init__(self, path=None, adev=None, dual_screen=False, volume_offset=None): # Handle omxplayer paths if path == None: self.path = "/usr/bin/omxplayer" @@ -24,7 +24,10 @@ def __init__(self, path=None, adev=None, dual_screen=False): self.paused = False - self.volume_offset = 0 + if volume_offset: + self.volume_offset = volume_offset + else: + self.volume_offset = 0 self.process = None def play_file(self, file_path, additional_parameters=None): diff --git a/vlcclient.py b/vlcclient.py index 59ef7cda..69f893a4 100644 --- a/vlcclient.py +++ b/vlcclient.py @@ -15,6 +15,18 @@ from get_platform import get_platform +def get_default_vlc_path(platform): + if platform == "osx": + return "/Applications/VLC.app/Contents/MacOS/VLC" + elif platform == "windows": + alt_vlc_path = r"C:\\Program Files (x86)\\VideoLAN\VLC\\vlc.exe" + if os.path.isfile(alt_vlc_path): + return alt_vlc_path + else: + return r"C:\Program Files\VideoLAN\VLC\vlc.exe" + else: + return "/usr/bin/vlc" + class VLCClient: def __init__(self, port=5002, path=None): @@ -30,16 +42,7 @@ def __init__(self, port=5002, path=None): # Handle vlc paths self.platform = get_platform() if path == None: - if self.platform == "osx": - self.path = "/Applications/VLC.app/Contents/MacOS/VLC" - elif self.platform == "windows": - alt_vlc_path = r"C:\\Program Files (x86)\\VideoLAN\VLC\\vlc.exe" - if os.path.isfile(alt_vlc_path): - self.path = alt_vlc_path - else: - self.path = r"C:\\Program Files\\VideoLAN\VLC\\vlc.exe" - else: - self.path = "/usr/bin/vlc" + self.path = get_default_vlc_path(self.platform) else: self.path = path From 632b5026e67470d4ea43e4fbd4cbeeeac4d7d8ed Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Wed, 30 Dec 2020 00:45:31 -0800 Subject: [PATCH 15/17] #77 allow manual song directory rescan from info screen --- app.py | 4 ++++ templates/info.html | 11 ++++++++++- vlcclient.py | 1 - 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index 8c2faf53..84506de9 100644 --- a/app.py +++ b/app.py @@ -387,6 +387,10 @@ def update_ytdl(): th.start() return redirect(url_for("home")) +@app.route("/refresh") +def refresh(): + k.get_available_songs() + return redirect(url_for("browse")) @app.route("/quit") def quit(): diff --git a/templates/info.html b/templates/info.html index 2a876ace..5920655c 100644 --- a/templates/info.html +++ b/templates/info.html @@ -57,7 +57,16 @@

System Info


Updates

-

If downloads or searches stopped working, updating youtube-dl will probably fix it. This may fail if you don't have proper file permissions. Check the pikaraoke log for errors

+

Manually update the song list: +

+ +

If downloads or searches stopped working, updating youtube-dl will probably fix it. This update link may fail if you don't have proper file permissions. Check the pikaraoke log for errors

  • Date: Wed, 30 Dec 2020 01:02:07 -0800 Subject: [PATCH 16/17] readme update --- README.md | 81 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index b526dd6f..59e20905 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,16 @@ PiKaraoke is a "KTV"-style karaoke song search and queueing system. It connects to your TV, and shows a QR code for computers and smartphones to connect to a web interface. From there, multiple users can seamlessly search your local track library, queue up songs, add an endless selection of new karaoke tracks from YouTube, and more. ~For use with Raspberry Pi devices.~ Works on Raspberry Pi, OSX, Windows, and Linux! -If you want to support this project with a little monetary tip, it's much appreciated: +If you want to support this project with a little monetary tip, it's much appreciated:
    Buy Me A Coffee ## What's new (January 2021) +- VLC is now the default media player. You can still use omxplayer for slower pi devices with `--use-omxplayer` - CDG file support! Also supports zipped cdg + mp3, just add the files to the root of the download directory (must be using vlc) -- Default download directories are now ~/pikaraoke-songs +- Refresh song list manually from the info screen. +- Default download directories are now ~/pikaraoke-songs on MacOS, Windows, and Linux +- General overdue cleanup of libraries ## Features @@ -16,12 +19,12 @@ If you want to support this project with a little monetary tip, it's much apprec - Searching song library via autocomplete - Adding new tracks from Youtube - Offline storage of video files -- mp3 + cdg file support (vlc-only. Not supported on omxplayer, must be copied to download directory manually) +- mp3 + cdg file support (vlc only) - Pause/Skip/Restart and volume control - Now playing and Up Next display - Basic editing of downloaded file names - Queue editing -- Key Change / Pitch shifting (only available while using vlc) +- Key Change / Pitch shifting (vlc only) ## Screenshots @@ -141,21 +144,22 @@ Note that if your wifi/network is inactive pikaraoke will error out 10 seconds a ## Usage -Here is the full list of command line arguments: +Here is the full list of command line arguments on OSX as an example (may not be up to date, run `python3 app.py --help` for the latest): ``` usage: app.py [-h] [-p PORT] [-d DOWNLOAD_PATH] [-o OMXPLAYER_PATH] [-y YOUTUBEDL_PATH] [-v VOLUME] [-s SPLASH_DELAY] [-l LOG_LEVEL] - [--show-overlay] [--hide-ip] [--hide-splash-screen] [--adev ADEV] - [--dual-screen] [--high-quality] [--use-vlc] [--vlc-path VLC_PATH] - [--vlc-port VLC_PORT] + [--hide-ip] [--hide-splash-screen] [--adev ADEV] [--dual-screen] + [--high-quality] [--use-omxplayer] [--use-vlc] + [--vlc-path VLC_PATH] [--vlc-port VLC_PORT] + [--logo_path LOGO_PATH] optional arguments: -h, --help show this help message and exit -p PORT, --port PORT Desired http port (default: 5000) -d DOWNLOAD_PATH, --download-path DOWNLOAD_PATH Desired path for downloaded songs. (default: - ~/pikaraoke/songs) + ~/pikaraoke-songs) -o OMXPLAYER_PATH, --omxplayer-path OMXPLAYER_PATH Path of omxplayer. Only important to raspberry pi hardware. (default: /usr/bin/omxplayer) @@ -172,26 +176,32 @@ optional arguments: -l LOG_LEVEL, --log-level LOG_LEVEL Logging level int value (DEBUG: 10, INFO: 20, WARNING: 30, ERROR: 40, CRITICAL: 50). (default: 20 ) - --show-overlay Show text overlay in omxplayer with song title and IP. - (feature is broken on Pi 4 omxplayer 12/24/2019) --hide-ip Hide IP address from the screen. --hide-splash-screen Hide splash screen before/between songs. --adev ADEV Pass the audio output device argument to omxplayer. Possible values: hdmi/local/both/alsa[:device]. If you are using a rpi USB soundcard or Hifi audio hat, try: - 'alsa:hw:0,0' Default 'both' + 'alsa:hw:0,0' Default: 'both' --dual-screen Output video to both HDMI ports (raspberry pi 4 only) --high-quality Download higher quality video. Note: requires ffmpeg and may cause CPU, download speed, and other performance issues - --use-vlc Use VLC Player instead of the default OMX Player. - Enabled by default on non-pi hardware. Note: if you - want to play audio to the headphone jack on a rpi, - you'll need to configure this in raspi-config: - 'Advanced Options > Audio > Force 3.5mm (headphone)' - --vlc-path VLC_PATH Full path to VLC (Defaults to standard installation - location) + --use-omxplayer Use OMX Player to play video instead of the default + VLC Player. This may be better-performing on older + raspberry pi devices. Certain features like key change + and cdg support wont be available. Note: if you want + to play audio to the headphone jack on a rpi, you'll + need to configure this in raspi-config: 'Advanced + Options > Audio > Force 3.5mm (headphone)' + --use-vlc Use VLC Player to play video. Enabled by default. + Note: if you want to play audio to the headphone jack + on a rpi, see troubleshooting steps in README.md + --vlc-path VLC_PATH Full path to VLC (Default: + /Applications/VLC.app/Contents/MacOS/VLC) --vlc-port VLC_PORT HTTP port for VLC remote control api (Default: 5002) + --logo_path LOGO_PATH + Path to a custom logo image file for the splash + screen. Recommended dimensions ~ 500x500px ``` ## Screen UI @@ -208,7 +218,7 @@ Make sure you are connected to the same network/wifi. You can then enter the sho - View Now Playing and Next tracks - Access controls to repeat, pause, skip and control volume -- (only when --use-vlc option is used) Transpose slider to change playback pitch +- Transpose slider to change playback pitch ### Queue @@ -227,6 +237,7 @@ Make sure you are connected to the same network/wifi. You can then enter the sho - Shows the IP and QR code to share with others - Shows CPU / Memory / Disk Use stats +- Allows updating the song list and youtube-dl version - Allows user to quit to console, shut down, or reboot system. Always shut down from here before you pull the plug on pikaraoke! ## Troubleshooting @@ -239,15 +250,7 @@ Advanced Options > Audio > Force 3.5mm (headphone) See: https://www.raspberrypi.org/documentation/configuration/audio-config.md -### I'm having audio issues with the headphone jack, external sound card, or other audio device - -Omxplayer tends to have some inconsistent results across different hardware combinations. Try experimenting with the --adev option, which specifies the audio device to omxplayer. Defaults to 'both' which is hdmi and headphone out. Other possible values are: hdmi/local/both/alsa[:device]. - -If you're hearing distorted audio out, try '--adev alsa' - -If you're using an external USB sound card or hifi audio hat like the hifiberry, you'll need to add the argument '--adev alsa:hw:0,0' when you launch pikaraoke - -You can also try vlc with the --use-vlc option. There have been reports that HDMI audio works fine with vlc, but to use the headphone jack you need to edit some also conf files: +If you're still having issues with hearing audio, it has been reported this helps on raspberry pi 4 devices: `sudo nano /usr/share/alsa/alsa.conf` @@ -258,11 +261,19 @@ defaults.ctl.card 1 defaults.pcm.card 1 ``` -Note this value might be diffent in older versions of Raspbian. See source article for details: https://raspberrypi.stackexchange.com/a/39942 +Note this value might be different in older versions of Raspbian or if you have external audio hardware. See source article for details: https://raspberrypi.stackexchange.com/a/39942 + +### I'm still having audio issues with the headphone jack, external sound card, or other audio device with omxplayer + +If using omxplayer with `--use-omxplayer`, it tends to have some inconsistent results across different hardware combinations. Try experimenting with the --adev option, which specifies the audio device to omxplayer. Defaults to 'both' which is hdmi and headphone out. Other possible values are: hdmi/local/both/alsa[:device]. + +If you're hearing distorted audio output, try '--adev alsa' with omxplauer. + +If you're using an external USB sound card or hifi audio hat like the hifiberry, you'll need to add the argument '--adev alsa:hw:0,0' when you launch pikaraoke ### Songs aren't downloading! -Make sure youtube-dl is up to date, old versions have higher failure rates due to security changes in Youtube. You can see your current version installed by navigating to `Info > System Info > Youtube-dl version`. The version number is usually the date it was released. If this is older than a few months, chances are it will need an update. +Make sure youtube-dl is up to date, old versions have higher failure rates due to security changes in Youtube. You can see your current version installed by navigating to `Info > System Info > Youtube-dl version`. The version number is usually the date it was released. If this is older than a couple of months, chances are it will need an update. You can update youtube-dl directly from the web UI. Go to `Info > Update Youtube-dl` (depending on how you installed, you may need to be running pikaraoke as sudo for this to work) @@ -310,17 +321,19 @@ The pi doesn't have a hardware audio input. Technically, you should be able to r ### How do I change song pitch/key? -First of all, you must be running pikaraoke with the --use-vlc option. - While a song is playing, the home screen of the web interface will show a transpose slider. Slide it up or down based on your preference and press the "ok" button to restart the song in the given key. +If you don't see this option, you may be running the `--use-omxplayer` option. Omxplayer does not support key change. + ### How do I add cdg or mp3+cdg zip files? You'll need to add them manually by copying them to the root of your download folder. Run `python app.py --help` and look under DOWNLOAD_PATH to find out what the default folder is, or specify your own. Only cdg/mp3 pairs and .zip files are supported. ### My mp3/cdg file is not playing -CDG files must have an mp3 file with a exact matching file name. They can also be bundled together in a single zip file, but the filenames in the zip must still match. They must also be placed in the root of the download directory and not stashed away in sub-directories. Also, if you're running omxplayer instead of vlc, there will only be audio. +CDG files must have an mp3 file with a exact matching file name. They can also be bundled together in a single zip file, but the filenames in the zip must still match. They must also be placed in the root of the download directory and not stashed away in sub-directories. + +If you only hear audio, you may be running the `--use-omxplayer` option. Omxplayer does not support cdg. ### I'm on a laptop, how do I output just pikaraoke to an external monitor/screen? From e221232f82b918443ba97a4e0133412359b55fb9 Mon Sep 17 00:00:00 2001 From: Vic Wong Date: Wed, 30 Dec 2020 18:02:32 -0800 Subject: [PATCH 17/17] Time for a version number "1.0.0" --- README.md | 3 ++- app.py | 2 ++ constants.py | 1 + templates/info.html | 7 +++++-- 4 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 constants.py diff --git a/README.md b/README.md index 59e20905..b5f958d3 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ If you want to support this project with a little monetary tip, it's much apprec - Refresh song list manually from the info screen. - Default download directories are now ~/pikaraoke-songs on MacOS, Windows, and Linux - General overdue cleanup of libraries +- Removed the --show-overlay feature since it is omxplayer only and not supported on pi4 ## Features @@ -19,7 +20,7 @@ If you want to support this project with a little monetary tip, it's much apprec - Searching song library via autocomplete - Adding new tracks from Youtube - Offline storage of video files -- mp3 + cdg file support (vlc only) +- mp3 + cdg support, including compressed .zip bundles (vlc only) - Pause/Skip/Restart and volume control - Now playing and Up Next display - Basic editing of downloaded file names diff --git a/app.py b/app.py index 84506de9..ad60b1a3 100644 --- a/app.py +++ b/app.py @@ -13,6 +13,7 @@ send_from_directory, url_for) import karaoke +from constants import VERSION from get_platform import get_platform from vlcclient import get_default_vlc_path @@ -354,6 +355,7 @@ def info(): disk=disk, youtubedl_version=youtubedl_version, show_shutdown=show_shutdown, + pikaraoke_version=VERSION ) diff --git a/constants.py b/constants.py new file mode 100644 index 00000000..369b4e07 --- /dev/null +++ b/constants.py @@ -0,0 +1 @@ +VERSION = "1.0.0" \ No newline at end of file diff --git a/templates/info.html b/templates/info.html index 5920655c..5936b16e 100644 --- a/templates/info.html +++ b/templates/info.html @@ -52,12 +52,13 @@

    System Info

  • Disk Usage: {{ disk }}
  • Memory: {{ memory }}
  • Youtube-dl version: {{ youtubedl_version }}
  • +
  • Pikaraoke version: {{ pikaraoke_version }}

Updates

-

Manually update the song list: +

Refresh the song list:

+

You should only need to do this if you manually copied files to the download directory while pikaraoke was running.

-

If downloads or searches stopped working, updating youtube-dl will probably fix it. This update link may fail if you don't have proper file permissions. Check the pikaraoke log for errors

+

If downloads or searches stopped working, updating youtube-dl will probably fix it. The current installed version is: "{{ youtubedl_version }}"

+

This update link above may fail if you don't have proper file permissions. Check the pikaraoke log for errors.

Shutdown