chore: bump Invidious client deps, update tests
* set result sorting by most viewed to return the mostly seen video - which probably is the one we want (yes, wild assumption I know)0.3.x
parent
30990304d9
commit
0561c49d47
|
@ -12,7 +12,7 @@ pretty_env_logger = "0.5.0"
|
||||||
tokio = { version = "1.20.0", features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1.20.0", features = ["rt-multi-thread", "macros"] }
|
||||||
rspotify = { version = "0.12.0", features = ["default"]}
|
rspotify = { version = "0.12.0", features = ["default"]}
|
||||||
sentry = "0.32.1"
|
sentry = "0.32.1"
|
||||||
invidious = "0.2.1"
|
invidious = { version = "0.7.4", no-default-features = true, features = ["reqwest_async"]}
|
||||||
itertools = "0.12.0"
|
itertools = "0.12.0"
|
||||||
async-trait = "0.1.56"
|
async-trait = "0.1.56"
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use mockall::{automock, mock, predicate::*};
|
use mockall::{automock, predicate::*};
|
||||||
use rspotify::model::Country::UnitedStates;
|
use rspotify::model::Country::UnitedStates;
|
||||||
use rspotify::model::PlayableItem::{Episode, Track};
|
use rspotify::model::PlayableItem::{Episode, Track};
|
||||||
use rspotify::model::{AlbumId, PlaylistId, TrackId, Market};
|
use rspotify::model::{AlbumId, Market, PlaylistId, TrackId};
|
||||||
use rspotify::prelude::*;
|
use rspotify::prelude::*;
|
||||||
use rspotify::{ClientCredsSpotify, Credentials};
|
use rspotify::{ClientCredsSpotify, Credentials};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -101,7 +101,11 @@ impl SearchableClient for Client {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Search track from US market
|
// Search track from US market
|
||||||
match self.client.track(track_id, Some(Market::Country(UnitedStates))).await {
|
match self
|
||||||
|
.client
|
||||||
|
.track(track_id, Some(Market::Country(UnitedStates)))
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(track) => Some(TrackInfo {
|
Ok(track) => Some(TrackInfo {
|
||||||
name: track.name,
|
name: track.name,
|
||||||
artists: track.artists.iter().map(|x| x.name.clone()).collect(),
|
artists: track.artists.iter().map(|x| x.name.clone()).collect(),
|
||||||
|
@ -118,7 +122,11 @@ impl SearchableClient for Client {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Search album from US market
|
// Search album from US market
|
||||||
match self.client.album(album_id, Some(Market::Country(UnitedStates))).await {
|
match self
|
||||||
|
.client
|
||||||
|
.album(album_id, Some(Market::Country(UnitedStates)))
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(album) => Some(AlbumInfo {
|
Ok(album) => Some(AlbumInfo {
|
||||||
name: album.name,
|
name: album.name,
|
||||||
artists: album.artists.iter().map(|x| x.name.clone()).collect(),
|
artists: album.artists.iter().map(|x| x.name.clone()).collect(),
|
||||||
|
|
|
@ -35,7 +35,6 @@ async fn should_search_track_by_spotify_id() {
|
||||||
published: 0,
|
published: 0,
|
||||||
published_text: "".to_string(),
|
published_text: "".to_string(),
|
||||||
live_now: false,
|
live_now: false,
|
||||||
paid: false,
|
|
||||||
premium: false,
|
premium: false,
|
||||||
}],
|
}],
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use invidious::hidden::SearchItem;
|
||||||
|
use invidious::{ClientAsync, ClientAsyncTrait, MethodAsync};
|
||||||
|
use itertools::Itertools;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use mockall::{automock, mock, predicate::*};
|
use mockall::{automock, predicate::*};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -33,7 +36,6 @@ pub(crate) struct Video {
|
||||||
pub(crate) published: u64,
|
pub(crate) published: u64,
|
||||||
pub(crate) published_text: String,
|
pub(crate) published_text: String,
|
||||||
pub(crate) live_now: bool,
|
pub(crate) live_now: bool,
|
||||||
pub(crate) paid: bool,
|
|
||||||
pub(crate) premium: bool,
|
pub(crate) premium: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,16 +54,18 @@ const BY_UPLOAD_DATE: &SearchSortBy = "upload_date";
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
const BY_VIEW_COUNT: &SearchSortBy = "view_count";
|
const BY_VIEW_COUNT: &SearchSortBy = "view_count";
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct Client {
|
pub(crate) struct Client {
|
||||||
client: Arc<invidious::asynchronous::Client>,
|
client: Arc<ClientAsync>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub(crate) async fn new() -> Self {
|
pub(crate) async fn new() -> Self {
|
||||||
// TODO check for a stable instance
|
// TODO check for a stable instance, or rotate between a pool of stable ones
|
||||||
let client =
|
let client = ClientAsync::new(
|
||||||
invidious::asynchronous::Client::new(String::from("https://inv.bp.projectsegfau.lt"));
|
String::from("https://inv.bp.projectsegfau.lt"),
|
||||||
|
MethodAsync::default(),
|
||||||
|
);
|
||||||
|
|
||||||
Client {
|
Client {
|
||||||
client: Arc::new(client),
|
client: Arc::new(client),
|
||||||
|
@ -71,7 +75,7 @@ impl Client {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn new_with_dependencies(client: invidious::asynchronous::Client) -> Self {
|
pub(crate) fn new_with_dependencies(client: invidious::ClientAsync) -> Self {
|
||||||
Client {
|
Client {
|
||||||
client: Arc::new(client),
|
client: Arc::new(client),
|
||||||
}
|
}
|
||||||
|
@ -99,44 +103,31 @@ impl SearchableClient for Client {
|
||||||
let videos: Vec<Video> = search
|
let videos: Vec<Video> = search
|
||||||
.items
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|search_result| match search_result {
|
.map(|search_result| {
|
||||||
invidious::structs::hidden::SearchItem::Video {
|
let video = match search_result {
|
||||||
title,
|
SearchItem::Video(item) => Some(Video {
|
||||||
videoId,
|
title: item.title.clone(),
|
||||||
author,
|
video_id: item.id.clone(),
|
||||||
authorId,
|
author: item.author.clone(),
|
||||||
authorUrl,
|
author_id: item.author_id.clone(),
|
||||||
lengthSeconds,
|
author_url: item.author_url.clone(),
|
||||||
videoThumbnails: _,
|
length_seconds: item.length.clone() as u64,
|
||||||
description,
|
description: item.description.clone(),
|
||||||
descriptionHtml,
|
description_html: item.description_html.clone(),
|
||||||
viewCount,
|
view_count: item.views.clone(),
|
||||||
published,
|
published: item.published.clone(),
|
||||||
publishedText,
|
published_text: item.published_text.clone(),
|
||||||
liveNow,
|
live_now: item.live.clone(),
|
||||||
paid,
|
premium: item.premium.clone(),
|
||||||
premium,
|
}),
|
||||||
} => Some(Video {
|
_ => None,
|
||||||
title: title.clone(),
|
};
|
||||||
video_id: videoId.clone(),
|
video
|
||||||
author: author.clone(),
|
|
||||||
author_id: authorId.clone(),
|
|
||||||
author_url: authorUrl.clone(),
|
|
||||||
length_seconds: lengthSeconds.clone(),
|
|
||||||
description: description.clone(),
|
|
||||||
description_html: descriptionHtml.clone(),
|
|
||||||
view_count: viewCount.clone(),
|
|
||||||
published: published.clone(),
|
|
||||||
published_text: publishedText.clone(),
|
|
||||||
live_now: liveNow.clone(),
|
|
||||||
paid: paid.clone(),
|
|
||||||
premium: premium.clone(),
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
})
|
})
|
||||||
.filter(|x| x.is_some())
|
.filter(|x| x.is_some())
|
||||||
.map(|x| x.unwrap())
|
.map(|x| x.unwrap())
|
||||||
.filter(|x| !x.premium && !x.paid && !x.live_now)
|
.filter(|x| !x.premium && !x.live_now)
|
||||||
|
.sorted_by(|a, b| b.view_count.cmp(&a.view_count))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Result::Ok(VideoSearch { items: videos })
|
Result::Ok(VideoSearch { items: videos })
|
||||||
|
@ -149,13 +140,17 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_search_properly() {
|
fn should_search_properly() {
|
||||||
|
// FIXME: the real solution for properly testing this piece of code would be to spin up a Mocked Invidious
|
||||||
|
// instance (i.e. mock-server) and mock the server responses. With the current implementation, the tests
|
||||||
|
// will always be flaky.
|
||||||
let client = tokio_test::block_on(Client::new());
|
let client = tokio_test::block_on(Client::new());
|
||||||
|
|
||||||
let result = tokio_test::block_on(client.search_video(
|
let result = tokio_test::block_on(
|
||||||
"vfdskvnfdsjklvnfdsjklvnfsdjkldsvmdlfmvkdfslvsdfmklsdvlvfdnjkvnfdsjkvnfsdjk",
|
client.search_video("Eminem - Lose Yourself", Some(BY_UPLOAD_DATE)),
|
||||||
Some(BY_UPLOAD_DATE),
|
)
|
||||||
))
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(30, result.items.len())
|
assert_eq!(19, result.items.len());
|
||||||
|
// Just check the first one for now - in the future we can check for the whole array
|
||||||
|
assert_eq!("Eminem - Lose Yourself [HD]", result.items[0].title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue