aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/reflecbeat/flower
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2025-11-11 16:45:33 -0800
committerPinapelz <yukais@pinapelz.com>2025-11-11 16:45:33 -0800
commit5aa04d60b1602dbb6166c5459a2f1c1792e634c0 (patch)
tree2e8439d355356764a9daa1f3773baf5ce7dd9f67 /scripts/reflecbeat/flower
parent9608610b0fef717c8f2d87ab518a077f4e0763cb (diff)
move import script paths according to import method
Diffstat (limited to 'scripts/reflecbeat/flower')
-rw-r--r--scripts/reflecbeat/flower/README.md4
-rw-r--r--scripts/reflecbeat/flower/reflecbeat_flower_scraper.user.js111
2 files changed, 115 insertions, 0 deletions
diff --git a/scripts/reflecbeat/flower/README.md b/scripts/reflecbeat/flower/README.md
new file mode 100644
index 0000000..85c1a3d
--- /dev/null
+++ b/scripts/reflecbeat/flower/README.md
@@ -0,0 +1,4 @@
+# REFLEC BEAT
+
+*Scripts Available:*
+- [Flower Play History, exports only the page you are on](./reflecbeat_flower_scraper.user.js) \ No newline at end of file
diff --git a/scripts/reflecbeat/flower/reflecbeat_flower_scraper.user.js b/scripts/reflecbeat/flower/reflecbeat_flower_scraper.user.js
new file mode 100644
index 0000000..dd756df
--- /dev/null
+++ b/scripts/reflecbeat/flower/reflecbeat_flower_scraper.user.js
@@ -0,0 +1,111 @@
+// ==UserScript==
+// @name REFLEC BEAT (Flower) Play History Scraper
+// @namespace http://tampermonkey.net/
+// @version 1.2
+// @description Export REFLEC BEAT scores including full judgements and timestamps as JSON
+// @match https://projectflower.eu/game/rb/profile/*
+// @grant none
+// ==/UserScript==
+
+(function() {
+ 'use strict';
+
+ // Add export button
+ const btn = document.createElement('button');
+ btn.textContent = 'Mirage Export Scores JSON';
+ btn.style.position = 'fixed';
+ btn.style.top = '10px';
+ btn.style.right = '10px';
+ btn.style.zIndex = 9999;
+ btn.style.padding = '8px 12px';
+ btn.style.background = '#4CAF50';
+ btn.style.color = 'white';
+ btn.style.border = 'none';
+ btn.style.borderRadius = '4px';
+ btn.style.cursor = 'pointer';
+ document.body.appendChild(btn);
+
+ btn.addEventListener('click', () => {
+ const json = {
+ meta: {
+ game: "reflecbeat",
+ playtype: "Single",
+ service: "REFLEC BEAT Flower Play History"
+ },
+ scores: parseScoreLog()
+ };
+
+ // Download JSON
+ const blob = new Blob([JSON.stringify(json, null, 2)], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = 'reflecbeat_scores.json';
+ a.click();
+ URL.revokeObjectURL(url);
+ });
+
+ function parseScoreLog() {
+ const rows = document.querySelectorAll('table.table tbody tr.accordion-toggle');
+ const scores = [];
+
+ rows.forEach(row => {
+ const titleElem = row.querySelector('td.pnmTitle a');
+ const artistElem = row.querySelector('td.pnmTitle small');
+ const difficultyElem = row.querySelector('td.gradeHARD, td.gradeMEDIUM, td.gradeEASY');
+ const levelElem = difficultyElem?.querySelector('strong');
+ const lampElem = row.querySelector('td.rb-rank strong'); // Rank / Lamp
+ const scoreText = row.querySelector('td.rb-rank span')?.innerText || '';
+ const scoreMatch = scoreText.match(/(\d+(?:,\d+)*)\s*\((\d+(?:\.\d+)?)%\)/);
+ const scoreNum = scoreMatch ? parseInt(scoreMatch[1].replace(/,/g, '')) : null;
+ const scorePercent = scoreMatch ? parseFloat(scoreMatch[2]) : null;
+
+ // Extract timestamp from <small> in last column
+ const timestampElem = row.querySelector('td.hidden-from-mobile small');
+ let timestamp = null;
+ if (timestampElem) {
+ const dateStr = timestampElem.innerText.trim(); // e.g., "2025-06-27 5:23 PM"
+ timestamp = new Date(dateStr).getTime(); // Unix ms
+ }
+
+ // Get accordion ID
+ const accordionId = row.getAttribute('data-target')?.replace('#', '');
+ const accordion = document.getElementById(accordionId);
+
+ // Initialize metadata
+ let lifeLeft = '', justReflec = 0, just = 0, great = 0, good = 0, miss = 0;
+
+ if (accordion) {
+ const divs = accordion.querySelectorAll('div.col-sm-3, div.col-sm-2, div.col-sm-4');
+ divs.forEach(div => {
+ const label = div.querySelector('strong')?.innerText.trim();
+ const value = div.childNodes[div.childNodes.length-1].nodeValue?.trim() || div.querySelector('a')?.innerText.trim() || '';
+ switch(label) {
+ case 'Life Left': lifeLeft = value; break;
+ case 'Just Reflec': justReflec = parseInt(value) || 0; break;
+ case 'Just': just = parseInt(value) || 0; break;
+ case 'Great': great = parseInt(value) || 0; break;
+ case 'Good': good = parseInt(value) || 0; break;
+ case 'Miss': miss = parseInt(value) || 0; break;
+ }
+ });
+ }
+
+ scores.push({
+ title: titleElem?.innerText.trim() || '',
+ artist: artistElem?.innerText.trim() || '',
+ difficulty: difficultyElem?.innerText.replace(levelElem?.innerText, '').trim() || '',
+ level: levelElem ? parseInt(levelElem.innerText.trim()) : null,
+ score: scoreNum,
+ scorePercent,
+ lamp: lampElem?.innerText.trim() || '',
+ lifeLeft: parseInt(lifeLeft) || null,
+ timestamp, // Unix ms
+ judgements: { justReflec, just, great, good, miss }
+ });
+ });
+
+ return scores;
+ }
+
+})();
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage