mirror of
https://github.com/amtoaer/bili-sync.git
synced 2026-07-10 23:12:18 +08:00
feat: 允许在 video_name 和 page_name 中使用对应平台的路径分隔符 (#163)
This commit is contained in:
@@ -9,8 +9,7 @@ use sea_orm::ActiveValue::Set;
|
|||||||
use sea_orm::{Condition, DatabaseTransaction, QuerySelect};
|
use sea_orm::{Condition, DatabaseTransaction, QuerySelect};
|
||||||
|
|
||||||
use crate::bilibili::{BiliError, PageInfo, VideoInfo};
|
use crate::bilibili::{BiliError, PageInfo, VideoInfo};
|
||||||
use crate::config::TEMPLATE;
|
use crate::config::{PathSafeTemplate, TEMPLATE};
|
||||||
use crate::utils::filenamify::filenamify;
|
|
||||||
use crate::utils::id_time_key;
|
use crate::utils::id_time_key;
|
||||||
|
|
||||||
/// 使用 condition 筛选视频,返回视频数量
|
/// 使用 condition 筛选视频,返回视频数量
|
||||||
@@ -66,11 +65,7 @@ pub(super) fn video_with_path(
|
|||||||
) -> video::ActiveModel {
|
) -> video::ActiveModel {
|
||||||
if let Some(fmt_args) = &video_info.to_fmt_args() {
|
if let Some(fmt_args) = &video_info.to_fmt_args() {
|
||||||
video_model.path = Set(Path::new(base_path)
|
video_model.path = Set(Path::new(base_path)
|
||||||
.join(filenamify(
|
.join(TEMPLATE.path_safe_render("video", fmt_args).unwrap())
|
||||||
TEMPLATE
|
|
||||||
.render("video", fmt_args)
|
|
||||||
.unwrap_or_else(|_| video_info.bvid().to_string()),
|
|
||||||
))
|
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_string());
|
.to_string());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use handlebars::handlebars_helper;
|
|||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use crate::config::clap::Args;
|
use crate::config::clap::Args;
|
||||||
|
use crate::config::item::PathSafeTemplate;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
|
||||||
/// 全局的 CONFIG,可以从中读取配置信息
|
/// 全局的 CONFIG,可以从中读取配置信息
|
||||||
@@ -39,10 +40,8 @@ pub static TEMPLATE: Lazy<handlebars::Handlebars> = Lazy::new(|| {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
handlebars.register_helper("truncate", Box::new(truncate));
|
handlebars.register_helper("truncate", Box::new(truncate));
|
||||||
handlebars
|
handlebars.path_safe_register("video", &CONFIG.video_name).unwrap();
|
||||||
.register_template_string("video", &CONFIG.video_name)
|
handlebars.path_safe_register("page", &CONFIG.page_name).unwrap();
|
||||||
.unwrap();
|
|
||||||
handlebars.register_template_string("page", &CONFIG.page_name).unwrap();
|
|
||||||
handlebars
|
handlebars
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use serde::de::{Deserializer, MapAccess, Visitor};
|
use serde::de::{Deserializer, MapAccess, Visitor};
|
||||||
use serde::ser::SerializeMap;
|
use serde::ser::SerializeMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::bilibili::{CollectionItem, CollectionType};
|
use crate::bilibili::{CollectionItem, CollectionType};
|
||||||
|
use crate::utils::filenamify::filenamify;
|
||||||
|
|
||||||
/// 稍后再看的配置
|
/// 稍后再看的配置
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
@@ -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 的序列化、反序列化的样板代码 */
|
/* 后面是用于自定义 Collection 的序列化、反序列化的样板代码 */
|
||||||
pub(super) fn serialize_collection_list<S>(
|
pub(super) fn serialize_collection_list<S>(
|
||||||
collection_list: &HashMap<CollectionItem, PathBuf>,
|
collection_list: &HashMap<CollectionItem, PathBuf>,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ mod item;
|
|||||||
use crate::bilibili::{CollectionItem, Credential, DanmakuOption, FilterOption};
|
use crate::bilibili::{CollectionItem, Credential, DanmakuOption, FilterOption};
|
||||||
pub use crate::config::global::{ARGS, CONFIG, CONFIG_DIR, TEMPLATE};
|
pub use crate::config::global::{ARGS, CONFIG, CONFIG_DIR, TEMPLATE};
|
||||||
use crate::config::item::{deserialize_collection_list, serialize_collection_list, ConcurrentLimit};
|
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 {
|
fn default_time_format() -> String {
|
||||||
"%Y-%m-%d".to_string()
|
"%Y-%m-%d".to_string()
|
||||||
|
|||||||
@@ -14,11 +14,10 @@ use tokio::sync::{Mutex, Semaphore};
|
|||||||
|
|
||||||
use crate::adapter::{video_list_from, Args, VideoListModel};
|
use crate::adapter::{video_list_from, Args, VideoListModel};
|
||||||
use crate::bilibili::{BestStream, BiliClient, BiliError, Dimension, PageInfo, Video, VideoInfo};
|
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::downloader::Downloader;
|
||||||
use crate::error::{DownloadAbortError, ProcessPageError};
|
use crate::error::{DownloadAbortError, ProcessPageError};
|
||||||
use crate::utils::delay;
|
use crate::utils::delay;
|
||||||
use crate::utils::filenamify::filenamify;
|
|
||||||
use crate::utils::model::{create_videos, update_pages_model, update_videos_model};
|
use crate::utils::model::{create_videos, update_pages_model, update_videos_model};
|
||||||
use crate::utils::nfo::{ModelWrapper, NFOMode, NFOSerializer};
|
use crate::utils::nfo::{ModelWrapper, NFOMode, NFOSerializer};
|
||||||
use crate::utils::status::{PageStatus, VideoStatus};
|
use crate::utils::status::{PageStatus, VideoStatus};
|
||||||
@@ -313,7 +312,7 @@ pub async fn download_page(
|
|||||||
let seprate_status = status.should_run();
|
let seprate_status = status.should_run();
|
||||||
let is_single_page = video_model.single_page.unwrap();
|
let is_single_page = video_model.single_page.unwrap();
|
||||||
let base_path = Path::new(&video_model.path);
|
let base_path = Path::new(&video_model.path);
|
||||||
let base_name = filenamify(TEMPLATE.render(
|
let base_name = TEMPLATE.path_safe_render(
|
||||||
"page",
|
"page",
|
||||||
&json!({
|
&json!({
|
||||||
"bvid": &video_model.bvid,
|
"bvid": &video_model.bvid,
|
||||||
@@ -325,7 +324,7 @@ pub async fn download_page(
|
|||||||
"pubtime": video_model.pubtime.format(&CONFIG.time_format).to_string(),
|
"pubtime": video_model.pubtime.format(&CONFIG.time_format).to_string(),
|
||||||
"fav_time": video_model.favtime.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 {
|
let (poster_path, video_path, nfo_path, danmaku_path, fanart_path) = if is_single_page {
|
||||||
(
|
(
|
||||||
base_path.join(format!("{}-poster.jpg", &base_name)),
|
base_path.join(format!("{}-poster.jpg", &base_name)),
|
||||||
@@ -623,15 +622,49 @@ mod tests {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
template.register_helper("truncate", Box::new(truncate));
|
template.register_helper("truncate", Box::new(truncate));
|
||||||
let _ = template.register_template_string("video", "test{{bvid}}test");
|
let _ = template.path_safe_register("video", "test{{bvid}}test");
|
||||||
let _ = template.register_template_string("test_truncate", "哈哈,{{ truncate title 30 }}");
|
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!(
|
assert_eq!(
|
||||||
template.render("video", &json!({"bvid": "BV1b5411h7g7"})).unwrap(),
|
template
|
||||||
|
.path_safe_render("video", &json!({"bvid": "BV1b5411h7g7"}))
|
||||||
|
.unwrap(),
|
||||||
"testBV1b5411h7g7test"
|
"testBV1b5411h7g7test"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
template
|
template
|
||||||
.render(
|
.path_safe_render(
|
||||||
"test_truncate",
|
"test_truncate",
|
||||||
&json!({"title": "你说得对,但是 Rust 是由 Mozilla 自主研发的一款全新的编译期格斗游戏。\
|
&json!({"title": "你说得对,但是 Rust 是由 Mozilla 自主研发的一款全新的编译期格斗游戏。\
|
||||||
编译将发生在一个被称作「Cargo」的构建系统中。在这里,被引用的指针将被授予「生命周期」之力,导引对象安全。\
|
编译将发生在一个被称作「Cargo」的构建系统中。在这里,被引用的指针将被授予「生命周期」之力,导引对象安全。\
|
||||||
|
|||||||
Reference in New Issue
Block a user