diff --git a/src/navigators/BottomNavigator.js b/src/navigators/BottomNavigator.js
index a70ab9d9..3fbad11a 100644
--- a/src/navigators/BottomNavigator.js
+++ b/src/navigators/BottomNavigator.js
@@ -1,5 +1,4 @@
import React from "react";
-import { View } from "react-native";
import { createMaterialBottomTabNavigator } from "@react-navigation/material-bottom-tabs";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
@@ -18,78 +17,76 @@ const BottomNavigator = () => {
const theme = useTheme();
return (
-
-
- (
-
- ),
- }}
- />
- (
-
- ),
- }}
- />
- (
-
- ),
- }}
- />
- (
-
- ),
- }}
- />
- (
-
- ),
- }}
- />
-
-
+
+ (
+
+ ),
+ }}
+ />
+ (
+
+ ),
+ }}
+ />
+ (
+
+ ),
+ }}
+ />
+ (
+
+ ),
+ }}
+ />
+ (
+
+ ),
+ }}
+ />
+
);
};
diff --git a/src/navigators/Main.js b/src/navigators/Main.js
index bc97bd75..010ca0fa 100644
--- a/src/navigators/Main.js
+++ b/src/navigators/Main.js
@@ -1,10 +1,17 @@
import React, { useEffect } from "react";
-import {
- createStackNavigator,
- TransitionPresets,
-} from "@react-navigation/stack";
+import { View } from "react-native";
+import { NavigationContainer } from "@react-navigation/native";
+import { createStackNavigator } from "@react-navigation/stack";
+
+import { setBarColor } from "../theme/utils/setBarColor";
+import { useTheme } from "../hooks/reduxHooks";
+import { githubUpdateChecker } from "../hooks/githubUpdateChecker";
+
+// Navigators
import BottomNavigator from "./BottomNavigator";
+
+// Screens
import Novel from "../screens/Novel/Novel";
import Chapter from "../screens/Chapter/Chapter";
import Extension from "../screens/Extension/Extension";
@@ -14,21 +21,11 @@ import SourceNovels from "../screens/Browse/SourceNovels";
import MigrateNovel from "../screens/Browse/migration/MigrationNovels";
import MoreStack from "./More";
import MalTopNovels from "../screens/Browse/discover/MalTopNovels";
-import { View } from "react-native";
-import { useTheme } from "../hooks/reduxHooks";
-import { githubUpdateChecker } from "../hooks/githubUpdateChecker";
import NewUpdateDialog from "../components/NewUpdateDialog";
-import { NavigationContainer } from "@react-navigation/native";
-import BrowseNovelUpdates from "../screens/Browse/discover/BrowseNovelUpdates";
import BrowseSettings from "../screens/Browse/BrowseSettings";
-import { setBarColor } from "../theme/utils/setBarColor";
const Stack = createStackNavigator();
-const stackNavigatorConfig = {
- headerShown: false,
-};
-
const MainNavigator = () => {
const theme = useTheme();
@@ -44,7 +41,7 @@ const MainNavigator = () => {
>
{isNewVersion && }
-
+
{
name="BrowseSettings"
component={BrowseSettings}
/>
-
{
- const theme = useTheme();
+const SettingsStack = () => (
+
+
+
+
+
+
+
+
+);
- return (
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-const MoreStack = () => {
- const theme = useTheme();
-
- return (
-
-
-
-
-
-
-
- );
-};
+const MoreStack = () => (
+
+
+
+
+
+);
export default MoreStack;
diff --git a/src/redux/source/source.actions.js b/src/redux/source/source.actions.js
index c4ec14b3..4cb3398d 100644
--- a/src/redux/source/source.actions.js
+++ b/src/redux/source/source.actions.js
@@ -6,15 +6,21 @@ import {
SEARCH_SOURCES,
} from "./source.types";
import { showToast } from "../../hooks/showToast";
-import { sources } from "../../sources/sources";
+import sources from "../../sources/sources.json";
export const getSourcesAction = () => async (dispatch) => {
try {
+ /**
+ * Sort sources aplhabetically
+ */
let sortedSources = sources.sort((a, b) =>
a.sourceName.localeCompare(b.sourceName)
);
- dispatch({ type: GET_SOURCES, payload: sortedSources });
+ dispatch({
+ type: GET_SOURCES,
+ payload: sortedSources,
+ });
} catch (error) {
showToast(error.message);
}
diff --git a/src/redux/source/source.reducers.js b/src/redux/source/source.reducers.js
index bf083511..ffbfd777 100644
--- a/src/redux/source/source.reducers.js
+++ b/src/redux/source/source.reducers.js
@@ -12,7 +12,6 @@ const initialState = {
search: [],
pinned: [],
filters: [],
- showNovelUpdates: true,
showMyAnimeList: true,
loading: true,
};
@@ -44,7 +43,6 @@ const sourceReducer = (state = initialState, action) => {
if (!state.filters) {
state.filters = [];
}
-
return {
...state,
filters:
diff --git a/src/screens/Browse/Browse.js b/src/screens/Browse/Browse.js
index e86a8ceb..1ed5b390 100644
--- a/src/screens/Browse/Browse.js
+++ b/src/screens/Browse/Browse.js
@@ -7,17 +7,18 @@ import {
ActivityIndicator,
RefreshControl,
} from "react-native";
-import { useTheme } from "../../hooks/reduxHooks";
+
import { useDispatch, useSelector } from "react-redux";
import {
getSourcesAction,
searchSourcesAction,
} from "../../redux/source/source.actions";
-
-import SourceCard from "./components/SourceCard";
+import { useTheme } from "../../hooks/reduxHooks";
import { showToast } from "../../hooks/showToast";
+
import { Searchbar } from "../../components/Searchbar/Searchbar";
+import SourceCard from "./components/SourceCard";
import EmptyView from "../../components/EmptyView";
import DiscoverCard from "./discover/DiscoverCard";
@@ -31,7 +32,6 @@ const Browse = ({ navigation }) => {
loading,
pinned,
filters = [],
- showNovelUpdates = true,
showMyAnimeList = true,
} = useSelector((state) => state.sourceReducer);
const theme = useTheme();
@@ -42,6 +42,8 @@ const Browse = ({ navigation }) => {
sources = sources.filter((source) => filters.indexOf(source.lang) === -1);
+ let pinnedSources = sources.filter((source) => isPinned(source.sourceId));
+
useEffect(() => {
dispatch(getSourcesAction());
}, [getSourcesAction]);
@@ -72,6 +74,14 @@ const Browse = ({ navigation }) => {
/>
);
+ const SourceHeader = ({ title }) => (
+
+ {title}
+
+ );
+
return (
{
data={!searchText ? sources : search}
keyExtractor={(item) => item.sourceId.toString()}
renderItem={renderItem}
- extraData={pinned}
+ extraData={pinnedSources}
ListHeaderComponent={
{showMyAnimeList && (
<>
-
- Discover
-
- {/* {showNovelUpdates && (
-
- navigation.navigate(
- "BrowseNovelUpdates"
- )
- }
- icon={require("../../../assets/novelupdates.png")}
- theme={theme}
- />
- )} */}
+
{showMyAnimeList && (
{
)}
>
)}
- {pinned.length > 0 && (
+ {pinnedSources.length > 0 && (
- isPinned(source.sourceId)
- )}
+ data={pinnedSources}
keyExtractor={(item) =>
item.sourceId.toString()
}
renderItem={renderItem}
- extraData={pinned}
+ extraData={pinnedSources}
ListHeaderComponent={
-
- Pinned
-
+
}
/>
)}
- {sources.length > 0 && (
-
- Sources
-
- )}
+ {sources.length > 0 && }
}
ListEmptyComponent={
@@ -214,5 +184,11 @@ const Browse = ({ navigation }) => {
export default Browse;
const styles = StyleSheet.create({
- container: { flex: 1 },
+ container: {
+ flex: 1,
+ },
+ sourcesHeader: {
+ paddingHorizontal: 16,
+ paddingVertical: 8,
+ },
});
diff --git a/src/screens/Browse/BrowseSettings.js b/src/screens/Browse/BrowseSettings.js
index 191aea10..99e0cd83 100644
--- a/src/screens/Browse/BrowseSettings.js
+++ b/src/screens/Browse/BrowseSettings.js
@@ -18,12 +18,10 @@ import {
const BrowseSettings = ({ navigation }) => {
const theme = useTheme();
const dispatch = useDispatch();
- const {
- filters = [],
- showNovelUpdates = true,
- showMyAnimeList = true,
- } = useSelector((state) => state.sourceReducer);
- const languages = ["English", "Spanish", "Japanese"];
+ const { filters = [], showMyAnimeList = true } = useSelector(
+ (state) => state.sourceReducer
+ );
+ const languages = ["English", "Spanish", "Japanese", "Chinese"];
const { searchAllSources = false } = useSettings();
const renderItem = ({ item }) => {
@@ -87,27 +85,7 @@ const BrowseSettings = ({ navigation }) => {
/>
Discover
- {/* dispatch(enableDiscover("showNovelUpdates"))}
- >
-
- Novel Updates
-
-
- dispatch(enableDiscover("showNovelUpdates"))
- }
- />
- */}
+
{
- const theme = useTheme();
-
- const [novels, setNovels] = useState([]);
- const [pageNo, setPageNo] = useState(1);
- const [loading, setLoading] = useState(true);
-
- const [searchText, setSearchText] = useState("");
-
- const getNovels = async (pageNo) => {
- const data = await scrapeTopNovels(pageNo);
-
- setNovels((novels) => novels.concat(data));
- setLoading(false);
- };
-
- useEffect(() => {
- getNovels(1);
- }, []);
-
- const getSearchResults = async () => {
- setLoading(true);
-
- const data = await scrapeSearchResults(searchText);
- setNovels(data);
- setLoading(false);
- };
-
- const clearSearchbar = () => {
- getNovels();
- setLoading(true);
- setSearchText("");
- };
-
- const isCloseToBottom = ({
- layoutMeasurement,
- contentOffset,
- contentSize,
- }) => {
- const paddingToBottom = 20;
- return (
- layoutMeasurement.height + contentOffset.y >=
- contentSize.height - paddingToBottom
- );
- };
-
- return (
-
- setSearchText(text)}
- onSubmitEditing={getSearchResults}
- clearSearchbar={clearSearchbar}
- actions={[
- {
- icon: "earth",
- onPress: () =>
- WebBrowser.openBrowserAsync(
- "https://www.novelupdates.com/"
- ),
- },
- ]}
- />
- {loading ? (
-
-
-
- ) : (
- item.novelName.toString()}
- renderItem={({ item }) => (
-
- navigation.navigate("GlobalSearch", {
- novelName: item.novelName,
- })
- }
- />
- )}
- onScroll={({ nativeEvent }) => {
- if (!searchText && isCloseToBottom(nativeEvent)) {
- getNovels(pageNo + 1);
- setPageNo((pageNo) => pageNo + 1);
- }
- }}
- ListFooterComponent={
- !searchText && (
-
-
-
- )
- }
- />
- )}
-
- );
-};
-
-export default BrowseNovelUpdates;
-
-const styles = StyleSheet.create({});
diff --git a/src/screens/Browse/discover/NovelUpdatesBottomsheet.js b/src/screens/Browse/discover/NovelUpdatesBottomsheet.js
deleted file mode 100644
index 9b0870f5..00000000
--- a/src/screens/Browse/discover/NovelUpdatesBottomsheet.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import React from "react";
-
-const NovelUpdatesBottomSheet = () => {};
-
-export default NovelUpdatesBottomSheet;
diff --git a/src/screens/Browse/discover/NovelUpdatesNovelCard.js b/src/screens/Browse/discover/NovelUpdatesNovelCard.js
deleted file mode 100644
index 61d42398..00000000
--- a/src/screens/Browse/discover/NovelUpdatesNovelCard.js
+++ /dev/null
@@ -1,122 +0,0 @@
-import React from "react";
-import { StyleSheet, Pressable, Text, View, FlatList } from "react-native";
-import FastImage from "react-native-fast-image";
-import { Row } from "../../../components/Common";
-
-const GenreChip = ({ children, theme }) => (
-
-
-
- {`${"Genre "}`}
-
- {children}
-
-
-);
-
-const NovelUpdatesNovelCard = ({ novel, theme, onPress }) => {
- return (
-
-
-
-
-
- {novel.novelName}
-
-
- {novel.genres}
-
-
-
- {`${"Chapter Count "}`}
-
- {novel.chapterCount}
-
-
-
-
- {novel.novelSummary}
-
-
-
-
- );
-};
-
-export default NovelUpdatesNovelCard;
-
-const styles = StyleSheet.create({
- novelUpdatesCard: {
- flex: 1,
- flexDirection: "row",
- },
- contentContainer: {
- flex: 1,
- },
- genreChip: {
- marginTop: 4,
- fontSize: 13,
- // fontStyle: "italic",
- },
-});
diff --git a/src/screens/Browse/discover/NovelUpdatesScraper.js b/src/screens/Browse/discover/NovelUpdatesScraper.js
deleted file mode 100644
index 9afe6708..00000000
--- a/src/screens/Browse/discover/NovelUpdatesScraper.js
+++ /dev/null
@@ -1,130 +0,0 @@
-import cheerio from "react-native-cheerio";
-
-const scrapeTopNovels = async (pageNo) => {
- const url =
- "https://www.novelupdates.com/series-ranking/?rank=sixmonths&pg=" +
- pageNo;
-
- let headers = new Headers({
- Accept: "application/json",
- "Content-Type": "application/json",
- "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 res = await fetch(url, {
- method: "GET",
- headers: headers,
- });
- const body = await res.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 genres = $(this)
- .find(".search_genre")
- .text()
- .replace(/\s/g, ", ");
-
- const novelSummary =
- $(this).find(".search_genre")[0].nextSibling.nodeValue;
-
- let chapterCount = "";
-
- $(this)
- .find(".search_stats > span")
- .each(function () {
- let detail = $(this).text();
-
- if (detail.includes("Chapters")) {
- chapterCount = detail.replace(" Chapters", "");
- }
- });
-
- // const chapterCount = $(this)
- // .find('i[title="Chapter Count"]')
- // const updateFrequency = $(this)
- // .find('i[title="Updates Frequency"]')
-
- const novel = {
- novelName,
- novelCover,
- genres,
- novelSummary,
- chapterCount,
- // updateFrequency,
- };
-
- novels.push(novel);
- });
-
- return novels;
-};
-
-const scrapeSearchResults = async (searchTerm) => {
- const url =
- "https://www.novelupdates.com/?s=" +
- searchTerm +
- "&post_type=seriesplans";
-
- let headers = new Headers({
- Accept: "application/json",
- "Content-Type": "application/json",
- "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 res = await fetch(url, {
- method: "GET",
- headers: headers,
- });
- const body = await res.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 genres = $(this)
- .find(".search_genre")
- .text()
- .replace(/\s/g, ", ");
-
- const novelSummary =
- $(this).find(".search_genre")[0].nextSibling.nodeValue;
-
- let chapterCount = "";
-
- $(this)
- .find(".search_stats > span")
- .each(function () {
- let detail = $(this).text();
-
- if (detail.includes("Chapters")) {
- chapterCount = detail.replace(" Chapters", "");
- }
- });
-
- const novel = {
- novelName,
- novelCover,
- genres,
- novelSummary,
- chapterCount,
- };
-
- novels.push(novel);
- });
-
- return novels;
-};
-
-export { scrapeTopNovels, scrapeSearchResults };
diff --git a/src/screens/Chapter/Chapter.js b/src/screens/Chapter/Chapter.js
index 13293f79..8acdb467 100644
--- a/src/screens/Chapter/Chapter.js
+++ b/src/screens/Chapter/Chapter.js
@@ -51,6 +51,7 @@ import FitImage from "react-native-fit-image";
import ChapterFooter from "./components/ChapterFooter";
import VerticalScrollbar from "./components/VerticalScrollbar";
import GestureRecognizer from "react-native-swipe-gestures";
+import TextToComponents from "./TextToComponent";
const Chapter = ({ route, navigation }) => {
const {
@@ -201,8 +202,7 @@ const Chapter = ({ route, navigation }) => {
};
const scrollToSavedProgress = useCallback((event) => {
- StatusBar.setHidden(true);
- hideNavigationBar();
+ setImmersiveMode();
if (position && firstLayout) {
position.percentage < 100 &&
scrollViewRef.current.scrollTo({
@@ -215,10 +215,7 @@ const Chapter = ({ route, navigation }) => {
}, []);
const hideHeader = () => {
- if (!hidden) {
- StatusBar.setHidden(true);
- hideNavigationBar();
- }
+ !hidden && setImmersiveMode();
setHidden(!hidden);
};
@@ -343,61 +340,12 @@ const Chapter = ({ route, navigation }) => {
onLayout={scrollToSavedProgress}
onPress={hideHeader}
>
- {chapter.chapterText
- .trim()
- .split(/(\[https.*?\])|(_.*?_)/g)
- .map((part, index) => {
- if (part) {
- const isItalic =
- part.match(/_(.*?)_/);
- const isImage =
- part.match(/\[https.*?\]/);
-
- if (isImage) {
- return (
-
- );
- } else if (isItalic) {
- return (
-
- {isItalic[1]}
-
- );
- } else {
- return (
-
- {part}
-
- );
- }
- }
- })}
+
)}
diff --git a/src/screens/Chapter/TextToComponent.js b/src/screens/Chapter/TextToComponent.js
new file mode 100644
index 00000000..bed266c5
--- /dev/null
+++ b/src/screens/Chapter/TextToComponent.js
@@ -0,0 +1,85 @@
+import React from "react";
+import { Text } from "react-native";
+
+import FitImage from "react-native-fit-image";
+
+const getHeadingSize = (size) => {
+ const headings = {
+ h1: 6,
+ h2: 5,
+ h3: 4,
+ h4: 3,
+ h5: 2,
+ h6: 1,
+ };
+
+ return headings[size];
+};
+
+const TextToComponents = ({ text, textStyle, textSelectable, textSize }) => {
+ // let imagePattern = /(\[Image\]\(http.*?\))/;
+ // let italicPattern = /(_.*?_)/;
+ // let headingPattern = /(\[h[1-6]\]\(.*?\))/;
+
+ // patterns = [imagePattern, headingPattern, italicPattern];
+ // regex = new RegExp(patterns.join("|"), "g");
+
+ // console.log(regex);
+
+ regex = /(\[Image\]\(http.*?\))|(\[h[1-6]\]\(.*?\))|(_.*?_)/;
+
+ let parseText = text.trim().split(regex);
+
+ // console.log(parseText);
+
+ return parseText.map((part, index) => {
+ if (part) {
+ const isItalic = part.match(/_(.*?)_/);
+ const isImage = part.match(/\[Image\]\((http.*?)\)/);
+ const isHeading = part.match(/\[(h[1-6])\]\((.*?)\)/);
+
+ if (isImage) {
+ return ;
+ } else if (isItalic) {
+ return (
+
+ {isItalic[1]}
+
+ );
+ } else if (isHeading) {
+ return (
+
+ {isHeading[2]}
+
+ );
+ } else {
+ return (
+
+ {part}
+
+ );
+ }
+ }
+ });
+};
+
+export default TextToComponents;
diff --git a/src/screens/Updates/Updates.js b/src/screens/Updates/Updates.js
index 6b3c3f4e..600c0dbe 100644
--- a/src/screens/Updates/Updates.js
+++ b/src/screens/Updates/Updates.js
@@ -1,11 +1,10 @@
-import React, { useState, useEffect, useCallback } from "react";
+import React, { useState, useCallback } from "react";
import {
StyleSheet,
View,
FlatList,
ActivityIndicator,
RefreshControl,
- Button,
Text,
} from "react-native";
import { useFocusEffect } from "@react-navigation/native";
@@ -15,7 +14,6 @@ import {
getUpdatesAction,
updateLibraryAction,
} from "../../redux/updates/updates.actions";
-import { Appbar } from "./components/Appbar";
import UpdatesItem from "./components/UpdatesItem";
import { useTheme } from "../../hooks/reduxHooks";
import { Searchbar } from "../../components/Searchbar/Searchbar";
@@ -44,20 +42,39 @@ const Updates = ({ navigation }) => {
setRefreshing(false);
};
- const onPress = (item) =>
- navigation.navigate("Chapter", {
- chapterId: item.chapterId,
- chapterUrl: item.chapterUrl,
- sourceId: item.sourceId,
- novelUrl: item.novelUrl,
- chapterName: item.chapterName,
- novelId: item.novelId,
- novelName: item.novelName,
- bookmark: item.bookmark,
- });
+ const onPress = useCallback(
+ (item) =>
+ navigation.navigate("Chapter", {
+ chapterId: item.chapterId,
+ chapterUrl: item.chapterUrl,
+ sourceId: item.sourceId,
+ novelUrl: item.novelUrl,
+ chapterName: item.chapterName,
+ novelId: item.novelId,
+ novelName: item.novelName,
+ bookmark: item.bookmark,
+ }),
+ []
+ );
+
+ const onPressCover = useCallback(
+ (item) =>
+ navigation.navigate("Novel", {
+ sourceId: item.sourceId,
+ novelUrl: item.novelUrl,
+ novelName: item.novelName,
+ novelCover: item.novelCover,
+ }),
+ []
+ );
const renderItem = ({ item }) => (
- onPress(item)} />
+ onPress(item)}
+ onPressCover={() => onPressCover(item)}
+ />
);
const ListFooterComponent = () =>
@@ -103,6 +120,8 @@ const Updates = ({ navigation }) => {
setSearchResults(results);
};
+ const keyExtractor = useCallback((item) => item.updateId.toString(), []);
+
return (
{
}
data={item.chapters}
- keyExtractor={(item) => item.updateId.toString()}
+ keyExtractor={keyExtractor}
renderItem={renderItem}
/>
)}
- ListFooterComponent={ListFooterComponent()}
- ListEmptyComponent={ListEmptyComponent()}
+ ListFooterComponent={}
+ ListEmptyComponent={}
refreshControl={refreshControl()}
/>
diff --git a/src/screens/Updates/components/UpdatesItem.js b/src/screens/Updates/components/UpdatesItem.js
index 44d3458b..06c79fc8 100644
--- a/src/screens/Updates/components/UpdatesItem.js
+++ b/src/screens/Updates/components/UpdatesItem.js
@@ -2,7 +2,7 @@ import React from "react";
import { View, Text, StyleSheet, Pressable } from "react-native";
import FastImage from "react-native-fast-image";
-const UpdateCard = ({ item, theme, onPress }) => {
+const UpdateCard = ({ item, theme, onPress, onPressCover }) => {
return (
{
android_ripple={{ color: theme.rippleColor }}
>
<>
-
+
+
+
{
"INSERT OR IGNORE INTO updates (chapterId, novelId, updateTime) values (?, ?, (datetime('now','localtime')))",
[insertId, novelId],
(txObj, res) => {
- console.log(
- "Inserted Chapter Id -> " +
- insertId +
- " Novel Id " +
- novelId
- );
+ // console.log(
+ // "Inserted Chapter Id -> " +
+ // insertId +
+ // " Novel Id " +
+ // novelId
+ // );
},
(txObj, error) => console.log("Error ", error)
);
@@ -91,14 +91,14 @@ export const updateNovelChapters = async (sourceId, novelUrl, novelId) => {
],
(txObj, { insertId }) => {
if (insertId !== -1) {
- console.log(
- "Inserted Chapter " +
- chapter.chapterUrl +
- " Chapter Name " +
- chapter.chapterName +
- " Novel Id " +
- novelId
- );
+ // console.log(
+ // "Inserted Chapter " +
+ // chapter.chapterUrl +
+ // " Chapter Name " +
+ // chapter.chapterName +
+ // " Novel Id " +
+ // novelId
+ // );
tx.executeSql(
"INSERT OR IGNORE INTO updates (chapterId, novelId, updateTime) values (?, ?, (datetime('now','localtime')))",
[insertId, novelId],
@@ -151,7 +151,7 @@ export const updateAllNovels = async () => {
libraryNovels[i].novelUrl,
libraryNovels[i].novelId
);
- console.log(libraryNovels[i].novelName + " Updated");
+ // console.log(libraryNovels[i].novelName + " Updated");
await BackgroundService.updateNotification({
taskTitle: libraryNovels[i].novelName,
taskDesc: i + 1 + "/" + libraryNovels.length,
diff --git a/src/sources/en/readlightnovel.js b/src/sources/en/readlightnovel.js
index 0d06698b..286ad1ff 100644
--- a/src/sources/en/readlightnovel.js
+++ b/src/sources/en/readlightnovel.js
@@ -143,17 +143,9 @@ const parseChapter = async (novelUrl, chapterUrl) => {
const chapterName = $(".block-title > h1").text().replace(" - ", "");
- $(".desc")
- .find("hr")
- .each(function (result) {
- $(this).remove();
- });
-
$(".alert").remove();
$(".hidden").remove();
- var re = new RegExp(String.fromCharCode(160), "g");
-
let chapterText = $(".desc").html();
chapterText = htmlToText(chapterText).replace(
diff --git a/src/sources/helpers/htmlToText.js b/src/sources/helpers/htmlToText.js
index a5241d8d..f6d9606b 100644
--- a/src/sources/helpers/htmlToText.js
+++ b/src/sources/helpers/htmlToText.js
@@ -23,13 +23,14 @@ const htmlToText = (html, options = {}) => {
// /<\s*h(\d)[^>]*>(.*?)<\/h(\d)>/gim,
// `${"#".repeat("$1")}$2${"#".repeat("$1")}`
// )
- // .replace(/(.*?)<\/strong>/gi, "*$1*")
+ .replace(/<(h[1-6])>(.*?)<\/h[1-6]>/gi, "[$1]($2)")
+ // .replace(/(.*?)<\/strong>/gi, "*$1*")
.replace(
/<\s*a[^>]*href=['"](.*?)['"][^>]*>([\s\S]*?)<\/\s*a\s*>/gi,
- "$2"
+ "[Link: $2]($1)"
)
- .replace(/<\s*img[^>]*src=['"](.*?)['"][^>]*>/gi, "[$1]")
+ .replace(/<\s*img[^>]*src=['"](.*?)['"][^>]*>/gi, "[Image]($1)")
// Remove all remaining tags.
.replace(/(<([^>]+)>)/gi, "")
// Make sure there are never more than two
diff --git a/src/sources/sources.js b/src/sources/sources.js
index e3f2fb6b..74b9b5ce 100644
--- a/src/sources/sources.js
+++ b/src/sources/sources.js
@@ -52,537 +52,60 @@ import NovelUpdatesScraper from "./en/novelupdates";
import RanobesScraper from "./en/ranobes";
import YushuboScraper from "./ch/yushubo";
-export const sources = [
- /**
- * English
- */
- {
- sourceId: 50,
- sourceUrl: "https://www.novelupdates.com/",
- sourceName: "Novel Updates",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelupdates/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 1,
- url: "https://boxnovel.com/",
- sourceName: "BoxNovel",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/boxnovel/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 2,
- sourceName: "ReadLightNovel",
- url: "https://www.readlightnovel.org",
- lang: "English",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/readlightnovel/icon.png?raw=true",
- },
- {
- sourceId: 3,
- url: "https://fastnovel.net/",
- sourceName: "FastNovel",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/fastnovel/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 4,
- url: "https://readnovelfull.com/",
- sourceName: "ReadNovelFull",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/readnovelfull/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 5,
- url: "https://www.mtlnovel.com/",
- sourceName: "MTLNovel",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/mtlnovel/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 6,
- url: "https://www.novelhall.com/",
- sourceName: "NovelHall",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelhall/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 7,
- url: "https://www.wuxiaworld.com/",
- sourceName: "WuxiaWorld",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworld/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 8,
- url: "https://novelfull.com/",
- sourceName: "NovelFull",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelfull/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 9,
- url: "https://noveltrench.com/",
- sourceName: "NovelTrench",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/noveltrench/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 10,
- url: "https://vipnovel.com/",
- sourceName: "VipNovel",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/vipnovel/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 11,
- url: "https://kisslightnovels.info/",
- sourceName: "KissLightNovels",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/kisslightnovels/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 12,
- url: "https://wuxiaworldsite.co/",
- sourceName: "WuxiaWorldSite",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworldsite/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 13,
- url: "https://freewebnovel.com/",
- sourceName: "FreeWebNovel",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/freewebnovel/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 14,
- url: "https://jpmtl.com/",
- sourceName: "JPMTL",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/jpmtl/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 15,
- url: "https://www.lightnovelpub.com/",
- sourceName: "LightNovelPub",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/lightnovelpub/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 16,
- url: "https://www.wuxiaworld.co/",
- sourceName: "WuxiaWorld.co",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworldco/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 17,
- url: "http://www.tapread.com/",
- sourceName: "TapRead",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/tapread/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 18,
- url: "https://www.novelupdates.cc/",
- sourceName: "NovelUpdates.cc",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelupdatescc/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 19,
- url: "https://m.readlightnovel.cc/",
- sourceName: "ReadLightNovel.cc",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/readlightnovelcc/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 20,
- url: "http://wuxiaworld.cloud/",
- sourceName: "WuxiaWorld.cloud",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworldcloud/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 21,
- url: "https://woopread.com/",
- sourceName: "WoopRead",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/woopread/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 22,
- url: "https://foxaholic.com/",
- sourceName: "Foxaholic",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/foxaholic/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 27,
- url: "https://comrademao.com/",
- sourceName: "ComradeMao",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/comrademao/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 33,
- url: "https://www.novelpassion.com/",
- sourceName: "NovelPassion",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelpassion/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 34,
- url: "https://www.royalroad.com/",
- sourceName: "Royal Road",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/royalroad/icon.png?raw=true",
- lang: "English",
- },
-
- {
- sourceId: 35,
- url: "https://www.scribblehub.com/",
- sourceName: "Scribble Hub",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/scribblehub/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 37,
- url: "https://lnmtl.com/",
- sourceName: "LNMTL",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/lnmtl/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 38,
- sourceUrl: "https://skynovel.org/",
- sourceName: "SkyNovel",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/skynovel.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 39,
- sourceUrl: "https://novelcake.com/",
- sourceName: "NovelCake",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/novelcake.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 40,
- sourceUrl: "https://novelsrock.com/",
- sourceName: "NovelsRock",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/novelsrock.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 41,
- sourceUrl: "https://zinnovel.com/",
- sourceName: "ZinnNovel",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/zinnovel.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 42,
- sourceUrl: "https://noveltranslate.com/",
- sourceName: "NovelTranslate",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/noveltranslate.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 43,
- sourceUrl: "https://www.lunarletters.com/",
- sourceName: "LunarLetters",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/lunarletters.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 44,
- sourceUrl: "https://sleepytranslations.com/",
- sourceName: "SleepyTranslations",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/sleepytranslations.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 45,
- sourceUrl: "https://freenovel.me/",
- sourceName: "FreeNovelMe",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/freenovel.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 46,
- sourceUrl: "https://1stkissnovel.love/",
- sourceName: "1stKissNovel",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/1stkissnovel.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 47,
- sourceUrl: "https://daonovel.com/",
- sourceName: "DaoNovel",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/daonovel.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 48,
- sourceUrl: "https://www.wuxia.blog/",
- sourceName: "Wuxia.Blog",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiablog/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 49,
- sourceUrl: "https://wuxia.city/",
- sourceName: "Wuxia.City",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiacity/icon.png?raw=true",
- lang: "English",
- },
- {
- sourceId: 51,
- sourceUrl: "https://ranobes.net/",
- sourceName: "Ranobes",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/en/ranobes/icon.png?raw=true",
- lang: "English",
- },
-
- /**
- * Spanish
- */
-
- {
- sourceId: 23,
- url: "https://tunovelaligera.com/",
- sourceName: "Tunovelaligera",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/es/tunovelaligera/icon.png?raw=true",
- lang: "Spanish",
- },
- {
- sourceId: 24,
- url: "https://www.skynovels.net/",
- sourceName: "SkyNovels",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/es/skynovels/icon.png?raw=true",
- lang: "Spanish",
- },
- {
- sourceId: 25,
- url: "https://einherjarproject.net/",
- sourceName: "EinherjarProject",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/es/einherjarproject/icon.png?raw=true",
- lang: "Spanish",
- },
- {
- sourceId: 26,
- url: "https://novelasligera.com/",
- sourceName: "NovelasLigera",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/es/novelasligera/icon.png?raw=true",
- lang: "Spanish",
- },
- {
- sourceId: 28,
- url: "https://yuukitls.com/",
- sourceName: "YuukiTls",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/es/yuukitls/icon.png?raw=true",
- lang: "Spanish",
- },
- {
- sourceId: 29,
- url: "https://hasutl.wordpress.com/",
- sourceName: "Hasu Translations",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/es/hasutl/icon.png?raw=true",
- lang: "Spanish",
- },
- {
- sourceId: 30,
- url: "https://oasistranslations.wordpress.com/",
- sourceName: "Oasis Translations",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/es/oasistranslations/icon.png?raw=true",
- lang: "Spanish",
- },
- {
- sourceId: 31,
- url: "http://www.novelawuxia.com/",
- sourceName: "Novela Wuxia",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/es/novelawuxia/icon.png?raw=true",
- lang: "Spanish",
- },
-
- /**
- * Japanese
- */
- {
- sourceId: 36,
- url: "https://syosetu.com",
- sourceName: "Syosetu",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/jp/syosetu/icon.png?raw=true",
- lang: "Japanese",
- },
-
- /**
- * Chinese
- */
- {
- sourceId: 52,
- url: "https://m.yushubo.com",
- sourceName: "Yushubo",
- icon: "https://github.com/LNReader/lnreader-sources/blob/main/src/ch/yushubo/icon.png?raw=true",
- lang: "Chinese",
- },
-];
-
export const getSource = (sourceId) => {
- switch (sourceId) {
- case 1: {
- return BoxNovelScraper;
- }
- case 2: {
- return ReadLightNovelScraper;
- }
- case 3: {
- return fastNovelScraper;
- }
- case 4: {
- return readNovelFullScraper;
- }
- case 5: {
- return mtlNovelScraper;
- }
- case 6: {
- return novelhallScraper;
- }
- case 7: {
- return WuxiaWorldScraper;
- }
- case 8: {
- return novelFullScraper;
- }
- case 9: {
- return novelTrenchScraper;
- }
- case 10: {
- return vipNovelScraper;
- }
- case 11: {
- return kissLightNovelScraper;
- }
- case 12: {
- return WuxiaWorldSiteScraper;
- }
- case 13: {
- return FreeWebNovelScraper;
- }
- case 14: {
- return JPMTLScraper;
- }
- case 15: {
- return lightNovelPubScraper;
- }
- case 16: {
- return WuxiaWorldCoScraper;
- }
- case 17: {
- return tapReadScraper;
- }
- case 18: {
- return novelUpdatesCcScraper;
- }
- case 19: {
- return readLightNovelCcScraper;
- }
- case 20: {
- return WuxiaWorldCloudScraper;
- }
- case 21: {
- return WoopReadScraper;
- }
- case 22: {
- return FoxaHolicScraper;
- }
- case 23: {
- return TuNovelaLigeraScraper;
- }
- case 24: {
- return SkyNovelsScraper;
- }
- case 25: {
- return EinharjarProjectScraper;
- }
- case 26: {
- return NovelasLigeraScraper;
- }
- case 27: {
- return ComradeMaoScraper;
- }
- case 28: {
- return YuukiTlsScraper;
- }
- case 29: {
- return HasuTlScraper;
- }
- case 30: {
- return OasisTranslationsScraper;
- }
- case 31: {
- return NovelaWuxiaScraper;
- }
- case 33: {
- return NovelPassionScraper;
- }
- case 34: {
- return RoyalRoadScraper;
- }
- case 35: {
- return ScribbleHubScraper;
- }
- case 36: {
- return SyosetuScraper;
- }
- case 37: {
- return LNMTLScraper;
- }
- case 38: {
- return SkyNovelScraper;
- }
- case 39: {
- return NovelCakeScraper;
- }
- case 40: {
- return NovelsRockScraper;
- }
- case 41: {
- return ZinnNovelScraper;
- }
- case 42: {
- return NovelTranslateScraper;
- }
- case 43: {
- return LunarLettersScraper;
- }
- case 44: {
- return SleepyTranslationsScraper;
- }
- case 45: {
- return FreeNovelScraper;
- }
- case 46: {
- return FirstKissNovelScraper;
- }
- case 47: {
- return DaoNovelScraper;
- }
- case 48: {
- return WuxiaBlogScraper;
- }
- case 49: {
- return WuxiaCityScraper;
- }
- case 50: {
- return NovelUpdatesScraper;
- }
- case 51: {
- return RanobesScraper;
- }
- case 52: {
- return YushuboScraper;
- }
- }
+ const scrapers = {
+ 1: BoxNovelScraper,
+ 2: ReadLightNovelScraper,
+ 3: fastNovelScraper,
+ 4: readNovelFullScraper,
+ 5: mtlNovelScraper,
+ 6: novelhallScraper,
+ 7: WuxiaWorldScraper,
+ 8: novelFullScraper,
+ 9: novelTrenchScraper,
+ 10: vipNovelScraper,
+ 11: kissLightNovelScraper,
+ 12: WuxiaWorldSiteScraper,
+ 13: FreeWebNovelScraper,
+ 14: JPMTLScraper,
+ 15: lightNovelPubScraper,
+ 16: WuxiaWorldCoScraper,
+ 17: tapReadScraper,
+ 18: novelUpdatesCcScraper,
+ 19: readLightNovelCcScraper,
+ 20: WuxiaWorldCloudScraper,
+ 21: WoopReadScraper,
+ 22: FoxaHolicScraper,
+ 23: TuNovelaLigeraScraper,
+ 24: SkyNovelsScraper,
+ 25: EinharjarProjectScraper,
+ 26: NovelasLigeraScraper,
+ 27: ComradeMaoScraper,
+ 28: YuukiTlsScraper,
+ 29: HasuTlScraper,
+ 30: OasisTranslationsScraper,
+ 31: NovelaWuxiaScraper,
+ 33: NovelPassionScraper,
+ 34: RoyalRoadScraper,
+ 35: ScribbleHubScraper,
+ 36: SyosetuScraper,
+ 37: LNMTLScraper,
+ 38: SkyNovelScraper,
+ 39: NovelCakeScraper,
+ 40: NovelsRockScraper,
+ 41: ZinnNovelScraper,
+ 42: NovelTranslateScraper,
+ 43: LunarLettersScraper,
+ 44: SleepyTranslationsScraper,
+ 45: FreeNovelScraper,
+ 46: FirstKissNovelScraper,
+ 47: DaoNovelScraper,
+ 48: WuxiaBlogScraper,
+ 49: WuxiaCityScraper,
+ 50: NovelUpdatesScraper,
+ 51: RanobesScraper,
+ 52: YushuboScraper,
+ };
+
+ return scrapers[sourceId];
};
diff --git a/src/sources/sources.json b/src/sources/sources.json
new file mode 100644
index 00000000..d695b4be
--- /dev/null
+++ b/src/sources/sources.json
@@ -0,0 +1,359 @@
+[
+ {
+ "sourceId": 1,
+ "url": "https://boxnovel.com/",
+ "sourceName": "BoxNovel",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/boxnovel/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 2,
+ "sourceName": "ReadLightNovel",
+ "url": "https://www.readlightnovel.org",
+ "lang": "English",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/readlightnovel/icon.png?raw=true"
+ },
+ {
+ "sourceId": 3,
+ "url": "https://fastnovel.net/",
+ "sourceName": "FastNovel",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/fastnovel/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 4,
+ "url": "https://readnovelfull.com/",
+ "sourceName": "ReadNovelFull",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/readnovelfull/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 5,
+ "url": "https://www.mtlnovel.com/",
+ "sourceName": "MTLNovel",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/mtlnovel/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 6,
+ "url": "https://www.novelhall.com/",
+ "sourceName": "NovelHall",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelhall/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 7,
+ "url": "https://www.wuxiaworld.com/",
+ "sourceName": "WuxiaWorld",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworld/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 8,
+ "url": "https://novelfull.com/",
+ "sourceName": "NovelFull",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelfull/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 9,
+ "url": "https://noveltrench.com/",
+ "sourceName": "NovelTrench",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/noveltrench/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 10,
+ "url": "https://vipnovel.com/",
+ "sourceName": "VipNovel",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/vipnovel/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 11,
+ "url": "https://kisslightnovels.info/",
+ "sourceName": "KissLightNovels",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/kisslightnovels/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 12,
+ "url": "https://wuxiaworldsite.co/",
+ "sourceName": "WuxiaWorldSite",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworldsite/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 13,
+ "url": "https://freewebnovel.com/",
+ "sourceName": "FreeWebNovel",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/freewebnovel/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 14,
+ "url": "https://jpmtl.com/",
+ "sourceName": "JPMTL",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/jpmtl/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 15,
+ "url": "https://www.lightnovelpub.com/",
+ "sourceName": "LightNovelPub",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/lightnovelpub/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 16,
+ "url": "https://www.wuxiaworld.co/",
+ "sourceName": "WuxiaWorld.co",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworldco/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 17,
+ "url": "http://www.tapread.com/",
+ "sourceName": "TapRead",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/tapread/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 18,
+ "url": "https://www.novelupdates.cc/",
+ "sourceName": "NovelUpdates.cc",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelupdatescc/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 19,
+ "url": "https://m.readlightnovel.cc/",
+ "sourceName": "ReadLightNovel.cc",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/readlightnovelcc/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 20,
+ "url": "http://wuxiaworld.cloud/",
+ "sourceName": "WuxiaWorld.cloud",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiaworldcloud/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 21,
+ "url": "https://woopread.com/",
+ "sourceName": "WoopRead",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/woopread/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 22,
+ "url": "https://foxaholic.com/",
+ "sourceName": "Foxaholic",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/foxaholic/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 23,
+ "url": "https://tunovelaligera.com/",
+ "sourceName": "Tunovelaligera",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/tunovelaligera/icon.png?raw=true",
+ "lang": "Spanish"
+ },
+ {
+ "sourceId": 24,
+ "url": "https://www.skynovels.net/",
+ "sourceName": "SkyNovels",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/skynovels/icon.png?raw=true",
+ "lang": "Spanish"
+ },
+ {
+ "sourceId": 25,
+ "url": "https://einherjarproject.net/",
+ "sourceName": "EinherjarProject",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/einherjarproject/icon.png?raw=true",
+ "lang": "Spanish"
+ },
+ {
+ "sourceId": 26,
+ "url": "https://novelasligera.com/",
+ "sourceName": "NovelasLigera",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/novelasligera/icon.png?raw=true",
+ "lang": "Spanish"
+ },
+ {
+ "sourceId": 27,
+ "url": "https://comrademao.com/",
+ "sourceName": "ComradeMao",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/comrademao/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 28,
+ "url": "https://yuukitls.com/",
+ "sourceName": "YuukiTls",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/yuukitls/icon.png?raw=true",
+ "lang": "Spanish"
+ },
+ {
+ "sourceId": 29,
+ "url": "https://hasutl.wordpress.com/",
+ "sourceName": "Hasu Translations",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/hasutl/icon.png?raw=true",
+ "lang": "Spanish"
+ },
+ {
+ "sourceId": 30,
+ "url": "https://oasistranslations.wordpress.com/",
+ "sourceName": "Oasis Translations",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/oasistranslations/icon.png?raw=true",
+ "lang": "Spanish"
+ },
+ {
+ "sourceId": 31,
+ "url": "http://www.novelawuxia.com/",
+ "sourceName": "Novela Wuxia",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/es/novelawuxia/icon.png?raw=true",
+ "lang": "Spanish"
+ },
+ {
+ "sourceId": 33,
+ "url": "https://www.novelpassion.com/",
+ "sourceName": "NovelPassion",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelpassion/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 34,
+ "url": "https://www.royalroad.com/",
+ "sourceName": "Royal Road",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/royalroad/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 35,
+ "url": "https://www.scribblehub.com/",
+ "sourceName": "Scribble Hub",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/scribblehub/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 36,
+ "url": "https://syosetu.com",
+ "sourceName": "Syosetu",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/jp/syosetu/icon.png?raw=true",
+ "lang": "Japanese"
+ },
+ {
+ "sourceId": 37,
+ "url": "https://lnmtl.com/",
+ "sourceName": "LNMTL",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/lnmtl/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 38,
+ "sourceUrl": "https://skynovel.org/",
+ "sourceName": "SkyNovel",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/skynovel.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 39,
+ "sourceUrl": "https://novelcake.com/",
+ "sourceName": "NovelCake",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/novelcake.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 40,
+ "sourceUrl": "https://novelsrock.com/",
+ "sourceName": "NovelsRock",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/novelsrock.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 41,
+ "sourceUrl": "https://zinnovel.com/",
+ "sourceName": "ZinnNovel",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/zinnovel.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 42,
+ "sourceUrl": "https://noveltranslate.com/",
+ "sourceName": "NovelTranslate",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/noveltranslate.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 43,
+ "sourceUrl": "https://www.lunarletters.com/",
+ "sourceName": "LunarLetters",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/lunarletters.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 44,
+ "sourceUrl": "https://sleepytranslations.com/",
+ "sourceName": "SleepyTranslations",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/sleepytranslations.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 45,
+ "sourceUrl": "https://freenovel.me/",
+ "sourceName": "FreeNovelMe",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/freenovel.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 46,
+ "sourceUrl": "https://1stkissnovel.love/",
+ "sourceName": "1stKissNovel",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/1stkissnovel.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 47,
+ "sourceUrl": "https://daonovel.com/",
+ "sourceName": "DaoNovel",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/daonovel.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 48,
+ "sourceUrl": "https://www.wuxia.blog/",
+ "sourceName": "Wuxia.Blog",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiablog/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 49,
+ "sourceUrl": "https://wuxia.city/",
+ "sourceName": "Wuxia.City",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/wuxiacity/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 50,
+ "sourceUrl": "https://www.novelupdates.com/",
+ "sourceName": "Novel Updates",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelupdates/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 51,
+ "sourceUrl": "https://ranobes.net/",
+ "sourceName": "Ranobes",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/ranobes/icon.png?raw=true",
+ "lang": "English"
+ },
+ {
+ "sourceId": 52,
+ "url": "https://m.yushubo.com",
+ "sourceName": "Yushubo",
+ "icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/ch/yushubo/icon.png?raw=true",
+ "lang": "Chinese"
+ }
+]