diff --git a/android/app/build.gradle b/android/app/build.gradle index 77a6275d..e92800cb 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: "com.android.application" +apply plugin: "com.chaquo.python" import com.android.build.OutputFile @@ -146,9 +147,23 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion // Generated version code. Supports versions up to 1024.1024.2048 - versionCode ((((versionMajor << 10) | versionMinor) << 11) | versionPatch) + versionCode((((versionMajor << 10) | versionMinor) << 11) | versionPatch) versionName "$versionMajor.$versionMinor.$versionPatch" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + ndk { + abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" + } + python { + //buildPython "C:/Users/kijij/AppData/Local/Programs/Python/Python39/python.exe" //don't believe you need this + pip { + install "-r", "./requirements.txt" + } + } + sourceSets { + main { + python.srcDirs = ["./src/main/python"] + } + } } splits { abi { diff --git a/android/app/requirements.txt b/android/app/requirements.txt new file mode 100644 index 00000000..3ea5a228 --- /dev/null +++ b/android/app/requirements.txt @@ -0,0 +1 @@ +lxml==4.6.3 \ No newline at end of file diff --git a/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainApplication.java b/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainApplication.java index a1d8b15a..c3659470 100644 --- a/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainApplication.java +++ b/android/app/src/main/java/com/rajarsheechatterjee/LNReader/MainApplication.java @@ -12,7 +12,7 @@ import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.soloader.SoLoader; import com.rajarsheechatterjee.VolumeButtonListener.VolumeButtonListenerPackage; - +import com.rajarsheechatterjee.EpubParser.EpubParserPackage; import expo.modules.ApplicationLifecycleDispatcher; import expo.modules.ReactNativeHostWrapper; @@ -40,6 +40,7 @@ public class MainApplication extends Application implements ReactApplication { List packages = new PackageList(this).getPackages(); packages.add(new com.rajarsheechatterjee.LNReader.NavigationBarColorPackage()); packages.add(new VolumeButtonListenerPackage()); + packages.add(new EpubParserPackage()); return packages; } diff --git a/android/app/src/main/java/com/rajarsheechatterjee/epubParser/EpubParser.java b/android/app/src/main/java/com/rajarsheechatterjee/epubParser/EpubParser.java new file mode 100644 index 00000000..2470347e --- /dev/null +++ b/android/app/src/main/java/com/rajarsheechatterjee/epubParser/EpubParser.java @@ -0,0 +1,153 @@ +package com.rajarsheechatterjee.EpubParser; + +import com.chaquo.python.PyObject; +import com.chaquo.python.Python; +import com.chaquo.python.android.AndroidPlatform; + +import android.app.Activity; +import android.content.Intent; +import android.content.Context; +import android.content.ContentResolver; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.OpenableColumns; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import com.facebook.react.bridge.ActivityEventListener; +import com.facebook.react.bridge.BaseActivityEventListener; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.modules.core.DeviceEventManagerModule; +import java.io.File; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.util.Log; + +public class EpubParser extends ReactContextBaseJavaModule implements ActivityEventListener{ + private static final int REQUEST_DIRECTORY=100; //constant request code dunno if will use it + private static String epubPath; + private static Promise promise; + private final EpubParserActivityEventListener activityEventListener; + private static ReactApplicationContext appContext = null; + + public EpubParser(ReactApplicationContext context){ + super(context); + appContext = context; + activityEventListener = new EpubParserActivityEventListener(context, this); + context.addActivityEventListener(activityEventListener); + if (!Python.isStarted()) { + // Initialize Python with the Chaquopy SDK + Python.start(new AndroidPlatform(context)); + } + } + @Override + public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { + if (activity != null && requestCode == REQUEST_DIRECTORY && resultCode == Activity.RESULT_OK) { + Uri uri = data.getData(); + String epubPath = getFilePathFromUri(uri); + + Python py = Python.getInstance(); + PyObject epubParser = py.getModule("epubParser"); + + if (epubPath != null && epubPath.toLowerCase().endsWith(".epub")) { + PyObject savePath = epubParser.callAttr("parseEpub", epubPath, "/data/data/com.rajarsheechatterjee.LNReader/files/"); + promise.resolve(savePath.toString()); + } else { + promise.reject("Invalid file format"); + } + } else { + promise.reject("Directory selection canceled"); + } + } + /* + private final ActivityEventListener activityEventListener = new BaseActivityEventListener() { + //@Override + public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data){ + if(requestCode == REQUEST_DIRECTORY && resultCode == Activity.RESULT_OK){ + Uri uri = data.getData(); + String folderPath = getFilePathFromUri(uri); + //Log.d("file name", folderPath); + epubPath = folderPath; + //Log.d("epub path", epubPath); + Python py = Python.getInstance(); + PyObject epubParser = py.getModule("epubParser"); + if(epubPath != null && epubPath.toLowerCase().endsWith(".epub")){ + //Log.d("file name", epubPath); + PyObject getContent = epubParser.callAttr("parseEpub",epubPath,"/data/data/com.rajarsheechatterjee.LNReader/files/"); //dunno if that's the right app ID + epubPath = null; + promise.resolve(null); + } else{ + promise.reject("Bad file"); + } + } + else{ + promise.reject("Directory selection canceled"); + } + } + };*/ + + @Override + public String getName(){ + return "EpubParser"; + } + + @ReactMethod + public void openDirectory(Promise promise){ + this.promise = promise; + try{ + Intent intent=new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); //there's an extra backslash in there + final Activity activity = getCurrentActivity(); + activity.startActivityForResult(intent,REQUEST_DIRECTORY); + } catch (Exception e){ + promise.reject(e); + } + } + + private String getFilePathFromUri(Uri uri){ + String filePath=null; + if(uri!=null){ + ContentResolver resolver = getCurrentActivity().getContentResolver(); + Cursor cursor=resolver.query(uri,null,null,null,null); + if(cursor!=null&&cursor.moveToFirst()){ + int index=cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + String fileName=cursor.getString(index); + cursor.close(); + + File file=new File(getReactApplicationContext().getCacheDir(),fileName); + filePath=file.getAbsolutePath(); + + try{ + InputStream inputStream=resolver.openInputStream(uri); + OutputStream outputStream=new FileOutputStream(file); + byte[]buffer=new byte[4096]; + int bytesRead; + while((bytesRead=inputStream.read(buffer))!=-1){ + outputStream.write(buffer,0,bytesRead); + } + outputStream.close(); + inputStream.close(); + }catch(IOException e){ + e.printStackTrace(); + } + } + } + return filePath; + } + @Override + public void onNewIntent(Intent intent) { + // Handle new intents if needed + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/rajarsheechatterjee/epubParser/EpubParserActivityEventListener.java b/android/app/src/main/java/com/rajarsheechatterjee/epubParser/EpubParserActivityEventListener.java new file mode 100644 index 00000000..ff4109c7 --- /dev/null +++ b/android/app/src/main/java/com/rajarsheechatterjee/epubParser/EpubParserActivityEventListener.java @@ -0,0 +1,25 @@ +package com.rajarsheechatterjee.EpubParser; + + +import android.app.Activity; +import android.content.Intent; +import com.facebook.react.bridge.ActivityEventListener; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.BaseActivityEventListener; +import com.facebook.react.bridge.ReactMethod; + +public class EpubParserActivityEventListener extends BaseActivityEventListener { + private final ReactApplicationContext reactContext; + private final EpubParser epubParser; + + public EpubParserActivityEventListener(ReactApplicationContext reactContext, EpubParser epubParser) { + this.reactContext = reactContext; + this.epubParser = epubParser; + } + + @Override + public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { + epubParser.onActivityResult(activity, requestCode, resultCode, data); + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/rajarsheechatterjee/epubParser/EpubParserPackage.java b/android/app/src/main/java/com/rajarsheechatterjee/epubParser/EpubParserPackage.java new file mode 100644 index 00000000..37f52833 --- /dev/null +++ b/android/app/src/main/java/com/rajarsheechatterjee/epubParser/EpubParserPackage.java @@ -0,0 +1,26 @@ +package com.rajarsheechatterjee.EpubParser; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class EpubParserPackage implements ReactPackage { + @Override + public List createNativeModules(ReactApplicationContext reactContext) { + List modules = new ArrayList(); + + modules.add(new EpubParser(reactContext)); + + return modules; + } + + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/android/app/src/main/python/epubParser.py b/android/app/src/main/python/epubParser.py new file mode 100644 index 00000000..1d071278 --- /dev/null +++ b/android/app/src/main/python/epubParser.py @@ -0,0 +1,268 @@ +import json +import os.path +import os +import re +import zipfile +from lxml import etree + +# import shutil +# from PIL import Image #not necessary only for debugging + +'''export interface SourceNovel { + url: string; //must be absoulute + name: string; + cover?: string; + genres?: string; + summary?: string; + author?: string; + artist?: string; + status?: string; + chapters?: ChapterItem[]; +} +export interface ChapterItem { + name: string; + url: string; //must be absoulute + releaseTime?: string; +}''' + + +class ChapterItem: + def __init__(self, name, url, releaseTime): + self.name = name + self.url = url + self.releaseTime = releaseTime # perhaps epub creation date? + + +namespaces = { + 'calibre': 'http://calibre.kovidgoyal.net/2009/metadata', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'dcterms': 'http://purl.org/dc/terms/', + 'opf': 'http://www.idpf.org/2007/opf', + 'ncx': 'http://www.daisy.org/z3986/2005/ncx/', + 'u': 'urn:oasis:names:tc:opendocument:xmlns:container', + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', + 'xhtml': 'http://www.w3.org/1999/xhtml' +} + + +def getContentOPF(epub_path): + with zipfile.ZipFile(epub_path) as z: + text = z.read('META-INF/container.xml') # get container xml to get content + tree = etree.fromstring(text) + rootfile_path = tree.xpath('/u:container/u:rootfiles/u:rootfile', + namespaces=namespaces)[0].get('full-path') + tree = etree.fromstring(z.read(rootfile_path)) + return tree + + +def cleanTitle(dir_name): + # cleans a string so that it is valid for directories + pattern = r'[<>:"/\\|?*\x00-\x1F]' + # Remove any disallowed characters from the directory name string + clean_dir_name = re.sub(pattern, '', dir_name) + return clean_dir_name + + +def getContent(epub_path, dest_dir): + extensions = ['.html', '.htm', '.xhtml', '.css', '.png', '.jpeg', '.jpg', '.gif', '.ncx', + '.opf'] # still need the content.opf file + with zipfile.ZipFile(epub_path) as z: + for filename in z.namelist(): + if (filename.endswith('opf')): + contentOPF = filename # search for opf file + break + + tree = etree.XML(z.read(contentOPF)) + title = tree.find('.//dc:title', namespaces=namespaces).text + cleanedTitle = cleanTitle(title) + dir = dest_dir + 'convertedEpubs/' + cleanedTitle # temporary directory for saving + + for fileInfo in z.infolist(): + if any(fileInfo.filename.endswith(ext) for ext in extensions): + z.extract(fileInfo, path=dir) + + return dir + + +def treeFindsAll(queryString, XMLtree): + list = XMLtree.findall(queryString, namespaces=namespaces) + query = '' + if len(list) == 0: + query = 'N/A' + return query + for item in list: + query += (item.text + ' ') + return query + + +class SourceNovel: + def __init__(self, url, name, cover, genres, summary, author, artist, chapters): + self.url = url # file location? + self.name = name + self.cover = cover + self.genres = genres + self.summary = summary + self.author = author + self.artist = artist + self.status = 'local' + self.chapters = chapters + + +def getChapters(z: zipfile.ZipFile, opf_tree, dir_path): + isEPUB2 = False + chapters = [] + + for filename in z.namelist(): + if filename.endswith('toc.ncx'): + isEPUB2 = True + chpts_data_filename = filename + break + + if isEPUB2: + name_by_path = {} + tree = etree.XML(z.read(chpts_data_filename)) + + for nav_point in tree.xpath('.//ncx:navPoint', namespaces=namespaces): + name = nav_point.find('.//ncx:text', namespaces=namespaces).text + path = nav_point.find('.//ncx:content', namespaces=namespaces).attrib['src'] + + chapter_path = path[:path.index('#')] if '#' in path else path + if chapter_path not in name_by_path: + name_by_path[chapter_path] = name + + chapters_elements = opf_tree.xpath('//opf:spine//opf:itemref', namespaces=namespaces) + for i, chapter_el in enumerate(chapters_elements): + id = chapter_el.get('idref') + item = opf_tree.find(f".//opf:manifest/opf:item[@id='{id}']", namespaces=namespaces) + path = item.attrib['href'] + chapter_has_name = path in name_by_path + print(chapter_has_name) + if chapter_has_name: + chapter_name = name_by_path[path] + if chapter_name.isnumeric(): + chapterName = f'Chapter {chapter_name}' + else: + chapterName = chapter_name + else: + chapterName = id.split('_')[0] + + chapters.append({ + 'name': chapterName, + 'path': dir_path + '/' + path if len(dir_path) > 0 else path, + }) + else: + # EPUB3 uses nav.xhtml instead but I don't have an example rn + # For now it will list all files without their corresponding chapter names + chapters_elements = opf_tree.xpath('//opf:spine//opf:itemref', namespaces=namespaces) + for i, chapter_el in enumerate(chapters_elements): + id = chapter_el.get('idref') + item = opf_tree.find(f".//opf:manifest/opf:item[@id='{id}']", namespaces=namespaces) + path = item.attrib['href'] + + chapters.append({ + 'name': f'EPUB3 - Chapter {i + 1}', + 'path': dir_path + '/' + path if len(dir_path) > 0 else path, + }) + + return chapters + + +def getMetadata(epub_path, save_path): + with zipfile.ZipFile(epub_path) as z: + for filename in z.namelist(): + if (filename.endswith('opf')): + contentOPF = filename # search for opf file + break + print('content.opf', contentOPF) + tree = etree.XML(z.read(contentOPF)) + + title = tree.find('.//dc:title', namespaces=namespaces).text + # print('title', title) + + cover = getCover(epub_path) + # print('cover', cover) + + authors = '' + for author in tree.findall('.//dc:creator', namespaces=namespaces): + authors += (author.text + ' ') + # print('authors', authors) + + genre = treeFindsAll('.//dc:subject', tree) + # print('genre', genre) + + summary = treeFindsAll('.//dc:description', tree) + # print('summary', summary) + + artist = '' # this will be included with author as epub files do not need to have an artist role and distinction is probably not necessary + chapters = getChapters(z, tree, os.path.dirname(contentOPF)) + print('chapters', chapters) + + return { + 'url': save_path, + 'title': title, + 'cover': cover, + 'genre': genre, + 'summary': summary, + 'authors': authors.strip(), + 'artist': artist, + 'chapters': chapters, + } + + +def getCover(epub_path): + with zipfile.ZipFile(epub_path) as z: + for filename in z.namelist(): + if (filename.endswith('opf')): + contentOPF = filename # search for opf file + + tree = etree.XML(z.read(contentOPF)) + coverHREF = None + + try: + coverID = tree.xpath("//opf:metadata/opf:meta[@name='cover']", namespaces=namespaces)[0].get('content') + # print('coverID 2', coverID) # now we know where the cover image is located + coverHREF = tree.xpath("//opf:manifest/opf:item[@id='" + coverID + "']", namespaces=namespaces)[0].get( + 'href') + + except IndexError: # not an EPUB 2.0 + # print('EPUB 2 failure') + pass + + if not coverHREF: # try EPUB 3.0 + try: + coverHREF = tree.xpath("//opf:manifest/opf:item[@properties='cover-image']", namespaces=namespaces)[ + 0].get('href') + except IndexError: + # print('EPUB 3 failure') + pass + elif not coverHREF: # some EPUBs don't explicitly declare cover images + try: + coverID = tree.xpath("//opf:spine/open:itemref[@idref='cover']", namespaces=namespaces)[0].get('idref') + temp = tree.xpath("//opf:manifest/opf:item[@id='" + coverID + "']", namespaces=namespaces)[0].get( + 'href') + + tree = etree.fromstring(z.read(temp)) + coverHREF = tree.xpath('//xhtml:img', namespaces=namespaces)[0].get('src') + except IndexError: + print('Edge case failure') + elif not coverHREF: + # print('No cover found') + return None + + coverPath = coverHREF.replace('\\', '/') + # print('coverPath', coverPath) + + return coverPath if os.path.dirname(contentOPF) == 0 else os.path.dirname(contentOPF) + '/' + coverPath + + +def dumpMetaData(metaData, dest_dir): + dir = dest_dir + 'convertedEpubs/' + cleanTitle(metaData.get('title')) + '/' + with open(dir + 'metadata.json', 'w') as fileout: + json.dump(metaData, fileout) + + +def parseEpub(epub_path, dest_dir): + save_path = getContent(epub_path, dest_dir) # simultaneously writes and saves content to a variable + metaData = getMetadata(epub_path, save_path) + dumpMetaData(metaData, dest_dir) + return save_path \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 64e7c52e..ae132e31 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,12 +20,13 @@ buildscript { repositories { google() mavenCentral() + maven { url "https://chaquo.com/maven" } } dependencies { classpath("com.android.tools.build:gradle:7.1.2") classpath("com.facebook.react:react-native-gradle-plugin") classpath("de.undercouch:gradle-download-task:5.0.1") - + classpath ("com.chaquo.python:gradle:14.0.2") // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/src/native/epubParser.ts b/src/native/epubParser.ts new file mode 100644 index 00000000..aab94d3f --- /dev/null +++ b/src/native/epubParser.ts @@ -0,0 +1,21 @@ +import { getNovel } from '@database/queries/NovelQueries'; +import { insertNovelInLibrary } from '@database/queries/NovelQueriesV2'; +import { NativeModules } from 'react-native'; + +const { EpubParser } = NativeModules; +const epubSourceId = 0; + +interface EpubParserInterface { + openDirectory(): Promise; +} + +export async function openDirectory() { + const ParserInterface = EpubParser as EpubParserInterface; + const epubPath = await ParserInterface.openDirectory(); + + let dbNovel = await getNovel(epubSourceId, epubPath); + if (dbNovel === undefined || dbNovel.followed === 0) { + // Insert novel if it doesn't exist in the database + await insertNovelInLibrary(epubSourceId, epubPath, false, 1); + } +} diff --git a/src/screens/reader/utils/sanitizeChapterText.ts b/src/screens/reader/utils/sanitizeChapterText.ts index 390e9eb2..cf014603 100644 --- a/src/screens/reader/utils/sanitizeChapterText.ts +++ b/src/screens/reader/utils/sanitizeChapterText.ts @@ -16,6 +16,7 @@ export const sanitizeChapterText = ( html: string, options?: Options, ): string => { + const isEPubSource = options?.sourceId === 0; let text = sanitizeHtml(html, { allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'img', @@ -25,13 +26,16 @@ export const sanitizeChapterText = ( 'b', 'a', 'center', + ...(isEPubSource ? ['head', 'title', 'style'] : []), ]), allowedAttributes: { 'img': ['src', 'class'], 'a': ['href'], 'input': ['type', 'offline'], + ...(isEPubSource ? { '*': ['class'] } : {}), }, allowedSchemes: ['data', 'http', 'https', 'file'], + allowVulnerableTags: isEPubSource, }); if (text) { if (options?.removeExtraParagraphSpacing) { diff --git a/src/screens/settings/SettingsAdvancedScreen.js b/src/screens/settings/SettingsAdvancedScreen.js index cd8d950b..a01aaf58 100644 --- a/src/screens/settings/SettingsAdvancedScreen.js +++ b/src/screens/settings/SettingsAdvancedScreen.js @@ -12,6 +12,7 @@ import { getString } from '@strings/translations'; import useBoolean from '@hooks/useBoolean'; import ConfirmationDialog from '@components/ConfirmationDialog/ConfirmationDialog'; import { deleteReadChaptersFromDb } from '../../database/queries/DownloadQueries'; +import { openDirectory } from '../../native/epubParser'; import { Appbar, Button, List } from '@components'; import useSourceStorage from '@hooks/useSourceStorage'; @@ -70,6 +71,11 @@ const AdvancedSettings = ({ navigation }) => { theme={theme} /> + openDirectory()} + theme={theme} + /> { + const absPathParts = absPath.split('/'); + const relPathParts = relPath.split('/'); + + let backNavigations = 0; + relPathParts.forEach(p => p === '..' && backNavigations++); + + const newAbsPath = absPathParts + .slice(0, absPathParts.length - backNavigations) + .join('/'); + const newRelPath = relPathParts.slice(backNavigations).join('/'); + + return `${newAbsPath}/${newRelPath}`; +}; + +const parseNovelAndChapters = async (epubPath: string) => { + const metadata = JSON.parse(await RNFS.readFile(`${epubPath}/metadata.json`)); + + let novel = { + sourceId, + sourceName, + url: epubPath, + novelUrl: epubPath, + + novelName: metadata.title, + novelCover: `file://${epubPath}/${metadata.cover}`, + artist: metadata.artist, + author: metadata.authors, + summary: htmlToText(metadata.summary), + status: 'Completed', + + chapters: metadata.chapters.map((chapter: any) => ({ + chapterName: chapter.name, + chapterUrl: `file://${epubPath}/${chapter.path}`, + })), + }; + + return novel; +}; + +const parseChapter = async (epubPath: string, chapterUrl: string) => { + const metadata = JSON.parse(await RNFS.readFile(`${epubPath}/metadata.json`)); + const chapterInfo = metadata.chapters.find((f: any) => + chapterUrl.includes(f.path), + ); + + const dirPath = chapterUrl.substring(0, chapterUrl.lastIndexOf('/')); + let chapterText = await RNFS.readFile(chapterUrl); + + const loadedCheerio = cheerio.load(chapterText); + const promises: Promise[] = []; + + const imageReplace = function (this: cheerio.Element) { + const src = + this.attribs.src ?? + this.attributes.find( + attr => attr.name.includes('src') || attr.name.includes('href'), + )?.value; + + if (src === undefined) { + loadedCheerio(this).remove(); + return; + } + + const promise: Promise = new Promise(async r => { + const url = resolveRelativePath(dirPath, src); + loadedCheerio(this).replaceWith(``); + r(0); + }); + + promises.push(promise); + }; + + // Specify images absolute path + loadedCheerio('img').each(imageReplace); + loadedCheerio('image').each(imageReplace); + + // Add styles + loadedCheerio('link').each(function () { + if (this.attribs.href === undefined) { + return; + } + + const promise: Promise = new Promise(async r => { + const path = resolveRelativePath(dirPath, this.attribs.href); + const style = await RNFS.readFile(path); + loadedCheerio(this).replaceWith(``); + r(0); + }); + + promises.push(promise); + }); + + await Promise.all(promises); + chapterText = loadedCheerio.html().replace(/href=".+?"/g, ''); + + const chapter = { + sourceId, + novelUrl: epubPath, + chapterUrl, + chapterName: chapterInfo.name, + chapterText, + }; + + return chapter; +}; + +const EPubSource = { + parseNovelAndChapters, + parseChapter, +}; + +export default EPubSource; diff --git a/src/sources/sourceManager.ts b/src/sources/sourceManager.ts index b96a6882..e4e8e0ec 100644 --- a/src/sources/sourceManager.ts +++ b/src/sources/sourceManager.ts @@ -1,3 +1,4 @@ +import EPubSource from './local/epubSource'; import ComradeMaoScraper from './en/comrademao'; import ReadLightNovelScraper from './en/readlightnovel'; import fastNovelScraper from './en/fastnovel'; @@ -185,6 +186,7 @@ interface Scraper { export const sourceManager = (sourceId: number): Scraper => { const scrapers: Record = { // @ts-ignore + 0: EPubSource, // @ts-ignore 1: BoxNovelScraper, // @ts-ignore 2: ReadLightNovelScraper, // @ts-ignore 3: fastNovelScraper, // @ts-ignore