aboutsummaryrefslogtreecommitdiff
path: root/import/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'import/src/lib.rs')
-rw-r--r--import/src/lib.rs102
1 files changed, 89 insertions, 13 deletions
diff --git a/import/src/lib.rs b/import/src/lib.rs
index 0d8a10a..0990ba1 100644
--- a/import/src/lib.rs
+++ b/import/src/lib.rs
@@ -18,6 +18,7 @@ use jellyclient::{Appearance, PeopleGroup, TmdbKind, TraktKind, Visibility};
use log::info;
use matroska::matroska_metadata;
use rayon::iter::{ParallelBridge, ParallelIterator};
+use regex::Regex;
use std::{
collections::{BTreeMap, HashMap},
fs::{read_to_string, File},
@@ -42,8 +43,8 @@ pub mod trakt;
static IMPORT_SEM: LazyLock<Semaphore> = LazyLock::new(|| Semaphore::new(1));
pub static IMPORT_ERRORS: RwLock<Vec<String>> = RwLock::const_new(Vec::new());
-// static RE_EPISODE_FILENAME: LazyLock<Regex> =
-// LazyLock::new(|| Regex::new(r#"([sS](\d+))?([eE](\d+))( (.+))?"#).unwrap());
+static RE_EPISODE_FILENAME: LazyLock<Regex> =
+ LazyLock::new(|| Regex::new(r#"([sS](?<season>\d+))?([eE](?<episode>\d+))( (.+))?"#).unwrap());
struct Apis {
trakt: Option<Trakt>,
@@ -296,11 +297,20 @@ fn import_media_file(
})
.unwrap_or_default();
- let filename = path
- .file_name()
- .ok_or(anyhow!("no file stem"))?
- .to_string_lossy()
- .to_string();
+ let filename = path.file_name().unwrap().to_string_lossy().to_string();
+
+ let mut episode_index = None;
+ if let Some(cap) = RE_EPISODE_FILENAME.captures(&filename) {
+ if let Some(episode) = cap.name("episode").map(|m| m.as_str()) {
+ let season = cap.name("season").map(|m| m.as_str());
+ let episode = episode.parse::<usize>().context("parse episode num")?;
+ let season = season
+ .unwrap_or("1")
+ .parse::<usize>()
+ .context("parse season num")?;
+ episode_index = Some((season, episode))
+ }
+ }
let mut filename_toks = filename.split(".");
let filepath_stem = filename_toks.next().unwrap();
@@ -309,17 +319,36 @@ fn import_media_file(
.infojson
.as_ref()
.map(|ij| format!("youtube-{}", ij.id))
- .unwrap_or(make_kebab(&filepath_stem));
+ .unwrap_or_else(|| {
+ if let Some((s, e)) = episode_index {
+ format!(
+ "{}-s{s}e{e}",
+ make_kebab(
+ &path
+ .parent()
+ .unwrap()
+ .file_name()
+ .unwrap_or_default()
+ .to_string_lossy()
+ )
+ )
+ } else {
+ make_kebab(&filepath_stem)
+ }
+ });
let node = NodeID::from_slug(&slug);
db.update_node_init(node, |node| {
node.slug = slug;
- node.title = info.title;
+ node.title = info.title.or(node.title.clone());
node.visibility = visibility;
node.poster = m.cover.clone();
- node.description = tags.remove("DESCRIPTION").or(tags.remove("SYNOPSIS"));
- node.tagline = tags.remove("COMMENT");
+ node.description = tags
+ .remove("DESCRIPTION")
+ .or(tags.remove("SYNOPSIS"))
+ .or(node.description.clone());
+ node.tagline = tags.remove("COMMENT").or(node.tagline.clone());
node.parents.insert(parent);
if let Some(ct) = tags.get("CONTENT_TYPE") {
@@ -440,6 +469,53 @@ fn import_media_file(
Ok(())
})?;
+ if let Some((season, episode)) = episode_index {
+ let mut trakt_id = None;
+ let flagspath = path.parent().unwrap().join("flags");
+ if flagspath.exists() {
+ for flag in read_to_string(flagspath)?.lines() {
+ if let Some(value) = flag.strip_prefix("trakt-").or(flag.strip_prefix("trakt=")) {
+ let (kind, id) = value.split_once(":").unwrap_or(("", value));
+ if kind == "show" {
+ trakt_id = Some(id.parse::<u64>()?);
+ }
+ }
+ }
+ }
+ if let Some(trakt_id) = trakt_id {
+ let trakt = apis.trakt.as_ref().ok_or(anyhow!("trakt required"))?;
+ let seasons = rthandle.block_on(trakt.show_seasons(trakt_id))?;
+ if seasons.iter().any(|x| x.number == season) {
+ let episodes = rthandle.block_on(trakt.show_season_episodes(trakt_id, season))?;
+ let mut poster = None;
+ if let Some(tmdb) = &apis.tmdb {
+ let trakt_details =
+ rthandle.block_on(trakt.lookup(TraktKind::Show, trakt_id))?;
+ if let Some(tmdb_id) = trakt_details.ids.tmdb {
+ let tmdb_details =
+ rthandle.block_on(tmdb.episode_details(tmdb_id, season, episode))?;
+ if let Some(still) = &tmdb_details.still_path {
+ poster = Some(
+ AssetInner::Cache(rthandle.block_on(tmdb.image(&still))?).ser(),
+ )
+ }
+ }
+ }
+ if let Some(episode) = episodes.get(episode.saturating_sub(1)) {
+ db.update_node_init(node, |node| {
+ node.kind = NodeKind::Episode;
+ node.index = Some(episode.number);
+ node.title = Some(episode.title.clone());
+ node.poster = poster.or(node.poster.clone());
+ node.description = episode.overview.clone().or(node.description.clone());
+ node.ratings.insert(Rating::Trakt, episode.rating);
+ Ok(())
+ })?
+ }
+ }
+ }
+ }
+
for tok in filename_toks {
apply_node_flag(db, rthandle, apis, node, tok)?;
}
@@ -510,10 +586,10 @@ fn apply_trakt_tmdb(
let trakt_id: u64 = trakt_id.parse().context("parse trakt id")?;
if let (Some(trakt), Some(tmdb)) = (&apis.trakt, &apis.tmdb) {
let data = rthandle
- .block_on(trakt.lookup(trakt_kind, trakt_id, true))
+ .block_on(trakt.lookup(trakt_kind, trakt_id))
.context("trakt lookup")?;
let people = rthandle
- .block_on(trakt.people(trakt_kind, trakt_id, true))
+ .block_on(trakt.people(trakt_kind, trakt_id))
.context("trakt people lookup")?;
let mut people_map = BTreeMap::<PeopleGroup, Vec<Appearance>>::new();