diff --git a/Cargo.lock b/Cargo.lock
index 556f69c..4fe3cf9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1049,7 +1049,6 @@ dependencies = [
"log",
"mpris-player",
"once_cell",
- "rand",
"serde",
"serde_json",
"tracing-subscriber",
@@ -1206,12 +1205,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
-[[package]]
-name = "ppv-lite86"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
-
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
@@ -1273,36 +1266,6 @@ dependencies = [
"proc-macro2",
]
-[[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom",
-]
-
[[package]]
name = "regex"
version = "1.10.2"
diff --git a/Cargo.toml b/Cargo.toml
index 82d1d28..674249e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,7 +17,6 @@ lazy_static = "1"
log = "0.4"
mpris-player = "0.6"
once_cell = "1"
-rand = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tracing-subscriber = "0.3"
diff --git a/data/de.johrpan.musicus.gschema.xml b/data/de.johrpan.musicus.gschema.xml
index 97e8741..ea75434 100644
--- a/data/de.johrpan.musicus.gschema.xml
+++ b/data/de.johrpan.musicus.gschema.xml
@@ -19,7 +19,7 @@
- '{"title":"Just play some music","description":"Randomly select some music. Customize programs using the button in the top right.","design":"Program1","prefer_recently_added":0.0,"prefer_least_recently_played":0.0,"play_full_recordings":true}'
+ '{"title":"Just play some music","description":"Randomly select some music. Customize programs using the button in the top right.","design":"Program1","prefer_recently_added":0.0,"prefer_least_recently_played":0.1,"play_full_recordings":true}'
Default settings for program 1
@@ -29,7 +29,7 @@
- '{"title":"A long time ago","description":"Works that you haven\'t listend to for a long time.","design":"Program3","prefer_recently_added":-1.0,"prefer_least_recently_played":1.0,"play_full_recordings":false}'
+ '{"title":"A long time ago","description":"Works that you haven\'t listend to for a long time.","design":"Program3","prefer_recently_added":0.0,"prefer_least_recently_played":1.0,"play_full_recordings":false}'
Default settings for program 3
diff --git a/src/library.rs b/src/library.rs
index b752721..ffad06c 100644
--- a/src/library.rs
+++ b/src/library.rs
@@ -5,7 +5,13 @@ use std::{
use anyhow::Result;
use chrono::prelude::*;
-use diesel::{dsl::exists, prelude::*, QueryDsl, SqliteConnection};
+use diesel::{
+ dsl::{exists, sql},
+ prelude::*,
+ sql_query,
+ sql_types::BigInt,
+ QueryDsl, SqliteConnection,
+};
use gtk::{glib, glib::Properties, prelude::*, subclass::prelude::*};
use crate::{
@@ -414,7 +420,59 @@ impl MusicusLibrary {
query = query.filter(album_recordings::album_id.eq(album_id));
}
- // TODO: Implement prefer_recently_added and prefer_least_recently_played.
+ if program.prefer_recently_added() > 0.0 {
+ let oldest_timestamp = sql_query(
+ "SELECT CAST(STRFTIME('%s', MIN(created_at)) AS INTEGER) AS value FROM recordings",
+ )
+ .get_result::(connection)?
+ .value;
+
+ let newest_timestamp = sql_query(
+ "SELECT CAST(STRFTIME('%s', MAX(created_at)) AS INTEGER) AS value FROM recordings",
+ )
+ .get_result::(connection)?
+ .value;
+
+ let range = newest_timestamp - oldest_timestamp;
+
+ if range >= 60 {
+ let proportion = program.prefer_recently_added().max(1.0) * 0.9;
+ let cutoff_timestamp =
+ oldest_timestamp + (proportion * range as f64).floor() as i64;
+
+ query = query.filter(
+ sql::("CAST(STRFTIME('%s', recordings.created_at) AS INTEGER)")
+ .ge(cutoff_timestamp)
+ .or(recordings::last_played_at.is_null()),
+ );
+ }
+ }
+
+ if program.prefer_least_recently_played() > 0.0 {
+ let oldest_timestamp =
+ sql_query("SELECT CAST(STRFTIME('%s', MIN(last_played_at)) AS INTEGER) AS value FROM recordings")
+ .get_result::(connection)?
+ .value;
+
+ let newest_timestamp =
+ sql_query("SELECT CAST(STRFTIME('%s', MAX(last_played_at)) AS INTEGER) AS value FROM recordings")
+ .get_result::(connection)?
+ .value;
+
+ let range = newest_timestamp - oldest_timestamp;
+
+ if range >= 60 {
+ let proportion = 1.0 - program.prefer_least_recently_played().max(1.0) * 0.9;
+ let cutoff_timestamp =
+ oldest_timestamp + (proportion * range as f64).floor() as i64;
+
+ query = query.filter(
+ sql::("CAST(STRFTIME('%s', recordings.last_played_at) AS INTEGER)")
+ .le(cutoff_timestamp)
+ .or(recordings::last_played_at.is_null()),
+ );
+ }
+ }
let row = query
.order(random())
@@ -424,6 +482,31 @@ impl MusicusLibrary {
Recording::from_table(row, &self.folder(), connection)
}
+ pub fn track_played(&self, track_id: &str) -> Result<()> {
+ let mut binding = self.imp().connection.borrow_mut();
+ let connection = &mut *binding.as_mut().unwrap();
+
+ let now = Local::now().naive_local();
+
+ diesel::update(recordings::table)
+ .filter(exists(
+ tracks::table.filter(
+ tracks::track_id
+ .eq(track_id)
+ .and(tracks::recording_id.eq(recordings::recording_id)),
+ ),
+ ))
+ .set(recordings::last_played_at.eq(now))
+ .execute(connection)?;
+
+ diesel::update(tracks::table)
+ .filter(tracks::track_id.eq(track_id))
+ .set(tracks::last_played_at.eq(now))
+ .execute(connection)?;
+
+ Ok(())
+ }
+
pub fn search_persons(&self, search: &str) -> Result> {
let search = format!("%{}%", search);
let mut binding = self.imp().connection.borrow_mut();
@@ -555,3 +638,9 @@ impl LibraryResults {
&& self.albums.is_empty()
}
}
+
+#[derive(QueryableByName)]
+pub struct IntegerValue {
+ #[diesel(sql_type = diesel::sql_types::BigInt)]
+ pub value: i64,
+}
diff --git a/src/player.rs b/src/player.rs
index 46134eb..d047db0 100644
--- a/src/player.rs
+++ b/src/player.rs
@@ -90,6 +90,13 @@ mod imp {
self.current_index.set(index);
item.set_is_playing(true);
+
+ self.library
+ .borrow()
+ .as_ref()
+ .unwrap()
+ .track_played(&item.track_id())
+ .unwrap();
}
}
}
@@ -228,6 +235,7 @@ impl MusicusPlayer {
Some(&performances),
None,
&tracks[0].path,
+ &tracks[0].track_id,
));
} else {
let mut tracks = tracks.into_iter();
@@ -254,6 +262,7 @@ impl MusicusPlayer {
Some(&performances),
Some(&track_title(&first_track, 1)),
&first_track.path,
+ &first_track.track_id,
));
for (index, track) in tracks.enumerate() {
@@ -264,6 +273,7 @@ impl MusicusPlayer {
// track number = track index + 1 (first track) + 1 (zero based)
Some(&track_title(&track, index + 2)),
&track.path,
+ &track.track_id,
));
}
}
diff --git a/src/playlist_item.rs b/src/playlist_item.rs
index 9f33432..279688d 100644
--- a/src/playlist_item.rs
+++ b/src/playlist_item.rs
@@ -27,6 +27,9 @@ mod imp {
#[property(get, construct_only)]
pub path: OnceCell,
+
+ #[property(get, construct_only)]
+ pub track_id: OnceCell,
}
#[glib::object_subclass]
@@ -50,6 +53,7 @@ impl PlaylistItem {
performers: Option<&str>,
part_title: Option<&str>,
path: impl AsRef,
+ track_id: &str,
) -> Self {
glib::Object::builder()
.property("is-title", is_title)
@@ -57,6 +61,7 @@ impl PlaylistItem {
.property("performers", performers)
.property("part-title", part_title)
.property("path", path.as_ref())
+ .property("track-id", track_id)
.build()
}