Skip to content

Commit

Permalink
better handling of temp dirs, remove debug logging, better stream rea…
Browse files Browse the repository at this point in the history
…diness detection
  • Loading branch information
vicwomg committed Dec 25, 2024
1 parent 6946917 commit 8663bc4
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 23 deletions.
9 changes: 3 additions & 6 deletions pikaraoke/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

from pikaraoke import VERSION, karaoke
from pikaraoke.constants import LANGUAGES
from pikaraoke.lib.file_resolver import get_tmp_dir
from pikaraoke.lib.get_platform import get_platform, is_raspberry_pi

try:
Expand Down Expand Up @@ -697,7 +698,7 @@ def expand_fs():
# Streams the file in chunks from the filesystem (chrome supports it, safari does not)
@app.route("/stream/<id>")
def stream(id):
file_path = f"/tmp/pikaraoke/{id}.mp4"
file_path = f"{get_tmp_dir()}/{id}.mp4"

def generate():
previous_size = -1
Expand All @@ -708,15 +709,12 @@ def generate():
current_size = os.path.getsize(file_path)
if current_size == previous_size:
# File size has stabilized, break the loop
print(f"**FILE SIZE STABILIZED {current_size}")
break
file.seek(position) # Move to the last read position
while True:
chunk = file.read(10240 * 100 * 30) # Read in 3mb chunks
if not chunk:
print(f"**CHUNK BREAK {len(chunk)}")
break # End of file reached
print(f"**YIELD CHUNK {len(chunk)}")
yield chunk
position += len(chunk) # Update the position with the size of the chunk
previous_size = current_size
Expand All @@ -729,7 +727,7 @@ def generate():
# (Safari compatible, but requires the ffmpeg transcoding to be complete to know file size)
@app.route("/stream/full/<id>")
def stream_full(id):
file_path = f"/tmp/pikaraoke/{id}.mp4"
file_path = f"{get_tmp_dir()}/{id}.mp4"
try:
file_size = os.path.getsize(file_path)
range_header = request.headers.get("Range", None)
Expand All @@ -745,7 +743,6 @@ def stream_full(id):
start = int(start)
end = int(end) if end else file_size - 1

print(f"***range header: {range_header} FILE_SIZE: {file_size}")
# Generate response with part of file
with open(file_path, "rb") as file:
file.seek(start)
Expand Down
29 changes: 19 additions & 10 deletions pikaraoke/karaoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import logging
import os
import random
import shutil
import socket
import subprocess
import time
from pathlib import Path
from queue import Queue
from queue import Empty, Queue
from subprocess import CalledProcessError, check_output
from threading import Thread
from urllib.parse import urlparse
Expand All @@ -16,7 +17,7 @@
import qrcode
from unidecode import unidecode

from pikaraoke.lib.file_resolver import FileResolver
from pikaraoke.lib.file_resolver import FileResolver, get_tmp_dir
from pikaraoke.lib.get_platform import (
get_ffmpeg_version,
get_os_version,
Expand Down Expand Up @@ -418,7 +419,7 @@ def log_ffmpeg_output(self):
def play_file(self, file_path, semitones=0):
logging.info(f"Playing file: {file_path} transposed {semitones} semitones")
stream_uid = int(time.time())
output_file = f"/tmp/pikaraoke/{stream_uid}.mp4"
output_file = f"{get_tmp_dir()}/{stream_uid}.mp4"
stream_url = f"{self.url}/stream/{stream_uid}"
# 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}"
Expand Down Expand Up @@ -453,13 +454,11 @@ def play_file(self, file_path, semitones=0):
# normalize the audio
audio = audio.filter("loudnorm", i=-16, tp=-1.5, lra=11) if self.normalize_audio else audio

# Ffmpeg outputs "Stream #0" when the stream is ready to consume
stream_ready_string = "Stream #"
# Ffmpeg outputs "out#0" when the stream is done transcoding
stream_ready_string = "out#0/mp4"

if fr.cdg_file_path != None: # handle CDG files
logging.info("Playing CDG/MP3 file: " + file_path)
# Ffmpeg outputs "Video: cdgraphics" when the stream is ready to consume
stream_ready_string = "Video: cdgraphics"
# copyts helps with sync issues, fps=25 prevents ffmpeg from needlessly encoding cdg at 300fps
cdg_input = ffmpeg.input(fr.cdg_file_path, copyts=None)
video = cdg_input.video.filter("fps", fps=25)
Expand Down Expand Up @@ -512,13 +511,20 @@ def play_file(self, file_path, semitones=0):
while self.ffmpeg_process.poll() is None:
try:
# Add a loop to check the size of output_file
output = self.ffmpeg_log.get_nowait()
output_file_size = os.path.getsize(output_file)
except FileNotFoundError:
logging.debug("[FFMPEG] " + decode_ignore(output))
logging.debug(f"Output file size: {output_file_size}")
except (FileNotFoundError, Empty, AttributeError):
# Handle the case where the file might not exist yet
time.sleep(0.1)
pass
else:
if output_file_size > 1048576: # 1MB in bytes
logging.debug("Stream ready!")
# Check if the stream is ready to play
# Determined by completed transcode stream_ready_string match
# or the file size being greater than a threshold
if (stream_ready_string in decode_ignore(output)) or (output_file_size > 4048576):
logging.debug(f"Stream ready! File size: {output_file_size}")
self.now_playing = self.filename_from_path(file_path)
self.now_playing_filename = file_path
self.now_playing_transpose = semitones
Expand All @@ -541,6 +547,7 @@ def play_file(self, file_path, semitones=0):
)
self.end_song()
break
time.sleep(0.1)

def kill_ffmpeg(self):
logging.debug("Killing ffmpeg process")
Expand All @@ -555,6 +562,8 @@ def end_song(self):
logging.info(f"Song ending: {self.now_playing}")
self.reset_now_playing()
self.kill_ffmpeg()
# delete the tmp dir
shutil.rmtree(get_tmp_dir())
logging.debug("ffmpeg process killed")

def transpose_current(self, semitones):
Expand Down
21 changes: 14 additions & 7 deletions pikaraoke/lib/file_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@
from pikaraoke.lib.get_platform import get_platform


def get_tmp_dir():
# Determine tmp directories (for things like extracted cdg files)
pid = os.getpid() # for scoping tmp directories to this process
if get_platform() == "windows":
tmp_dir = os.path.expanduser(r"~\\AppData\\Local\\Temp\\pikaraoke\\" + str(pid) + r"\\")
else:
tmp_dir = f"/tmp/pikaraoke/{pid}"
# create tmp_dir if it doesn't exist
if not os.path.exists(tmp_dir):
os.makedirs(tmp_dir)
return tmp_dir


# Processes a given file path and determines the file format and file path, extracting zips into cdg + mp3 if necessary.
class FileResolver:
file_path = None
Expand All @@ -14,13 +27,7 @@ class FileResolver:
pid = os.getpid() # for scoping tmp directories to this process

def __init__(self, file_path):
# Determine tmp directories (for things like extracted cdg files)
if get_platform() == "windows":
self.tmp_dir = os.path.expanduser(
r"~\\AppData\\Local\\Temp\\pikaraoke\\" + str(self.pid) + r"\\"
)
else:
self.tmp_dir = f"/tmp/pikaraoke/{self.pid}"
self.tmp_dir = get_tmp_dir()
self.resolved_file_path = self.process_file(file_path)

# Extract zipped cdg + mp3 files into a temporary directory, and set the paths to both files.
Expand Down

0 comments on commit 8663bc4

Please sign in to comment.