feat: unify default source paths and fix windows drive path rendering
This commit is contained in:
@@ -99,8 +99,6 @@ pub struct FollowedUppersRequest {
|
||||
#[derive(Deserialize, Validate)]
|
||||
pub struct InsertFavoriteRequest {
|
||||
pub fid: i64,
|
||||
#[validate(custom(function = "crate::utils::validation::validate_path"))]
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Validate)]
|
||||
@@ -109,21 +107,16 @@ pub struct InsertCollectionRequest {
|
||||
pub mid: i64,
|
||||
#[serde(default)]
|
||||
pub collection_type: CollectionType,
|
||||
#[validate(custom(function = "crate::utils::validation::validate_path"))]
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Validate)]
|
||||
pub struct InsertSubmissionRequest {
|
||||
pub upper_id: i64,
|
||||
#[validate(custom(function = "crate::utils::validation::validate_path"))]
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Validate)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateVideoSourceRequest {
|
||||
#[validate(custom(function = "crate::utils::validation::validate_path"))]
|
||||
pub path: String,
|
||||
pub enabled: bool,
|
||||
pub rule: Option<Rule>,
|
||||
|
||||
@@ -10,6 +10,7 @@ use bili_sync_migration::Expr;
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use sea_orm::entity::prelude::*;
|
||||
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QuerySelect, QueryTrait, TransactionTrait};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::adapter::{_ActiveModel, VideoSource as _, VideoSourceEnum};
|
||||
use crate::api::error::InnerApiError;
|
||||
@@ -138,10 +139,13 @@ pub async fn get_video_sources_details(
|
||||
.all(&db)
|
||||
)?;
|
||||
if watch_later.is_empty() {
|
||||
let path = TEMPLATE
|
||||
.read()
|
||||
.path_safe_render("watch_later_default_path", &json!({ "name": "稍后再看" }))?;
|
||||
watch_later.push(VideoSourceDetail {
|
||||
id: 1,
|
||||
name: "稍后再看".to_string(),
|
||||
path: String::new(),
|
||||
path,
|
||||
rule: None,
|
||||
rule_display: None,
|
||||
use_dynamic_api: None,
|
||||
@@ -171,6 +175,7 @@ pub async fn get_video_sources_default_path(
|
||||
"favorites" => "favorite_default_path",
|
||||
"collections" => "collection_default_path",
|
||||
"submissions" => "submission_default_path",
|
||||
"watch_later" => "watch_later_default_path",
|
||||
_ => return Err(InnerApiError::BadRequest("Invalid video source type".to_string()).into()),
|
||||
};
|
||||
let template = TEMPLATE.read();
|
||||
@@ -185,7 +190,22 @@ pub async fn update_video_source(
|
||||
Extension(db): Extension<DatabaseConnection>,
|
||||
ValidatedJson(request): ValidatedJson<UpdateVideoSourceRequest>,
|
||||
) -> Result<ApiResponse<UpdateVideoSourceResponse>, ApiError> {
|
||||
if source_type != "watch_later" && crate::utils::validation::validate_path(&request.path).is_err() {
|
||||
return Err(
|
||||
InnerApiError::BadRequest("path: Validation error: path must be a non-empty absolute path".to_string())
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let rule_display = request.rule.as_ref().map(|rule| rule.to_string());
|
||||
let watch_later_path = if source_type == "watch_later" {
|
||||
Some(
|
||||
TEMPLATE
|
||||
.read()
|
||||
.path_safe_render("watch_later_default_path", &json!({ "name": "稍后再看" }))?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let active_model = match source_type.as_str() {
|
||||
"collections" => collection::Entity::find_by_id(id).one(&db).await?.map(|model| {
|
||||
let mut active_model: collection::ActiveModel = model.into();
|
||||
@@ -217,7 +237,11 @@ pub async fn update_video_source(
|
||||
Some(model) => {
|
||||
// 如果有记录,使用 id 对应的记录更新
|
||||
let mut active_model: watch_later::ActiveModel = model.into();
|
||||
active_model.path = Set(request.path);
|
||||
active_model.path = Set(
|
||||
watch_later_path
|
||||
.clone()
|
||||
.expect("watch_later path should always be initialized"),
|
||||
);
|
||||
active_model.enabled = Set(request.enabled);
|
||||
active_model.rule = Set(request.rule);
|
||||
Some(_ActiveModel::WatchLater(active_model))
|
||||
@@ -228,7 +252,9 @@ pub async fn update_video_source(
|
||||
} else {
|
||||
// 如果没有记录且 id 为 1,插入一个新的稍后再看记录
|
||||
Some(_ActiveModel::WatchLater(watch_later::ActiveModel {
|
||||
path: Set(request.path),
|
||||
path: Set(
|
||||
watch_later_path.expect("watch_later path should always be initialized"),
|
||||
),
|
||||
enabled: Set(request.enabled),
|
||||
rule: Set(request.rule),
|
||||
..Default::default()
|
||||
@@ -368,10 +394,13 @@ pub async fn insert_favorite(
|
||||
let credential = &VersionedConfig::get().read().credential;
|
||||
let favorite = FavoriteList::new(bili_client.as_ref(), request.fid.to_string(), credential);
|
||||
let favorite_info = favorite.get_info().await?;
|
||||
let path = TEMPLATE
|
||||
.read()
|
||||
.path_safe_render("favorite_default_path", &json!({ "name": favorite_info.title }))?;
|
||||
favorite::Entity::insert(favorite::ActiveModel {
|
||||
f_id: Set(favorite_info.id),
|
||||
name: Set(favorite_info.title.clone()),
|
||||
path: Set(request.path),
|
||||
path: Set(path),
|
||||
enabled: Set(false),
|
||||
..Default::default()
|
||||
})
|
||||
@@ -397,12 +426,15 @@ pub async fn insert_collection(
|
||||
credential,
|
||||
);
|
||||
let collection_info = collection.get_info().await?;
|
||||
let path = TEMPLATE
|
||||
.read()
|
||||
.path_safe_render("collection_default_path", &json!({ "name": collection_info.name }))?;
|
||||
collection::Entity::insert(collection::ActiveModel {
|
||||
s_id: Set(collection_info.sid),
|
||||
m_id: Set(collection_info.mid),
|
||||
r#type: Set(collection_info.collection_type.into()),
|
||||
name: Set(collection_info.name.clone()),
|
||||
path: Set(request.path),
|
||||
path: Set(path),
|
||||
enabled: Set(false),
|
||||
..Default::default()
|
||||
})
|
||||
@@ -418,13 +450,18 @@ pub async fn insert_submission(
|
||||
Extension(bili_client): Extension<Arc<BiliClient>>,
|
||||
ValidatedJson(request): ValidatedJson<InsertSubmissionRequest>,
|
||||
) -> Result<ApiResponse<bool>, ApiError> {
|
||||
let credential = &VersionedConfig::get().read().credential;
|
||||
let config = VersionedConfig::get().snapshot();
|
||||
let credential = &config.credential;
|
||||
let submission = Submission::new(bili_client.as_ref(), request.upper_id.to_string(), credential);
|
||||
let upper = submission.get_info().await?;
|
||||
let upper_name = upper.name;
|
||||
let path = TEMPLATE
|
||||
.read()
|
||||
.path_safe_render("submission_default_path", &json!({ "name": upper_name }))?;
|
||||
submission::Entity::insert(submission::ActiveModel {
|
||||
upper_id: Set(upper.mid.parse()?),
|
||||
upper_name: Set(upper.name),
|
||||
path: Set(request.path),
|
||||
upper_name: Set(upper_name),
|
||||
path: Set(path),
|
||||
enabled: Set(false),
|
||||
..Default::default()
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::bilibili::{Credential, DanmakuOption, FilterOption};
|
||||
use crate::config::args::ARGS;
|
||||
use crate::config::default::{
|
||||
default_auth_token, default_bind_address, default_collection_path, default_favorite_path, default_submission_path,
|
||||
default_time_format,
|
||||
default_time_format, default_watch_later_path,
|
||||
};
|
||||
use crate::config::item::{ConcurrentLimit, NFOTimeType, SkipOption, Trigger};
|
||||
use crate::notifier::Notifier;
|
||||
@@ -43,6 +43,8 @@ pub struct Config {
|
||||
pub collection_default_path: String,
|
||||
#[serde(default = "default_submission_path")]
|
||||
pub submission_default_path: String,
|
||||
#[serde(default = "default_watch_later_path")]
|
||||
pub watch_later_default_path: String,
|
||||
pub interval: Trigger,
|
||||
pub upper_path: PathBuf,
|
||||
pub nfo_time_type: NFOTimeType,
|
||||
@@ -130,6 +132,7 @@ impl Default for Config {
|
||||
favorite_default_path: default_favorite_path(),
|
||||
collection_default_path: default_collection_path(),
|
||||
submission_default_path: default_submission_path(),
|
||||
watch_later_default_path: default_watch_later_path(),
|
||||
interval: Trigger::default(),
|
||||
upper_path: CONFIG_DIR.join("upper_face"),
|
||||
nfo_time_type: NFOTimeType::FavTime,
|
||||
|
||||
@@ -28,3 +28,7 @@ pub fn default_collection_path() -> String {
|
||||
pub fn default_submission_path() -> String {
|
||||
"投稿/{{name}}".to_owned()
|
||||
}
|
||||
|
||||
pub fn default_watch_later_path() -> String {
|
||||
"稍后再看".to_owned()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ fn create_template(config: &Config) -> Result<handlebars::Handlebars<'static>> {
|
||||
handlebars.path_safe_register("favorite_default_path", config.favorite_default_path.clone())?;
|
||||
handlebars.path_safe_register("collection_default_path", config.collection_default_path.clone())?;
|
||||
handlebars.path_safe_register("submission_default_path", config.submission_default_path.clone())?;
|
||||
handlebars.path_safe_register("watch_later_default_path", config.watch_later_default_path.clone())?;
|
||||
if let Some(notifiers) = &config.notifiers {
|
||||
for notifier in notifiers.iter() {
|
||||
if let Notifier::Webhook { url, template, .. } = notifier {
|
||||
@@ -79,6 +80,13 @@ mod tests {
|
||||
.unwrap(),
|
||||
r"关注_永雏塔菲\\test\\a"
|
||||
);
|
||||
let _ = template.path_safe_register("test_drive_prefix", r"W:\\投稿\\{{title}}");
|
||||
assert_eq!(
|
||||
template
|
||||
.path_safe_render("test_drive_prefix", &json!({"title": "林:杏仁/Almond"}))
|
||||
.unwrap(),
|
||||
r"W:\\投稿\\林_杏仁_Almond"
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
template
|
||||
|
||||
@@ -95,6 +95,19 @@ impl PathSafeTemplate for handlebars::Handlebars<'_> {
|
||||
}
|
||||
|
||||
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))
|
||||
let rendered = self.render(name, data)?;
|
||||
#[cfg(windows)]
|
||||
let path = {
|
||||
let bytes = rendered.as_bytes();
|
||||
if bytes.len() >= 2 && bytes[0].is_ascii_alphabetic() && bytes[1] == b':' {
|
||||
let (drive_prefix, rest) = rendered.split_at(2);
|
||||
format!("{drive_prefix}{}", filenamify(rest))
|
||||
} else {
|
||||
filenamify(&rendered)
|
||||
}
|
||||
};
|
||||
#[cfg(not(windows))]
|
||||
let path = filenamify(&rendered);
|
||||
Ok(path.replace("__SEP__", std::path::MAIN_SEPARATOR_STR))
|
||||
}
|
||||
}
|
||||
|
||||
23
web/package-lock.json
generated
23
web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "bili-sync-web",
|
||||
"version": "2.9.4",
|
||||
"version": "2.10.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bili-sync-web",
|
||||
"version": "2.9.4",
|
||||
"version": "2.10.3",
|
||||
"dependencies": {
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"qrcode": "^1.5.4"
|
||||
@@ -33,6 +33,7 @@
|
||||
"layerchart": "^2.0.0-next.43",
|
||||
"mode-watcher": "^1.1.0",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-organize-imports": "^4.3.0",
|
||||
"prettier-plugin-svelte": "^3.4.1",
|
||||
"prettier-plugin-tailwindcss": "^0.7.2",
|
||||
"svelte": "^5.46.1",
|
||||
@@ -4201,6 +4202,24 @@
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-organize-imports": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.3.0.tgz",
|
||||
"integrity": "sha512-FxFz0qFhyBsGdIsb697f/EkvHzi5SZOhWAjxcx2dLt+Q532bAlhswcXGYB1yzjZ69kW8UoadFBw7TyNwlq96Iw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"prettier": ">=2.0",
|
||||
"typescript": ">=2.9",
|
||||
"vue-tsc": "^2.1.0 || 3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"vue-tsc": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-svelte": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.4.1.tgz",
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
}
|
||||
|
||||
async function handleSubscribe() {
|
||||
if (!item || !customPath.trim()) return;
|
||||
if (!item) return;
|
||||
|
||||
loading = true;
|
||||
try {
|
||||
@@ -83,8 +83,7 @@
|
||||
switch (item.type) {
|
||||
case 'favorite': {
|
||||
const request: InsertFavoriteRequest = {
|
||||
fid: item.fid,
|
||||
path: customPath.trim()
|
||||
fid: item.fid
|
||||
};
|
||||
response = await api.insertFavorite(request);
|
||||
break;
|
||||
@@ -92,16 +91,14 @@
|
||||
case 'collection': {
|
||||
const request: InsertCollectionRequest = {
|
||||
sid: item.sid,
|
||||
mid: item.mid,
|
||||
path: customPath.trim()
|
||||
mid: item.mid
|
||||
};
|
||||
response = await api.insertCollection(request);
|
||||
break;
|
||||
}
|
||||
case 'upper': {
|
||||
const request: InsertSubmissionRequest = {
|
||||
upper_id: item.mid,
|
||||
path: customPath.trim()
|
||||
upper_id: item.mid
|
||||
};
|
||||
response = await api.insertSubmission(request);
|
||||
break;
|
||||
@@ -109,8 +106,9 @@
|
||||
}
|
||||
|
||||
if (response && response.data) {
|
||||
const successPath = customPath.trim() || '自动生成路径';
|
||||
toast.success('订阅成功', {
|
||||
description: `已订阅${getTypeLabel()}「${getItemTitle()}」到路径「${customPath.trim()}」`
|
||||
description: `已订阅${getTypeLabel()}「${getItemTitle()}」到路径「${successPath}」`
|
||||
});
|
||||
open = false;
|
||||
if (onSuccess) {
|
||||
@@ -156,7 +154,13 @@
|
||||
<SheetTitle class="text-lg">订阅{typeLabel}</SheetTitle>
|
||||
<SheetDescription class="text-muted-foreground space-y-1 text-sm">
|
||||
<div>即将订阅{typeLabel}「{itemTitle}」</div>
|
||||
<div>请手动编辑本地保存路径:</div>
|
||||
{#if item?.type === 'upper'}
|
||||
<div>投稿下载路径将使用“设置-基本设置”中的默认地址。</div>
|
||||
{:else if item?.type === 'favorite'}
|
||||
<div>收藏夹下载路径将使用“设置-基本设置-收藏夹快捷订阅路径模板”自动生成。</div>
|
||||
{:else}
|
||||
<div>合集下载路径将使用“设置-基本设置-合集快捷订阅路径模板”自动生成。</div>
|
||||
{/if}
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
|
||||
@@ -183,29 +187,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 路径输入 -->
|
||||
<div class="space-y-3">
|
||||
<Label for="custom-path" class="text-sm font-medium">
|
||||
本地保存路径 <span class="text-destructive">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
id="custom-path"
|
||||
type="text"
|
||||
placeholder="请输入保存路径,例如:/home/我的收藏"
|
||||
bind:value={customPath}
|
||||
disabled={loading}
|
||||
class="w-full"
|
||||
/>
|
||||
<div class="text-muted-foreground space-y-3 text-xs">
|
||||
<p>路径将作为文件夹名称,用于存放下载的视频文件。</p>
|
||||
<div>
|
||||
<p class="mb-2 font-medium">路径示例:</p>
|
||||
<div class="space-y-1 pl-4">
|
||||
<div class="font-mono text-xs">Mac/Linux: /home/downloads/我的收藏</div>
|
||||
<div class="font-mono text-xs">Windows: C:\Downloads\我的收藏</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Label class="text-sm font-medium">本地保存路径</Label>
|
||||
<Input id="custom-path" type="text" bind:value={customPath} disabled class="w-full" />
|
||||
<p class="text-muted-foreground text-xs">
|
||||
该路径由“设置-基本设置”中的对应模板自动生成。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -221,7 +208,7 @@
|
||||
</Button>
|
||||
<Button
|
||||
onclick={handleSubscribe}
|
||||
disabled={loading || !customPath.trim()}
|
||||
disabled={loading}
|
||||
class="flex-1 cursor-pointer"
|
||||
>
|
||||
{loading ? '订阅中...' : '确认订阅'}
|
||||
|
||||
@@ -173,19 +173,16 @@ export interface UppersResponse {
|
||||
|
||||
export interface InsertFavoriteRequest {
|
||||
fid: number;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface InsertCollectionRequest {
|
||||
sid: number;
|
||||
mid: number;
|
||||
collection_type?: number;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface InsertSubmissionRequest {
|
||||
upper_id: number;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface Condition<T> {
|
||||
@@ -315,6 +312,7 @@ export interface Config {
|
||||
favorite_default_path: string;
|
||||
collection_default_path: string;
|
||||
submission_default_path: string;
|
||||
watch_later_default_path: string;
|
||||
interval: Trigger;
|
||||
upper_path: string;
|
||||
nfo_time_type: string;
|
||||
|
||||
@@ -347,9 +347,16 @@
|
||||
<Input id="collection-default-path" bind:value={formData.collection_default_path} />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="submission-default-path">UP 主投稿快捷订阅路径模板</Label>
|
||||
<Label for="submission-default-path">UP 主投稿默认下载路径</Label>
|
||||
<Input id="submission-default-path" bind:value={formData.submission_default_path} />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="watch-later-default-path">稍后再看默认下载路径</Label>
|
||||
<Input
|
||||
id="watch-later-default-path"
|
||||
bind:value={formData.watch_later_default_path}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
@@ -67,9 +67,9 @@
|
||||
};
|
||||
|
||||
// 表单数据
|
||||
let favoriteForm = { fid: '', path: '' };
|
||||
let collectionForm = { sid: '', mid: '', collection_type: '2', path: '' }; // 默认为合集
|
||||
let submissionForm = { upper_id: '', path: '' };
|
||||
let favoriteForm = { fid: '' };
|
||||
let collectionForm = { sid: '', mid: '', collection_type: '2' }; // 默认为合集
|
||||
let submissionForm = { upper_id: '' };
|
||||
|
||||
const TAB_CONFIG = {
|
||||
favorites: { label: '收藏夹', icon: HeartIcon },
|
||||
@@ -218,9 +218,9 @@
|
||||
function openAddDialog(type: 'favorites' | 'collections' | 'submissions') {
|
||||
addDialogType = type;
|
||||
// 重置表单
|
||||
favoriteForm = { fid: '', path: '' };
|
||||
collectionForm = { sid: '', mid: '', collection_type: '2', path: '' };
|
||||
submissionForm = { upper_id: '', path: '' };
|
||||
favoriteForm = { fid: '' };
|
||||
collectionForm = { sid: '', mid: '', collection_type: '2' };
|
||||
submissionForm = { upper_id: '' };
|
||||
showAddDialog = true;
|
||||
}
|
||||
|
||||
@@ -230,35 +230,32 @@
|
||||
try {
|
||||
switch (addDialogType) {
|
||||
case 'favorites':
|
||||
if (!favoriteForm.fid || !favoriteForm.path.trim()) {
|
||||
if (!favoriteForm.fid) {
|
||||
toast.error('请填写完整的收藏夹信息');
|
||||
return;
|
||||
}
|
||||
await api.insertFavorite({
|
||||
fid: parseInt(favoriteForm.fid),
|
||||
path: favoriteForm.path
|
||||
fid: parseInt(favoriteForm.fid)
|
||||
});
|
||||
break;
|
||||
case 'collections':
|
||||
if (!collectionForm.sid || !collectionForm.mid || !collectionForm.path.trim()) {
|
||||
if (!collectionForm.sid || !collectionForm.mid) {
|
||||
toast.error('请填写完整的合集信息');
|
||||
return;
|
||||
}
|
||||
await api.insertCollection({
|
||||
sid: parseInt(collectionForm.sid),
|
||||
mid: parseInt(collectionForm.mid),
|
||||
collection_type: parseInt(collectionForm.collection_type),
|
||||
path: collectionForm.path
|
||||
collection_type: parseInt(collectionForm.collection_type)
|
||||
});
|
||||
break;
|
||||
case 'submissions':
|
||||
if (!submissionForm.upper_id || !submissionForm.path.trim()) {
|
||||
if (!submissionForm.upper_id) {
|
||||
toast.error('请填写完整的用户投稿信息');
|
||||
return;
|
||||
}
|
||||
await api.insertSubmission({
|
||||
upper_id: parseInt(submissionForm.upper_id),
|
||||
path: submissionForm.path
|
||||
upper_id: parseInt(submissionForm.upper_id)
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -484,8 +481,14 @@
|
||||
type="text"
|
||||
bind:value={editForm.path}
|
||||
placeholder="请输入下载路径,例如:/path/to/download"
|
||||
disabled={editingType === 'watch_later'}
|
||||
class="mt-2"
|
||||
/>
|
||||
{#if editingType === 'watch_later'}
|
||||
<p class="text-muted-foreground mt-2 text-xs">
|
||||
稍后再看固定使用“设置 - 基本设置 - 稍后再看默认下载路径”自动生成。
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- 启用状态 -->
|
||||
@@ -664,33 +667,17 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="mt-4">
|
||||
<Label for="path" class="text-sm font-medium">下载路径</Label>
|
||||
{#if addDialogType === 'favorites'}
|
||||
<Input
|
||||
id="path"
|
||||
type="text"
|
||||
bind:value={favoriteForm.path}
|
||||
placeholder="请输入下载路径,例如:/path/to/download"
|
||||
class="mt-1"
|
||||
/>
|
||||
{:else if addDialogType === 'collections'}
|
||||
<Input
|
||||
id="path"
|
||||
type="text"
|
||||
bind:value={collectionForm.path}
|
||||
placeholder="请输入下载路径,例如:/path/to/download"
|
||||
class="mt-1"
|
||||
/>
|
||||
{:else}
|
||||
<Input
|
||||
id="path"
|
||||
type="text"
|
||||
bind:value={submissionForm.path}
|
||||
placeholder="请输入下载路径,例如:/path/to/download"
|
||||
class="mt-1"
|
||||
/>
|
||||
{/if}
|
||||
<div class="mt-4 space-y-1.5">
|
||||
<Label class="text-sm font-medium">下载路径</Label>
|
||||
<p class="text-muted-foreground text-xs">
|
||||
{#if addDialogType === 'favorites'}
|
||||
收藏夹会固定使用“设置 - 基本设置 - 收藏夹快捷订阅路径模板”自动生成。
|
||||
{:else if addDialogType === 'collections'}
|
||||
合集会固定使用“设置 - 基本设置 - 合集快捷订阅路径模板”自动生成。
|
||||
{:else}
|
||||
用户投稿会固定使用“设置 - 基本设置 - UP 主投稿默认下载路径”自动生成。
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 flex justify-end gap-2">
|
||||
|
||||
Reference in New Issue
Block a user