From 122214a89622af7e8c3dda1e973e206ea4a6d9fd Mon Sep 17 00:00:00 2001 From: Ultradesu Date: Mon, 27 Jul 2026 23:41:29 +0100 Subject: [PATCH] Fixed playback history window --- Cargo.toml | 2 +- src/music/mod.rs | 43 ++++++++++++++++++++++++++++++++++++ src/player/mod.rs | 25 +++++++++++++-------- templates/player/shell.html | 6 ++++- templates/player/styles.html | 11 ++++++++- 5 files changed, 75 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 16b6638..0483473 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "furumusic" -version = "0.9.4" +version = "0.9.5" edition = "2024" description = "Reusable web-app boilerplate: auth, OIDC/SSO, admin panel, user management, i18n, PostgreSQL" diff --git a/src/music/mod.rs b/src/music/mod.rs index 880b0a9..eeb3284 100644 --- a/src/music/mod.rs +++ b/src/music/mod.rs @@ -2434,6 +2434,48 @@ pub mod db_migrations { &[Operation::custom(create_synced_listen_history).build()]; } + #[cot::db::migrations::migration_op] + async fn repair_legacy_listen_qualification( + ctx: migrations::MigrationContext<'_>, + ) -> cot::db::Result<()> { + ctx.db + .raw( + "UPDATE furumusic__listen_event le + SET qualified = ( + ph.completed + OR ( + COALESCE(ph.duration_listened, 0) >= 5 + AND COALESCE(t.duration_seconds, 0) > 0 + AND COALESCE(ph.duration_listened, 0) >= LEAST( + COALESCE(t.duration_seconds, 0) / 2.0, + 240.0 + ) + ) + ) + FROM furumusic__play_history ph + JOIN furumusic__track t ON t.id = ph.track_id + WHERE le.user_id = ph.user_id + AND le.listen_id = 'legacy-web:' || ph.id::text", + ) + .await?; + Ok(()) + } + + #[derive(Debug, Copy, Clone)] + pub struct M0042RepairLegacyListenQualification; + + impl migrations::Migration for M0042RepairLegacyListenQualification { + const APP_NAME: &'static str = "furumusic"; + const MIGRATION_NAME: &'static str = "m_0042_repair_legacy_listen_qualification"; + const DEPENDENCIES: &'static [migrations::MigrationDependency] = + &[migrations::MigrationDependency::migration( + "furumusic", + "m_0041_create_synced_listen_history", + )]; + const OPERATIONS: &'static [Operation] = + &[Operation::custom(repair_legacy_listen_qualification).build()]; + } + pub const MIGRATIONS: &[&SyncDynMigration] = &[ &M0006CreateMediaFile, &M0007CreateArtist, @@ -2466,5 +2508,6 @@ pub mod db_migrations { &M0039EnsureFederationContentIdCache, &M0040CreateContentAddressedMusicRefs, &M0041CreateSyncedListenHistory, + &M0042RepairLegacyListenQualification, ]; } diff --git a/src/player/mod.rs b/src/player/mod.rs index fb7d4a7..588df54 100644 --- a/src/player/mod.rs +++ b/src/player/mod.rs @@ -1259,15 +1259,19 @@ async fn me_handler( .await .map_err(|e| cot::Error::internal(e.to_string()))?; - let plays: (i64,) = - sqlx::query_as("SELECT COUNT(*) FROM furumusic__play_history WHERE user_id = $1") - .bind(user.id) - .fetch_one(pool) - .await - .map_err(|e| cot::Error::internal(e.to_string()))?; + let plays: (i64,) = sqlx::query_as( + "SELECT COUNT(*) FROM furumusic__listen_event + WHERE user_id = $1 AND qualified = true", + ) + .bind(user.id) + .fetch_one(pool) + .await + .map_err(|e| cot::Error::internal(e.to_string()))?; let listened_seconds: Option = sqlx::query_scalar( - "SELECT COALESCE(SUM(duration_listened), 0) FROM furumusic__play_history WHERE user_id = $1", + "SELECT COALESCE(SUM(listened_ms), 0) / 1000 + FROM furumusic__listen_event + WHERE user_id = $1 AND qualified = true", ) .bind(user.id) .fetch_one(pool) @@ -5573,7 +5577,8 @@ async fn history_list_handler( .map_err(|e| cot::Error::internal(e.to_string()))?; let rows = sqlx::query( - "SELECT le.listen_id, le.content_id, le.local_track_id, + "SELECT le.listen_id, le.content_id, + COALESCE(le.local_track_id, tr.local_track_id) AS local_track_id, le.origin_device_id, le.started_at_ms, le.listened_ms, le.track_duration_ms, le.metadata_json, COALESCE(NULLIF(d.name, ''), le.origin_device_id) AS device_name, @@ -5582,7 +5587,9 @@ async fn history_list_handler( FROM furumusic__listen_event le LEFT JOIN furumusic__fed_device d ON d.user_id = le.user_id AND d.device_id = le.origin_device_id - LEFT JOIN furumusic__track t ON t.id = le.local_track_id + LEFT JOIN furumusic__track_ref tr ON tr.content_id = le.content_id + LEFT JOIN furumusic__track t + ON t.id = COALESCE(le.local_track_id, tr.local_track_id) LEFT JOIN furumusic__release r ON r.id = t.release_id WHERE le.user_id = $1 AND le.qualified = true ORDER BY le.started_at_ms DESC, le.listen_id DESC diff --git a/templates/player/shell.html b/templates/player/shell.html index 869e8c4..7660752 100644 --- a/templates/player/shell.html +++ b/templates/player/shell.html @@ -1626,7 +1626,11 @@ x-text="'-' + formatTime(Math.max(0, $store.player.duration - $store.player.currentTime)) + ' / ' + formatTime($store.player.duration)"> -
v{{ t.app_version() }}
+ v{{ t.app_version() }}
diff --git a/templates/player/styles.html b/templates/player/styles.html index eabfe4a..1ccb2ec 100644 --- a/templates/player/styles.html +++ b/templates/player/styles.html @@ -2437,7 +2437,16 @@ button.user-stat:hover { line-height: 1; font-weight: 500; text-align: center; - pointer-events: none; + text-decoration: none; + pointer-events: auto; + transition: opacity 120ms ease; +} + +.player-version-chip:hover, +.player-version-chip:focus-visible { + color: var(--text-subdued); + opacity: 0.9; + text-decoration: none; } .mobile-account-chip {