diff --git a/.gitignore b/.gitignore index 58023cf..4bc1d10 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ lib/ Thumbs.db /test/**/*.png **/theme/override/ -TODO \ No newline at end of file +TODO diff --git a/src/cli.ts b/src/cli.ts index c406c09..1c23624 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -31,7 +31,7 @@ export async function run(args: string[] = process.argv) { .option('-w, --width ', 'Max width of the image', Number.parseFloat, 300) .option('-h, --height ', 'Max height of the image', Number.parseFloat) .option('-t, --type ', 'Art type (boxart, snap, title, box+snap, box+title)', 'boxart') - .option('-o, --output ', 'Artwork format (minui, nextui, muos, anbernic)', 'minui') + .option('-o, --output ', 'Artwork format (minui, nextui, muos, anbernic, funkey)', 'minui') .option('-a, --ai', 'Use AI for advanced matching', false) .option('-m, --ai-model ', 'Ollama model to use for AI matching', 'gemma2:2b') .option('-r, --regions ', 'Preferred regions to use for AI matching', 'World,Europe,USA,Japan') diff --git a/src/format/format.ts b/src/format/format.ts index 941d843..9a8e5bc 100644 --- a/src/format/format.ts +++ b/src/format/format.ts @@ -5,7 +5,8 @@ export enum Format { MinUI = 'minui', NextUI = 'nextui', MuOS = 'muos', - Anbernic = 'anbernic' + Anbernic = 'anbernic', + Funkey = 'funkey' } export type SeparateArtworksFunction = (options: Options) => Promise; @@ -53,6 +54,11 @@ export async function getOutputFormat(options: Options): Promise { return anbernic.default; } + case Format.Funkey: { + const funkey = await import('./funkey.js'); + return funkey.default; + } + // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check default: { throw new Error(`Unknown format: ${options.output}`); diff --git a/src/format/funkey.ts b/src/format/funkey.ts new file mode 100644 index 0000000..b3ed1b2 --- /dev/null +++ b/src/format/funkey.ts @@ -0,0 +1,51 @@ +import path from 'node:path'; +import createDebug from 'debug'; +import { getArtTypes } from '../libretro.js'; +import { type Options } from '../options.js'; +import { composeImageTo, resizeImageTo } from '../image.js'; +import { type ArtType } from '../art.js'; + +const debug = createDebug('funkey'); + +export async function useSeparateArtworks(_options: Options) { + return false; +} + +export async function getArtPath(filePath: string, _machine: string, _type?: ArtType) { + const fileName = path.basename(filePath, path.extname(filePath)); + return path.join(path.dirname(filePath), `${fileName}.png`); +} + +export async function exportArtwork( + art1Url: string | undefined, + art2Url: string | undefined, + artPath: string, + options: Options +) { + const artTypes = getArtTypes(options); + if (artTypes.art2 && (art1Url ?? art2Url)) { + debug(`Found art URL(s): "${art1Url}" / "${art2Url}"`); + await composeImageTo(art1Url, art2Url, artPath, { width: options.width, height: options.height }); + } else if art1Url) { + debug(`Found art URL: "${art1Url}"`); + await resizeImageTo(art1Url, artPath, { width: options.width, height: options.height }); + } else { + return false; + } + + return true; +} + +export async function cleanupArtwork(_targetPath: string, _romFolders: string[], _options: Options) { + // No cleanup needed since images are stored in the same folder as ROMs + console.info('No artwork folders to clean up for funkey format'); +} + +const funkey = { + useSeparateArtworks, + getArtPath, + exportArtwork, + cleanupArtwork +}; + +export default funkey;