Add Novel Updates

This commit is contained in:
Rajarshee Chatterjee
2021-07-09 14:24:26 +05:30
parent b0639e79a8
commit ff59330d20
8 changed files with 404 additions and 127 deletions
+1
View File
@@ -200,6 +200,7 @@ const styles = StyleSheet.create({
paddingHorizontal: 4.5,
paddingVertical: 4,
borderRadius: 4,
flex: 1,
},
extensionIcon: {
width: 42,
+3 -3
View File
@@ -110,7 +110,7 @@ const Browse = ({ navigation }) => {
extraData={pinned}
ListHeaderComponent={
<View>
{(showNovelUpdates || showMyAnimeList) && (
{showMyAnimeList && (
<>
<Text
style={{
@@ -121,7 +121,7 @@ const Browse = ({ navigation }) => {
>
Discover
</Text>
{showNovelUpdates && (
{/* {showNovelUpdates && (
<DiscoverCard
label="Novel Updates"
onPress={() =>
@@ -132,7 +132,7 @@ const Browse = ({ navigation }) => {
icon={require("../../../assets/novelupdates.png")}
theme={theme}
/>
)}
)} */}
{showMyAnimeList && (
<DiscoverCard
label="MyAnimeList"
+2 -2
View File
@@ -87,7 +87,7 @@ const BrowseSettings = ({ navigation }) => {
/>
</Pressable>
<ListSubHeader theme={theme}>Discover</ListSubHeader>
<Pressable
{/* <Pressable
android_ripple={{ color: theme.rippleColor }}
style={{
padding: 16,
@@ -107,7 +107,7 @@ const BrowseSettings = ({ navigation }) => {
dispatch(enableDiscover("showNovelUpdates"))
}
/>
</Pressable>
</Pressable> */}
<Pressable
android_ripple={{ color: theme.rippleColor }}
style={{
+155 -119
View File
@@ -8,6 +8,7 @@ import {
ScrollView,
} from "react-native";
import { WebView } from "react-native-webview";
import { useDispatch, useSelector } from "react-redux";
import { IconButton, Portal } from "react-native-paper";
import {
@@ -207,6 +208,11 @@ const Chapter = ({ route, navigation }) => {
}
}, []);
const setImmersiveMode = () => {
StatusBar.setHidden(true);
hideNavigationBar();
};
const scrollToSavedProgress = useCallback((event) => {
StatusBar.setHidden(true);
hideNavigationBar();
@@ -279,133 +285,163 @@ const Chapter = ({ route, navigation }) => {
return (
<>
<ChapterAppbar
novelName={novelName}
chapterName={chapterName}
chapterId={chapterId}
bookmark={bookmark}
readerSheetRef={readerSheetRef}
hide={hidden}
navigation={navigation}
dispatch={dispatch}
/>
<ScrollView
ref={(ref) => (scrollViewRef.current = ref)}
contentContainerStyle={[
styles.screenContainer,
{ backgroundColor: readerBackground(reader.theme) },
]}
onScroll={onScroll}
onContentSizeChange={(x, y) => setContentSize(y)}
showsVerticalScrollIndicator={false}
>
<GestureRecognizer
onSwipeRight={swipeGestures && navigateToPrevChapter}
onSwipeLeft={swipeGestures && navigateToNextChapter}
config={config}
style={{ flex: 1 }}
>
{error ? (
<View style={{ flex: 1, justifyContent: "center" }}>
<EmptyView
icon="(-_-;)・・・"
description={error}
style={{ color: errorTextColor(reader.theme) }}
>
<IconButton
icon="reload"
size={25}
style={{ margin: 0, marginTop: 16 }}
color={errorTextColor(reader.theme)}
onPress={() => {
getChapter(chapterId);
setLoading(true);
setError();
}}
/>
<Text
{sourceId === 50 ? (
loading ? (
<View style={{ flex: 1, justifyContent: "center" }}>
<ActivityIndicator
size={50}
color={theme.colorAccent}
/>
</View>
) : (
<WebView
originWhitelist={["*"]}
source={{
html: chapter.chapterText,
}}
onLayout={setImmersiveMode}
onScroll={onScroll}
/>
)
) : (
<>
<ChapterAppbar
novelName={novelName}
chapterName={chapterName}
chapterId={chapterId}
bookmark={bookmark}
readerSheetRef={readerSheetRef}
hide={hidden}
navigation={navigation}
dispatch={dispatch}
/>
<ScrollView
ref={(ref) => (scrollViewRef.current = ref)}
contentContainerStyle={[
styles.screenContainer,
{ backgroundColor: readerBackground(reader.theme) },
]}
onScroll={onScroll}
onContentSizeChange={(x, y) => setContentSize(y)}
showsVerticalScrollIndicator={false}
>
{error ? (
<View style={{ flex: 1, justifyContent: "center" }}>
<EmptyView
icon="(-_-;)・・・"
description={error}
style={{
color: errorTextColor(reader.theme),
}}
>
Retry
</Text>
</EmptyView>
</View>
) : loading ? (
<View style={{ flex: 1, justifyContent: "center" }}>
<ActivityIndicator
size={50}
color={theme.colorAccent}
/>
</View>
) : (
<View style={{ flex: 1 }}>
{images.length > 0 &&
images.map((image, index) => (
<FitImage
source={{ uri: image }}
key={index}
<IconButton
icon="reload"
size={25}
style={{ margin: 0, marginTop: 16 }}
color={errorTextColor(reader.theme)}
onPress={() => {
getChapter(chapterId);
setLoading(true);
setError();
}}
/>
))}
<Text
style={readerStyles}
onLayout={scrollToSavedProgress}
selectable={textSelectable}
onPress={hideHeader}
<Text
style={{
color: errorTextColor(reader.theme),
}}
>
Retry
</Text>
</EmptyView>
</View>
) : loading ? (
<View style={{ flex: 1, justifyContent: "center" }}>
<ActivityIndicator
size={50}
color={theme.colorAccent}
/>
</View>
) : (
<GestureRecognizer
onSwipeRight={
swipeGestures && navigateToPrevChapter
}
onSwipeLeft={
swipeGestures && navigateToNextChapter
}
config={config}
style={{ flex: 1 }}
>
{chapter.chapterText
.trim()
.replace(/\[https.*?\]/g, "")}
</Text>
</View>
)}
</GestureRecognizer>
<Portal>
<ReaderSheet
<View style={{ flex: 1 }}>
{images.length > 0 &&
images.map((image, index) => (
<FitImage
source={{ uri: image }}
key={index}
/>
))}
<Text
style={readerStyles}
onLayout={scrollToSavedProgress}
selectable={textSelectable}
onPress={hideHeader}
>
{chapter.chapterText
.trim()
.replace(/\[https.*?\]/g, "")}
</Text>
</View>
</GestureRecognizer>
)}
<Portal>
<ReaderSheet
theme={theme}
reader={reader}
dispatch={dispatch}
bottomSheetRef={readerSheetRef}
selectText={textSelectable}
showScrollPercentage={showScrollPercentage}
/>
</Portal>
</ScrollView>
<VerticalScrollbar
theme={theme}
reader={reader}
dispatch={dispatch}
bottomSheetRef={readerSheetRef}
selectText={textSelectable}
showScrollPercentage={showScrollPercentage}
hide={hidden}
setLoading={setLoading}
contentSize={contentSize}
scrollViewRef={scrollViewRef}
scrollPercentage={scrollPercentage}
setScrollPercentage={setScrollPercentage}
/>
</Portal>
</ScrollView>
<VerticalScrollbar
theme={theme}
hide={hidden}
setLoading={setLoading}
contentSize={contentSize}
scrollViewRef={scrollViewRef}
scrollPercentage={scrollPercentage}
setScrollPercentage={setScrollPercentage}
/>
<ChapterFooter
theme={theme}
swipeGestures={swipeGestures}
dispatch={dispatch}
nextChapter={nextChapter}
prevChapter={prevChapter}
hide={hidden}
navigateToNextChapter={navigateToNextChapter}
navigateToPrevChapter={navigateToPrevChapter}
readerSheetRef={readerSheetRef}
scrollViewRef={scrollViewRef}
/>
{showScrollPercentage && (
<Text
style={[
styles.scrollPercentage,
{
color: reader.textColor,
backgroundColor: readerBackground(reader.theme),
},
]}
>
{scrollPercentage + "%"}
</Text>
<ChapterFooter
theme={theme}
swipeGestures={swipeGestures}
dispatch={dispatch}
nextChapter={nextChapter}
prevChapter={prevChapter}
hide={hidden}
navigateToNextChapter={navigateToNextChapter}
navigateToPrevChapter={navigateToPrevChapter}
readerSheetRef={readerSheetRef}
scrollViewRef={scrollViewRef}
/>
{showScrollPercentage && (
<Text
style={[
styles.scrollPercentage,
{
color: reader.textColor,
backgroundColor: readerBackground(
reader.theme
),
},
]}
>
{scrollPercentage + "%"}
</Text>
)}
</>
)}
</>
);
+9 -2
View File
@@ -1,5 +1,12 @@
import React, { memo } from "react";
import { View, Text, StyleSheet, Clipboard, Pressable } from "react-native";
import {
View,
Text,
StyleSheet,
Clipboard,
Pressable,
Button,
} from "react-native";
import * as WebBrowser from "expo-web-browser";
import { TouchableRipple, IconButton } from "react-native-paper";
@@ -186,7 +193,7 @@ const NovelInfoHeader = ({
followed={novel.followed}
theme={theme}
/>
{novel.genre && novel.genre !== "" && (
{novel.genre.length > 0 && novel.genre !== "" && (
<NovelGenres theme={theme} genre={novel.genre} />
)}
<ReadButton
+194
View File
@@ -0,0 +1,194 @@
import cheerio from "react-native-cheerio";
import { htmlToText } from "../helpers/htmlToText";
import moment from "moment";
const sourceId = 50;
const sourceName = "Novel Updates";
const baseUrl = "https://www.novelupdates.com/";
let headers = new Headers({
"User-Agent":
"'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
});
const popularNovels = async (page) => {
const totalPages = 100;
let url = `${baseUrl}series-ranking/?rank=sixmonths&pg=` + page;
const result = await fetch(url, {
method: "GET",
headers: headers,
});
const body = await result.text();
const $ = cheerio.load(body);
const novels = [];
$("div.search_main_box_nu").each(function (res) {
const novelCover = $(this).find("img").attr("src");
const novelName = $(this).find(".search_title > a").text();
const novelUrl = $(this)
.find(".search_title > a")
.attr("href")
.split("/")[4];
const novel = {
sourceId,
novelName,
novelCover,
novelUrl,
};
novels.push(novel);
});
return { totalPages, novels };
};
const parseNovelAndChapters = async (novelUrl) => {
const url = `${baseUrl}series/${novelUrl}`;
const result = await fetch(url, {
method: "GET",
headers: headers,
});
const body = await result.text();
$ = cheerio.load(body);
let novel = {
sourceId,
sourceName,
url,
novelUrl,
};
novel.novelName = $(".seriestitlenu").text();
novel.novelCover = $(".seriesimg > img").attr("src");
novel.author = $("#showauthors").text().trim();
novel.genre = $(".book-generes").text().trim();
novel.status = $("#editstatus").text().includes("Ongoing")
? "Ongoing"
: "Completed";
novel.summary = $("#editdescription").text().trim();
let novelChapters = [];
const novelId = $("input#mypostid").attr("value");
let formData = new FormData();
formData.append("action", "nd_getchapters");
formData.append("mygrr", 0);
formData.append("mypostid", parseInt(novelId));
const data = await fetch(
"https://www.novelupdates.com/wp-admin/admin-ajax.php",
{
method: "POST",
headers,
body: formData,
}
);
const text = await data.text();
$ = cheerio.load(text);
$("li.sp_li_chp").each(function () {
const chapterName = $(this).text().trim();
const releaseDate = null;
const chapterUrl =
"https:" + $(this).find("a").first().next().attr("href");
novelChapters.push({ chapterName, releaseDate, chapterUrl });
});
novel.chapters = novelChapters.reverse();
return novel;
};
const parseChapter = async (novelUrl, chapterUrl) => {
const url = chapterUrl;
const result = await fetch(url, {
method: "GET",
headers: headers,
});
const body = await result.text();
$ = cheerio.load(body);
let chapterName = "";
let chapterText = body;
let nextChapter = null;
let prevChapter = null;
const chapter = {
sourceId,
novelUrl,
chapterUrl,
chapterName,
chapterText,
nextChapter,
prevChapter,
};
return chapter;
};
const searchNovels = async (searchTerm) => {
const url =
"https://www.novelupdates.com/?s=" +
searchTerm +
"&post_type=seriesplans";
const res = await fetch(url, {
method: "GET",
headers: headers,
});
const body = await res.text();
$ = cheerio.load(body);
let novels = [];
$("div.search_main_box_nu").each(function (res) {
const novelCover = $(this).find("img").attr("src");
const novelName = $(this).find(".search_title > a").text();
const novelUrl = $(this)
.find(".search_title > a")
.attr("href")
.split("/")[4];
const novel = {
sourceId,
novelName,
novelCover,
novelUrl,
};
novels.push(novel);
});
return novels;
};
const NovelUpdatesScraper = {
popularNovels,
parseNovelAndChapters,
parseChapter,
searchNovels,
};
export default NovelUpdatesScraper;
+29
View File
@@ -97,6 +97,35 @@ const parseNovelAndChapters = async (novelUrl) => {
novelChapters.push(chapter);
});
let moreChaptersUrl =
baseUrl +
"temphtml/_tempChapterList_all_" +
$("div#more").attr("data-nid") +
".html";
let moreChapters = await fetch(moreChaptersUrl);
let moreChaptersString = await moreChapters.text();
$ = cheerio.load(moreChaptersString);
$("a").each(function () {
const chapterName = $(this).text();
const releaseDate = $(this)[0]
.nextSibling.nodeValue.replace(/-/g, "/")
.trim();
const chapterUrl = $(this).attr("href").replace(url, "");
const chapter = {
chapterName,
releaseDate,
chapterUrl,
};
novelChapters.push(chapter);
});
novel.chapters = novelChapters.reverse();
return novel;
+11 -1
View File
@@ -48,12 +48,19 @@ import {
} from "./multisrc/madara/MadaraGenerator";
import WuxiaBlogScraper from "./en/wuxiablog";
import WuxiaCityScraper from "./en/wuxiacity";
import NovelUpdatesScraper from "./en/novelupdates";
export const sources = [
/**
* English
*/
{
sourceId: 50,
sourceUrl: "https://www.novelupdates.com/",
sourceName: "Novel Updates",
icon: "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/novelupdates/icon.png?raw=true",
lang: "English",
},
{
sourceId: 1,
url: "https://boxnovel.com/",
@@ -548,5 +555,8 @@ export const getSource = (sourceId) => {
case 49: {
return WuxiaCityScraper;
}
case 50: {
return NovelUpdatesScraper;
}
}
};