Add a help message and set up attributes for choosing a playlist strategy.

This commit is contained in:
Brian Lee 2024-10-04 22:27:49 -07:00
parent 2d87cdd59e
commit 106c97ae41

View File

@ -1,5 +1,7 @@
import argparse
import json
import os
import sys
import logging
from collections import defaultdict
from datetime import datetime, timedelta, timezone
@ -94,9 +96,105 @@ def normalize_difficulty_name(difficulty_name):
# Return the mapped value or the original name if there is no mapping
return difficulty_names.get(difficulty_name, difficulty_name)
def playlist_strategy_scoresaber_oldscores(
api: ScoreSaberAPI,
song_count: int = 20 # Total number of songs to select
) -> List[Dict[str, Any]]:
"""Build and format a list of songs based on old scores from ScoreSaber, avoiding reusing the same song+difficulty."""
player_id = prompt_for_player_id()
history = load_history()
history.setdefault('scoresaber_oldscores', {})
scores_data = api.get_player_scores(player_id, use_cache=True)
all_scores = scores_data.get('playerScores', [])
if not all_scores:
logging.warning(f"No scores found for player ID {player_id}.")
return []
logging.debug(f"Found {len(all_scores)} scores for player ID {player_id}.")
# Sort scores by timeSet in ascending order (oldest first)
all_scores.sort(key=lambda x: x['score'].get('timeSet', ''))
playlist_data = []
current_time = datetime.now(timezone.utc)
for score in all_scores:
leaderboard = score.get('leaderboard', {})
song_id = leaderboard.get('songHash')
difficulty_raw = leaderboard.get('difficulty', {}).get('difficultyRaw', '')
if not song_id or not difficulty_raw:
logging.debug(f"Skipping score due to missing song_id or difficulty_raw: {score}")
continue # Skip if essential data is missing
# Calculate time ago
time_set_str = score['score'].get('timeSet')
if not time_set_str:
logging.debug(f"Skipping score due to missing timeSet: {score}")
continue # Skip if time_set is missing
try:
time_set = datetime.fromisoformat(time_set_str.replace('Z', '+00:00'))
except ValueError as e:
logging.error(f"Invalid time format for score ID {score['score'].get('id')}: {e}")
continue
time_difference = current_time - time_set
time_ago = format_time_ago(time_difference)
# Normalize the difficulty name
difficulty = normalize_difficulty_name(difficulty_raw)
game_mode = leaderboard.get('difficulty', {}).get('gameMode', 'Standard')
if 'Standard' in game_mode:
game_mode = 'Standard'
# Check history to avoid reusing song+difficulty
if song_id in history['scoresaber_oldscores'] and difficulty in history['scoresaber_oldscores'][song_id]:
logging.debug(f"Skipping song {song_id} with difficulty {difficulty} as it's in history.")
continue # Skip if already used
# Format the song data as expected by PlaylistBuilder
song_dict = {
'hash': song_id,
'songName': leaderboard.get('songName', 'Unknown'),
'difficulties': [
{
'name': difficulty,
'characteristic': game_mode
}
]
}
# Add the song to the playlist
playlist_data.append(song_dict)
logging.debug(f"Selected song for playlist: {song_dict['songName']} ({difficulty})")
# Log the song addition
mapper = "Unknown" # Mapper information can be added if available
logging.info(f"Song added: {song_dict['songName']} ({difficulty}), mapped by {mapper}. Last played {time_ago} ago.")
# Check if the desired number of songs has been reached
if len(playlist_data) >= song_count:
logging.debug(f"Reached the desired song count: {song_count}.")
break
# Log if no songs were added
if not playlist_data:
logging.info("No new songs found to add to the playlist based on history.")
else:
logging.info(f"Total songs added to playlist: {len(playlist_data)}")
# Update history to avoid reusing the same song+difficulty
for song in playlist_data:
song_id = song['hash']
difficulty_name = song['difficulties'][0]['name']
history['scoresaber_oldscores'].setdefault(song_id, []).append(difficulty_name)
save_history(history)
return playlist_data
def playlist_strategy_beatleader_oldscores(
api: BeatLeaderAPI,
song_count: int = 40
song_count: int = 20
) -> List[Dict[str, Any]]:
"""
Build and format a list of songs based on old scores from BeatLeader,
@ -186,12 +284,13 @@ def playlist_strategy_beatleader_oldscores(
return playlist_data
def saberlist(strategy='beatleader_oldscores') -> None:
def saberlist() -> None:
"""
Generate a playlist of songs from a range of difficulties, all with scores previously set a long time ago.
The range of difficulties ensures that the first few songs are good for warming up.
Avoids reusing the same song+difficulty in a playlist based on history.
"""
strategy = get_strategy()
if strategy == 'scoresaber_oldscores':
api = ScoreSaberAPI(cache_expiry_days=CACHE_EXPIRY_DAYS)
elif strategy == 'beatleader_oldscores':
@ -206,7 +305,7 @@ def saberlist(strategy='beatleader_oldscores') -> None:
if strategy == 'scoresaber_oldscores':
playlist_data = playlist_strategy_scoresaber_oldscores(api)
elif strategy == 'beatleader_oldscores':
playlist_data = playlist_strategy_beatleader_oldscores(api, song_count=40)
playlist_data = playlist_strategy_beatleader_oldscores(api)
if not playlist_data:
logging.info("No new scores found to add to the playlist.")
@ -218,19 +317,16 @@ def saberlist(strategy='beatleader_oldscores') -> None:
playlist_author="SaberList Tool"
)
"""
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Generate a playlist based on player scores.")
parser.add_argument(
'--strategy',
type=str,
default='beatleader_oldscores',
choices=['scoresaber_oldscores', 'beatleader_oldscores'],
help='Strategy to use for building the playlist.'
)
def get_strategy():
parser = argparse.ArgumentParser(description="Generate Beat Saber playlists")
parser.add_argument("-s", "--strategy",
choices=["scoresaber_oldscores", "beatleader_oldscores"],
help="Specify the playlist generation strategy",
required=True)
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
saberlist(strategy=args.strategy)
"""
return args.strategy