feat(web): simplify subscription and manual download flows and sync UHD styles
This commit is contained in:
1 parent
27c0f27434
commit
c3c4456913
33 files changed
+1083
-633
No files matched your search
@@ -1,5 +1,12 @@
|
||||
import type { FileRef, OrganizeSourceInput, OrganizeSourceView } from "@umt/contracts";
|
||||
import {
|
||||
AcquisitionPolicyScope,
|
||||
type FileRef,
|
||||
type OrganizeSourceInput,
|
||||
type OrganizeSourceView,
|
||||
} from "@umt/contracts";
|
||||
import { selectAcquisitionPolicy } from "@umt/domain";
|
||||
import { cloneElement, type ReactElement, useId, useState } from "react";
|
||||
import { Link } from "react-router";
|
||||
import { toast } from "sonner";
|
||||
import { filesErrorText, useFileRoots } from "~/api/files.ts";
|
||||
import { downloaders } from "~/api/instances.ts";
|
||||
@@ -11,6 +18,7 @@ import {
|
||||
useSaveClassification,
|
||||
useSaveOrganizeSource,
|
||||
} from "~/api/organize-sources.ts";
|
||||
import { usePolicyRevisions } from "~/api/policies.ts";
|
||||
import { openAgentDrawer } from "~/components/agent/agent-store.ts";
|
||||
import { useListPagination } from "~/components/console/use-list-pagination.ts";
|
||||
import { DirectoryDialog } from "~/components/files/directory-dialog.tsx";
|
||||
@@ -39,6 +47,20 @@ function Field({ label, children }: { label: string; children: ReactElement<{ id
|
||||
export function ClassificationSection() {
|
||||
const config = useClassification();
|
||||
const save = useSaveClassification();
|
||||
const policies = usePolicyRevisions({ status: "published" });
|
||||
const available = (policies.data?.items ?? [])
|
||||
.filter((p) => p.kind === "acquisition" && Array.isArray(p.rules.defaultSlots) && p.rules.defaultSlots.length)
|
||||
.sort((a, b) => (b.publishedAt ?? "").localeCompare(a.publishedAt ?? "") || a.policyKey.localeCompare(b.policyKey))
|
||||
.map((p) => ({ ...p, scope: AcquisitionPolicyScope.parse(p.scope) }));
|
||||
const classificationPolicies = Object.entries(config.data?.categories ?? {}).flatMap(([type, names]) =>
|
||||
names.flatMap((category) =>
|
||||
(type === "movie" ? ["movie" as const] : ["series" as const, "variety" as const]).map((mediaKind) => ({
|
||||
category,
|
||||
mediaKind,
|
||||
policy: selectAcquisitionPolicy(available, { mediaKind, category }),
|
||||
})),
|
||||
),
|
||||
);
|
||||
const [draft, setDraft] = useState<string | null>(null);
|
||||
const yaml = draft ?? config.data?.yaml ?? "";
|
||||
const dirty = draft !== null && draft !== config.data?.yaml;
|
||||
@@ -85,6 +107,43 @@ export function ClassificationSection() {
|
||||
ID)、production_countries(电影制作国家)、origin_country(剧集来源国家)、release_year(发行年份,支持
|
||||
2020-2025 范围)。
|
||||
</p>
|
||||
{classificationPolicies.length ? (
|
||||
<div className="mt-4 overflow-x-auto">
|
||||
<p className="mb-2 text-caption text-muted-foreground">
|
||||
当前已保存分类的默认收录策略;在收录策略的适用分类中调整。
|
||||
</p>
|
||||
<table className="w-full text-left text-caption">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="py-2">分类</th>
|
||||
<th>作品类型</th>
|
||||
<th>默认策略</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{classificationPolicies.map(({ category, mediaKind, policy }) => (
|
||||
<tr key={`${mediaKind}:${category}`} className="border-t border-border/60">
|
||||
<td className="py-2">{category}</td>
|
||||
<td>{mediaKind === "movie" ? "电影" : mediaKind === "variety" ? "综艺" : "剧集"}</td>
|
||||
<td>
|
||||
{policy ? (
|
||||
<Link
|
||||
className="underline underline-offset-2"
|
||||
to={`/console/rules?tab=policies&open=${policy.id}`}
|
||||
>
|
||||
{policy.title} ·{" "}
|
||||
{Array.isArray(policy.rules.defaultSlots) ? policy.rules.defaultSlots.length : 0} 个保留版本
|
||||
</Link>
|
||||
) : (
|
||||
"默认版本"
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : null}
|
||||
<SaveBar dirty={dirty}>
|
||||
{dirty ? (
|
||||
<Button variant="ghost" size="sm" disabled={save.isPending} onClick={() => setDraft(null)}>
|
||||
|
||||
Reference in new issue
Block a user