0
1
mirror of https://github.com/radio95-rnt/RadioPlayer.git synced 2026-02-27 06:03:52 +01:00
This commit is contained in:
Kuba
2025-10-18 19:51:14 +02:00
parent c2b2a63ce8
commit 40b299f2de
8 changed files with 86 additions and 76 deletions

29
modules/__init__.py Normal file
View 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

View File

@@ -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()

View File

@@ -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")

View File

@@ -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:

View File

@@ -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"

View File

@@ -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]]):

View File

@@ -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:

View File

@@ -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(full_module_name, module_path)
spec = importlib.util.spec_from_file_location(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()