Add strategy to pick ranked scores of a given star level that haven't been played in awhile
This commit is contained in:
parent
3b1d66cb30
commit
faf42af274
@ -40,7 +40,7 @@ all_beatleader_ranked_maps = beatleader_api.get_player_scores(
|
||||
```python
|
||||
from helpers.SimpleScoreSaberAPI import SimpleScoreSaberAPI
|
||||
scoresaber_api = SimpleScoreSaberAPI()
|
||||
scoresaber_all_ranked_maps = scoresaber_api.get_ranked_maps(use_cache=False)
|
||||
scoresaber_ranked_maps = scoresaber_api.get_ranked_maps()
|
||||
```
|
||||
|
||||
## BeatSaverClient
|
||||
@ -57,7 +57,10 @@ map_data = beatsaver_api.get_maps(year=2024, month=9)
|
||||
from helpers.SimpleBeatLeaderAPI import SimpleBeatLeaderAPI
|
||||
player_id = "76561199407393962"
|
||||
beatleader_api = SimpleBeatLeaderAPI()
|
||||
data = beatleader_api.get_player_accgraph(player_id)
|
||||
|
||||
scores_data = beatleader_api.get_player_scores(player_id).get('data', [])
|
||||
|
||||
acc_graph = beatleader_api.get_player_accgraph(player_id)
|
||||
data[0]
|
||||
filtered_data = [{'acc': item['acc'], 'stars': item['stars'], 'hash': item['hash']} for item in data]
|
||||
filtered_data[0]
|
||||
|
@ -22,23 +22,21 @@ from helpers.SimpleScoreSaberAPI import SimpleScoreSaberAPI
|
||||
from helpers.SimpleBeatSaverAPI import SimpleBeatSaverAPI
|
||||
|
||||
from saberlist.utils import reset_history
|
||||
from saberlist.playlist_strategies.oldscores import (
|
||||
from saberlist.playlist_strategies.beatleader import (
|
||||
playlist_strategy_beatleader_oldscores,
|
||||
playlist_strategy_scoresaber_oldscores,
|
||||
playlist_strategy_beatleader_oldscores_stars,
|
||||
playlist_strategy_ranked_both,
|
||||
)
|
||||
from saberlist.playlist_strategies.scoresaber import (
|
||||
playlist_strategy_scoresaber_oldscores,
|
||||
playlist_strategy_scoresaber_ranked,
|
||||
)
|
||||
from saberlist.playlist_strategies.accuracy import (
|
||||
playlist_strategy_beatleader_lowest_acc,
|
||||
playlist_strategy_beatleader_accuracy_gaps,
|
||||
playlist_strategy_scoresaber_accuracy_gaps,
|
||||
playlist_strategy_beatleader_accuracy_gaps_star_range,
|
||||
)
|
||||
from saberlist.playlist_strategies.performance import (
|
||||
playlist_strategy_beatleader_lowest_pp,
|
||||
playlist_strategy_scoresaber_lowest_pp,
|
||||
)
|
||||
from saberlist.playlist_strategies.beatsaver import (
|
||||
playlist_strategy_beatsaver_acc,
|
||||
playlist_strategy_beatsaver_curated,
|
||||
playlist_strategy_beatsaver_mappers,
|
||||
)
|
||||
@ -63,12 +61,26 @@ def saberlist() -> None:
|
||||
)
|
||||
playlist_builder = PlaylistBuilder()
|
||||
|
||||
elif strategy == 'beatleader_oldscores_stars':
|
||||
stars = input("Enter star level (Default: 6.0): ") or 6.0
|
||||
playlist_data, playlist_title = playlist_strategy_beatleader_oldscores_stars(
|
||||
SimpleBeatLeaderAPI(cache_expiry_days=CACHE_EXPIRY_DAYS),
|
||||
stars=float(stars)
|
||||
)
|
||||
playlist_builder = PlaylistBuilder(covers_dir='./covers/pajamas')
|
||||
|
||||
elif strategy == 'beatleader_oldscores':
|
||||
playlist_data, playlist_title = playlist_strategy_beatleader_oldscores(
|
||||
BeatLeaderAPI(cache_expiry_days=CACHE_EXPIRY_DAYS)
|
||||
)
|
||||
playlist_builder = PlaylistBuilder()
|
||||
|
||||
elif strategy == 'scoresaber_ranked':
|
||||
playlist_data, playlist_title = playlist_strategy_scoresaber_ranked(
|
||||
SimpleScoreSaberAPI(cache_expiry_days=CACHE_EXPIRY_DAYS)
|
||||
)
|
||||
playlist_builder = PlaylistBuilder(covers_dir='./covers/scoresaber')
|
||||
|
||||
elif strategy == 'ranked_both':
|
||||
playlist_data, playlist_title = playlist_strategy_ranked_both(
|
||||
SimpleBeatLeaderAPI(cache_expiry_days=CACHE_EXPIRY_DAYS),
|
||||
@ -143,6 +155,13 @@ def parse_args_subcommands():
|
||||
action="store_true",
|
||||
help="Reset the history for scoresaber_oldscores")
|
||||
|
||||
# -------- beatleader_oldscores_stars --------
|
||||
parser_bl_old_stars = subparsers.add_parser("beatleader_oldscores_stars",
|
||||
help="Generate a playlist using BeatLeader old-scores-stars strategy")
|
||||
parser_bl_old_stars.add_argument("-r", "--reset",
|
||||
action="store_true",
|
||||
help="Reset the history for beatleader_oldscores_stars")
|
||||
|
||||
# -------- beatleader_oldscores --------
|
||||
parser_bl_old = subparsers.add_parser("beatleader_oldscores",
|
||||
help="Generate a playlist using BeatLeader old-scores strategy")
|
||||
@ -150,6 +169,13 @@ def parse_args_subcommands():
|
||||
action="store_true",
|
||||
help="Reset the history for beatleader_oldscores")
|
||||
|
||||
# -------- scoresaber_ranked --------
|
||||
parser_ss_ranked = subparsers.add_parser("scoresaber_ranked",
|
||||
help="Generate a playlist using scoresaber_ranked strategy")
|
||||
parser_ss_ranked.add_argument("-r", "--reset",
|
||||
action="store_true",
|
||||
help="Reset the history for scoresaber_ranked")
|
||||
|
||||
# -------- ranked_both --------
|
||||
parser_ranked_both = subparsers.add_parser("ranked_both",
|
||||
help="Generate a playlist using ranked_both strategy")
|
||||
|
@ -13,7 +13,6 @@ logging.basicConfig(
|
||||
level=LOG_LEVEL
|
||||
)
|
||||
|
||||
from helpers.ScoreSaberAPI import ScoreSaberAPI
|
||||
from helpers.BeatLeaderAPI import BeatLeaderAPI
|
||||
from helpers.SimpleBeatLeaderAPI import SimpleBeatLeaderAPI
|
||||
from helpers.SimpleScoreSaberAPI import SimpleScoreSaberAPI
|
||||
@ -105,6 +104,127 @@ def playlist_strategy_ranked_both(
|
||||
|
||||
return playlist_data, f"ranked_both-{new_count:02d}"
|
||||
|
||||
def playlist_strategy_beatleader_oldscores_stars(
|
||||
beatleader_api: SimpleBeatLeaderAPI,
|
||||
song_count: int = 40,
|
||||
stars: float = 6.0
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Build and format a list of songs from BeatLeader that have a star rating
|
||||
between 'stars' (default: 6) and stars+1 (exclusive). Avoid reusing the same
|
||||
song+difficulty from history.
|
||||
|
||||
Args:
|
||||
beatleader_api (SimpleBeatLeaderAPI): API interface to fetch player scores.
|
||||
song_count (int, optional): Number of songs to return in the playlist. Defaults to 20.
|
||||
stars (float, optional): Starting range of star rating (x). Only songs with
|
||||
x <= star < x+1 are included. Defaults to 6.0.
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]: A playlist list of dictionary items suitable for building a playlist
|
||||
(along with a formatted playlist identifier).
|
||||
"""
|
||||
player_id = prompt_for_player_id()
|
||||
history = load_history()
|
||||
history.setdefault('beatleader_oldscores_stars', {})
|
||||
history.setdefault('playlist_counts', {})
|
||||
|
||||
# Get the current count for the star-based strategy and increment it
|
||||
count_key = 'beatleader_oldscores_stars'
|
||||
current_count = history['playlist_counts'].get(count_key, 0)
|
||||
new_count = current_count + 1
|
||||
history['playlist_counts'][count_key] = new_count
|
||||
|
||||
# Fetch player scores
|
||||
scores_data = beatleader_api.get_player_scores(player_id).get('data', [])
|
||||
if not scores_data:
|
||||
logging.warning(f"No scores found for player ID {player_id} on BeatLeader.")
|
||||
return [], ""
|
||||
|
||||
logging.debug(f"Found {len(scores_data)} total scores for player ID {player_id} on BeatLeader.")
|
||||
|
||||
# Sort scores by epochTime in ascending order (if you want "oldest" style, similar to oldscores).
|
||||
# Remove this sort if you prefer a different ordering.
|
||||
scores_data.sort(key=lambda x: x.get('timepost', 0))
|
||||
|
||||
playlist_data = []
|
||||
current_time = datetime.now(timezone.utc)
|
||||
|
||||
for score_entry in scores_data:
|
||||
if len(playlist_data) >= song_count:
|
||||
break # Stop if we've reached the desired number of songs
|
||||
|
||||
leaderboard = score_entry.get('leaderboard', {})
|
||||
difficulty_data = leaderboard.get('difficulty', {})
|
||||
star_rating = difficulty_data.get('stars')
|
||||
song_hash = leaderboard.get('song', {}).get('hash')
|
||||
difficulty_raw = difficulty_data.get('value')
|
||||
game_mode = difficulty_data.get('modeName', 'Standard')
|
||||
epoch_time = score_entry.get('timepost')
|
||||
|
||||
# Skip if we have missing data
|
||||
if not song_hash or not difficulty_raw or star_rating is None:
|
||||
continue
|
||||
|
||||
# Filter by star rating range [stars, stars + 1)
|
||||
if not (stars <= star_rating < stars + 1):
|
||||
continue
|
||||
|
||||
# Normalize difficulty name
|
||||
difficulty = normalize_difficulty_name(difficulty_raw)
|
||||
|
||||
# Avoid reusing song+difficulty
|
||||
if (song_hash in history['beatleader_oldscores_stars'] and
|
||||
difficulty in history['beatleader_oldscores_stars'][song_hash]):
|
||||
logging.debug(f"Skipping song {song_hash} (difficulty {difficulty}) as it's in history.")
|
||||
continue
|
||||
|
||||
# Calculate how long ago the score was set
|
||||
try:
|
||||
time_set = datetime.fromtimestamp(epoch_time, tz=timezone.utc)
|
||||
except (ValueError, OSError) as e:
|
||||
logging.error(f"Invalid epochTime for this score: {e}")
|
||||
continue
|
||||
time_difference = current_time - time_set
|
||||
time_ago = format_time_ago(time_difference)
|
||||
|
||||
# Format the song data for PlaylistBuilder
|
||||
song_dict = {
|
||||
'hash': song_hash,
|
||||
'difficulties': [
|
||||
{
|
||||
'name': difficulty,
|
||||
'characteristic': game_mode
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Add to playlist
|
||||
playlist_data.append(song_dict)
|
||||
logging.debug(
|
||||
f"Selected song for playlist: Hash={song_hash}, Difficulty={difficulty}, "
|
||||
f"Stars={star_rating:.2f}. Last played {time_ago} ago."
|
||||
)
|
||||
|
||||
# Update history
|
||||
history['beatleader_oldscores_stars'].setdefault(song_hash, []).append(difficulty)
|
||||
|
||||
if len(playlist_data) >= song_count:
|
||||
break
|
||||
|
||||
if not playlist_data:
|
||||
logging.info("No new songs found to add to the playlist based on history for BeatLeader (star-based).")
|
||||
else:
|
||||
for song in playlist_data:
|
||||
song_hash = song['hash']
|
||||
diff_name = song['difficulties'][0]['name']
|
||||
logging.info(f"Song added: Hash={song_hash}, Difficulty={diff_name}.")
|
||||
logging.info(f"Total songs added to playlist from BeatLeader (star-based): {len(playlist_data)}")
|
||||
|
||||
save_history(history)
|
||||
|
||||
return playlist_data, f"beatleader_oldscores_stars-{new_count:02d}"
|
||||
|
||||
def playlist_strategy_beatleader_oldscores(
|
||||
api: BeatLeaderAPI,
|
||||
song_count: int = 20
|
||||
@ -196,106 +316,3 @@ def playlist_strategy_beatleader_oldscores(
|
||||
save_history(history)
|
||||
|
||||
return playlist_data, f"beatleader_oldscores-{new_count:02d}"
|
||||
|
||||
def playlist_strategy_scoresaber_oldscores(
|
||||
api: ScoreSaberAPI,
|
||||
song_count: int = 20
|
||||
) -> 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', {})
|
||||
history.setdefault('playlist_counts', {})
|
||||
|
||||
# Get the current count for ScoreSaber old scores and increment it
|
||||
count_key = 'scoresaber_oldscores'
|
||||
current_count = history['playlist_counts'].get(count_key, 0)
|
||||
new_count = current_count + 1
|
||||
history['playlist_counts'][count_key] = new_count
|
||||
|
||||
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, f"scoresaber_oldscores-{new_count:02d}"
|
214
src/saberlist/playlist_strategies/scoresaber.py
Normal file
214
src/saberlist/playlist_strategies/scoresaber.py
Normal file
@ -0,0 +1,214 @@
|
||||
from typing import Dict, Any, List, Tuple
|
||||
from datetime import datetime, timezone
|
||||
import logging
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO').upper()
|
||||
|
||||
import logging
|
||||
logging.basicConfig(
|
||||
format='%(asctime)s %(levelname)s: %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S',
|
||||
level=LOG_LEVEL
|
||||
)
|
||||
|
||||
from helpers.ScoreSaberAPI import ScoreSaberAPI
|
||||
from helpers.SimpleScoreSaberAPI import SimpleScoreSaberAPI
|
||||
|
||||
from saberlist.utils import load_history, save_history, normalize_difficulty_name, format_time_ago, prompt_for_player_id
|
||||
|
||||
def playlist_strategy_scoresaber_ranked(
|
||||
scoresaber_api: SimpleScoreSaberAPI,
|
||||
min_star: float = 7.0,
|
||||
max_star: float = 10.0,
|
||||
song_count: int = 40
|
||||
) -> Tuple[List[Dict[str, Any]], str]:
|
||||
"""
|
||||
Build and format a list of the latest ranked songs from ScoreSaber within the given star range,
|
||||
avoiding reusing the same song+difficulty.
|
||||
Returns:
|
||||
(playlist_data, playlist_identifier):
|
||||
playlist_data: A list of playlist songs.
|
||||
playlist_identifier: A formatted string identifying this playlist version.
|
||||
"""
|
||||
history = load_history()
|
||||
# Set up the history keys if they don't exist
|
||||
history.setdefault('scoresaber_ranked', {})
|
||||
history.setdefault('playlist_counts', {})
|
||||
|
||||
# Prepare a unique key for this strategy and update its usage count
|
||||
count_key = 'scoresaber_ranked'
|
||||
current_count = history['playlist_counts'].get(count_key, 0)
|
||||
new_count = current_count + 1
|
||||
history['playlist_counts'][count_key] = new_count
|
||||
|
||||
# Fetch ScoreSaber ranked maps within the desired star range
|
||||
logging.debug(f"Fetching ScoreSaber ranked maps with star range [{min_star}, {max_star}]...")
|
||||
ranked_maps = scoresaber_api.get_ranked_maps(
|
||||
min_star=min_star,
|
||||
max_star=max_star
|
||||
)
|
||||
|
||||
# Filter maps that are actually ranked, and sort by rankedDate descending (latest first)
|
||||
filtered_maps = [
|
||||
m for m in ranked_maps
|
||||
if m.get('ranked') is True and m.get('rankedDate') is not None
|
||||
]
|
||||
filtered_maps.sort(key=lambda x: x['rankedDate'], reverse=True)
|
||||
logging.info(f"Retrieved {len(filtered_maps)} ranked maps from ScoreSaber in the star range.")
|
||||
|
||||
playlist_data = []
|
||||
|
||||
for map_data in filtered_maps:
|
||||
if len(playlist_data) >= song_count:
|
||||
logging.debug(f"Reached the desired song count: {song_count}.")
|
||||
break
|
||||
|
||||
song_hash = map_data.get('songHash')
|
||||
difficulty_info = map_data.get('difficulty', {})
|
||||
difficulty_raw = difficulty_info.get('difficultyRaw')
|
||||
game_mode = difficulty_info.get('gameMode', 'Standard')
|
||||
|
||||
if not song_hash or not difficulty_raw:
|
||||
logging.debug(f"Skipping map due to missing hash or difficulty info: {map_data}")
|
||||
continue
|
||||
|
||||
# Normalize the difficulty name (reusing the helper from oldscores)
|
||||
difficulty = normalize_difficulty_name(difficulty_raw)
|
||||
|
||||
# Avoid reusing the same song + difficulty
|
||||
if song_hash in history['scoresaber_ranked'] and difficulty in history['scoresaber_ranked'][song_hash]:
|
||||
logging.debug(f"Skipping song {song_hash} - {difficulty} as it's in history.")
|
||||
continue
|
||||
|
||||
# Prepare the data for the playlist
|
||||
song_dict = {
|
||||
'hash': song_hash,
|
||||
'songName': map_data.get('songName', 'Unknown'),
|
||||
'difficulties': [
|
||||
{
|
||||
'name': difficulty,
|
||||
'characteristic': 'Standard' if 'Standard' in game_mode else game_mode
|
||||
}
|
||||
]
|
||||
}
|
||||
playlist_data.append(song_dict)
|
||||
logging.debug(f"Added {song_dict['songName']} (Difficulty Raw: {difficulty_raw}) to the playlist.")
|
||||
|
||||
# Update history to mark this song + difficulty combination as used
|
||||
history['scoresaber_ranked'].setdefault(song_hash, []).append(difficulty)
|
||||
|
||||
# Log final results
|
||||
if playlist_data:
|
||||
logging.info(f"Added {len(playlist_data)} new songs to the playlist.")
|
||||
else:
|
||||
logging.info("No new ranked songs were added to the playlist based on history.")
|
||||
|
||||
save_history(history)
|
||||
|
||||
return playlist_data, f"scoresaber_ranked-{new_count:02d}"
|
||||
|
||||
|
||||
def playlist_strategy_scoresaber_oldscores(
|
||||
api: ScoreSaberAPI,
|
||||
song_count: int = 20
|
||||
) -> 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', {})
|
||||
history.setdefault('playlist_counts', {})
|
||||
|
||||
# Get the current count for ScoreSaber old scores and increment it
|
||||
count_key = 'scoresaber_oldscores'
|
||||
current_count = history['playlist_counts'].get(count_key, 0)
|
||||
new_count = current_count + 1
|
||||
history['playlist_counts'][count_key] = new_count
|
||||
|
||||
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, f"scoresaber_oldscores-{new_count:02d}"
|
Loading…
x
Reference in New Issue
Block a user