Last active
March 17, 2024 16:36
-
-
Save myjian/e161f14b5ab6319a006a221fbed87dd7 to your computer and use it in GitHub Desktop.
Collect score from maimai dx net
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
收集並複製 maimai 成績 | |
Collect score from maimai dx net. | |
使用方法: | |
1. 登入 maimai dx net | |
2. 畫面停留在首頁就行 | |
3. 按下 F12 開啟網頁開發者工具 | |
4. 切換到開發者工具的 Console 分頁 | |
5. 複製並貼上以下程式碼到 Console 內 | |
6. 按下 Enter 執行程式碼 | |
7. 程式執行完畢會顯示「全部成績下載完畢」。按下網頁中的「複製成績」按鈕將資料複製到剪貼簿 | |
8. 開啟 Excel 或 Google 試算表後貼上表格 | |
9. 表格應該會有六欄:歌名、分類、難度(EXPERT、MASTER 等)、等級、譜面類型(STANDARD、DX)、成績 | |
*/ | |
// 從這裡開始複製 Starts here | |
(function(){ | |
const SCORE_URLS = new Map([ | |
["Re:MASTER", "/maimai-mobile/record/musicGenre/search/?genre=99&diff=4"], | |
["MASTER", "/maimai-mobile/record/musicGenre/search/?genre=99&diff=3"], | |
["EXPERT", "/maimai-mobile/record/musicGenre/search/?genre=99&diff=2"], | |
["ADVANCED", "/maimai-mobile/record/musicGenre/search/?genre=99&diff=1"], | |
]); | |
function statusText(difficulty, end) { | |
switch (difficulty) { | |
case "Re:MASTER": | |
return end ? "✔ 白譜成績下載完畢!" : "🕓 下載白譜成績中…"; | |
case "MASTER": | |
return end ? "✔ 紫譜成績下載完畢!" : "🕓 下載紫譜成績中…"; | |
case "EXPERT": | |
return end ? "✔ 紅譜成績下載完畢!" : "🕓 下載紅譜成績中…"; | |
case "ADVANCED": | |
return end ? "✔ 黃譜成績下載完畢!" : "🕓 下載黃譜成績中…"; | |
case "ALL": | |
return "✅ 全部成績下載完畢,請按網頁上的「複製成績」把資料複製到剪貼簿。"; | |
} | |
} | |
function processRow(row, state) { | |
const isGenreRow = row.classList.contains("screw_block"); | |
const isScoreRow = ( | |
row.classList.contains("w_450") | |
&& row.classList.contains("m_15") | |
&& row.classList.contains("p_r") | |
&& row.classList.contains("f_0") | |
); | |
if (isGenreRow) { | |
state.genre = row.innerText; | |
} | |
else if (isScoreRow) { | |
const songName = row.getElementsByClassName("music_name_block")[0].innerText; | |
const level = row.getElementsByClassName("music_lv_block")[0].innerText; | |
let difficulty = row.children[0].className.match(/music_([a-z]+)_score_back/)[1].toUpperCase(); | |
if (difficulty.indexOf("RE") === 0) { | |
difficulty = "Re:MASTER"; | |
} | |
let chartType = "DX"; | |
if (row.id) { | |
if (row.id.includes("sta_")) { | |
chartType = "STANDARD"; | |
} | |
} else if (row.children[1].src.includes("_standard")) { | |
chartType = "STANDARD"; | |
} | |
const achievementElem = row.querySelector(".music_score_block.w_120"); | |
if (!achievementElem) { | |
return; | |
} | |
const achievement = achievementElem.innerText; | |
state.scoreList.push([ | |
songName, | |
state.genre, | |
difficulty, | |
level, | |
chartType, | |
achievement, | |
].join("\t")); | |
} | |
} | |
async function fetchScores(url, scoreList) { | |
const response = await fetch(url); | |
const html = await response.text(); | |
const parser = new DOMParser(); | |
const dom = parser.parseFromString(html, "text/html"); | |
const rows = dom.querySelectorAll(".main_wrapper.t_c .m_15"); | |
const state = {genre: "", scoreList: scoreList}; | |
rows.forEach(row => processRow(row, state)); | |
} | |
function createOutputElement(container) { | |
const dv = document.createElement("div"); | |
dv.style.position = "relative"; | |
dv.style.marginBottom = "16px"; | |
const tx = document.createElement("textarea"); | |
dv.appendChild(tx); | |
const btn = document.createElement("button"); | |
btn.innerText = "複製成績"; | |
btn.style.backgroundColor = "#9f51dc"; | |
btn.style.border = "2px solid black" | |
btn.style.borderRadius = "5px" | |
btn.style.color = "white" | |
btn.style.fontWeight = "700" | |
btn.style.padding = "8px 12px"; | |
dv.appendChild(btn); | |
const res = document.createElement("span"); | |
res.className = "f_16" | |
res.style.position = "absolute"; | |
res.style.left = "300px"; | |
res.style.bottom = "10px"; | |
res.style.fontWeight = "700"; | |
res.style.color = "#fff000"; | |
dv.appendChild(res); | |
btn.addEventListener("click", () => { | |
tx.select(); | |
document.execCommand("copy"); | |
res.innerText = "已複製到剪貼簿" | |
setTimeout(() => { | |
res.innerText = ""; | |
}, 5000); | |
}); | |
container.appendChild(dv); | |
return tx | |
} | |
async function fetchAllScores(cache, onError, onLog) { | |
const host = document.location.host; | |
if (host !== "maimaidx-eng.com" && host !== "maimaidx.jp") { | |
onError("請登入 maimai NET"); | |
return; | |
} | |
const scoreList = []; | |
for (const [difficulty, url] of SCORE_URLS) { | |
onLog(statusText(difficulty, false)); | |
await fetchScores(url, scoreList); | |
onLog(statusText(difficulty, true)); | |
} | |
const textareaKey = Symbol.for("outputTextarea"); | |
if (!cache[textareaKey]) { | |
cache[textareaKey] = createOutputElement( | |
document.querySelector(".main_wrapper header") | |
); | |
} | |
cache[textareaKey].value = scoreList.join("\n"); | |
onLog(statusText("ALL", true)); | |
} | |
function handleError(msg) { | |
alert(msg); | |
} | |
function handleOutput(msg) { | |
const comment = document.querySelector(".comment_block"); | |
if (comment) { | |
comment.innerText = comment.innerText + msg + "\n"; | |
} | |
else { | |
console.log(msg); | |
} | |
} | |
fetchAllScores(window, handleError, handleOutput); | |
})(); | |
// 複製到這裡 Ends here |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment