Skip to content

Commit c7d0c79

Browse files
authored
Merge pull request #2 from neckaros/t3code/fix-movie-search-decode-error
2 parents b54b8b9 + 0a20f28 commit c7d0c79

7 files changed

Lines changed: 127 additions & 75 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ REDSEAT_DOMAIN: set domain name (ex redseat.myserver.com)
2929

3030
REDSEAT_NOCERT: **Boolean** | Don't use certificate creation (if your domain already has ssl via proxy)
3131

32+
REDSEAT_TRAKT_CLIENT_ID: Trakt application client ID used for movie, series, and people metadata searches
33+
3234
# Docker install
3335
Image:
3436
`docker pull neckaros/redseat-rust`

src/model/entity_search.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,35 @@ use rs_plugin_common_interfaces::{
55
lookup::{RsLookupMetadataResultWrapper, RsLookupMetadataResults, RsLookupQuery},
66
};
77

8-
use crate::{domain::library::LibraryRole, error::RsResult};
8+
use crate::{
9+
domain::library::LibraryRole,
10+
error::RsResult,
11+
tools::log::{log_warn, LogServiceType},
12+
};
913

1014
use super::{users::ConnectedUser, ModelController};
1115

1216
/// Type alias for grouped search results (source_id, source_name, results)
1317
pub type SearchResultGroups = Vec<(String, String, RsLookupMetadataResults)>;
1418

19+
/// Treat Trakt as one optional search provider. A Trakt failure must not prevent
20+
/// plugin providers from returning their own results.
21+
pub fn optional_trakt_search<T>(entity: &str, result: crate::Result<T>) -> Option<T> {
22+
match result {
23+
Ok(value) => Some(value),
24+
Err(error) => {
25+
log_warn(
26+
LogServiceType::Source,
27+
format!(
28+
"Trakt {} search failed; continuing with other providers: {}",
29+
entity, error
30+
),
31+
);
32+
None
33+
}
34+
}
35+
}
36+
1537
/// Merge RsIds across results that share at least one common ID.
1638
/// Uses union-find for O(n·k) performance where n = total results, k = IDs per result.
1739
pub fn merge_result_ids(groups: &mut SearchResultGroups) {

src/model/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,7 @@ impl ModelController {
162162
let mc = Self {
163163
store: Arc::new(store),
164164
plugin_manager: Arc::new(plugin_manager),
165-
trakt: Arc::new(TraktContext::new(
166-
"fcb0d3a87a808a5a0897291350e23cddbbef14502ccb91f1f7bf9c339cb93bcb".to_string(),
167-
)),
165+
trakt: Arc::new(TraktContext::from_env()),
168166
imdb: Arc::new(ImdbContext::new()),
169167
scheduler: Arc::new(scheduler),
170168
chache_libraries: Arc::new(RwLock::new(HashMap::new())),

src/model/movies.rs

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::{
3131

3232
use super::{
3333
entity_images::EntityImageConfig,
34-
entity_search::merge_result_ids,
34+
entity_search::{merge_result_ids, optional_trakt_search},
3535
error::{Error, Result},
3636
store::sql::SqlOrder,
3737
users::{ConnectedUser, HistoryQuery},
@@ -195,16 +195,17 @@ impl ModelController {
195195
.as_deref()
196196
.map_or(true, |s| s.iter().any(|id| id == "trakt"));
197197
let trakt_entries = if include_trakt {
198-
let trakt_results = self.trakt.search_movie(&query).await?;
199-
Some(
200-
trakt_results
201-
.into_iter()
202-
.map(|(movie, match_type)| RsLookupMetadataResultWrapper {
203-
metadata: RsLookupMetadataResult::Movie(movie),
204-
match_type,
205-
..Default::default()
206-
})
207-
.collect(),
198+
optional_trakt_search("movie", self.trakt.search_movie(&query).await).map(
199+
|trakt_results| {
200+
trakt_results
201+
.into_iter()
202+
.map(|(movie, match_type)| RsLookupMetadataResultWrapper {
203+
metadata: RsLookupMetadataResult::Movie(movie),
204+
match_type,
205+
..Default::default()
206+
})
207+
.collect()
208+
},
208209
)
209210
} else {
210211
None
@@ -232,16 +233,17 @@ impl ModelController {
232233
.as_deref()
233234
.map_or(true, |s| s.iter().any(|id| id == "trakt"));
234235
let trakt_entries = if include_trakt {
235-
let trakt_results = self.trakt.search_movie(&query).await?;
236-
Some(
237-
trakt_results
238-
.into_iter()
239-
.map(|(movie, match_type)| RsLookupMetadataResultWrapper {
240-
metadata: RsLookupMetadataResult::Movie(movie),
241-
match_type,
242-
..Default::default()
243-
})
244-
.collect(),
236+
optional_trakt_search("movie", self.trakt.search_movie(&query).await).map(
237+
|trakt_results| {
238+
trakt_results
239+
.into_iter()
240+
.map(|(movie, match_type)| RsLookupMetadataResultWrapper {
241+
metadata: RsLookupMetadataResult::Movie(movie),
242+
match_type,
243+
..Default::default()
244+
})
245+
.collect()
246+
},
245247
)
246248
} else {
247249
None

src/model/people.rs

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use rs_plugin_common_interfaces::{
4646
use tokio_util::io::StreamReader;
4747

4848
use super::{
49+
entity_search::optional_trakt_search,
4950
error::{Error, Result},
5051
users::ConnectedUser,
5152
ModelController,
@@ -684,16 +685,17 @@ impl ModelController {
684685
.as_deref()
685686
.map_or(true, |s| s.iter().any(|id| id == "trakt"));
686687
let trakt_entries = if include_trakt {
687-
let trakt_results = self.trakt.search_person(&query).await?;
688-
Some(
689-
trakt_results
690-
.into_iter()
691-
.map(|(person, match_type)| RsLookupMetadataResultWrapper {
692-
metadata: RsLookupMetadataResult::Person(person),
693-
match_type,
694-
..Default::default()
695-
})
696-
.collect(),
688+
optional_trakt_search("people", self.trakt.search_person(&query).await).map(
689+
|trakt_results| {
690+
trakt_results
691+
.into_iter()
692+
.map(|(person, match_type)| RsLookupMetadataResultWrapper {
693+
metadata: RsLookupMetadataResult::Person(person),
694+
match_type,
695+
..Default::default()
696+
})
697+
.collect()
698+
},
697699
)
698700
} else {
699701
None
@@ -726,16 +728,17 @@ impl ModelController {
726728
.as_deref()
727729
.map_or(true, |s| s.iter().any(|id| id == "trakt"));
728730
let trakt_entries = if include_trakt {
729-
let trakt_results = self.trakt.search_person(&query).await?;
730-
Some(
731-
trakt_results
732-
.into_iter()
733-
.map(|(person, match_type)| RsLookupMetadataResultWrapper {
734-
metadata: RsLookupMetadataResult::Person(person),
735-
match_type,
736-
..Default::default()
737-
})
738-
.collect(),
731+
optional_trakt_search("people", self.trakt.search_person(&query).await).map(
732+
|trakt_results| {
733+
trakt_results
734+
.into_iter()
735+
.map(|(person, match_type)| RsLookupMetadataResultWrapper {
736+
metadata: RsLookupMetadataResult::Person(person),
737+
match_type,
738+
..Default::default()
739+
})
740+
.collect()
741+
},
739742
)
740743
} else {
741744
None

src/model/series.rs

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use crate::{
3535

3636
use super::{
3737
entity_images::EntityImageConfig,
38-
entity_search::merge_result_ids,
38+
entity_search::{merge_result_ids, optional_trakt_search},
3939
episodes::{EpisodeForUpdate, EpisodeQuery},
4040
error::{Error, Result},
4141
medias::{MediaQuery, RsSort},
@@ -284,16 +284,17 @@ impl ModelController {
284284
.as_deref()
285285
.map_or(true, |s| s.iter().any(|id| id == "trakt"));
286286
let trakt_entries = if include_trakt {
287-
let trakt_results = self.trakt.search_show(&query).await?;
288-
Some(
289-
trakt_results
290-
.into_iter()
291-
.map(|(serie, match_type)| RsLookupMetadataResultWrapper {
292-
metadata: RsLookupMetadataResult::Serie(serie),
293-
match_type,
294-
..Default::default()
295-
})
296-
.collect(),
287+
optional_trakt_search("series", self.trakt.search_show(&query).await).map(
288+
|trakt_results| {
289+
trakt_results
290+
.into_iter()
291+
.map(|(serie, match_type)| RsLookupMetadataResultWrapper {
292+
metadata: RsLookupMetadataResult::Serie(serie),
293+
match_type,
294+
..Default::default()
295+
})
296+
.collect()
297+
},
297298
)
298299
} else {
299300
None
@@ -328,16 +329,17 @@ impl ModelController {
328329
.as_deref()
329330
.map_or(true, |s| s.iter().any(|id| id == "trakt"));
330331
let trakt_entries = if include_trakt {
331-
let trakt_results = self.trakt.search_show(&query).await?;
332-
Some(
333-
trakt_results
334-
.into_iter()
335-
.map(|(serie, match_type)| RsLookupMetadataResultWrapper {
336-
metadata: RsLookupMetadataResult::Serie(serie),
337-
match_type,
338-
..Default::default()
339-
})
340-
.collect(),
332+
optional_trakt_search("series", self.trakt.search_show(&query).await).map(
333+
|trakt_results| {
334+
trakt_results
335+
.into_iter()
336+
.map(|(serie, match_type)| RsLookupMetadataResultWrapper {
337+
metadata: RsLookupMetadataResult::Serie(serie),
338+
match_type,
339+
..Default::default()
340+
})
341+
.collect()
342+
},
341343
)
342344
} else {
343345
None

src/plugins/medias/trakt/mod.rs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rs_plugin_common_interfaces::{
1212
lookup::{RsLookupMatchType, RsLookupMovie},
1313
};
1414
use serde::de::DeserializeOwned;
15+
use std::env;
1516
use tower::Service;
1617
use trakt_people::{TraktActorsResult, TraktPeopleSearchElement, TraktPerson};
1718

@@ -26,6 +27,8 @@ use self::{
2627
// Context required for all requests
2728
use unidecode::unidecode;
2829

30+
pub const ENV_TRAKT_CLIENT_ID: &str = "REDSEAT_TRAKT_CLIENT_ID";
31+
2932
/// Deserialize JSON response with detailed error path information
3033
async fn json_with_path<T: DeserializeOwned>(
3134
response: Response,
@@ -73,6 +76,13 @@ fn as_id_for_trakt(ids: &RsIds) -> Option<String> {
7376
}
7477

7578
impl TraktContext {
79+
pub fn from_env() -> Self {
80+
let client_id = env::var(ENV_TRAKT_CLIENT_ID)
81+
.map(|value| value.trim().to_string())
82+
.unwrap_or_default();
83+
Self::new(client_id)
84+
}
85+
7686
pub fn new(client_id: String) -> Self {
7787
let base_url = reqwest::Url::parse("https://api.trakt.tv").unwrap();
7888
let mut headers = HeaderMap::new();
@@ -88,6 +98,17 @@ impl TraktContext {
8898
client,
8999
}
90100
}
101+
102+
fn ensure_configured(&self) -> crate::Result<()> {
103+
if self.client_id.is_empty() {
104+
Err(Error::Error(format!(
105+
"Trakt API client ID is not configured; set {}",
106+
ENV_TRAKT_CLIENT_ID
107+
)))
108+
} else {
109+
Ok(())
110+
}
111+
}
91112
}
92113

93114
impl TraktContext {
@@ -153,6 +174,7 @@ impl TraktContext {
153174
&self,
154175
search: &RsLookupMovie,
155176
) -> crate::Result<Vec<(Serie, Option<RsLookupMatchType>)>> {
177+
self.ensure_configured()?;
156178
let query = search.name.as_deref().unwrap_or_default();
157179
let url = self
158180
.base_url
@@ -167,7 +189,8 @@ impl TraktContext {
167189
.get(url)
168190
.header("trakt-api-key", &self.client_id)
169191
.send()
170-
.await?;
192+
.await?
193+
.error_for_status()?;
171194

172195
let shows = r
173196
.json::<Vec<TraktShowSearchElement>>()
@@ -371,6 +394,7 @@ impl TraktContext {
371394
&self,
372395
search: &RsLookupMovie,
373396
) -> crate::Result<Vec<(Movie, Option<RsLookupMatchType>)>> {
397+
self.ensure_configured()?;
374398
let query = search.name.as_deref().unwrap_or_default();
375399
let url = self
376400
.base_url
@@ -385,7 +409,8 @@ impl TraktContext {
385409
.get(url)
386410
.header("trakt-api-key", &self.client_id)
387411
.send()
388-
.await?;
412+
.await?
413+
.error_for_status()?;
389414
let movies = r
390415
.json::<Vec<TraktMovieSearchElement>>()
391416
.await?
@@ -489,6 +514,7 @@ impl TraktContext {
489514
&self,
490515
search: &RsLookupMovie,
491516
) -> crate::Result<Vec<(Person, Option<RsLookupMatchType>)>> {
517+
self.ensure_configured()?;
492518
let query = search.name.as_deref().unwrap_or_default();
493519
let url = self
494520
.base_url
@@ -503,7 +529,8 @@ impl TraktContext {
503529
.get(url)
504530
.header("trakt-api-key", &self.client_id)
505531
.send()
506-
.await?;
532+
.await?
533+
.error_for_status()?;
507534
let people = r
508535
.json::<Vec<TraktPeopleSearchElement>>()
509536
.await?
@@ -540,9 +567,7 @@ mod tests {
540567
#[tokio::test]
541568
#[ignore] // requires network + valid Trakt API key
542569
async fn trakt_releases() -> RsResult<()> {
543-
let trakt = TraktContext::new(
544-
"455f81b3409a8dd140a941e9250ff22b2ed92d68003491c3976363fe752a9024".to_owned(),
545-
);
570+
let trakt = TraktContext::from_env();
546571

547572
let releases = trakt.get_movie_releases(&exemple_movie()).await?;
548573

@@ -559,9 +584,7 @@ mod tests {
559584
#[tokio::test]
560585
#[ignore] // requires network + valid Trakt API key
561586
async fn trakt_search_person() -> RsResult<()> {
562-
let trakt = TraktContext::new(
563-
"455f81b3409a8dd140a941e9250ff22b2ed92d68003491c3976363fe752a9024".to_owned(),
564-
);
587+
let trakt = TraktContext::from_env();
565588

566589
let search_result = trakt
567590
.search_person(&RsLookupMovie {

0 commit comments

Comments
 (0)