use std::ops::RangeInclusive;
use tokio::sync::{
Mutex as AsyncMutex,
RwLockReadGuard as AsyncRwLockReadGuard,
};
use crate::channel::Channel;
use crate::command::browse::*;
use crate::command::{Command, CommandError};
use crate::data::media::{AlbumMetadata, MediaItem, MediaItemsResponse};
use crate::data::option::*;
use crate::data::source::*;
use crate::state::{locked_data_iter, FromLockedData};
#[derive(Debug)]
pub(super) struct SourceData {
pub info: SourceInfo,
}
impl SourceData {
pub async fn get(_channel: &AsyncMutex<Channel>, info: SourceInfo) -> Result<Self, CommandError> {
Ok(Self {
info,
})
}
}
pub struct Source<'a> {
channel: &'a AsyncMutex<Channel>,
data: AsyncRwLockReadGuard<'a, SourceData>,
}
impl<'a> FromLockedData<'a> for Source<'a> {
type Data = SourceData;
#[inline]
fn from_locked_data(
channel: &'a AsyncMutex<Channel>,
data: AsyncRwLockReadGuard<'a, Self::Data>
) -> Self
where
Self: 'a,
{
Self {
channel,
data,
}
}
}
impl<'a> Source<'a> {
#[inline]
pub fn info(&self) -> &SourceInfo {
&self.data.info
}
async fn retrieve_all<C>(
&self,
cmd_fn: impl Fn(Option<RangeInclusive<usize>>) -> C,
) -> Result<WithOptions<Vec<MediaItem>>, CommandError>
where
C: Command<Response=WithOptions<MediaItemsResponse>>,
{
let response = self.channel.lock().await
.send_command(cmd_fn(None)).await?;
let total_count = response.value.count;
let mut all_items = response.value.items;
let options = response.options;
let batch_size = all_items.len();
while all_items.len() < total_count {
let current_count = all_items.len();
let response = self.channel.lock().await
.send_command(cmd_fn(Some(current_count..=(current_count+batch_size-1)))).await?;
all_items.extend(response.value.items);
}
Ok(WithOptions {
value: all_items,
options,
})
}
pub async fn browse(&self) -> Result<WithOptions<Vec<MediaItem>>, CommandError> {
let source_id = self.data.info.source_id;
self.retrieve_all(move |range| Browse {
source_id,
container_id: None,
range,
}).await
}
pub async fn browse_container(
&self,
container_id: impl Into<String>,
) -> Result<WithOptions<Vec<MediaItem>>, CommandError> {
let source_id = self.data.info.source_id;
let container_id = container_id.into();
self.retrieve_all(move |range| Browse {
source_id,
container_id: Some(container_id.clone()),
range,
}).await
}
pub async fn browse_container_range(
&self,
container_id: impl Into<String>,
range: RangeInclusive<usize>,
) -> Result<WithOptions<MediaItemsResponse>, CommandError> {
self.channel.lock().await.send_command(Browse {
source_id: self.data.info.source_id,
container_id: Some(container_id.into()),
range: Some(range),
}).await
}
pub async fn search_criteria(&self) -> Result<Vec<SearchCriteria>, CommandError> {
self.channel.lock().await
.send_command(GetSearchCriteria {
source_id: self.data.info.source_id,
}).await
}
pub async fn search(
&self,
search: impl Into<String>,
criteria: CriteriaId,
) -> Result<WithOptions<Vec<MediaItem>>, CommandError> {
let source_id = self.data.info.source_id;
let search = search.into();
self.retrieve_all(move |range| Search {
source_id,
search: search.clone(),
criteria,
range,
}).await
}
pub async fn search_range(
&self,
search: impl Into<String>,
criteria: CriteriaId,
range: RangeInclusive<usize>,
) -> Result<WithOptions<MediaItemsResponse>, CommandError> {
self.channel.lock().await.send_command(Search {
source_id: self.data.info.source_id,
search: search.into(),
criteria,
range: Some(range),
}).await
}
pub async fn rename_playlist(
&self,
container_id: impl Into<String>,
new_name: impl Into<String>,
) -> Result<(), CommandError> {
self.channel.lock().await
.send_command(RenamePlaylist {
source_id: self.data.info.source_id,
container_id: container_id.into(),
name: new_name.into(),
}).await
}
pub async fn delete_playlist(
&self,
container_id: impl Into<String>,
) -> Result<(), CommandError> {
self.channel.lock().await
.send_command(DeletePlaylist {
source_id: self.data.info.source_id,
container_id: container_id.into(),
}).await
}
pub async fn album_metadata(
&self,
container_id: impl Into<String>,
) -> Result<Vec<AlbumMetadata>, CommandError> {
self.channel.lock().await
.send_command(GetAlbumMetadata {
source_id: self.data.info.source_id,
container_id: container_id.into(),
}).await
}
pub async fn set_service_option(
&self,
option: ServiceOption,
) -> Result<(), CommandError> {
self.channel.lock().await
.send_command(SetServiceOption {
source_id: self.data.info.source_id,
option,
}).await
}
}
locked_data_iter!(SourcesIter, SourceId, SourceData, Source);