diff options
Diffstat (limited to 'mai2/tachi/tachi_to_tachi_session.js')
| -rw-r--r-- | mai2/tachi/tachi_to_tachi_session.js | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/mai2/tachi/tachi_to_tachi_session.js b/mai2/tachi/tachi_to_tachi_session.js new file mode 100644 index 0000000..f9befa5 --- /dev/null +++ b/mai2/tachi/tachi_to_tachi_session.js @@ -0,0 +1,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); +})(); |
