You've already forked flix
Update all libraries to the new database format
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flix-cli"
|
||||
version = "0.0.16"
|
||||
version = "0.0.17"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
description = "CLI for interacting with a flix database"
|
||||
|
||||
@@ -1,12 +1,30 @@
|
||||
use flix::model::numbers::{EpisodeNumber, SeasonNumber};
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use clap::Subcommand;
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum AddCommand {
|
||||
/// Process a flix collection
|
||||
/// Add a flix collection
|
||||
Collection {
|
||||
#[arg(value_name = "TITLE")]
|
||||
title: String,
|
||||
#[arg(value_name = "OVERVIEW")]
|
||||
overview: String,
|
||||
},
|
||||
/// Add a flix episode
|
||||
Episode {
|
||||
#[arg(value_name = "SHOW_WEB_SLUG")]
|
||||
show_slug: String,
|
||||
#[arg(value_name = "NUMBER")]
|
||||
season_number: SeasonNumber,
|
||||
#[arg(value_name = "NUMBER")]
|
||||
episode_number: EpisodeNumber,
|
||||
#[arg(value_name = "TITLE")]
|
||||
title: String,
|
||||
#[arg(value_name = "OVERVIEW")]
|
||||
overview: String,
|
||||
#[arg(value_name = "DATE")]
|
||||
air_date: NaiveDate,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use clap::{Parser, Subcommand};
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
|
||||
pub mod flix;
|
||||
pub mod tmdb;
|
||||
@@ -13,8 +13,12 @@ pub struct Cli {
|
||||
#[arg(short, long, value_name = "FILE", default_value = "~/.flix")]
|
||||
config: PathBuf,
|
||||
|
||||
/// Use a custom cache file
|
||||
#[arg(short = 'C', long, value_name = "FILE", default_value = "./flix.redb")]
|
||||
cache: PathBuf,
|
||||
|
||||
/// Use a custom database file
|
||||
#[arg(short, long, value_name = "DATABASE", default_value = "./flix.db")]
|
||||
#[arg(short, long, value_name = "FILE", default_value = "./flix.db")]
|
||||
database: PathBuf,
|
||||
|
||||
/// Enable tracing
|
||||
@@ -38,6 +42,10 @@ impl Cli {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cache_path(&self) -> &Path {
|
||||
&self.cache
|
||||
}
|
||||
|
||||
pub fn database_path(&self) -> Result<String> {
|
||||
self.database
|
||||
.as_os_str()
|
||||
@@ -51,12 +59,26 @@ impl Cli {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct AddOverrides {
|
||||
#[arg(long)]
|
||||
pub title: Option<String>,
|
||||
#[arg(long)]
|
||||
pub sort_title: Option<String>,
|
||||
#[arg(long)]
|
||||
pub fs_slug: Option<String>,
|
||||
#[arg(long)]
|
||||
pub web_slug: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Command {
|
||||
/// Initialize a new database
|
||||
Init,
|
||||
/// Add new items to the database
|
||||
Add {
|
||||
#[command(flatten)]
|
||||
overrides: AddOverrides,
|
||||
#[command(subcommand)]
|
||||
command: AddCommand,
|
||||
},
|
||||
|
||||
+20
-6
@@ -1,6 +1,7 @@
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
use flix::tmdb::Client;
|
||||
use flix::tmdb::{self, CachePolicy, Client, RedbCache};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
@@ -12,6 +13,8 @@ use cli::{AddCommand, Cli, Command, DeleteCommand, UpdateCommand};
|
||||
mod config;
|
||||
use config::Config;
|
||||
|
||||
use crate::cli::AddOverrides;
|
||||
|
||||
mod db;
|
||||
mod run;
|
||||
|
||||
@@ -26,7 +29,11 @@ async fn main() -> Result<()> {
|
||||
|
||||
let database_path = cli.database_path()?;
|
||||
|
||||
let client = Client::new(config.tmdb().bearer_token().to_owned());
|
||||
let client = Client::new(
|
||||
tmdb::Config::new(config.tmdb().bearer_token().to_owned()),
|
||||
Rc::new(RedbCache::new(cli.cache_path())?),
|
||||
CachePolicy::Full,
|
||||
);
|
||||
|
||||
if cli.trace {
|
||||
tracing_subscriber::fmt()
|
||||
@@ -37,7 +44,9 @@ async fn main() -> Result<()> {
|
||||
|
||||
match cli.command() {
|
||||
Command::Init => exec_init(database_path).await?,
|
||||
Command::Add { command } => exec_add(client, database_path, command).await?,
|
||||
Command::Add { command, overrides } => {
|
||||
exec_add(client, database_path, command, overrides).await?
|
||||
}
|
||||
Command::Update { command } => exec_update(client, database_path, command).await?,
|
||||
Command::Delete { command } => exec_delete(client, database_path, command).await?,
|
||||
Command::Backup { output } => exec_backup(database_path, output).await?,
|
||||
@@ -53,15 +62,20 @@ async fn exec_init(database_path: String) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn exec_add(client: Client, database_path: String, command: AddCommand) -> Result<()> {
|
||||
async fn exec_add(
|
||||
client: Client,
|
||||
database_path: String,
|
||||
command: AddCommand,
|
||||
overrides: AddOverrides,
|
||||
) -> Result<()> {
|
||||
let database = db::open(database_path).await?;
|
||||
|
||||
match command {
|
||||
AddCommand::Flix { command } => {
|
||||
run::flix::add(database.as_ref(), command).await?;
|
||||
run::flix::add(database.as_ref(), command, overrides).await?;
|
||||
}
|
||||
AddCommand::Tmdb { command } => {
|
||||
run::tmdb::add(client, database.as_ref(), command).await?;
|
||||
run::tmdb::add(client, database.as_ref(), command, overrides).await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,35 @@
|
||||
use flix::db::entity;
|
||||
use flix::model::id::CollectionId;
|
||||
use flix::model::id::{CollectionId, ShowId};
|
||||
use flix::model::numbers::{EpisodeNumber, SeasonNumber};
|
||||
use flix::model::text;
|
||||
|
||||
use anyhow::Result;
|
||||
use sea_orm::ActiveValue::{NotSet, Set};
|
||||
use sea_orm::{ActiveModelTrait, DatabaseConnection, DbErr, TransactionError, TransactionTrait};
|
||||
|
||||
use crate::cli::AddOverrides;
|
||||
use crate::cli::flix::AddCommand;
|
||||
|
||||
pub async fn add(db: &DatabaseConnection, command: AddCommand) -> Result<()> {
|
||||
pub async fn add(
|
||||
db: &DatabaseConnection,
|
||||
command: AddCommand,
|
||||
overrides: AddOverrides,
|
||||
) -> Result<()> {
|
||||
match command {
|
||||
AddCommand::Collection { title, overview } => {
|
||||
let result: Result<CollectionId, TransactionError<DbErr>> = db
|
||||
.transaction(|txn| {
|
||||
let title = title.clone();
|
||||
let title = overrides.title.unwrap_or_else(|| title.clone());
|
||||
|
||||
let sort_title = text::make_sortable_title(&title);
|
||||
let fs_slug = text::make_fs_slug(&title);
|
||||
let web_slug = text::make_web_slug(&title);
|
||||
let sort_title = overrides
|
||||
.sort_title
|
||||
.unwrap_or_else(|| text::make_sortable_title(&title));
|
||||
let fs_slug = overrides
|
||||
.fs_slug
|
||||
.unwrap_or_else(|| text::make_fs_slug(&title));
|
||||
let web_slug = overrides
|
||||
.web_slug
|
||||
.unwrap_or_else(|| text::make_web_slug(&title));
|
||||
|
||||
Box::pin(async move {
|
||||
let flix = entity::info::collections::ActiveModel {
|
||||
@@ -43,6 +55,57 @@ pub async fn add(db: &DatabaseConnection, command: AddCommand) -> Result<()> {
|
||||
};
|
||||
println!("Created Collection: {} [{}]", title, flix_id.into_raw());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
AddCommand::Episode {
|
||||
show_slug,
|
||||
season_number,
|
||||
episode_number,
|
||||
title,
|
||||
overview,
|
||||
air_date,
|
||||
} => {
|
||||
let result: Result<(ShowId, SeasonNumber, EpisodeNumber), TransactionError<DbErr>> = db
|
||||
.transaction(|txn| {
|
||||
let title = overrides.title.unwrap_or_else(|| title.clone());
|
||||
|
||||
Box::pin(async move {
|
||||
let show = entity::info::shows::Entity::find_by_web_slug(&show_slug)
|
||||
.one(txn)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
DbErr::Custom(format!("show '{}' does not exist", show_slug))
|
||||
})?;
|
||||
|
||||
let flix = entity::info::episodes::ActiveModel {
|
||||
show_id: Set(show.id),
|
||||
season_number: Set(season_number),
|
||||
episode_number: Set(episode_number),
|
||||
title: Set(title),
|
||||
overview: Set(overview),
|
||||
date: Set(air_date),
|
||||
}
|
||||
.insert(txn)
|
||||
.await?;
|
||||
|
||||
Ok((flix.show_id, flix.season_number, flix.episode_number))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
|
||||
let (flix_show, season_number, episode_number) = match result {
|
||||
Ok(id) => id,
|
||||
Err(TransactionError::Connection(err)) => Err(err)?,
|
||||
Err(TransactionError::Transaction(err)) => Err(err)?,
|
||||
};
|
||||
println!(
|
||||
"Created Episode: {} [{} S{} E{}]",
|
||||
title,
|
||||
flix_show.into_raw(),
|
||||
season_number,
|
||||
episode_number
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+45
-18
@@ -16,9 +16,15 @@ use sea_orm::{
|
||||
ActiveModelTrait, DatabaseConnection, DbErr, EntityTrait, TransactionError, TransactionTrait,
|
||||
};
|
||||
|
||||
use crate::cli::AddOverrides;
|
||||
use crate::cli::tmdb::Command;
|
||||
|
||||
pub async fn add(client: Client, db: &DatabaseConnection, command: Command) -> Result<()> {
|
||||
pub async fn add(
|
||||
client: Client,
|
||||
db: &DatabaseConnection,
|
||||
command: Command,
|
||||
overrides: AddOverrides,
|
||||
) -> Result<()> {
|
||||
match command {
|
||||
Command::Collection { id } => {
|
||||
let id = TmdbCollectionId::from_raw(id);
|
||||
@@ -36,18 +42,25 @@ pub async fn add(client: Client, db: &DatabaseConnection, command: Command) -> R
|
||||
.await
|
||||
.with_context(|| format!("collections().get_details({})", id.into_raw()))?;
|
||||
|
||||
let title = collection.title.clone();
|
||||
let title = overrides.title.unwrap_or(collection.title);
|
||||
|
||||
let sort_title = text::make_sortable_title(&title);
|
||||
let fs_slug = text::make_fs_slug(&title);
|
||||
let web_slug = text::make_web_slug(&title);
|
||||
let sort_title = overrides
|
||||
.sort_title
|
||||
.unwrap_or_else(|| text::make_sortable_title(&title));
|
||||
let fs_slug = overrides
|
||||
.fs_slug
|
||||
.unwrap_or_else(|| text::make_fs_slug(&title));
|
||||
let web_slug = overrides
|
||||
.web_slug
|
||||
.unwrap_or_else(|| text::make_web_slug(&title));
|
||||
|
||||
let result: Result<CollectionId, TransactionError<DbErr>> = db
|
||||
.transaction(|txn| {
|
||||
let title = title.clone();
|
||||
Box::pin(async move {
|
||||
let flix = entity::info::collections::ActiveModel {
|
||||
id: NotSet,
|
||||
title: Set(collection.title),
|
||||
title: Set(title),
|
||||
overview: Set(collection.overview),
|
||||
sort_title: Set(sort_title),
|
||||
fs_slug: Set(fs_slug),
|
||||
@@ -93,19 +106,26 @@ pub async fn add(client: Client, db: &DatabaseConnection, command: Command) -> R
|
||||
.await
|
||||
.with_context(|| format!("movies().get_details({})", id.into_raw()))?;
|
||||
|
||||
let title = movie.title.clone();
|
||||
let title = overrides.title.unwrap_or(movie.title);
|
||||
let year = movie.release_date.year();
|
||||
|
||||
let sort_title = text::make_sortable_title(&title);
|
||||
let fs_slug = text::make_fs_slug_year(&title, year);
|
||||
let web_slug = text::make_web_slug_year(&title, year);
|
||||
let sort_title = overrides
|
||||
.sort_title
|
||||
.unwrap_or_else(|| text::make_sortable_title(&title));
|
||||
let fs_slug = overrides
|
||||
.fs_slug
|
||||
.unwrap_or_else(|| text::make_fs_slug_year(&title, year));
|
||||
let web_slug = overrides
|
||||
.web_slug
|
||||
.unwrap_or_else(|| text::make_web_slug_year(&title, year));
|
||||
|
||||
let result: Result<MovieId, TransactionError<DbErr>> = db
|
||||
.transaction(|txn| {
|
||||
let title = title.clone();
|
||||
Box::pin(async move {
|
||||
let flix = entity::info::movies::ActiveModel {
|
||||
id: NotSet,
|
||||
title: Set(movie.title),
|
||||
title: Set(title),
|
||||
tagline: Set(movie.tagline),
|
||||
overview: Set(movie.overview),
|
||||
date: Set(movie.release_date),
|
||||
@@ -161,9 +181,6 @@ pub async fn add(client: Client, db: &DatabaseConnection, command: Command) -> R
|
||||
let mut seasons = Vec::new();
|
||||
let mut episodes = HashMap::new();
|
||||
|
||||
let title = show.title.clone();
|
||||
let year = show.first_air_date.year();
|
||||
|
||||
for season in 1..=show.number_of_seasons {
|
||||
let season = SeasonNumber::new(season);
|
||||
let season = match client
|
||||
@@ -218,16 +235,26 @@ pub async fn add(client: Client, db: &DatabaseConnection, command: Command) -> R
|
||||
seasons.push(season);
|
||||
}
|
||||
|
||||
let sort_title = text::make_sortable_title(&show.title);
|
||||
let fs_slug = text::make_fs_slug_year(&show.title, show.first_air_date.year());
|
||||
let web_slug = text::make_web_slug_year(&show.title, show.first_air_date.year());
|
||||
let title = overrides.title.unwrap_or(show.title);
|
||||
let year = show.first_air_date.year();
|
||||
|
||||
let sort_title = overrides
|
||||
.sort_title
|
||||
.unwrap_or_else(|| text::make_sortable_title(&title));
|
||||
let fs_slug = overrides
|
||||
.fs_slug
|
||||
.unwrap_or_else(|| text::make_fs_slug_year(&title, year));
|
||||
let web_slug = overrides
|
||||
.web_slug
|
||||
.unwrap_or_else(|| text::make_web_slug_year(&title, year));
|
||||
|
||||
let result: Result<ShowId, TransactionError<DbErr>> = db
|
||||
.transaction(|txn| {
|
||||
let title = title.clone();
|
||||
Box::pin(async move {
|
||||
let flix = entity::info::shows::ActiveModel {
|
||||
id: NotSet,
|
||||
title: Set(show.title),
|
||||
title: Set(title),
|
||||
tagline: Set(show.tagline),
|
||||
overview: Set(show.overview),
|
||||
date: Set(show.first_air_date),
|
||||
|
||||
Reference in New Issue
Block a user