Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 允许在 video_name 和 page_name 中使用对应平台的路径分隔符 #163

Merged
merged 1 commit into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions crates/bili_sync/src/adapter/helper/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ use sea_orm::ActiveValue::Set;
use sea_orm::{Condition, DatabaseTransaction, QuerySelect};

use crate::bilibili::{BiliError, PageInfo, VideoInfo};
use crate::config::TEMPLATE;
use crate::utils::filenamify::filenamify;
use crate::config::{PathSafeTemplate, TEMPLATE};
use crate::utils::id_time_key;

/// 使用 condition 筛选视频,返回视频数量
Expand Down Expand Up @@ -66,11 +65,7 @@ pub(super) fn video_with_path(
) -> video::ActiveModel {
if let Some(fmt_args) = &video_info.to_fmt_args() {
video_model.path = Set(Path::new(base_path)
.join(filenamify(
TEMPLATE
.render("video", fmt_args)
.unwrap_or_else(|_| video_info.bvid().to_string()),
))
.join(TEMPLATE.path_safe_render("video", fmt_args).unwrap())
.to_string_lossy()
.to_string());
}
Expand Down
7 changes: 3 additions & 4 deletions crates/bili_sync/src/config/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use handlebars::handlebars_helper;
use once_cell::sync::Lazy;

use crate::config::clap::Args;
use crate::config::item::PathSafeTemplate;
use crate::config::Config;

/// 全局的 CONFIG,可以从中读取配置信息
Expand Down Expand Up @@ -39,10 +40,8 @@ pub static TEMPLATE: Lazy<handlebars::Handlebars> = Lazy::new(|| {
}
});
handlebars.register_helper("truncate", Box::new(truncate));
handlebars
.register_template_string("video", &CONFIG.video_name)
.unwrap();
handlebars.register_template_string("page", &CONFIG.page_name).unwrap();
handlebars.path_safe_register("video", &CONFIG.video_name).unwrap();
handlebars.path_safe_register("page", &CONFIG.page_name).unwrap();
handlebars
});

Expand Down
17 changes: 17 additions & 0 deletions crates/bili_sync/src/config/item.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::collections::HashMap;
use std::path::PathBuf;

use anyhow::Result;
use serde::de::{Deserializer, MapAccess, Visitor};
use serde::ser::SerializeMap;
use serde::{Deserialize, Serialize};

use crate::bilibili::{CollectionItem, CollectionType};
use crate::utils::filenamify::filenamify;

/// 稍后再看的配置
#[derive(Serialize, Deserialize, Default)]
Expand Down Expand Up @@ -58,6 +60,21 @@ impl Default for ConcurrentLimit {
}
}

pub trait PathSafeTemplate {
fn path_safe_register(&mut self, name: &'static str, template: &'static str) -> Result<()>;
fn path_safe_render(&self, name: &'static str, data: &serde_json::Value) -> Result<String>;
}

/// 通过将模板字符串中的分隔符替换为自定义的字符串,使得模板字符串中的分隔符得以保留
impl PathSafeTemplate for handlebars::Handlebars<'_> {
fn path_safe_register(&mut self, name: &'static str, template: &'static str) -> Result<()> {
Ok(self.register_template_string(name, template.replace(std::path::MAIN_SEPARATOR_STR, "__SEP__"))?)
}

fn path_safe_render(&self, name: &'static str, data: &serde_json::Value) -> Result<String> {
Ok(filenamify(&self.render(name, data)?).replace("__SEP__", std::path::MAIN_SEPARATOR_STR))
}
}
/* 后面是用于自定义 Collection 的序列化、反序列化的样板代码 */
pub(super) fn serialize_collection_list<S>(
collection_list: &HashMap<CollectionItem, PathBuf>,
Expand Down
2 changes: 1 addition & 1 deletion crates/bili_sync/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ mod item;
use crate::bilibili::{CollectionItem, Credential, DanmakuOption, FilterOption};
pub use crate::config::global::{ARGS, CONFIG, CONFIG_DIR, TEMPLATE};
use crate::config::item::{deserialize_collection_list, serialize_collection_list, ConcurrentLimit};
pub use crate::config::item::{Delay, NFOTimeType, WatchLaterConfig};
pub use crate::config::item::{Delay, NFOTimeType, PathSafeTemplate, WatchLaterConfig};

fn default_time_format() -> String {
"%Y-%m-%d".to_string()
Expand Down
49 changes: 41 additions & 8 deletions crates/bili_sync/src/workflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ use tokio::sync::{Mutex, Semaphore};

use crate::adapter::{video_list_from, Args, VideoListModel};
use crate::bilibili::{BestStream, BiliClient, BiliError, Dimension, PageInfo, Video, VideoInfo};
use crate::config::{ARGS, CONFIG, TEMPLATE};
use crate::config::{PathSafeTemplate, ARGS, CONFIG, TEMPLATE};
use crate::downloader::Downloader;
use crate::error::{DownloadAbortError, ProcessPageError};
use crate::utils::delay;
use crate::utils::filenamify::filenamify;
use crate::utils::model::{create_videos, update_pages_model, update_videos_model};
use crate::utils::nfo::{ModelWrapper, NFOMode, NFOSerializer};
use crate::utils::status::{PageStatus, VideoStatus};
Expand Down Expand Up @@ -313,7 +312,7 @@ pub async fn download_page(
let seprate_status = status.should_run();
let is_single_page = video_model.single_page.unwrap();
let base_path = Path::new(&video_model.path);
let base_name = filenamify(TEMPLATE.render(
let base_name = TEMPLATE.path_safe_render(
"page",
&json!({
"bvid": &video_model.bvid,
Expand All @@ -325,7 +324,7 @@ pub async fn download_page(
"pubtime": video_model.pubtime.format(&CONFIG.time_format).to_string(),
"fav_time": video_model.favtime.format(&CONFIG.time_format).to_string(),
}),
)?);
)?;
let (poster_path, video_path, nfo_path, danmaku_path, fanart_path) = if is_single_page {
(
base_path.join(format!("{}-poster.jpg", &base_name)),
Expand Down Expand Up @@ -623,15 +622,49 @@ mod tests {
}
});
template.register_helper("truncate", Box::new(truncate));
let _ = template.register_template_string("video", "test{{bvid}}test");
let _ = template.register_template_string("test_truncate", "哈哈,{{ truncate title 30 }}");
let _ = template.path_safe_register("video", "test{{bvid}}test");
let _ = template.path_safe_register("test_truncate", "哈哈,{{ truncate title 30 }}");
let _ = template.path_safe_register("test_path_unix", "{{ truncate title 7 }}/test/a");
let _ = template.path_safe_register("test_path_windows", r"{{ truncate title 7 }}\\test\\a");
#[cfg(not(windows))]
{
assert_eq!(
template
.path_safe_render("test_path_unix", &json!({"title": "关注/永雏塔菲喵"}))
.unwrap(),
"关注_永雏塔菲/test/a"
);
assert_eq!(
template
.path_safe_render("test_path_windows", &json!({"title": "关注/永雏塔菲喵"}))
.unwrap(),
"关注_永雏塔菲_test_a"
);
}
#[cfg(windows)]
{
assert_eq!(
template
.path_safe_render("test_path_unix", &json!({"title": "关注/永雏塔菲喵"}))
.unwrap(),
"关注_永雏塔菲_test_a"
);
assert_eq!(
template
.path_safe_render("test_path_windows", &json!({"title": "关注永雏/塔菲喵"}))
.unwrap(),
r"关注_永雏塔菲\\test\\a"
);
}
assert_eq!(
template.render("video", &json!({"bvid": "BV1b5411h7g7"})).unwrap(),
template
.path_safe_render("video", &json!({"bvid": "BV1b5411h7g7"}))
.unwrap(),
"testBV1b5411h7g7test"
);
assert_eq!(
template
.render(
.path_safe_render(
"test_truncate",
&json!({"title": "你说得对,但是 Rust 是由 Mozilla 自主研发的一款全新的编译期格斗游戏。\
编译将发生在一个被称作「Cargo」的构建系统中。在这里,被引用的指针将被授予「生命周期」之力,导引对象安全。\
Expand Down