Skip to content

Commit d2cbbd2

Browse files
authored
feat: Optionally load art work from tmdb (#18)
1 parent cf7ffb2 commit d2cbbd2

12 files changed

+485
-20
lines changed

Cargo.lock

+45
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ moka = { version = "0.11.2", features = ["future"] }
4040
tonic = {version = "0.8.0", features = ["tls", "tls-roots"]}
4141
async-recursion = "1.0.4"
4242
console-subscriber = "0.1.10"
43+
tmdb-api = "0.5.0"
44+
bincode = "1.3.3"
4345

4446
[dev-dependencies]
4547
async-std = { version = "^1.12", features = ["attributes"] }

Makefile

+4-4
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ docker-run:
2828
# REPLEX_CACHE_TTL=0 REPLEX_HOST=https://46-4-30-217.01b0839de64b49138531cab1bf32f7c2.plex.direct:42405 REPLEX_NEWRELIC_API_KEY="eu01xx2d3c6a5e537373a8f8b52003b3FFFFNRAL" RUST_LOG="debug,replex=debug" cargo watch -x run
2929

3030

31-
# run:
32-
# REPLEX_ENABLE_CONSOLE=0 REPLEX_CACHE_TTL=0 REPLEX_HOST=https://46-4-30-217.01b0839de64b49138531cab1bf32f7c2.plex.direct:42405 RUST_LOG="debug" cargo watch -x run
33-
3431
run:
35-
REPLEX_ENABLE_CONSOLE=0 REPLEX_CACHE_TTL=0 REPLEX_HOST=https://46-4-30-217.01b0839de64b49138531cab1bf32f7c2.plex.direct:42405 RUST_LOG="info" cargo run
32+
REPLEX_TMDB_API_KEY=0d73e0cb91f39e670b0efa6913afbd58 REPLEX_ENABLE_CONSOLE=0 REPLEX_CACHE_TTL=0 REPLEX_HOST=https://46-4-30-217.01b0839de64b49138531cab1bf32f7c2.plex.direct:42405 RUST_LOG="info" cargo watch -x run
33+
34+
# run:
35+
# REPLEX_ENABLE_CONSOLE=0 REPLEX_CACHE_TTL=0 REPLEX_HOST=https://46-4-30-217.01b0839de64b49138531cab1bf32f7c2.plex.direct:42405 RUST_LOG="info" cargo run
3636

3737

3838
fix:

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,15 @@ Settings are set via [environment variables](https://kinsta.com/knowledgebase/wh
2222
| REPLEX_HOST | | Plex target host to proxy |
2323
| REPLEX_INCLUDE_WATCHED | false | If set to false, hide watched items. |
2424
| REPLEX_CACHE_TTL | 300 | Time to live for caches in seconds. Set to 0 to disable |
25+
| REPLEX_TMDB_API_KEY | | Enables tmdb artwork for hero hubs instead of plex background artwork |
2526

2627
## hub style
2728

2829
You can change the hub style to hero elements by setting the label "REPLEXHERO" on an collection.
30+
Plex uses an items background for hero styles rows. Often these dont have any text or are not suitable for hero artwork in general.
31+
You can use tmdb to automaticly load hero artwork by providing the env `REPLEX_TMDB_API_KEY`. This way you can keep your backgrounds and hero artwork seperated.
32+
33+
see https://developer.themoviedb.org/docs/getting-started on how to get an api key.
2934

3035
## usage example
3136

src/cache.rs

+140-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,145 @@
11
use async_trait::async_trait;
2-
use salvo::{cache::CacheIssuer, Request, Depot};
2+
use moka::{future::Cache, future::ConcurrentCacheExt, Expiry};
3+
use once_cell::sync::Lazy;
4+
use salvo::{cache::CacheIssuer, Depot, Request};
5+
use serde::de::DeserializeOwned;
6+
use serde::{Deserialize, Deserializer, Serialize};
7+
use std::hash::Hash;
8+
use std::{
9+
sync::Arc,
10+
time::{Duration, Instant},
11+
};
12+
use std::error::Error;
13+
14+
use crate::config::Config;
15+
16+
// we close, this is a good example: https://github.com/getsentry/symbolicator/blob/170062d5bc7d4638a3e6af8a564cd881d798f1f0/crates/symbolicator-service/src/caching/memory.rs#L85
17+
18+
pub type CacheKey = String;
19+
pub type CacheValue = (Expiration, Arc<Vec<u8>>);
20+
// pub type CacheValue = Arc<Vec<u8>>;
21+
pub type GlobalCacheType = Cache<CacheKey, CacheValue>;
22+
23+
pub(crate) static GLOBAL_CACHE: Lazy<CacheManager> = Lazy::new(|| {
24+
let expiry = CacheExpiry;
25+
26+
// let store: GlobalCacheType =
27+
CacheManager::new(
28+
Cache::builder()
29+
.max_capacity(10000)
30+
.expire_after(expiry)
31+
.build(),
32+
)
33+
});
34+
35+
/// An enum to represent the expiration of a value.
36+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37+
pub enum Expiration {
38+
/// The value never expires.
39+
Never,
40+
/// Global TTL from the config
41+
Global,
42+
}
43+
44+
impl Expiration {
45+
/// Returns the duration of this expiration.
46+
pub fn as_duration(&self) -> Option<Duration> {
47+
let config: Config = Config::figment().extract().unwrap();
48+
match self {
49+
Expiration::Never => None,
50+
Expiration::Global => Some(Duration::from_secs(config.cache_ttl)),
51+
}
52+
}
53+
}
54+
55+
/// An expiry that implements `moka::Expiry` trait. `Expiry` trait provides the
56+
/// default implementations of three callback methods `expire_after_create`,
57+
/// `expire_after_read`, and `expire_after_update`.
58+
///
59+
/// In this example, we only override the `expire_after_create` method.
60+
pub struct CacheExpiry;
61+
62+
impl Expiry<CacheKey, (Expiration, Arc<Vec<u8>>)> for CacheExpiry {
63+
/// Returns the duration of the expiration of the value that was just
64+
/// created.
65+
fn expire_after_create(
66+
&self,
67+
_key: &CacheKey,
68+
value: &(Expiration, Arc<Vec<u8>>),
69+
_current_time: Instant,
70+
) -> Option<Duration> {
71+
let duration = value.0.as_duration();
72+
duration
73+
}
74+
}
75+
76+
#[derive(Clone)]
77+
pub struct CacheManager {
78+
/// The instance of `moka::future::Cache`
79+
// pub store: Arc<Cache<String, Arc<Vec<u8>>>>,
80+
// pub inner: S,
81+
pub inner: GlobalCacheType,
82+
}
83+
84+
impl CacheManager {
85+
/// Create a new manager from a pre-configured Cache
86+
// pub fn new(store: Cache<String, Arc<Vec<u8>>>) -> Self {
87+
pub fn new(cache: GlobalCacheType) -> Self {
88+
Self {
89+
inner: cache, // store: Arc::new(store),
90+
}
91+
}
92+
/// Clears out the entire cache.
93+
pub async fn clear(&self) -> anyhow::Result<()> {
94+
self.inner.invalidate_all();
95+
self.inner.sync();
96+
Ok(())
97+
}
98+
99+
pub async fn get<T>(&self, cache_key: &str) -> Option<T>
100+
where
101+
T: DeserializeOwned,
102+
{
103+
match self.inner.get(cache_key) {
104+
Some(d) => {
105+
let result: T = bincode::deserialize(&d.1).unwrap();
106+
Some(result)
107+
},
108+
None => None,
109+
}
110+
}
111+
112+
pub async fn insert<V>(
113+
&self,
114+
cache_key: String,
115+
v: V,
116+
expires: Expiration,
117+
) -> anyhow::Result<()>
118+
where
119+
V: Serialize,
120+
{
121+
122+
let value = (expires, Arc::new(bincode::serialize(&v)?));
123+
// let bytes = bincode::serialize(&value)?;
124+
self.inner.insert(cache_key, value).await;
125+
self.inner.sync();
126+
Ok(())
127+
}
128+
129+
pub async fn delete(&self, cache_key: &str) -> anyhow::Result<()> {
130+
self.inner.invalidate(cache_key).await;
131+
self.inner.sync();
132+
Ok(())
133+
}
134+
}
3135

4136
pub struct RequestIssuer {
5137
use_scheme: bool,
6138
use_authority: bool,
7139
use_path: bool,
8140
use_query: bool,
9141
use_method: bool,
10-
use_token: bool
142+
use_token: bool,
11143
}
12144
impl Default for RequestIssuer {
13145
fn default() -> Self {
@@ -60,7 +192,11 @@ impl RequestIssuer {
60192
#[async_trait]
61193
impl CacheIssuer for RequestIssuer {
62194
type Key = String;
63-
async fn issue(&self, req: &mut Request, _depot: &Depot) -> Option<Self::Key> {
195+
async fn issue(
196+
&self,
197+
req: &mut Request,
198+
_depot: &Depot,
199+
) -> Option<Self::Key> {
64200
let mut key = String::new();
65201
if self.use_scheme {
66202
if let Some(scheme) = req.uri().scheme_str() {
@@ -97,4 +233,4 @@ impl CacheIssuer for RequestIssuer {
97233
}
98234
Some(key)
99235
}
100-
}
236+
}

src/config.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ pub struct Config {
2828
default = "default_as_false",
2929
deserialize_with = "figment::util::bool_from_str_or_int"
3030
)]
31-
pub enable_console: bool
31+
pub enable_console: bool,
32+
pub tmdb_api_key: Option<String>,
3233
}
3334

3435
fn default_cache_ttl() -> u64 {

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub mod transform;
1313
pub mod logging;
1414
pub mod cache;
1515
pub mod routes;
16+
pub mod tmdb;
1617

1718
#[cfg(test)]
1819
mod test_helpers;

0 commit comments

Comments
 (0)