Fix race condition with file manager preloading.

This commit is contained in:
Matthew Stratford 2021-05-02 15:41:14 +01:00
parent 05b64fb317
commit c0cc6ff418
2 changed files with 74 additions and 32 deletions

View file

@ -22,6 +22,7 @@ import json
from logging import INFO, ERROR, WARNING
import os
import requests
import time
from baps_types.plan import PlanItem
from helpers.os_environment import resolve_external_file_path
@ -219,6 +220,8 @@ class MyRadioAPI:
# Now check if the file already exists
path: str = resolve_external_file_path("/music-tmp/")
dl_suffix = ".downloading"
if not os.path.isdir(path):
self._log("Music-tmp folder is missing, attempting to create.")
try:
@ -230,19 +233,42 @@ class MyRadioAPI:
filename: str = resolve_external_file_path(
"/music-tmp/{}-{}.{}".format(itemType, id, format)
)
# Check if we already downloaded the file. If we did, give that.
if os.path.isfile(filename):
self.logger.log.debug("Already got file. " + filename)
return (filename, False) if did_download else filename
# If something else (another channel, the preloader etc) is downloading the track, wait for it.
if os.path.isfile(filename + dl_suffix):
time_waiting_s = 0
self.logger.log.debug("Waiting for download to complete from another worker. " + filename)
while time_waiting_s < 20:
# TODO: Make something better here.
# If the connectivity is super poor or we're loading reeaaaalllly long files, this may be annoying, but this is just in case somehow the other api download gives up.
if os.path.isfile(filename):
# Now the file is downloaded successfully
return (filename, False) if did_download else filename
time_waiting_s +=1
self.logger.log.debug("Still waiting")
time.sleep(1)
# File doesn't exist, download it.
try:
# Just create the file to stop other sources from trying to download too.
open(filename + dl_suffix, "a").close()
except Exception:
self.logger.log.exception("Couldn't create new temp file.")
return (None, False) if did_download else None
request = await self.async_api_call(url, api_version="non")
if not request:
return (None, False) if did_download else None
try:
with open(filename, "wb") as file:
with open(filename + dl_suffix, "wb") as file:
file.write(await request)
os.rename(filename + dl_suffix, filename)
except Exception as e:
self._logException("Failed to write music file: {}".format(e))
return (None, False) if did_download else None

View file

@ -412,40 +412,56 @@ class Player:
break
# TODO: Update the show plan filenames???
try:
self.logger.log.info("Loading file: " +
str(loaded_item.filename))
mixer.music.load(loaded_item.filename)
except Exception:
# We couldn't load that file.
self.logger.log.exception(
"Couldn't load file: " + str(loaded_item.filename)
)
return False
try:
if ".mp3" in loaded_item.filename:
song = MP3(loaded_item.filename)
self.state.update("length", song.info.length)
else:
self.state.update(
"length", mixer.Sound(
loaded_item.filename).get_length() / 1000
load_attempt = 0
while load_attempt < 5:
load_attempt += 1
try:
self.logger.log.info("Loading file: " +
str(loaded_item.filename))
mixer.music.load(loaded_item.filename)
except Exception:
# We couldn't load that file.
self.logger.log.exception(
"Couldn't load file: " + str(loaded_item.filename)
)
except Exception:
self.logger.log.exception(
"Failed to update the length of item.")
return False
time.sleep(1)
continue # Try loading again.
if loaded_item.cue > 0:
self.seek(loaded_item.cue)
else:
self.seek(0)
if not self.isLoaded:
self.logger.log.error("Pygame loaded file without error, but never actually loaded.")
time.sleep(1)
continue # Try loading again.
if self.state.get()["play_on_load"]:
self.unpause()
try:
if ".mp3" in loaded_item.filename:
song = MP3(loaded_item.filename)
self.state.update("length", song.info.length)
else:
self.state.update(
"length", mixer.Sound(
loaded_item.filename).get_length() / 1000
)
except Exception:
self.logger.log.exception(
"Failed to update the length of item.")
time.sleep(1)
continue # Try loading again.
return True
# Everything worked, we made it!
if loaded_item.cue > 0:
self.seek(loaded_item.cue)
else:
self.seek(0)
if self.state.get()["play_on_load"]:
self.unpause()
return True
self.logger.log.error("Failed to load track after numerous retries.")
return False
return False
def unload(self):
if not self.isPlaying: