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<Writable<ScraperContext>>('scraper', writable({ scraper: '', warnings: [] }));
}
export function getScraperContext() {
return getContext<Writable<ScraperContext>>('scraper');
}
export class Selector<T extends string> {
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<T extends string>(
scraped: T | undefined | null,
have: string | undefined | null,
label?: Record<string, string>
) {
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<T extends string>(selector?: Selector<T>): T | undefined | null {
if (selector?.keep) {
return selector.value;
}
return undefined;
}
function keepList<T extends string>(
selectorList: Selector<T>[],
onMissing: OnMissing
): { names: T[]; options: UpsertOptions } {
return {
names: selectorList.filter((v) => v.keep).map((v) => v.value),
options: { onMissing }
};
}
export class ScrapedComicSelector {
title?: Selector<string>;
originalTitle?: Selector<string>;
url?: Selector<string>;
date?: Selector<string>;
category?: Selector<Category>;
censorship?: Selector<Censorship>;
rating?: Selector<Rating>;
language?: Selector<Language>;
direction?: Selector<Direction>;
layout?: Selector<Layout>;
artists: Selector<string>[];
circles: Selector<string>[];
characters: Selector<string>[];
worlds: Selector<string>[];
tags: Selector<string>[];
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)
};
}
}