You've already forked RadioPlayer
mirror of
https://github.com/radio95-rnt/RadioPlayer.git
synced 2026-02-27 06:03:52 +01:00
reforms!
This commit is contained in:
29
modules/__init__.py
Normal file
29
modules/__init__.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
class PlayerModule:
|
||||||
|
"""
|
||||||
|
Simple passive observer, this allows you to send the current track the your RDS encoder, or to your website
|
||||||
|
"""
|
||||||
|
def on_new_playlist(self, playlist: list[tuple[str, bool, bool, bool, dict[str, str]]]):
|
||||||
|
"""Tuple consists of the track path, to fade out, fade in, official, and args"""
|
||||||
|
pass
|
||||||
|
def on_new_track(self, index: int, track: str, to_fade_in: bool, to_fade_out: bool, official: bool): pass
|
||||||
|
class PlaylistModifierModule:
|
||||||
|
"""
|
||||||
|
Playlist modifier, this type of module allows you to shuffle, or put jingles into your playlist
|
||||||
|
"""
|
||||||
|
def modify(self, global_args: dict, playlist: list[tuple[str, bool, bool, bool, dict[str, str]]]): return playlist
|
||||||
|
class PlaylistAdvisor:
|
||||||
|
"""
|
||||||
|
Only one of a playlist advisor can be loaded. This module picks the playlist file to play, this can be a scheduler or just a static file
|
||||||
|
"""
|
||||||
|
def advise(self, arguments: str | None) -> str: return "/path/to/playlist.txt"
|
||||||
|
def new_playlist(self) -> int:
|
||||||
|
"""
|
||||||
|
Whether to play a new playlist, if this is 1, then the player will refresh, if this is two then the player will refresh quietly
|
||||||
|
"""
|
||||||
|
return 0
|
||||||
|
class ActiveModifier:
|
||||||
|
"""
|
||||||
|
This changes the next song to be played live, which means that this picks the next song, not the playlist, but this is affected by the playlist
|
||||||
|
"""
|
||||||
|
def play(self, index:int, track: tuple[str, bool, bool, bool, dict[str, str]]) -> tuple[tuple[str, bool, bool, bool, dict[str, str]], bool] | tuple[None, None]: return track, False
|
||||||
|
def on_new_playlist(self, playlist: list[tuple[str, bool, bool, bool, dict[str, str]]]): pass
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
class ActiveModifier:
|
from . import ActiveModifier
|
||||||
"""
|
|
||||||
This changes the next song to be played live, which means that this picks the next song, not the playlist, but this is affected by the playlist
|
|
||||||
"""
|
|
||||||
"""Tuple consists of the track path, to fade out, fade in, official, and args"""
|
|
||||||
def play(self, index: int, track: tuple[str, bool, bool, bool, dict[str, str]]): return track, False
|
|
||||||
def on_new_playlist(self, playlist: list[tuple[str, bool, bool, bool, dict[str, str]]]): pass
|
|
||||||
|
|
||||||
import os, log95
|
import os, log95
|
||||||
|
import subprocess
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from .advisor import MORNING_START, DAY_END
|
||||||
|
|
||||||
|
def get_audio_duration(file_path):
|
||||||
|
result = subprocess.run(['ffprobe', '-v', 'quiet', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', file_path], capture_output=True, text=True)
|
||||||
|
if result.returncode == 0: return float(result.stdout.strip())
|
||||||
|
return None
|
||||||
|
|
||||||
logger = log95.log95("AC-MOD")
|
logger = log95.log95("AC-MOD")
|
||||||
|
|
||||||
@@ -50,6 +52,18 @@ class Module(ActiveModifier):
|
|||||||
return self.last_track, True
|
return self.last_track, True
|
||||||
elif len(self.originals): self.last_track = self.originals.pop(0)
|
elif len(self.originals): self.last_track = self.originals.pop(0)
|
||||||
else: self.last_track = track
|
else: self.last_track = track
|
||||||
|
|
||||||
|
if last_track_duration := get_audio_duration(self.last_track[0]):
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
timestamp = now.timestamp() + last_track_duration
|
||||||
|
future = datetime.datetime.fromtimestamp(timestamp)
|
||||||
|
if now.hour < MORNING_START and future.hour > MORNING_START:
|
||||||
|
return None, None
|
||||||
|
elif now.hour < DAY_END and future.hour > DAY_END:
|
||||||
|
return None, None
|
||||||
|
elif future.day > now.day: # late night goes mid day, as it starts at midnight
|
||||||
|
return None, None
|
||||||
|
|
||||||
return self.last_track, False
|
return self.last_track, False
|
||||||
|
|
||||||
activemod = Module()
|
activemod = Module()
|
||||||
@@ -1,14 +1,4 @@
|
|||||||
class PlaylistAdvisor:
|
from . import PlaylistAdvisor
|
||||||
"""
|
|
||||||
Only one of a playlist advisor can be loaded. This module picks the playlist file to play, this can be a scheduler or just a static file
|
|
||||||
"""
|
|
||||||
def advise(self, arguments: str | None) -> str: return "/path/to/playlist.txt"
|
|
||||||
def new_playlist(self) -> int:
|
|
||||||
"""
|
|
||||||
Whether to play a new playlist, if this is 1, then the player will refresh, if this is two then the player will refresh quietly
|
|
||||||
"""
|
|
||||||
return 0
|
|
||||||
|
|
||||||
import os, datetime, log95
|
import os, datetime, log95
|
||||||
|
|
||||||
logger = log95.log95("ADVISOR")
|
logger = log95.log95("ADVISOR")
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ Reacts to the 'no_jingle' argument, for global usage it does not add jingles to
|
|||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
class PlaylistModifierModule:
|
from . import PlaylistModifierModule
|
||||||
def modify(self, global_args: dict, playlist: list[tuple[str, bool, bool, bool, dict]]):
|
|
||||||
return playlist
|
|
||||||
|
|
||||||
class Module(PlaylistModifierModule):
|
class Module(PlaylistModifierModule):
|
||||||
def __init__(self, file: str) -> None:
|
def __init__(self, file: str) -> None:
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
class PlayerModule:
|
from . import PlayerModule
|
||||||
def on_new_playlist(self, playlist: list[tuple[str, bool, bool, bool, dict]]):
|
|
||||||
pass
|
|
||||||
def on_new_track(self, index: int, track: str, to_fade_in: bool, to_fade_out: bool, official: bool):
|
|
||||||
pass
|
|
||||||
|
|
||||||
import socket, re, log95, os
|
import socket, re, log95, os
|
||||||
|
|
||||||
name_table_path = "/home/user/mixes/name_table.txt"
|
name_table_path = "/home/user/mixes/name_table.txt"
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
class PlaylistModifierModule:
|
from . import PlaylistModifierModule
|
||||||
def modify(self, global_args: dict, playlist: list[tuple[str, bool, bool, bool, dict]]):
|
|
||||||
return playlist
|
|
||||||
|
|
||||||
class Module(PlaylistModifierModule):
|
class Module(PlaylistModifierModule):
|
||||||
def modify(self, global_args: dict, playlist: list[tuple[str, bool, bool, bool, dict]]):
|
def modify(self, global_args: dict, playlist: list[tuple[str, bool, bool, bool, dict]]):
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
class PlayerModule:
|
from . import PlayerModule
|
||||||
def on_new_playlist(self, playlist: list[tuple[str, bool, bool, bool, dict]]):
|
|
||||||
pass
|
|
||||||
def on_new_track(self, index: int, track: str, to_fade_in: bool, to_fade_out: bool, official: bool):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class Module(PlayerModule):
|
class Module(PlayerModule):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|||||||
@@ -7,36 +7,7 @@ import unidecode
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import log95
|
import log95
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from modules import *
|
||||||
class PlayerModule:
|
|
||||||
"""
|
|
||||||
Simple passive observer, this allows you to send the current track the your RDS encoder, or to your website
|
|
||||||
"""
|
|
||||||
def on_new_playlist(self, playlist: list[tuple[str, bool, bool, bool, dict[str, str]]]):
|
|
||||||
"""Tuple consists of the track path, to fade out, fade in, official, and args"""
|
|
||||||
pass
|
|
||||||
def on_new_track(self, index: int, track: str, to_fade_in: bool, to_fade_out: bool, official: bool): pass
|
|
||||||
class PlaylistModifierModule:
|
|
||||||
"""
|
|
||||||
Playlist modifier, this type of module allows you to shuffle, or put jingles into your playlist
|
|
||||||
"""
|
|
||||||
def modify(self, global_args: dict, playlist: list[tuple[str, bool, bool, bool, dict[str, str]]]): return playlist
|
|
||||||
class PlaylistAdvisor:
|
|
||||||
"""
|
|
||||||
Only one of a playlist advisor can be loaded. This module picks the playlist file to play, this can be a scheduler or just a static file
|
|
||||||
"""
|
|
||||||
def advise(self, arguments: str | None) -> str: return "/path/to/playlist.txt"
|
|
||||||
def new_playlist(self) -> int:
|
|
||||||
"""
|
|
||||||
Whether to play a new playlist, if this is 1, then the player will refresh, if this is two then the player will refresh quietly
|
|
||||||
"""
|
|
||||||
return 0
|
|
||||||
class ActiveModifier:
|
|
||||||
"""
|
|
||||||
This changes the next song to be played live, which means that this picks the next song, not the playlist, but this is affected by the playlist
|
|
||||||
"""
|
|
||||||
def play(self, index:int, track: tuple[str, bool, bool, bool, dict[str, str]]): return track, False
|
|
||||||
def on_new_playlist(self, playlist: list[tuple[str, bool, bool, bool, dict[str, str]]]): pass
|
|
||||||
|
|
||||||
simple_modules: list[PlayerModule] = []
|
simple_modules: list[PlayerModule] = []
|
||||||
playlist_modifier_modules: list[PlaylistModifierModule] = []
|
playlist_modifier_modules: list[PlaylistModifierModule] = []
|
||||||
@@ -44,7 +15,8 @@ playlist_advisor: PlaylistAdvisor | None = None
|
|||||||
active_modifier: ActiveModifier | None = None
|
active_modifier: ActiveModifier | None = None
|
||||||
|
|
||||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||||
MODULES_DIR = SCRIPT_DIR / "modules"
|
MODULES_PACKAGE = "modules"
|
||||||
|
MODULES_DIR = SCRIPT_DIR / MODULES_PACKAGE
|
||||||
MODULES_DIR = MODULES_DIR.resolve()
|
MODULES_DIR = MODULES_DIR.resolve()
|
||||||
|
|
||||||
def print_wait(ttw: float, frequency: float, duration: float=-1, prefix: str="", bias: float = 0):
|
def print_wait(ttw: float, frequency: float, duration: float=-1, prefix: str="", bias: float = 0):
|
||||||
@@ -228,9 +200,12 @@ def play_playlist(playlist_path):
|
|||||||
procman.wait_all()
|
procman.wait_all()
|
||||||
return
|
return
|
||||||
|
|
||||||
old_track_tuple = playlist[song_i]
|
old_track_tuple = playlist[song_i % len(playlist)]
|
||||||
if active_modifier:
|
if active_modifier:
|
||||||
track_tuple, extend = active_modifier.play(song_i, old_track_tuple)
|
track_tuple, extend = active_modifier.play(song_i, old_track_tuple)
|
||||||
|
if track_tuple is None:
|
||||||
|
song_i += 1
|
||||||
|
continue
|
||||||
logger.debug(repr(song_i), repr(old_track_tuple), repr(track_tuple), repr(old_track_tuple != track_tuple))
|
logger.debug(repr(song_i), repr(old_track_tuple), repr(track_tuple), repr(old_track_tuple != track_tuple))
|
||||||
if extend: max_iterator += 1
|
if extend: max_iterator += 1
|
||||||
else:
|
else:
|
||||||
@@ -275,25 +250,39 @@ def main():
|
|||||||
if filename.endswith(".py") and filename != "__init__.py":
|
if filename.endswith(".py") and filename != "__init__.py":
|
||||||
module_name = filename[:-3]
|
module_name = filename[:-3]
|
||||||
module_path = MODULES_DIR / filename
|
module_path = MODULES_DIR / filename
|
||||||
|
full_module_name = f"{MODULES_PACKAGE}.{module_name}"
|
||||||
# Load module from file path directly
|
|
||||||
spec = importlib.util.spec_from_file_location(module_name, module_path)
|
spec = importlib.util.spec_from_file_location(full_module_name, module_path)
|
||||||
if not spec: continue
|
if not spec: continue
|
||||||
module = importlib.util.module_from_spec(spec)
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
|
||||||
|
sys.modules[full_module_name] = module
|
||||||
|
|
||||||
|
if MODULES_PACKAGE not in sys.modules:
|
||||||
|
import types
|
||||||
|
parent = types.ModuleType(MODULES_PACKAGE)
|
||||||
|
parent.__path__ = [str(MODULES_DIR)]
|
||||||
|
parent.__package__ = MODULES_PACKAGE
|
||||||
|
sys.modules[MODULES_PACKAGE] = parent
|
||||||
|
|
||||||
|
module.__package__ = MODULES_PACKAGE
|
||||||
|
|
||||||
if not spec.loader: continue
|
if not spec.loader: continue
|
||||||
spec.loader.exec_module(module)
|
spec.loader.exec_module(module)
|
||||||
|
|
||||||
if md := getattr(module, "module", None):
|
if md := getattr(module, "module", None):
|
||||||
simple_modules.append(md)
|
if isinstance(md, list): simple_modules.extend(md)
|
||||||
elif md := getattr(module, "playlistmod", None):
|
else: simple_modules.append(md)
|
||||||
|
if md := getattr(module, "playlistmod", None):
|
||||||
if isinstance(md, tuple):
|
if isinstance(md, tuple):
|
||||||
md, index = md
|
md, index = md
|
||||||
playlist_modifier_modules.insert(index, md)
|
if isinstance(md, list): playlist_modifier_modules[index:index] = md
|
||||||
|
else: playlist_modifier_modules.insert(index, md)
|
||||||
else: playlist_modifier_modules.append(md)
|
else: playlist_modifier_modules.append(md)
|
||||||
elif md := getattr(module, "advisor", None):
|
if md := getattr(module, "advisor", None):
|
||||||
if playlist_advisor: raise Exception("Multiple playlist advisors")
|
if playlist_advisor: raise Exception("Multiple playlist advisors")
|
||||||
playlist_advisor = md
|
playlist_advisor = md
|
||||||
elif md := getattr(module, "activemod", None):
|
if md := getattr(module, "activemod", None):
|
||||||
if active_modifier: raise Exception("Multiple active modifiers")
|
if active_modifier: raise Exception("Multiple active modifiers")
|
||||||
active_modifier = md
|
active_modifier = md
|
||||||
|
|
||||||
@@ -311,3 +300,4 @@ def main():
|
|||||||
procman.stop_all()
|
procman.stop_all()
|
||||||
raise
|
raise
|
||||||
finally: procman.stop_all()
|
finally: procman.stop_all()
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user