Parse chapter
This commit is contained in:
@@ -6,9 +6,11 @@ import { getDeviceOrientation } from "../Services/utils/helpers";
|
||||
|
||||
const NovelList = ({
|
||||
data,
|
||||
onScroll,
|
||||
renderItem,
|
||||
refreshControl,
|
||||
ListEmptyComponent,
|
||||
ListFooterComponent,
|
||||
}) => {
|
||||
const { displayMode, novelsPerRow } = useSettings();
|
||||
|
||||
@@ -36,6 +38,8 @@ const NovelList = ({
|
||||
renderItem={renderItem}
|
||||
refreshControl={refreshControl}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onScroll={onScroll}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { pinSourceAction } from "../../../redux/source/source.actions";
|
||||
import FastImage from "react-native-fast-image";
|
||||
|
||||
const ExtensionCard = ({ item, theme, isPinned }) => {
|
||||
const { sourceId, sourceName, sourceCover, sourceLanguage, status } = item;
|
||||
const { sourceId, sourceName, icon, lang } = item;
|
||||
|
||||
const navigation = useNavigation();
|
||||
const dispatch = useDispatch();
|
||||
@@ -27,7 +27,7 @@ const ExtensionCard = ({ item, theme, isPinned }) => {
|
||||
>
|
||||
<>
|
||||
<Image
|
||||
source={{ uri: sourceCover }}
|
||||
source={{ uri: icon }}
|
||||
style={styles.extensionIcon}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
@@ -41,19 +41,14 @@ const ExtensionCard = ({ item, theme, isPinned }) => {
|
||||
>
|
||||
{sourceName}
|
||||
</Text>
|
||||
<View style={{ flexDirection: "row" }}>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorSecondary,
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
{sourceLanguage}
|
||||
</Text>
|
||||
<Text style={styles.sourceStatus}>
|
||||
{status === 0 && "• Down"}
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorSecondary,
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
{lang}
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
style={{ flexDirection: "row", alignItems: "center" }}
|
||||
|
||||
@@ -13,6 +13,8 @@ import ErrorView from "../../Components/ErrorView";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { setAppSettings } from "../../redux/settings/settings.actions";
|
||||
|
||||
import { getSource } from "../../sources/sources";
|
||||
|
||||
const Extension = ({ navigation, route }) => {
|
||||
const { sourceId, sourceName, sourceUrl } = route.params;
|
||||
|
||||
@@ -22,35 +24,38 @@ const Extension = ({ navigation, route }) => {
|
||||
const { displayMode } = useSettings();
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [novels, setNovels] = useState();
|
||||
const [novels, setNovels] = useState([]);
|
||||
const [page, setPage] = useState(1);
|
||||
const [error, setError] = useState();
|
||||
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
const getSourceUrl = () => {
|
||||
if (sourceId === 1) {
|
||||
return `https://lnreader-extensions.vercel.app/api/1/novels/1/?o=rating`;
|
||||
} else {
|
||||
return `https://lnreader-extensions.vercel.app/api/${sourceId}/novels/`;
|
||||
}
|
||||
};
|
||||
// const getSourceUrl = () => {
|
||||
// if (sourceId === 1) {
|
||||
// return `https://lnreader-extensions.vercel.app/api/1/novels/1/?o=rating`;
|
||||
// } else {
|
||||
// return `https://lnreader-extensions.vercel.app/api/${sourceId}/novels/`;
|
||||
// }
|
||||
// };
|
||||
|
||||
const getSearchUrl = () => {
|
||||
if (sourceId === 1) {
|
||||
return `https://lnreader-extensions.vercel.app/api/1/search/?s=${searchText}&?o=rating`;
|
||||
} else {
|
||||
return `https://lnreader-extensions.vercel.app/api/${sourceId}/search/?s=${searchText}`;
|
||||
}
|
||||
};
|
||||
// const getSearchUrl = () => {
|
||||
// if (sourceId === 1) {
|
||||
// return `https://lnreader-extensions.vercel.app/api/1/search/?s=${searchText}&?o=rating`;
|
||||
// } else {
|
||||
// return `https://lnreader-extensions.vercel.app/api/${sourceId}/search/?s=${searchText}`;
|
||||
// }
|
||||
// };
|
||||
|
||||
const getNovels = async () => {
|
||||
const getNovels = async (page) => {
|
||||
try {
|
||||
const url = getSourceUrl();
|
||||
const source = getSource(sourceId);
|
||||
|
||||
const res = await fetch(url);
|
||||
const data = await res.json();
|
||||
// console.log(page);
|
||||
const res = await source.popularNovels(page);
|
||||
|
||||
setNovels(data);
|
||||
// console.log(res);
|
||||
|
||||
setNovels((novels) => novels.concat(res));
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
setError(error.message);
|
||||
@@ -88,9 +93,28 @@ const Extension = ({ navigation, route }) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getNovels();
|
||||
getNovels(1);
|
||||
}, []);
|
||||
|
||||
const isCloseToBottom = ({
|
||||
layoutMeasurement,
|
||||
contentOffset,
|
||||
contentSize,
|
||||
}) => {
|
||||
const paddingToBottom = 20;
|
||||
return (
|
||||
layoutMeasurement.height + contentOffset.y >=
|
||||
contentSize.height - paddingToBottom
|
||||
);
|
||||
};
|
||||
|
||||
const onScroll = ({ nativeEvent }) => {
|
||||
if (!searchText && isCloseToBottom(nativeEvent)) {
|
||||
getNovels(page + 1);
|
||||
setPage((page) => page + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const renderItem = ({ item }) => (
|
||||
<NovelCover
|
||||
item={item}
|
||||
@@ -99,7 +123,7 @@ const Extension = ({ navigation, route }) => {
|
||||
novelName: item.novelName,
|
||||
novelCover: item.novelCover,
|
||||
novelUrl: item.novelUrl,
|
||||
sourceId: item.extensionId,
|
||||
sourceId,
|
||||
})
|
||||
}
|
||||
libraryStatus={checkIFInLibrary(item.extensionId, item.novelUrl)}
|
||||
@@ -174,6 +198,14 @@ const Extension = ({ navigation, route }) => {
|
||||
data={novels}
|
||||
renderItem={renderItem}
|
||||
ListEmptyComponent={listEmptyComponent()}
|
||||
onScroll={onScroll}
|
||||
ListFooterComponent={
|
||||
!searchText && (
|
||||
<View style={{ paddingVertical: 16 }}>
|
||||
<ActivityIndicator color={theme.colorAccent} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { showToast } from "../../Hooks/showToast";
|
||||
import { getSource } from "../../sources/sources";
|
||||
|
||||
export const fetchSources = async () => {
|
||||
const url = `https://lnreader-extensions.vercel.app/api/`;
|
||||
@@ -10,35 +11,35 @@ export const fetchSources = async () => {
|
||||
};
|
||||
|
||||
export const fetchNovel = async (sourceId, novelUrl) => {
|
||||
const url = `https://lnreader-extensions.vercel.app/api/${sourceId}/novel/${novelUrl}`;
|
||||
const source = getSource(sourceId);
|
||||
|
||||
let data = await fetch(url);
|
||||
let res = await data.json();
|
||||
const res = await source.parseNovelAndChapters(novelUrl);
|
||||
|
||||
const novel = {
|
||||
novelUrl: res.novelUrl,
|
||||
sourceUrl: res.sourceUrl,
|
||||
sourceUrl: res.url,
|
||||
source: res.sourceName,
|
||||
sourceId: res.extensionId,
|
||||
sourceId: res.sourceId,
|
||||
novelName: res.novelName,
|
||||
novelCover: res.novelCover,
|
||||
novelSummary: res.novelSummary,
|
||||
author: res["Author(s)"],
|
||||
artist: res["Artist(s)"],
|
||||
status: res.Status,
|
||||
genre: res["Genre(s)"],
|
||||
novelSummary: res.summary,
|
||||
author: res.author,
|
||||
artist: res.artist,
|
||||
status: res.status,
|
||||
genre: res.genre,
|
||||
followed: 0,
|
||||
chapters: res.novelChapters,
|
||||
chapters: res.chapters,
|
||||
};
|
||||
|
||||
// console.log(novel);
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
export const fetchChapter = async (sourceId, novelUrl, chapterUrl) => {
|
||||
const url = `https://lnreader-extensions.vercel.app/api/${sourceId}/novel/${novelUrl}${chapterUrl}`;
|
||||
const source = getSource(sourceId);
|
||||
|
||||
let res = await fetch(url);
|
||||
let chapter = await res.json();
|
||||
let chapter = await source.parseChapter(novelUrl, chapterUrl);
|
||||
|
||||
return chapter;
|
||||
};
|
||||
|
||||
@@ -57,6 +57,7 @@ export const setNovel = (novel) => async (dispatch) => {
|
||||
export const getNovelAction =
|
||||
(followed, sourceId, novelUrl, novelId, sort, filter) =>
|
||||
async (dispatch) => {
|
||||
console.log(followed, sourceId, novelUrl, novelId, sort, filter);
|
||||
try {
|
||||
dispatch({ type: LOADING_NOVEL });
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { GET_SOURCES, PIN_SOURCES, SEARCH_SOURCES } from "./source.types";
|
||||
import { fetchSources } from "../../Services/Source/source";
|
||||
import { showToast } from "../../Hooks/showToast";
|
||||
import { sources } from "../../sources/sources";
|
||||
|
||||
export const getSourcesAction = () => async (dispatch) => {
|
||||
try {
|
||||
const res = await fetchSources();
|
||||
|
||||
dispatch({
|
||||
type: GET_SOURCES,
|
||||
payload: res,
|
||||
});
|
||||
dispatch({ type: GET_SOURCES, payload: sources });
|
||||
} catch (error) {
|
||||
showToast(error.message);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ const parseNovelAndChapters = async (novelUrl) => {
|
||||
|
||||
const novelCover = $(".novel-cover > a > img").attr("src");
|
||||
|
||||
let author, artist, genre, summary;
|
||||
let author, artist, genre, summary, status;
|
||||
|
||||
$(".novel-detail-item").each(function (result) {
|
||||
const detailName = $(this).find(".novel-detail-header > h6").text();
|
||||
@@ -62,7 +62,7 @@ const parseNovelAndChapters = async (novelUrl) => {
|
||||
|
||||
switch (detailName) {
|
||||
case "Genre":
|
||||
genre = detail.replace(/[\t\n]/g, ",");
|
||||
genre = detail.trim().replace(/[\t\n]/g, ",");
|
||||
break;
|
||||
case "Author(s)":
|
||||
author = detail.trim();
|
||||
@@ -73,6 +73,9 @@ const parseNovelAndChapters = async (novelUrl) => {
|
||||
case "Description":
|
||||
summary = detail;
|
||||
break;
|
||||
case "Status":
|
||||
status = detail;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -111,10 +114,12 @@ const parseNovelAndChapters = async (novelUrl) => {
|
||||
sourceId,
|
||||
sourceName,
|
||||
url,
|
||||
novelUrl,
|
||||
novelName,
|
||||
novelCover,
|
||||
genre,
|
||||
author,
|
||||
status,
|
||||
artist,
|
||||
summary,
|
||||
chapters,
|
||||
@@ -123,4 +128,67 @@ const parseNovelAndChapters = async (novelUrl) => {
|
||||
return novel;
|
||||
};
|
||||
|
||||
export { popularNovels, parseNovelAndChapters };
|
||||
const parseChapter = async (novelUrl, chapterUrl) => {
|
||||
const url = `${baseUrl}/${novelUrl}/${chapterUrl}/`;
|
||||
|
||||
const result = await fetch(url);
|
||||
const body = await result.text();
|
||||
|
||||
$ = cheerio.load(body);
|
||||
|
||||
$(".block-title > h1").find("a").remove();
|
||||
|
||||
const chapterName = $(".block-title > h1").text().replace(" - ", "");
|
||||
|
||||
$(".desc")
|
||||
.find("hr")
|
||||
.each(function (result) {
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
$(".alert").remove();
|
||||
$(".hidden").remove();
|
||||
|
||||
let chapterText = $(".desc").html();
|
||||
|
||||
// chapterText = parseHtml(chapterText).replace(
|
||||
// /\n\nSponsored Content\n\n|If audio player doesn't work, press Stop then Play button again/g,
|
||||
// ""
|
||||
// );
|
||||
|
||||
let nextChapter = null;
|
||||
|
||||
if ($("a.next.next-link").length) {
|
||||
nextChapter = $("a.next.next-link")
|
||||
.attr("href")
|
||||
.replace(`${baseUrl}/${novelUrl}/`, "");
|
||||
}
|
||||
|
||||
let prevChapter = null;
|
||||
|
||||
if ($("a.prev.prev-link").length) {
|
||||
prevChapter = $("a.prev.prev-link")
|
||||
.attr("href")
|
||||
.replace(`${baseUrl}/${novelUrl}/`, "");
|
||||
}
|
||||
|
||||
const chapter = {
|
||||
extensionId: 2,
|
||||
novelUrl,
|
||||
chapterUrl,
|
||||
chapterName,
|
||||
chapterText,
|
||||
nextChapter,
|
||||
prevChapter,
|
||||
};
|
||||
|
||||
return chapter;
|
||||
};
|
||||
|
||||
const ReadLightNovelScraper = {
|
||||
popularNovels,
|
||||
parseNovelAndChapters,
|
||||
parseChapter,
|
||||
};
|
||||
|
||||
export default ReadLightNovelScraper;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import ReadLightNovelScraper from "./en/readlightnovel";
|
||||
|
||||
export const sources = [
|
||||
{
|
||||
sourceId: 2,
|
||||
sourceName: "ReadLightNovel",
|
||||
url: "https://www.readlightnovel.org",
|
||||
lang: "English",
|
||||
icon: "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/readlightnovel/icon.png?raw=true",
|
||||
},
|
||||
];
|
||||
|
||||
export const getSource = (sourceId) => {
|
||||
switch (sourceId) {
|
||||
case 2: {
|
||||
return ReadLightNovelScraper;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user