Correction de petits bugs d'interface
This commit is contained in:
@@ -47,6 +47,8 @@ pub struct PlaylistSummaryResponse {
|
||||
pub cover_pk: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cover_url: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub artist: Option<String>,
|
||||
pub track_count: usize,
|
||||
pub max_size: Option<usize>,
|
||||
pub default_ttl_secs: Option<u64>,
|
||||
@@ -103,6 +105,8 @@ pub struct UpdatePlaylistRequest {
|
||||
pub default_ttl_secs: Option<Option<u64>>,
|
||||
/// Utiliser `null` explicite pour supprimer la cover, ou omettre pour ne pas modifier.
|
||||
pub cover_pk: Option<Option<String>>,
|
||||
/// Utiliser `null` explicite pour supprimer l'artiste, ou omettre pour ne pas modifier.
|
||||
pub artist: Option<Option<String>>,
|
||||
}
|
||||
|
||||
/// Requête pour ajouter des morceaux dans une playlist.
|
||||
@@ -262,6 +266,7 @@ pub async fn update_playlist(
|
||||
max_size,
|
||||
default_ttl_secs,
|
||||
cover_pk,
|
||||
artist,
|
||||
} = req;
|
||||
|
||||
let manager = crate::manager::PlaylistManager();
|
||||
@@ -289,6 +294,9 @@ pub async fn update_playlist(
|
||||
};
|
||||
writer.set_cover_pk(normalized).await?;
|
||||
}
|
||||
if let Some(artist) = artist {
|
||||
writer.set_artist(artist).await?;
|
||||
}
|
||||
|
||||
manager.playlist_snapshot(&playlist_id).await
|
||||
}
|
||||
@@ -545,6 +553,7 @@ impl From<PlaylistOverview> for PlaylistSummaryResponse {
|
||||
persistent: value.persistent,
|
||||
cover_pk: cover_pk.clone(),
|
||||
cover_url: cover_pk.as_deref().map(cover_url_from_pk),
|
||||
artist: value.artist,
|
||||
track_count: value.track_count,
|
||||
max_size: value.max_size,
|
||||
default_ttl_secs: value.default_ttl.map(|ttl| ttl.as_secs()),
|
||||
|
||||
@@ -69,6 +69,7 @@ impl ReadHandle {
|
||||
let title = self.playlist.title().await;
|
||||
let role = self.playlist.role().await;
|
||||
let cover_pk = self.playlist.cover_pk().await;
|
||||
let artist = self.playlist.artist().await;
|
||||
let core = self.playlist.core.read().await;
|
||||
let _ = persistence
|
||||
.save_playlist(
|
||||
@@ -76,6 +77,7 @@ impl ReadHandle {
|
||||
&title,
|
||||
&role,
|
||||
cover_pk.as_deref(),
|
||||
artist.as_deref(),
|
||||
&core.config,
|
||||
&core.tracks,
|
||||
)
|
||||
@@ -175,8 +177,13 @@ impl ReadHandle {
|
||||
}
|
||||
|
||||
let title = self.playlist.title().await;
|
||||
let artist = self.playlist.artist().await;
|
||||
let cover_pk = self.playlist.cover_pk().await;
|
||||
let _remaining = self.remaining().await?;
|
||||
|
||||
// Convertir cover_pk en URL si présent
|
||||
let album_art = cover_pk.map(|pk| format!("/cover/{}", pk));
|
||||
|
||||
Ok(Container {
|
||||
id: self.playlist.id.clone(),
|
||||
parent_id: "0".to_string(),
|
||||
@@ -185,6 +192,8 @@ impl ReadHandle {
|
||||
searchable: Some("0".to_string()),
|
||||
title,
|
||||
class: "object.container.playlistContainer".to_string(),
|
||||
artist,
|
||||
album_art,
|
||||
containers: vec![],
|
||||
items: vec![],
|
||||
})
|
||||
|
||||
@@ -309,6 +309,23 @@ impl WriteHandle {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Met à jour l'artiste associé à la playlist.
|
||||
pub async fn set_artist(&self, artist: Option<String>) -> Result<()> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
self.playlist.set_artist(artist).await;
|
||||
|
||||
if self.playlist.persistent {
|
||||
self.save_to_db().await?;
|
||||
}
|
||||
|
||||
crate::manager::PlaylistManager().notify_playlist_changed(&self.playlist.id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Vérifie si la playlist contient déjà un pk
|
||||
pub async fn contains_pk(&self, cache_pk: &str) -> Result<bool> {
|
||||
if !self.playlist.is_alive() {
|
||||
@@ -549,6 +566,7 @@ impl WriteHandle {
|
||||
let tracks = &core.tracks;
|
||||
|
||||
let cover_pk = self.playlist.cover_pk().await;
|
||||
let artist = self.playlist.artist().await;
|
||||
|
||||
persistence
|
||||
.save_playlist(
|
||||
@@ -556,6 +574,7 @@ impl WriteHandle {
|
||||
&title,
|
||||
&role,
|
||||
cover_pk.as_deref(),
|
||||
artist.as_deref(),
|
||||
config,
|
||||
tracks,
|
||||
)
|
||||
|
||||
@@ -71,6 +71,7 @@ pub struct PlaylistOverview {
|
||||
pub role: PlaylistRole,
|
||||
pub persistent: bool,
|
||||
pub cover_pk: Option<String>,
|
||||
pub artist: Option<String>,
|
||||
pub track_count: usize,
|
||||
pub max_size: Option<usize>,
|
||||
pub default_ttl: Option<Duration>,
|
||||
@@ -205,6 +206,7 @@ impl PlaylistManager {
|
||||
let title = playlist.title().await;
|
||||
let role = playlist.role().await;
|
||||
let cover_pk = playlist.cover_pk().await;
|
||||
let artist = playlist.artist().await;
|
||||
let core = playlist.core.read().await;
|
||||
persistence
|
||||
.save_playlist(
|
||||
@@ -212,6 +214,7 @@ impl PlaylistManager {
|
||||
&title,
|
||||
&role,
|
||||
cover_pk.as_deref(),
|
||||
artist.as_deref(),
|
||||
&core.config,
|
||||
&core.tracks,
|
||||
)
|
||||
@@ -519,7 +522,7 @@ impl PlaylistManager {
|
||||
|
||||
// Pas en mémoire, essayer de charger depuis la DB
|
||||
if let Some(persistence) = &self.inner.persistence {
|
||||
if let Some((title, role, config, cover_pk, tracks)) =
|
||||
if let Some((title, role, config, cover_pk, artist, tracks)) =
|
||||
persistence.load_playlist(&id).await?
|
||||
{
|
||||
// Reconstruire la playlist
|
||||
@@ -534,6 +537,11 @@ impl PlaylistManager {
|
||||
cover_pk,
|
||||
));
|
||||
|
||||
// Restaurer l'artiste si présent
|
||||
if let Some(artist_name) = artist {
|
||||
playlist.set_artist(Some(artist_name)).await;
|
||||
}
|
||||
|
||||
// Restaurer les tracks
|
||||
{
|
||||
let mut core = playlist.core.write().await;
|
||||
@@ -575,7 +583,7 @@ impl PlaylistManager {
|
||||
|
||||
// Pas en m<>moire, essayer de ressusciter depuis la DB
|
||||
if let Some(persistence) = &self.inner.persistence {
|
||||
if let Some((title, role, config, cover_pk, tracks)) =
|
||||
if let Some((title, role, config, cover_pk, artist, tracks)) =
|
||||
persistence.load_playlist(id).await?
|
||||
{
|
||||
// Reconstruire la playlist
|
||||
@@ -590,6 +598,11 @@ impl PlaylistManager {
|
||||
cover_pk,
|
||||
));
|
||||
|
||||
// Restaurer l'artiste si présent
|
||||
if let Some(artist_name) = artist {
|
||||
playlist.set_artist(Some(artist_name)).await;
|
||||
}
|
||||
|
||||
// Restaurer les tracks
|
||||
{
|
||||
let mut core = playlist.core.write().await;
|
||||
@@ -653,12 +666,15 @@ impl PlaylistManager {
|
||||
let track_count = core.len();
|
||||
let config = core.config.clone();
|
||||
|
||||
let artist = playlist.artist().await;
|
||||
|
||||
Ok(PlaylistOverview {
|
||||
id: playlist.id.clone(),
|
||||
title,
|
||||
role,
|
||||
persistent,
|
||||
cover_pk,
|
||||
artist,
|
||||
track_count,
|
||||
max_size: config.max_size,
|
||||
default_ttl: config.default_ttl,
|
||||
@@ -978,6 +994,7 @@ impl PlaylistManager {
|
||||
let title = playlist.title().await;
|
||||
let role = playlist.role().await;
|
||||
let cover_pk = playlist.cover_pk().await;
|
||||
let artist = playlist.artist().await;
|
||||
let core = playlist.core.read().await;
|
||||
let _ = persistence
|
||||
.save_playlist(
|
||||
@@ -985,6 +1002,7 @@ impl PlaylistManager {
|
||||
&title,
|
||||
&role,
|
||||
cover_pk.as_deref(),
|
||||
artist.as_deref(),
|
||||
&core.config,
|
||||
&core.tracks,
|
||||
)
|
||||
|
||||
@@ -37,6 +37,7 @@ impl PersistenceManager {
|
||||
title TEXT NOT NULL,
|
||||
role TEXT NOT NULL,
|
||||
cover_pk TEXT,
|
||||
artist TEXT,
|
||||
max_size INTEGER,
|
||||
default_ttl_secs INTEGER,
|
||||
created_at INTEGER NOT NULL,
|
||||
@@ -86,6 +87,7 @@ impl PersistenceManager {
|
||||
title: &str,
|
||||
role: &PlaylistRole,
|
||||
cover_pk: Option<&str>,
|
||||
artist: Option<&str>,
|
||||
config: &PlaylistConfig,
|
||||
tracks: &VecDeque<Arc<Record>>,
|
||||
) -> Result<()> {
|
||||
@@ -98,15 +100,16 @@ impl PersistenceManager {
|
||||
|
||||
// Upsert playlist metadata
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO playlists (id, title, role, cover_pk, max_size, default_ttl_secs, created_at, last_modified)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6,
|
||||
COALESCE((SELECT created_at FROM playlists WHERE id = ?1), ?7),
|
||||
?7)",
|
||||
"INSERT OR REPLACE INTO playlists (id, title, role, cover_pk, artist, max_size, default_ttl_secs, created_at, last_modified)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7,
|
||||
COALESCE((SELECT created_at FROM playlists WHERE id = ?1), ?8),
|
||||
?8)",
|
||||
params![
|
||||
id,
|
||||
title,
|
||||
role.as_str(),
|
||||
cover_pk,
|
||||
artist,
|
||||
config.max_size.map(|s| s as i64),
|
||||
config.default_ttl.map(|d| d.as_secs() as i64),
|
||||
now_nanos,
|
||||
@@ -150,6 +153,7 @@ impl PersistenceManager {
|
||||
PlaylistRole,
|
||||
PlaylistConfig,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
VecDeque<Arc<Record>>,
|
||||
)>,
|
||||
> {
|
||||
@@ -157,7 +161,7 @@ impl PersistenceManager {
|
||||
|
||||
// Charger les métadonnées
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT title, role, cover_pk, max_size, default_ttl_secs FROM playlists WHERE id = ?1",
|
||||
"SELECT title, role, cover_pk, artist, max_size, default_ttl_secs FROM playlists WHERE id = ?1",
|
||||
)
|
||||
.map_err(|e| {
|
||||
crate::Error::PersistenceError(format!("Failed to prepare statement: {}", e))
|
||||
@@ -167,8 +171,9 @@ impl PersistenceManager {
|
||||
let title: String = row.get(0)?;
|
||||
let role_raw: String = row.get(1)?;
|
||||
let cover_pk: Option<String> = row.get(2)?;
|
||||
let max_size: Option<i64> = row.get(3)?;
|
||||
let default_ttl_secs: Option<i64> = row.get(4)?;
|
||||
let artist: Option<String> = row.get(3)?;
|
||||
let max_size: Option<i64> = row.get(4)?;
|
||||
let default_ttl_secs: Option<i64> = row.get(5)?;
|
||||
|
||||
Ok((
|
||||
title,
|
||||
@@ -179,10 +184,11 @@ impl PersistenceManager {
|
||||
default_ttl: default_ttl_secs.map(|s| Duration::from_secs(s as u64)),
|
||||
},
|
||||
cover_pk,
|
||||
artist,
|
||||
))
|
||||
});
|
||||
|
||||
let (title, role, config, cover_pk) = match result {
|
||||
let (title, role, config, cover_pk, artist) = match result {
|
||||
Ok(data) => data,
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => return Ok(None),
|
||||
Err(e) => {
|
||||
@@ -225,7 +231,7 @@ impl PersistenceManager {
|
||||
tracks.push_back(Arc::new(record));
|
||||
}
|
||||
|
||||
Ok(Some((title, role, config, cover_pk, tracks)))
|
||||
Ok(Some((title, role, config, cover_pk, artist, tracks)))
|
||||
}
|
||||
|
||||
/// Supprime une playlist
|
||||
|
||||
@@ -35,6 +35,7 @@ pub struct Playlist {
|
||||
title: RwLock<String>,
|
||||
role: RwLock<PlaylistRole>,
|
||||
cover_pk: RwLock<Option<String>>,
|
||||
artist: RwLock<Option<String>>,
|
||||
state: Arc<AtomicU8>,
|
||||
pub core: Arc<RwLock<PlaylistCore>>,
|
||||
pub persistent: bool,
|
||||
@@ -57,6 +58,7 @@ impl Playlist {
|
||||
title: RwLock::new(title),
|
||||
role: RwLock::new(role),
|
||||
cover_pk: RwLock::new(cover_pk),
|
||||
artist: RwLock::new(None),
|
||||
state: Arc::new(AtomicU8::new(PlaylistState::Active as u8)),
|
||||
core: Arc::new(RwLock::new(PlaylistCore::new(config))),
|
||||
persistent,
|
||||
@@ -114,6 +116,17 @@ impl Playlist {
|
||||
self.touch().await;
|
||||
}
|
||||
|
||||
/// Retourne l'artiste associé à la playlist.
|
||||
pub async fn artist(&self) -> Option<String> {
|
||||
self.artist.read().await.clone()
|
||||
}
|
||||
|
||||
/// Modifie l'artiste de la playlist.
|
||||
pub async fn set_artist(&self, value: Option<String>) {
|
||||
*self.artist.write().await = value;
|
||||
self.touch().await;
|
||||
}
|
||||
|
||||
/// Timestamp du dernier changement
|
||||
pub async fn last_change(&self) -> SystemTime {
|
||||
*self.last_change.read().await
|
||||
|
||||
Reference in New Issue
Block a user