Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3425892a16 | |||
| 97890c1596 | |||
| c97e1b8e23 | |||
| c44537a1c0 | |||
| fd8fb367bd | |||
| a3d8aa5bcd | |||
| e7a1f8210f | |||
| 9423054b10 | |||
| d11348d9a3 | |||
| 8ec90ae8f7 | |||
| 5fde669106 | |||
| c6cdf1da0e | |||
| 8cb0a5eb73 | |||
| 09e15607d5 | |||
| a9d399ea6d | |||
| b9eddc2cc2 | |||
| bb68b2f407 | |||
| 5e0d19a9c9 | |||
| 0aa78e56c8 | |||
| 078e311995 | |||
| 9a22eba72c | |||
| 2b0032a4e4 | |||
| 708225c855 |
@@ -136,7 +136,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.1.6"
|
||||
versionName "1.1.7"
|
||||
}
|
||||
splits {
|
||||
abi {
|
||||
|
||||
@@ -72,13 +72,7 @@ const InfoItem = ({title, icon, theme}) => (
|
||||
titleStyle={{color: theme.textColorSecondary, fontSize: 14}}
|
||||
titleNumberOfLines={5}
|
||||
left={() =>
|
||||
icon && (
|
||||
<PaperList.Icon
|
||||
color={theme.textColorSecondary}
|
||||
icon={icon}
|
||||
style={{marginVertical: 0}}
|
||||
/>
|
||||
)
|
||||
icon && <PaperList.Icon color={theme.textColorSecondary} icon={icon} />
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -29,7 +29,7 @@ const NovelList = ({
|
||||
}
|
||||
};
|
||||
|
||||
const keyExtractor = useCallback(item => item.novelUrl, []);
|
||||
const keyExtractor = useCallback(item => item.sourceId + item.novelUrl, []);
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
|
||||
@@ -24,7 +24,7 @@ export const useGithubUpdateChecker = () => {
|
||||
};
|
||||
|
||||
const isNewVersion = versionTag => {
|
||||
let currentVersion = 'v1.1.6';
|
||||
let currentVersion = 'v1.1.7';
|
||||
const regex = /[^\\d.]/;
|
||||
|
||||
let newVersion = versionTag.replace(regex, '');
|
||||
@@ -34,10 +34,11 @@ export const useGithubUpdateChecker = () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
checkForRelease().catch(e => {
|
||||
showToast(`Could not connect to github:\n${`${e}`.split(':')[1].trim()}`);
|
||||
// console.error(e);
|
||||
});
|
||||
checkForRelease();
|
||||
// .catch(e => {
|
||||
// showToast(`Could not connect to github:\n${`${e}`.split(':')[1].trim()}`);
|
||||
// // console.error(e);
|
||||
// });
|
||||
}, []);
|
||||
|
||||
if (!checking) {
|
||||
|
||||
@@ -113,7 +113,7 @@ export const getNovelAction =
|
||||
* Get novel from db.
|
||||
*/
|
||||
novel = await getNovel(sourceId, novelUrl);
|
||||
novel.chapters = await getChapters(novel.novelId);
|
||||
novel.chapters = await getChapters(novel.novelId, sort, filter);
|
||||
|
||||
dispatch({
|
||||
type: GET_NOVEL,
|
||||
|
||||
@@ -99,6 +99,7 @@ const initialState = {
|
||||
*/
|
||||
|
||||
hideBackdrop: false,
|
||||
defaultChapterSort: null,
|
||||
|
||||
/**
|
||||
* Reader settings
|
||||
|
||||
@@ -89,6 +89,11 @@ const BrowseSettings = ({navigation}) => {
|
||||
}
|
||||
/>
|
||||
</Pressable>
|
||||
<List.InfoItem
|
||||
title="Searching a large number of sources may freeze the app till searching is finished."
|
||||
icon="information-outline"
|
||||
theme={theme}
|
||||
/>
|
||||
<List.Divider theme={theme} />
|
||||
<List.SubHeader theme={theme}>Browse</List.SubHeader>
|
||||
<Pressable
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import {
|
||||
/* StyleSheet, */ View,
|
||||
FlatList,
|
||||
ActivityIndicator,
|
||||
} from 'react-native';
|
||||
import {FlatList} from 'react-native';
|
||||
|
||||
import {ProgressBar} from 'react-native-paper';
|
||||
import {useSelector} from 'react-redux';
|
||||
@@ -14,7 +10,6 @@ import EmptyView from '../../../components/EmptyView';
|
||||
import {ScreenContainer} from '../../../components/Common';
|
||||
import GlobalSearchSourceItem from './GlobalSearchSourceItem';
|
||||
|
||||
import {showToast} from '../../../hooks/showToast';
|
||||
import {useLibrary, useSettings, useTheme} from '../../../hooks/reduxHooks';
|
||||
import {getSource} from '../../../sources/sources';
|
||||
|
||||
@@ -38,7 +33,6 @@ const GlobalSearch = ({route, navigation}) => {
|
||||
);
|
||||
const {searchAllSources = false} = useSettings();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [searchText, setSearchText] = useState(novelName);
|
||||
const [searchResults, setSearchResults] = useState([]);
|
||||
const [progress, setProgress] = useState(0);
|
||||
@@ -51,58 +45,64 @@ const GlobalSearch = ({route, navigation}) => {
|
||||
if (novelName) {
|
||||
onSubmitEditing();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const clearSearchbar = () => setSearchText('');
|
||||
const onChangeText = text => setSearchText(text);
|
||||
|
||||
const onSubmitEditing = async () => {
|
||||
setSearchResults([]);
|
||||
setProgress(0);
|
||||
|
||||
let globalSearchSources = searchAllSources ? sources : pinnedSources;
|
||||
|
||||
for (let i = 0; i < globalSearchSources.length; i++) {
|
||||
if (isMounted.current === false) {
|
||||
break;
|
||||
setSearchResults(
|
||||
globalSearchSources.map(item => ({
|
||||
sourceId: item.sourceId,
|
||||
sourceName: item.sourceName,
|
||||
lang: item.lang,
|
||||
loading: true,
|
||||
novels: [],
|
||||
error: null,
|
||||
})),
|
||||
);
|
||||
|
||||
globalSearchSources.map(async item => {
|
||||
if (isMounted.current === true) {
|
||||
try {
|
||||
const source = getSource(item.sourceId);
|
||||
const data = await source.searchNovels(encodeURI(searchText));
|
||||
|
||||
setSearchResults(prevState =>
|
||||
prevState.map(sourceItem =>
|
||||
sourceItem.sourceId === item.sourceId
|
||||
? {...sourceItem, novels: data, loading: false}
|
||||
: {...sourceItem},
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
setSearchResults(prevState =>
|
||||
prevState.map(sourceItem =>
|
||||
sourceItem.sourceId === item.sourceId
|
||||
? {
|
||||
...sourceItem,
|
||||
loading: false,
|
||||
error: e.message,
|
||||
}
|
||||
: sourceItem,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
setProgress(before => before + 1 / globalSearchSources.length);
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const source = getSource(globalSearchSources[i].sourceId);
|
||||
const data = await source.searchNovels(encodeURI(searchText));
|
||||
|
||||
setSearchResults(before => [
|
||||
...before,
|
||||
{
|
||||
sourceId: globalSearchSources[i].sourceId,
|
||||
sourceName: globalSearchSources[i].sourceName,
|
||||
lang: globalSearchSources[i].lang,
|
||||
novels: data,
|
||||
},
|
||||
]);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
showToast(globalSearchSources[i].sourceName + ': ' + error.message);
|
||||
setSearchResults(before => [
|
||||
...before,
|
||||
{
|
||||
sourceId: globalSearchSources[i].sourceId,
|
||||
sourceName: globalSearchSources[i].sourceName,
|
||||
lang: globalSearchSources[i].lang,
|
||||
novels: [],
|
||||
},
|
||||
]);
|
||||
setLoading(false);
|
||||
}
|
||||
setProgress(before => before + 1 / globalSearchSources.length);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const renderItem = ({item}) => (
|
||||
@@ -130,42 +130,22 @@ const GlobalSearch = ({route, navigation}) => {
|
||||
<ProgressBar color={theme.colorAccent} progress={progress} />
|
||||
)}
|
||||
<FlatList
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
padding: 4,
|
||||
}}
|
||||
contentContainerStyle={{flexGrow: 1, padding: 4}}
|
||||
data={searchResults}
|
||||
keyExtractor={item => item.sourceId.toString()}
|
||||
renderItem={renderItem}
|
||||
extraData={pinned}
|
||||
ListEmptyComponent={
|
||||
<>
|
||||
{!loading && (
|
||||
<EmptyView
|
||||
icon="__φ(..)"
|
||||
description={`Search a novel in ${
|
||||
searchAllSources
|
||||
? 'all sources'
|
||||
: pinned.length === 0
|
||||
? 'pinned sources\n(No sources pinned)'
|
||||
: 'pinned sources'
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
ListFooterComponent={
|
||||
loading && (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
padding: 16,
|
||||
}}
|
||||
>
|
||||
<ActivityIndicator size="large" color={theme.colorAccent} />
|
||||
</View>
|
||||
)
|
||||
<EmptyView
|
||||
icon="__φ(..)"
|
||||
description={`Search a novel in ${
|
||||
searchAllSources
|
||||
? 'all sources'
|
||||
: pinned.length === 0
|
||||
? 'pinned sources\n(No sources pinned)'
|
||||
: 'pinned sources'
|
||||
}`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</ScreenContainer>
|
||||
@@ -173,5 +153,3 @@ const GlobalSearch = ({route, navigation}) => {
|
||||
};
|
||||
|
||||
export default GlobalSearch;
|
||||
|
||||
// const styles = StyleSheet.create({});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import {StyleSheet, View, Text, Image, Pressable} from 'react-native';
|
||||
import {defaultCoverUri} from '../../../sources/helpers/constants';
|
||||
|
||||
const GlobalSearchNovelCover = ({
|
||||
novel,
|
||||
@@ -10,32 +11,28 @@ const GlobalSearchNovelCover = ({
|
||||
}) => {
|
||||
const {novelName, novelCover} = novel;
|
||||
|
||||
const uri =
|
||||
novelCover && !novelCover.startsWith('/') ? novelCover : defaultCoverUri;
|
||||
|
||||
return (
|
||||
<View style={{flex: 1, borderRadius: 6, overflow: 'hidden'}}>
|
||||
<View style={styles.container}>
|
||||
<Pressable
|
||||
android_ripple={{color: theme.rippleColor}}
|
||||
style={styles.novelContainer}
|
||||
style={styles.pressable}
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
>
|
||||
<>
|
||||
<Image
|
||||
source={{
|
||||
uri:
|
||||
novelCover && !novelCover.startsWith('/')
|
||||
? novelCover
|
||||
: 'https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true',
|
||||
}}
|
||||
style={[styles.novelCover, inLibrary && {opacity: 0.5}]}
|
||||
progressiveRenderingEnabled={true}
|
||||
/>
|
||||
<Text
|
||||
numberOfLines={2}
|
||||
style={[styles.title, {color: theme.textColorPrimary}]}
|
||||
>
|
||||
{novelName}
|
||||
</Text>
|
||||
</>
|
||||
<Image
|
||||
source={{uri}}
|
||||
style={[styles.novelCover, inLibrary && {opacity: 0.5}]}
|
||||
progressiveRenderingEnabled={true}
|
||||
/>
|
||||
<Text
|
||||
numberOfLines={2}
|
||||
style={[styles.title, {color: theme.textColorPrimary}]}
|
||||
>
|
||||
{novelName}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
@@ -44,10 +41,16 @@ const GlobalSearchNovelCover = ({
|
||||
export default GlobalSearchNovelCover;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
novelContainer: {
|
||||
container: {
|
||||
flex: 1,
|
||||
borderRadius: 6,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
pressable: {
|
||||
paddingHorizontal: 5,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 4,
|
||||
flex: 1,
|
||||
},
|
||||
novelCover: {
|
||||
height: 150,
|
||||
|
||||
@@ -29,7 +29,7 @@ const GlobalSearchNovelList = ({data, theme, library, navigation}) => {
|
||||
contentContainerStyle={styles.listContainer}
|
||||
horizontal={true}
|
||||
data={data}
|
||||
keyExtractor={item => item.novelUrl}
|
||||
keyExtractor={item => item.sourceId + item.novelUrl}
|
||||
renderItem={renderItem}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
/>
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import React from 'react';
|
||||
import {StyleSheet, View, Text} from 'react-native';
|
||||
import {StyleSheet, View, Text, ActivityIndicator} from 'react-native';
|
||||
|
||||
import GlobalSearchNovelList from './GlobalSearchNovelList';
|
||||
|
||||
const GlobalSearchSourceItem = ({source, library, theme, navigation}) => {
|
||||
const {sourceName, lang, novels} = source;
|
||||
const {sourceName, lang, loading, novels, error} = source;
|
||||
|
||||
const colorError = {
|
||||
color: theme.statusBar === 'dark-content' ? '#B3261E' : '#F2B8B5',
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -14,12 +18,21 @@ const GlobalSearchSourceItem = ({source, library, theme, navigation}) => {
|
||||
{lang}
|
||||
</Text>
|
||||
</View>
|
||||
<GlobalSearchNovelList
|
||||
data={novels}
|
||||
theme={theme}
|
||||
library={library}
|
||||
navigation={navigation}
|
||||
/>
|
||||
{error ? (
|
||||
<Text style={[styles.error, colorError]}>{error}</Text>
|
||||
) : loading ? (
|
||||
<ActivityIndicator
|
||||
color={theme.colorAccent}
|
||||
style={{marginVertical: 16}}
|
||||
/>
|
||||
) : (
|
||||
<GlobalSearchNovelList
|
||||
data={novels}
|
||||
theme={theme}
|
||||
library={library}
|
||||
navigation={navigation}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -31,4 +44,8 @@ const styles = StyleSheet.create({
|
||||
padding: 8,
|
||||
paddingVertical: 16,
|
||||
},
|
||||
error: {
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 16,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@ const MigrationNovelList = ({data, theme, library, navigation}) => {
|
||||
contentContainerStyle={styles.flatListCont}
|
||||
horizontal={true}
|
||||
data={data}
|
||||
keyExtractor={item => item.novelUrl}
|
||||
keyExtractor={item => item.sourceId + item.novelUrl}
|
||||
renderItem={renderItem}
|
||||
ListEmptyComponent={
|
||||
<Text
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {
|
||||
/* StyleSheet, */
|
||||
View,
|
||||
Text,
|
||||
FlatList,
|
||||
ActivityIndicator,
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import {ProgressBar} from 'react-native-paper';
|
||||
import {useLibrary, useSettings, useTheme} from '../../../hooks/reduxHooks';
|
||||
@@ -14,7 +14,6 @@ import EmptyView from '../../../components/EmptyView';
|
||||
import MigrationNovelList from './MigrationNovelList';
|
||||
import {Appbar} from '../../../components/Appbar';
|
||||
|
||||
import {showToast} from '../../../hooks/showToast';
|
||||
import {ScreenContainer} from '../../../components/Common';
|
||||
import {getSource} from '../../../sources/sources';
|
||||
|
||||
@@ -25,7 +24,6 @@ const MigrationNovels = ({navigation, route}) => {
|
||||
const isMounted = React.useRef(true);
|
||||
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [searchResults, setSearchResults] = useState('');
|
||||
const {searchAllSources = false} = useSettings();
|
||||
let {
|
||||
@@ -44,110 +42,110 @@ const MigrationNovels = ({navigation, route}) => {
|
||||
|
||||
const getSearchResults = async () => {
|
||||
let migrationSources = searchAllSources ? sources : pinnedSources;
|
||||
for (let i = 0; i < migrationSources.length; i++) {
|
||||
if (isMounted.current === false) {
|
||||
break;
|
||||
|
||||
setSearchResults(
|
||||
migrationSources.map(item => ({
|
||||
sourceId: item.sourceId,
|
||||
sourceName: item.sourceName,
|
||||
lang: item.lang,
|
||||
loading: true,
|
||||
novels: [],
|
||||
error: null,
|
||||
})),
|
||||
);
|
||||
|
||||
migrationSources.map(async item => {
|
||||
if (isMounted.current === true) {
|
||||
try {
|
||||
const source = getSource(item.sourceId);
|
||||
const data = await source.searchNovels(novelName);
|
||||
|
||||
setSearchResults(prevState =>
|
||||
prevState.map(sourceItem =>
|
||||
sourceItem.sourceId === item.sourceId
|
||||
? {...sourceItem, novels: data, loading: false}
|
||||
: {...sourceItem},
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
setSearchResults(prevState =>
|
||||
prevState.map(sourceItem =>
|
||||
sourceItem.sourceId === item.sourceId
|
||||
? {
|
||||
...sourceItem,
|
||||
loading: false,
|
||||
error: e.message,
|
||||
}
|
||||
: sourceItem,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
setProgress(before => before + 1 / migrationSources.length);
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const source = getSource(migrationSources[i].sourceId);
|
||||
const data = await source.searchNovels(novelName);
|
||||
|
||||
setSearchResults(before => [
|
||||
...before,
|
||||
{
|
||||
sourceId: migrationSources[i].sourceId,
|
||||
sourceName: migrationSources[i].sourceName,
|
||||
sourceLanguage: migrationSources[i].lang,
|
||||
novels: data,
|
||||
},
|
||||
]);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
showToast(error.message);
|
||||
|
||||
setSearchResults(before => [
|
||||
...before,
|
||||
{
|
||||
sourceId: migrationSources[i].sourceId,
|
||||
sourceName: migrationSources[i].sourceName,
|
||||
sourceLanguage: migrationSources[i].lang,
|
||||
novels: [],
|
||||
},
|
||||
]);
|
||||
setLoading(false);
|
||||
}
|
||||
setProgress(before => before + 1 / migrationSources.length);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getSearchResults();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const colorError = {
|
||||
color: theme.statusBar === 'dark-content' ? '#B3261E' : '#F2B8B5',
|
||||
};
|
||||
|
||||
const renderItem = ({item}) => (
|
||||
<>
|
||||
<View style={{padding: 8, paddingVertical: 16}}>
|
||||
<Text style={{color: theme.textColorPrimary}}>{item.sourceName}</Text>
|
||||
<Text style={{color: theme.textColorSecondary, fontSize: 12}}>
|
||||
{item.sourceLanguage}
|
||||
{item.lang}
|
||||
</Text>
|
||||
</View>
|
||||
<MigrationNovelList
|
||||
data={item.novels}
|
||||
theme={theme}
|
||||
library={library}
|
||||
navigation={navigation}
|
||||
/>
|
||||
{item.error ? (
|
||||
<Text style={[styles.error, colorError]}>{item.error}</Text>
|
||||
) : item.loading ? (
|
||||
<ActivityIndicator
|
||||
color={theme.colorAccent}
|
||||
style={{marginVertical: 16}}
|
||||
/>
|
||||
) : (
|
||||
<MigrationNovelList
|
||||
data={item.novels}
|
||||
theme={theme}
|
||||
library={library}
|
||||
navigation={navigation}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<ScreenContainer theme={theme}>
|
||||
<Appbar title={novelName} onBackAction={() => navigation.goBack()} />
|
||||
<Appbar title={novelName} onBackAction={navigation.goBack} />
|
||||
{progress > 0 && (
|
||||
<ProgressBar color={theme.colorAccent} progress={progress} />
|
||||
)}
|
||||
<FlatList
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
padding: 4,
|
||||
}}
|
||||
contentContainerStyle={{flexGrow: 1, padding: 4}}
|
||||
data={searchResults}
|
||||
keyExtractor={item => item.sourceId.toString()}
|
||||
renderItem={renderItem}
|
||||
extraData={pinned}
|
||||
ListEmptyComponent={
|
||||
<>
|
||||
{!loading && (
|
||||
<EmptyView
|
||||
icon="__φ(..)"
|
||||
description={`Search a novel in your pinned sources ${
|
||||
pinned.length === 0 ? '(No sources pinned)' : ''
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
ListFooterComponent={
|
||||
loading && (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
padding: 16,
|
||||
}}
|
||||
>
|
||||
<ActivityIndicator size="large" color={theme.colorAccent} />
|
||||
</View>
|
||||
)
|
||||
<EmptyView
|
||||
icon="__φ(..)"
|
||||
description={`Search a novel in your pinned sources ${
|
||||
pinned.length === 0 ? '(No sources pinned)' : ''
|
||||
}`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</ScreenContainer>
|
||||
@@ -156,4 +154,9 @@ const MigrationNovels = ({navigation, route}) => {
|
||||
|
||||
export default MigrationNovels;
|
||||
|
||||
// const styles = StyleSheet.create({});
|
||||
const styles = StyleSheet.create({
|
||||
error: {
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 16,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -74,7 +74,7 @@ const LibrarySheet = ({
|
||||
DESC: 'novels.novelId DESC',
|
||||
},
|
||||
{
|
||||
label: 'Aplhabetically',
|
||||
label: 'Alphabetically',
|
||||
ASC: 'novels.novelName ASC',
|
||||
DESC: 'novels.novelName DESC',
|
||||
},
|
||||
|
||||
@@ -23,14 +23,14 @@ const AboutScreen = ({navigation}) => {
|
||||
<List.Section>
|
||||
<List.Item
|
||||
title="Version"
|
||||
description="Stable 1.1.6 (26/10/21 09:00 PM)"
|
||||
description="Stable 1.1.7 (01/11/21 10:00 AM)"
|
||||
theme={theme}
|
||||
/>
|
||||
<List.Item
|
||||
title="What's new"
|
||||
onPress={() =>
|
||||
Linking.openURL(
|
||||
'https://github.com/LNReader/lnreader/releases/tag/v1.1.6',
|
||||
'https://github.com/LNReader/lnreader/releases/tag/v1.1.7',
|
||||
)
|
||||
}
|
||||
theme={theme}
|
||||
|
||||
@@ -77,15 +77,18 @@ const Novel = ({route, navigation}) => {
|
||||
const deleteDownloadsSnackbar = useModal();
|
||||
|
||||
const {
|
||||
sort = 'ORDER BY chapterId ASC',
|
||||
useFabForContinueReading = false,
|
||||
defaultChapterSort = 'ORDER BY chapterId ASC',
|
||||
} = useSettings();
|
||||
|
||||
const {
|
||||
sort = defaultChapterSort,
|
||||
filter = '',
|
||||
showChapterTitles = false,
|
||||
} = usePreferences(novel.novelId);
|
||||
|
||||
let {lastReadChapter, position} = useContinueReading(chapters, novel.novelId);
|
||||
|
||||
const {useFabForContinueReading = lastReadChapter && false} = useSettings();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
getNovelAction(followed, sourceId, novelUrl, novelId, sort, filter),
|
||||
@@ -202,7 +205,7 @@ const Novel = ({route, navigation}) => {
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{chapter.releaseDate && '• '}
|
||||
{chapter.releaseDate ? '• ' : null}
|
||||
{'Progress ' + position[chapter.chapterId].percentage + '%'}
|
||||
</Text>
|
||||
);
|
||||
|
||||
@@ -66,7 +66,7 @@ const ChapterItem = ({
|
||||
marginTop: 5,
|
||||
}}
|
||||
>
|
||||
{releaseDate && (
|
||||
{releaseDate ? (
|
||||
<Text
|
||||
style={{
|
||||
color: read
|
||||
@@ -80,7 +80,7 @@ const ChapterItem = ({
|
||||
>
|
||||
{releaseDate}
|
||||
</Text>
|
||||
)}
|
||||
) : null}
|
||||
{showProgressPercentage(chapter)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -237,9 +237,9 @@ const NovelInfoHeader = ({
|
||||
followed={novel.followed}
|
||||
theme={theme}
|
||||
/>
|
||||
{novel.genre !== null && novel.genre !== '' && (
|
||||
{novel.genre ? (
|
||||
<NovelGenres theme={theme} genre={novel.genre} />
|
||||
)}
|
||||
) : null}
|
||||
<ReadButton
|
||||
novel={novel}
|
||||
chapters={chapters}
|
||||
@@ -252,16 +252,14 @@ const NovelInfoHeader = ({
|
||||
onPress={() => novelBottomSheetRef.current.show()}
|
||||
android_ripple={{color: theme.rippleColor}}
|
||||
>
|
||||
<>
|
||||
<Text style={[{color: theme.textColorPrimary}, styles.chapters]}>
|
||||
{`${chapters.length} chapters`}
|
||||
</Text>
|
||||
<IconButton
|
||||
icon="filter-variant"
|
||||
color={filter ? theme.filterColor : theme.textColorPrimary}
|
||||
size={24}
|
||||
/>
|
||||
</>
|
||||
<Text style={[{color: theme.textColorPrimary}, styles.chapters]}>
|
||||
{`${chapters.length} chapters`}
|
||||
</Text>
|
||||
<IconButton
|
||||
icon="filter-variant"
|
||||
color={filter ? theme.filterColor : theme.textColorPrimary}
|
||||
size={24}
|
||||
/>
|
||||
</Pressable>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -37,7 +37,7 @@ const JumpToChapterModal = ({
|
||||
});
|
||||
hideModal();
|
||||
} else {
|
||||
setError(`Enter a valid chapter number (<= ${chapters.length})`);
|
||||
setError(`Enter a valid chapter number (≤ ${chapters.length})`);
|
||||
}
|
||||
} else {
|
||||
const chapter = chapters.find(chap =>
|
||||
@@ -89,7 +89,7 @@ const JumpToChapterModal = ({
|
||||
<TextInput
|
||||
value={text}
|
||||
placeholder={
|
||||
mode ? 'Chapter Name' : `Chapter Number (<= ${chapters.length})`
|
||||
mode ? 'Chapter Name' : `Chapter Number (≤ ${chapters.length})`
|
||||
}
|
||||
onChangeText={onChangeText}
|
||||
onSubmitEditing={onSubmit}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {useSettings, useTheme} from '../../hooks/reduxHooks';
|
||||
import {setAppSettings} from '../../redux/settings/settings.actions';
|
||||
import {SHOW_LAST_UPDATE_TIME} from '../../redux/updates/updates.types';
|
||||
import {useModal} from '../../hooks/useModal';
|
||||
import DefaultChapterSortModal from './components/DefaultChapterSortModal';
|
||||
|
||||
const GenralSettings = ({navigation}) => {
|
||||
const theme = useTheme();
|
||||
@@ -27,6 +28,7 @@ const GenralSettings = ({navigation}) => {
|
||||
updateLibraryOnLaunch = false,
|
||||
downloadNewChapters = false,
|
||||
onlyUpdateOngoingNovels = false,
|
||||
defaultChapterSort = 'ORDER BY chapterId ASC',
|
||||
} = useSettings();
|
||||
|
||||
const {showLastUpdateTime = true} = useSelector(
|
||||
@@ -54,6 +56,8 @@ const GenralSettings = ({navigation}) => {
|
||||
*/
|
||||
const gridSizeModalRef = useModal();
|
||||
|
||||
const defaultChapterSortModal = useModal();
|
||||
|
||||
return (
|
||||
<ScreenContainer theme={theme}>
|
||||
<Appbar title="General" onBackAction={navigation.goBack} />
|
||||
@@ -85,6 +89,18 @@ const GenralSettings = ({navigation}) => {
|
||||
theme={theme}
|
||||
/>
|
||||
<List.Divider theme={theme} />
|
||||
<List.SubHeader theme={theme}>Novel</List.SubHeader>
|
||||
<List.Item
|
||||
title="Default chapter sort"
|
||||
description={`By source ${
|
||||
defaultChapterSort === 'ORDER BY chapterId ASC'
|
||||
? '(Ascending)'
|
||||
: '(Descending)'
|
||||
}`}
|
||||
onPress={defaultChapterSortModal.showModal}
|
||||
theme={theme}
|
||||
/>
|
||||
<List.Divider theme={theme} />
|
||||
<List.SubHeader theme={theme}>Global update</List.SubHeader>
|
||||
<SwitchSetting
|
||||
label="Only update ongoing novels"
|
||||
@@ -131,6 +147,13 @@ const GenralSettings = ({navigation}) => {
|
||||
dispatch={dispatch}
|
||||
theme={theme}
|
||||
/>
|
||||
<DefaultChapterSortModal
|
||||
defaultChapterSort={defaultChapterSort}
|
||||
displayModalVisible={defaultChapterSortModal.visible}
|
||||
hideDisplayModal={defaultChapterSortModal.hideModal}
|
||||
dispatch={dispatch}
|
||||
theme={theme}
|
||||
/>
|
||||
<GridSizeModal
|
||||
dispatch={dispatch}
|
||||
novelsPerRow={novelsPerRow}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import {StyleSheet} from 'react-native';
|
||||
|
||||
import {Portal, Modal} from 'react-native-paper';
|
||||
import {SortItem} from '../../../components/Checkbox/Checkbox';
|
||||
|
||||
import {setAppSettings} from '../../../redux/settings/settings.actions';
|
||||
|
||||
const DefaultChapterSortModal = ({
|
||||
theme,
|
||||
dispatch,
|
||||
defaultChapterSort,
|
||||
hideDisplayModal,
|
||||
displayModalVisible,
|
||||
}) => {
|
||||
return (
|
||||
<Portal>
|
||||
<Modal
|
||||
visible={displayModalVisible}
|
||||
onDismiss={hideDisplayModal}
|
||||
contentContainerStyle={[
|
||||
styles.containerStyle,
|
||||
{backgroundColor: theme.colorPrimaryDark},
|
||||
]}
|
||||
>
|
||||
<SortItem
|
||||
label="By source"
|
||||
theme={theme}
|
||||
status={
|
||||
defaultChapterSort === 'ORDER BY chapterId ASC' ? 'asc' : 'desc'
|
||||
}
|
||||
onPress={() =>
|
||||
defaultChapterSort === 'ORDER BY chapterId ASC'
|
||||
? dispatch(
|
||||
setAppSettings(
|
||||
'defaultChapterSort',
|
||||
'ORDER BY chapterId DESC',
|
||||
),
|
||||
)
|
||||
: dispatch(
|
||||
setAppSettings(
|
||||
'defaultChapterSort',
|
||||
'ORDER BY chapterId ASC',
|
||||
),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Modal>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DefaultChapterSortModal;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
containerStyle: {
|
||||
paddingVertical: 20,
|
||||
margin: 20,
|
||||
borderRadius: 6,
|
||||
},
|
||||
});
|
||||
@@ -87,12 +87,13 @@ const SourceScreen = ({navigation, route}) => {
|
||||
useEffect(() => {
|
||||
getNovels();
|
||||
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [page]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => (isMounted.current = false);
|
||||
}, []);
|
||||
|
||||
const getNextPage = () => {
|
||||
if (!searchText) {
|
||||
incrementPage();
|
||||
|
||||
+10
-23
@@ -7,7 +7,7 @@ const sourceName = 'ArNovel';
|
||||
const baseUrl = 'https://arnovel.me/';
|
||||
|
||||
const popularNovels = async page => {
|
||||
const totalPages = 21;
|
||||
const totalPages = 25;
|
||||
const url = baseUrl + 'novels/page/' + page;
|
||||
|
||||
const result = await fetch(url);
|
||||
@@ -20,11 +20,7 @@ const popularNovels = async page => {
|
||||
loadedCheerio('.page-item-detail').each(function () {
|
||||
const novelName = loadedCheerio(this).find('.h5 > a').text();
|
||||
const novelCover = defaultCoverUri;
|
||||
|
||||
let novelUrl = loadedCheerio(this)
|
||||
.find('.h5 > a')
|
||||
.attr('href')
|
||||
.split('/')[4];
|
||||
const novelUrl = loadedCheerio(this).find('.h5 > a').attr('href');
|
||||
|
||||
const novel = {
|
||||
sourceId,
|
||||
@@ -40,7 +36,7 @@ const popularNovels = async page => {
|
||||
};
|
||||
|
||||
const parseNovelAndChapters = async novelUrl => {
|
||||
const url = `${baseUrl}novel/${novelUrl}`;
|
||||
const url = novelUrl;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
@@ -83,20 +79,15 @@ const parseNovelAndChapters = async novelUrl => {
|
||||
|
||||
let novelChapters = [];
|
||||
|
||||
const data = await fetch(`${url}/ajax/chapters/`, {method: 'POST'});
|
||||
const text = await data.text();
|
||||
const data = await fetch(`${url}ajax/chapters/`, {method: 'POST'});
|
||||
const chapterListRaw = await data.text();
|
||||
|
||||
loadedCheerio = cheerio.load(text);
|
||||
loadedCheerio = cheerio.load(chapterListRaw);
|
||||
|
||||
loadedCheerio('.wp-manga-chapter').each(function () {
|
||||
const chapterName = loadedCheerio(this).find('a').text().trim();
|
||||
const releaseDate = loadedCheerio(this).find('span').text().trim();
|
||||
|
||||
let chapterUrl = loadedCheerio(this).find('a').attr('href').split('/');
|
||||
|
||||
chapterUrl[6]
|
||||
? (chapterUrl = chapterUrl[5] + '/' + chapterUrl[6])
|
||||
: (chapterUrl = chapterUrl[5]);
|
||||
const releaseDate = null;
|
||||
const chapterUrl = loadedCheerio(this).find('a').attr('href');
|
||||
|
||||
novelChapters.push({chapterName, releaseDate, chapterUrl});
|
||||
});
|
||||
@@ -107,7 +98,7 @@ const parseNovelAndChapters = async novelUrl => {
|
||||
};
|
||||
|
||||
const parseChapter = async (novelUrl, chapterUrl) => {
|
||||
const url = `${baseUrl}novel/${novelUrl}/${chapterUrl}/`;
|
||||
const url = chapterUrl;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
@@ -141,11 +132,7 @@ const searchNovels = async searchTerm => {
|
||||
loadedCheerio('.c-tabs-item__content').each(function () {
|
||||
const novelName = loadedCheerio(this).find('.h4 > a').text();
|
||||
const novelCover = defaultCoverUri;
|
||||
|
||||
let novelUrl = loadedCheerio(this)
|
||||
.find('.h4 > a')
|
||||
.attr('href')
|
||||
.split('/')[4];
|
||||
const novelUrl = loadedCheerio(this).find('.h4 > a').attr('href');
|
||||
|
||||
const novel = {
|
||||
sourceId,
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import cheerio from 'react-native-cheerio';
|
||||
import {Status} from '../helpers/constants';
|
||||
|
||||
const sourceId = 54;
|
||||
const sourceName = 'RewayatAr';
|
||||
|
||||
const baseUrl = 'https://rewayat-ar.site/';
|
||||
|
||||
const popularNovels = async page => {
|
||||
const totalPages = 2;
|
||||
const url = baseUrl + 'novels/topview/page/' + page;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio('.update_item.list_category').each(function () {
|
||||
const novelName = loadedCheerio(this).find('h3 > a').text();
|
||||
const novelCover = loadedCheerio(this).find('img').attr('src');
|
||||
|
||||
let novelUrl = loadedCheerio(this).find('h3 > a').attr('href');
|
||||
|
||||
const novel = {
|
||||
sourceId,
|
||||
novelName,
|
||||
novelCover,
|
||||
novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return {totalPages, novels};
|
||||
};
|
||||
|
||||
const parseNovelAndChapters = async novelUrl => {
|
||||
const url = novelUrl;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novel = {
|
||||
sourceId,
|
||||
sourceName,
|
||||
url,
|
||||
novelUrl,
|
||||
novelName: '',
|
||||
novelCover: '',
|
||||
author: '',
|
||||
status: Status.UNKNOWN,
|
||||
genre: '',
|
||||
summary: '',
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.novelName = loadedCheerio('.truyen_info_right h1').text().trim();
|
||||
novel.novelCover = loadedCheerio('.info_image img').attr('src');
|
||||
novel.summary = loadedCheerio('#noidungm').text()?.trim();
|
||||
novel.author = loadedCheerio(
|
||||
'#main_body > div.cotgiua > div.truyen_info_wrapper > div.truyen_info > div.entry-header > div.truyen_if_wrap > ul > li:nth-child(2) > a',
|
||||
)
|
||||
.text()
|
||||
?.trim();
|
||||
|
||||
let novelChapters = [];
|
||||
|
||||
loadedCheerio('.chapter-list div.row').each(function () {
|
||||
const chapterName = loadedCheerio(this).find('a').text().trim();
|
||||
const releaseDate = loadedCheerio(this)
|
||||
.find('span:nth-child(2)')
|
||||
.text()
|
||||
.trim();
|
||||
const chapterUrl = loadedCheerio(this).find('a').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);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
const chapterName = loadedCheerio('h1.name_chapter').text();
|
||||
const chapterText = loadedCheerio('#vung_doc').html();
|
||||
|
||||
const chapter = {
|
||||
sourceId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName,
|
||||
chapterText,
|
||||
};
|
||||
|
||||
return chapter;
|
||||
};
|
||||
|
||||
const searchNovels = async searchTerm => {
|
||||
const url = `${baseUrl}search/${searchTerm}`;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio('.update_item.list_category').each(function () {
|
||||
const novelName = loadedCheerio(this).find('h3 > a').text();
|
||||
const novelCover = loadedCheerio(this).find('img').attr('src');
|
||||
|
||||
let novelUrl = loadedCheerio(this).find('h3 > a').attr('href');
|
||||
|
||||
const novel = {sourceId, novelName, novelCover, novelUrl};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
const RewayatArScraper = {
|
||||
popularNovels,
|
||||
parseNovelAndChapters,
|
||||
parseChapter,
|
||||
searchNovels,
|
||||
};
|
||||
|
||||
export default RewayatArScraper;
|
||||
@@ -89,7 +89,7 @@ const parseNovelAndChapters = async novelUrl => {
|
||||
|
||||
let chapters = [];
|
||||
|
||||
loadedCheerio('.bookshelf-list #chapter').each(function () {
|
||||
loadedCheerio('.bookshelf-list #chapter a').each(function () {
|
||||
let chapterUrl = loadedCheerio(this).attr('href').substring(1);
|
||||
let chapterName = loadedCheerio(this).text();
|
||||
let releaseDate = null;
|
||||
@@ -134,11 +134,11 @@ const parseChapter = async (novelUrl, chapterUrl) => {
|
||||
};
|
||||
|
||||
const searchNovels = async searchTerm => {
|
||||
const searchUrl = `${baseUrl}/search.html`;
|
||||
const searchUrl = `${baseUrl}/so.html`;
|
||||
|
||||
const result = await fetch(searchUrl, {
|
||||
method: 'post',
|
||||
body: JSON.stringify({keyword: searchTerm}),
|
||||
body: JSON.stringify({ss: searchTerm}),
|
||||
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",
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
import cheerio from 'react-native-cheerio';
|
||||
import {defaultCoverUri, Status} from '../helpers/constants';
|
||||
|
||||
const sourceId = 89;
|
||||
const sourceName = 'IndoWebNovel';
|
||||
|
||||
const baseUrl = 'https://mtlcorner.com/';
|
||||
|
||||
const popularNovels = async page => {
|
||||
const totalPages = 1;
|
||||
const url = `${baseUrl}series/`;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio('.wp-block-latest-posts__list li').each(function () {
|
||||
const novelName = loadedCheerio(this).find('a').text();
|
||||
const novelCover = defaultCoverUri;
|
||||
const novelUrl = loadedCheerio(this).find('a').attr('href');
|
||||
|
||||
const novel = {sourceId, novelName, novelCover, novelUrl};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return {totalPages, novels};
|
||||
};
|
||||
|
||||
const parseNovelAndChapters = async novelUrl => {
|
||||
const url = novelUrl;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novel = {
|
||||
sourceId,
|
||||
sourceName,
|
||||
url,
|
||||
novelUrl,
|
||||
novelName: '',
|
||||
novelCover: '',
|
||||
author: '',
|
||||
status: '',
|
||||
genre: '',
|
||||
summary: '',
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
const novelName = loadedCheerio('.entry-title').text().trim();
|
||||
novel.novelName = novelName;
|
||||
|
||||
novel.novelCover = loadedCheerio('figure > img').attr('src');
|
||||
|
||||
novel.summary = loadedCheerio('figcaption').text().trim();
|
||||
|
||||
novel.status = Status.UNKNOWN;
|
||||
|
||||
novel.author = loadedCheerio(
|
||||
' li.post-author.meta-wrapper > span.meta-text > a',
|
||||
)
|
||||
.text()
|
||||
?.trim();
|
||||
|
||||
let chapters = [];
|
||||
|
||||
loadedCheerio('.lcp_catlist li').each(function () {
|
||||
let chapterNameSuffix = loadedCheerio(this)
|
||||
.find('a')
|
||||
.text()
|
||||
?.trim()
|
||||
.match(/([a-zA-Z\s?]+)(\d+)-(\d+)/);
|
||||
|
||||
if (chapterNameSuffix) {
|
||||
let chaptersTemp = [];
|
||||
|
||||
let chapterUrlSuffix = loadedCheerio(this).find('a').attr('href');
|
||||
|
||||
let i = chapterNameSuffix[2];
|
||||
|
||||
while (i <= chapterNameSuffix[3]) {
|
||||
const chapterName = `${chapterNameSuffix[1].replace(
|
||||
novelName,
|
||||
'',
|
||||
)} ${i}`;
|
||||
const chapterUrl = `${chapterUrlSuffix}${i - chapterNameSuffix[2] + 1}`;
|
||||
const releaseDate = null;
|
||||
|
||||
chaptersTemp.push({chapterName, releaseDate, chapterUrl});
|
||||
i++;
|
||||
}
|
||||
|
||||
chapters.push(...chaptersTemp.reverse());
|
||||
}
|
||||
});
|
||||
|
||||
novel.chapters = chapters.reverse();
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
const parseChapter = async (novelUrl, chapterUrl) => {
|
||||
const url = chapterUrl;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
const chapterName = '';
|
||||
const chapterText = loadedCheerio('.entry-content').html();
|
||||
|
||||
const chapter = {
|
||||
sourceId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName,
|
||||
chapterText,
|
||||
};
|
||||
|
||||
return chapter;
|
||||
};
|
||||
|
||||
const searchNovels = async searchTerm => {
|
||||
const url = `${baseUrl}series/`;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio('.wp-block-latest-posts__list li').each(function () {
|
||||
const novelName = loadedCheerio(this).find('a').text();
|
||||
const novelCover = defaultCoverUri;
|
||||
const novelUrl = loadedCheerio(this).find('a').attr('href');
|
||||
|
||||
const novel = {sourceId, novelName, novelCover, novelUrl};
|
||||
|
||||
if (novelName.toLowerCase().includes(searchTerm.toLowerCase())) {
|
||||
novels.push(novel);
|
||||
}
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
const MTLCornerScraper = {
|
||||
popularNovels,
|
||||
parseNovelAndChapters,
|
||||
parseChapter,
|
||||
searchNovels,
|
||||
};
|
||||
|
||||
export default MTLCornerScraper;
|
||||
@@ -0,0 +1,166 @@
|
||||
import cheerio from 'react-native-cheerio';
|
||||
import {Status} from '../helpers/constants';
|
||||
|
||||
const sourceId = 90;
|
||||
const sourceName = 'NitroScans';
|
||||
|
||||
const baseUrl = 'https://nitroscans.com/';
|
||||
|
||||
const popularNovels = async page => {
|
||||
const totalPages = 2;
|
||||
const url = baseUrl + 'manga-genre/novel/page/' + page;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio('.page-item-detail').each(function () {
|
||||
const novelName = loadedCheerio(this).find('h3 > a').text();
|
||||
const novelCover = loadedCheerio(this).find('img').attr('data-src');
|
||||
const novelUrl = loadedCheerio(this).find('h3 > a').attr('href');
|
||||
|
||||
const novel = {sourceId, novelName, novelCover, novelUrl};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return {totalPages, novels};
|
||||
};
|
||||
|
||||
const parseNovelAndChapters = async novelUrl => {
|
||||
const url = novelUrl;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novel = {
|
||||
sourceId,
|
||||
sourceName,
|
||||
url,
|
||||
novelUrl,
|
||||
novelName: '',
|
||||
novelCover: '',
|
||||
author: '',
|
||||
status: Status.UNKNOWN,
|
||||
genre: '',
|
||||
summary: '',
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
loadedCheerio('.manga-title-badges.custom.novel').remove();
|
||||
|
||||
novel.novelName = loadedCheerio('.post-title > h1').text().trim();
|
||||
novel.novelCover = loadedCheerio('.summary_image')
|
||||
.find('img')
|
||||
.attr('data-src');
|
||||
|
||||
novel.summary = loadedCheerio('.summary__content').text()?.trim();
|
||||
|
||||
loadedCheerio('.post-content_item').each(function () {
|
||||
const detailName = loadedCheerio(this)
|
||||
.find('.summary-heading > h5')
|
||||
.text()
|
||||
.trim();
|
||||
const detail = loadedCheerio(this).find('.summary-content').text().trim();
|
||||
|
||||
switch (detailName) {
|
||||
case 'Genre(s)':
|
||||
novel.genre = detail.replace(/[\t\n]/g, ',');
|
||||
break;
|
||||
case 'Author(s)':
|
||||
novel.author = detail;
|
||||
break;
|
||||
case 'Status':
|
||||
novel.status = detail.includes('OnGoing')
|
||||
? Status.ONGOING
|
||||
: Status.COMPLETED;
|
||||
break;
|
||||
}
|
||||
});
|
||||
let novelChapters = [];
|
||||
|
||||
const novelId = loadedCheerio('.rating-post-id').attr('value');
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append('action', 'manga_get_chapters');
|
||||
formData.append('manga', novelId);
|
||||
|
||||
const data = await fetch(baseUrl + 'wp-admin/admin-ajax.php', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
const text = await data.text();
|
||||
|
||||
loadedCheerio = cheerio.load(text);
|
||||
|
||||
loadedCheerio('.wp-manga-chapter').each(function () {
|
||||
const chapterName = loadedCheerio(this).find('a').text().trim();
|
||||
const releaseDate = loadedCheerio(this).find('span').text().trim();
|
||||
const chapterUrl = loadedCheerio(this).find('a').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);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
const chapterName = loadedCheerio('h1#chapter-heading').text();
|
||||
const chapterText = loadedCheerio('.reading-content').html();
|
||||
|
||||
const chapter = {
|
||||
sourceId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName,
|
||||
chapterText,
|
||||
};
|
||||
|
||||
return chapter;
|
||||
};
|
||||
|
||||
const searchNovels = async searchTerm => {
|
||||
const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga&genre%5B%5D=novel&op=&author=&artist=&release=&adult=`;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio('.c-tabs-item__content').each(function () {
|
||||
const novelName = loadedCheerio(this).find('h3 > a').text();
|
||||
const novelCover = loadedCheerio(this).find('img').attr('data-src');
|
||||
const novelUrl = loadedCheerio(this).find('h3 > a').attr('href');
|
||||
|
||||
const novel = {sourceId, novelName, novelCover, novelUrl};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
const NitroScansScraper = {
|
||||
popularNovels,
|
||||
parseNovelAndChapters,
|
||||
parseChapter,
|
||||
searchNovels,
|
||||
};
|
||||
|
||||
export default NitroScansScraper;
|
||||
@@ -0,0 +1,182 @@
|
||||
import cheerio from 'react-native-cheerio';
|
||||
|
||||
const sourceId = 92;
|
||||
const sourceName = 'NovelForest';
|
||||
|
||||
const baseUrl = 'https://novelforest.com/';
|
||||
|
||||
const popularNovels = async page => {
|
||||
const totalPages = 25;
|
||||
const url = `${baseUrl}popular?page=${page}`;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio('.book-item').each(function () {
|
||||
const novelName = loadedCheerio(this).find('.title').text();
|
||||
const novelCover =
|
||||
'https:' + loadedCheerio(this).find('img').attr('data-src');
|
||||
const novelUrl =
|
||||
baseUrl + loadedCheerio(this).find('.title a').attr('href').substring(1);
|
||||
|
||||
const novel = {sourceId, novelName, novelCover, novelUrl};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return {totalPages, novels};
|
||||
};
|
||||
|
||||
const parseNovelAndChapters = async novelUrl => {
|
||||
const url = novelUrl;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novel = {
|
||||
sourceId,
|
||||
sourceName,
|
||||
url,
|
||||
novelUrl,
|
||||
novelName: '',
|
||||
novelCover: '',
|
||||
author: '',
|
||||
status: '',
|
||||
genre: '',
|
||||
summary: '',
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.novelName = loadedCheerio('.name h1').text().trim();
|
||||
|
||||
novel.novelCover =
|
||||
'https:' + loadedCheerio('.img-cover img').attr('data-src');
|
||||
|
||||
novel.summary = loadedCheerio('#info > p').text().trim();
|
||||
|
||||
novel.author = loadedCheerio(
|
||||
'body > div.layout > div.main-container.book-details > div > div.row.no-gutters > div.col-lg-8 > div.book-info > div.detail > div.meta.box.mt-1.p-10 > p:nth-child(1) > a > span',
|
||||
)
|
||||
.text()
|
||||
?.trim();
|
||||
|
||||
novel.status = loadedCheerio(
|
||||
'body > div.layout > div.main-container.book-details > div > div.row.no-gutters > div.col-lg-8 > div.book-info > div.detail > div.meta.box.mt-1.p-10 > p:nth-child(2) > a > span',
|
||||
)
|
||||
.text()
|
||||
?.trim();
|
||||
|
||||
novel.genre = loadedCheerio(
|
||||
'body > div.layout > div.main-container.book-details > div > div.row.no-gutters > div.col-lg-8 > div.book-info > div.detail > div.meta.box.mt-1.p-10 > p:nth-child(3)',
|
||||
)
|
||||
.text()
|
||||
?.replace('Genres :', '')
|
||||
.replace(/[\s\n]+/g, ' ')
|
||||
.trim();
|
||||
|
||||
let chapters = [];
|
||||
|
||||
const delay = ms => new Promise(res => setTimeout(res, ms));
|
||||
|
||||
const lastPage = loadedCheerio('label:contains("❯❯")').attr('id');
|
||||
|
||||
for (let i = 1; i <= lastPage; i++) {
|
||||
const chaptersUrl =
|
||||
novelUrl.replace(baseUrl, 'https://novelforest.com/api/novels/') +
|
||||
'/chapters?page=' +
|
||||
i;
|
||||
|
||||
const chaptersRequest = await fetch(chaptersUrl);
|
||||
const chaptersJSON = await chaptersRequest.json();
|
||||
|
||||
loadedCheerio = cheerio.load(chaptersJSON.chapters);
|
||||
|
||||
loadedCheerio('li').each(function () {
|
||||
const chapterName = loadedCheerio(this)
|
||||
.find('.chapter-title')
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
const releaseDate = loadedCheerio(this)
|
||||
.find('.chapter-update')
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
const chapterUrl =
|
||||
baseUrl + loadedCheerio(this).find('a').attr('href').substring(1);
|
||||
|
||||
chapters.push({chapterName, releaseDate, chapterUrl});
|
||||
|
||||
delay(1000);
|
||||
});
|
||||
}
|
||||
|
||||
novel.chapters = chapters;
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
const parseChapter = async (novelUrl, chapterUrl) => {
|
||||
const url = chapterUrl;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
loadedCheerio('#listen-chapter').remove();
|
||||
loadedCheerio('#google_translate_element').remove();
|
||||
|
||||
const chapterName = loadedCheerio('#chapter__content > h1').text();
|
||||
const chapterText = loadedCheerio('.chapter__content').html();
|
||||
|
||||
const chapter = {
|
||||
sourceId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName,
|
||||
chapterText,
|
||||
};
|
||||
|
||||
return chapter;
|
||||
};
|
||||
|
||||
const searchNovels = async searchTerm => {
|
||||
const url = `${baseUrl}search?q=${searchTerm}`;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio('.book-item').each(function () {
|
||||
const novelName = loadedCheerio(this).find('.title').text();
|
||||
const novelCover =
|
||||
'https:' + loadedCheerio(this).find('img').attr('data-src');
|
||||
const novelUrl =
|
||||
baseUrl + loadedCheerio(this).find('.title a').attr('href').substring(1);
|
||||
|
||||
const novel = {sourceId, novelName, novelCover, novelUrl};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
const NovelForestScraper = {
|
||||
popularNovels,
|
||||
parseNovelAndChapters,
|
||||
parseChapter,
|
||||
searchNovels,
|
||||
};
|
||||
|
||||
export default NovelForestScraper;
|
||||
@@ -39,7 +39,7 @@ const parseNovelAndChapters = async novelUrl => {
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novel = {};
|
||||
|
||||
@@ -93,6 +93,11 @@ const parseNovelAndChapters = async novelUrl => {
|
||||
|
||||
let novelChapters = [];
|
||||
|
||||
const data = await fetch(`${url}ajax/chapters/`, {method: 'POST'});
|
||||
const text = await data.text();
|
||||
|
||||
loadedCheerio = cheerio.load(text);
|
||||
|
||||
loadedCheerio('.wp-manga-chapter').each(function () {
|
||||
const chapterName = loadedCheerio(this)
|
||||
.find('a')
|
||||
|
||||
@@ -41,7 +41,7 @@ const parseNovelAndChapters = async novelUrl => {
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novel = {};
|
||||
|
||||
@@ -96,6 +96,11 @@ const parseNovelAndChapters = async novelUrl => {
|
||||
|
||||
let novelChapters = [];
|
||||
|
||||
const data = await fetch(`${url}ajax/chapters/`, {method: 'POST'});
|
||||
const text = await data.text();
|
||||
|
||||
loadedCheerio = cheerio.load(text);
|
||||
|
||||
loadedCheerio('.wp-manga-chapter').each(function () {
|
||||
const chapterName = loadedCheerio(this)
|
||||
.find('a')
|
||||
@@ -165,10 +170,10 @@ const searchNovels = async searchTerm => {
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio('.c-tabs-item__content').each(function () {
|
||||
const novelName = loadedCheerio(this).find('h4 > a').text();
|
||||
const novelName = loadedCheerio(this).find('.post-title a').text();
|
||||
const novelCover = loadedCheerio(this).find('img').attr('src');
|
||||
|
||||
let novelUrl = loadedCheerio(this).find('h4 > a').attr('href');
|
||||
let novelUrl = loadedCheerio(this).find('.post-title a').attr('href');
|
||||
novelUrl = novelUrl.replace(`${baseUrl}vipnovel/`, '');
|
||||
|
||||
const novel = {
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import cheerio from 'react-native-cheerio';
|
||||
|
||||
const sourceId = 88;
|
||||
const sourceName = 'Chireads';
|
||||
|
||||
const baseUrl = 'https://chireads.com/';
|
||||
|
||||
const popularNovels = async page => {
|
||||
const totalPages = 2;
|
||||
const url = `${baseUrl}category/translatedtales/page/${page}/`;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio('#content li').each(function () {
|
||||
const novelName = loadedCheerio(this).find('.news-list-tit h5').text();
|
||||
const novelCover = loadedCheerio(this).find('img').attr('src');
|
||||
const novelUrl = loadedCheerio(this)
|
||||
.find('.news-list-tit h5 a')
|
||||
.attr('href');
|
||||
|
||||
const novel = {sourceId, novelName, novelCover, novelUrl};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return {totalPages, novels};
|
||||
};
|
||||
|
||||
const parseNovelAndChapters = async novelUrl => {
|
||||
const url = novelUrl;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novel = {
|
||||
sourceId,
|
||||
sourceName,
|
||||
url,
|
||||
novelUrl,
|
||||
novelName: '',
|
||||
novelCover: '',
|
||||
author: '',
|
||||
status: '',
|
||||
genre: '',
|
||||
summary: '',
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.novelName = loadedCheerio('.inform-title').text().trim();
|
||||
|
||||
novel.novelCover = loadedCheerio('.inform-product img').attr('src');
|
||||
|
||||
novel.summary = loadedCheerio('.inform-inform-txt').text().trim();
|
||||
|
||||
let chapters = [];
|
||||
|
||||
loadedCheerio('.chapitre-table a').each(function () {
|
||||
const chapterName = loadedCheerio(this).text().trim();
|
||||
const releaseDate = null;
|
||||
const chapterUrl = loadedCheerio(this).attr('href');
|
||||
|
||||
chapters.push({chapterName, releaseDate, chapterUrl});
|
||||
});
|
||||
|
||||
novel.chapters = chapters;
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
const parseChapter = async (novelUrl, chapterUrl) => {
|
||||
const url = chapterUrl;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
const chapterName = loadedCheerio('.article-title').text();
|
||||
const chapterText = loadedCheerio('#content').html();
|
||||
|
||||
const chapter = {
|
||||
sourceId,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName,
|
||||
chapterText,
|
||||
};
|
||||
|
||||
return chapter;
|
||||
};
|
||||
|
||||
const searchNovels = async searchTerm => {
|
||||
const url = `${baseUrl}search?x=0&y=0&name=${searchTerm}`;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels = [];
|
||||
|
||||
loadedCheerio('#content li').each(function () {
|
||||
const novelName = loadedCheerio(this).find('.news-list-tit h5').text();
|
||||
const novelCover = loadedCheerio(this).find('img').attr('src');
|
||||
const novelUrl = loadedCheerio(this)
|
||||
.find('.news-list-tit h5 a')
|
||||
.attr('href');
|
||||
|
||||
const novel = {sourceId, novelName, novelCover, novelUrl};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
const ChireadsScraper = {
|
||||
popularNovels,
|
||||
parseNovelAndChapters,
|
||||
parseChapter,
|
||||
searchNovels,
|
||||
};
|
||||
|
||||
export default ChireadsScraper;
|
||||
@@ -23,6 +23,7 @@ const getPath = extensionId => {
|
||||
63: {novels: 'novel', novel: 'novel', chapter: 'novel'},
|
||||
75: {novels: 'series', novel: 'series', chapter: 'series'},
|
||||
80: {novels: 'manga', novel: 'manga', chapter: 'manga'},
|
||||
91: {novels: 'manga', novel: 'manga', chapter: 'manga'},
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -134,6 +135,7 @@ export const LightNovelHeavenScraper = new MadaraScraper(
|
||||
'https://lightnovelheaven.com/',
|
||||
'LightNovelHeaven',
|
||||
getPath(57),
|
||||
true,
|
||||
);
|
||||
|
||||
export const LightNovelsHubScraper = new MadaraScraper(
|
||||
@@ -162,8 +164,8 @@ export const WebNovelLoverScraper = new MadaraScraper(
|
||||
|
||||
export const BoxNovelOnlineScraper = new MadaraScraper(
|
||||
63,
|
||||
'https://boxnovel.online/',
|
||||
'BoxNovel.online',
|
||||
'https://tipnovel.com/',
|
||||
'TipNovel',
|
||||
getPath(63),
|
||||
true,
|
||||
);
|
||||
@@ -211,3 +213,10 @@ export const MoreNovelScraper = new MadaraScraper(
|
||||
getPath(84),
|
||||
true,
|
||||
);
|
||||
|
||||
export const OnlyMTLScraper = new MadaraScraper(
|
||||
91,
|
||||
'https://www.onlymtl.com/',
|
||||
'OnlyMTL',
|
||||
getPath(91),
|
||||
);
|
||||
|
||||
@@ -5,9 +5,3 @@ export const KolNovelScraper = new WPMangaStreamScraper(
|
||||
'https://alightnovel.com/',
|
||||
'KolNovel',
|
||||
);
|
||||
|
||||
export const RewayatArScraper = new WPMangaStreamScraper(
|
||||
54,
|
||||
'https://rewayat-ar.site/',
|
||||
'RewayatAr',
|
||||
);
|
||||
|
||||
+30
-25
@@ -90,24 +90,27 @@ const parseChapter = async (novelUrl, chapterUrl) => {
|
||||
let chapterName = loadedCheerio(
|
||||
'body > div.pusher.container_main > div:nth-child(5) > div:nth-child(1) > div.title-wrapper > h1',
|
||||
).text();
|
||||
|
||||
loadedCheerio(
|
||||
'body > div.pusher.container_main > div:nth-child(5) > div:nth-child(1) img',
|
||||
).each(function () {
|
||||
if (!loadedCheerio(this).attr('src')?.startsWith('http')) {
|
||||
const dataMediaId = loadedCheerio(this).attr('data-media-id');
|
||||
const newSrc = `https://ranobehub.org/api/media/${dataMediaId}`;
|
||||
|
||||
loadedCheerio(this).attr('src', newSrc);
|
||||
}
|
||||
});
|
||||
|
||||
let chapterText = loadedCheerio(
|
||||
'body > div.pusher.container_main > div:nth-child(5) > div:nth-child(1)',
|
||||
).html();
|
||||
|
||||
if (chapterText) {
|
||||
/**
|
||||
* Convert relative url to absolute
|
||||
*/
|
||||
|
||||
chapterText = chapterText
|
||||
.replace(/<\s*script[^>]*>[\s\S]*?<\/script>/gim, '')
|
||||
.replace(
|
||||
/data-src="(.*)"/g,
|
||||
`src="https://ranobehub.org/img/ranobe/content/${chapterId}/$1.jpg"`,
|
||||
);
|
||||
}
|
||||
|
||||
// console.log(chapterText);
|
||||
// Remove script tags
|
||||
chapterText = chapterText?.replace(
|
||||
/<\s*script[^>]*>[\s\S]*?<\/script>/gim,
|
||||
'',
|
||||
);
|
||||
|
||||
const chapter = {
|
||||
sourceId,
|
||||
@@ -121,23 +124,25 @@ const parseChapter = async (novelUrl, chapterUrl) => {
|
||||
};
|
||||
|
||||
const searchNovels = async searchTerm => {
|
||||
const url = 'https://ranobehub.org/api/fulltext/ranobe?query=' + searchTerm;
|
||||
const url = `https://ranobehub.org/api/fulltext/global?query=${searchTerm}&take=5`;
|
||||
|
||||
const result = await fetch(url);
|
||||
const data = await result.json();
|
||||
|
||||
let novels = [];
|
||||
|
||||
data.data.map(novel =>
|
||||
novels.push({
|
||||
sourceId,
|
||||
novelName: novel.names.rus,
|
||||
novelUrl: novel.url.match(
|
||||
/https:\/\/ranobehub\.org\/ranobe\/(.*?)\?utm_source=search_name&utm_medium=search&utm_campaign=search_using/,
|
||||
)[1],
|
||||
novelCover: novel.image,
|
||||
}),
|
||||
);
|
||||
data
|
||||
.find(item => item.meta.key === 'ranobe')
|
||||
?.data.map(novel =>
|
||||
novels.push({
|
||||
sourceId,
|
||||
novelName: novel.names.rus,
|
||||
novelUrl: novel.url.match(
|
||||
/https:\/\/ranobehub\.org\/ranobe\/(.*?)\?utm_source=search_name&utm_medium=search&utm_campaign=search_using/,
|
||||
)[1],
|
||||
novelCover: novel.image,
|
||||
}),
|
||||
);
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
+12
-4
@@ -50,6 +50,7 @@ import {
|
||||
NovelMultiverseScraper,
|
||||
NovelsRockScraper,
|
||||
NovelTranslateScraper,
|
||||
OnlyMTLScraper,
|
||||
ReadWebNovelsScraper,
|
||||
SkyNovelScraper,
|
||||
SleepyTranslationsScraper,
|
||||
@@ -63,10 +64,7 @@ import WuxiaCityScraper from './en/wuxiacity';
|
||||
import NovelUpdatesScraper from './en/novelupdates';
|
||||
import RanobesScraper from './en/ranobes';
|
||||
import YushuboScraper from './ch/yushubo';
|
||||
import {
|
||||
KolNovelScraper,
|
||||
RewayatArScraper,
|
||||
} from './multisrc/wpmangastream/WPMangaStreamGenerator';
|
||||
import {KolNovelScraper} from './multisrc/wpmangastream/WPMangaStreamGenerator';
|
||||
import WLNUpdatesScraper from './en/wlnupdates';
|
||||
import ReaperScansScraper from './en/reaperscans';
|
||||
import ReadwnScraper from './en/readwn';
|
||||
@@ -88,6 +86,11 @@ import MyLoveNovelScraper from './en/mylovenovel';
|
||||
import RiwayatScraper from './ar/riwayat';
|
||||
import NovelRinganScraper from './id/novelringan';
|
||||
import IndoWebNovelScraper from './id/indowebnovel';
|
||||
import ChireadsScraper from './fr/chireads';
|
||||
import RewayatArScraper from './ar/rewayatar';
|
||||
import MTLCornerScraper from './en/mtlcorner';
|
||||
import NitroScansScraper from './en/nitroscans';
|
||||
import NovelForestScraper from './en/novelforest';
|
||||
|
||||
export const getSource = sourceId => {
|
||||
const scrapers = {
|
||||
@@ -177,6 +180,11 @@ export const getSource = sourceId => {
|
||||
85: RiwayatScraper,
|
||||
86: NovelRinganScraper,
|
||||
87: IndoWebNovelScraper,
|
||||
88: ChireadsScraper,
|
||||
89: MTLCornerScraper,
|
||||
90: NitroScansScraper,
|
||||
91: OnlyMTLScraper,
|
||||
92: NovelForestScraper,
|
||||
};
|
||||
|
||||
return scrapers[sourceId];
|
||||
|
||||
@@ -600,5 +600,40 @@
|
||||
"sourceName": "IndoWebNovel",
|
||||
"icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/id/indowebnovel/icon.png?raw=true",
|
||||
"lang": "Indonesian"
|
||||
},
|
||||
{
|
||||
"sourceId": 88,
|
||||
"url": "https://chireads.com/",
|
||||
"sourceName": "Chireads",
|
||||
"icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/fr/chireads/icon.png?raw=true",
|
||||
"lang": "French"
|
||||
},
|
||||
{
|
||||
"sourceId": 89,
|
||||
"url": "https://mtlcorner.com/",
|
||||
"sourceName": "MTLCorner",
|
||||
"icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/mtlcorner/icon.png?raw=true",
|
||||
"lang": "English"
|
||||
},
|
||||
{
|
||||
"sourceId": 90,
|
||||
"url": "https://nitroscans.com/",
|
||||
"sourceName": "NitroScans",
|
||||
"icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/nitroscans/icon.png?raw=true",
|
||||
"lang": "English"
|
||||
},
|
||||
{
|
||||
"sourceId": 91,
|
||||
"url": "https://www.onlymtl.com/",
|
||||
"sourceName": "OnlyMTL",
|
||||
"icon": "https://github.com/LNReader/lnreader-sources/blob/main/multisrc/madara/icons/onlymtl.png?raw=true",
|
||||
"lang": "English"
|
||||
},
|
||||
{
|
||||
"sourceId": 92,
|
||||
"url": "https://novelforest.com/",
|
||||
"sourceName": "NovelForest",
|
||||
"icon": "https://github.com/LNReader/lnreader-sources/blob/main/src/en/novelforest/icon.png?raw=true",
|
||||
"lang": "English"
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user