diff --git a/config/recorder.development.yaml b/config/recorder.development.yaml index 7f220c0..5fda64c 100644 --- a/config/recorder.development.yaml +++ b/config/recorder.development.yaml @@ -65,7 +65,7 @@ workers: # - BackgroundQueue - Workers operate asynchronously in the background, processing queued. # - ForegroundBlocking - Workers operate in the foreground and block until tasks are completed. # - BackgroundAsync - Workers operate asynchronously in the background, processing tasks with async capabilities. - mode: BackgroundQueue + mode: BackgroundAsync # Database Configuration database: @@ -88,3 +88,5 @@ database: # Recreating schema when application loaded. This is a dangerous operation, make sure that you using this flag only on dev environments or test mode dangerously_recreate: false +tmdb: + api_token: '{{ get_env(name="TMDB_API_TOKEN") }}' diff --git a/crates/recorder/src/app.rs b/crates/recorder/src/app.rs index 8c462a8..50dd792 100644 --- a/crates/recorder/src/app.rs +++ b/crates/recorder/src/app.rs @@ -1,20 +1,21 @@ -use std::path::Path; - use async_trait::async_trait; use loco_rs::{ - app::{AppContext, Hooks, Initializer}, + app::Hooks, boot::{create_app, BootResult, StartMode}, controller::AppRoutes, db::truncate_table, environment::Environment, + prelude::*, task::Tasks, - worker::{AppWorker, Processor}, - Result, + worker::Processor, }; -use sea_orm::DatabaseConnection; +use sea_orm::prelude::*; use crate::{ - controllers, migrations::Migrator, models::entities::subscribers, storage::AppDalInitializer, + controllers, + migrations::Migrator, + models::{bangumi, downloaders, episodes, resources, subscribers, subscriptions}, + storage::AppDalInitializer, workers::subscription::SubscriptionWorker, }; @@ -53,11 +54,20 @@ impl Hooks for App { fn register_tasks(_tasks: &mut Tasks) {} async fn truncate(db: &DatabaseConnection) -> Result<()> { - truncate_table(db, subscribers::Entity).await?; + futures::try_join!( + subscribers::Entity::delete_many() + .filter(subscribers::Column::Id.ne(subscribers::ROOT_SUBSCRIBER_ID)) + .exec(db), + truncate_table(db, subscriptions::Entity), + truncate_table(db, resources::Entity), + truncate_table(db, downloaders::Entity), + truncate_table(db, bangumi::Entity), + truncate_table(db, episodes::Entity), + )?; Ok(()) } - async fn seed(_db: &DatabaseConnection, _base: &Path) -> Result<()> { + async fn seed(_db: &DatabaseConnection, _base: &std::path::Path) -> Result<()> { Ok(()) } diff --git a/crates/recorder/src/downloaders/torrent.rs b/crates/recorder/src/downloaders/torrent.rs index cb01c59..6618dc9 100644 --- a/crates/recorder/src/downloaders/torrent.rs +++ b/crates/recorder/src/downloaders/torrent.rs @@ -20,7 +20,7 @@ use super::{ qbitorrent::QBittorrentDownloader, }; use crate::{ - models::{bangumi, downloaders, downloaders::DownloaderCategory, downloads}, + models::{bangumi, downloaders, downloaders::DownloaderCategory, resources}, path::torrent_path::gen_bangumi_sub_path, }; @@ -296,10 +296,10 @@ pub trait TorrentDownloader { fn get_save_path(&self, sub_path: &Path) -> PathBuf; - async fn add_downloads_for_bangumi<'a, 'b>( + async fn add_resources_for_bangumi<'a, 'b>( &self, db: &'a DatabaseConnection, - downloads: &[&downloads::Model], + resources: &[&resources::Model], mut bangumi: bangumi::Model, ) -> eyre::Result { if bangumi.save_path.is_none() { @@ -315,12 +315,12 @@ pub trait TorrentDownloader { .unwrap_or_else(|| unreachable!("must have a sub path")); let mut torrent_urls = vec![]; - for m in downloads.iter() { + for m in resources.iter() { torrent_urls.push(Url::parse(&m.url as &str)?); } // make sequence to prevent too fast to be banned - for d in downloads.iter() { + for d in resources.iter() { let source = TorrentSource::parse(&d.url).await?; self.add_torrents(source, sub_path.clone(), Some("bangumi")) .await?; diff --git a/crates/recorder/src/migrations/defs.rs b/crates/recorder/src/migrations/defs.rs index 23c1892..5162a98 100644 --- a/crates/recorder/src/migrations/defs.rs +++ b/crates/recorder/src/migrations/defs.rs @@ -9,6 +9,7 @@ use crate::migrations::extension::postgres::Type; pub enum GeneralIds { CreatedAt, UpdatedAt, + Id, } #[derive(DeriveIden)] @@ -63,7 +64,7 @@ pub enum Episodes { SNameJp, SNameEn, BangumiId, - DownloadId, + ResourceId, SavePath, Resolution, Season, @@ -76,7 +77,7 @@ pub enum Episodes { } #[derive(DeriveIden)] -pub enum Downloads { +pub enum Resources { Table, Id, SubscriptionId, diff --git a/crates/recorder/src/migrations/m20220101_000001_init.rs b/crates/recorder/src/migrations/m20220101_000001_init.rs index b7069f7..6bb1fde 100644 --- a/crates/recorder/src/migrations/m20220101_000001_init.rs +++ b/crates/recorder/src/migrations/m20220101_000001_init.rs @@ -4,7 +4,10 @@ use sea_orm_migration::{prelude::*, schema::*}; use super::defs::{ Bangumi, CustomSchemaManagerExt, Episodes, GeneralIds, Subscribers, Subscriptions, }; -use crate::models::{subscribers::ROOT_SUBSCRIBER, subscriptions}; +use crate::models::{ + subscribers::{ROOT_SUBSCRIBER_ID, ROOT_SUBSCRIBER_NAME}, + subscriptions, +}; #[derive(DeriveMigrationName)] pub struct Migration; @@ -34,8 +37,12 @@ impl MigrationTrait for Migration { let insert = Query::insert() .into_table(Subscribers::Table) - .columns([Subscribers::Pid, Subscribers::DisplayName]) - .values_panic([ROOT_SUBSCRIBER.into(), ROOT_SUBSCRIBER.into()]) + .columns([Subscribers::Id, Subscribers::Pid, Subscribers::DisplayName]) + .values_panic([ + ROOT_SUBSCRIBER_ID.into(), + ROOT_SUBSCRIBER_NAME.into(), + ROOT_SUBSCRIBER_NAME.into(), + ]) .to_owned(); manager.exec_stmt(insert).await?; @@ -147,7 +154,7 @@ impl MigrationTrait for Migration { .col(text_null(Episodes::SNameJp)) .col(text_null(Episodes::SNameEn)) .col(integer(Episodes::BangumiId)) - .col(integer(Episodes::DownloadId)) + .col(integer(Episodes::ResourceId)) .col(text_null(Episodes::SavePath)) .col(string_null(Episodes::Resolution)) .col(integer(Episodes::Season)) diff --git a/crates/recorder/src/migrations/m20240224_082543_add_downloads.rs b/crates/recorder/src/migrations/m20240224_082543_add_resources.rs similarity index 64% rename from crates/recorder/src/migrations/m20240224_082543_add_downloads.rs rename to crates/recorder/src/migrations/m20240224_082543_add_resources.rs index 3c840c7..b0f4a96 100644 --- a/crates/recorder/src/migrations/m20240224_082543_add_downloads.rs +++ b/crates/recorder/src/migrations/m20240224_082543_add_resources.rs @@ -2,9 +2,8 @@ use loco_rs::schema::table_auto; use sea_orm_migration::{prelude::*, schema::*}; use super::defs::*; -use crate::models::prelude::{ - downloads::{DownloadMimeEnum, DownloadStatusEnum}, - DownloadMime, DownloadStatus, +use crate::models::resources::{ + DownloadStatus, DownloadStatusEnum, ResourceMime, ResourceMimeEnum, }; #[derive(DeriveMigrationName)] @@ -15,8 +14,8 @@ impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { manager .create_postgres_enum_for_active_enum( - DownloadMimeEnum, - &[DownloadMime::OctetStream, DownloadMime::BitTorrent], + ResourceMimeEnum, + &[ResourceMime::OctetStream, ResourceMime::BitTorrent], ) .await?; @@ -36,29 +35,29 @@ impl MigrationTrait for Migration { manager .create_table( - table_auto(Downloads::Table) - .col(pk_auto(Downloads::Id)) - .col(text(Downloads::OriginTitle)) - .col(text(Downloads::DisplayName)) - .col(integer(Downloads::SubscriptionId)) + table_auto(Resources::Table) + .col(pk_auto(Resources::Id)) + .col(text(Resources::OriginTitle)) + .col(text(Resources::DisplayName)) + .col(integer(Resources::SubscriptionId)) .col(enumeration( - Downloads::Status, + Resources::Status, DownloadStatusEnum, - DownloadMime::iden_values(), + ResourceMime::iden_values(), )) .col(enumeration( - Downloads::Mime, - DownloadMimeEnum, - DownloadMime::iden_values(), + Resources::Mime, + ResourceMimeEnum, + ResourceMime::iden_values(), )) - .col(big_unsigned_null(Downloads::AllSize)) - .col(big_unsigned_null(Downloads::CurrSize)) - .col(text(Downloads::Url)) - .col(text_null(Downloads::HomePage)) + .col(big_unsigned_null(Resources::AllSize)) + .col(big_unsigned_null(Resources::CurrSize)) + .col(text(Resources::Url)) + .col(text_null(Resources::HomePage)) .foreign_key( ForeignKey::create() .name("fk_download_subscription_id") - .from(Downloads::Table, Downloads::SubscriptionId) + .from(Resources::Table, Resources::SubscriptionId) .to(Subscriptions::Table, Subscriptions::Id) .on_update(ForeignKeyAction::Restrict) .on_delete(ForeignKeyAction::Cascade), @@ -66,14 +65,14 @@ impl MigrationTrait for Migration { .index( Index::create() .name("idx_download_url") - .table(Downloads::Table) - .col(Downloads::Url), + .table(Resources::Table) + .col(Resources::Url), ) .index( Index::create() .name("idx_download_home_page") - .table(Downloads::Table) - .col(Downloads::HomePage), + .table(Resources::Table) + .col(Resources::HomePage), ) .to_owned(), ) @@ -87,14 +86,14 @@ impl MigrationTrait for Migration { .alter_table( Table::alter() .table(Episodes::Table) - .add_column_if_not_exists(integer_null(Episodes::DownloadId)) + .add_column_if_not_exists(integer_null(Episodes::ResourceId)) .add_foreign_key( TableForeignKey::new() - .name("fk_episode_download_id") + .name("fk_episode_resource_id") .from_tbl(Episodes::Table) - .from_col(Episodes::DownloadId) - .to_tbl(Downloads::Table) - .to_col(Downloads::Id) + .from_col(Episodes::ResourceId) + .to_tbl(Resources::Table) + .to_col(Resources::Id) .on_update(ForeignKeyAction::Restrict) .on_delete(ForeignKeyAction::SetNull), ) @@ -110,8 +109,8 @@ impl MigrationTrait for Migration { .alter_table( Table::alter() .table(Episodes::Table) - .drop_foreign_key(Alias::new("fk_episode_download_id")) - .drop_column(Episodes::DownloadId) + .drop_foreign_key(Alias::new("fk_episode_resource_id")) + .drop_column(Episodes::ResourceId) .to_owned(), ) .await?; @@ -121,11 +120,11 @@ impl MigrationTrait for Migration { .await?; manager - .drop_table(Table::drop().table(Downloads::Table).to_owned()) + .drop_table(Table::drop().table(Resources::Table).to_owned()) .await?; manager - .drop_postgres_enum_for_active_enum(DownloadMimeEnum) + .drop_postgres_enum_for_active_enum(ResourceMimeEnum) .await?; manager .drop_postgres_enum_for_active_enum(DownloadStatusEnum) diff --git a/crates/recorder/src/migrations/mod.rs b/crates/recorder/src/migrations/mod.rs index 4c31493..e0ebca8 100644 --- a/crates/recorder/src/migrations/mod.rs +++ b/crates/recorder/src/migrations/mod.rs @@ -2,7 +2,7 @@ pub use sea_orm_migration::prelude::*; pub mod defs; pub mod m20220101_000001_init; -pub mod m20240224_082543_add_downloads; +pub mod m20240224_082543_add_resources; pub mod m20240225_060853_subscriber_add_downloader; pub struct Migrator; @@ -12,7 +12,7 @@ impl MigratorTrait for Migrator { fn migrations() -> Vec> { vec![ Box::new(m20220101_000001_init::Migration), - Box::new(m20240224_082543_add_downloads::Migration), + Box::new(m20240224_082543_add_resources::Migration), Box::new(m20240225_060853_subscriber_add_downloader::Migration), ] } diff --git a/crates/recorder/src/models/db_utils.rs b/crates/recorder/src/models/db_utils.rs index a8d2cd7..1c40122 100644 --- a/crates/recorder/src/models/db_utils.rs +++ b/crates/recorder/src/models/db_utils.rs @@ -1,9 +1,11 @@ use sea_orm::{ - sea_query::{Expr, InsertStatement, Query, SimpleExpr}, - ActiveModelTrait, ActiveValue, ColumnTrait, ConnectionTrait, EntityName, EntityTrait, + sea_query::{Expr, InsertStatement, IntoIden, Query, SimpleExpr}, + ActiveModelTrait, ActiveValue, ColumnTrait, ConnectionTrait, DynIden, EntityName, EntityTrait, FromQueryResult, Iterable, SelectModel, SelectorRaw, TryGetable, }; +use crate::migrations::{defs::GeneralIds, ColumnRef}; + #[derive(FromQueryResult)] pub(crate) struct OnlyIdsModel where @@ -81,3 +83,25 @@ where Ok(result) } + +pub(crate) async fn insert_many_with_returning_id( + db: &D, + insert_values: impl IntoIterator, + extra_config: F, +) -> eyre::Result>> +where + D: ConnectionTrait, + V: ActiveModelTrait, + F: FnOnce(&mut InsertStatement), + I: TryGetable, +{ + let result: Vec> = insert_many_with_returning_columns( + db, + insert_values, + [Expr::col(ColumnRef::Column(GeneralIds::Id.into_iden()))], + extra_config, + ) + .await?; + + Ok(result) +} diff --git a/crates/recorder/src/models/entities/episodes.rs b/crates/recorder/src/models/entities/episodes.rs index 0f0b1fa..4a57375 100644 --- a/crates/recorder/src/models/entities/episodes.rs +++ b/crates/recorder/src/models/entities/episodes.rs @@ -20,7 +20,7 @@ pub struct Model { pub s_name_jp: Option, pub s_name_en: Option, pub bangumi_id: i32, - pub download_id: Option, + pub resource_id: Option, pub save_path: Option, pub resolution: Option, pub season: u32, @@ -41,11 +41,11 @@ pub enum Relation { )] Bangumi, #[sea_orm( - belongs_to = "super::downloads::Entity", - from = "Column::DownloadId", - to = "super::downloads::Column::Id" + belongs_to = "super::resources::Entity", + from = "Column::ResourceId", + to = "super::resources::Column::Id" )] - Downloads, + Resources, } impl Related for Entity { @@ -54,8 +54,8 @@ impl Related for Entity { } } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - Relation::Downloads.def() + Relation::Resources.def() } } diff --git a/crates/recorder/src/models/entities/mod.rs b/crates/recorder/src/models/entities/mod.rs index 4f42fd5..047e79d 100644 --- a/crates/recorder/src/models/entities/mod.rs +++ b/crates/recorder/src/models/entities/mod.rs @@ -1,7 +1,7 @@ //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.4 pub mod bangumi; -pub mod downloads; +pub mod downloaders; pub mod episodes; +pub mod resources; pub mod subscribers; pub mod subscriptions; -pub mod downloaders; diff --git a/crates/recorder/src/models/entities/downloads.rs b/crates/recorder/src/models/entities/resources.rs similarity index 92% rename from crates/recorder/src/models/entities/downloads.rs rename to crates/recorder/src/models/entities/resources.rs index c939361..905e19f 100644 --- a/crates/recorder/src/models/entities/downloads.rs +++ b/crates/recorder/src/models/entities/resources.rs @@ -24,8 +24,8 @@ pub enum DownloadStatus { #[derive( Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay, Serialize, Deserialize, )] -#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "download_mime")] -pub enum DownloadMime { +#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "resource_mime")] +pub enum ResourceMime { #[sea_orm(string_value = "application/octet-stream")] #[serde(rename = "application/octet-stream")] OctetStream, @@ -35,7 +35,7 @@ pub enum DownloadMime { } #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] -#[sea_orm(table_name = "downloads")] +#[sea_orm(table_name = "resources")] pub struct Model { pub created_at: DateTime, pub updated_at: DateTime, @@ -45,7 +45,7 @@ pub struct Model { pub display_name: String, pub subscription_id: i32, pub status: DownloadStatus, - pub mime: DownloadMime, + pub mime: ResourceMime, pub url: String, pub all_size: Option, pub curr_size: Option, diff --git a/crates/recorder/src/models/episodes.rs b/crates/recorder/src/models/episodes.rs index c05e6f8..43627cd 100644 --- a/crates/recorder/src/models/episodes.rs +++ b/crates/recorder/src/models/episodes.rs @@ -2,7 +2,7 @@ use sea_orm::{entity::prelude::*, ActiveValue}; pub use super::entities::episodes::*; use crate::{ - models::downloads, + models::resources, parsers::{mikan::MikanEpisodeMeta, raw::RawEpisodeMeta}, }; @@ -12,13 +12,13 @@ impl ActiveModelBehavior for ActiveModel {} impl ActiveModel { pub fn from_mikan_meta( bangumi_id: i32, - dl: downloads::Model, + resource: resources::Model, raw_meta: RawEpisodeMeta, mikan_meta: MikanEpisodeMeta, mikan_poster: Option, ) -> Self { Self { - origin_title: ActiveValue::Set(dl.origin_title), + origin_title: ActiveValue::Set(resource.origin_title), official_title: ActiveValue::Set(mikan_meta.official_title.clone()), display_name: ActiveValue::Set(mikan_meta.official_title), name_zh: ActiveValue::Set(raw_meta.name_zh), @@ -28,13 +28,13 @@ impl ActiveModel { s_name_jp: ActiveValue::Set(raw_meta.s_name_jp), s_name_en: ActiveValue::Set(raw_meta.s_name_en), bangumi_id: ActiveValue::Set(bangumi_id), - download_id: ActiveValue::Set(Some(dl.id)), + resource_id: ActiveValue::Set(Some(resource.id)), resolution: ActiveValue::Set(raw_meta.resolution), season: ActiveValue::Set(raw_meta.season), season_raw: ActiveValue::Set(raw_meta.season_raw), fansub: ActiveValue::Set(raw_meta.fansub), poster_link: ActiveValue::Set(mikan_poster), - home_page: ActiveValue::Set(dl.homepage), + home_page: ActiveValue::Set(resource.homepage), subtitle: ActiveValue::Set(raw_meta.sub), source: ActiveValue::Set(raw_meta.source), ..Default::default() diff --git a/crates/recorder/src/models/mod.rs b/crates/recorder/src/models/mod.rs index ef0183b..f026be3 100644 --- a/crates/recorder/src/models/mod.rs +++ b/crates/recorder/src/models/mod.rs @@ -1,10 +1,10 @@ pub mod bangumi; pub(crate) mod db_utils; pub mod downloaders; -pub mod downloads; pub mod entities; pub mod episodes; pub mod notifications; pub mod prelude; +pub mod resources; pub mod subscribers; pub mod subscriptions; diff --git a/crates/recorder/src/models/prelude.rs b/crates/recorder/src/models/prelude.rs index eb144f7..37066f8 100644 --- a/crates/recorder/src/models/prelude.rs +++ b/crates/recorder/src/models/prelude.rs @@ -1,8 +1,8 @@ pub use super::{ bangumi::{self, Entity as Bangumi}, downloaders::{self, DownloaderCategory, Entity as Downloader}, - downloads::{self, DownloadMime, DownloadStatus, Entity as Download}, episodes::{self, Entity as Episode}, + resources::{self, DownloadStatus, Entity as Download, ResourceMime}, subscribers::{self, Entity as Subscriber}, subscriptions::{self, Entity as Subscription, SubscriptionCategory}, }; diff --git a/crates/recorder/src/models/downloads.rs b/crates/recorder/src/models/resources.rs similarity index 95% rename from crates/recorder/src/models/downloads.rs rename to crates/recorder/src/models/resources.rs index d1a022d..91c22fb 100644 --- a/crates/recorder/src/models/downloads.rs +++ b/crates/recorder/src/models/resources.rs @@ -1,6 +1,6 @@ use sea_orm::{prelude::*, ActiveValue}; -pub use crate::models::entities::downloads::*; +pub use crate::models::entities::resources::*; use crate::parsers::mikan::MikanRssItem; #[async_trait::async_trait] diff --git a/crates/recorder/src/models/subscribers.rs b/crates/recorder/src/models/subscribers.rs index 84cd0ee..0130334 100644 --- a/crates/recorder/src/models/subscribers.rs +++ b/crates/recorder/src/models/subscribers.rs @@ -4,7 +4,8 @@ use serde::{Deserialize, Serialize}; pub use super::entities::subscribers::*; -pub const ROOT_SUBSCRIBER: &str = "konobangu"; +pub const ROOT_SUBSCRIBER_ID: i32 = 1; +pub const ROOT_SUBSCRIBER_NAME: &str = "konobangu"; #[derive(Debug, Deserialize, Serialize)] pub struct SubscriberIdParams { @@ -43,7 +44,7 @@ impl Model { } pub async fn find_root(db: &DatabaseConnection) -> ModelResult { - Self::find_by_pid(db, ROOT_SUBSCRIBER).await + Self::find_by_pid(db, ROOT_SUBSCRIBER_NAME).await } /// Asynchronously creates a user with a password and saves it to the @@ -56,8 +57,8 @@ impl Model { let txn = db.begin().await?; let user = ActiveModel { - display_name: ActiveValue::set(ROOT_SUBSCRIBER.to_string()), - pid: ActiveValue::set(ROOT_SUBSCRIBER.to_string()), + display_name: ActiveValue::set(ROOT_SUBSCRIBER_NAME.to_string()), + pid: ActiveValue::set(ROOT_SUBSCRIBER_NAME.to_string()), ..Default::default() } .insert(&txn) diff --git a/crates/recorder/src/models/subscriptions.rs b/crates/recorder/src/models/subscriptions.rs index 17eb55d..c17f21b 100644 --- a/crates/recorder/src/models/subscriptions.rs +++ b/crates/recorder/src/models/subscriptions.rs @@ -12,7 +12,7 @@ use tracing::{event, instrument, Level}; pub use super::entities::subscriptions::{self, *}; use crate::{ - models::{bangumi, db_utils::insert_many_with_returning_all, downloads, episodes}, + models::{bangumi, db_utils::insert_many_with_returning_all, episodes, resources}, parsers::{ mikan::{ parse_episode_meta_from_mikan_homepage, parse_mikan_rss_items_from_rss_link, @@ -33,7 +33,7 @@ pub struct SubscriptionCreateFromRssDto { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[serde(tag = "category")] +#[serde(tag = "category", rename_all = "snake_case")] pub enum SubscriptionCreateDto { Mikan(SubscriptionCreateFromRssDto), } @@ -106,7 +106,7 @@ impl Model { fields(subscriber_id = "self.subscriber_id", subscription_id = "self.id"), skip(self, db, ctx) )] - pub async fn pull_item(&self, db: &DatabaseConnection, ctx: &AppContext) -> eyre::Result<()> { + pub async fn pull_one(&self, db: &DatabaseConnection, ctx: &AppContext) -> eyre::Result<()> { let subscription = self; let subscription_id = subscription.id; match &subscription.category { @@ -122,20 +122,20 @@ impl Model { return Ok(()); } - let new_downloads = all_items + let new_resources = all_items .into_iter() .map(|rss_item| { - downloads::ActiveModel::from_mikan_rss_item(rss_item, subscription.id) + resources::ActiveModel::from_mikan_rss_item(rss_item, subscription.id) }) .collect_vec(); // insert and filter out duplicated items - let new_downloads: Vec = insert_many_with_returning_all( + let new_resources: Vec = insert_many_with_returning_all( db, - new_downloads, + new_resources, |stat: &mut InsertStatement| { stat.on_conflict( - OnConflict::column(downloads::Column::Url) + OnConflict::column(resources::Column::Url) .do_nothing() .to_owned(), ); @@ -144,7 +144,7 @@ impl Model { .await?; pub struct MikanEpMetaBundle { - pub download: downloads::Model, + pub resource: resources::Model, pub mikan: MikanEpisodeMeta, pub raw: RawEpisodeMeta, pub poster: Option, @@ -154,8 +154,8 @@ impl Model { HashMap::new(); let dal = ctx.get_dal_unwrap().await; { - for dl in new_downloads { - let mut mikan_meta = if let Some(homepage) = dl.homepage.as_deref() { + for r in new_resources { + let mut mikan_meta = if let Some(homepage) = r.homepage.as_deref() { match parse_episode_meta_from_mikan_homepage(&mikan_client, homepage) .await { @@ -208,14 +208,14 @@ impl Model { } else { None }; - let raw_meta = match parse_episode_meta_from_raw_name(&dl.origin_title) { + let raw_meta = match parse_episode_meta_from_raw_name(&r.origin_title) { Ok(raw_meta) => raw_meta, Err(e) => { let error: &dyn std::error::Error = e.as_ref(); event!( Level::ERROR, desc = "failed to parse episode meta from origin name", - origin_name = &dl.origin_title, + origin_name = &r.origin_title, error = error ); continue; @@ -227,7 +227,7 @@ impl Model { fansub: raw_meta.fansub.clone(), }; let meta = MikanEpMetaBundle { - download: dl, + resource: r, mikan: mikan_meta, raw: raw_meta, poster: mikan_poster_link, @@ -239,7 +239,7 @@ impl Model { for (_, eps) in ep_metas { let meta = eps.first().unwrap_or_else(|| { unreachable!( - "subscriptions pull items bangumi must have at least one episode meta" + "subscriptions pull one bangumi must have at least one episode meta" ) }); let last_ep = eps.iter().fold(0, |acc, ep| acc.max(ep.raw.episode_index)); @@ -271,7 +271,7 @@ impl Model { let eps = eps.into_iter().map(|ep| { episodes::ActiveModel::from_mikan_meta( bgm.id, - ep.download, + ep.resource, ep.raw, ep.mikan, ep.poster, diff --git a/crates/recorder/src/parsers/mikan/mikan_rss_parser.rs b/crates/recorder/src/parsers/mikan/mikan_rss_parser.rs index 134f8d3..600f4e7 100644 --- a/crates/recorder/src/parsers/mikan/mikan_rss_parser.rs +++ b/crates/recorder/src/parsers/mikan/mikan_rss_parser.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::{ downloaders::defs::BITTORRENT_MIME_TYPE, - models::prelude::DownloadMime, + models::prelude::ResourceMime, parsers::{errors::ParseError, mikan::mikan_client::MikanClient}, }; @@ -19,8 +19,8 @@ pub struct MikanRssItem { } impl MikanRssItem { - pub fn get_download_mime(&self) -> DownloadMime { - DownloadMime::BitTorrent + pub fn get_download_mime(&self) -> ResourceMime { + ResourceMime::BitTorrent } } diff --git a/crates/recorder/tests/models/mod.rs b/crates/recorder/tests/models/mod.rs index dcea459..8a10781 100644 --- a/crates/recorder/tests/models/mod.rs +++ b/crates/recorder/tests/models/mod.rs @@ -1 +1,2 @@ mod subscribers; +mod subscriptions; diff --git a/crates/recorder/tests/models/snapshots/can_find_by_pid@subscribers-2.snap b/crates/recorder/tests/models/snapshots/can_find_by_pid@subscribers-2.snap deleted file mode 100644 index ec6d638..0000000 --- a/crates/recorder/tests/models/snapshots/can_find_by_pid@subscribers-2.snap +++ /dev/null @@ -1,7 +0,0 @@ ---- -source: tests/models/subscribers.rs -expression: non_existing_subscriber_results ---- -Err( - EntityNotFound, -) diff --git a/crates/recorder/tests/models/snapshots/can_pull_subscription@subscriptions.snap b/crates/recorder/tests/models/snapshots/can_pull_subscription@subscriptions.snap new file mode 100644 index 0000000..9227bab --- /dev/null +++ b/crates/recorder/tests/models/snapshots/can_pull_subscription@subscriptions.snap @@ -0,0 +1,13 @@ +--- +source: tests/models/subscription.rs +expression: existing_subscription +--- +Ok( + Model { + created_at: 2023-11-12T12:34:56.789, + updated_at: 2023-11-12T12:34:56.789, + id: 1, + pid: "11111111-1111-1111-1111-111111111111", + display_name: "user1" + }, +) diff --git a/crates/recorder/tests/models/subscribers.rs b/crates/recorder/tests/models/subscribers.rs index 1800e08..5723f2a 100644 --- a/crates/recorder/tests/models/subscribers.rs +++ b/crates/recorder/tests/models/subscribers.rs @@ -7,7 +7,7 @@ macro_rules! configure_insta { ($($expr:expr),*) => { let mut settings = insta::Settings::clone_current(); settings.set_prepend_module_to_snapshot(false); - settings.set_snapshot_suffix("users"); + settings.set_snapshot_suffix("subscribers"); let _guard = settings.bind_to_scope(); }; } diff --git a/crates/recorder/tests/models/subscriptions.rs b/crates/recorder/tests/models/subscriptions.rs new file mode 100644 index 0000000..a6c38fe --- /dev/null +++ b/crates/recorder/tests/models/subscriptions.rs @@ -0,0 +1,42 @@ +use insta::assert_debug_snapshot; +use loco_rs::testing; +use recorder::{ + app::App, + models::{subscribers::ROOT_SUBSCRIBER_ID, subscriptions}, +}; +use serial_test::serial; + +macro_rules! configure_insta { + ($($expr:expr),*) => { + let mut settings = insta::Settings::clone_current(); + settings.set_prepend_module_to_snapshot(false); + settings.set_snapshot_suffix("subscriptions"); + let _guard = settings.bind_to_scope(); + }; +} + +#[tokio::test] +#[serial] +async fn can_pull_subscription() { + configure_insta!(); + + let boot = testing::boot_test::().await.unwrap(); + testing::seed::(&boot.app_context.db).await.unwrap(); + + let create_rss = serde_json::from_str( + r#"{ + "rss_link": "https://mikanani.me/RSS/Bangumi?bangumiId=3141&subgroupid=370", + "display_name": "Mikan Project - 葬送的芙莉莲", + "aggregate": false, + "enabled": true, + "category": "mikan" + }"#, + ) + .expect("should parse create rss dto from json"); + + let subscription = subscriptions::ActiveModel::from_create_dto(create_rss, ROOT_SUBSCRIBER_ID) + .await + .expect("should create subscription"); + + let subscription = subscriptions::ActiveModel::assert_debug_snapshot!(existing_subscriber); +}