Enorme refactoring de PMO control step 2
This commit is contained in:
@@ -15,18 +15,11 @@
|
||||
//! - maintains a `current_index`,
|
||||
//! - never starts playback (transport control is handled elsewhere).
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{
|
||||
DeviceId, RendererInfo,
|
||||
DeviceIdentity,
|
||||
DeviceId, DeviceIdentity, RendererInfo,
|
||||
errors::ControlPointError,
|
||||
queue::{
|
||||
MusicQueue,
|
||||
backend::{PlaybackItem, QueueBackend, QueueSnapshot},
|
||||
},
|
||||
queue::{MusicQueue, PlaybackItem, QueueBackend, QueueFromRendererInfo, QueueSnapshot},
|
||||
};
|
||||
|
||||
/// Internal/local queue implementation.
|
||||
@@ -54,27 +47,59 @@ impl InternalQueue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_renderer_info(
|
||||
info: &RendererInfo,
|
||||
) -> Result<InternalQueue, ControlPointError> {
|
||||
Ok(InternalQueue::new(
|
||||
info.id(),
|
||||
),
|
||||
)
|
||||
pub fn from_renderer_info(info: &RendererInfo) -> Result<InternalQueue, ControlPointError> {
|
||||
Ok(InternalQueue::new(info.id()))
|
||||
}
|
||||
|
||||
/// Exposes a read-only view of the underlying items.
|
||||
pub fn items(&self) -> &[PlaybackItem] {
|
||||
&self.items
|
||||
}
|
||||
|
||||
/// Exposes the current index (read-only).
|
||||
pub fn current_index(&self) -> Option<usize> {
|
||||
self.current_index
|
||||
}
|
||||
}
|
||||
|
||||
impl QueueBackend for InternalQueue {
|
||||
fn len(&self) -> Result<usize, ControlPointError> {
|
||||
Ok(self.items.len())
|
||||
}
|
||||
|
||||
fn track_ids(&self) -> Result<Vec<u32>, ControlPointError> {
|
||||
let ids: Vec<u32> = (0..self.len()?).map(|i| i as u32).collect();
|
||||
Ok(ids)
|
||||
}
|
||||
|
||||
fn id_to_position(&self, id: u32) -> Result<usize, ControlPointError> {
|
||||
Ok(id as usize)
|
||||
}
|
||||
|
||||
fn position_to_id(&self, id: usize) -> Result<u32, ControlPointError> {
|
||||
u32::try_from(id).map_err(|_| {
|
||||
ControlPointError::QueueError(format!(
|
||||
"Position {} exceeds u32::MAX",
|
||||
id
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn current_track(&self) -> Result<Option<u32>, ControlPointError> {
|
||||
match self.current_index {
|
||||
None => Ok(None),
|
||||
Some(i) => {
|
||||
u32::try_from(i)
|
||||
.map(Some)
|
||||
.map_err(|_| {
|
||||
ControlPointError::QueueError(format!(
|
||||
"Current index {} exceeds u32::MAX",
|
||||
i
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn current_index(&self) -> Result<Option<usize>, ControlPointError> {
|
||||
Ok(self.current_index)
|
||||
}
|
||||
|
||||
fn queue_snapshot(&self) -> Result<QueueSnapshot, ControlPointError> {
|
||||
let mut items = self.items.clone();
|
||||
for (i, item) in items.iter_mut().enumerate() {
|
||||
@@ -96,7 +121,11 @@ impl QueueBackend for InternalQueue {
|
||||
if i < self.items.len() {
|
||||
self.current_index = Some(i);
|
||||
} else {
|
||||
self.current_index = None;
|
||||
return Err(ControlPointError::QueueError(format!(
|
||||
"Index out of bound {} >= {}",
|
||||
i,
|
||||
self.items.len()
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,14 +142,187 @@ impl QueueBackend for InternalQueue {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sync_queue(
|
||||
&mut self,
|
||||
items: Vec<PlaybackItem>
|
||||
) -> Result<(), ControlPointError> {
|
||||
if items.is_empty() {
|
||||
return self.replace_queue(Vec::new(), None);
|
||||
}
|
||||
|
||||
// Récupérer l'item actuel
|
||||
let current = self.current_index
|
||||
.and_then(|idx| self.items.get(idx).map(|item| (idx, item.uri.clone())));
|
||||
|
||||
if let Some((_current_idx, current_uri)) = current {
|
||||
// Chercher l'item actuel dans la nouvelle liste (par URI)
|
||||
let new_idx = items.iter().position(|item| item.uri == current_uri);
|
||||
|
||||
if let Some(new_idx) = new_idx {
|
||||
// Item trouvé dans la nouvelle liste
|
||||
self.replace_queue(items, Some(new_idx))
|
||||
} else {
|
||||
// Item pas trouvé, le garder comme premier
|
||||
let current_item = self.items[self.current_index.unwrap()].clone();
|
||||
let mut new_items = Vec::with_capacity(items.len() + 1);
|
||||
new_items.push(current_item);
|
||||
new_items.extend(items);
|
||||
self.replace_queue(new_items, Some(0))
|
||||
}
|
||||
} else {
|
||||
// Pas d'item actuel
|
||||
self.replace_queue(items, None)
|
||||
}
|
||||
}
|
||||
|
||||
fn enqueue_items(
|
||||
&mut self,
|
||||
items: Vec<PlaybackItem>,
|
||||
mode: crate::queue::EnqueueMode,
|
||||
) -> Result<(), ControlPointError> {
|
||||
use crate::queue::EnqueueMode;
|
||||
|
||||
match mode {
|
||||
EnqueueMode::AppendToEnd => {
|
||||
self.items.extend(items);
|
||||
}
|
||||
EnqueueMode::InsertAfterCurrent => {
|
||||
let insert_pos = self.current_index
|
||||
.map(|i| (i + 1).min(self.items.len()))
|
||||
.unwrap_or(0);
|
||||
|
||||
for (offset, item) in items.into_iter().enumerate() {
|
||||
self.items.insert(insert_pos + offset, item);
|
||||
}
|
||||
}
|
||||
EnqueueMode::ReplaceAll => {
|
||||
self.items = items;
|
||||
self.current_index = None;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_item(&self, index: usize) -> Result<Option<PlaybackItem>, ControlPointError> {
|
||||
Ok(self.items.get(index).cloned())
|
||||
if index < self.items.len() {
|
||||
Ok(self.items.get(index).cloned())
|
||||
} else {
|
||||
Err(ControlPointError::QueueError(format!(
|
||||
"get_item index out of bound {} >= {}",
|
||||
index,
|
||||
self.items.len()
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
// Optimized helpers for InternalQueue
|
||||
fn clear_queue(&mut self) -> Result<(), ControlPointError> {
|
||||
self.items.clear();
|
||||
self.current_index = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> Result<bool, ControlPointError> {
|
||||
Ok(self.items.is_empty())
|
||||
}
|
||||
|
||||
fn upcoming_len(&self) -> Result<usize, ControlPointError> {
|
||||
let len = self.items.len();
|
||||
match self.current_index {
|
||||
None => Ok(len),
|
||||
Some(idx) => Ok(len.saturating_sub(idx + 1)),
|
||||
}
|
||||
}
|
||||
|
||||
fn upcoming_items(&self) -> Result<Vec<PlaybackItem>, ControlPointError> {
|
||||
let items = match self.current_index {
|
||||
None => self.items.clone(),
|
||||
Some(idx) => self.items.iter().skip(idx + 1).cloned().collect(),
|
||||
};
|
||||
Ok(items)
|
||||
}
|
||||
|
||||
fn peek_current(&self) -> Result<Option<(PlaybackItem, usize)>, ControlPointError> {
|
||||
if self.items.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let len = self.items.len();
|
||||
let (item, resolved_index) = match self.current_index {
|
||||
Some(idx) if idx < len => (self.items.get(idx).cloned(), Some(idx)),
|
||||
_ => (self.items.first().cloned(), None),
|
||||
};
|
||||
|
||||
let item = match item {
|
||||
Some(item) => item,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let remaining = match resolved_index {
|
||||
Some(idx) => len.saturating_sub(idx + 1),
|
||||
None => len,
|
||||
};
|
||||
|
||||
Ok(Some((item, remaining)))
|
||||
}
|
||||
|
||||
fn dequeue_next(&mut self) -> Result<Option<(PlaybackItem, usize)>, ControlPointError> {
|
||||
if self.items.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let len = self.items.len();
|
||||
let next_index = match self.current_index {
|
||||
None => 0,
|
||||
Some(idx) => {
|
||||
let candidate = idx + 1;
|
||||
if candidate >= len {
|
||||
return Ok(None);
|
||||
}
|
||||
candidate
|
||||
}
|
||||
};
|
||||
|
||||
let Some(item) = self.items.get(next_index).cloned() else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let remaining = len.saturating_sub(next_index + 1);
|
||||
self.current_index = Some(next_index);
|
||||
Ok(Some((item, remaining)))
|
||||
}
|
||||
|
||||
fn append_or_init_index(&mut self, items: Vec<PlaybackItem>) -> Result<(), ControlPointError> {
|
||||
let was_empty = self.items.is_empty();
|
||||
self.items.extend(items);
|
||||
|
||||
if was_empty && !self.items.is_empty() {
|
||||
self.current_index = Some(0);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn replace_item(&mut self, index: usize, item: PlaybackItem) -> Result<(), ControlPointError> {
|
||||
if index < self.items.len() {
|
||||
self.items[index] = item;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ControlPointError::QueueError(format!(
|
||||
"Index out of bound {} >= {}",
|
||||
index,
|
||||
self.items.len()
|
||||
)))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl QueueFromRendererInfo for InternalQueue {
|
||||
fn from_renderer_info(renderer: &RendererInfo) -> Result<Self, ControlPointError> {
|
||||
InternalQueue::from_renderer_info(renderer)
|
||||
}
|
||||
|
||||
fn to_backend(self) -> MusicQueue {
|
||||
MusicQueue::Internal(self)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user