1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
javascript:void(async function () {
function waitForRows() {
return new Promise((resolve) => {
const check = () => {
const rows = document.querySelectorAll("table tbody tr");
if (rows.length > 0) resolve(rows);
else setTimeout(check, 500);
};
check();
});
}
const difficultyMap = {
"DX Basic": "DX Basic",
"DX Advanced": "DX Advanced",
"DX Expert": "DX Expert",
"DX Master": "DX Master",
"DX Re:Master": "DX Re:Master"
};
const lampMap = {
"FAILED": "FAILED",
"CLEAR": "CLEAR",
"FULL COMBO": "FULL COMBO",
"FULL COMBO+": "FULL COMBO+",
"ALL PERFECT": "ALL PERFECT",
"ALL PERFECT+": "ALL PERFECT+"
};
const gradeList = ["D", "C", "B", "BB", "BBB", "A", "AA", "AAA", "S", "S+", "SS", "SS+", "SSS", "SSS+"];
const rows = await waitForRows();
const results = {
meta: {
game: "maimaidx",
playtype: "Single",
service: "Tachi to Tachi PB Export"
},
scores: []
};
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
if (row.classList.contains("expandable-pseudo-row") || row.classList.contains("fake-row")) continue;
const cells = row.querySelectorAll("td");
if (cells.length < 9) continue;
const difficultyText = cells[0].innerText.split("\n")[0].trim();
const difficulty = Object.keys(difficultyMap).find(d => difficultyText.includes(d)) || difficultyText;
const titleAnchor = cells[2].querySelector("a");
const title = titleAnchor?.childNodes[0]?.textContent.trim() || "";
const artist = titleAnchor?.querySelector("small")?.textContent.trim() || "";
const percentText = cells[3].innerText.trim();
const percent = parseFloat(percentText.match(/([\d.]+)%/)?.[1]) || 0;
const grade = gradeList.find(g => percentText.includes(g)) || "D";
const judgmentSpans = cells[4].querySelectorAll("span");
const [pcrit, perfect, great, good, miss] = Array.from(judgmentSpans).map(span => parseInt(span.textContent.trim()));
const lampText = cells[5].innerText.trim();
const lamp = lampMap[lampText] || "FAILED";
const timeText = cells[7].querySelector("small")?.textContent?.trim();
let timeAchieved = null;
if (timeText) {
const parsed = new Date(timeText);
if (!isNaN(parsed)) {
timeAchieved = parsed.getTime();
}
}
const score = {
identifier: title,
artist,
difficulty,
percent,
lamp,
judgements: {
pcrit,
perfect,
great,
good,
miss
},
matchType: "songTitle",
timeAchieved: timeAchieved
};
results.scores.push(score);
}
const blob = new Blob([JSON.stringify(results, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "maimaidx_session_scores.json";
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(url);
})();
|