Skip to content

Commit

Permalink
feat: Trakt import Request/response + initial wip impl of import
Browse files Browse the repository at this point in the history
Signed-off-by: Lachezar Lechev <[email protected]>
  • Loading branch information
elpiel committed Aug 26, 2024
1 parent 0e64b56 commit 8443b02
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 0 deletions.
61 changes: 61 additions & 0 deletions src/models/ctx/update_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::types::profile::{Auth, AuthKey, Profile, Settings, User};
use crate::types::streams::StreamsBucket;

pub fn update_profile<E: Env + 'static>(
// library: &LibraryBucket,
profile: &mut Profile,
streams: &mut StreamsBucket,
status: &CtxStatus,
Expand Down Expand Up @@ -221,6 +222,66 @@ pub fn update_profile<E: Env + 'static>(
}))
.unchanged(),
},

Msg::Action(Action::Ctx(ActionCtx::ImportTrakt)) => match &profile.auth {
Some(auth) => {
// 1. Check trakt auth
let auth_token = match auth.user.trakt.as_ref() {
Some(trakt_info) if trakt_info.is_valid() => {
trakt_info.access_token.clone()
},
_ => return Effects::none().unchanged(),
};

// 2. List of Trakt items: `https://www.strem.io/trakt/watched.json?token=${access_token}`
// web_service::fetch()

// 3. Check LibraryBucket if Trakt Id exists as LibraryItem

// 3.1 if it exists, get videos (from LibraryItem) and check against Trakt Response
// watched episodes for series

// 3.1.0 update episodes watched (for series) using watchedBitfield
// 3.1.1 update last watched if > that LibraryItem last_watched

// 3.2 if does not exist

// ctime if LibraryItem
// allow notifs.

// 3.* Update as follows:
// change.name = change.name || element.movie.title
// change.type = change.type || "movie"
// change.posterShape = change.posterShape || "poster"
// change.year = change.year || element.movie.year
// change.behaviorHints = { defaultVideoId: change._id, hasScheduledVideos: false }
// change.removed = false;
// change.temp = false;

// change._mtime = new Date().toISOString();

// 4. Modify LibraryItems by:
// 4.1 For existing library items:
// Internal::UpdateLibraryItem
// TODO: for multiple items create a new Internal message that update multiple items

// 4.2 For missing library items:
//
// Effects::msg(Msg::Internal(Internal::UpdateLibraryItem(library_item)))
// .join(Effects::msg(Msg::Event(Event::LibraryItemAdded {
// id: meta_preview.id.to_owned(),
// })))
// .unchanged()

Effects::msg(Msg::Event(Event::TraktImported {
movies_count: 0,
series_count: 0,
uid: profile.uid(),
}))
.unchanged()
},
_ => Effects::none().unchanged()
},
Msg::Action(Action::Ctx(ActionCtx::UpdateSettings(settings))) => {
if profile.settings != *settings {
settings.clone_into(&mut profile.settings);
Expand Down
1 change: 1 addition & 0 deletions src/runtime/msg/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub enum ActionCtx {
InstallAddon(Descriptor),
InstallTraktAddon,
LogoutTrakt,
ImportTrakt,
UpgradeAddon(Descriptor),
UninstallAddon(Descriptor),
UpdateSettings(ProfileSettings),
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/msg/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ pub enum Event {
TraktPaused {
context: PlayerAnalyticsContext,
},
TraktImported {
movies_count: usize,
series_count: usize,
uid: UID,
},
ProfilePushedToStorage {
uid: UID,
},
Expand Down
1 change: 1 addition & 0 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod search_history;
pub mod streaming_server;
pub mod streams;
pub mod torrent;
pub mod web_services;

mod query_params_encode;
pub use query_params_encode::*;
Expand Down
11 changes: 11 additions & 0 deletions src/types/profile/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ pub struct TraktInfo {
#[cfg_attr(test, derivative(Default(value = r#"String::from("token")"#)))]
pub access_token: String,
}

impl TraktInfo {
pub fn is_expired(&self) -> bool {
todo!()
}

pub fn is_valid(&self) -> bool {
!self.is_expired()
}
}

impl fmt::Debug for TraktInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TraktInfo")
Expand Down
21 changes: 21 additions & 0 deletions src/types/web_services.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use crate::{
runtime::{ConditionalSend, Env, TryEnvFuture},
types::api::{APIResult, FetchRequestParams},
};


pub mod response;

use http::Request;
use serde::{Deserialize, Serialize};

pub fn fetch<
E: Env,
BODY: Serialize + ConditionalSend + 'static,
REQ: FetchRequestParams<BODY> + Clone + Serialize,
RESP: for<'de> Deserialize<'de> + ConditionalSend + 'static,
>(
api_request: &REQ,
) -> TryEnvFuture<APIResult<RESP>> {
todo!()
}
200 changes: 200 additions & 0 deletions src/types/web_services/response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
use std::collections::HashMap;

use chrono::{DateTime, Utc};
use http::Request;
use serde::{Deserialize, Serialize};

pub struct TraktWatchedRequest {
pub access_token: String,
}

impl From<TraktWatchedRequest> for Request<()> {
fn from(value: TraktWatchedRequest) -> Self {
todo!()
}
}

#[derive(Serialize, Deserialize)]
pub struct TraktWatchedResponse(Vec<TraktType>);

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", content = "data", rename_all = "camelCase")]
pub enum TraktType {
Movies(Vec<Movie>),
Shows(Vec<Show>),
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Movie {
plays: u64,
last_watched_at: DateTime<Utc>,
last_updated_at: DateTime<Utc>,
movie: Data,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Data {
pub title: String,
pub year: u32,
pub ids: HashMap<String, serde_json::Value>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Show {
pub plays: u64,
pub last_watched_at: DateTime<Utc>,
pub last_updated_at: DateTime<Utc>,
pub reset_at: Option<DateTime<Utc>>,
pub show: Data,
pub seasons: Vec<Season>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Season {
pub number: u64,
pub episodes: Vec<Episode>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Episode {
pub number: u64,
pub plays: u64,
pub last_watched_at: DateTime<Utc>,
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_movie_deserialization() {
let movie_json = serde_json::json!({
"type": "movies",
"data": [
{
"plays": 3,
"last_watched_at": "2023-12-31T15:46:51.000Z",
"last_updated_at": "2023-12-31T15:46:51.000Z",
"movie": {
"title": "The Beanie Bubble",
"year": 2023,
"ids": {
"trakt": 741930,
"slug": "the-beanie-bubble-2023",
"imdb": "tt17007120",
"tmdb": 926008
}
}
},
],
});

// serde_path_to_error::
let response = serde_json::from_value::<TraktWatchedResponse>(movie_json)
.expect("Should deserialize movie");

match response.0.get(0).unwrap() {
TraktType::Movies(movies) => {
// assert!
// assert_eq!
// ....
}
_ => panic!("We expected a movie, found different TraktType"),
}
}

#[test]
fn test_show_deserialization() {
let show_json = serde_json::json!([{
"type": "shows",
"data": [{
"plays": 12,
"last_watched_at": "2024-08-22T21:30:08.000Z",
"last_updated_at": "2024-08-22T21:30:08.000Z",
"reset_at": null,
"show": {
"title": "The Marvelous Mrs. Maisel",
"year": 2017,
"ids": {
"trakt": 118164,
"slug": "the-marvelous-mrs-maisel",
"tvdb": 326791,
"imdb": "tt5788792",
"tmdb": 70796,
"tvrage": null
}
},
"seasons": [
{
"number": 1,
"episodes": [
{
"number": 1,
"plays": 1,
"last_watched_at": "2024-08-16T20:47:26.000Z"
},
{
"number": 2,
"plays": 2,
"last_watched_at": "2024-08-17T18:05:16.000Z"
},
{
"number": 3,
"plays": 2,
"last_watched_at": "2024-08-18T18:15:45.000Z"
},
{
"number": 4,
"plays": 1,
"last_watched_at": "2024-08-18T19:31:33.000Z"
},
{
"number": 6,
"plays": 1,
"last_watched_at": "2024-08-19T19:37:19.000Z"
}
]
},
{
"number": 2,
"episodes": [
{
"number": 1,
"plays": 1,
"last_watched_at": "2024-08-20T21:28:05.000Z"
},
{
"number": 2,
"plays": 1,
"last_watched_at": "2024-08-21T20:41:39.000Z"
},
{
"number": 3,
"plays": 1,
"last_watched_at": "2024-08-22T19:21:07.000Z"
},
{
"number": 4,
"plays": 2,
"last_watched_at": "2024-08-22T21:30:08.000Z"
}
]
}
]
}]
}]);

let response = serde_json::from_value::<TraktWatchedResponse>(show_json)
.expect("Should deserialize movie");

match &response.0.get(0).unwrap() {
TraktType::Shows(shows) => {
// assert!
// assert_eq!
// ....
dbg!(&shows);
}
_ => panic!("We expected a show, found different TraktType"),
}
}
}

0 comments on commit 8443b02

Please sign in to comment.