Skip to content

Commit

Permalink
[FEATURE] Pikaraoke should not require multiple ports (proxy http req…
Browse files Browse the repository at this point in the history
…uests to ffmpeg streams) #303
  • Loading branch information
vicwomg committed Dec 28, 2023
1 parent cef2497 commit 54d9c91
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 28 deletions.
32 changes: 28 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ Raspberry Pi 3 and above. Anything else will likely be too slow.
Other pi considerations:

- Should be running Raspberry pi desktop OS if running headed, since it requires a browser
- 32-bit version of the OS is recommended. 64-bit seemed slower in my testing, but pi4 and above can probably handle it.
- 32-bit version of the Bookworm or latest OS is recommended. There have been reported issues with Bullseye and 64-bit seemed slower on the pi 3, but pi4 and above can probably handle it.
- Disable "screen blanking" in raspi-config if you want to prevent the display from turning off when idle
- Pi3 might struggle a bit with high-res video playback. Overclocking seems to help
- Pi 3 might struggle a bit with high-res video playback. Overclocking to 1300 seems to help

Works fine on modern Mac, PCs, and Linux!

Expand Down Expand Up @@ -142,7 +142,7 @@ Note that if your wifi/network is inactive pikaraoke will error out 10 seconds a

## Usage

May not be up to date, run `python3 app.py --help` for the latest:
May not be up to date, run `./pikaraoke.sh --help` for the latest:

```
usage: app.py [-h] [-p PORT] [-f FFMPEG_PORT] [-d DOWNLOAD_PATH] [-y YOUTUBEDL_PATH] [-v VOLUME] [-s SPLASH_DELAY] [-t SCREENSAVER_TIMEOUT]
Expand Down Expand Up @@ -182,7 +182,7 @@ options:
and system shutdown. If unspecified, everyone is an admin.
```

## Troubleshooting
## FAQ / Troubleshooting

### I'm not hearing audio out of the headphone jack

Expand Down Expand Up @@ -262,3 +262,27 @@ You'll need to add them manually by copying them to the root of your download fo
### 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.

### Video playback is choppy and slow on my Raspberry Pi 3

The Pi 3 might struggle with full screen video playback and transcoding of user-supplied high-resolution or high-framrerate video files. The files coming from the default configuration of yt-dlp should be fine, however. There are a couple of optimizations that can be made to make it usable:

I also found that 32-bit versions of the OS are faster than 64 bit on the pi 3.

Overclocking to 1300 also seemed to clear up any issues for our test hardware. Ensure your pi has sufficient cooling on the CPUs and memory (headsinks or even active cooling)

To overclock, edit your boot.config:

```
sudo nano /boot/config.txt
```

Add these lines:

```
arm_freq=1300
core_freq=500
gpu_freq=500
over_voltage=4
sdram_freq=500
```
45 changes: 25 additions & 20 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
import cherrypy
import flask_babel
import psutil
from flask import (Flask, flash, make_response, redirect, render_template,
request, send_file, url_for)
import requests
from flask import (Flask, Response, flash, make_response, redirect,
render_template, request, send_file, url_for)
from flask_babel import Babel
from flask_paginate import Pagination, get_page_parameter
from selenium import webdriver
Expand Down Expand Up @@ -608,6 +609,27 @@ def expand_fs():
flash("You don't have permission to resize the filesystem", "is-danger")
return redirect(url_for("home"))

# Proxy the video stream from ffmpeg to /stream/<path>, so pikaraoke works over a single port
@app.route('/stream/<path>', methods=["GET", "POST"])
def redirect_to_ffmpeg_stream(path): #NOTE var :path will be unused as all path we need will be read from :request ie from flask import request
res = requests.request( # ref. https://stackoverflow.com/a/36601467/248616
method = request.method,
url = request.url.replace(request.host_url, f'{k.ffmmpeg_url_base}/'),
headers = {k:v for k,v in request.headers if k.lower() != 'host'}, # exclude 'host' header
data = request.get_data(),
cookies = request.cookies,
allow_redirects = False,
)

#region exlcude some keys in :res response
excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection'] #NOTE we here exclude all "hop-by-hop headers" defined by RFC 2616 section 13.5.1 ref. https://www.rfc-editor.org/rfc/rfc2616#section-13.5.1
headers = [
(k,v) for k,v in res.raw.headers.items()
if k.lower() not in excluded_headers
]
#endregion exclude some keys in :res response
response = Response(res.content, res.status_code, headers)
return response

# Handle sigterm, apparently cherrypy won't shut down without explicit handling
signal.signal(signal.SIGTERM, lambda signum, stack_frame: k.stop())
Expand All @@ -616,24 +638,7 @@ def get_default_youtube_dl_path(platform):
if platform == "windows":
return os.path.join(os.path.dirname(__file__), ".venv\Scripts\yt-dlp.exe")
return os.path.join(os.path.dirname(__file__), ".venv/bin/yt-dlp")
# if platform == "windows":
# choco_ytdl_path = r"C:\ProgramData\chocolatey\bin\yt-dlp.exe"
# scoop_ytdl_path = os.path.expanduser(r"~\scoop\shims\yt-dlp.exe")
# if os.path.isfile(choco_ytdl_path):
# return choco_ytdl_path
# if os.path.isfile(scoop_ytdl_path):
# return scoop_ytdl_path
# return r"C:\Program Files\yt-dlp\yt-dlp.exe"
# default_ytdl_unix_path = "/usr/local/bin/yt-dlp"
# if platform == "osx":
# if os.path.isfile(default_ytdl_unix_path):
# return default_ytdl_unix_path
# else:
# # just a guess based on the default python 3 install in OSX monterey
# return "/Library/Frameworks/Python.framework/Versions/3.10/bin/yt-dlp"
# else:
# return default_ytdl_unix_path



def get_default_dl_dir(platform):
if is_raspberry_pi:
Expand Down
5 changes: 3 additions & 2 deletions karaoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,9 @@ def get_youtube_id_from_url(self, url):
def play_file(self, file_path, semitones=0):
logging.info(f"Playing file: {file_path} transposed {semitones} semitones")
stream_uid = int(time.time())
stream_url = f"{self.url_parsed.scheme}://{self.url_parsed.hostname}:{self.ffmpeg_port}/{stream_uid}"
# pass a 0.0.0.0 IP to ffmpeg which will work for both hostnames and direct IP access
# This is the stream URL that will be accessed by the splash screen client, Flask will proxy to the ffmpeg_url
stream_url = f"{self.url}/stream/{stream_uid}"
# Used by ffmpeg, pass a 0.0.0.0 IP to ffmpeg which will work for both hostnames and direct IP access
ffmpeg_url = f"http://0.0.0.0:{self.ffmpeg_port}/{stream_uid}"

pitch = 2**(semitones/12) #The pitch value is (2^x/12), where x represents the number of semitones
Expand Down
4 changes: 2 additions & 2 deletions templates/splash.html
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,8 @@
<p>
{# MSG: Prompt for interaction in order to enable video autoplay. #} {%
trans %}Due to limititations with browser permissions, you must interact
with the page once before it allows autoplay of videos. Pikaraoke will not
play otherwise. Click the button below to confirm.{% endtrans %}
with the page once before it allows videos to play. Click the button below
to continue.{% endtrans %}
</p>
<div class="has-text-centered">
<button
Expand Down

0 comments on commit 54d9c91

Please sign in to comment.