From 40b299f2decb455084de3562817be701f6c45180 Mon Sep 17 00:00:00 2001 From: Kuba <132459354+KubaPro010@users.noreply.github.com> Date: Sat, 18 Oct 2025 19:51:14 +0200 Subject: [PATCH] reforms! --- modules/__init__.py | 29 ++++++++++++++++ modules/active_modifier.py | 30 +++++++++++----- modules/advisor.py | 12 +------ modules/jingle.py | 4 +-- modules/rds.py | 7 +--- modules/shuffler.py | 4 +-- modules/write_playlists.py | 6 +--- radioPlayer.py | 70 ++++++++++++++++---------------------- 8 files changed, 86 insertions(+), 76 deletions(-) create mode 100644 modules/__init__.py diff --git a/modules/__init__.py b/modules/__init__.py new file mode 100644 index 0000000..480d4fe --- /dev/null +++ b/modules/__init__.py @@ -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 \ No newline at end of file diff --git a/modules/active_modifier.py b/modules/active_modifier.py index f64baab..4726e30 100644 --- a/modules/active_modifier.py +++ b/modules/active_modifier.py @@ -1,12 +1,14 @@ -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 - """ - """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 - +from . import ActiveModifier 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") @@ -50,6 +52,18 @@ class Module(ActiveModifier): return self.last_track, True elif len(self.originals): self.last_track = self.originals.pop(0) 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 activemod = Module() \ No newline at end of file diff --git a/modules/advisor.py b/modules/advisor.py index 0b8d6d8..c8a0616 100644 --- a/modules/advisor.py +++ b/modules/advisor.py @@ -1,14 +1,4 @@ -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 - +from . import PlaylistAdvisor import os, datetime, log95 logger = log95.log95("ADVISOR") diff --git a/modules/jingle.py b/modules/jingle.py index e4962b3..008f98e 100644 --- a/modules/jingle.py +++ b/modules/jingle.py @@ -8,9 +8,7 @@ Reacts to the 'no_jingle' argument, for global usage it does not add jingles to import random -class PlaylistModifierModule: - def modify(self, global_args: dict, playlist: list[tuple[str, bool, bool, bool, dict]]): - return playlist +from . import PlaylistModifierModule class Module(PlaylistModifierModule): def __init__(self, file: str) -> None: diff --git a/modules/rds.py b/modules/rds.py index 458f72e..04db761 100644 --- a/modules/rds.py +++ b/modules/rds.py @@ -1,9 +1,4 @@ -class 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 - +from . import PlayerModule import socket, re, log95, os name_table_path = "/home/user/mixes/name_table.txt" diff --git a/modules/shuffler.py b/modules/shuffler.py index f3fdd89..04a3d8f 100644 --- a/modules/shuffler.py +++ b/modules/shuffler.py @@ -1,8 +1,6 @@ import random -class PlaylistModifierModule: - def modify(self, global_args: dict, playlist: list[tuple[str, bool, bool, bool, dict]]): - return playlist +from . import PlaylistModifierModule class Module(PlaylistModifierModule): def modify(self, global_args: dict, playlist: list[tuple[str, bool, bool, bool, dict]]): diff --git a/modules/write_playlists.py b/modules/write_playlists.py index f90bdc9..51e0316 100644 --- a/modules/write_playlists.py +++ b/modules/write_playlists.py @@ -1,8 +1,4 @@ -class 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 +from . import PlayerModule class Module(PlayerModule): def __init__(self) -> None: diff --git a/radioPlayer.py b/radioPlayer.py index c978105..cbc51c8 100644 --- a/radioPlayer.py +++ b/radioPlayer.py @@ -7,36 +7,7 @@ import unidecode from dataclasses import dataclass import log95 from pathlib import Path - -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 +from modules import * simple_modules: list[PlayerModule] = [] playlist_modifier_modules: list[PlaylistModifierModule] = [] @@ -44,7 +15,8 @@ playlist_advisor: PlaylistAdvisor | None = None active_modifier: ActiveModifier | None = None 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() 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() return - old_track_tuple = playlist[song_i] + old_track_tuple = playlist[song_i % len(playlist)] if active_modifier: 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)) if extend: max_iterator += 1 else: @@ -275,25 +250,39 @@ def main(): if filename.endswith(".py") and filename != "__init__.py": module_name = filename[:-3] module_path = MODULES_DIR / filename - - # Load module from file path directly - spec = importlib.util.spec_from_file_location(module_name, module_path) + full_module_name = f"{MODULES_PACKAGE}.{module_name}" + + spec = importlib.util.spec_from_file_location(full_module_name, module_path) if not spec: continue 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 spec.loader.exec_module(module) if md := getattr(module, "module", None): - simple_modules.append(md) - elif md := getattr(module, "playlistmod", None): + if isinstance(md, list): simple_modules.extend(md) + else: simple_modules.append(md) + if md := getattr(module, "playlistmod", None): if isinstance(md, tuple): 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) - elif md := getattr(module, "advisor", None): + if md := getattr(module, "advisor", None): if playlist_advisor: raise Exception("Multiple playlist advisors") playlist_advisor = md - elif md := getattr(module, "activemod", None): + if md := getattr(module, "activemod", None): if active_modifier: raise Exception("Multiple active modifiers") active_modifier = md @@ -311,3 +300,4 @@ def main(): procman.stop_all() raise finally: procman.stop_all() +main() \ No newline at end of file