import { Category, Censorship, Direction, Language, Layout, OnMissing, Rating, type FullComicFragment, type ScrapedComic, type UpsertComicInput, type UpsertOptions } from '$gql/graphql'; import { CategoryLabel, CensorshipLabel, DirectionLabel, LanguageLabel, LayoutLabel, RatingLabel } from '$lib/Enums'; import { getContext, setContext } from 'svelte'; import { writable, type Writable } from 'svelte/store'; interface ScraperContext { scraper: string; warnings: string[]; selector?: ScrapedComicSelector; } export function initScraperContext() { return setContext>('scraper', writable({ scraper: '', warnings: [] })); } export function getScraperContext() { return getContext>('scraper'); } export class Selector { keep = true; value: T; display: string | undefined; constructor(value: T, display?: string) { this.value = value; this.display = display; } toString() { return this.display ?? this.value; } static from( scraped: T | undefined | null, have: string | undefined | null, label?: Record ) { if (scraped && have !== scraped) { return new Selector(scraped, label ? label[scraped] : undefined); } return undefined; } static fromList(scraped: string[], have: { name: string }[]) { const haves = new Set(have.map((i) => i.name)); return scraped.filter((i) => !haves.has(i)).map((i) => new Selector(i)); } } function keepItem(selector?: Selector): T | undefined | null { if (selector?.keep) { return selector.value; } return undefined; } function keepList( selectorList: Selector[], onMissing: OnMissing ): { names: T[]; options: UpsertOptions } { return { names: selectorList.filter((v) => v.keep).map((v) => v.value), options: { onMissing } }; } export class ScrapedComicSelector { title?: Selector; originalTitle?: Selector; url?: Selector; date?: Selector; category?: Selector; censorship?: Selector; rating?: Selector; language?: Selector; direction?: Selector; layout?: Selector; artists: Selector[]; circles: Selector[]; characters: Selector[]; worlds: Selector[]; tags: Selector[]; constructor(scraped: ScrapedComic, comic: FullComicFragment) { this.title = Selector.from(scraped.title, comic.title); this.originalTitle = Selector.from(scraped.originalTitle, comic.originalTitle); this.url = Selector.from(scraped.url, comic.url); this.date = Selector.from(scraped.date, comic.date); this.category = Selector.from(scraped.category, comic.category, CategoryLabel); this.censorship = Selector.from(scraped.censorship, comic.censorship, CensorshipLabel); this.rating = Selector.from(scraped.rating, comic.rating, RatingLabel); this.language = Selector.from(scraped.language, comic.language, LanguageLabel); this.direction = Selector.from(scraped.direction, comic.direction, DirectionLabel); this.layout = Selector.from(scraped.layout, comic.layout, LayoutLabel); this.artists = Selector.fromList(scraped.artists, comic.artists); this.circles = Selector.fromList(scraped.circles, comic.circles); this.characters = Selector.fromList(scraped.characters, comic.characters); this.tags = Selector.fromList(scraped.tags, comic.tags); this.worlds = Selector.fromList(scraped.worlds, comic.worlds); } hasData() { return ( Object.values(this).filter((i) => { if (i === undefined) { return false; } else if (Array.isArray(i) && i.length === 0) { return false; } return true; }).length > 0 ); } toInput(onMissing: OnMissing): UpsertComicInput { return { title: keepItem(this.title), originalTitle: keepItem(this.originalTitle), url: keepItem(this.url), date: keepItem(this.date), category: keepItem(this.category), censorship: keepItem(this.censorship), rating: keepItem(this.rating), language: keepItem(this.language), direction: keepItem(this.direction), layout: keepItem(this.layout), artists: keepList(this.artists, onMissing), circles: keepList(this.circles, onMissing), characters: keepList(this.characters, onMissing), worlds: keepList(this.worlds, onMissing), tags: keepList(this.tags, onMissing) }; } }