mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-14 00:10:43 +08:00
Merge pull request #55 from x1ao4/dev
修复资源搜索结果发布时间时区显示错误的问题,为系统配置页面的插件配置和 Cookie 增加占位提示与悬停信息
This commit is contained in:
commit
58f39a61fe
@ -2088,10 +2088,13 @@ def get_user_info():
|
||||
account = Quark(cookie, idx)
|
||||
account_info = account.init()
|
||||
if account_info:
|
||||
# 检查是否有移动端参数
|
||||
has_mparam = bool(account.mparam)
|
||||
user_info_list.append({
|
||||
"index": idx,
|
||||
"nickname": account_info["nickname"],
|
||||
"is_active": account.is_active
|
||||
"is_active": account.is_active,
|
||||
"has_mparam": has_mparam
|
||||
})
|
||||
else:
|
||||
# 检查是否有移动端参数
|
||||
|
||||
@ -24,10 +24,33 @@ class PanSou:
|
||||
except Exception as e:
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
def _get_pansou_source(self, result_item: dict, merged_item: dict = None) -> str:
|
||||
"""
|
||||
获取PanSou内部来源信息
|
||||
返回格式:
|
||||
- "tg:频道名称" - 来自Telegram频道,需要+8小时
|
||||
- "plugin:插件名" - 来自指定插件,不+8小时
|
||||
- "unknown" - 未知来源,不+8小时
|
||||
"""
|
||||
# 优先从 results 的 channel 字段判断
|
||||
if result_item and result_item.get("channel"):
|
||||
channel = result_item.get("channel", "").strip()
|
||||
if channel:
|
||||
return f"tg:{channel}"
|
||||
|
||||
# 从 merged_by_type 的 source 字段获取
|
||||
if merged_item and merged_item.get("source"):
|
||||
source = merged_item.get("source", "").strip()
|
||||
if source:
|
||||
return source
|
||||
|
||||
# 默认返回 unknown
|
||||
return "unknown"
|
||||
|
||||
def search(self, keyword: str):
|
||||
"""
|
||||
搜索资源(仅返回夸克网盘结果)
|
||||
返回:{"success": True, "data": [{taskname, content, shareurl, tags[]}]}
|
||||
返回:{"success": True, "data": [{taskname, content, shareurl, tags[], pansou_source}]}
|
||||
"""
|
||||
if not self.server:
|
||||
return {"success": False, "message": "PanSou未配置服务器"}
|
||||
@ -82,6 +105,9 @@ class PanSou:
|
||||
content = result_item.get("content", "")
|
||||
datetime_str = result_item.get("datetime", "") # 获取发布日期
|
||||
|
||||
# 获取PanSou内部来源
|
||||
pansou_source = self._get_pansou_source(result_item)
|
||||
|
||||
# 从 links 获取具体链接
|
||||
links = result_item.get("links", [])
|
||||
if isinstance(links, list):
|
||||
@ -96,7 +122,8 @@ class PanSou:
|
||||
"shareurl": url,
|
||||
"tags": [link_type] if link_type else (result_item.get("tags", []) or []),
|
||||
"publish_date": datetime_str, # 原始时间(可能是 ISO)
|
||||
"source": "PanSou" # 添加来源标识
|
||||
"source": "PanSou", # 添加来源标识
|
||||
"pansou_source": pansou_source # 添加PanSou内部来源
|
||||
})
|
||||
|
||||
# 2) merged_by_type: 兜底解析,使用 note 字段作为标题
|
||||
@ -112,6 +139,10 @@ class PanSou:
|
||||
note = link.get("note", "") # 使用 note 字段作为标题
|
||||
note = strip_links(note)
|
||||
datetime_str = link.get("datetime", "") # 获取发布日期
|
||||
|
||||
# 获取PanSou内部来源
|
||||
pansou_source = self._get_pansou_source(None, link)
|
||||
|
||||
if url:
|
||||
cleaned.append({
|
||||
"taskname": note,
|
||||
@ -119,7 +150,8 @@ class PanSou:
|
||||
"shareurl": url,
|
||||
"tags": [cloud_type] if cloud_type else [],
|
||||
"publish_date": datetime_str, # 原始时间
|
||||
"source": "PanSou" # 添加来源标识
|
||||
"source": "PanSou", # 添加来源标识
|
||||
"pansou_source": pansou_source # 添加PanSou内部来源
|
||||
})
|
||||
|
||||
# 3) 直接 data 数组兜底
|
||||
@ -132,7 +164,8 @@ class PanSou:
|
||||
"shareurl": item.get("url", ""),
|
||||
"tags": item.get("tags", []) or [],
|
||||
"publish_date": item.get("datetime", ""), # 原始时间
|
||||
"source": "PanSou" # 添加来源标识
|
||||
"source": "PanSou", # 添加来源标识
|
||||
"pansou_source": "unknown" # 兜底来源
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@ -463,12 +463,14 @@
|
||||
<p style="margin-top: -4px; margin-bottom: 4px;">所有账号都会进行签到(纯签到只需填写移动端参数),只有第一个账号会进行转存,请自行确认账号顺序;所有填写了 Cookie 的账号均支持文件整理,如需签到请在 Cookie 后方添加签到参数。</p>
|
||||
<div v-for="(value, index) in formData.cookie" :key="index" class="input-group mb-2">
|
||||
<div class="input-group-prepend" v-if="userInfoList[index]">
|
||||
<span class="input-group-text" :style="userInfoList[index].nickname ? (userInfoList[index].is_active ? 'color: var(--dark-text-color);' : 'color: red;') : 'color: var(--dark-text-color);'">
|
||||
<span class="input-group-text"
|
||||
:style="userInfoList[index].nickname ? (userInfoList[index].is_active ? 'color: var(--dark-text-color);' : 'color: red;') : 'color: var(--dark-text-color);'"
|
||||
:title="getCookieStatusTooltip(userInfoList[index])">
|
||||
{{ userInfoList[index].nickname || (userInfoList[index].has_mparam ? '仅签到' : '未登录') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="input-group-prepend" v-else>
|
||||
<span class="input-group-text" style="color: var(--dark-text-color);">未验证</span>
|
||||
<span class="input-group-text" style="color: var(--dark-text-color);" title="账号信息未验证,请获取Cookie后自动验证">未验证</span>
|
||||
</div>
|
||||
<input type="text" v-model="formData.cookie[index]" class="form-control" placeholder="打开 pan.quark.com 按 F12 抓取" @change="fetchUserInfo">
|
||||
<div class="input-group-append">
|
||||
@ -1036,7 +1038,7 @@
|
||||
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
||||
<small class="text-muted">
|
||||
<a :href="suggestion.shareurl" target="_blank" @click.stop> · {{ suggestion.shareurl.replace(/^https?:\/\/pan\.quark\.cn\/s\//, '') }}</a>
|
||||
<template v-if="suggestion.source"><span class="source-badge" :class="suggestion.source.toLowerCase()" :data-publish-date="suggestion.publish_date ? ' · ' + formatPublishDate(suggestion.publish_date) : ''">{{ suggestion.source }}</span></template>
|
||||
<template v-if="suggestion.source"><span class="source-badge" :class="suggestion.source.toLowerCase()" :data-publish-date="suggestion.publish_date ? ' · ' + formatPublishDate(suggestion.publish_date, suggestion.pansou_source, suggestion.source) : ''">{{ suggestion.source }}</span></template>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
@ -1964,7 +1966,7 @@
|
||||
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
||||
<small class="text-muted">
|
||||
<a :href="suggestion.shareurl" target="_blank" @click.stop> · {{ suggestion.shareurl.replace(/^https?:\/\/pan\.quark\.cn\/s\//, '') }}</a>
|
||||
<template v-if="suggestion.source"><span class="source-badge" :class="suggestion.source.toLowerCase()" :data-publish-date="suggestion.publish_date ? ' · ' + formatPublishDate(suggestion.publish_date) : ''">{{ suggestion.source }}</span></template>
|
||||
<template v-if="suggestion.source"><span class="source-badge" :class="suggestion.source.toLowerCase()" :data-publish-date="suggestion.publish_date ? ' · ' + formatPublishDate(suggestion.publish_date, suggestion.pansou_source, suggestion.source) : ''">{{ suggestion.source }}</span></template>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
@ -2847,22 +2849,80 @@
|
||||
|
||||
// 获取插件配置的占位符文本
|
||||
getPluginConfigPlaceholder(pluginName, key) {
|
||||
if (pluginName === 'plex' && key === 'quark_root_path') {
|
||||
return '输入夸克根目录相对于 Plex 媒体库目录的路径,多个路径用逗号分隔';
|
||||
} else if (pluginName === 'alist' && key === 'storage_id') {
|
||||
return '输入 AList 服务器夸克存储的 ID,多个 ID 用逗号分隔';
|
||||
}
|
||||
return '';
|
||||
const placeholders = {
|
||||
aria2: {
|
||||
host_port: "Aria2 RPC 地址,如:192.168.1.100:6800",
|
||||
secret: "Aria2 RPC 密钥",
|
||||
dir: "下载目录,需要 Aria2 拥有读写权限,如:/downloads"
|
||||
},
|
||||
alist: {
|
||||
url: "AList 服务器地址,如:http://192.168.1.100:5244",
|
||||
token: "AList 访问令牌,在 AList 的管理 - 设置 - 其他中获取",
|
||||
storage_id: "AList 服务器夸克存储的 ID,多个 ID 用逗号分隔,如:1, 2, 3"
|
||||
},
|
||||
alist_strm: {
|
||||
url: "AList Strm 服务器地址,如:http://192.168.1.100:5000",
|
||||
cookie: "AList Strm 的 Cookie,通过 F12 抓取,关键参数:session=ey***",
|
||||
config_id: "要触发运行的配置 ID,多个 ID 用逗号分隔,如:1, 2, 3"
|
||||
},
|
||||
alist_strm_gen: {
|
||||
url: "AList 服务器地址,如:http://192.168.1.100:5244",
|
||||
token: "AList 访问令牌,在 AList 的管理 - 设置 - 其他中获取",
|
||||
storage_id: "AList 服务器夸克存储的 ID",
|
||||
strm_save_dir: "生成的 strm 文件的保存路径,如:/volume1/media/strm,请确保宿主机路径和容器路径映射一致",
|
||||
strm_replace_host: "strm 文件内链接的主机地址,可选,缺省时使用 url"
|
||||
},
|
||||
plex: {
|
||||
url: "Plex 服务器地址,如:http://192.168.1.100:32400",
|
||||
token: "Plex 访问令牌,通过 F12 抓取(或通过 XML 地址获取)",
|
||||
quark_root_path: "夸克根目录在 Plex 中的路径,多个路径用逗号分隔,如:/volume1/media/quark1, /volume1/media/quark2"
|
||||
},
|
||||
emby: {
|
||||
url: "Emby 服务器地址,如:http://192.168.1.100:8096",
|
||||
token: "Emby 的 API 密钥,在 Emby 的管理 - 高级 - API 密钥中生成"
|
||||
}
|
||||
};
|
||||
|
||||
return placeholders[pluginName]?.[key] || '';
|
||||
},
|
||||
|
||||
// 获取插件配置的帮助文本
|
||||
getPluginConfigHelp(pluginName, key) {
|
||||
if (pluginName === 'plex' && key === 'quark_root_path') {
|
||||
return '多账号支持:多个路径用逗号分隔,顺序与Cookie顺序对应,如:/path1, /path2';
|
||||
} else if (pluginName === 'alist' && key === 'storage_id') {
|
||||
return '多账号支持:多个存储ID用逗号分隔,顺序与Cookie顺序对应,如:1, 2, 3';
|
||||
}
|
||||
return '';
|
||||
const helpTexts = {
|
||||
aria2: {
|
||||
host_port: "Aria2 RPC服务地址,确保网络可达且端口开放",
|
||||
secret: "Aria2 RPC密钥,用于安全认证",
|
||||
dir: "下载文件的保存目录,确保Aria2有读写权限"
|
||||
},
|
||||
alist: {
|
||||
url: "AList服务器地址,用于获取存储信息",
|
||||
token: "AList访问令牌,用于API调用",
|
||||
storage_id: "多账号支持:多个存储ID用逗号分隔,顺序与Cookie顺序对应,如:1, 2, 3"
|
||||
},
|
||||
alist_strm: {
|
||||
url: "AList Strm服务器地址",
|
||||
cookie: "AList Strm的Cookie",
|
||||
config_id: "要触发运行的配置ID,多个ID用逗号分隔,如:1, 2, 3"
|
||||
},
|
||||
alist_strm_gen: {
|
||||
url: "AList服务器地址,用于获取存储信息",
|
||||
token: "AList访问令牌,用于API调用",
|
||||
storage_id: "夸克网盘在AList中的存储ID,在AList的存储管理中查看",
|
||||
strm_save_dir: "生成的strm文件的保存路径,确保有写入权限",
|
||||
strm_replace_host: "strm文件内链接的主机地址,用于替换默认的AList地址"
|
||||
},
|
||||
plex: {
|
||||
url: "Plex服务器地址",
|
||||
token: "Plex访问令牌",
|
||||
quark_root_path: "夸克根目录在Plex中的路径,多个路径用逗号分隔,如:/volume1/media/quark1, /volume1/media/quark2"
|
||||
},
|
||||
emby: {
|
||||
url: "Emby服务器地址",
|
||||
token: "Emby的API密钥"
|
||||
}
|
||||
};
|
||||
|
||||
return helpTexts[pluginName]?.[key] || '';
|
||||
},
|
||||
|
||||
// 获取插件任务配置
|
||||
@ -2898,11 +2958,11 @@
|
||||
auto_delete_quark_files: "是否在添加下载任务后自动删除夸克网盘文件"
|
||||
},
|
||||
alist_strm_gen: {
|
||||
auto_gen: "是否自动生成 strm 文件"
|
||||
auto_gen: "是否自动生成strm文件"
|
||||
},
|
||||
emby: {
|
||||
try_match: "是否尝试匹配",
|
||||
media_id: "媒体ID,当为0时不刷新"
|
||||
try_match: "是否尝试自动匹配媒体",
|
||||
media_id: "指定要刷新的媒体ID,留空则自动匹配,0表示不刷新"
|
||||
}
|
||||
};
|
||||
return helpTexts[pluginName]?.[key] || '';
|
||||
@ -2921,7 +2981,7 @@
|
||||
},
|
||||
emby: {
|
||||
try_match: "",
|
||||
media_id: "输入媒体ID,留空则自动匹配"
|
||||
media_id: "输入媒体 ID,留空则自动匹配,0 表示不刷新"
|
||||
}
|
||||
};
|
||||
return placeholders[pluginName]?.[key] || '';
|
||||
@ -2998,6 +3058,45 @@
|
||||
return "单个任务的插件配置,具体键值由插件定义,查阅Wiki了解详情";
|
||||
},
|
||||
|
||||
// 获取Cookie状态悬停提示信息
|
||||
getCookieStatusTooltip(userInfo) {
|
||||
if (!userInfo) {
|
||||
return "账号未验证,请获取Cookie";
|
||||
}
|
||||
|
||||
// 有昵称且账号激活
|
||||
if (userInfo.nickname && userInfo.is_active) {
|
||||
if (userInfo.has_mparam) {
|
||||
// 第一个账号支持转存和签到,其他账号只支持签到
|
||||
if (userInfo.index === 0) {
|
||||
return "账号已登录,支持转存和签到";
|
||||
} else {
|
||||
return "账号已登录,仅支持签到功能";
|
||||
}
|
||||
} else {
|
||||
// 第一个账号支持转存,其他账号不支持任何功能
|
||||
if (userInfo.index === 0) {
|
||||
return "账号已登录,仅支持转存功能";
|
||||
} else {
|
||||
return "账号已登录,不支持转存和签到";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 有昵称但账号未激活
|
||||
if (userInfo.nickname && !userInfo.is_active) {
|
||||
return "账号已失效,请重新获取Cookie";
|
||||
}
|
||||
|
||||
// 没有昵称但有移动端参数
|
||||
if (!userInfo.nickname && userInfo.has_mparam) {
|
||||
return "仅支持签到功能";
|
||||
}
|
||||
|
||||
// 没有昵称也没有移动端参数
|
||||
return "账号未登录,请获取Cookie";
|
||||
},
|
||||
|
||||
fetchUserInfo() {
|
||||
// 获取所有cookie对应的用户信息
|
||||
axios.get('/get_user_info')
|
||||
@ -6625,21 +6724,64 @@
|
||||
return isNaN(ts) ? 0 : ts;
|
||||
},
|
||||
// 规范化资源发布日期展示:将 ISO 格式(含 T/Z/偏移)转为 "YYYY-MM-DD HH:mm:ss"
|
||||
formatPublishDate(value) {
|
||||
formatPublishDate(value, pansouSource = null, source = null) {
|
||||
if (!value) return '';
|
||||
const s = String(value).trim();
|
||||
|
||||
// 已是标准格式则直接返回
|
||||
if (/^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}$/.test(s)) return s;
|
||||
|
||||
// 优先匹配 ISO 主体部分
|
||||
const m = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})/.exec(s);
|
||||
if (m) {
|
||||
const [, y, mo, d, h, mi, se] = m;
|
||||
// 判断是否需要+8小时:
|
||||
// 1. 如果是PanSou的tg来源,需要+8小时
|
||||
// 2. 如果是CloudSaver来源,需要+8小时
|
||||
// 3. 其他来源都不+8小时
|
||||
const needAdd8Hours = (pansouSource && pansouSource.startsWith('tg:')) ||
|
||||
(source === 'CloudSaver');
|
||||
|
||||
if (needAdd8Hours) {
|
||||
// 创建Date对象并加上8小时
|
||||
const date = new Date(Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi), Number(se));
|
||||
date.setHours(date.getHours() + 8);
|
||||
return date.getFullYear() + '-' +
|
||||
String(date.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(date.getDate()).padStart(2, '0') + ' ' +
|
||||
String(date.getHours()).padStart(2, '0') + ':' +
|
||||
String(date.getMinutes()).padStart(2, '0') + ':' +
|
||||
String(date.getSeconds()).padStart(2, '0');
|
||||
}
|
||||
return `${y}-${mo}-${d} ${h}:${mi}:${se}`;
|
||||
}
|
||||
|
||||
// 回退:简单替换T为空格并去除尾部Z/时区偏移
|
||||
let out = s.replace('T', ' ');
|
||||
out = out.replace(/Z$/i, '');
|
||||
out = out.replace(/([+-]\d{2}:?\d{2})$/i, '');
|
||||
|
||||
// 判断是否需要+8小时
|
||||
const needAdd8Hours = (pansouSource && pansouSource.startsWith('tg:')) ||
|
||||
(source === 'CloudSaver');
|
||||
|
||||
if (needAdd8Hours) {
|
||||
try {
|
||||
const date = new Date(out);
|
||||
if (!isNaN(date.getTime())) {
|
||||
date.setHours(date.getHours() + 8);
|
||||
return date.getFullYear() + '-' +
|
||||
String(date.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(date.getDate()).padStart(2, '0') + ' ' +
|
||||
String(date.getHours()).padStart(2, '0') + ':' +
|
||||
String(date.getMinutes()).padStart(2, '0') + ':' +
|
||||
String(date.getSeconds()).padStart(2, '0');
|
||||
}
|
||||
} catch (e) {
|
||||
// 如果转换失败,返回原始处理结果
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
},
|
||||
changeFolderPage(page) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user