from . import log95, PlaylistModifierModule, Track, Path # The module loader injects this variable. # We assert it to satisfy static analysis and inform runtime about its presence. _log_out: log95.TextIO assert _log_out # pyright: ignore[reportUnboundVariable] def load_play_counts(file_path: Path) -> dict[str, int]: """ Loads the play counts from the file generated by the play_counter module. """ counts = {} try: with open(file_path, 'r') as file: for line in file: if line.strip() == "" or line.startswith(";"): continue try: key, value = line.split(':', 1) counts[key.strip()] = int(value.strip()) except ValueError: # Ignore lines with invalid formats continue return counts except FileNotFoundError: # It's okay if the file doesn't exist yet, means no counts have been recorded. return {} class GroupedSorterModule(PlaylistModifierModule): """ A playlist modifier that sorts tracks based on their play history but only within sequential groups of 3. """ def __init__(self) -> None: self.logger = log95.log95("GroupSort", output=_log_out) # The play_counter file is located in the parent directory of the 'modules' folder. self.play_counts_file = Path(__file__, "..", "..", "play_counter").resolve() def modify(self, global_args: dict, playlist: list[Track]) -> list[Track]: self.logger.info("Applying grouped sorting to the playlist...") play_counts = load_play_counts(self.play_counts_file) if not play_counts: self.logger.info("Play counter file not found or is empty. No sorting will be applied.") return playlist group_size = 3 # Iterate through the playlist in steps of 'group_size' for i in range(0, len(playlist), group_size): # Get the current group (slice) of tracks group = playlist[i:i+group_size] # Sort this smaller group based on play count, from least played to most played. # Tracks not in play_counts are given a count of 0. sorted_group = sorted(group, key=lambda track: play_counts.get(track.path.as_posix(), 0)) # Replace the original slice in the playlist with the newly sorted group playlist[i:i+group_size] = sorted_group self.logger.info("Grouped sorting complete.") return playlist # The radioPlayer will look for a 'playlistmod' variable to load the module. # playlistmod = (GroupedSorterModule(), 1)