From 9935d70e31267897bdaec6268efe7089f181cb7b Mon Sep 17 00:00:00 2001 From: AnthonyDuan Date: Sat, 17 Jan 2026 19:56:41 +0800 Subject: [PATCH] Fix: Support both numeric and hash song IDs for leaderboard --- app.py | 9 ++++++--- public/src/js/leaderboard.js | 9 +++++---- public/src/js/scoresheet.js | 7 ++++--- public/src/js/songselect.js | 7 +++---- schema.py | 3 ++- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/app.py b/app.py index 8374c69..0ed3fdd 100644 --- a/app.py +++ b/app.py @@ -858,7 +858,6 @@ def route_api_leaderboard_submit(): return jsonify({'status': 'ok', 'message': 'score_submitted'}) - @app.route(basedir + 'api/leaderboard/get') def route_api_leaderboard_get(): song_id = request.args.get('song_id', None) @@ -867,10 +866,13 @@ def route_api_leaderboard_get(): if not song_id or not difficulty: return abort(400) + # Accept both numeric IDs and hash strings + # Try to convert to int if possible, otherwise use as string try: song_id = int(song_id) - except: - return abort(400) + except (ValueError, TypeError): + # Keep as string (hash ID) + pass # Validate difficulty valid_difficulties = ['easy', 'normal', 'hard', 'oni', 'ura'] @@ -901,6 +903,7 @@ def route_api_leaderboard_get(): return jsonify({'status': 'ok', 'leaderboard': leaderboard, 'month': current_month}) + @app.route(basedir + 'privacy') def route_api_privacy(): last_modified = time.strftime('%d %B %Y', time.gmtime(os.path.getmtime('templates/privacy.txt'))) diff --git a/public/src/js/leaderboard.js b/public/src/js/leaderboard.js index 2969a40..ab62e74 100644 --- a/public/src/js/leaderboard.js +++ b/public/src/js/leaderboard.js @@ -77,17 +77,18 @@ class Leaderboard { } async fetchLeaderboard() { - // Validate songId is a valid number - if (!this.songId || isNaN(parseInt(this.songId))) { - console.error("Invalid song ID for leaderboard:", this.songId) + // Validate songId exists + if (!this.songId) { + console.error("Missing song ID for leaderboard") this.leaderboardData = [] return } try { var response = await loader.ajax( - `${gameConfig.basedir || "/"}api/leaderboard/get?song_id=${parseInt(this.songId)}&difficulty=${this.difficulty}` + `${gameConfig.basedir || "/"}api/leaderboard/get?song_id=${encodeURIComponent(this.songId)}&difficulty=${this.difficulty}` ) + var data = JSON.parse(response) if (data.status === "ok") { this.leaderboardData = data.leaderboard || [] diff --git a/public/src/js/scoresheet.js b/public/src/js/scoresheet.js index af64b78..78cb015 100644 --- a/public/src/js/scoresheet.js +++ b/public/src/js/scoresheet.js @@ -958,11 +958,12 @@ class Scoresheet { } submitToLeaderboard(songId, difficulty, scoreObj) { - // Only submit if user is logged in and song has valid numeric ID - if (!account.loggedIn || !songId || isNaN(parseInt(songId))) { + // Only submit if user is logged in and song has an ID + if (!account.loggedIn || !songId) { return } + var self = this loader.getCsrfToken().then(token => { var request = new XMLHttpRequest() @@ -984,7 +985,7 @@ class Scoresheet { } request.send(JSON.stringify({ - song_id: parseInt(songId), + song_id: songId, difficulty: difficulty, score: scoreObj })) diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js index d2004c0..0674f69 100644 --- a/public/src/js/songselect.js +++ b/public/src/js/songselect.js @@ -3336,12 +3336,11 @@ class SongSelect { } toLeaderboard() { var songId = this.songs[this.selectedSong].id - // Only allow leaderboard for server songs with numeric IDs - if (!songId || typeof songId !== 'number' || isNaN(songId)) { - // Show alert for custom/local songs - alert("排行榜仅支持服务器歌曲\nLeaderboard only available for server songs") + // Allow leaderboard for any song with an ID (numeric or hash) + if (!songId) { return } + // Default to first available difficulty if not in a valid difficulty selection var selectedDiff = this.selectedDiff - this.diffOptions.length if (selectedDiff < 0) { diff --git a/schema.py b/schema.py index 482c618..359ac01 100644 --- a/schema.py +++ b/schema.py @@ -85,9 +85,10 @@ leaderboard_submit = { '$schema': 'http://json-schema.org/schema#', 'type': 'object', 'properties': { - 'song_id': {'type': 'number'}, + 'song_id': {'type': ['number', 'string']}, 'difficulty': {'type': 'string'}, 'score': {'type': 'object'} }, 'required': ['song_id', 'difficulty', 'score'] } +