diff --git a/api/src/controllers/videos.ts b/api/src/controllers/videos.ts index ddfdd073..e6244159 100644 --- a/api/src/controllers/videos.ts +++ b/api/src/controllers/videos.ts @@ -3,6 +3,7 @@ import { desc, eq, gt, + inArray, isNotNull, lt, max, @@ -11,8 +12,7 @@ import { or, type SQL, sql, - inArray, - WithSubquery, + type WithSubquery, } from "drizzle-orm"; import { alias } from "drizzle-orm/pg-core"; import { Elysia, t } from "elysia"; @@ -33,7 +33,6 @@ import { jsonbBuildObject, jsonbObjectAgg, sqlarr, - unnest, } from "~/db/utils"; import type { Entry } from "~/models/entry"; import { KError } from "~/models/error"; @@ -82,7 +81,7 @@ const videoSort = Sort( ], }, { - default: ["entry", "path"], + default: ["path"], tablePk: videos.pk, }, ); diff --git a/biome.json b/biome.json index 79b45ac7..5123d159 100644 --- a/biome.json +++ b/biome.json @@ -28,7 +28,8 @@ "suspicious": { "noExplicitAny": "off", "noArrayIndexKey": "off", - "noTemplateCurlyInString": "off" + "noTemplateCurlyInString": "off", + "noDuplicateCustomProperties": "off" }, "security": { "noDangerouslySetInnerHtml": "off" @@ -44,16 +45,8 @@ "useSortedClasses": { "level": "warn", "options": { - "attributes": [ - "classList" - ], - "functions": [ - "clsx", - "cva", - "cn", - "tw", - "tw.*" - ] + "attributes": ["classList"], + "functions": ["clsx", "cva", "cn", "tw", "tw.*"] } } } diff --git a/front/public/translations/en.json b/front/public/translations/en.json index c7424a2f..e8dac9c8 100644 --- a/front/public/translations/en.json +++ b/front/public/translations/en.json @@ -45,13 +45,17 @@ "season": "Season {{number}}", "multiVideos": "Multiples video files available", "videosCount": "{{number}} videos", - "videos-map": "Edit video mappings", - "videos-map-none": "NONE", - "videos-map-add": "Add another video", - "videos-map-delete": "Remove video from serie", - "videos-map-validate": "Validate guess and add video to serie", - "videos-map-no-guess": "This video was guessed to be part of this serie but we don't know which episode", - "videos-map-related": "Added videos related to the title {{title}}" + "videos-map": "Edit video mappings" + }, + "videos-map": { + "none": "NONE", + "add": "Add another video", + "delete": "Remove video from serie", + "validate": "Validate guess and add video to serie", + "no-guess": "This video was guessed to be part of this serie but we don't know which episode", + "related": "Added videos related to the title {{title}}", + "sort-path": "Path", + "sort-entry": "Episode order" }, "browse": { "mediatypekey": { diff --git a/front/src/primitives/button.tsx b/front/src/primitives/button.tsx index d380e8b4..c90c6bf2 100644 --- a/front/src/primitives/button.tsx +++ b/front/src/primitives/button.tsx @@ -1,9 +1,5 @@ import type { ComponentProps, ComponentType, Ref } from "react"; -import { - type Falsy, - type PressableProps, - View, -} from "react-native"; +import { type Falsy, type PressableProps, View } from "react-native"; import { cn } from "~/utils"; import { Icon } from "./icons"; import { PressableFeedback } from "./links"; diff --git a/front/src/ui/admin/videos-modal.tsx b/front/src/ui/admin/videos-modal.tsx deleted file mode 100644 index 767f60d4..00000000 --- a/front/src/ui/admin/videos-modal.tsx +++ /dev/null @@ -1,233 +0,0 @@ -import Check from "@material-symbols/svg-400/rounded/check-fill.svg"; -import Close from "@material-symbols/svg-400/rounded/close-fill.svg"; -import Question from "@material-symbols/svg-400/rounded/question_mark-fill.svg"; -import LibraryAdd from "@material-symbols/svg-400/rounded/library_add-fill.svg"; -import { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { View } from "react-native"; -import { entryDisplayNumber } from "~/components/entries"; -import { Entry, FullVideo, type Page } from "~/models"; -import { - Button, - ComboBox, - IconButton, - Modal, - P, - Skeleton, - tooltip, -} from "~/primitives"; -import { - InfiniteFetch, - type QueryIdentifier, - useFetch, - useMutation, -} from "~/query"; -import { useQueryState } from "~/utils"; -import { Header } from "../details/header"; -import { uniqBy } from "~/utils"; - -export const VideosModal = () => { - const [slug] = useQueryState("slug", undefined!); - const { data } = useFetch(Header.query("serie", slug)); - const { t } = useTranslation(); - const [titles, setTitles] = useState([]); - - const { mutateAsync: editLinks } = useMutation({ - method: "PUT", - path: ["api", "videos", "link"], - compute: ({ - video, - entries, - guess = false, - }: { - video: string; - entries: Omit[]; - guess?: boolean; - }) => ({ - body: [ - { - id: video, - for: entries.map((x) => - guess && x.kind === "episode" - ? { - serie: slug, - // @ts-expect-error: idk why it couldn't match x as an episode - season: x.seasonNumber, - // @ts-expect-error: idk why it couldn't match x as an episode - episode: x.episodeNumber, - } - : { slug: x.slug }, - ), - }, - ], - }), - invalidate: ["api", "series", slug], - optimisticKey: VideosModal.query(slug, null), - optimistic: (params, prev?: { pages: Page[] }) => ({ - ...prev!, - pages: prev!.pages.map((p) => ({ - ...p, - items: p!.items.map((x) => { - if (x.id !== params.video) return x; - return { ...x, entries: params.entries }; - }) as FullVideo[], - })), - }), - }); - - return ( - - {[...titles].map((title) => ( - -

{t("show.videos-map-related", { title })}

- { - setTitles(titles.filter((x) => x !== title)); - }} - {...tooltip(t("misc.cancel"))} - /> -
- ))} - { - const saved = item.entries.length; - const guess = !saved - ? uniqBy( - item.guess.episodes.map( - (x) => - ({ - kind: "episode", - id: `s${x.season}-e${x.episode}`, - seasonNumber: x.season, - episodeNumber: x.episode, - }) as Entry, - ), - (x) => x.id, - ) - : []; - return ( - - - {saved ? ( - { - if (!titles.includes(item.guess.title)) - setTitles([...titles, item.guess.title]); - await editLinks({ video: item.id, entries: [] }); - }} - {...tooltip(t("show.videos-map-delete"))} - /> - ) : guess.length ? ( - { - await editLinks({ - video: item.id, - entries: guess, - guess: true, - }); - }} - {...tooltip(t("show.videos-map-validate"))} - /> - ) : ( - - )} -

{item.path}

-
- ({ - parser: Entry, - path: ["api", "series", slug, "entries"], - params: { - query: q, - }, - infinite: true, - })} - getKey={ - saved - ? (x) => x.id - : (x) => - x.kind === "episode" - ? `${x.seasonNumber}-${x.episodeNumber}` - : x.id - } - getLabel={(x) => `${entryDisplayNumber(x)} - ${x.name}`} - getSmallLabel={entryDisplayNumber} - onValueChange={async (entries) => { - if (!entries.length && !titles.includes(item.guess.title)) - setTitles([...titles, item.guess.title]); - await editLinks({ - video: item.id, - entries, - }); - }} - /> -
- ); - }} - Loader={() => } - Footer={ - ( -