Use background service for downloads and updates (fixes #36)
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
|
||||
@@ -32,5 +33,6 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
|
||||
<service android:name="com.asterinet.react.bgactions.RNBackgroundActionsTask" />
|
||||
</application>
|
||||
</manifest>
|
||||
Generated
+13
@@ -3534,6 +3534,11 @@
|
||||
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-1.0.5.tgz",
|
||||
"integrity": "sha1-+YNhBRexc3wLncZDvsqTiTwE3xg="
|
||||
},
|
||||
"eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
|
||||
},
|
||||
"exec-sh": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz",
|
||||
@@ -6827,6 +6832,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-native-background-actions": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-background-actions/-/react-native-background-actions-2.6.1.tgz",
|
||||
"integrity": "sha512-4YQSEq7Y+qLKU9BxuzMG0DEIeZkvD54jTqA1IKai/w3sV+hc4cJNoSjZScRj1+663oWrEAZwLUk2I8P5QRvgiQ==",
|
||||
"requires": {
|
||||
"eventemitter3": "^4.0.7"
|
||||
}
|
||||
},
|
||||
"react-native-cheerio": {
|
||||
"version": "1.0.0-rc.4",
|
||||
"resolved": "https://registry.npmjs.org/react-native-cheerio/-/react-native-cheerio-1.0.0-rc.4.tgz",
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-native": "^0.64.2",
|
||||
"react-native-background-actions": "^2.6.1",
|
||||
"react-native-cheerio": "^1.0.0-rc.4",
|
||||
"react-native-collapsible-header-views": "^1.1.2",
|
||||
"react-native-device-info": "^8.1.3",
|
||||
|
||||
+14
-10
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { StyleSheet } from "react-native";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
|
||||
import { List, Divider as PaperDivider } from "react-native-paper";
|
||||
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
@@ -34,15 +34,19 @@ const ListItem = ({
|
||||
description={description}
|
||||
descriptionStyle={{ color: theme.textColorSecondary }}
|
||||
descriptionNumberOfLines={1}
|
||||
left={() =>
|
||||
icon && (
|
||||
<List.Icon
|
||||
color={theme.colorAccent}
|
||||
icon={icon}
|
||||
style={{ marginVertical: 0 }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
left={() => (
|
||||
<View style={{ justifyContent: "center" }}>
|
||||
{icon && (
|
||||
<List.Icon
|
||||
color={theme.colorAccent}
|
||||
icon={icon}
|
||||
style={{
|
||||
marginVertical: 0,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
right={() =>
|
||||
right && (
|
||||
<MaterialCommunityIcons
|
||||
|
||||
@@ -11,6 +11,7 @@ import ReaderSettings from "../screens/More/ReaderSettings";
|
||||
import BackupSettings from "../screens/More/BackupSettings";
|
||||
import AdvancedSettings from "../screens/More/AdvancedSettings";
|
||||
import GeneralSettings from "../screens/More/GeneralSettings";
|
||||
import DownloadQueue from "../screens/More/DownloadQueue";
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
@@ -56,6 +57,7 @@ const MoreStack = () => {
|
||||
<Stack.Navigator screenOptions={stackNavigatorConfig}>
|
||||
<Stack.Screen name="SettingsStack" component={SettingsStack} />
|
||||
<Stack.Screen name="About" component={About} />
|
||||
<Stack.Screen name="DownloadQueue" component={DownloadQueue} />
|
||||
</Stack.Navigator>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export const SET_DOWNLOAD_QUEUE = "SET_DOWNLOAD_QUEUE";
|
||||
export const CANCEL_DOWNLOAD = "CANCEL_DOWNLOAD";
|
||||
@@ -0,0 +1,8 @@
|
||||
import { SET_DOWNLOAD_QUEUE, CANCEL_DOWNLOAD } from "./donwloads.types";
|
||||
import BackgroundService from "react-native-background-actions";
|
||||
|
||||
export const cancelDownload = () => async (dispatch) => {
|
||||
await BackgroundService.stop();
|
||||
|
||||
dispatch({ type: CANCEL_DOWNLOAD });
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
import { CHAPTER_DOWNLOADED } from "../novel/novel.types";
|
||||
import { CANCEL_DOWNLOAD, SET_DOWNLOAD_QUEUE } from "./donwloads.types";
|
||||
|
||||
const initialState = {
|
||||
downloadQueue: [],
|
||||
};
|
||||
|
||||
const downloadsReducer = (state = initialState, action) => {
|
||||
const { type, payload } = action;
|
||||
|
||||
switch (type) {
|
||||
case SET_DOWNLOAD_QUEUE:
|
||||
return {
|
||||
...state,
|
||||
downloadQueue: [...state.downloadQueue, ...payload],
|
||||
};
|
||||
|
||||
case CHAPTER_DOWNLOADED:
|
||||
return {
|
||||
...state,
|
||||
downloadQueue: state.downloadQueue.filter(
|
||||
(chapter) => chapter.chapterId !== payload
|
||||
),
|
||||
};
|
||||
case CANCEL_DOWNLOAD:
|
||||
return {
|
||||
...state,
|
||||
downloadQueue: [],
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default downloadsReducer;
|
||||
@@ -50,6 +50,22 @@ import { getLibrary } from "../../database/queries/LibraryQueries";
|
||||
import { showToast } from "../../hooks/showToast";
|
||||
import { store } from "../store";
|
||||
|
||||
import * as Notifications from "expo-notifications";
|
||||
|
||||
Notifications.setNotificationHandler({
|
||||
handleNotification: async () => {
|
||||
return {
|
||||
shouldShowAlert: true,
|
||||
shouldPlaySound: true,
|
||||
shouldSetBadge: true,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
import BackgroundService from "react-native-background-actions";
|
||||
import { setDownloadQueue } from "../downloads/downloads.actions";
|
||||
import { SET_DOWNLOAD_QUEUE } from "../downloads/donwloads.types";
|
||||
|
||||
export const setNovel = (novel) => async (dispatch) => {
|
||||
dispatch({ type: SET_NOVEL, payload: novel });
|
||||
};
|
||||
@@ -267,9 +283,13 @@ export const markChapterUnreadAction =
|
||||
export const downloadChapterAction =
|
||||
(sourceId, novelUrl, chapterUrl, chapterName, chapterId) =>
|
||||
async (dispatch) => {
|
||||
// dispatch({
|
||||
// type: CHAPTER_DOWNLOADING,
|
||||
// payload: chapterId,
|
||||
// });
|
||||
dispatch({
|
||||
type: CHAPTER_DOWNLOADING,
|
||||
payload: chapterId,
|
||||
type: SET_DOWNLOAD_QUEUE,
|
||||
payload: [{ chapterId, chapterName }],
|
||||
});
|
||||
|
||||
await downloadChapter(sourceId, novelUrl, chapterUrl, chapterId);
|
||||
@@ -283,34 +303,87 @@ export const downloadChapterAction =
|
||||
};
|
||||
|
||||
export const downloadAllChaptersAction =
|
||||
(sourceId, novelUrl, chapters) => async (dispatch) => {
|
||||
(sourceId, novelUrl, chaps) => async (dispatch) => {
|
||||
try {
|
||||
await chapters.map((chapter, index) => {
|
||||
setTimeout(async () => {
|
||||
dispatch({
|
||||
type: CHAPTER_DOWNLOADING,
|
||||
payload: chapter.chapterId,
|
||||
});
|
||||
let chapters = chaps.filter((chapter) => chapter.downloaded === 0);
|
||||
|
||||
if (!chapter.downloaded) {
|
||||
await downloadChapter(
|
||||
sourceId,
|
||||
novelUrl,
|
||||
chapter.chapterUrl,
|
||||
chapter.chapterId
|
||||
);
|
||||
dispatch({ type: SET_DOWNLOAD_QUEUE, payload: chapters });
|
||||
const options = {
|
||||
taskName: "Library Update",
|
||||
taskTitle: chapters[0].chapterName,
|
||||
taskDesc: "0/" + chapters.length,
|
||||
taskIcon: {
|
||||
name: "notification_icon",
|
||||
type: "drawable",
|
||||
},
|
||||
color: "#00adb5",
|
||||
parameters: {
|
||||
delay: 1000,
|
||||
},
|
||||
progressBar: {
|
||||
max: chapters.length,
|
||||
value: 0,
|
||||
},
|
||||
};
|
||||
|
||||
const sleep = (time) =>
|
||||
new Promise((resolve) => setTimeout(() => resolve(), time));
|
||||
|
||||
const veryIntensiveTask = async (taskData) => {
|
||||
await new Promise(async (resolve) => {
|
||||
for (
|
||||
let i = 0;
|
||||
BackgroundService.isRunning() && i < chapters.length;
|
||||
i++
|
||||
) {
|
||||
if (BackgroundService.isRunning()) {
|
||||
// dispatch({
|
||||
// type: CHAPTER_DOWNLOADING,
|
||||
// payload: chapters[i].chapterId,
|
||||
// });
|
||||
|
||||
if (!chapters[i].downloaded) {
|
||||
await downloadChapter(
|
||||
sourceId,
|
||||
novelUrl,
|
||||
chapters[i].chapterUrl,
|
||||
chapters[i].chapterId
|
||||
);
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: CHAPTER_DOWNLOADED,
|
||||
payload: chapters[i].chapterId,
|
||||
});
|
||||
|
||||
await BackgroundService.updateNotification({
|
||||
taskTitle: chapters[i].chapterName,
|
||||
taskDesc: i + 1 + "/" + chapters.length,
|
||||
progressBar: {
|
||||
max: chapters.length,
|
||||
value: i + 1,
|
||||
},
|
||||
});
|
||||
|
||||
if (i + 1 === chapters.length) {
|
||||
resolve();
|
||||
await BackgroundService.stop();
|
||||
|
||||
Notifications.scheduleNotificationAsync({
|
||||
content: {
|
||||
title: "Downloader",
|
||||
body: "Download completed",
|
||||
},
|
||||
trigger: null,
|
||||
});
|
||||
}
|
||||
await sleep(taskData.delay);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
dispatch({
|
||||
type: CHAPTER_DOWNLOADED,
|
||||
payload: chapter.chapterId,
|
||||
});
|
||||
|
||||
if (index + 1 === chapters.length) {
|
||||
showToast(`Download completed `);
|
||||
}
|
||||
}, 1000 * index);
|
||||
});
|
||||
await BackgroundService.start(veryIntensiveTask, options);
|
||||
} catch (error) {
|
||||
showToast(error.message);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import historyReducer from "./history/history.reducer";
|
||||
import updatesReducer from "./updates/updates.reducer";
|
||||
import trackerReducer from "./tracker/tracker.reducer";
|
||||
import preferenceReducer from "./preferences/preference.reducer";
|
||||
import downloadsReducer from "./downloads/downloads.reducer";
|
||||
|
||||
const persistConfig = {
|
||||
key: "root",
|
||||
@@ -29,6 +30,7 @@ const persistedReducer = persistReducer(
|
||||
updatesReducer,
|
||||
trackerReducer,
|
||||
preferenceReducer,
|
||||
downloadsReducer,
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ const ChapterAppbar = ({
|
||||
style={{ backgroundColor: "transparent", elevation: 0 }}
|
||||
>
|
||||
<Appbar.BackAction
|
||||
onPress={() => navigation.goBack()}
|
||||
onPress={navigation.goBack}
|
||||
color="#FFFFFF"
|
||||
size={26}
|
||||
style={{ marginRight: 0 }}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { FlatList, View, Text, StyleSheet } from "react-native";
|
||||
import { FAB, ProgressBar } from "react-native-paper";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import { Appbar } from "../../components/Appbar";
|
||||
import { ScreenContainer } from "../../components/Common";
|
||||
import EmptyView from "../../components/EmptyView";
|
||||
|
||||
import { useTheme } from "../../hooks/reduxHooks";
|
||||
|
||||
import { cancelDownload } from "../../redux/downloads/downloads.actions";
|
||||
|
||||
import BackgroundService from "react-native-background-actions";
|
||||
import { downloadAllChaptersAction } from "../../redux/novel/novel.actions";
|
||||
|
||||
const DownloadQueue = ({ navigation }) => {
|
||||
const theme = useTheme();
|
||||
const { downloadQueue } = useSelector((state) => state.downloadsReducer);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
if (!BackgroundService.isRunning() && downloadQueue.length > 0) {
|
||||
dispatch(cancelDownload());
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ScreenContainer theme={theme}>
|
||||
<Appbar title="Download queue" onBackAction={navigation.goBack} />
|
||||
<FlatList
|
||||
contentContainerStyle={{ flexGrow: 1 }}
|
||||
keyExtractor={(item) => item.chapterId.toString()}
|
||||
data={downloadQueue}
|
||||
renderItem={({ item }) => (
|
||||
<View style={{ padding: 16 }}>
|
||||
<Text style={{ color: theme.textColorPrimary }}>
|
||||
{item.chapterName}
|
||||
</Text>
|
||||
<ProgressBar
|
||||
indeterminate={true}
|
||||
color={theme.colorAccent}
|
||||
style={{ marginTop: 8 }}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
ListEmptyComponent={
|
||||
<EmptyView icon="(・o・;)" description="No downloads" />
|
||||
}
|
||||
/>
|
||||
{downloadQueue.length > 0 && (
|
||||
<FAB
|
||||
style={[styles.fab, { backgroundColor: theme.colorAccent }]}
|
||||
color={theme.textColorPrimary}
|
||||
label="Cancel"
|
||||
uppercase={false}
|
||||
small
|
||||
icon="close"
|
||||
onPress={() => dispatch(cancelDownload())}
|
||||
/>
|
||||
)}
|
||||
</ScreenContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default DownloadQueue;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fab: {
|
||||
position: "absolute",
|
||||
margin: 16,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
@@ -1,13 +1,15 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, View, Image } from "react-native";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import { Appbar } from "../../components/Appbar";
|
||||
import { ListItem, ListSection } from "../../components/List";
|
||||
import { Divider, ListItem, ListSection } from "../../components/List";
|
||||
|
||||
import { useTheme } from "../../hooks/reduxHooks";
|
||||
|
||||
const MoreScreen = ({ navigation }) => {
|
||||
const theme = useTheme();
|
||||
const { downloadQueue } = useSelector((state) => state.downloadsReducer);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -34,6 +36,21 @@ const MoreScreen = ({ navigation }) => {
|
||||
</View>
|
||||
</View>
|
||||
<ListSection>
|
||||
<ListItem
|
||||
title="Download queue"
|
||||
description={
|
||||
downloadQueue.length > 0 &&
|
||||
downloadQueue.length + " remaining"
|
||||
}
|
||||
icon="download-outline"
|
||||
onPress={() =>
|
||||
navigation.navigate("MoreStack", {
|
||||
screen: "DownloadQueue",
|
||||
})
|
||||
}
|
||||
theme={theme}
|
||||
/>
|
||||
<Divider theme={theme} />
|
||||
<ListItem
|
||||
title="Settings"
|
||||
icon="cog-outline"
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from "react-native";
|
||||
|
||||
import { Provider, Portal, Appbar, IconButton, Menu } from "react-native-paper";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import {
|
||||
bookmarkChapterAction,
|
||||
@@ -46,6 +46,7 @@ const Novel = ({ route, navigation }) => {
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
const { novel, chapters, loading, updating, downloading } = useNovel();
|
||||
const { downloadQueue } = useSelector((state) => state.downloadsReducer);
|
||||
|
||||
const [selected, setSelected] = useState([]);
|
||||
const [downloadMenu, showDownloadMenu] = useState(false);
|
||||
@@ -103,6 +104,7 @@ const Novel = ({ route, navigation }) => {
|
||||
setSelected={setSelected}
|
||||
showChapterTitles={showChapterTitles}
|
||||
chapterActionsSheetRef={chapterActionsSheetRef}
|
||||
downloadQueue={downloadQueue}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ const ChapterItem = ({
|
||||
setSelected,
|
||||
showChapterTitles,
|
||||
chapterActionsSheetRef,
|
||||
downloadQueue,
|
||||
}) => {
|
||||
const [deleteChapterMenu, setDeleteChapterMenu] = useState(false);
|
||||
const showDeleteChapterMenu = () => setDeleteChapterMenu(true);
|
||||
@@ -49,7 +50,9 @@ const ChapterItem = ({
|
||||
});
|
||||
|
||||
const displayDownloadButton = () => {
|
||||
if (downloading.indexOf(chapter.chapterId) !== -1) {
|
||||
if (
|
||||
downloadQueue.some((chap) => chap.chapterId === chapter.chapterId)
|
||||
) {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
color={theme.textColorHint}
|
||||
|
||||
+60
-23
@@ -5,6 +5,8 @@ import { getLibrary } from "../database/queries/LibraryQueries";
|
||||
import { fetchChapters, fetchNovel } from "../services/Source/source";
|
||||
import * as Notifications from "expo-notifications";
|
||||
|
||||
import BackgroundService from "react-native-background-actions";
|
||||
|
||||
Notifications.setNotificationHandler({
|
||||
handleNotification: async () => {
|
||||
return {
|
||||
@@ -113,32 +115,67 @@ export const updateNovelChapters = async (sourceId, novelUrl, novelId) => {
|
||||
export const updateAllNovels = async () => {
|
||||
const libraryNovels = await getLibrary();
|
||||
|
||||
Notifications.scheduleNotificationAsync({
|
||||
identifier: "updatingNotif",
|
||||
content: { title: "Updating library" },
|
||||
trigger: null,
|
||||
});
|
||||
const options = {
|
||||
taskName: "Library Update",
|
||||
taskTitle: "Updating library",
|
||||
taskDesc: "0/" + libraryNovels.length,
|
||||
taskIcon: {
|
||||
name: "notification_icon",
|
||||
type: "drawable",
|
||||
},
|
||||
color: "#00adb5",
|
||||
parameters: {
|
||||
delay: 1000,
|
||||
},
|
||||
progressBar: {
|
||||
max: libraryNovels.length,
|
||||
value: 0,
|
||||
},
|
||||
};
|
||||
|
||||
libraryNovels.map((novel, index) =>
|
||||
setTimeout(async () => {
|
||||
await updateNovelChapters(
|
||||
novel.sourceId,
|
||||
novel.novelUrl,
|
||||
novel.novelId
|
||||
);
|
||||
console.log(novel.novelName + " Updated");
|
||||
const sleep = (time) =>
|
||||
new Promise((resolve) => setTimeout(() => resolve(), time));
|
||||
|
||||
if (index + 1 === libraryNovels.length) {
|
||||
Notifications.scheduleNotificationAsync({
|
||||
content: {
|
||||
title: "Library Updated",
|
||||
body: libraryNovels.length + " novels updated",
|
||||
},
|
||||
trigger: null,
|
||||
});
|
||||
const veryIntensiveTask = async (taskData) =>
|
||||
await new Promise(async (resolve) => {
|
||||
for (
|
||||
let i = 0;
|
||||
BackgroundService.isRunning() && i < libraryNovels.length;
|
||||
i++
|
||||
) {
|
||||
if (BackgroundService.isRunning()) {
|
||||
await updateNovelChapters(
|
||||
libraryNovels[i].sourceId,
|
||||
libraryNovels[i].novelUrl,
|
||||
libraryNovels[i].novelId
|
||||
);
|
||||
console.log(libraryNovels[i].novelName + " Updated");
|
||||
await BackgroundService.updateNotification({
|
||||
taskTitle: libraryNovels[i].novelName,
|
||||
taskDesc: i + 1 + "/" + libraryNovels.length,
|
||||
progressBar: {
|
||||
max: libraryNovels.length,
|
||||
value: i + 1,
|
||||
},
|
||||
});
|
||||
if (libraryNovels.length === i + 1) {
|
||||
resolve();
|
||||
await BackgroundService.stop();
|
||||
Notifications.scheduleNotificationAsync({
|
||||
content: {
|
||||
title: "Library Updated",
|
||||
body: libraryNovels.length + " novels updated",
|
||||
},
|
||||
trigger: null,
|
||||
});
|
||||
}
|
||||
|
||||
await sleep(taskData.delay);
|
||||
}
|
||||
}
|
||||
}, 3000 * index)
|
||||
);
|
||||
});
|
||||
|
||||
await BackgroundService.start(veryIntensiveTask, options);
|
||||
};
|
||||
|
||||
export const parseChapterNumber = (chapterName) => {
|
||||
|
||||
@@ -83,6 +83,17 @@ const parseNovelAndChapters = async (novelUrl) => {
|
||||
|
||||
let novelChapters = [];
|
||||
|
||||
let chapterUrlPrefix = $("tbody > tr")
|
||||
.first()
|
||||
.next()
|
||||
.find("td > a")
|
||||
.text()
|
||||
.split(" ");
|
||||
|
||||
chapterUrlPrefix.pop();
|
||||
|
||||
chapterUrlPrefix = chapterUrlPrefix.join("-").toLowerCase();
|
||||
|
||||
const latestChapter = $("tbody > tr")
|
||||
.first()
|
||||
.next()
|
||||
@@ -93,7 +104,7 @@ const parseNovelAndChapters = async (novelUrl) => {
|
||||
for (let i = 1; i <= latestChapter; i++) {
|
||||
const chapterName = "Chapter " + i;
|
||||
const releaseDate = null;
|
||||
const chapterUrl = novelUrl.slice(0, -1) + "-chapter-" + i + "/";
|
||||
const chapterUrl = chapterUrlPrefix + "-" + i + "/";
|
||||
|
||||
const chapter = {
|
||||
chapterName,
|
||||
@@ -103,7 +114,6 @@ const parseNovelAndChapters = async (novelUrl) => {
|
||||
|
||||
novelChapters.push(chapter);
|
||||
}
|
||||
|
||||
novel.chapters = novelChapters;
|
||||
|
||||
return novel;
|
||||
|
||||
Reference in New Issue
Block a user