Refactoring de pmoflac -factorisation de code ogg et opus
This commit is contained in:
@@ -22,7 +22,7 @@ impl ReadHandle {
|
||||
cursor: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Pop le prochain morceau (avance le curseur)
|
||||
///
|
||||
/// Skip automatiquement les entrées invalides dans le cache.
|
||||
@@ -31,24 +31,24 @@ impl ReadHandle {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
let pos = self.cursor.load(Ordering::SeqCst);
|
||||
|
||||
|
||||
let core = self.playlist.core.read().await;
|
||||
|
||||
|
||||
// Fin de playlist ?
|
||||
if pos >= core.len() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
|
||||
let record = match core.get(pos) {
|
||||
Some(r) => r,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
|
||||
let cache_pk = record.cache_pk.clone();
|
||||
drop(core);
|
||||
|
||||
|
||||
// Vérifier validité dans le cache
|
||||
let cache = crate::manager::audio_cache()?;
|
||||
if cache.is_valid_pk(&cache_pk) {
|
||||
@@ -61,31 +61,33 @@ impl ReadHandle {
|
||||
let mut core = self.playlist.core.write().await;
|
||||
core.remove_by_cache_pk(&cache_pk);
|
||||
drop(core);
|
||||
|
||||
|
||||
// Sauvegarder si persistante
|
||||
if self.playlist.persistent {
|
||||
if let Some(persistence) = crate::manager::PlaylistManager().persistence() {
|
||||
let title = self.playlist.title().await;
|
||||
let core = self.playlist.core.read().await;
|
||||
let _ = persistence.save_playlist(&self.playlist.id, &title, &core.config, &core.tracks).await;
|
||||
let _ = persistence
|
||||
.save_playlist(&self.playlist.id, &title, &core.config, &core.tracks)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Ne pas avancer le curseur, continuer avec la position actuelle
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Peek le prochain morceau sans avancer le curseur
|
||||
pub async fn peek(&self) -> Result<Option<PlaylistTrack>> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
let pos = self.cursor.load(Ordering::SeqCst);
|
||||
let core = self.playlist.core.read().await;
|
||||
|
||||
|
||||
match core.get(pos) {
|
||||
Some(record) => {
|
||||
// Vérifier validité
|
||||
@@ -99,28 +101,28 @@ impl ReadHandle {
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Position actuelle du curseur
|
||||
pub fn position(&self) -> usize {
|
||||
self.cursor.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
|
||||
/// Nombre de morceaux restants (compte uniquement les valides)
|
||||
pub async fn remaining(&self) -> Result<usize> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
let pos = self.cursor.load(Ordering::SeqCst);
|
||||
let core = self.playlist.core.read().await;
|
||||
|
||||
|
||||
if pos >= core.len() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
|
||||
let cache = crate::manager::audio_cache()?;
|
||||
let mut count = 0;
|
||||
|
||||
|
||||
for i in pos..core.len() {
|
||||
if let Some(record) = core.get(i) {
|
||||
if cache.is_valid_pk(&record.cache_pk) {
|
||||
@@ -128,38 +130,38 @@ impl ReadHandle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
|
||||
/// Crée un nouveau handle avec cursor à 0
|
||||
pub fn get_new_handle(&self) -> Result<ReadHandle> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
Ok(ReadHandle::new(self.playlist.clone()))
|
||||
}
|
||||
|
||||
|
||||
/// Vérifie si la playlist est vivante
|
||||
pub fn is_alive(&self) -> bool {
|
||||
self.playlist.is_alive()
|
||||
}
|
||||
|
||||
|
||||
/// ID de la playlist
|
||||
pub fn id(&self) -> &str {
|
||||
&self.playlist.id
|
||||
}
|
||||
|
||||
|
||||
/// Génère un Container DIDL-Lite
|
||||
pub async fn to_container(&self) -> Result<Container> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
let title = self.playlist.title().await;
|
||||
let remaining = self.remaining().await?;
|
||||
|
||||
|
||||
Ok(Container {
|
||||
id: self.playlist.id.clone(),
|
||||
parent_id: "0".to_string(),
|
||||
@@ -172,48 +174,48 @@ impl ReadHandle {
|
||||
items: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Génère des Items DIDL-Lite depuis la position actuelle
|
||||
pub async fn to_items(&self, limit: usize) -> Result<Vec<Item>> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
let pos = self.cursor.load(Ordering::SeqCst);
|
||||
let core = self.playlist.core.read().await;
|
||||
let cache = crate::manager::audio_cache()?;
|
||||
|
||||
|
||||
// Récupérer base_url depuis le cache (via route_for)
|
||||
let mut items = Vec::new();
|
||||
let mut idx = 0;
|
||||
|
||||
|
||||
for i in pos..core.len() {
|
||||
if items.len() >= limit {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
let record = match core.get(i) {
|
||||
Some(r) => r,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
|
||||
// Vérifier validité
|
||||
if !cache.is_valid_pk(&record.cache_pk) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Charger métadonnées
|
||||
let metadata = match pmoaudiocache::get_metadata(&*cache, &record.cache_pk) {
|
||||
Ok(m) => m,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
|
||||
// Construire l'URL via route_for
|
||||
let url = cache.route_for(&record.cache_pk, None);
|
||||
|
||||
|
||||
// Créer le Resource DIDL
|
||||
let resource = metadata.to_didl_resource(url);
|
||||
|
||||
|
||||
// Créer l'Item
|
||||
let item = Item {
|
||||
id: format!("{}:{}", self.playlist.id, pos + idx),
|
||||
@@ -232,11 +234,11 @@ impl ReadHandle {
|
||||
resources: vec![resource],
|
||||
descriptions: vec![],
|
||||
};
|
||||
|
||||
|
||||
items.push(item);
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
|
||||
Ok(items)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,41 +22,41 @@ impl WriteHandle {
|
||||
_write_token: write_token,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Ajoute un morceau à la playlist
|
||||
pub async fn push(&self, cache_pk: String) -> Result<()> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
// Vérifier que le pk existe dans le cache
|
||||
let cache = crate::manager::audio_cache()?;
|
||||
if !cache.is_valid_pk(&cache_pk) {
|
||||
return Err(crate::Error::CacheEntryNotFound(cache_pk));
|
||||
}
|
||||
|
||||
|
||||
// Ajouter à la playlist
|
||||
let record = Record::new(cache_pk);
|
||||
let mut core = self.playlist.core.write().await;
|
||||
core.push(record);
|
||||
drop(core);
|
||||
|
||||
|
||||
self.playlist.touch().await;
|
||||
|
||||
|
||||
// Sauvegarder si persistante
|
||||
if self.playlist.persistent {
|
||||
self.save_to_db().await?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Ajoute plusieurs morceaux de manière atomique
|
||||
pub async fn push_set(&self, cache_pks: Vec<String>) -> Result<()> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
// Vérifier tous les pks d'abord
|
||||
let cache = crate::manager::audio_cache()?;
|
||||
for pk in &cache_pks {
|
||||
@@ -64,195 +64,194 @@ impl WriteHandle {
|
||||
return Err(crate::Error::CacheEntryNotFound(pk.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Créer tous les records
|
||||
let records: Vec<Record> = cache_pks.into_iter()
|
||||
.map(Record::new)
|
||||
.collect();
|
||||
|
||||
let records: Vec<Record> = cache_pks.into_iter().map(Record::new).collect();
|
||||
|
||||
// Ajouter atomiquement
|
||||
let mut core = self.playlist.core.write().await;
|
||||
core.push_all(records);
|
||||
drop(core);
|
||||
|
||||
|
||||
self.playlist.touch().await;
|
||||
|
||||
|
||||
// Une seule sauvegarde pour tout le batch
|
||||
if self.playlist.persistent {
|
||||
self.save_to_db().await?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Vide la playlist
|
||||
pub async fn flush(&self) -> Result<()> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
let mut core = self.playlist.core.write().await;
|
||||
core.clear();
|
||||
drop(core);
|
||||
|
||||
|
||||
self.playlist.touch().await;
|
||||
|
||||
|
||||
if self.playlist.persistent {
|
||||
self.save_to_db().await?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Supprime la playlist définitivement
|
||||
pub async fn delete(self) -> Result<()> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
// Marquer comme supprimée
|
||||
self.playlist.mark_deleted();
|
||||
|
||||
|
||||
// Supprimer du manager
|
||||
crate::manager::delete_playlist_internal(&self.playlist.id).await?;
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Change le titre
|
||||
pub async fn set_title(&self, title: String) -> Result<()> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
self.playlist.set_title(title).await;
|
||||
|
||||
|
||||
if self.playlist.persistent {
|
||||
self.save_to_db().await?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Change la capacité maximale
|
||||
pub async fn set_capacity(&self, max_size: Option<usize>) -> Result<()> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
let mut core = self.playlist.core.write().await;
|
||||
core.set_capacity(max_size);
|
||||
drop(core);
|
||||
|
||||
|
||||
self.playlist.touch().await;
|
||||
|
||||
|
||||
if self.playlist.persistent {
|
||||
self.save_to_db().await?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Change le TTL par défaut
|
||||
pub async fn set_default_ttl(&self, ttl: Option<Duration>) -> Result<()> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
let mut core = self.playlist.core.write().await;
|
||||
core.set_default_ttl(ttl);
|
||||
drop(core);
|
||||
|
||||
|
||||
self.playlist.touch().await;
|
||||
|
||||
|
||||
if self.playlist.persistent {
|
||||
self.save_to_db().await?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Clone vers une nouvelle playlist persistante
|
||||
pub async fn clone_as_persistent(&self, new_id: String) -> Result<WriteHandle> {
|
||||
if !self.playlist.is_alive() {
|
||||
return Err(crate::Error::PlaylistDeleted(self.playlist.id.clone()));
|
||||
}
|
||||
|
||||
|
||||
// Récupérer les données actuelles
|
||||
let title = self.playlist.title().await;
|
||||
let core = self.playlist.core.read().await;
|
||||
let config = core.config.clone();
|
||||
let tracks = core.snapshot();
|
||||
drop(core);
|
||||
|
||||
|
||||
// Créer la nouvelle playlist persistante
|
||||
let manager = crate::manager::PlaylistManager();
|
||||
let mut new_handle = manager.create_persistent_playlist(new_id).await?;
|
||||
|
||||
|
||||
// Copier le titre et la config
|
||||
new_handle.set_title(title).await?;
|
||||
new_handle.set_capacity(config.max_size).await?;
|
||||
new_handle.set_default_ttl(config.default_ttl).await?;
|
||||
|
||||
|
||||
// Copier tous les morceaux
|
||||
let pks: Vec<String> = tracks.iter()
|
||||
.map(|r| r.cache_pk.clone())
|
||||
.collect();
|
||||
let pks: Vec<String> = tracks.iter().map(|r| r.cache_pk.clone()).collect();
|
||||
new_handle.push_set(pks).await?;
|
||||
|
||||
|
||||
Ok(new_handle)
|
||||
}
|
||||
|
||||
|
||||
// Métadonnées
|
||||
|
||||
|
||||
pub fn id(&self) -> &str {
|
||||
&self.playlist.id
|
||||
}
|
||||
|
||||
|
||||
pub async fn title(&self) -> String {
|
||||
self.playlist.title().await
|
||||
}
|
||||
|
||||
|
||||
pub fn is_persistent(&self) -> bool {
|
||||
self.playlist.persistent
|
||||
}
|
||||
|
||||
|
||||
pub async fn capacity(&self) -> Option<usize> {
|
||||
let core = self.playlist.core.read().await;
|
||||
core.config.max_size
|
||||
}
|
||||
|
||||
|
||||
pub async fn default_ttl(&self) -> Option<Duration> {
|
||||
let core = self.playlist.core.read().await;
|
||||
core.config.default_ttl
|
||||
}
|
||||
|
||||
|
||||
pub async fn len(&self) -> usize {
|
||||
let core = self.playlist.core.read().await;
|
||||
core.len()
|
||||
}
|
||||
|
||||
|
||||
pub async fn is_empty(&self) -> bool {
|
||||
let core = self.playlist.core.read().await;
|
||||
core.is_empty()
|
||||
}
|
||||
|
||||
|
||||
pub async fn last_change(&self) -> SystemTime {
|
||||
self.playlist.last_change().await
|
||||
}
|
||||
|
||||
|
||||
// Helpers internes
|
||||
|
||||
|
||||
async fn save_to_db(&self) -> Result<()> {
|
||||
let manager = crate::manager::PlaylistManager();
|
||||
let persistence = manager.persistence()
|
||||
let persistence = manager
|
||||
.persistence()
|
||||
.ok_or_else(|| crate::Error::PersistenceError("No persistence manager".into()))?;
|
||||
|
||||
|
||||
let title = self.playlist.title().await;
|
||||
let core = self.playlist.core.read().await;
|
||||
let config = &core.config;
|
||||
let tracks = &core.tracks;
|
||||
|
||||
persistence.save_playlist(&self.playlist.id, &title, config, tracks).await
|
||||
|
||||
persistence
|
||||
.save_playlist(&self.playlist.id, &title, config, tracks)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user