diff options
| author | Pinapelz <yukais@pinapelz.com> | 2025-08-24 11:47:35 -0700 |
|---|---|---|
| committer | Pinapelz <yukais@pinapelz.com> | 2025-08-24 11:50:13 -0700 |
| commit | 014d59aa7f7b833dbe2f4039ddd3a57a8a432066 (patch) | |
| tree | 624499a9f2e5669d2ed6df031b6aed9dcb660f20 | |
| parent | 356a5c5844ac707669ae91f18f23e80f429cdaf0 (diff) | |
sdvx: add tachi unique pb merge tool
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | sdvx/pb_merge/README.md | 9 | ||||
| -rw-r--r-- | sdvx/pb_merge/pb_merge.py | 110 |
3 files changed, 124 insertions, 0 deletions
@@ -16,6 +16,11 @@ Use with caution as there may be some cases missing. This is useful if you want to use your data on Tachi for something else and you do not have access to DB. Converts scores as shown on the site back to a batch-manual format - [Universal Tachi to Tachi Userscript](./tachi_to_tachi) +**Tools** +- [SDVX e-amusement CSV PB Deduplication (with limited Session/Date data)](./sdvx/pb_merge) + +This script compares a e-amusement exported SDVX score CSV against your scores on Tachi. Then generates a new CSV in the same format with only scores that have not been successfully imported to Tachi. + > [!CAUTION] > If you are using Kamaitachi or using someone else's Tachi instance please check the rules to ensure that you are complying with the rules before importing your scores!!! diff --git a/sdvx/pb_merge/README.md b/sdvx/pb_merge/README.md new file mode 100644 index 0000000..e634b7f --- /dev/null +++ b/sdvx/pb_merge/README.md @@ -0,0 +1,9 @@ +# e-amusement PB de-duplicator +This script takes in a e-amusement SDVX CSV and generates a new CSV with only UNIQUE PBs (aka doesn't already exist on Tachi). This is done by looking up your player info on Tachi. + +| Argument | Short | Long | Required | Default | Description | +|-----------------------|-------|-------------|----------|-----------------------------------|------------------------------------------| +| `file` | `-f` | `--file` | ✅ Yes | – | SOUND VOLTEX score CSV (`score.csv`) | +| `output` | `-o` | `--output` | ❌ No | `merged_sdvx_scores.csv` | Output filename | +| `tachi` | `-t` | `--tachi` | ❌ No | `https://kamai.tachi.ac` | API URL for your Tachi instance | +| `username` | `-u` | `--username`| ❌ No | `""` (empty string) | Your unique username on Tachi | diff --git a/sdvx/pb_merge/pb_merge.py b/sdvx/pb_merge/pb_merge.py new file mode 100644 index 0000000..cdeacbf --- /dev/null +++ b/sdvx/pb_merge/pb_merge.py @@ -0,0 +1,110 @@ +import argparse +import csv +import os +import requests + +DIFFICULTY_MAPPING = { + "NOVICE": "NOV", + "ADVANCED": "ADV", + "EXHAUST": "EXH", + "INFINITE": "INF", + "GRAVITY": "GRV", + "HEAVENLY": "HVN", + "VIVID": "VVD", + "EXCEED": "XCD", + "MAXIMUM": "MXM" +} + + +def merge_csv(input_file: str, tachi_url: str, username: str, output_file: str): + encoding = "utf-8" + header_written = False + + with open(input_file, encoding=encoding) as old_csv, open(output_file, 'w', newline='', encoding=encoding) as new_csv: + reader = csv.reader(old_csv, delimiter=",") + writer = csv.writer(new_csv, delimiter=",") + + header = next(reader) + + # Count total rows for progress tracking + rows = list(reader) + total_rows = len(rows) + current_row = 0 + + for row in rows: + current_row += 1 + title = requests.utils.quote(row[0]) + diff = DIFFICULTY_MAPPING[row[1]] + score = int(row[5]) + api_url = f"{tachi_url}/api/v1/users/{username}/games/sdvx/Single/pbs?search={title}" + print(f"[{current_row}/{total_rows}] Searching for score: {title} at difficulty {diff}") + try: + response = requests.get(api_url) + response.raise_for_status() + data = response.json() + charts = data["body"]["charts"] + is_unique = False + if len(charts) == 0: + print("Score is unique") + is_unique = True + if not header_written: + writer.writerow(header) + header_written = True + writer.writerow(row) + continue + chart_id = "" + for chart in charts: + if chart["difficulty"] == diff: + print("Found proper chart ID") + chart_id = chart["chartID"] + break + if chart_id == "": + print("Score is Unique, unable to find a match in the DB") + if not header_written: + writer.writerow(header) + header_written = True + writer.writerow(row) + continue + pbs = data["body"]["pbs"] + for pb in pbs: + if pb["chartID"] == chart_id and pb["scoreData"]["score"] == score: + print("Found match, score is not unique") + is_unique = False + break + if is_unique: + if not header_written: + writer.writerow(header) + header_written = True + writer.writerow(row) + + except requests.RequestException as e: + print(f"Error fetching data for {title}: {e}") + continue + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + prog="pb_merge.py", + description="Takes in a SDVX e-amusement PB CSV, checks against scores on Tachi and generates a new file with only unique scores", + ) + parser.add_argument( + "-f", "--file", help="SOUND VOLTEX score CSV (score.csv)", required=True + ) + parser.add_argument( + "-o", "--output", help="Output filename", default="merged_sdvx_scores.csv" + ) + parser.add_argument( + "-t", + "--tachi", + help="API URL for your Tachi instance", + default="https://kamai.tachi.ac", + ) + parser.add_argument("-u", "--username", help="Your unique username on Tachi", default="") + args = parser.parse_args() + if args.file is None: + print("ERROR: Please specify the path to the score CSV") + exit(1) + if not os.path.exists(args.file): + print(f"ERROR: The file {args.file} does not exist.") + exit(1) + merge_csv(args.file, args.tachi, args.username, args.output) |
