Skip to content

Commit

Permalink
Merge pull request #4 from woodruffw-forks/ww/more-refurbish
Browse files Browse the repository at this point in the history
More refurbishment
  • Loading branch information
xeals authored May 27, 2022
2 parents 02ec798 + 9569888 commit f19dbc8
Show file tree
Hide file tree
Showing 21 changed files with 301 additions and 218 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ readme = "README.md"
keywords = ["subsonic", "airsonic", "music", "api", "webapi"]
categories = ["api-bindings"]
license = "Apache-2.0/MIT"
edition = "2021"

[dependencies]
failure = "0.1.3"
log = "0.4.6"
md5 = "0.6.0"
rand = "0.6.1"
readonly = "0.2"
serde = "1.0.80"
serde_derive = "1.0.80"
serde_json = "1.0.33"
Expand Down
6 changes: 4 additions & 2 deletions src/annotate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use query::Query;
use {Album, Artist, Client, Error, Result, Song};
//! Annotation APIs.

use crate::query::Query;
use crate::{Album, Artist, Client, Error, Result, Song};

/// Allows starring, rating, and scrobbling media.
pub trait Annotatable {
Expand Down
30 changes: 14 additions & 16 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
use media::NowPlaying;
use query::Query;
use std::io::Read;
use std::iter;

use md5;
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use reqwest::Client as ReqwestClient;
use reqwest::Url;
use response::Response;
use search::{SearchPage, SearchResult};
use serde_json;
use {Error, Genre, Hls, Lyrics, MusicFolder, Result, UrlError, Version};

use crate::media::NowPlaying;
use crate::query::Query;
use crate::response::Response;
use crate::search::{SearchPage, SearchResult};
use crate::{Error, Genre, Hls, Lyrics, MusicFolder, Result, UrlError, Version};

const SALT_SIZE: usize = 36; // Minimum 6 characters.

Expand Down Expand Up @@ -73,11 +79,6 @@ impl SubsonicAuth {
fn to_url(&self, ver: Version) -> String {
// First md5 support.
let auth = if ver >= "1.13.0".into() {
use std::iter;

use md5;
use rand::{distributions::Alphanumeric, thread_rng, Rng};

let mut rng = thread_rng();
let salt: String = iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
Expand Down Expand Up @@ -205,15 +206,13 @@ impl Client {

/// Returns a response as a vector of bytes rather than serialising it.
pub(crate) fn get_bytes(&self, query: &str, args: Query) -> Result<Vec<u8>> {
use std::io::Read;
let uri: Url = self.build_url(query, args)?.parse().unwrap();
let res = self.reqclient.get(uri).send()?;
Ok(res.bytes().map(|b| b.unwrap()).collect())
}

/// Returns the raw bytes of a HLS slice.
pub fn hls_bytes(&self, hls: &Hls) -> Result<Vec<u8>> {
use std::io::Read;
let url: Url = self.url.join(&hls.url)?;
let res = self.reqclient.get(url).send()?;
Ok(res.bytes().map(|b| b.unwrap()).collect())
Expand Down Expand Up @@ -387,9 +386,8 @@ pub struct License {

#[cfg(test)]
mod tests {
use test_util;

use super::*;
use crate::test_util;

#[test]
fn test_token_auth() {
Expand Down Expand Up @@ -424,8 +422,8 @@ mod tests {
fn demo_scan_status() {
let cli = test_util::demo_site().unwrap();
let (status, n) = cli.scan_status().unwrap();
assert_eq!(status, false);
assert_eq!(n, 521);
assert!(!status);
assert_eq!(n, 525);
}

#[test]
Expand Down
30 changes: 18 additions & 12 deletions src/collections/album.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
//! Album APIs.

use std::{fmt, result};

use query::{Arg, IntoArg, Query};
use search::SearchPage;
use serde::de::{Deserialize, Deserializer};
use serde_json;
use {Client, Error, Media, Result, Song};

use crate::query::{Arg, IntoArg, Query};
use crate::search::SearchPage;
use crate::{Client, Error, Media, Result, Song};

#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
pub enum ListType {
AlphaByArtist,
Expand Down Expand Up @@ -47,18 +51,20 @@ impl IntoArg for ListType {
}
}

#[allow(missing_docs)]
#[derive(Debug, Clone)]
#[readonly::make]
pub struct Album {
pub id: u64,
pub name: String,
pub artist: Option<String>,
artist_id: Option<u64>,
cover_id: Option<String>,
pub artist_id: Option<u64>,
pub cover_id: Option<String>,
pub duration: u64,
pub year: Option<u64>,
pub genre: Option<String>,
pub song_count: u64,
songs: Vec<Song>,
pub songs: Vec<Song>,
}

impl Album {
Expand Down Expand Up @@ -121,7 +127,7 @@ impl<'de> Deserialize<'de> for Album {
where
D: Deserializer<'de>,
{
#[derive(Debug, Deserialize)]
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct _Album {
id: String,
Expand All @@ -131,7 +137,7 @@ impl<'de> Deserialize<'de> for Album {
cover_art: Option<String>,
song_count: u64,
duration: u64,
created: String,
// created: String,
year: Option<u64>,
genre: Option<String>,
#[serde(default)]
Expand Down Expand Up @@ -179,6 +185,7 @@ impl Media for Album {
}
}

#[allow(missing_docs)]
#[derive(Debug)]
pub struct AlbumInfo {
pub notes: String,
Expand Down Expand Up @@ -246,14 +253,13 @@ where

#[cfg(test)]
mod tests {
use test_util;

use super::*;
use crate::test_util;

#[test]
fn demo_get_albums() {
let mut srv = test_util::demo_site().unwrap();
let albums = get_albums(&mut srv, ListType::AlphaByArtist, None, None, None).unwrap();
let srv = test_util::demo_site().unwrap();
let albums = get_albums(&srv, ListType::AlphaByArtist, None, None, None).unwrap();

assert!(!albums.is_empty())
}
Expand Down
20 changes: 12 additions & 8 deletions src/collections/artist.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
//! Artist APIs.

use std::{fmt, result};

use query::Query;
use serde::de::{Deserialize, Deserializer};
use serde_json;
use {Album, Client, Error, Media, Result, Song};

use crate::query::Query;
use crate::{Album, Client, Error, Media, Result, Song};

/// Basic information about an artist.
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct Artist {
pub id: usize,
Expand All @@ -31,6 +35,7 @@ pub struct ArtistInfo {
}

impl Artist {
#[allow(missing_docs)]
pub fn get(client: &Client, id: usize) -> Result<Artist> {
self::get_artist(client, id)
}
Expand Down Expand Up @@ -187,9 +192,8 @@ fn get_artist(client: &Client, id: usize) -> Result<Artist> {

#[cfg(test)]
mod tests {
use test_util;

use super::*;
use crate::test_util;

#[test]
fn parse_artist() {
Expand All @@ -212,9 +216,9 @@ mod tests {

#[test]
fn remote_artist_album_list() {
let mut srv = test_util::demo_site().unwrap();
let srv = test_util::demo_site().unwrap();
let parsed = serde_json::from_value::<Artist>(raw()).unwrap();
let albums = parsed.albums(&mut srv).unwrap();
let albums = parsed.albums(&srv).unwrap();

assert_eq!(albums[0].id, 1);
assert_eq!(albums[0].name, String::from("Bellevue"));
Expand All @@ -223,11 +227,11 @@ mod tests {

#[test]
fn remote_artist_cover_art() {
let mut srv = test_util::demo_site().unwrap();
let srv = test_util::demo_site().unwrap();
let parsed = serde_json::from_value::<Artist>(raw()).unwrap();
assert_eq!(parsed.cover_id, Some(String::from("ar-1")));

let cover = parsed.cover_art(&mut srv, None).unwrap();
let cover = parsed.cover_art(&srv, None).unwrap();
assert!(!cover.is_empty())
}

Expand Down
8 changes: 5 additions & 3 deletions src/collections/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! Collections management APIs.

use std::result;

use serde::de::{Deserialize, Deserializer};

mod album;
mod artist;
mod playlist;
pub mod album;
pub mod artist;
pub mod playlist;

pub use self::album::{Album, AlbumInfo, ListType};
pub use self::artist::{Artist, ArtistInfo};
Expand Down
66 changes: 37 additions & 29 deletions src/collections/playlist.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
//! Playlist APIs.

use std::result;

use query::Query;
use serde::de::{Deserialize, Deserializer};
use serde_json;
use {Client, Error, Media, Result, Song};

use crate::query::Query;
use crate::{Client, Error, Media, Result, Song};

#[allow(missing_docs)]
#[derive(Debug)]
#[readonly::make]
pub struct Playlist {
id: u64,
name: String,
duration: u64,
cover_id: String,
song_count: u64,
songs: Vec<Song>,
pub id: u64,
pub name: String,
pub duration: u64,
pub cover_id: String,
pub song_count: u64,
pub songs: Vec<Song>,
}

impl Playlist {
Expand All @@ -31,18 +36,18 @@ impl<'de> Deserialize<'de> for Playlist {
where
D: Deserializer<'de>,
{
#[derive(Debug, Deserialize)]
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct _Playlist {
id: String,
name: String,
#[serde(default)]
comment: String,
owner: String,
// #[serde(default)]
// comment: String,
// owner: String,
song_count: u64,
duration: u64,
created: String,
changed: String,
// created: String,
// changed: String,
cover_art: String,
#[serde(default)]
songs: Vec<Song>,
Expand Down Expand Up @@ -85,12 +90,14 @@ impl Media for Playlist {
}
}

fn get_playlists(client: &Client, user: Option<String>) -> Result<Vec<Playlist>> {
#[allow(missing_docs)]
pub fn get_playlists(client: &Client, user: Option<String>) -> Result<Vec<Playlist>> {
let playlist = client.get("getPlaylists", Query::with("username", user))?;
Ok(get_list_as!(playlist, Playlist))
}

fn get_playlist(client: &Client, id: u64) -> Result<Playlist> {
#[allow(missing_docs)]
pub fn get_playlist(client: &Client, id: u64) -> Result<Playlist> {
let res = client.get("getPlaylist", Query::with("id", id))?;
Ok(serde_json::from_value::<Playlist>(res)?)
}
Expand All @@ -99,7 +106,7 @@ fn get_playlist(client: &Client, id: u64) -> Result<Playlist> {
///
/// Since API version 1.14.0, the newly created playlist is returned. In earlier
/// versions, an empty response is returned.
fn create_playlist(client: &Client, name: String, songs: &[u64]) -> Result<Option<Playlist>> {
pub fn create_playlist(client: &Client, name: String, songs: &[u64]) -> Result<Option<Playlist>> {
let args = Query::new()
.arg("name", name)
.arg_list("songId", songs)
Expand All @@ -116,7 +123,7 @@ fn create_playlist(client: &Client, name: String, songs: &[u64]) -> Result<Optio
}

/// Updates a playlist. Only the owner of the playlist is privileged to do so.
fn update_playlist<'a, B, S>(
pub fn update_playlist<'a, B, S>(
client: &Client,
id: u64,
name: S,
Expand All @@ -142,29 +149,30 @@ where
Ok(())
}

fn delete_playlist(client: &Client, id: u64) -> Result<()> {
#[allow(missing_docs)]
pub fn delete_playlist(client: &Client, id: u64) -> Result<()> {
client.get("deletePlaylist", Query::with("id", id))?;
Ok(())
}

#[cfg(test)]
mod tests {
use test_util;

use super::*;
use crate::test_util;

// The demo playlist exists, but can't be accessed
#[test]
fn remote_playlist_songs() {
let parsed = serde_json::from_value::<Playlist>(raw()).unwrap();
let mut srv = test_util::demo_site().unwrap();
let songs = parsed.songs(&mut srv);

match songs {
Err(::error::Error::Api(::error::ApiError::NotAuthorized(_))) => assert!(true),
Err(e) => panic!("unexpected error: {}", e),
Ok(_) => panic!("test should have failed; insufficient privilege"),
}
let srv = test_util::demo_site().unwrap();
let songs = parsed.songs(&srv);

assert!(matches!(
songs,
Err(crate::error::Error::Api(
crate::error::ApiError::NotAuthorized(_)
))
));
}

fn raw() -> serde_json::Value {
Expand Down
Loading

0 comments on commit f19dbc8

Please sign in to comment.