refactor: refactor subscription
This commit is contained in:
@@ -1,10 +1,19 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_graphql::SimpleObject;
|
||||
use async_trait::async_trait;
|
||||
use sea_orm::{ActiveValue, FromJsonQueryResult, entity::prelude::*, sea_query::OnConflict};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::subscription_bangumi;
|
||||
use crate::{app::AppContextTrait, errors::RecorderResult};
|
||||
use crate::{
|
||||
app::AppContextTrait,
|
||||
errors::RecorderResult,
|
||||
extract::{
|
||||
mikan::{MikanBangumiMeta, build_mikan_bangumi_subscription_rss_url},
|
||||
rawname::parse_episode_meta_from_raw_name,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult, SimpleObject,
|
||||
@@ -174,5 +183,38 @@ impl Model {
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModel {
|
||||
pub fn from_mikan_bangumi_meta(
|
||||
ctx: Arc<dyn AppContextTrait>,
|
||||
meta: MikanBangumiMeta,
|
||||
subscriber_id: i32,
|
||||
) -> RecorderResult<Self> {
|
||||
let mikan_base_url = ctx.mikan().base_url();
|
||||
|
||||
let raw_meta = parse_episode_meta_from_raw_name(&meta.bangumi_title)?;
|
||||
|
||||
let rss_url = build_mikan_bangumi_subscription_rss_url(
|
||||
mikan_base_url.clone(),
|
||||
&meta.mikan_bangumi_id,
|
||||
Some(&meta.mikan_fansub_id),
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
mikan_bangumi_id: ActiveValue::Set(Some(meta.mikan_bangumi_id)),
|
||||
mikan_fansub_id: ActiveValue::Set(Some(meta.mikan_fansub_id)),
|
||||
subscriber_id: ActiveValue::Set(subscriber_id),
|
||||
display_name: ActiveValue::Set(meta.bangumi_title.clone()),
|
||||
raw_name: ActiveValue::Set(meta.bangumi_title),
|
||||
season: ActiveValue::Set(raw_meta.season),
|
||||
season_raw: ActiveValue::Set(raw_meta.season_raw),
|
||||
fansub: ActiveValue::Set(Some(meta.fansub)),
|
||||
poster_link: ActiveValue::Set(meta.origin_poster_src.map(|url| url.to_string())),
|
||||
homepage: ActiveValue::Set(Some(meta.homepage.to_string())),
|
||||
rss_link: ActiveValue::Set(Some(rss_url.to_string())),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use async_trait::async_trait;
|
||||
use sea_orm::{
|
||||
prelude::Expr,
|
||||
sea_query::{Alias, IntoColumnRef, IntoTableRef, Query, SelectStatement},
|
||||
ActiveModelTrait, ColumnTrait, ConnectionTrait, DbErr, EntityTrait, Insert, IntoActiveModel,
|
||||
Iterable, QueryResult, QueryTrait, SelectModel, SelectorRaw, Value,
|
||||
prelude::Expr,
|
||||
sea_query::{Alias, IntoColumnRef, IntoTableRef, Query, SelectStatement},
|
||||
};
|
||||
|
||||
pub fn filter_values_in<
|
||||
@@ -17,12 +17,9 @@ pub fn filter_values_in<
|
||||
values: I,
|
||||
) -> SelectStatement {
|
||||
Query::select()
|
||||
.expr(Expr::col((Alias::new("t"), Alias::new("column1"))))
|
||||
.from_values(values, Alias::new("t"))
|
||||
.left_join(
|
||||
tbl_ref,
|
||||
Expr::col((Alias::new("t"), Alias::new("column1"))).equals(col_ref),
|
||||
)
|
||||
.expr(Expr::col(("t", "column1")))
|
||||
.from_values(values, "t")
|
||||
.left_join(tbl_ref, Expr::col(("t", "column1")).equals(col_ref))
|
||||
.and_where(Expr::col(col_ref).is_not_null())
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
@@ -4,7 +4,11 @@ use sea_orm::{ActiveValue, FromJsonQueryResult, JsonValue, TryIntoModel, prelude
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use crate::task::{SubscriberTaskType, SubscriberTaskTypeEnum};
|
||||
use crate::{app::AppContextTrait, errors::RecorderResult, task::SubscriberTask};
|
||||
use crate::{
|
||||
app::AppContextTrait,
|
||||
errors::RecorderResult,
|
||||
task::{SubscriberTask, SubscriberTaskPayload},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromJsonQueryResult, PartialEq, Eq)]
|
||||
pub struct SubscriberTaskErrorSnapshot {
|
||||
@@ -125,9 +129,11 @@ impl Model {
|
||||
pub async fn add_subscriber_task(
|
||||
ctx: Arc<dyn AppContextTrait>,
|
||||
subscriber_id: i32,
|
||||
task_type: SubscriberTaskType,
|
||||
request: JsonValue,
|
||||
) -> RecorderResult<Model> {
|
||||
payload: SubscriberTaskPayload,
|
||||
) -> RecorderResult<SubscriberTask> {
|
||||
let task_type = payload.task_type();
|
||||
let request: JsonValue = payload.clone().try_into()?;
|
||||
|
||||
let am = ActiveModel {
|
||||
subscriber_id: ActiveValue::Set(subscriber_id),
|
||||
task_type: ActiveValue::Set(task_type.clone()),
|
||||
@@ -137,17 +143,18 @@ impl Model {
|
||||
|
||||
let db = ctx.db();
|
||||
|
||||
let model = am.insert(db).await?.try_into_model()?;
|
||||
let task_id = Entity::insert(am).exec(db).await?.last_insert_id;
|
||||
|
||||
let task_value: SubscriberTask = serde_json::from_value(serde_json::json!({
|
||||
"id": model.id,
|
||||
"subscriber_id": model.subscriber_id.clone(),
|
||||
"task_type": model.task_type.clone(),
|
||||
"request": model.request.clone(),
|
||||
}))?;
|
||||
let subscriber_task = SubscriberTask {
|
||||
id: task_id,
|
||||
subscriber_id,
|
||||
payload,
|
||||
};
|
||||
|
||||
ctx.task().add_subscriber_task(task_value).await?;
|
||||
ctx.task()
|
||||
.add_subscriber_task(subscriber_task.clone())
|
||||
.await?;
|
||||
|
||||
Ok(model)
|
||||
Ok(subscriber_task)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ use serde::{Deserialize, Serialize};
|
||||
use super::{bangumi, episodes, query::filter_values_in};
|
||||
use crate::{
|
||||
app::AppContextTrait,
|
||||
errors::RecorderResult,
|
||||
errors::{RecorderError, RecorderResult},
|
||||
extract::{
|
||||
mikan::{
|
||||
MikanBangumiPosterMeta, build_mikan_bangumi_homepage_url, build_mikan_bangumi_rss_url,
|
||||
extract_mikan_rss_channel_from_rss_link,
|
||||
MikanBangumiPosterMeta, MikanBangumiSubscription, MikanSeasonSubscription,
|
||||
MikanSubscriberSubscription, build_mikan_bangumi_homepage_url,
|
||||
build_mikan_bangumi_subscription_rss_url,
|
||||
scrape_mikan_bangumi_meta_from_bangumi_homepage_url,
|
||||
scrape_mikan_episode_meta_from_episode_homepage_url,
|
||||
scrape_mikan_poster_meta_from_image_url,
|
||||
@@ -32,12 +33,55 @@ use crate::{
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SubscriptionCategory {
|
||||
#[sea_orm(string_value = "mikan")]
|
||||
Mikan,
|
||||
#[sea_orm(string_value = "mikan_subscriber")]
|
||||
MikanSubscriber,
|
||||
#[sea_orm(string_value = "mikan_season")]
|
||||
MikanSeason,
|
||||
#[sea_orm(string_value = "mikan_bangumi")]
|
||||
MikanBangumi,
|
||||
#[sea_orm(string_value = "manual")]
|
||||
Manual,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "category")]
|
||||
pub enum SubscriptionPayload {
|
||||
#[serde(rename = "mikan_subscriber")]
|
||||
MikanSubscriber(MikanSubscriberSubscription),
|
||||
#[serde(rename = "mikan_season")]
|
||||
MikanSeason(MikanSeasonSubscription),
|
||||
#[serde(rename = "mikan_bangumi")]
|
||||
MikanBangumi(MikanBangumiSubscription),
|
||||
#[serde(rename = "manual")]
|
||||
Manual,
|
||||
}
|
||||
|
||||
impl SubscriptionPayload {
|
||||
pub fn category(&self) -> SubscriptionCategory {
|
||||
match self {
|
||||
Self::MikanSubscriber(_) => SubscriptionCategory::MikanSubscriber,
|
||||
Self::MikanSeason(_) => SubscriptionCategory::MikanSeason,
|
||||
Self::MikanBangumi(_) => SubscriptionCategory::MikanBangumi,
|
||||
Self::Manual => SubscriptionCategory::Manual,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_from_model(model: &Model) -> RecorderResult<Self> {
|
||||
Ok(match model.category {
|
||||
SubscriptionCategory::MikanSubscriber => {
|
||||
Self::MikanSubscriber(MikanSubscriberSubscription::try_from_model(model)?)
|
||||
}
|
||||
SubscriptionCategory::MikanSeason => {
|
||||
Self::MikanSeason(MikanSeasonSubscription::try_from_model(model)?)
|
||||
}
|
||||
SubscriptionCategory::MikanBangumi => {
|
||||
Self::MikanBangumi(MikanBangumiSubscription::try_from_model(model)?)
|
||||
}
|
||||
SubscriptionCategory::Manual => Self::Manual,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "subscriptions")]
|
||||
pub struct Model {
|
||||
@@ -149,57 +193,15 @@ pub enum RelatedEntity {
|
||||
SubscriptionBangumi,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct SubscriptionCreateFromRssDto {
|
||||
pub rss_link: String,
|
||||
pub display_name: String,
|
||||
pub enabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(tag = "category")]
|
||||
pub enum SubscriptionCreateDto {
|
||||
Mikan(SubscriptionCreateFromRssDto),
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
impl ActiveModel {
|
||||
pub fn from_create_dto(create_dto: SubscriptionCreateDto, subscriber_id: i32) -> Self {
|
||||
match create_dto {
|
||||
SubscriptionCreateDto::Mikan(create_dto) => {
|
||||
Self::from_rss_create_dto(SubscriptionCategory::Mikan, create_dto, subscriber_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_rss_create_dto(
|
||||
category: SubscriptionCategory,
|
||||
create_dto: SubscriptionCreateFromRssDto,
|
||||
subscriber_id: i32,
|
||||
) -> Self {
|
||||
Self {
|
||||
display_name: ActiveValue::Set(create_dto.display_name),
|
||||
enabled: ActiveValue::Set(create_dto.enabled.unwrap_or(false)),
|
||||
subscriber_id: ActiveValue::Set(subscriber_id),
|
||||
category: ActiveValue::Set(category),
|
||||
source_url: ActiveValue::Set(create_dto.rss_link),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ActiveModel {}
|
||||
|
||||
impl Model {
|
||||
pub async fn add_subscription(
|
||||
ctx: &dyn AppContextTrait,
|
||||
create_dto: SubscriptionCreateDto,
|
||||
subscriber_id: i32,
|
||||
) -> RecorderResult<Self> {
|
||||
pub async fn find_by_id(ctx: &dyn AppContextTrait, id: i32) -> RecorderResult<Option<Self>> {
|
||||
let db = ctx.db();
|
||||
let subscription = ActiveModel::from_create_dto(create_dto, subscriber_id);
|
||||
|
||||
Ok(subscription.insert(db).await?)
|
||||
Ok(Entity::find_by_id(id).one(db).await?)
|
||||
}
|
||||
|
||||
pub async fn toggle_with_ids(
|
||||
@@ -229,8 +231,8 @@ impl Model {
|
||||
}
|
||||
|
||||
pub async fn pull_subscription(&self, ctx: &dyn AppContextTrait) -> RecorderResult<()> {
|
||||
match &self.category {
|
||||
SubscriptionCategory::Mikan => {
|
||||
match payload {
|
||||
SubscriptionPayload::MikanSubscriber(payload) => {
|
||||
let mikan_client = ctx.mikan();
|
||||
let channel =
|
||||
extract_mikan_rss_channel_from_rss_link(mikan_client, &self.source_url).await?;
|
||||
@@ -288,7 +290,7 @@ impl Model {
|
||||
&mikan_bangumi_id,
|
||||
Some(&mikan_fansub_id),
|
||||
);
|
||||
let bgm_rss_link = build_mikan_bangumi_rss_url(
|
||||
let bgm_rss_link = build_mikan_bangumi_subscription_rss_url(
|
||||
mikan_base_url.clone(),
|
||||
&mikan_bangumi_id,
|
||||
Some(&mikan_fansub_id),
|
||||
|
||||
Reference in New Issue
Block a user