feat: test?
This commit is contained in:
parent
035d4e20dd
commit
50243db5dc
@ -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") }}'
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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<bangumi::Model> {
|
||||
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?;
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
|
@ -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)
|
@ -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<Box<dyn MigrationTrait>> {
|
||||
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),
|
||||
]
|
||||
}
|
||||
|
@ -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<Id>
|
||||
where
|
||||
@ -81,3 +83,25 @@ where
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) async fn insert_many_with_returning_id<D, V, F, I>(
|
||||
db: &D,
|
||||
insert_values: impl IntoIterator<Item = V>,
|
||||
extra_config: F,
|
||||
) -> eyre::Result<Vec<OnlyIdsModel<I>>>
|
||||
where
|
||||
D: ConnectionTrait,
|
||||
V: ActiveModelTrait,
|
||||
F: FnOnce(&mut InsertStatement),
|
||||
I: TryGetable,
|
||||
{
|
||||
let result: Vec<OnlyIdsModel<I>> = insert_many_with_returning_columns(
|
||||
db,
|
||||
insert_values,
|
||||
[Expr::col(ColumnRef::Column(GeneralIds::Id.into_iden()))],
|
||||
extra_config,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ pub struct Model {
|
||||
pub s_name_jp: Option<String>,
|
||||
pub s_name_en: Option<String>,
|
||||
pub bangumi_id: i32,
|
||||
pub download_id: Option<i32>,
|
||||
pub resource_id: Option<i32>,
|
||||
pub save_path: Option<String>,
|
||||
pub resolution: Option<String>,
|
||||
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<super::bangumi::Entity> for Entity {
|
||||
@ -54,8 +54,8 @@ impl Related<super::bangumi::Entity> for Entity {
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::downloads::Entity> for Entity {
|
||||
impl Related<super::resources::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Downloads.def()
|
||||
Relation::Resources.def()
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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<u64>,
|
||||
pub curr_size: Option<u64>,
|
@ -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<String>,
|
||||
) -> 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()
|
||||
|
@ -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;
|
||||
|
@ -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},
|
||||
};
|
||||
|
@ -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]
|
@ -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> {
|
||||
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)
|
||||
|
@ -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<downloads::Model> = insert_many_with_returning_all(
|
||||
let new_resources: Vec<resources::Model> = 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<String>,
|
||||
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1 +1,2 @@
|
||||
mod subscribers;
|
||||
mod subscriptions;
|
||||
|
@ -1,7 +0,0 @@
|
||||
---
|
||||
source: tests/models/subscribers.rs
|
||||
expression: non_existing_subscriber_results
|
||||
---
|
||||
Err(
|
||||
EntityNotFound,
|
||||
)
|
@ -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"
|
||||
},
|
||||
)
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
42
crates/recorder/tests/models/subscriptions.rs
Normal file
42
crates/recorder/tests/models/subscriptions.rs
Normal file
@ -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::<App>().await.unwrap();
|
||||
testing::seed::<App>(&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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user