feat(web): merge search filters into one row and tidy episode versions (POLISH-1)
- H&R becomes a quick-filter facet (yes/no/unknown, with counts) on search results and the site resources tab; drop the old free/H&R selects, keep size range and sort on the same row (mobile: drawer), single clear button - legacy free/hr URL params migrate into quick filters; stored selections keep the same localStorage key - search timing and query terms move into TechInfo - library episode versions: click the whole row to expand, versions listed as sibling rows, details only under the selected version, raw values in TechInfo - slot comparison gaps get reason codes (matchGapText in contracts reasons.ts): "可换成 2160p", "缺 HDR 版本" Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
12bc4664b6
commit
71b129d85a
19 files changed
+668
-290
No files matched your search
@@ -72,7 +72,10 @@ export const UnitMatch = z.object({
|
||||
slotId: Id.nullable(),
|
||||
state: UnitMatchState,
|
||||
assetId: Id.nullable(),
|
||||
/** 比对说明原文(界面放进技术信息) */
|
||||
reasons: z.array(z.string()),
|
||||
/** 差距原因码(`version_resolution:2160p` …),人话见 reasons.ts matchGapText */
|
||||
codes: z.array(z.string()),
|
||||
});
|
||||
export type UnitMatch = z.infer<typeof UnitMatch>;
|
||||
export const MatchCounts = z.object({ satisfied: NonNegInt, missing: NonNegInt, upgradeable: NonNegInt });
|
||||
|
||||
@@ -73,3 +73,51 @@ export function reasonSentence(raw: string | null | undefined, fallback = "处
|
||||
|
||||
/** 看起来像内部码(小写下划线 / 冒号)而不是给人看的句子 */
|
||||
export const looksLikeCode = (v: string | null | undefined) => !!v && /^[a-z0-9_]+([:.,][a-z0-9_:.,-]*)?$/.test(v);
|
||||
|
||||
// ── 入库版本与收录版本的差距(POLISH-1)──
|
||||
// 码由 @umt/domain matchVersions 给出,冒号后是参数:目标分辨率 / 来源 / 编码 / 字幕语言。
|
||||
|
||||
const SOURCE_NAME: Record<string, string> = { REMUX: "Remux", BluRay: "蓝光压制", "BluRay-Disc": "原盘" };
|
||||
const LANG_NAME: Record<string, string> = { "zh-Hans": "简中", "zh-Hant": "繁中", zh: "中文", en: "英文", ja: "日文" };
|
||||
|
||||
const GAPS: Record<string, (p: string) => string> = {
|
||||
version_resolution: (p) => (p ? `可换成 ${p}` : "分辨率不符合收录版本"),
|
||||
version_resolution_upgrade: (p) => (p ? `可换成 ${p}` : "分辨率还能更高"),
|
||||
version_source: (p) => (p ? `可换成 ${SOURCE_NAME[p] ?? p}` : "来源不符合收录版本"),
|
||||
version_source_upgrade: () => "可换成更好的来源",
|
||||
version_encode: () => "收录版本要蓝光压制",
|
||||
version_hdr: (p) => `缺 ${p || "HDR"} 版本`,
|
||||
version_sdr: () => "收录版本要 SDR",
|
||||
version_hdr_excluded: () => "HDR 格式被收录版本排除",
|
||||
version_codec: (p) => (p ? `可换成 ${p} 编码` : "编码被收录版本排除"),
|
||||
version_subtitles: (p) => {
|
||||
const langs = p
|
||||
.split("/")
|
||||
.filter(Boolean)
|
||||
.map((l) => LANG_NAME[l] ?? l)
|
||||
.join("/");
|
||||
if (!langs) return "缺字幕";
|
||||
return /^[\x20-\x7e]+$/.test(langs) ? `缺 ${langs} 字幕` : `缺${langs}字幕`;
|
||||
},
|
||||
version_size: () => "体积不在收录范围",
|
||||
version_edition: () => "剪辑版本不符合",
|
||||
version_group: () => "制作组被排除",
|
||||
version_content_type: () => "内容类型不符合",
|
||||
version_rules: () => "不符合收录版本的规则",
|
||||
version_spec_unknown: () => "认不出规格",
|
||||
version_priority: () => "还有更好的版本可换",
|
||||
};
|
||||
|
||||
/** 一个差距码 → 短句("可换成 2160p"、"缺 HDR 版本");不认识的码返回 null */
|
||||
export function matchGapText(code: string | null | undefined): string | null {
|
||||
if (!code) return null;
|
||||
const i = code.indexOf(":");
|
||||
const head = i < 0 ? code : code.slice(0, i);
|
||||
const fn = GAPS[head];
|
||||
return fn ? fn(i < 0 ? "" : code.slice(i + 1)) : null;
|
||||
}
|
||||
|
||||
/** 一组差距码 → 去重后的短句列表 */
|
||||
export function matchGapTexts(codes: readonly string[] | null | undefined): string[] {
|
||||
return [...new Set((codes ?? []).map(matchGapText).filter((t): t is string => !!t))];
|
||||
}
|
||||
@@ -12,7 +12,7 @@ export type FacetCount = z.infer<typeof FacetCount>;
|
||||
* 快速筛选参数 qf:`维度:值,值;维度:值`,值经 encodeURIComponent。
|
||||
* 维度:resolution(2160p/1080p/720p/other)、videoCodec(H.265/H.264/AV1/other)、
|
||||
* releaseType(disc/remux/encode/webdl/hdtv/other)、site(站点 ID)、promotion(free/2x/50%/other/none)、
|
||||
* hdr(DV/HDR10/HDR10+/other/SDR)、releaseGroup(制作组名,不分大小写)、season(季号)、episode(集号 / pack 整季)。
|
||||
* hdr(DV/HDR10/HDR10+/other/SDR)、hr(yes/no/unknown)、releaseGroup(制作组名,不分大小写)、season(季号)、episode(集号 / pack 整季)。
|
||||
*/
|
||||
export const QuickFilterParam = z.string().max(4000).optional();
|
||||
|
||||
@@ -24,6 +24,8 @@ export const ReleaseQuickFacets = z.object({
|
||||
site: z.array(FacetCount),
|
||||
promotion: z.array(FacetCount),
|
||||
hdr: z.array(FacetCount),
|
||||
/** H&R:有 / 无 / 未知(POLISH-1) */
|
||||
hr: z.array(FacetCount),
|
||||
/** 计数最多的前 20 个 + 已选的 */
|
||||
releaseGroup: z.array(FacetCount),
|
||||
/** 没有剧集资源时为空 */
|
||||
|
||||
Reference in new issue
Block a user