mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-16 09:20:43 +08:00
修复资源搜索结果发布日期时区(显示)错误的问题
This commit is contained in:
parent
6fd9683ff9
commit
5c50453acd
24
app/run.py
24
app/run.py
@ -1061,19 +1061,33 @@ def get_task_suggestions():
|
|||||||
seen_fingerprints.add(fingerprint)
|
seen_fingerprints.add(fingerprint)
|
||||||
dedup.append(item)
|
dedup.append(item)
|
||||||
|
|
||||||
# 全局时间排序:所有来源的结果混合排序,按时间倒序(最新的在前)
|
# 仅在排序时对多种格式进行解析(优先解析 YYYY-MM-DD HH:mm:ss,其次 ISO)
|
||||||
if dedup:
|
if dedup:
|
||||||
def parse_datetime_for_sort(item):
|
def parse_datetime_for_sort(item):
|
||||||
"""解析时间字段,返回可比较的时间戳(统一以 publish_date 为准)"""
|
"""解析时间字段,返回可比较的时间戳(统一以 publish_date 为准)"""
|
||||||
datetime_str = item.get("publish_date")
|
datetime_str = item.get("publish_date")
|
||||||
if not datetime_str:
|
if not datetime_str:
|
||||||
return 0 # 没有时间的排在最后
|
return 0 # 没有时间的排在最后
|
||||||
|
from datetime import datetime
|
||||||
|
s = str(datetime_str).strip()
|
||||||
|
# 优先解析标准显示格式
|
||||||
try:
|
try:
|
||||||
from datetime import datetime
|
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
|
||||||
# 尝试解析格式: 2025-01-01 12:00:00
|
|
||||||
dt = datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
|
|
||||||
return dt.timestamp()
|
return dt.timestamp()
|
||||||
except:
|
except Exception:
|
||||||
|
pass
|
||||||
|
# 补充解析仅日期格式
|
||||||
|
try:
|
||||||
|
dt = datetime.strptime(s, "%Y-%m-%d")
|
||||||
|
return dt.timestamp()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# 其次尝试 ISO(支持 Z/偏移)
|
||||||
|
try:
|
||||||
|
s2 = s.replace('Z', '+00:00')
|
||||||
|
dt = datetime.fromisoformat(s2)
|
||||||
|
return dt.timestamp()
|
||||||
|
except Exception:
|
||||||
return 0 # 解析失败排在最后
|
return 0 # 解析失败排在最后
|
||||||
|
|
||||||
# 按时间倒序排序(最新的在前)
|
# 按时间倒序排序(最新的在前)
|
||||||
|
|||||||
@ -106,6 +106,19 @@ class CloudSaver:
|
|||||||
pattern_title = r"(名称|标题)[::]?(.*)"
|
pattern_title = r"(名称|标题)[::]?(.*)"
|
||||||
pattern_content = r"(描述|简介)[::]?(.*)(链接|标签)"
|
pattern_content = r"(描述|简介)[::]?(.*)(链接|标签)"
|
||||||
clean_results = []
|
clean_results = []
|
||||||
|
# 工具:移除标题中的链接(http/https 以及常见裸域名的夸克分享)
|
||||||
|
def strip_links(text: str) -> str:
|
||||||
|
if not isinstance(text, str):
|
||||||
|
return text
|
||||||
|
s = text
|
||||||
|
import re
|
||||||
|
# 去除 http/https 链接
|
||||||
|
s = re.sub(r"https?://\S+", "", s)
|
||||||
|
# 去除裸域夸克分享链接(不带协议的 pan.quark.cn/...)
|
||||||
|
s = re.sub(r"\bpan\.quark\.cn/\S+", "", s)
|
||||||
|
# 收尾多余空白和分隔符
|
||||||
|
s = re.sub(r"\s+", " ", s).strip(" -|·,,::;;" + " ")
|
||||||
|
return s.strip()
|
||||||
link_array = []
|
link_array = []
|
||||||
for channel in search_results:
|
for channel in search_results:
|
||||||
for item in channel.get("list", []):
|
for item in channel.get("list", []):
|
||||||
@ -117,6 +130,8 @@ class CloudSaver:
|
|||||||
if match := re.search(pattern_title, title, re.DOTALL):
|
if match := re.search(pattern_title, title, re.DOTALL):
|
||||||
title = match.group(2)
|
title = match.group(2)
|
||||||
title = title.replace("&", "&").strip()
|
title = title.replace("&", "&").strip()
|
||||||
|
# 标题去除链接
|
||||||
|
title = strip_links(title)
|
||||||
# 清洗内容
|
# 清洗内容
|
||||||
content = item.get("content", "")
|
content = item.get("content", "")
|
||||||
if match := re.search(pattern_content, content, re.DOTALL):
|
if match := re.search(pattern_content, content, re.DOTALL):
|
||||||
@ -125,9 +140,8 @@ class CloudSaver:
|
|||||||
content = content.replace("</mark>", "")
|
content = content.replace("</mark>", "")
|
||||||
content = content.strip()
|
content = content.strip()
|
||||||
# 获取发布时间 - 采用与原始实现一致的方式
|
# 获取发布时间 - 采用与原始实现一致的方式
|
||||||
pubdate = item.get("pubDate", "") # 使用 pubDate 字段
|
pubdate_iso = item.get("pubDate", "") # 原始时间字符串(可能为 ISO 或已是北京时间)
|
||||||
if pubdate:
|
pubdate = pubdate_iso # 不做时区转换,保留来源原始时间
|
||||||
pubdate = self._iso_to_cst(pubdate) # 转换为中国标准时间
|
|
||||||
# 链接去重
|
# 链接去重
|
||||||
if link.get("link") not in link_array:
|
if link.get("link") not in link_array:
|
||||||
link_array.append(link.get("link"))
|
link_array.append(link.get("link"))
|
||||||
@ -136,7 +150,7 @@ class CloudSaver:
|
|||||||
"shareurl": link.get("link"),
|
"shareurl": link.get("link"),
|
||||||
"taskname": title,
|
"taskname": title,
|
||||||
"content": content,
|
"content": content,
|
||||||
"datetime": pubdate, # 使用 datetime 字段名,与原始实现一致
|
"datetime": pubdate, # 显示用时间
|
||||||
"tags": item.get("tags", []),
|
"tags": item.get("tags", []),
|
||||||
"channel": item.get("channelId", ""),
|
"channel": item.get("channelId", ""),
|
||||||
"source": "CloudSaver"
|
"source": "CloudSaver"
|
||||||
@ -147,24 +161,6 @@ class CloudSaver:
|
|||||||
# 返回原始顺序的结果,由全局排序函数统一处理
|
# 返回原始顺序的结果,由全局排序函数统一处理
|
||||||
return clean_results
|
return clean_results
|
||||||
|
|
||||||
def _iso_to_cst(self, iso_time_str: str) -> str:
|
|
||||||
"""将 ISO 格式的时间字符串转换为 CST(China Standard Time) 时间并格式化为 %Y-%m-%d %H:%M:%S 格式
|
|
||||||
|
|
||||||
Args:
|
|
||||||
iso_time_str (str): ISO 格式时间字符串
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: CST(China Standard Time) 时间字符串
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
from datetime import datetime, timezone, timedelta
|
|
||||||
dt = datetime.fromisoformat(iso_time_str)
|
|
||||||
dt_cst = dt.astimezone(timezone(timedelta(hours=8)))
|
|
||||||
return dt_cst.strftime("%Y-%m-%d %H:%M:%S") if dt_cst.year >= 1970 else ""
|
|
||||||
except:
|
|
||||||
return iso_time_str # 转换失败时返回原始字符串
|
|
||||||
|
|
||||||
|
|
||||||
# 测试示例
|
# 测试示例
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# 创建CloudSaver实例
|
# 创建CloudSaver实例
|
||||||
|
|||||||
@ -57,6 +57,16 @@ class PanSou:
|
|||||||
|
|
||||||
# 解析结果:优先 results,然后 merged_by_type
|
# 解析结果:优先 results,然后 merged_by_type
|
||||||
cleaned = []
|
cleaned = []
|
||||||
|
# 工具:移除标题中的链接
|
||||||
|
def strip_links(text: str) -> str:
|
||||||
|
if not isinstance(text, str):
|
||||||
|
return text
|
||||||
|
s = text
|
||||||
|
import re
|
||||||
|
s = re.sub(r"https?://\S+", "", s)
|
||||||
|
s = re.sub(r"\bpan\.quark\.cn/\S+", "", s)
|
||||||
|
s = re.sub(r"\s+", " ", s).strip(" -|·,,::;;" + " ")
|
||||||
|
return s.strip()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 1) results: 主要结果数组,每个结果包含 title 和 links
|
# 1) results: 主要结果数组,每个结果包含 title 和 links
|
||||||
@ -68,6 +78,7 @@ class PanSou:
|
|||||||
|
|
||||||
# 从 result_item 获取标题、内容和发布日期
|
# 从 result_item 获取标题、内容和发布日期
|
||||||
title = result_item.get("title", "")
|
title = result_item.get("title", "")
|
||||||
|
title = strip_links(title)
|
||||||
content = result_item.get("content", "")
|
content = result_item.get("content", "")
|
||||||
datetime_str = result_item.get("datetime", "") # 获取发布日期
|
datetime_str = result_item.get("datetime", "") # 获取发布日期
|
||||||
|
|
||||||
@ -84,7 +95,7 @@ class PanSou:
|
|||||||
"content": content,
|
"content": content,
|
||||||
"shareurl": url,
|
"shareurl": url,
|
||||||
"tags": [link_type] if link_type else (result_item.get("tags", []) or []),
|
"tags": [link_type] if link_type else (result_item.get("tags", []) or []),
|
||||||
"publish_date": datetime_str, # 添加发布日期字段
|
"publish_date": datetime_str, # 原始时间(可能是 ISO)
|
||||||
"source": "PanSou" # 添加来源标识
|
"source": "PanSou" # 添加来源标识
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -99,6 +110,7 @@ class PanSou:
|
|||||||
# 从 merged_by_type 获取链接信息
|
# 从 merged_by_type 获取链接信息
|
||||||
url = link.get("url", "")
|
url = link.get("url", "")
|
||||||
note = link.get("note", "") # 使用 note 字段作为标题
|
note = link.get("note", "") # 使用 note 字段作为标题
|
||||||
|
note = strip_links(note)
|
||||||
datetime_str = link.get("datetime", "") # 获取发布日期
|
datetime_str = link.get("datetime", "") # 获取发布日期
|
||||||
if url:
|
if url:
|
||||||
cleaned.append({
|
cleaned.append({
|
||||||
@ -106,7 +118,7 @@ class PanSou:
|
|||||||
"content": note, # 如果没有 content,使用 note
|
"content": note, # 如果没有 content,使用 note
|
||||||
"shareurl": url,
|
"shareurl": url,
|
||||||
"tags": [cloud_type] if cloud_type else [],
|
"tags": [cloud_type] if cloud_type else [],
|
||||||
"publish_date": datetime_str, # 添加发布日期字段
|
"publish_date": datetime_str, # 原始时间
|
||||||
"source": "PanSou" # 添加来源标识
|
"source": "PanSou" # 添加来源标识
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -119,7 +131,7 @@ class PanSou:
|
|||||||
"content": item.get("content", ""),
|
"content": item.get("content", ""),
|
||||||
"shareurl": item.get("url", ""),
|
"shareurl": item.get("url", ""),
|
||||||
"tags": item.get("tags", []) or [],
|
"tags": item.get("tags", []) or [],
|
||||||
"publish_date": item.get("datetime", ""), # 添加发布日期字段
|
"publish_date": item.get("datetime", ""), # 原始时间
|
||||||
"source": "PanSou" # 添加来源标识
|
"source": "PanSou" # 添加来源标识
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -153,36 +165,4 @@ class PanSou:
|
|||||||
seen_urls.add(url)
|
seen_urls.add(url)
|
||||||
unique_results.append(item)
|
unique_results.append(item)
|
||||||
|
|
||||||
# 按发布日期排序:最新的在前
|
|
||||||
def parse_datetime(datetime_str):
|
|
||||||
"""解析日期时间字符串,返回可比较的时间戳"""
|
|
||||||
if not datetime_str:
|
|
||||||
return 0 # 没有日期的排在最后
|
|
||||||
try:
|
|
||||||
from datetime import datetime, timezone, timedelta
|
|
||||||
# 尝试解析 ISO 8601 格式: 2025-07-28T20:43:27Z
|
|
||||||
dt = datetime.fromisoformat(datetime_str.replace('Z', '+00:00'))
|
|
||||||
return dt.timestamp()
|
|
||||||
except:
|
|
||||||
return 0 # 解析失败排在最后
|
|
||||||
|
|
||||||
def convert_to_cst(datetime_str):
|
|
||||||
"""将 ISO 时间转换为中国标准时间 (CST)"""
|
|
||||||
if not datetime_str:
|
|
||||||
return ""
|
|
||||||
try:
|
|
||||||
from datetime import datetime, timezone, timedelta
|
|
||||||
dt = datetime.fromisoformat(datetime_str.replace('Z', '+00:00'))
|
|
||||||
dt_cst = dt.astimezone(timezone(timedelta(hours=8)))
|
|
||||||
return dt_cst.strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
except:
|
|
||||||
return datetime_str # 转换失败时返回原始字符串
|
|
||||||
|
|
||||||
# 转换时间为中国标准时间格式
|
|
||||||
for item in unique_results:
|
|
||||||
if item.get("publish_date"):
|
|
||||||
item["publish_date"] = convert_to_cst(item["publish_date"])
|
|
||||||
|
|
||||||
# 注意:排序逻辑已移至全局,这里不再进行内部排序
|
|
||||||
# 返回原始顺序的结果,由全局排序函数统一处理
|
|
||||||
return {"success": True, "data": unique_results}
|
return {"success": True, "data": unique_results}
|
||||||
|
|||||||
@ -587,7 +587,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<h2 style="display: inline-block; font-size: 1.5rem;">搜索来源</h2>
|
<h2 style="display: inline-block; font-size: 1.5rem;">搜索来源</h2>
|
||||||
<span class="badge badge-pill badge-light">
|
<span class="badge badge-pill badge-light">
|
||||||
<a href="https://github.com/x1ao4/quark-auto-save-x/wiki/CloudSaver搜索源" target="_blank"><i class="bi bi-question-circle"></i></a>
|
<a href="https://github.com/x1ao4/quark-auto-save-x/wiki/资源搜索" target="_blank"><i class="bi bi-question-circle"></i></a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1026,7 +1026,7 @@
|
|||||||
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
||||||
<small class="text-muted">
|
<small class="text-muted">
|
||||||
<a :href="suggestion.shareurl" target="_blank" @click.stop> · {{ suggestion.shareurl.replace(/^https?:\/\/pan\.quark\.cn\/s\//, '') }}</a>
|
<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 ? ' · ' + 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.source }}</span></template>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1954,7 +1954,7 @@
|
|||||||
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
||||||
<small class="text-muted">
|
<small class="text-muted">
|
||||||
<a :href="suggestion.shareurl" target="_blank" @click.stop> · {{ suggestion.shareurl.replace(/^https?:\/\/pan\.quark\.cn\/s\//, '') }}</a>
|
<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 ? ' · ' + 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.source }}</span></template>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -4329,11 +4329,7 @@
|
|||||||
const batchSize = 5;
|
const batchSize = 5;
|
||||||
|
|
||||||
// 解析时间用于排序(降序:最新在前)
|
// 解析时间用于排序(降序:最新在前)
|
||||||
const getItemTs = (item) => {
|
const getItemTs = (item) => this.parsePublishTs(item && item.publish_date);
|
||||||
const raw = item.publish_date || '';
|
|
||||||
const ts = Date.parse(raw);
|
|
||||||
return isNaN(ts) ? 0 : ts;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理单个链接的函数
|
// 处理单个链接的函数
|
||||||
const processLink = (link) => {
|
const processLink = (link) => {
|
||||||
@ -4462,11 +4458,7 @@
|
|||||||
this.smart_param._hasShownInterimResults = false;
|
this.smart_param._hasShownInterimResults = false;
|
||||||
|
|
||||||
// 结束前做一次排序,确保最终顺序正确
|
// 结束前做一次排序,确保最终顺序正确
|
||||||
const getItemTs = (item) => {
|
const getItemTs = (item) => this.parsePublishTs(item && item.publish_date);
|
||||||
const raw = item.publish_date || '';
|
|
||||||
const ts = Date.parse(raw);
|
|
||||||
return isNaN(ts) ? 0 : ts;
|
|
||||||
};
|
|
||||||
validResults.sort((a, b) => getItemTs(b) - getItemTs(a));
|
validResults.sort((a, b) => getItemTs(b) - getItemTs(a));
|
||||||
|
|
||||||
// 更新搜索结果
|
// 更新搜索结果
|
||||||
@ -6535,6 +6527,44 @@
|
|||||||
const seconds = String(d.getSeconds()).padStart(2, '0');
|
const seconds = String(d.getSeconds()).padStart(2, '0');
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
},
|
},
|
||||||
|
// 统一解析资源发布日期为时间戳
|
||||||
|
parsePublishTs(raw) {
|
||||||
|
if (!raw) return 0;
|
||||||
|
const s = String(raw).trim();
|
||||||
|
// YYYY-MM-DD HH:mm:ss
|
||||||
|
let m = /^\s*(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})\s*$/.exec(s);
|
||||||
|
if (m) {
|
||||||
|
const [, y, mo, d, h, mi, se] = m;
|
||||||
|
return new Date(Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi), Number(se)).getTime();
|
||||||
|
}
|
||||||
|
// YYYY-MM-DD
|
||||||
|
m = /^\s*(\d{4})-(\d{2})-(\d{2})\s*$/.exec(s);
|
||||||
|
if (m) {
|
||||||
|
const [, y, mo, d] = m;
|
||||||
|
return new Date(Number(y), Number(mo) - 1, Number(d), 0, 0, 0).getTime();
|
||||||
|
}
|
||||||
|
// ISO 回退
|
||||||
|
const ts = Date.parse(s);
|
||||||
|
return isNaN(ts) ? 0 : ts;
|
||||||
|
},
|
||||||
|
// 规范化资源发布日期展示:将 ISO 格式(含 T/Z/偏移)转为 "YYYY-MM-DD HH:mm:ss"
|
||||||
|
formatPublishDate(value) {
|
||||||
|
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;
|
||||||
|
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, '');
|
||||||
|
return out;
|
||||||
|
},
|
||||||
changeFolderPage(page) {
|
changeFolderPage(page) {
|
||||||
if (page < 1) page = 1;
|
if (page < 1) page = 1;
|
||||||
if (page > this.fileManager.totalPages) page = this.fileManager.totalPages;
|
if (page > this.fileManager.totalPages) page = this.fileManager.totalPages;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user