feature: rewrite season subscription extractor
This commit is contained in:
143
apps/recorder/src/models/credential_3rd.rs
Normal file
143
apps/recorder/src/models/credential_3rd.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use sea_orm::{ActiveValue, prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
app::AppContextTrait,
|
||||
crypto::UserPassCredential,
|
||||
errors::{RecorderError, RecorderResult},
|
||||
};
|
||||
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay, Serialize, Deserialize,
|
||||
)]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
enum_name = "credential_3rd_type"
|
||||
)]
|
||||
pub enum Credential3rdType {
|
||||
#[sea_orm(string_value = "mikan")]
|
||||
Mikan,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "credential_3rd")]
|
||||
pub struct Model {
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub created_at: DateTimeUtc,
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub updated_at: DateTimeUtc,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub subscriber_id: i32,
|
||||
pub credential_type: Credential3rdType,
|
||||
pub cookies: Option<String>,
|
||||
pub username: Option<String>,
|
||||
pub password: Option<String>,
|
||||
pub user_agent: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::subscribers::Entity",
|
||||
from = "Column::SubscriberId",
|
||||
to = "super::subscribers::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Subscriber,
|
||||
#[sea_orm(has_many = "super::subscriptions::Entity")]
|
||||
Subscription,
|
||||
}
|
||||
|
||||
impl Related<super::subscribers::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Subscriber.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::subscriptions::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Subscription.def()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
impl ActiveModel {
|
||||
pub async fn try_encrypt(mut self, ctx: Arc<dyn AppContextTrait>) -> RecorderResult<Self> {
|
||||
let crypto = ctx.crypto();
|
||||
|
||||
if let ActiveValue::Set(Some(username)) = self.username {
|
||||
let username_enc = crypto.encrypt_credentials(&username)?;
|
||||
self.username = ActiveValue::Set(Some(username_enc));
|
||||
}
|
||||
|
||||
if let ActiveValue::Set(Some(password)) = self.password {
|
||||
let password_enc = crypto.encrypt_credentials(&password)?;
|
||||
self.password = ActiveValue::Set(Some(password_enc));
|
||||
}
|
||||
|
||||
if let ActiveValue::Set(Some(cookies)) = self.cookies {
|
||||
let cookies_enc = crypto.encrypt_credentials(&cookies)?;
|
||||
self.cookies = ActiveValue::Set(Some(cookies_enc));
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub async fn find_by_id(
|
||||
ctx: Arc<dyn AppContextTrait>,
|
||||
id: i32,
|
||||
) -> RecorderResult<Option<Self>> {
|
||||
let db = ctx.db();
|
||||
let credential = Entity::find_by_id(id).one(db).await?;
|
||||
|
||||
Ok(credential)
|
||||
}
|
||||
|
||||
pub fn try_into_userpass_credential(
|
||||
self,
|
||||
ctx: Arc<dyn AppContextTrait>,
|
||||
) -> RecorderResult<UserPassCredential> {
|
||||
let crypto = ctx.crypto();
|
||||
let username_enc = self
|
||||
.username
|
||||
.ok_or_else(|| RecorderError::Credential3rdError {
|
||||
message: "UserPassCredential username is required".to_string(),
|
||||
source: None.into(),
|
||||
})?;
|
||||
|
||||
let username: String = crypto.decrypt_credentials(&username_enc)?;
|
||||
|
||||
let password_enc = self
|
||||
.password
|
||||
.ok_or_else(|| RecorderError::Credential3rdError {
|
||||
message: "UserPassCredential password is required".to_string(),
|
||||
source: None.into(),
|
||||
})?;
|
||||
|
||||
let password: String = crypto.decrypt_credentials(&password_enc)?;
|
||||
|
||||
let cookies: Option<String> = if let Some(cookies_enc) = self.cookies {
|
||||
let cookies = crypto.decrypt_credentials(&cookies_enc)?;
|
||||
Some(cookies)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(UserPassCredential {
|
||||
username,
|
||||
password,
|
||||
cookies,
|
||||
user_agent: self.user_agent,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
app::AppContextTrait,
|
||||
errors::RecorderResult,
|
||||
extract::{
|
||||
mikan::{MikanEpisodeMeta, build_mikan_episode_homepage},
|
||||
mikan::{MikanEpisodeMeta, build_mikan_episode_homepage_url},
|
||||
rawname::parse_episode_meta_from_raw_name,
|
||||
},
|
||||
};
|
||||
@@ -200,8 +200,10 @@ impl ActiveModel {
|
||||
})
|
||||
.ok()
|
||||
.unwrap_or_default();
|
||||
let homepage =
|
||||
build_mikan_episode_homepage(ctx.mikan().base_url().clone(), &item.mikan_episode_id);
|
||||
let homepage = build_mikan_episode_homepage_url(
|
||||
ctx.mikan().base_url().clone(),
|
||||
&item.mikan_episode_id,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
mikan_episode_id: ActiveValue::Set(Some(item.mikan_episode_id)),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod auth;
|
||||
pub mod bangumi;
|
||||
pub mod credential_3rd;
|
||||
pub mod downloaders;
|
||||
pub mod downloads;
|
||||
pub mod episodes;
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
errors::RecorderResult,
|
||||
extract::{
|
||||
mikan::{
|
||||
build_mikan_bangumi_homepage, build_mikan_bangumi_rss_link,
|
||||
build_mikan_bangumi_homepage_url, build_mikan_bangumi_rss_url,
|
||||
extract_mikan_bangumi_meta_from_bangumi_homepage,
|
||||
extract_mikan_episode_meta_from_episode_homepage,
|
||||
extract_mikan_rss_channel_from_rss_link,
|
||||
@@ -54,6 +54,7 @@ pub struct Model {
|
||||
pub category: SubscriptionCategory,
|
||||
pub source_url: String,
|
||||
pub enabled: bool,
|
||||
pub credential_id: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
@@ -74,6 +75,14 @@ pub enum Relation {
|
||||
SubscriptionEpisode,
|
||||
#[sea_orm(has_many = "super::subscription_bangumi::Entity")]
|
||||
SubscriptionBangumi,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::credential_3rd::Entity",
|
||||
from = "Column::CredentialId",
|
||||
to = "super::credential_3rd::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "SetNull"
|
||||
)]
|
||||
Credential3rd,
|
||||
}
|
||||
|
||||
impl Related<super::subscribers::Entity> for Entity {
|
||||
@@ -122,6 +131,12 @@ impl Related<super::episodes::Entity> for Entity {
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::credential_3rd::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Credential3rd.def()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
|
||||
pub enum RelatedEntity {
|
||||
#[sea_orm(entity = "super::subscribers::Entity")]
|
||||
@@ -134,6 +149,8 @@ pub enum RelatedEntity {
|
||||
SubscriptionEpisode,
|
||||
#[sea_orm(entity = "super::subscription_bangumi::Entity")]
|
||||
SubscriptionBangumi,
|
||||
#[sea_orm(entity = "super::credential_3rd::Entity")]
|
||||
Credential3rd,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
@@ -270,12 +287,12 @@ impl Model {
|
||||
for ((mikan_bangumi_id, mikan_fansub_id), new_ep_metas) in new_mikan_bangumi_groups
|
||||
{
|
||||
let mikan_base_url = ctx.mikan().base_url();
|
||||
let bgm_homepage = build_mikan_bangumi_homepage(
|
||||
let bgm_homepage = build_mikan_bangumi_homepage_url(
|
||||
mikan_base_url.clone(),
|
||||
&mikan_bangumi_id,
|
||||
Some(&mikan_fansub_id),
|
||||
);
|
||||
let bgm_rss_link = build_mikan_bangumi_rss_link(
|
||||
let bgm_rss_link = build_mikan_bangumi_rss_url(
|
||||
mikan_base_url.clone(),
|
||||
&mikan_bangumi_id,
|
||||
Some(&mikan_fansub_id),
|
||||
|
||||
Reference in New Issue
Block a user