mirror of
https://github.com/amtoaer/bili-sync.git
synced 2026-09-06 16:07:26 +08:00
chore: 移除旧版配置文件的迁移逻辑 (#501)
This commit is contained in:
@@ -7,7 +7,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
use crate::bilibili::{Credential, DanmakuOption, FilterOption};
|
use crate::bilibili::{Credential, DanmakuOption, FilterOption};
|
||||||
use crate::config::LegacyConfig;
|
|
||||||
use crate::config::default::{default_auth_token, default_bind_address, default_time_format};
|
use crate::config::default::{default_auth_token, default_bind_address, default_time_format};
|
||||||
use crate::config::item::{ConcurrentLimit, NFOTimeType, SkipOption};
|
use crate::config::item::{ConcurrentLimit, NFOTimeType, SkipOption};
|
||||||
use crate::utils::model::{load_db_config, save_db_config};
|
use crate::utils::model::{load_db_config, save_db_config};
|
||||||
@@ -109,25 +108,3 @@ impl Default for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LegacyConfig> for Config {
|
|
||||||
fn from(legacy: LegacyConfig) -> Self {
|
|
||||||
Self {
|
|
||||||
auth_token: legacy.auth_token,
|
|
||||||
bind_address: legacy.bind_address,
|
|
||||||
credential: legacy.credential,
|
|
||||||
filter_option: legacy.filter_option,
|
|
||||||
danmaku_option: legacy.danmaku_option,
|
|
||||||
skip_option: SkipOption::default(),
|
|
||||||
video_name: legacy.video_name,
|
|
||||||
page_name: legacy.page_name,
|
|
||||||
interval: legacy.interval,
|
|
||||||
upper_path: legacy.upper_path,
|
|
||||||
nfo_time_type: legacy.nfo_time_type,
|
|
||||||
concurrent_limit: legacy.concurrent_limit,
|
|
||||||
time_format: legacy.time_format,
|
|
||||||
cdn_sorting: legacy.cdn_sorting,
|
|
||||||
version: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::utils::filenamify::filenamify;
|
use crate::utils::filenamify::filenamify;
|
||||||
|
|
||||||
/// 稍后再看的配置
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
|
||||||
pub struct WatchLaterConfig {
|
|
||||||
pub enabled: bool,
|
|
||||||
pub path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// NFO 文件使用的时间类型
|
/// NFO 文件使用的时间类型
|
||||||
#[derive(Serialize, Deserialize, Default, Clone)]
|
#[derive(Serialize, Deserialize, Default, Clone)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use sea_orm::DatabaseConnection;
|
|
||||||
use serde::de::{Deserializer, MapAccess, Visitor};
|
|
||||||
use serde::ser::SerializeMap;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::bilibili::{CollectionItem, CollectionType, Credential, DanmakuOption, FilterOption};
|
|
||||||
use crate::config::Config;
|
|
||||||
use crate::config::default::{default_auth_token, default_bind_address, default_time_format};
|
|
||||||
use crate::config::item::{ConcurrentLimit, NFOTimeType, WatchLaterConfig};
|
|
||||||
use crate::utils::model::migrate_legacy_config;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct LegacyConfig {
|
|
||||||
#[serde(default = "default_auth_token")]
|
|
||||||
pub auth_token: String,
|
|
||||||
#[serde(default = "default_bind_address")]
|
|
||||||
pub bind_address: String,
|
|
||||||
pub credential: Credential,
|
|
||||||
pub filter_option: FilterOption,
|
|
||||||
#[serde(default)]
|
|
||||||
pub danmaku_option: DanmakuOption,
|
|
||||||
pub favorite_list: HashMap<String, PathBuf>,
|
|
||||||
#[serde(
|
|
||||||
default,
|
|
||||||
serialize_with = "serialize_collection_list",
|
|
||||||
deserialize_with = "deserialize_collection_list"
|
|
||||||
)]
|
|
||||||
pub collection_list: HashMap<CollectionItem, PathBuf>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub submission_list: HashMap<String, PathBuf>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub watch_later: WatchLaterConfig,
|
|
||||||
pub video_name: String,
|
|
||||||
pub page_name: String,
|
|
||||||
pub interval: u64,
|
|
||||||
pub upper_path: PathBuf,
|
|
||||||
#[serde(default)]
|
|
||||||
pub nfo_time_type: NFOTimeType,
|
|
||||||
#[serde(default)]
|
|
||||||
pub concurrent_limit: ConcurrentLimit,
|
|
||||||
#[serde(default = "default_time_format")]
|
|
||||||
pub time_format: String,
|
|
||||||
#[serde(default)]
|
|
||||||
pub cdn_sorting: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LegacyConfig {
|
|
||||||
async fn load_from_file(path: &Path) -> Result<Self> {
|
|
||||||
let legacy_config_str = tokio::fs::read_to_string(path).await?;
|
|
||||||
Ok(toml::from_str(&legacy_config_str)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn migrate_from_file(path: &Path, connection: &DatabaseConnection) -> Result<Config> {
|
|
||||||
let legacy_config = Self::load_from_file(path).await?;
|
|
||||||
migrate_legacy_config(&legacy_config, connection).await?;
|
|
||||||
Ok(legacy_config.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
后面是用于自定义 Collection 的序列化、反序列化的样板代码
|
|
||||||
*/
|
|
||||||
pub(super) fn serialize_collection_list<S>(
|
|
||||||
collection_list: &HashMap<CollectionItem, PathBuf>,
|
|
||||||
serializer: S,
|
|
||||||
) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
let mut map = serializer.serialize_map(Some(collection_list.len()))?;
|
|
||||||
for (k, v) in collection_list {
|
|
||||||
let prefix = match k.collection_type {
|
|
||||||
CollectionType::Series => "series",
|
|
||||||
CollectionType::Season => "season",
|
|
||||||
};
|
|
||||||
map.serialize_entry(&[prefix, &k.mid, &k.sid].join(":"), v)?;
|
|
||||||
}
|
|
||||||
map.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn deserialize_collection_list<'de, D>(deserializer: D) -> Result<HashMap<CollectionItem, PathBuf>, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct CollectionListVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for CollectionListVisitor {
|
|
||||||
type Value = HashMap<CollectionItem, PathBuf>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
formatter.write_str("a map of collection list")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
|
||||||
where
|
|
||||||
A: MapAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut collection_list = HashMap::new();
|
|
||||||
while let Some((key, value)) = map.next_entry::<String, PathBuf>()? {
|
|
||||||
let collection_item = match key.split(':').collect::<Vec<&str>>().as_slice() {
|
|
||||||
[prefix, mid, sid] => {
|
|
||||||
let collection_type = match *prefix {
|
|
||||||
"series" => CollectionType::Series,
|
|
||||||
"season" => CollectionType::Season,
|
|
||||||
_ => {
|
|
||||||
return Err(serde::de::Error::custom(
|
|
||||||
"invalid collection type, should be series or season",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
CollectionItem {
|
|
||||||
mid: mid.to_string(),
|
|
||||||
sid: sid.to_string(),
|
|
||||||
collection_type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(serde::de::Error::custom(
|
|
||||||
"invalid collection key, should be series:mid:sid or season:mid:sid",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
collection_list.insert(collection_item, value);
|
|
||||||
}
|
|
||||||
Ok(collection_list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_map(CollectionListVisitor)
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ mod current;
|
|||||||
mod default;
|
mod default;
|
||||||
mod handlebar;
|
mod handlebar;
|
||||||
mod item;
|
mod item;
|
||||||
mod legacy;
|
|
||||||
mod versioned_cache;
|
mod versioned_cache;
|
||||||
mod versioned_config;
|
mod versioned_config;
|
||||||
|
|
||||||
@@ -11,6 +10,5 @@ pub use crate::config::args::{ARGS, version};
|
|||||||
pub use crate::config::current::{CONFIG_DIR, Config};
|
pub use crate::config::current::{CONFIG_DIR, Config};
|
||||||
pub use crate::config::handlebar::TEMPLATE;
|
pub use crate::config::handlebar::TEMPLATE;
|
||||||
pub use crate::config::item::{NFOTimeType, PathSafeTemplate, RateLimit, SkipOption};
|
pub use crate::config::item::{NFOTimeType, PathSafeTemplate, RateLimit, SkipOption};
|
||||||
pub use crate::config::legacy::LegacyConfig;
|
|
||||||
pub use crate::config::versioned_cache::VersionedCache;
|
pub use crate::config::versioned_cache::VersionedCache;
|
||||||
pub use crate::config::versioned_config::VersionedConfig;
|
pub use crate::config::versioned_config::VersionedConfig;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use sea_orm::DatabaseConnection;
|
|||||||
use tokio::sync::OnceCell;
|
use tokio::sync::OnceCell;
|
||||||
|
|
||||||
use crate::bilibili::Credential;
|
use crate::bilibili::Credential;
|
||||||
use crate::config::{CONFIG_DIR, Config, LegacyConfig};
|
use crate::config::{CONFIG_DIR, Config};
|
||||||
|
|
||||||
pub static VERSIONED_CONFIG: OnceCell<VersionedConfig> = OnceCell::const_new();
|
pub static VERSIONED_CONFIG: OnceCell<VersionedConfig> = OnceCell::const_new();
|
||||||
|
|
||||||
@@ -22,23 +22,19 @@ impl VersionedConfig {
|
|||||||
Some(Ok(config)) => config,
|
Some(Ok(config)) => config,
|
||||||
Some(Err(e)) => bail!("解析数据库配置失败: {}", e),
|
Some(Err(e)) => bail!("解析数据库配置失败: {}", e),
|
||||||
None => {
|
None => {
|
||||||
let config = match LegacyConfig::migrate_from_file(&CONFIG_DIR.join("config.toml"), connection).await {
|
if CONFIG_DIR.join("config.toml").exists() {
|
||||||
Ok(config) => config,
|
// 数据库中没有配置,但旧版配置文件存在,说明是从 2.6.0 之前的版本直接升级的
|
||||||
Err(e) => {
|
bail!(
|
||||||
if e.downcast_ref::<std::io::Error>()
|
"当前版本已移除配置文件的迁移逻辑,不再支持从配置文件加载配置。\n\
|
||||||
.is_none_or(|e| e.kind() != std::io::ErrorKind::NotFound)
|
如果你正在运行 2.6.0 之前的版本,请先升级至 2.6.x 或 2.7.x,\n\
|
||||||
{
|
启动时会自动将配置文件迁移至数据库,然后再升级至最新版本。"
|
||||||
bail!("未成功读取并迁移旧版本配置:{:#}", e);
|
);
|
||||||
} else {
|
}
|
||||||
let config = Config::default();
|
let config = Config::default();
|
||||||
warn!(
|
warn!(
|
||||||
"生成 auth_token:{},可使用该 token 登录 web UI,该信息仅在首次运行时打印",
|
"生成 auth_token:{},可使用该 token 登录 web UI,该信息仅在首次运行时打印",
|
||||||
config.auth_token
|
config.auth_token
|
||||||
);
|
);
|
||||||
config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
config.save_to_database(connection).await?;
|
config.save_to_database(connection).await?;
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
use anyhow::{Context, Result, anyhow};
|
use anyhow::{Context, Result, anyhow};
|
||||||
use bili_sync_entity::*;
|
use bili_sync_entity::*;
|
||||||
use sea_orm::ActiveValue::Set;
|
use sea_orm::ActiveValue::Set;
|
||||||
|
use sea_orm::DatabaseTransaction;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use sea_orm::sea_query::{OnConflict, SimpleExpr};
|
use sea_orm::sea_query::{OnConflict, SimpleExpr};
|
||||||
use sea_orm::{DatabaseTransaction, TransactionTrait};
|
|
||||||
|
|
||||||
use crate::adapter::{VideoSource, VideoSourceEnum};
|
use crate::adapter::{VideoSource, VideoSourceEnum};
|
||||||
use crate::bilibili::VideoInfo;
|
use crate::bilibili::VideoInfo;
|
||||||
use crate::config::{Config, LegacyConfig};
|
use crate::config::Config;
|
||||||
use crate::utils::status::STATUS_COMPLETED;
|
use crate::utils::status::STATUS_COMPLETED;
|
||||||
|
|
||||||
/// 筛选未填充的视频
|
/// 筛选未填充的视频
|
||||||
@@ -166,69 +166,3 @@ pub async fn save_db_config(config: &Config, connection: &DatabaseConnection) ->
|
|||||||
.context("Failed to save config to database")?;
|
.context("Failed to save config to database")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 迁移旧版本配置(即将所有相关联的内容设置为 enabled)
|
|
||||||
pub async fn migrate_legacy_config(config: &LegacyConfig, connection: &DatabaseConnection) -> Result<()> {
|
|
||||||
let transaction = connection.begin().await.context("Failed to begin transaction")?;
|
|
||||||
tokio::try_join!(
|
|
||||||
migrate_favorite(config, &transaction),
|
|
||||||
migrate_watch_later(config, &transaction),
|
|
||||||
migrate_submission(config, &transaction),
|
|
||||||
migrate_collection(config, &transaction)
|
|
||||||
)?;
|
|
||||||
transaction.commit().await.context("Failed to commit transaction")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn migrate_favorite(config: &LegacyConfig, connection: &DatabaseTransaction) -> Result<()> {
|
|
||||||
favorite::Entity::update_many()
|
|
||||||
.filter(favorite::Column::FId.is_in(config.favorite_list.keys().collect::<Vec<_>>()))
|
|
||||||
.col_expr(favorite::Column::Enabled, Expr::value(true))
|
|
||||||
.exec(connection)
|
|
||||||
.await
|
|
||||||
.context("Failed to migrate favorite config")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn migrate_watch_later(config: &LegacyConfig, connection: &DatabaseTransaction) -> Result<()> {
|
|
||||||
if config.watch_later.enabled {
|
|
||||||
watch_later::Entity::update_many()
|
|
||||||
.col_expr(watch_later::Column::Enabled, Expr::value(true))
|
|
||||||
.exec(connection)
|
|
||||||
.await
|
|
||||||
.context("Failed to migrate watch later config")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn migrate_submission(config: &LegacyConfig, connection: &DatabaseTransaction) -> Result<()> {
|
|
||||||
submission::Entity::update_many()
|
|
||||||
.filter(submission::Column::UpperId.is_in(config.submission_list.keys().collect::<Vec<_>>()))
|
|
||||||
.col_expr(submission::Column::Enabled, Expr::value(true))
|
|
||||||
.exec(connection)
|
|
||||||
.await
|
|
||||||
.context("Failed to migrate submission config")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn migrate_collection(config: &LegacyConfig, connection: &DatabaseTransaction) -> Result<()> {
|
|
||||||
let tuples: Vec<(i64, i64, i32)> = config
|
|
||||||
.collection_list
|
|
||||||
.keys()
|
|
||||||
.filter_map(|key| Some((key.sid.parse().ok()?, key.mid.parse().ok()?, key.collection_type.into())))
|
|
||||||
.collect();
|
|
||||||
collection::Entity::update_many()
|
|
||||||
.filter(
|
|
||||||
Expr::tuple([
|
|
||||||
Expr::column(collection::Column::SId),
|
|
||||||
Expr::column(collection::Column::MId),
|
|
||||||
Expr::column(collection::Column::Type),
|
|
||||||
])
|
|
||||||
.in_tuples(tuples),
|
|
||||||
)
|
|
||||||
.col_expr(collection::Column::Enabled, Expr::value(true))
|
|
||||||
.exec(connection)
|
|
||||||
.await
|
|
||||||
.context("Failed to migrate collection config")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user