214 lines
8.3 KiB
Python
214 lines
8.3 KiB
Python
import sqlite3
|
|
from flask import Flask, render_template_string, redirect
|
|
from datetime import datetime
|
|
import os
|
|
import json
|
|
import requests
|
|
import re
|
|
from collections import defaultdict
|
|
|
|
app = Flask(__name__)
|
|
|
|
def is_youtube_id(videoid):
|
|
return re.match(r'^[a-zA-Z0-9_-]{11}$', videoid) is not None
|
|
|
|
def get_db_values(url="https://halhadus.rocks/assets/localwrapped/music.db"):
|
|
if not os.path.exists("/var/www/swingmusic/.swingmusic/music.db"):
|
|
try:
|
|
download = requests.get(url)
|
|
with open("/var/www/swingmusic/.swingmusic/music.db", "wb") as f:
|
|
f.write(download.content)
|
|
except:
|
|
print("Could not download the database.")
|
|
exit()
|
|
|
|
def extract_filename(filepath):
|
|
return os.path.basename(filepath)
|
|
|
|
def get_scrobbles():
|
|
get_db_values()
|
|
|
|
swing_conn = sqlite3.connect('/var/www/swingmusic/.swingmusic/swingmusic.db')
|
|
swing_cursor = swing_conn.cursor()
|
|
swing_cursor.execute('SELECT timestamp, duration, extra FROM scrobble ORDER BY timestamp DESC')
|
|
scrobbles = swing_cursor.fetchall()
|
|
swing_conn.close()
|
|
|
|
music_conn = sqlite3.connect('/var/www/swingmusic/.swingmusic/music.db')
|
|
music_cursor = music_conn.cursor()
|
|
music_cursor.execute('SELECT listmusicname, listvideoid, filelocation FROM music')
|
|
music_data = {extract_filename(row[2]): (row[0], row[1]) for row in music_cursor.fetchall()}
|
|
music_conn.close()
|
|
|
|
total_durations = defaultdict(int)
|
|
enriched_data = []
|
|
for ts, duration, extra_json in scrobbles:
|
|
try:
|
|
extra = json.loads(extra_json.replace("'", '"'))
|
|
filepath = extra.get("filepath", "")
|
|
filename = extract_filename(filepath)
|
|
if filename in music_data:
|
|
title, videoid = music_data[filename]
|
|
total_durations[(title, videoid)] += duration
|
|
enriched_data.append({
|
|
'title': title,
|
|
'videoid': videoid,
|
|
'duration': duration,
|
|
'timestamp': ts
|
|
})
|
|
except json.JSONDecodeError:
|
|
continue
|
|
|
|
unique_tracks = {}
|
|
for track in enriched_data:
|
|
key = (track['title'], track['videoid'])
|
|
if key not in unique_tracks:
|
|
unique_tracks[key] = {
|
|
'title': track['title'],
|
|
'videoid': track['videoid'],
|
|
'total_duration': total_durations[key],
|
|
'last_played': track['timestamp']
|
|
}
|
|
return sorted(unique_tracks.values(), key=lambda x: x['total_duration'], reverse=True)
|
|
|
|
@app.route('/swingmusicwrapped.html')
|
|
def swing_history():
|
|
tracks = get_scrobbles()
|
|
return render_template_string('''
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Halhadus' SwingMusic Wrapped</title>
|
|
<link rel="icon" type="image/png" href="assets/favicon.png">
|
|
<meta name="description" content="Halhadus' SwingMusic Wrapped">
|
|
<meta name="author" content="Halhadus">
|
|
<meta property="og:title" content="Halhadus' SwingMusic Wrapped">
|
|
<meta property="og:description" content="Halhadus' SwingMusic Wrapped">
|
|
<meta property="og:url" content="https://halhadus.rocks/swingmusicwrapped.html">
|
|
<style>
|
|
:root {
|
|
--bg-color: #1f1f1f;
|
|
--card-bg: #2a2a2a;
|
|
--border-color: #333;
|
|
--text-color: #ffffff;
|
|
--font-family: 'JetBrains Mono', monospace;
|
|
}
|
|
body {
|
|
background: var(--bg-color);
|
|
color: var(--text-color);
|
|
font-family: var(--font-family);
|
|
margin: 0;
|
|
padding: 20px;
|
|
}
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
}
|
|
.header-buttons {
|
|
display: flex;
|
|
gap: 1rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
.play-button {
|
|
background: var(--card-bg);
|
|
border: 1px solid var(--border-color);
|
|
color: var(--text-color);
|
|
padding: 0.8rem 1.5rem;
|
|
border-radius: 5px;
|
|
text-decoration: none;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
}
|
|
.play-button:hover {
|
|
background: #444;
|
|
}
|
|
.history-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 2rem;
|
|
}
|
|
.history-table th, .history-table td {
|
|
border: 1px solid var(--border-color);
|
|
padding: 12px;
|
|
text-align: left;
|
|
}
|
|
.history-table th {
|
|
background: var(--card-bg);
|
|
}
|
|
.thumbnail {
|
|
width: 80px;
|
|
border-radius: 4px;
|
|
aspect-ratio: 16/9;
|
|
object-fit: cover;
|
|
border: 2px solid var(--border-color);
|
|
}
|
|
.no-thumbnail {
|
|
width: 80px;
|
|
height: 45px;
|
|
background: #333;
|
|
border-radius: 4px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.no-thumbnail span {
|
|
color: #666;
|
|
font-size: 0.8rem;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🎵 Halhadus' SwingMusic Wrapped</h1>
|
|
<div class="header-buttons">
|
|
<a href="/index.html" class="play-button">← Main Page</a>
|
|
<a href="https://git.halhadus.rocks/Halhadus/my-swingmusic-wrapped" class="play-button">📁 Source Code</a>
|
|
</div>
|
|
<table class="history-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Cover</th>
|
|
<th>Song</th>
|
|
<th>Total Play Time</th>
|
|
<th>Last Play Date</th>
|
|
<th>Play</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for track in tracks %}
|
|
<tr>
|
|
<td>
|
|
{% if is_youtube(track.videoid) %}
|
|
<img src="https://img.youtube.com/vi/{{ track.videoid }}/hqdefault.jpg" class="thumbnail" alt="{{ track.title }} cover">
|
|
{% else %}
|
|
<div class="no-thumbnail">
|
|
<span>N/A</span>
|
|
</div>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ track.title }}</td>
|
|
<td>{{ (track.total_duration // 3600)|int }}h {{ ((track.total_duration % 3600) // 60)|int }}m</td>
|
|
<td>{{ datetime.fromtimestamp(track.last_played).strftime('%Y-%m-%d %H:%M') }}</td>
|
|
<td><a href="https://music.youtube.com/watch?v={{ track.videoid }}" class="play-button" target="_blank">▶ YT Music</a></td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
''', tracks=tracks, datetime=datetime, is_youtube=is_youtube_id)
|
|
|
|
@app.route('/<path:path>')
|
|
def catch_all(path):
|
|
return redirect(f'https://halhadus.rocks/{path}')
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return redirect('https://halhadus.rocks')
|
|
|
|
if __name__ == '__main__':
|
|
app.run(host='127.0.0.1', port=os.environ.get("PORT"))
|