You've already forked flix
508 lines
13 KiB
Rust
508 lines
13 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use flix::db::entity;
|
|
use flix::model::id::{CollectionId, MovieId, ShowId};
|
|
use flix::model::numbers::{EpisodeNumber, SeasonNumber};
|
|
use flix::tmdb::Client;
|
|
use flix::tmdb::model::id::{
|
|
CollectionId as TmdbCollectionId, MovieId as TmdbMovieId, ShowId as TmdbShowId,
|
|
};
|
|
|
|
use anyhow::{Context, Result, bail};
|
|
use chrono::Utc;
|
|
use sea_orm::ActiveValue::{NotSet, Set};
|
|
use sea_orm::{
|
|
ActiveModelTrait, DatabaseConnection, DbErr, EntityTrait, TransactionError, TransactionTrait,
|
|
};
|
|
|
|
use crate::cli::tmdb::Command;
|
|
|
|
pub async fn add(client: Client, db: &DatabaseConnection, command: Command) -> Result<()> {
|
|
match command {
|
|
Command::Collection { id } => {
|
|
let id = TmdbCollectionId::from_raw(id);
|
|
|
|
let collection = entity::tmdb::collections::Entity::find_by_id(id)
|
|
.one(db)
|
|
.await?;
|
|
if collection.is_some() {
|
|
bail!("collection already exists");
|
|
}
|
|
|
|
let collection = client
|
|
.collections()
|
|
.get_details(id, None)
|
|
.await
|
|
.with_context(|| format!("collections().get_details({})", id.into_raw()))?;
|
|
|
|
let result: Result<CollectionId, TransactionError<DbErr>> = db
|
|
.transaction(|txn| {
|
|
Box::pin(async move {
|
|
let flix = entity::info::collections::ActiveModel {
|
|
id: NotSet,
|
|
title: Set(collection.title),
|
|
overview: Set(collection.overview),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
entity::tmdb::collections::ActiveModel {
|
|
tmdb_id: Set(id),
|
|
flix_id: Set(flix.id),
|
|
last_update: Set(Utc::now().date_naive()),
|
|
movie_count: Set(collection.movies.len().try_into().unwrap_or(0)),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
Ok(flix.id)
|
|
})
|
|
})
|
|
.await;
|
|
|
|
let flix_id = match result {
|
|
Ok(id) => id,
|
|
Err(TransactionError::Connection(err)) => Err(err)?,
|
|
Err(TransactionError::Transaction(err)) => Err(err)?,
|
|
};
|
|
println!("Created Collection: {}", flix_id.into_raw());
|
|
|
|
Ok(())
|
|
}
|
|
Command::Movie { id } => {
|
|
let id = TmdbMovieId::from_raw(id);
|
|
|
|
let movie = entity::tmdb::movies::Entity::find_by_id(id).one(db).await?;
|
|
if movie.is_some() {
|
|
bail!("movie already exists");
|
|
}
|
|
|
|
let movie = client
|
|
.movies()
|
|
.get_details(id, None)
|
|
.await
|
|
.with_context(|| format!("movies().get_details({})", id.into_raw()))?;
|
|
|
|
let result: Result<MovieId, TransactionError<DbErr>> = db
|
|
.transaction(|txn| {
|
|
Box::pin(async move {
|
|
let flix = entity::info::movies::ActiveModel {
|
|
id: NotSet,
|
|
title: Set(movie.title),
|
|
tagline: Set(movie.tagline),
|
|
overview: Set(movie.overview),
|
|
date: Set(movie.release_date),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
entity::tmdb::movies::ActiveModel {
|
|
tmdb_id: Set(id),
|
|
flix_id: Set(flix.id),
|
|
last_update: Set(Utc::now().date_naive()),
|
|
runtime: Set(movie.runtime.into()),
|
|
collection: Set(movie.collection.map(|c| c.id)),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
Ok(flix.id)
|
|
})
|
|
})
|
|
.await;
|
|
|
|
let flix_id = match result {
|
|
Ok(id) => id,
|
|
Err(TransactionError::Connection(err)) => Err(err)?,
|
|
Err(TransactionError::Transaction(err)) => Err(err)?,
|
|
};
|
|
println!("Created Movie: {}", flix_id.into_raw());
|
|
|
|
Ok(())
|
|
}
|
|
Command::Show { id } => {
|
|
let id = TmdbShowId::from_raw(id);
|
|
|
|
let show = entity::tmdb::shows::Entity::find_by_id(id).one(db).await?;
|
|
if show.is_some() {
|
|
bail!("show already exists");
|
|
}
|
|
|
|
let show = client
|
|
.shows()
|
|
.get_details(id, None)
|
|
.await
|
|
.with_context(|| format!("shows().get_details({})", id.into_raw()))?;
|
|
let mut seasons = Vec::new();
|
|
let mut episodes = HashMap::new();
|
|
|
|
for season in 1..=show.number_of_seasons {
|
|
let season = client
|
|
.seasons()
|
|
.get_details(id, season, None)
|
|
.await
|
|
.with_context(|| {
|
|
format!("seasons().get_details({}, {})", id.into_raw(), season)
|
|
})?;
|
|
if season.air_date > Utc::now().date_naive() {
|
|
eprintln!(
|
|
"skipping season ({}, {})",
|
|
id.into_raw(),
|
|
season.season_number
|
|
);
|
|
break;
|
|
}
|
|
|
|
let Ok(number_of_episodes) = EpisodeNumber::try_from(season.episodes.len()) else {
|
|
bail!(
|
|
"could not convert {} to an EpisodeNumber",
|
|
season.episodes.len()
|
|
)
|
|
};
|
|
|
|
let mut season_episodes = Vec::new();
|
|
for episode in 1..=number_of_episodes {
|
|
let Ok(episode) = client
|
|
.episodes()
|
|
.get_details(id, season.season_number, episode, None)
|
|
.await
|
|
else {
|
|
eprintln!(
|
|
"skipping episode ({}, {}, {})",
|
|
id.into_raw(),
|
|
season.season_number,
|
|
episode
|
|
);
|
|
break;
|
|
};
|
|
season_episodes.push(episode);
|
|
}
|
|
|
|
episodes.insert(season.season_number, season_episodes);
|
|
seasons.push(season);
|
|
}
|
|
|
|
let result: Result<ShowId, TransactionError<DbErr>> = db
|
|
.transaction(|txn| {
|
|
Box::pin(async move {
|
|
let flix = entity::info::shows::ActiveModel {
|
|
id: NotSet,
|
|
title: Set(show.title),
|
|
tagline: Set(show.tagline),
|
|
overview: Set(show.overview),
|
|
date: Set(show.first_air_date),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
entity::tmdb::shows::ActiveModel {
|
|
tmdb_id: Set(id),
|
|
flix_id: Set(flix.id),
|
|
last_update: Set(Utc::now().date_naive()),
|
|
number_of_seasons: Set(show.number_of_seasons),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
for season in seasons {
|
|
entity::info::seasons::ActiveModel {
|
|
show: Set(flix.id),
|
|
season: Set(season.season_number),
|
|
title: Set(season.title),
|
|
overview: Set(season.overview),
|
|
date: Set(season.air_date),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
entity::tmdb::seasons::ActiveModel {
|
|
tmdb_show: Set(id),
|
|
tmdb_season: Set(season.season_number),
|
|
flix_show: Set(flix.id),
|
|
flix_season: Set(season.season_number),
|
|
last_update: Set(Utc::now().date_naive()),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
}
|
|
|
|
for (season, episodes) in episodes {
|
|
for episode in episodes {
|
|
entity::info::episodes::ActiveModel {
|
|
show: Set(flix.id),
|
|
season: Set(season),
|
|
episode: Set(episode.episode_number),
|
|
title: Set(episode.title),
|
|
overview: Set(episode.overview),
|
|
date: Set(episode.air_date),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
entity::tmdb::episodes::ActiveModel {
|
|
tmdb_show: Set(id),
|
|
tmdb_season: Set(season),
|
|
tmdb_episode: Set(episode.episode_number),
|
|
flix_show: Set(flix.id),
|
|
flix_season: Set(season),
|
|
flix_episode: Set(episode.episode_number),
|
|
last_update: Set(Utc::now().date_naive()),
|
|
runtime: Set(episode.runtime.into()),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
}
|
|
}
|
|
|
|
Ok(flix.id)
|
|
})
|
|
})
|
|
.await;
|
|
|
|
let flix_id = match result {
|
|
Ok(id) => id,
|
|
Err(TransactionError::Connection(err)) => Err(err)?,
|
|
Err(TransactionError::Transaction(err)) => Err(err)?,
|
|
};
|
|
println!("Created Show: {}", flix_id.into_raw());
|
|
|
|
Ok(())
|
|
}
|
|
Command::Season { id, season } => {
|
|
let id = TmdbShowId::from_raw(id);
|
|
let season_number = season;
|
|
|
|
let Some(show) = entity::tmdb::shows::Entity::find_by_id(id).one(db).await? else {
|
|
bail!("show does not exists");
|
|
};
|
|
|
|
let season = entity::tmdb::seasons::Entity::find_by_id((id, season))
|
|
.one(db)
|
|
.await?;
|
|
if season.is_some() {
|
|
bail!("season already exists");
|
|
}
|
|
|
|
let season = client
|
|
.seasons()
|
|
.get_details(id, season_number, None)
|
|
.await
|
|
.with_context(|| {
|
|
format!(
|
|
"seasons().get_details({}, {})",
|
|
id.into_raw(),
|
|
season_number
|
|
)
|
|
})?;
|
|
let mut episodes = Vec::new();
|
|
|
|
let Ok(number_of_episodes) = EpisodeNumber::try_from(season.episodes.len()) else {
|
|
bail!(
|
|
"could not convert {} to an EpisodeNumber",
|
|
season.episodes.len()
|
|
)
|
|
};
|
|
|
|
for episode in 1..=number_of_episodes {
|
|
let Ok(episode) = client
|
|
.episodes()
|
|
.get_details(id, season.season_number, episode, None)
|
|
.await
|
|
else {
|
|
eprintln!(
|
|
"skipping episode ({}, {}, {})",
|
|
id.into_raw(),
|
|
season.season_number,
|
|
episode
|
|
);
|
|
break;
|
|
};
|
|
episodes.push(episode);
|
|
}
|
|
|
|
let result: Result<(), TransactionError<DbErr>> = db
|
|
.transaction(|txn| {
|
|
Box::pin(async move {
|
|
entity::info::seasons::ActiveModel {
|
|
show: Set(show.flix_id),
|
|
season: Set(season_number),
|
|
title: Set(season.title),
|
|
overview: Set(season.overview),
|
|
date: Set(season.air_date),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
entity::tmdb::seasons::ActiveModel {
|
|
tmdb_show: Set(show.tmdb_id),
|
|
tmdb_season: Set(season_number),
|
|
flix_show: Set(show.flix_id),
|
|
flix_season: Set(season_number),
|
|
last_update: Set(Utc::now().date_naive()),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
for episode in episodes {
|
|
entity::info::episodes::ActiveModel {
|
|
show: Set(show.flix_id),
|
|
season: Set(season_number),
|
|
episode: Set(episode.episode_number),
|
|
title: Set(episode.title),
|
|
overview: Set(episode.overview),
|
|
date: Set(episode.air_date),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
entity::tmdb::episodes::ActiveModel {
|
|
tmdb_show: Set(show.tmdb_id),
|
|
tmdb_season: Set(season_number),
|
|
tmdb_episode: Set(episode.episode_number),
|
|
flix_show: Set(show.flix_id),
|
|
flix_season: Set(season_number),
|
|
flix_episode: Set(episode.episode_number),
|
|
last_update: Set(Utc::now().date_naive()),
|
|
runtime: Set(episode.runtime.into()),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
}
|
|
|
|
Ok(())
|
|
})
|
|
})
|
|
.await;
|
|
|
|
match result {
|
|
Ok(_) => (),
|
|
Err(TransactionError::Connection(err)) => Err(err)?,
|
|
Err(TransactionError::Transaction(err)) => Err(err)?,
|
|
};
|
|
println!(
|
|
"Created Season: {} S{}",
|
|
show.flix_id.into_raw(),
|
|
season_number
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
Command::Episode {
|
|
id,
|
|
season,
|
|
episode,
|
|
episodes,
|
|
} => {
|
|
let id = TmdbShowId::from_raw(id);
|
|
let season_number = season;
|
|
|
|
let Some(show) = entity::tmdb::shows::Entity::find_by_id(id).one(db).await? else {
|
|
bail!("show does not exists");
|
|
};
|
|
let Some(_) = entity::tmdb::seasons::Entity::find_by_id((id, season))
|
|
.one(db)
|
|
.await?
|
|
else {
|
|
bail!("season does not exists");
|
|
};
|
|
|
|
async fn fetch_episode(
|
|
client: &Client,
|
|
db: &DatabaseConnection,
|
|
flix_id: ShowId,
|
|
tmdb_id: TmdbShowId,
|
|
id: TmdbShowId,
|
|
season: SeasonNumber,
|
|
episode: EpisodeNumber,
|
|
) -> Result<()> {
|
|
let episode_number = episode;
|
|
|
|
let episode = entity::tmdb::episodes::Entity::find_by_id((id, season, episode))
|
|
.one(db)
|
|
.await?;
|
|
if episode.is_some() {
|
|
bail!("episode already exists");
|
|
}
|
|
|
|
let episode = client
|
|
.episodes()
|
|
.get_details(id, season, episode_number, None)
|
|
.await
|
|
.with_context(|| {
|
|
format!("episodes().get_details({}, {})", id.into_raw(), season)
|
|
})?;
|
|
|
|
let result: Result<(), TransactionError<DbErr>> = db
|
|
.transaction(|txn| {
|
|
Box::pin(async move {
|
|
entity::info::episodes::ActiveModel {
|
|
show: Set(flix_id),
|
|
season: Set(season),
|
|
episode: Set(episode_number),
|
|
title: Set(episode.title),
|
|
overview: Set(episode.overview),
|
|
date: Set(episode.air_date),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
entity::tmdb::episodes::ActiveModel {
|
|
tmdb_show: Set(tmdb_id),
|
|
tmdb_season: Set(season),
|
|
tmdb_episode: Set(episode_number),
|
|
flix_show: Set(flix_id),
|
|
flix_season: Set(season),
|
|
flix_episode: Set(episode_number),
|
|
last_update: Set(Utc::now().date_naive()),
|
|
runtime: Set(episode.runtime.into()),
|
|
}
|
|
.insert(txn)
|
|
.await?;
|
|
|
|
Ok(())
|
|
})
|
|
})
|
|
.await;
|
|
|
|
match result {
|
|
Ok(_) => (),
|
|
Err(TransactionError::Connection(err)) => Err(err)?,
|
|
Err(TransactionError::Transaction(err)) => Err(err)?,
|
|
};
|
|
println!(
|
|
"Created Episode: {} S{}E{}",
|
|
flix_id.into_raw(),
|
|
season,
|
|
episode_number
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
let flix_id = show.flix_id;
|
|
let tmdb_id = show.tmdb_id;
|
|
|
|
fetch_episode(&client, db, flix_id, tmdb_id, id, season_number, episode).await?;
|
|
for episode in episodes {
|
|
fetch_episode(&client, db, flix_id, tmdb_id, id, season_number, episode).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn update(client: Client, database: &DatabaseConnection, command: Command) -> Result<()> {
|
|
_ = client;
|
|
_ = database;
|
|
_ = command;
|
|
unimplemented!("updates")
|
|
}
|
|
|
|
pub async fn delete(client: Client, database: &DatabaseConnection, command: Command) -> Result<()> {
|
|
_ = client;
|
|
_ = database;
|
|
_ = command;
|
|
unimplemented!("deletions")
|
|
}
|