diff --git a/Readme.md b/Readme.md index 8330fd5..f9234c2 100644 --- a/Readme.md +++ b/Readme.md @@ -6,6 +6,8 @@ - [x] /api/phi/bests +**Get Users Best, support Overflow songs, you can delete this limit.** + > args: Session | overflow (max:20) ``` @@ -55,8 +57,106 @@ ``` -- [ ] /api/phi/re8 -- [ ] /api/phi/songs +- [x] /api/phi/best + +**Check User's best Songs.** + +> args: songid | Session | diff: Optional(Default "IN") + +- tips: songid just like "DESTRUCTION321.Normal1zervsBrokenNerdz" || diff just like "EZ" "AT" + +``` +{ + "status": true, + "content": { + "record": { + "score": 999383, + "acc": 99.93145751953125, + "level": "IN", + "fc": true, + "songId": "DESTRUCTION321.Normal1zervsBrokenNerdz", + "songname": "DESTRUCTION 3,2,1", + "difficulty": 15.9, + "rks": 15.851600202364502 + }, + "PlayerID": "压压鸭ya", + "ChallengeModeRank": 445, + "RankingScore": 15.800082206726074 + } +} + +``` + +- [x] /api/phi/info + +**User's Status, no others** + +> args: session + +``` +{ + "status": true, + "Content": { + "PlayerID": "MoeMagicMango", + "ChallengeModeRank": 245, + "RankingScore": 13.175806999206543 + } +} +``` + +- [x] /api/phi/rand + +> no args + +``` +{ + "status": true, + "content": { + "songid": "BetterGraphicAnimation.ルゼ", + "songname": "Better Graphic Animation", + "composer": "ルゼ", + "level": "HD", + "rating": 11.7 + } +} +``` + +- [x] /api/phi/song + +> args: songid + +``` +{ + "status": true, + "content": { + "songid": "BetterGraphicAnimation.ルゼ", + "info": { + "songname": "Better Graphic Animation", + "composer": "ルゼ", + "illustrator": "A-Zero Project", + "chartDetail": { + "EZ": { + "rating": 6.5, + "charter": "NerSAN" + }, + "HD": { + "rating": 11.7, + "charter": "NerSAN" + }, + "In": { + "rating": 15.3, + "charter": "縱連打の信者☆無極" + }, + "level_list": [ + 6.5, + 11.7, + 15.3 + ] + } + } + } +} +``` ... diff --git a/app.py b/app.py index bdcdc2e..411d1fb 100644 --- a/app.py +++ b/app.py @@ -1,13 +1,28 @@ import json from flask import Flask, jsonify, request, make_response -from method import BestsRender +from method import ( + BestsRender, + difficulty, + info, + info_by, + levels, + info_illustrator, + info_hd_designer, + info_at_designer, + info_ez_desinger, + info_in_desingner, +) +import random as rand + app = Flask(__name__) # init BestsRender.read_difficulty("difficulty.csv") BestsRender.read_playerInfo("info.csv") +listDiff = list(difficulty.items()) +getLength = len(difficulty) @app.route("/", methods=["GET"]) @@ -41,5 +56,137 @@ def get_bests(): return data +@app.route("/api/phi/best", methods=["GET"]) +def get_songs(): + session = request.args.get("session") + songs = request.args.get("songid") + diff = request.args.get("diff") + if session is None: + return jsonify({"message": "session is required."}) + if songs is None: + return jsonify({"message": "songid is required."}) + if diff is None: + diff = "IN" + try: + Contents = BestsRender.get_songs_stats(session, songs, diff) + if Contents is None: + data = {"status": False, "message": "SongID or Diff Not found"} + else: + get_formatData = BestsRender.get_formatData(session) + Contents = {"record": Contents} + Content = {**Contents, **get_formatData} + data = { + "status": True, + "content": Content, + } + data = json.dumps(data, ensure_ascii=False).encode("utf-8") + data = make_response(data) + data.headers["Content-Type"] = "application/json; charset=utf-8" + except Exception as e: + return jsonify({"status": False, "message": str(e)}) + return data + + +@app.route("/api/phi/rand", methods=["GET"]) +def get_rand(): + try: + getRandSongIDNum = rand.randrange(0, getLength) + result = listDiff[getRandSongIDNum - 1] + result_info_name = info[result[0]] + result_info_by = info_by[result[0]] + result_get_diff_list = result[1] + getLength_result_range = rand.randrange(0, len(result_get_diff_list)) + getLevel = levels[getLength_result_range] + getRating = result_get_diff_list[getLength_result_range] + Content = { + "songid": result[0], + "songname": result_info_name, + "composer": result_info_by, + "level": getLevel, + "rating": getRating, + } + data = { + "status": True, + "content": Content, + } + data = json.dumps(data, ensure_ascii=False).encode("utf-8") + data = make_response(data) + data.headers["Content-Type"] = "application/json; charset=utf-8" + except Exception as e: + return jsonify({"status": False, "message": str(e)}) + return data + + +@app.route("/api/phi/info", methods=["GET"]) +def get_info(): + session = request.args.get("session") + if session is None: + return jsonify({"message": "session is required."}) + try: + get_formatData = BestsRender.get_formatData(session) + except Exception as e: + return jsonify({"status": False, "message": str(e)}) + data = {"status": True, "Content": get_formatData} + data = json.dumps(data, ensure_ascii=False).encode("utf-8") + data = make_response(data) + data.headers["Content-Type"] = "application/json; charset=utf-8" + return data + + +@app.route("/api/phi/song", methods=["GET"]) +def get_song_info(): + songid = request.args.get("songid") + if songid is None: + return jsonify({"message": "songid is required."}) + try: + result_info_name = info[songid] + result_info_by = info_by[songid] + result_get_diff_list: list = difficulty[songid] # list + result_get_ins_by = info_illustrator[songid] + result_get_ez_designer = info_ez_desinger[songid] + result_get_hd_desinger = info_hd_designer[songid] + result_get_in_desinger = info_in_desingner[songid] + result_get_at_desinger = info_at_designer[songid] + showEZDetailed = { + "EZ": {"rating": result_get_diff_list[0], "charter": result_get_ez_designer} + } + showHDDetailed = { + "HD": {"rating": result_get_diff_list[1], "charter": result_get_hd_desinger} + } + showInDetailed = { + "In": {"rating": result_get_diff_list[2], "charter": result_get_in_desinger} + } + if len(result_get_diff_list) >= 4: + showAtDetailed = { + "In": { + "rating": result_get_diff_list[3], + "charter": result_get_at_desinger, + } + } + else: + showAtDetailed = {} + getChartDetailedInfo = { + **showEZDetailed, + **showHDDetailed, + **showInDetailed, + **showAtDetailed, + "level_list": result_get_diff_list, + } + infos = { + "songname": result_info_name, + "composer": result_info_by, + "illustrator": result_get_ins_by, + "chartDetail": getChartDetailedInfo, + } + content = {"songid": songid, "info": infos} + data = {"status": True, "content": content} + except Exception as e: + return jsonify({"status": False, "message": str(e)}) + data = json.dumps(data, ensure_ascii=False).encode("utf-8") + data = make_response(data) + data.headers["Content-Type"] = "application/json; charset=utf-8" + return data + + if __name__ == "__main__": app.run(debug=True) diff --git a/method.py b/method.py index 958440c..2f3a588 100644 --- a/method.py +++ b/method.py @@ -5,10 +5,21 @@ import struct import zipfile import requests +from collections import OrderedDict + levels = ["EZ", "HD", "IN", "AT"] -difficulty = {} +difficulty = OrderedDict() info = {} +info_by = {} +info_illustrator = {} +info_ez_desinger = {} +info_hd_designer = {} +info_in_desingner = {} +info_at_designer = {} + +# info saver + global_headers = { "X-LC-Id": "rAK3FfdieFob2Nn8Am", @@ -41,11 +52,11 @@ def readVarShort(self): def readString(self): length = self.data[self.position] self.position += length + 1 - return self.data[self.position - length: self.position].decode() + return self.data[self.position - length : self.position].decode() def readScoreAcc(self): self.position += 8 - scoreAcc = struct.unpack("if", self.data[self.position - 8: self.position]) + scoreAcc = struct.unpack("if", self.data[self.position - 8 : self.position]) return {"score": scoreAcc[0], "acc": scoreAcc[1]} def readRecord(self, songId): @@ -68,7 +79,7 @@ def readRecord(self, songId): scoreAcc["difficulty"] = diff[level] scoreAcc["rks"] = (scoreAcc["acc"] - 55) / 45 scoreAcc["rks"] = ( - scoreAcc["rks"] * scoreAcc["rks"] * scoreAcc["difficulty"] + scoreAcc["rks"] * scoreAcc["rks"] * scoreAcc["difficulty"] ) records.append(scoreAcc) self.position = end_position @@ -113,14 +124,30 @@ def parse_render_bests(gameRecord, overflow: int): key=lambda x: x["difficulty"], ) ] - render.extend(records[:19 + overflow]) + render.extend(records[: 19 + overflow]) isPhi = True except ValueError: - render = records[:19 + overflow] + render = records[: 19 + overflow] isPhi = False return render, isPhi +def get_songs_stat_main(gameRecord, songid, diff): + reader = ByteReader(gameRecord) + getdiff = diff # Diff should be EZ,HD,IN,AT + if getdiff != "EZ" and getdiff != "HD" and getdiff != "IN" and getdiff != "AT": + getdiff = "IN" + for i in range(reader.readVarShort()): + songId = reader.readString()[:-2] + record = reader.readRecord(songId) + if songId == songid: + if record[0]["level"] == getdiff: + return record[0] + else: + continue + return None + + class BestsRender: @staticmethod def read_difficulty(path): @@ -145,6 +172,15 @@ def read_playerInfo(path): for i in range(1, len(line)): infos.append(str(line[i])) info[line[0]] = infos[0] + info_by[line[0]] = infos[1] + info_illustrator[line[0]] = infos[2] + info_ez_desinger[line[0]] = infos[3] + info_hd_designer[line[0]] = infos[4] + info_in_desingner[line[0]] = infos[5] + try: + info_at_designer[line[0]] = infos[6] + except Exception: + info_at_designer[line[0]] = "" @staticmethod def get_playerId(sessionToken): @@ -194,7 +230,6 @@ def get_formatData(sessionToken): result = response.json()["results"][0] summary = base64.b64decode(result["summary"]) get_id = BestsRender.get_playerId(sessionToken) - print(get_id) summary = struct.unpack("=BHfBx%ds12H" % summary[8], summary) return { "PlayerID": get_id, @@ -214,3 +249,16 @@ def get_bests(session, overflow): gameRecord = DataPackage.GameReader(result) gameRecord = decrypt_gameRecord(gameRecord) return parse_render_bests(gameRecord, overflow) + + @staticmethod + def get_songs_stats(session, songid, diff): + headers = global_headers.copy() + headers["X-LC-Session"] = session + response = requests.get( + "https://rak3ffdi.cloud.tds1.tapapis.cn/1.1/classes/_GameSave", + headers=headers, + ) + result = response.json()["results"][0]["gameFile"]["url"] + gameRecord = DataPackage.GameReader(result) + gameRecord = decrypt_gameRecord(gameRecord) + return get_songs_stat_main(gameRecord, songid, diff)