mirror of
https://github.com/tachiyomiorg/tachiyomi-extensions-inspector.git
synced 2024-12-26 16:51:50 +01:00
barebones anime player
This commit is contained in:
parent
5c7123a997
commit
bd02edf0b1
@ -21,7 +21,6 @@ import suwayomi.anime.impl.extension.Extension.installExtension
|
|||||||
import suwayomi.anime.impl.extension.Extension.uninstallExtension
|
import suwayomi.anime.impl.extension.Extension.uninstallExtension
|
||||||
import suwayomi.anime.impl.extension.Extension.updateExtension
|
import suwayomi.anime.impl.extension.Extension.updateExtension
|
||||||
import suwayomi.anime.impl.extension.ExtensionsList.getExtensionList
|
import suwayomi.anime.impl.extension.ExtensionsList.getExtensionList
|
||||||
import suwayomi.server.JavalinSetup
|
|
||||||
import suwayomi.server.JavalinSetup.future
|
import suwayomi.server.JavalinSetup.future
|
||||||
|
|
||||||
object AnimeAPI {
|
object AnimeAPI {
|
||||||
@ -40,7 +39,7 @@ object AnimeAPI {
|
|||||||
val pkgName = ctx.pathParam("pkgName")
|
val pkgName = ctx.pathParam("pkgName")
|
||||||
|
|
||||||
ctx.json(
|
ctx.json(
|
||||||
JavalinSetup.future {
|
future {
|
||||||
installExtension(pkgName)
|
installExtension(pkgName)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -51,7 +50,7 @@ object AnimeAPI {
|
|||||||
val pkgName = ctx.pathParam("pkgName")
|
val pkgName = ctx.pathParam("pkgName")
|
||||||
|
|
||||||
ctx.json(
|
ctx.json(
|
||||||
JavalinSetup.future {
|
future {
|
||||||
updateExtension(pkgName)
|
updateExtension(pkgName)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -70,7 +69,7 @@ object AnimeAPI {
|
|||||||
val apkName = ctx.pathParam("apkName")
|
val apkName = ctx.pathParam("apkName")
|
||||||
|
|
||||||
ctx.result(
|
ctx.result(
|
||||||
JavalinSetup.future { getExtensionIcon(apkName) }
|
future { getExtensionIcon(apkName) }
|
||||||
.thenApply {
|
.thenApply {
|
||||||
ctx.header("content-type", it.second)
|
ctx.header("content-type", it.second)
|
||||||
it.first
|
it.first
|
||||||
@ -94,7 +93,7 @@ object AnimeAPI {
|
|||||||
val sourceId = ctx.pathParam("sourceId").toLong()
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
val pageNum = ctx.pathParam("pageNum").toInt()
|
val pageNum = ctx.pathParam("pageNum").toInt()
|
||||||
ctx.json(
|
ctx.json(
|
||||||
JavalinSetup.future {
|
future {
|
||||||
getAnimeList(sourceId, pageNum, popular = true)
|
getAnimeList(sourceId, pageNum, popular = true)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -105,7 +104,7 @@ object AnimeAPI {
|
|||||||
val sourceId = ctx.pathParam("sourceId").toLong()
|
val sourceId = ctx.pathParam("sourceId").toLong()
|
||||||
val pageNum = ctx.pathParam("pageNum").toInt()
|
val pageNum = ctx.pathParam("pageNum").toInt()
|
||||||
ctx.json(
|
ctx.json(
|
||||||
JavalinSetup.future {
|
future {
|
||||||
getAnimeList(sourceId, pageNum, popular = false)
|
getAnimeList(sourceId, pageNum, popular = false)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -117,7 +116,7 @@ object AnimeAPI {
|
|||||||
val onlineFetch = ctx.queryParam("onlineFetch", "false").toBoolean()
|
val onlineFetch = ctx.queryParam("onlineFetch", "false").toBoolean()
|
||||||
|
|
||||||
ctx.json(
|
ctx.json(
|
||||||
JavalinSetup.future {
|
future {
|
||||||
getAnime(animeId, onlineFetch)
|
getAnime(animeId, onlineFetch)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -128,7 +127,7 @@ object AnimeAPI {
|
|||||||
val animeId = ctx.pathParam("animeId").toInt()
|
val animeId = ctx.pathParam("animeId").toInt()
|
||||||
|
|
||||||
ctx.result(
|
ctx.result(
|
||||||
JavalinSetup.future { getAnimeThumbnail(animeId) }
|
future { getAnimeThumbnail(animeId) }
|
||||||
.thenApply {
|
.thenApply {
|
||||||
ctx.header("content-type", it.second)
|
ctx.header("content-type", it.second)
|
||||||
it.first
|
it.first
|
||||||
@ -164,14 +163,14 @@ object AnimeAPI {
|
|||||||
|
|
||||||
val onlineFetch = ctx.queryParam("onlineFetch")?.toBoolean()
|
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 <Quality pending>
|
// used to display a episode, get a episode in order to show it's <Quality pending>
|
||||||
app.get("/api/v1/anime/anime/:animeId/episode/:episodeIndex") { ctx ->
|
app.get("/api/v1/anime/anime/:animeId/episode/:episodeIndex") { ctx ->
|
||||||
val episodeIndex = ctx.pathParam("episodeIndex").toInt()
|
val episodeIndex = ctx.pathParam("episodeIndex").toInt()
|
||||||
val animeId = ctx.pathParam("animeId").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
|
// used to modify a episode's parameters
|
||||||
|
@ -8,6 +8,7 @@ package suwayomi.anime.impl
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* 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.SAnime
|
||||||
|
import eu.kanade.tachiyomi.source.model.SEpisode
|
||||||
import org.jetbrains.exposed.sql.SortOrder.DESC
|
import org.jetbrains.exposed.sql.SortOrder.DESC
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
import org.jetbrains.exposed.sql.deleteWhere
|
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.Anime.getAnime
|
||||||
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
|
import suwayomi.anime.impl.util.GetAnimeHttpSource.getAnimeHttpSource
|
||||||
import suwayomi.anime.model.dataclass.EpisodeDataClass
|
import suwayomi.anime.model.dataclass.EpisodeDataClass
|
||||||
|
import suwayomi.anime.model.table.AnimeTable
|
||||||
import suwayomi.anime.model.table.EpisodeTable
|
import suwayomi.anime.model.table.EpisodeTable
|
||||||
import suwayomi.anime.model.table.toDataClass
|
import suwayomi.anime.model.table.toDataClass
|
||||||
import suwayomi.tachidesk.impl.util.lang.awaitSingle
|
import suwayomi.tachidesk.impl.util.lang.awaitSingle
|
||||||
@ -59,7 +61,7 @@ object Episode {
|
|||||||
val episodeEntry = EpisodeTable.select { EpisodeTable.url eq fetchedEpisode.url }.firstOrNull()
|
val episodeEntry = EpisodeTable.select { EpisodeTable.url eq fetchedEpisode.url }.firstOrNull()
|
||||||
if (episodeEntry == null) {
|
if (episodeEntry == null) {
|
||||||
EpisodeTable.insert {
|
EpisodeTable.insert {
|
||||||
it[url] = source.
|
it[url] = fetchedEpisode.url
|
||||||
it[name] = fetchedEpisode.name
|
it[name] = fetchedEpisode.name
|
||||||
it[date_upload] = fetchedEpisode.date_upload
|
it[date_upload] = fetchedEpisode.date_upload
|
||||||
it[episode_number] = fetchedEpisode.episode_number
|
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 */
|
/** used to display a episode, get a episode in order to show it's video */
|
||||||
suspend fun getEpisode(episodeIndex: Int, animeId: Int): EpisodeDataClass {
|
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 */
|
// /** used to display a episode, get a episode in order to show it's pages */
|
||||||
|
@ -27,9 +27,9 @@ data class EpisodeDataClass(
|
|||||||
/** this chapter's index, starts with 1 */
|
/** this chapter's index, starts with 1 */
|
||||||
val index: Int,
|
val index: Int,
|
||||||
|
|
||||||
/** total chapter count, used to calculate if there's a next and prev chapter */
|
/** total episode count, used to calculate if there's a next and prev episode */
|
||||||
val chapterCount: Int? = null,
|
val episodeCount: Int? = null,
|
||||||
|
|
||||||
/** used to construct pages in the front-end */
|
/** used to construct pages in the front-end */
|
||||||
val pageCount: Int? = null,
|
val linkUrl: String? = null,
|
||||||
)
|
)
|
||||||
|
@ -9,6 +9,8 @@ package suwayomi.anime.model.table
|
|||||||
|
|
||||||
import org.jetbrains.exposed.dao.id.IntIdTable
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
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
|
import suwayomi.anime.model.dataclass.EpisodeDataClass
|
||||||
|
|
||||||
object EpisodeTable : IntIdTable() {
|
object EpisodeTable : IntIdTable() {
|
||||||
@ -40,4 +42,5 @@ fun EpisodeTable.toDataClass(episodeEntry: ResultRow) =
|
|||||||
episodeEntry[isBookmarked],
|
episodeEntry[isBookmarked],
|
||||||
episodeEntry[lastPageRead],
|
episodeEntry[lastPageRead],
|
||||||
episodeEntry[episodeIndex],
|
episodeEntry[episodeIndex],
|
||||||
|
transaction { EpisodeTable.select { anime eq episodeEntry[anime] }.count().toInt() }
|
||||||
)
|
)
|
||||||
|
@ -32,6 +32,7 @@ import MangaExtensions from 'screens/manga/MangaExtensions';
|
|||||||
import SourceMangas from 'screens/manga/SourceMangas';
|
import SourceMangas from 'screens/manga/SourceMangas';
|
||||||
import SourceAnimes from 'screens/anime/SourceAnimes';
|
import SourceAnimes from 'screens/anime/SourceAnimes';
|
||||||
import Reader from 'screens/manga/Reader';
|
import Reader from 'screens/manga/Reader';
|
||||||
|
import Player from 'screens/anime/Player';
|
||||||
import AnimeExtensions from 'screens/anime/AnimeExtensions';
|
import AnimeExtensions from 'screens/anime/AnimeExtensions';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
@ -133,13 +134,6 @@ export default function App() {
|
|||||||
<Route path="/library">
|
<Route path="/library">
|
||||||
<Library />
|
<Library />
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
|
||||||
path="/manga/:mangaId/chapter/:chapterIndex"
|
|
||||||
// passing a key re-mounts the reader when changing chapters
|
|
||||||
render={
|
|
||||||
(props:any) => <Reader key={props.match.params.chapterIndex} />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Anime Routes */}
|
{/* Anime Routes */}
|
||||||
<Route path="/anime/extensions">
|
<Route path="/anime/extensions">
|
||||||
@ -154,11 +148,21 @@ export default function App() {
|
|||||||
<Route path="/anime/sources">
|
<Route path="/anime/sources">
|
||||||
<AnimeSources />
|
<AnimeSources />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/anime/:animeId/episode/:episodeIndex">
|
||||||
|
<Player />
|
||||||
|
</Route>
|
||||||
<Route path="/anime/:id">
|
<Route path="/anime/:id">
|
||||||
<Anime />
|
<Anime />
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Container>
|
</Container>
|
||||||
|
<Switch>
|
||||||
|
<Route
|
||||||
|
path="/manga/:mangaId/chapter/:chapterIndex"
|
||||||
|
// passing a key re-mounts the reader when changing chapters
|
||||||
|
render={(props:any) => <Reader key={props.match.params.chapterIndex} />}
|
||||||
|
/>
|
||||||
|
</Switch>
|
||||||
</NavbarContext.Provider>
|
</NavbarContext.Provider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</Router>
|
</Router>
|
||||||
|
77
webUI/react/src/screens/anime/Player.tsx
Normal file
77
webUI/react/src/screens/anime/Player.tsx
Normal file
@ -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<IEpisode | IPartialEpisode>(initialEpisode());
|
||||||
|
const [episodeLink, setEpisodeLink] = useState<string>();
|
||||||
|
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 (
|
||||||
|
<div className={classes.loading}>
|
||||||
|
<CircularProgress thickness={5} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
|
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
|
||||||
|
<video className={classes.video} controls>
|
||||||
|
<source src={episodeLink} />
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
10
webUI/react/src/typings.d.ts
vendored
10
webUI/react/src/typings.d.ts
vendored
@ -55,7 +55,6 @@ interface IManga {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IChapter {
|
interface IChapter {
|
||||||
id: number
|
|
||||||
url: string
|
url: string
|
||||||
name: string
|
name: string
|
||||||
uploadDate: number
|
uploadDate: number
|
||||||
@ -71,7 +70,6 @@ interface IChapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IEpisode {
|
interface IEpisode {
|
||||||
id: number
|
|
||||||
url: string
|
url: string
|
||||||
name: string
|
name: string
|
||||||
uploadDate: number
|
uploadDate: number
|
||||||
@ -83,7 +81,7 @@ interface IEpisode {
|
|||||||
lastPageRead: number
|
lastPageRead: number
|
||||||
index: number
|
index: number
|
||||||
episodeCount: number
|
episodeCount: number
|
||||||
pageCount: number
|
linkUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPartialChpter {
|
interface IPartialChpter {
|
||||||
@ -92,6 +90,12 @@ interface IPartialChpter {
|
|||||||
chapterCount: number
|
chapterCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IPartialEpisode {
|
||||||
|
linkUrl: string
|
||||||
|
index: number
|
||||||
|
episodeCount: number
|
||||||
|
}
|
||||||
|
|
||||||
interface ICategory {
|
interface ICategory {
|
||||||
id: number
|
id: number
|
||||||
order: number
|
order: number
|
||||||
|
Loading…
Reference in New Issue
Block a user