diff --git a/apps/list2series/src/app.ts b/apps/list2series/src/app.ts index 55f8f1ac..e9d456f3 100644 --- a/apps/list2series/src/app.ts +++ b/apps/list2series/src/app.ts @@ -2,19 +2,19 @@ import axios from 'axios'; import { linkSync, mkdirSync, readFileSync, rmSync } from 'fs'; import { basename } from 'path'; -import { type Book } from './types'; +import { type Book, type ReadList, type Series } from './types'; -const API = JSON.parse( - readFileSync(`${process.env.FLAKE}/apps/list2series/.env`, { encoding: 'utf-8' }), -).API; - // Examples of calling this script: // $ just l2s copy 0K65Q482KK7SD // $ just l2s meta 0K65Q482KK7SD +const API = JSON.parse( + readFileSync(`${process.env.FLAKE}/apps/list2series/.env`, { encoding: 'utf-8' }), +).API; + const LIST_ID = process.argv[3]; -const getListInfo = async() => { +const getListInfo = async(): Promise<ReadList> => { const res = await axios.request({ method: 'get', maxBodyLength: Infinity, @@ -28,8 +28,8 @@ const getListInfo = async() => { return res.data; }; -const getSeriesBooks = async(listName: string, seriesPath: string): Promise<Book[]> => { - const series = await axios.request({ +const getSeries = async(seriesTitle: string, operator = true): Promise<Series[]> => { + return (await axios.request({ method: 'post', maxBodyLength: Infinity, url: 'https://komga.nelim.org/api/v1/series/list?unpaged=true', @@ -41,14 +41,16 @@ const getSeriesBooks = async(listName: string, seriesPath: string): Promise<Book data: JSON.stringify({ condition: { title: { - operator: 'isNot', - value: '', + operator: operator ? 'is' : 'isNot', + value: seriesTitle, }, }, }), - }); + })).data.content; +}; - const thisSeries = (series.data.content as Book[]).find((s) => s.url === seriesPath); +const getSeriesBooks = async(listName: string, seriesPath: string): Promise<Book[]> => { + const thisSeries = (await getSeries('', false)).find((s) => s.url === seriesPath); if (!thisSeries) { throw new Error('Series could not be found'); @@ -115,7 +117,7 @@ const getSeriesBooks = async(listName: string, seriesPath: string): Promise<Book return books.data.content; }; -const getBookInfo = async(id: string) => { +const getBookInfo = async(id: string): Promise<Book> => { const res = await axios.request({ method: 'get', maxBodyLength: Infinity, @@ -129,8 +131,9 @@ const getBookInfo = async(id: string) => { return res.data; }; -const scanLibrary = async() => { - return await axios.request({ +// There doesn't seem to be a way to wait for the scan to be done +const scanLibrary = (): void => { + axios.request({ method: 'post', maxBodyLength: Infinity, url: 'https://komga.nelim.org/api/v1/libraries/0K4QG58XA29DZ/scan', @@ -140,14 +143,19 @@ const scanLibrary = async() => { }); }; -const setBookMetadata = async(i: number, source: Book, target: Book) => { - source.metadata.title = `${source.seriesTitle} Issue #${source.metadata.number}`; +const setBookMetadata = async(i: number, source: Book, target: Book): Promise<void> => { + const thisSeries = (await getSeries(source.seriesTitle))[0]; + + source.metadata.title = thisSeries.booksCount !== 1 ? + `${source.seriesTitle} Issue #${source.metadata.number}` : + source.metadata.title = source.seriesTitle; + source.metadata.number = i.toString(); source.metadata.numberSort = i; const metadata = JSON.stringify(source.metadata); - const res = await axios.request({ + axios.request({ method: 'patch', maxBodyLength: Infinity, url: `https://komga.nelim.org/api/v1/books/${target.id}/metadata`, @@ -157,13 +165,11 @@ const setBookMetadata = async(i: number, source: Book, target: Book) => { }, data: metadata, }); - - return res; }; -const main = async() => { +const main = async(): Promise<void> => { const list = await getListInfo(); - const ids = list.bookIds as string[]; + const ids = list.bookIds; const seriesPath = `/data/comics/[List] ${list.name}`; const listBooks = [] as Book[]; @@ -186,7 +192,7 @@ const main = async() => { linkSync(bookPath, inListPath); } - await scanLibrary(); + scanLibrary(); } else if (process.argv[2] === 'meta') { diff --git a/apps/list2series/src/types.ts b/apps/list2series/src/types.ts index 419eeb90..da05a10a 100644 --- a/apps/list2series/src/types.ts +++ b/apps/list2series/src/types.ts @@ -13,7 +13,22 @@ export interface Author { role: string } -export interface Metadata { +export interface Link { + label: string + url: string +} + +export interface ShortBookMetadata { + authors: Author[] + tags: unknown[] + releaseDate: string + summary: string + summaryNumber: string + created: string + lastModified: string +} + +export interface BookMetadata { title: string titleLock: boolean summary: string @@ -26,11 +41,11 @@ export interface Metadata { releaseDateLock: boolean authors: Author[] authorsLock: boolean - tags: [] + tags: unknown[] tagsLock: boolean isbn: string isbnLock: boolean - links: unknown[] + links: Link[] linksLock: boolean created: string lastModified: string @@ -50,9 +65,71 @@ export interface Book { sizeBytes: number size: string media: Media - metadata: Metadata + metadata: BookMetadata readProgress: null | unknown deleted: boolean fileHash: string oneshot: boolean } + +export interface SeriesMetadata { + status: string + statusLock: boolean + title: string + titleLock: boolean + titleSort: string + titleSortLock: boolean + summary: string + summaryLock: boolean + readingDirection: string + readingDirectionLock: boolean + publisher: string + publisherLock: boolean + ageRating: null | string + ageRatingLock: boolean + language: string + languageLock: boolean + genres: string[] + genresLock: boolean + tags: unknown[] + tagsLock: boolean + totalBookCount: null | number + totalBookCountLock: boolean + sharingLabels: unknown[] + sharingLabelsLock: boolean + links: Link[] + linksLock: boolean + alternateTitles: string[] + alternateTitlesLock: boolean + created: string + lastModified: string +} + +export interface Series { + id: string + libraryId: string + name: string + url: string + created: string + lastModified: string + fileLastModified: string + booksCount: number + booksReadCount: number + booksUnreadCount: number + booksInProgressCount: number + metadata: SeriesMetadata + booksMetadata: ShortBookMetadata + deleted: boolean + oneshot: boolean +} + +export interface ReadList { + id: string + name: string + summary: string + ordered: boolean + bookIds: string[] + createdDate: string + lastModifiedDate: string + filtered: boolean +}