From bd02edf0b14832c309d34c5851a907459397a9c8 Mon Sep 17 00:00:00 2001 From: Aria Moradi Date: Thu, 27 May 2021 18:37:45 +0430 Subject: [PATCH] barebones anime player --- .../main/kotlin/suwayomi/anime/AnimeAPI.kt | 19 +++-- .../kotlin/suwayomi/anime/impl/Episode.kt | 31 +++++++- .../anime/model/dataclass/EpisodeDataClass.kt | 6 +- .../anime/model/table/EpisodeTable.kt | 3 + webUI/react/src/App.tsx | 18 +++-- webUI/react/src/screens/anime/Player.tsx | 77 +++++++++++++++++++ webUI/react/src/typings.d.ts | 10 ++- 7 files changed, 139 insertions(+), 25 deletions(-) create mode 100644 webUI/react/src/screens/anime/Player.tsx diff --git a/server/src/main/kotlin/suwayomi/anime/AnimeAPI.kt b/server/src/main/kotlin/suwayomi/anime/AnimeAPI.kt index 0c13d52..130418d 100644 --- a/server/src/main/kotlin/suwayomi/anime/AnimeAPI.kt +++ b/server/src/main/kotlin/suwayomi/anime/AnimeAPI.kt @@ -21,7 +21,6 @@ import suwayomi.anime.impl.extension.Extension.installExtension import suwayomi.anime.impl.extension.Extension.uninstallExtension import suwayomi.anime.impl.extension.Extension.updateExtension import suwayomi.anime.impl.extension.ExtensionsList.getExtensionList -import suwayomi.server.JavalinSetup import suwayomi.server.JavalinSetup.future object AnimeAPI { @@ -40,7 +39,7 @@ object AnimeAPI { val pkgName = ctx.pathParam("pkgName") ctx.json( - JavalinSetup.future { + future { installExtension(pkgName) } ) @@ -51,7 +50,7 @@ object AnimeAPI { val pkgName = ctx.pathParam("pkgName") ctx.json( - JavalinSetup.future { + future { updateExtension(pkgName) } ) @@ -70,7 +69,7 @@ object AnimeAPI { val apkName = ctx.pathParam("apkName") ctx.result( - JavalinSetup.future { getExtensionIcon(apkName) } + future { getExtensionIcon(apkName) } .thenApply { ctx.header("content-type", it.second) it.first @@ -94,7 +93,7 @@ object AnimeAPI { val sourceId = ctx.pathParam("sourceId").toLong() val pageNum = ctx.pathParam("pageNum").toInt() ctx.json( - JavalinSetup.future { + future { getAnimeList(sourceId, pageNum, popular = true) } ) @@ -105,7 +104,7 @@ object AnimeAPI { val sourceId = ctx.pathParam("sourceId").toLong() val pageNum = ctx.pathParam("pageNum").toInt() ctx.json( - JavalinSetup.future { + future { getAnimeList(sourceId, pageNum, popular = false) } ) @@ -117,7 +116,7 @@ object AnimeAPI { val onlineFetch = ctx.queryParam("onlineFetch", "false").toBoolean() ctx.json( - JavalinSetup.future { + future { getAnime(animeId, onlineFetch) } ) @@ -128,7 +127,7 @@ object AnimeAPI { val animeId = ctx.pathParam("animeId").toInt() ctx.result( - JavalinSetup.future { getAnimeThumbnail(animeId) } + future { getAnimeThumbnail(animeId) } .thenApply { ctx.header("content-type", it.second) it.first @@ -164,14 +163,14 @@ object AnimeAPI { val onlineFetch = ctx.queryParam("onlineFetch")?.toBoolean() - ctx.json(JavalinSetup.future { getEpisodeList(animeId, onlineFetch) }) + ctx.json(future { getEpisodeList(animeId, onlineFetch) }) } // used to display a episode, get a episode in order to show it's app.get("/api/v1/anime/anime/:animeId/episode/:episodeIndex") { ctx -> val episodeIndex = ctx.pathParam("episodeIndex").toInt() val animeId = ctx.pathParam("animeId").toInt() - ctx.json(JavalinSetup.future { getEpisode(episodeIndex, animeId) }) + ctx.json(future { getEpisode(episodeIndex, animeId) }) } // used to modify a episode's parameters diff --git a/server/src/main/kotlin/suwayomi/anime/impl/Episode.kt b/server/src/main/kotlin/suwayomi/anime/impl/Episode.kt index 827ccfb..ab22a79 100644 --- a/server/src/main/kotlin/suwayomi/anime/impl/Episode.kt +++ b/server/src/main/kotlin/suwayomi/anime/impl/Episode.kt @@ -8,6 +8,7 @@ package suwayomi.anime.impl * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import eu.kanade.tachiyomi.source.model.SAnime +import eu.kanade.tachiyomi.source.model.SEpisode import org.jetbrains.exposed.sql.SortOrder.DESC import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.deleteWhere @@ -18,6 +19,7 @@ import org.jetbrains.exposed.sql.update import suwayomi.anime.impl.Anime.getAnime import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource import suwayomi.anime.model.dataclass.EpisodeDataClass +import suwayomi.anime.model.table.AnimeTable import suwayomi.anime.model.table.EpisodeTable import suwayomi.anime.model.table.toDataClass import suwayomi.tachidesk.impl.util.lang.awaitSingle @@ -59,7 +61,7 @@ object Episode { val episodeEntry = EpisodeTable.select { EpisodeTable.url eq fetchedEpisode.url }.firstOrNull() if (episodeEntry == null) { EpisodeTable.insert { - it[url] = source. + it[url] = fetchedEpisode.url it[name] = fetchedEpisode.name it[date_upload] = fetchedEpisode.date_upload it[episode_number] = fetchedEpisode.episode_number @@ -128,7 +130,32 @@ object Episode { /** used to display a episode, get a episode in order to show it's video */ suspend fun getEpisode(episodeIndex: Int, animeId: Int): EpisodeDataClass { - return getEpisodeList(animeId, true).first { it.index == episodeIndex } + val episode = getEpisodeList(animeId, false) + .first { it.index == episodeIndex } + + val animeEntry = transaction { AnimeTable.select { AnimeTable.id eq animeId }.first() } + val source = getAnimeHttpSource(animeEntry[AnimeTable.sourceReference]) + val fetchedLinkUrl = source.fetchEpisodeLink( + SEpisode.create().also { + it.url = episode.url + it.name = episode.name + } + ).awaitSingle() + + return EpisodeDataClass( + episode.url, + episode.name, + episode.uploadDate, + episode.episodeNumber, + episode.scanlator, + animeId, + episode.read, + episode.bookmarked, + episode.lastPageRead, + episode.index, + episode.episodeCount, + fetchedLinkUrl + ) } // /** used to display a episode, get a episode in order to show it's pages */ diff --git a/server/src/main/kotlin/suwayomi/anime/model/dataclass/EpisodeDataClass.kt b/server/src/main/kotlin/suwayomi/anime/model/dataclass/EpisodeDataClass.kt index 73c1c6a..9c7af94 100644 --- a/server/src/main/kotlin/suwayomi/anime/model/dataclass/EpisodeDataClass.kt +++ b/server/src/main/kotlin/suwayomi/anime/model/dataclass/EpisodeDataClass.kt @@ -27,9 +27,9 @@ data class EpisodeDataClass( /** this chapter's index, starts with 1 */ val index: Int, - /** total chapter count, used to calculate if there's a next and prev chapter */ - val chapterCount: Int? = null, + /** total episode count, used to calculate if there's a next and prev episode */ + val episodeCount: Int? = null, /** used to construct pages in the front-end */ - val pageCount: Int? = null, + val linkUrl: String? = null, ) diff --git a/server/src/main/kotlin/suwayomi/anime/model/table/EpisodeTable.kt b/server/src/main/kotlin/suwayomi/anime/model/table/EpisodeTable.kt index a4eaa48..4713ac1 100644 --- a/server/src/main/kotlin/suwayomi/anime/model/table/EpisodeTable.kt +++ b/server/src/main/kotlin/suwayomi/anime/model/table/EpisodeTable.kt @@ -9,6 +9,8 @@ package suwayomi.anime.model.table import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.anime.model.dataclass.EpisodeDataClass object EpisodeTable : IntIdTable() { @@ -40,4 +42,5 @@ fun EpisodeTable.toDataClass(episodeEntry: ResultRow) = episodeEntry[isBookmarked], episodeEntry[lastPageRead], episodeEntry[episodeIndex], + transaction { EpisodeTable.select { anime eq episodeEntry[anime] }.count().toInt() } ) diff --git a/webUI/react/src/App.tsx b/webUI/react/src/App.tsx index 634e576..6abd206 100644 --- a/webUI/react/src/App.tsx +++ b/webUI/react/src/App.tsx @@ -32,6 +32,7 @@ import MangaExtensions from 'screens/manga/MangaExtensions'; import SourceMangas from 'screens/manga/SourceMangas'; import SourceAnimes from 'screens/anime/SourceAnimes'; import Reader from 'screens/manga/Reader'; +import Player from 'screens/anime/Player'; import AnimeExtensions from 'screens/anime/AnimeExtensions'; export default function App() { @@ -133,13 +134,6 @@ export default function App() { - - } - /> {/* Anime Routes */} @@ -154,11 +148,21 @@ export default function App() { + + + + + } + /> + diff --git a/webUI/react/src/screens/anime/Player.tsx b/webUI/react/src/screens/anime/Player.tsx new file mode 100644 index 0000000..5a2c4f5 --- /dev/null +++ b/webUI/react/src/screens/anime/Player.tsx @@ -0,0 +1,77 @@ +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import CircularProgress from '@material-ui/core/CircularProgress'; +import { makeStyles } from '@material-ui/core/styles'; +import React, { useContext, useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import NavbarContext from 'context/NavbarContext'; +import client from 'util/client'; + +const useStyles = makeStyles({ + root: { + width: 'calc(100vw - 10px)', + height: 'calc(100vh - 64px)', + }, + + loading: { + margin: '50px auto', + }, + + video: { + maxWidth: '100%', + maxHeight: '100%', + }, +}); + +const initialEpisode = () => ({ linkUrl: '', index: -1, episodeCount: 0 }); + +export default function Player() { + const classes = useStyles(); + + const { episodeIndex, animeId } = useParams<{ episodeIndex: string, animeId: string }>(); + const [episode, setEpisode] = useState(initialEpisode()); + const [episodeLink, setEpisodeLink] = useState(); + const { setTitle } = useContext(NavbarContext); + + useEffect(() => { + setTitle('Reader'); + client.get(`/api/v1/anime/anime/${animeId}/`) + .then((response) => response.data) + .then((data: IManga) => { + setTitle(data.title); + }); + }, [episodeIndex]); + + useEffect(() => { + setEpisode(initialEpisode); + client.get(`/api/v1/anime/anime/${animeId}/episode/${episodeIndex}`) + .then((response) => response.data) + .then((data:IEpisode) => { + setEpisode(data); + setEpisodeLink(data.linkUrl); + }); + }, [episodeIndex]); + + // return spinner while chpater data is loading + if (episode.linkUrl === '') { + return ( +
+ +
+ ); + } + + return ( +
+ {/* eslint-disable-next-line jsx-a11y/media-has-caption */} + +
+ ); +} diff --git a/webUI/react/src/typings.d.ts b/webUI/react/src/typings.d.ts index 864c509..f70c485 100644 --- a/webUI/react/src/typings.d.ts +++ b/webUI/react/src/typings.d.ts @@ -55,7 +55,6 @@ interface IManga { } interface IChapter { - id: number url: string name: string uploadDate: number @@ -71,7 +70,6 @@ interface IChapter { } interface IEpisode { - id: number url: string name: string uploadDate: number @@ -83,7 +81,7 @@ interface IEpisode { lastPageRead: number index: number episodeCount: number - pageCount: number + linkUrl: string } interface IPartialChpter { @@ -92,6 +90,12 @@ interface IPartialChpter { chapterCount: number } +interface IPartialEpisode { + linkUrl: string + index: number + episodeCount: number +} + interface ICategory { id: number order: number