From 89bfd7baed0a4476c8a9db089ba3ac1a1712a5f7 Mon Sep 17 00:00:00 2001 From: Nil Silva Date: Sun, 1 Dec 2024 09:24:00 +0000 Subject: [PATCH] feat: Add Komga plugin (#1341) * Added komga plugin * moved icon to multi folder * Added multi to languages * fix komga icon * Added guide to komga plugin --- docs/komga-plugin.md | 5 + public/static/src/multi/komga/icon.png | Bin 0 -> 41204 bytes scripts/languages.js | 1 + src/plugins/multi/komga.ts | 272 +++++++++++++++++++++++++ 4 files changed, 278 insertions(+) create mode 100644 docs/komga-plugin.md create mode 100644 public/static/src/multi/komga/icon.png create mode 100644 src/plugins/multi/komga.ts diff --git a/docs/komga-plugin.md b/docs/komga-plugin.md new file mode 100644 index 0000000..360bc8a --- /dev/null +++ b/docs/komga-plugin.md @@ -0,0 +1,5 @@ +1. Install Komga plugin; +2. In the installed plugins page press the cog icon to open the plugin settings; +3. Fill in the required information (email, password and your komga server url); +4. Press save and restart the app; +5. The komga plugin will now work like the other plugins. \ No newline at end of file diff --git a/public/static/src/multi/komga/icon.png b/public/static/src/multi/komga/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e18c8f4a4513a00103474f99bb26ab1d156c62f2 GIT binary patch literal 41204 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 yP#Qbex { + return await fetchApi(url, { + headers: { + Accept: 'application/json, text/plain, */*', + 'Content-Type': 'application/json;charset=utf-8', + 'Authorization': `Basic ${this.btoa(this.email + ':' + this.password)}`, + }, + Referer: this.site, + }).then(res => res.text()); + } + + btoa(input: string = '') { + const chars = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + let str = input; + let output = ''; + + for ( + let block = 0, charCode, i = 0, map = chars; + str.charAt(i | 0) || ((map = '='), i % 1); + output += map.charAt(63 & (block >> (8 - (i % 1) * 8))) + ) { + charCode = str.charCodeAt((i += 3 / 4)); + + if (charCode > 0xff) { + throw new Error( + "'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.", + ); + } + + block = (block << 8) | charCode; + } + + return output; + } + + flattenArray(arr: any) { + return arr.reduce((acc: any, obj: any) => { + const { children, ...rest } = obj; + acc.push(rest); + + if (children) { + acc.push(...this.flattenArray(children)); + } + + return acc; + }, []); + } + + async getSeries(url: string): Promise { + const novels: Plugin.NovelItem[] = []; + + var response = await this.makeRequest(url); + + const series = JSON.parse(response).content; + + for (let s of series) { + novels.push({ + name: s.name, + path: 'api/v1/series/' + s.id, + cover: this.site + `api/v1/series/${s.id}/thumbnail`, + }); + } + + return novels; + } + + async popularNovels( + pageNo: number, + { + showLatestNovels, + filters, + }: Plugin.PopularNovelsOptions, + ): Promise { + const read_status = filters?.read_status.value + ? '&read_status=' + filters?.read_status.value + : ''; + const status = filters?.status.value + ? '&status=' + filters?.status.value + : ''; + const sort = showLatestNovels ? 'lastModified,desc' : 'name,asc'; + + const url = `${this.site}api/v1/series?page=${pageNo - 1}${read_status}${status}&sort=${sort}`; + + return await this.getSeries(url); + } + + async parseNovel(novelPath: string): Promise { + const novel: Plugin.SourceNovel = { + path: novelPath, + name: 'Untitled', + }; + + const url = this.site + novelPath; + + var response = await this.makeRequest(url); + + const series = JSON.parse(response); + + novel.name = series.name; + novel.author = series.booksMetadata.authors + .filter((author: any) => author.role === 'writer') + .reduce( + (accumulated: string, current: any) => + accumulated + (accumulated !== '' ? ', ' : '') + current.name, + '', + ); + novel.cover = this.site + `api/v1/series/${series.id}/thumbnail`; + novel.genres = series.metadata.genres.join(', '); + + switch (series.metadata.status) { + case 'ENDED': + novel.status = NovelStatus.Completed; + break; + case 'ONGOING': + novel.status = NovelStatus.Ongoing; + break; + case 'ABANDONED': + novel.status = NovelStatus.Cancelled; + break; + case 'HIATUS': + novel.status = NovelStatus.OnHiatus; + break; + default: + novel.status = NovelStatus.Unknown; + } + + novel.summary = series.booksMetadata.summary; + + const chapters: Plugin.ChapterItem[] = []; + + const booksResponse = await this.makeRequest( + this.site + `api/v1/series/${series.id}/books?unpaged=true`, + ); + + const booksData = JSON.parse(booksResponse).content; + + for (let book of booksData) { + const bookManifestResponse = await this.makeRequest( + this.site + `opds/v2/books/${book.id}/manifest`, + ); + + const bookManifest = JSON.parse(bookManifestResponse); + + const toc = this.flattenArray(bookManifest.toc); + + let i = 1; + for (let page of bookManifest.readingOrder) { + const tocItem = toc.find( + (v: any) => v.href?.split('#')[0] === page.href, + ); + const title = tocItem ? tocItem.title : null; + chapters.push({ + name: `${i}/${bookManifest.readingOrder.length} - ${book.metadata.title}${title ? ' - ' + title : ''}`, + path: 'opds/v2' + page.href?.split('opds/v2').pop(), + }); + i++; + } + } + + novel.chapters = chapters; + return novel; + } + async parseChapter(chapterPath: string): Promise { + const chapterText = await this.makeRequest(this.site + chapterPath); + return this.addUrlToImageHref( + chapterText, + this.site + chapterPath.split('/').slice(0, -1).join('/') + '/', + ); + } + + // Convert images to tag and correct url + addUrlToImageHref(htmlString: string, baseUrl: string): string { + const $ = parseHTML(htmlString, { xmlMode: true }); + + // Convert SVG elements to and add baseUrl if necessary + $('svg image').each((_, image) => { + const href = $(image).attr('href') || $(image).attr('xlink:href'); + const width = $(image).attr('width'); + const height = $(image).attr('height'); + + if (href) { + const img = $('').attr({ + src: href.startsWith('http') ? href : `${baseUrl}${href}`, + width: width || undefined, + height: height || undefined, + }); + $(image).closest('svg').replaceWith(img); + } + }); + + // Update elements to include the base URL if their src is relative + $('img').each((_, img) => { + const src = $(img).attr('src'); + if (src && !src.startsWith('http')) { + $(img).attr('src', `${baseUrl}${src}`); + } + }); + + return $.xml(); + } + + async searchNovels( + searchTerm: string, + pageNo: number, + ): Promise { + const url = `${this.site}api/v1/series?search=${searchTerm}&page=${pageNo - 1}`; + + return await this.getSeries(url); + } + + filters = { + status: { + value: '', + label: 'Status', + options: [ + { label: 'All', value: '' }, + { label: 'Completed', value: NovelStatus.Completed }, + { label: 'Ongoing', value: NovelStatus.Ongoing }, + { label: 'Cancelled', value: NovelStatus.Cancelled }, + { label: 'OnHiatus', value: NovelStatus.OnHiatus }, + ], + type: FilterTypes.Picker, + }, + read_status: { + value: '', + label: 'Read status', + options: [ + { label: 'All', value: '' }, + { label: 'Unread', value: 'UNREAD' }, + { label: 'Read', value: 'READ' }, + { label: 'In progress', value: 'IN_PROGRESS' }, + ], + type: FilterTypes.Picker, + }, + } satisfies Filters; + + pluginSettings = { + email: { + value: '', + label: 'Email', + type: 'Text', + }, + password: { + value: '', + label: 'Password', + }, + url: { + value: '', + label: 'URL', + }, + }; +} + +export default new KomgaPlugin();