Last active
December 26, 2023 18:10
-
-
Save clsource/d186654815795621598241e2caef8064 to your computer and use it in GitHub Desktop.
Export Gameplay data from PIU Phoenix Website to CSV (Using TamperMonkey)
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
// ==UserScript== | |
// @name PIU.Phoenix.PlayData | |
// @namespace https://ninjas.cl | |
// @version 2023-12-25 | |
// @description Save data from Phoenix Gameplays to local csv | |
// @author Camilo Castro <clsource> | |
// @match https://piugame.com/my_page/recently_played.php | |
// @match https://www.piugame.com/my_page/recently_played.php | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=piugame.com | |
// @grant none | |
// @require https://cdnjs.cloudflare.com/ajax/libs/luxon/3.4.4/luxon.min.js | |
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/excellentexport.min.js | |
// @require https://cdn.jsdelivr.net/npm/[email protected]/src/md5.min.js | |
// ==/UserScript== | |
// Eslint | |
// jQuery already present in website | |
// Imported Luxon Time Lib | |
/* global $, luxon, ExcellentExport, md5 */ | |
const get_bg = (element) => { | |
return element.css("background-image").trim().replace('url("',"").replace('")',""); | |
}; | |
// All dates are in Default Korea Timezone | |
const to_iso = (date, time) => { | |
const timestamp = luxon.DateTime.fromISO(date + "T" + time + "+09:00").setZone("Asia/Seoul"); | |
return { | |
date: timestamp.toString(), | |
utc: timestamp.toUTC().toString() | |
}; | |
}; | |
const sanitize_number = (value) => { | |
return value.toString().trim().replaceAll(",",""); | |
}; | |
const get_filename = (value) => { | |
return value.split(/(\\|\/)/g).pop().replace(".png", ""); | |
}; | |
const export_data = () => { | |
const data = { | |
player: {}, | |
songs: [] | |
}; | |
const data_name = $(".name_w").children(".t2").first().text().split("#"); | |
data.player.name = data_name[0].trim(); | |
data.player.id = data_name[1].trim(); | |
data.player.title = $(".name_w").children(".t1").first().text(); | |
data.player.points = sanitize_number($(".profile_etc").first().text()); | |
data.player.avatar = get_bg($(".profile_img div.resize").children().first()); | |
// Remove unwanted text and extract only the data then join for time and place | |
const access_at = $(".time_w ul").first().children("li").first().text().split(":").slice(1).join(":").trim().split(" "); | |
data.player.access = { | |
date: access_at[0].trim(), | |
time: access_at[1].trim(), | |
tz: "GMT+9", | |
at: to_iso(access_at[0].trim(), access_at[1].trim()), | |
in: $(".time_w ul").first().children("li").last().text().split(":").slice(1).join(":").trim(), | |
}; | |
// Process Songs | |
const songs = $("ul.recently_playeList li div.wrap_in"); | |
songs.each((index, el) => { | |
const song = {}; | |
const song_time = $(el).children(".in2").text().trim().split(" "); | |
song.played = { | |
date: song_time[0].trim(), | |
time: song_time[1].trim(), | |
tz: song_time[2].trim().replace("(", "").replace(")", "") | |
}; | |
song.played.at = to_iso(song.played.date, song.played.time); | |
const song_data = $(el).children(".in").first(); | |
song.bg = get_bg(song_data); | |
song.name = song_data.find(".song_name").first().text(); | |
song.difficulty = { | |
numbers: [] | |
}; | |
song_data.find(".stepBall_in div img").each((index, el) => { | |
const image = $(el).attr("src"); | |
if(image.includes("s_text")) { | |
song.difficulty.type = "single"; | |
} else if(image.includes("d_text")) { | |
song.difficulty.type = "double"; | |
} else if(image.includes("c_text")) { | |
song.difficulty.type = "co-op"; | |
} | |
if(image.includes("_num_")) { | |
const number = image.substring(image.indexOf("num_") + 4).replace(".png", ""); | |
song.difficulty.numbers.push(number); | |
} | |
}); | |
song.difficulty.value = song.difficulty.numbers.join(""); | |
const grade = song_data.find(".li_in.ac img").first().attr("src") || "none"; | |
const plate = song_data.find(".st1 img").first().attr("src") || "none"; | |
const score = sanitize_number(song_data.find(".ac .tx").first().text()); | |
const stage_break = (score == "STAGE BREAK"); | |
song.evaluation = { | |
grade: get_filename(grade), | |
plate: get_filename(plate), | |
score: (stage_break ? "0" : score), | |
stage_break: stage_break | |
}; | |
song.steps = { | |
perfect: sanitize_number(song_data.find(".fontCol1 .tx").first().text()), | |
great: sanitize_number(song_data.find(".fontCol2 .tx").first().text()), | |
good: sanitize_number(song_data.find(".fontCol3 .tx").first().text()), | |
bad: sanitize_number(song_data.find(".fontCol4 .tx").first().text()), | |
miss: sanitize_number(song_data.find(".fontCol5 .tx").first().text()) | |
}; | |
song.steps.total = parseInt(song.steps.perfect) + parseInt(song.steps.great) + parseInt(song.steps.good) + parseInt(song.steps.bad) + parseInt(song.steps.miss); | |
// Example total of 689 steps will give 38,023 kcal | |
// This means that 1 step is 0.0551857764876633 kcal | |
const kcal_per_step = 0.0551857764876633; | |
song.steps.kcal = song.steps.total * kcal_per_step; | |
// Last hash | |
song.hash = md5(data.player.id + song.name + song.difficulty.type + song.difficulty.value + song.evaluation.score + song.steps.total + song.played.at.utc) | |
data.songs.push(song); | |
}); | |
return data; | |
}; | |
const download_file = (data) => { | |
console.log("Begin Generating Data File"); | |
// Delete previous table | |
$("#export-data-table").remove(); | |
// Create a new one | |
$("body").append(`<table id="export-data-table" style="display:none"> | |
<tr> | |
<th>Player Id</th> | |
<th>Player Name</th> | |
<th>Location</th> | |
<th>Song Name</th> | |
<th>Difficulty Type</th> | |
<th>Difficulty Value</th> | |
<th>Grade</th> | |
<th>Plate</th> | |
<th>Score</th> | |
<th>Stage Break?</th> | |
<th>Perfect</th> | |
<th>Great</th> | |
<th>Good</th> | |
<th>Bad</th> | |
<th>Miss</th> | |
<th>Total</th> | |
<th>Kcal</th> | |
<th>Date</th> | |
<th>Hash</th> | |
</tr> | |
</table>`); | |
const table = document.getElementById("export-data-table"); | |
data.songs.forEach(song => { | |
const row = table.insertRow(); | |
const playerId = row.insertCell(); | |
playerId.innerHTML = data.player.id; | |
const playerName = row.insertCell(); | |
playerName.innerHTML = data.player.name; | |
const location = row.insertCell(); | |
location.innerHTML = data.player.access.in; | |
const songName = row.insertCell(); | |
songName.innerHTML = song.name; | |
const diffType = row.insertCell(); | |
diffType.innerHTML = song.difficulty.type; | |
const diffValue = row.insertCell(); | |
diffValue.innerHTML = song.difficulty.value; | |
const grade = row.insertCell(); | |
grade.innerHTML = song.evaluation.grade; | |
const plate = row.insertCell(); | |
plate.innerHTML = song.evaluation.plate; | |
const score = row.insertCell(); | |
score.innerHTML = song.evaluation.score; | |
const stageBreak = row.insertCell(); | |
stageBreak.innerHTML = song.evaluation.stage_break; | |
const perfect = row.insertCell(); | |
perfect.innerHTML = song.steps.perfect; | |
const great = row.insertCell(); | |
great.innerHTML = song.steps.great; | |
const good = row.insertCell(); | |
good.innerHTML = song.steps.good; | |
const bad = row.insertCell(); | |
bad.innerHTML = song.steps.bad; | |
const miss = row.insertCell(); | |
miss.innerHTML = song.steps.miss; | |
const total = row.insertCell(); | |
total.innerHTML = song.steps.total; | |
const kcal = row.insertCell(); | |
kcal.innerHTML = song.steps.kcal; | |
const date = row.insertCell(); | |
date.innerHTML = song.played.at.utc; | |
const hash = row.insertCell(); | |
hash.innerHTML = song.hash; | |
}); | |
// Export File | |
return ExcellentExport.convert({ | |
anchor: "btn-export-data", | |
filename: "piugame-phoenix-" + data.player.id + "-" + luxon.DateTime.now().toString(), | |
format: "csv" | |
}, [{ | |
name: 'Gameplay Data', | |
from: { | |
table: 'export-data-table' | |
} | |
}]); | |
}; | |
(function() { | |
'use strict'; | |
console.log("PIU Data Export Ready"); | |
// Add Export Button and Export Table | |
$(".profile_btn").append('<a href="#" id="btn-export-data" class="btn flex vc mg1" style="margin-left:0.5%"><i class="xi xi-external-link icon"></i><i class="tt">Export Data</i></a>'); | |
$("#btn-export-data").click(() => { | |
download_file(export_data()); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment