calling HttpSource.imageRequest now

This commit is contained in:
Aria Moradi 2021-02-04 03:42:30 +03:30
parent 8f28c3b74b
commit 63a444bd81
18 changed files with 219 additions and 69 deletions

View File

@ -4,8 +4,10 @@ package eu.kanade.tachiyomi.network
// import eu.kanade.tachiyomi.BuildConfig // import eu.kanade.tachiyomi.BuildConfig
// import eu.kanade.tachiyomi.data.preference.PreferencesHelper // import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import android.content.Context import android.content.Context
import okhttp3.Dispatcher
// import okhttp3.HttpUrl.Companion.toHttpUrl // import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.Executors
// import okhttp3.dnsoverhttps.DnsOverHttps // import okhttp3.dnsoverhttps.DnsOverHttps
// import okhttp3.logging.HttpLoggingInterceptor // import okhttp3.logging.HttpLoggingInterceptor
// import uy.kohesive.injekt.injectLazy // import uy.kohesive.injekt.injectLazy
@ -26,7 +28,8 @@ class NetworkHelper(context: Context) {
.cookieJar(cookieManager) .cookieJar(cookieManager)
// .cache(Cache(cacheDir, cacheSize)) // .cache(Cache(cacheDir, cacheSize))
.connectTimeout(30, TimeUnit.SECONDS) .connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(5, TimeUnit.MINUTES)
.writeTimeout(5, TimeUnit.MINUTES)
// .dispatcher(Dispatcher(Executors.newFixedThreadPool(1))) // .dispatcher(Dispatcher(Executors.newFixedThreadPool(1)))
// .addInterceptor(UserAgentInterceptor()) // .addInterceptor(UserAgentInterceptor())

View File

@ -80,17 +80,18 @@ fun Call.asObservable(): Observable<Response> {
// } // }
fun Call.asObservableSuccess(): Observable<Response> { fun Call.asObservableSuccess(): Observable<Response> {
return asObservable().doOnNext { response -> return asObservable()
if (!response.isSuccessful) { // .doOnNext { response ->
response.close() // if (!response.isSuccessful) {
throw Exception("HTTP error ${response.code}") // response.close()
} // throw Exception("HTTP error ${response.code}")
} // }
// }
} }
// fun OkHttpClient.newCallWithProgress(request: Request, listener: ProgressListener): Call { // fun OkHttpClient.newCallWithProgress(request: Request, listener: ProgressListener): Call {
// val progressClient = newBuilder() // val progressClient = newBuilder()
// .cache(null) // .cache(nasObservableSuccessull)
// .addNetworkInterceptor { chain -> // .addNetworkInterceptor { chain ->
// val originalResponse = chain.proceed(chain.request()) // val originalResponse = chain.proceed(chain.request())
// originalResponse.newBuilder() // originalResponse.newBuilder()
@ -104,7 +105,7 @@ fun Call.asObservableSuccess(): Observable<Response> {
fun OkHttpClient.newCallWithProgress(request: Request, listener: ProgressListener): Call { fun OkHttpClient.newCallWithProgress(request: Request, listener: ProgressListener): Call {
val progressClient = newBuilder() val progressClient = newBuilder()
.cache(null) // .cache(null)
// .addNetworkInterceptor { chain -> // .addNetworkInterceptor { chain ->
// val originalResponse = chain.proceed(chain.request()) // val originalResponse = chain.proceed(chain.request())
// originalResponse.newBuilder() // originalResponse.newBuilder()

View File

@ -311,7 +311,7 @@ abstract class HttpSource : CatalogueSource {
* *
* @param page the chapter whose page list has to be fetched * @param page the chapter whose page list has to be fetched
*/ */
protected open fun imageRequest(page: Page): Request { open fun imageRequest(page: Page): Request {
return GET(page.imageUrl!!, headers) return GET(page.imageUrl!!, headers)
} }

View File

@ -7,12 +7,12 @@ package ir.armor.tachidesk
import eu.kanade.tachiyomi.App import eu.kanade.tachiyomi.App
import io.javalin.Javalin import io.javalin.Javalin
import ir.armor.tachidesk.util.applicationSetup import ir.armor.tachidesk.util.applicationSetup
import ir.armor.tachidesk.util.getChapter
import ir.armor.tachidesk.util.getChapterList import ir.armor.tachidesk.util.getChapterList
import ir.armor.tachidesk.util.getExtensionList import ir.armor.tachidesk.util.getExtensionList
import ir.armor.tachidesk.util.getManga import ir.armor.tachidesk.util.getManga
import ir.armor.tachidesk.util.getMangaList import ir.armor.tachidesk.util.getMangaList
// import ir.armor.tachidesk.util.getMangaUpdateQueueThread import ir.armor.tachidesk.util.getPageImage
import ir.armor.tachidesk.util.getPages
import ir.armor.tachidesk.util.getSource import ir.armor.tachidesk.util.getSource
import ir.armor.tachidesk.util.getSourceList import ir.armor.tachidesk.util.getSourceList
import ir.armor.tachidesk.util.getThumbnail import ir.armor.tachidesk.util.getThumbnail
@ -119,6 +119,14 @@ class Main {
ctx.json(getManga(mangaId)) ctx.json(getManga(mangaId))
} }
app.get("api/v1/manga/:mangaId/thumbnail") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt()
val result = getThumbnail(mangaId)
ctx.result(result.first)
ctx.header("content-type", result.second)
}
app.get("/api/v1/manga/:mangaId/chapters") { ctx -> app.get("/api/v1/manga/:mangaId/chapters") { ctx ->
val mangaId = ctx.pathParam("mangaId").toInt() val mangaId = ctx.pathParam("mangaId").toInt()
ctx.json(getChapterList(mangaId)) ctx.json(getChapterList(mangaId))
@ -127,13 +135,14 @@ class Main {
app.get("/api/v1/manga/:mangaId/chapter/:chapterId") { ctx -> app.get("/api/v1/manga/:mangaId/chapter/:chapterId") { ctx ->
val chapterId = ctx.pathParam("chapterId").toInt() val chapterId = ctx.pathParam("chapterId").toInt()
val mangaId = ctx.pathParam("mangaId").toInt() val mangaId = ctx.pathParam("mangaId").toInt()
ctx.json(getPages(chapterId, mangaId)) ctx.json(getChapter(chapterId, mangaId))
} }
app.get("api/v1/manga/:mangaId/thumbnail") { ctx -> app.get("/api/v1/manga/:mangaId/chapter/:chapterId/page/:index") { ctx ->
val chapterId = ctx.pathParam("chapterId").toInt()
val mangaId = ctx.pathParam("mangaId").toInt() val mangaId = ctx.pathParam("mangaId").toInt()
println("got request for: $mangaId") val index = ctx.pathParam("index").toInt()
val result = getThumbnail(mangaId) val result = getPageImage(mangaId, chapterId, index)
ctx.result(result.first) ctx.result(result.first)
ctx.header("content-type", result.second) ctx.header("content-type", result.second)

View File

@ -8,6 +8,7 @@ import ir.armor.tachidesk.Config
import ir.armor.tachidesk.database.table.ChapterTable import ir.armor.tachidesk.database.table.ChapterTable
import ir.armor.tachidesk.database.table.ExtensionsTable import ir.armor.tachidesk.database.table.ExtensionsTable
import ir.armor.tachidesk.database.table.MangaTable import ir.armor.tachidesk.database.table.MangaTable
import ir.armor.tachidesk.database.table.PageTable
import ir.armor.tachidesk.database.table.SourceTable import ir.armor.tachidesk.database.table.SourceTable
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.SchemaUtils
@ -30,5 +31,6 @@ fun makeDataBaseTables() {
SchemaUtils.create(SourceTable) SchemaUtils.create(SourceTable)
SchemaUtils.create(MangaTable) SchemaUtils.create(MangaTable)
SchemaUtils.create(ChapterTable) SchemaUtils.create(ChapterTable)
SchemaUtils.create(PageTable)
} }
} }

View File

@ -12,4 +12,5 @@ data class ChapterDataClass(
val chapter_number: Float, val chapter_number: Float,
val scanlator: String?, val scanlator: String?,
val mangaId: Int, val mangaId: Int,
val pageCount: Int? = null,
) )

View File

@ -1,5 +1,9 @@
package ir.armor.tachidesk.database.table package ir.armor.tachidesk.database.table
/* 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 org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.dao.id.IntIdTable
object ChapterTable : IntIdTable() { object ChapterTable : IntIdTable() {

View File

@ -1,5 +1,9 @@
package ir.armor.tachidesk.database.table package ir.armor.tachidesk.database.table
/* 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 org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.dao.id.IntIdTable
object ExtensionsTable : IntIdTable() { object ExtensionsTable : IntIdTable() {

View File

@ -1,5 +1,9 @@
package ir.armor.tachidesk.database.table package ir.armor.tachidesk.database.table
/* 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 eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.dao.id.IntIdTable

View File

@ -0,0 +1,15 @@
package ir.armor.tachidesk.database.table
/* 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 org.jetbrains.exposed.dao.id.IntIdTable
object PageTable : IntIdTable() {
val index = integer("index")
val url = varchar("url", 2048)
val imageUrl = varchar("imageUrl", 2048).nullable()
val chapter = reference("chapter", ChapterTable)
}

View File

@ -1,5 +1,9 @@
package ir.armor.tachidesk.database.table package ir.armor.tachidesk.database.table
/* 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 org.jetbrains.exposed.dao.id.IdTable import org.jetbrains.exposed.dao.id.IdTable
object SourceTable : IdTable<Long>() { object SourceTable : IdTable<Long>() {

View File

@ -4,14 +4,14 @@ package ir.armor.tachidesk.util
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import ir.armor.tachidesk.database.dataclass.ChapterDataClass import ir.armor.tachidesk.database.dataclass.ChapterDataClass
import ir.armor.tachidesk.database.dataclass.PageDataClass
import ir.armor.tachidesk.database.table.ChapterTable import ir.armor.tachidesk.database.table.ChapterTable
import ir.armor.tachidesk.database.table.MangaTable import ir.armor.tachidesk.database.table.MangaTable
import ir.armor.tachidesk.database.table.PageTable
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.insertAndGetId import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
@ -57,14 +57,14 @@ fun getChapterList(mangaId: Int): List<ChapterDataClass> {
} }
} }
fun getPages(chapterId: Int, mangaId: Int): Pair<ChapterDataClass, List<PageDataClass>> { fun getChapter(chapterId: Int, mangaId: Int): ChapterDataClass {
return transaction { return transaction {
val chapterEntry = ChapterTable.select { ChapterTable.id eq chapterId }.firstOrNull()!! val chapterEntry = ChapterTable.select { ChapterTable.id eq chapterId }.firstOrNull()!!
assert(mangaId == chapterEntry[ChapterTable.manga].value) // sanity check assert(mangaId == chapterEntry[ChapterTable.manga].value) // sanity check
val mangaEntry = MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! val mangaEntry = MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!!
val source = getHttpSource(mangaEntry[MangaTable.sourceReference].value) val source = getHttpSource(mangaEntry[MangaTable.sourceReference].value)
val pagesList = source.fetchPageList( val pageList = source.fetchPageList(
SChapter.create().apply { SChapter.create().apply {
url = chapterEntry[ChapterTable.url] url = chapterEntry[ChapterTable.url]
name = chapterEntry[ChapterTable.name] name = chapterEntry[ChapterTable.name]
@ -78,22 +78,24 @@ fun getPages(chapterId: Int, mangaId: Int): Pair<ChapterDataClass, List<PageData
chapterEntry[ChapterTable.date_upload], chapterEntry[ChapterTable.date_upload],
chapterEntry[ChapterTable.chapter_number], chapterEntry[ChapterTable.chapter_number],
chapterEntry[ChapterTable.scanlator], chapterEntry[ChapterTable.scanlator],
mangaId mangaId,
pageList.count()
) )
val pages = pagesList.map { pageList.forEach { page ->
PageDataClass( val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq page.index) }.firstOrNull() }
it.index, if (pageEntry == null) {
getTrueImageUrl(it, source) transaction {
) PageTable.insert {
it[index] = page.index
it[url] = page.url
it[imageUrl] = page.imageUrl
it[this.chapter] = chapterId
}
}
}
} }
return@transaction Pair(chapter, pages) return@transaction chapter
} }
} }
fun getTrueImageUrl(page: Page, source: HttpSource): String {
return if (page.imageUrl == null) {
source.fetchImageUrl(page).toBlocking().first()!!
} else page.imageUrl!!
}

View File

@ -1,15 +1,23 @@
package ir.armor.tachidesk.util package ir.armor.tachidesk.util
/* 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 okio.BufferedSource
import okio.buffer
import okio.sink
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Paths import java.nio.file.Paths
fun writeStream(fileStream: InputStream, path: String) { fun writeStream(fileStream: InputStream, path: String) {
Files.newOutputStream(Paths.get(path)).use { os -> Files.newOutputStream(Paths.get(path)).use { os ->
val buffer = ByteArray(1024) val buffer = ByteArray(128 * 1024)
var len: Int var len: Int
while (fileStream.read(buffer).also { len = it } > 0) { while (fileStream.read(buffer).also { len = it } > 0) {
os.write(buffer, 0, len) os.write(buffer, 0, len)
@ -28,3 +36,17 @@ fun findFileNameStartingWith(directoryPath: String, fileName: String): String? {
} }
return null return null
} }
/**
* Saves the given source to an output stream and closes both resources.
*
* @param stream the stream where the source is copied.
*/
fun BufferedSource.saveTo(stream: OutputStream) {
use { input ->
stream.sink().buffer().use {
it.writeAll(input)
it.flush()
}
}
}

View File

@ -85,14 +85,14 @@ fun getManga(mangaId: Int, proxyThumbnail: Boolean = true): MangaDataClass {
fun getThumbnail(mangaId: Int): Pair<InputStream, String> { fun getThumbnail(mangaId: Int): Pair<InputStream, String> {
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! } val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
var filePath = Config.thumbnailsRoot + "/$mangaId" var filePath = "${Config.thumbnailsRoot}/$mangaId."
val potentialCache = findFileNameStartingWith(Config.thumbnailsRoot, mangaId.toString()) val potentialCache = findFileNameStartingWith(Config.thumbnailsRoot, mangaId.toString())
if (potentialCache != null) { if (potentialCache != null) {
println("using cached thumbnail file") println("using cached thumbnail file")
return Pair( return Pair(
pathToInputStream(potentialCache), pathToInputStream(potentialCache),
"image/${potentialCache.substringAfter("$mangaId.")}" "image/${potentialCache.substringAfter(filePath)}"
) )
} }
@ -108,11 +108,9 @@ fun getThumbnail(mangaId: Int): Pair<InputStream, String> {
GET(thumbnailUrl, source.headers) GET(thumbnailUrl, source.headers)
).execute() ).execute()
println(response.code)
if (response.code == 200) { if (response.code == 200) {
val contentType = response.headers["content-type"]!! val contentType = response.headers["content-type"]!!
filePath += "." + contentType.substringAfter("image/") filePath += contentType.substringAfter("image/")
writeStream(response.body!!.byteStream(), filePath) writeStream(response.body!!.byteStream(), filePath)
@ -126,9 +124,9 @@ fun getThumbnail(mangaId: Int): Pair<InputStream, String> {
} }
fun getMangaDir(mangaId: Int): String { fun getMangaDir(mangaId: Int): String {
val mangaEntry = MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
val sourceId = mangaEntry[MangaTable.sourceReference].value val sourceId = mangaEntry[MangaTable.sourceReference].value
val sourceEntry = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()!! val sourceEntry = transaction { SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()!! }
val mangaTitle = mangaEntry[MangaTable.title] val mangaTitle = mangaEntry[MangaTable.title]
val sourceName = sourceEntry[SourceTable.name] val sourceName = sourceEntry[SourceTable.name]

View File

@ -1,15 +1,15 @@
package ir.armor.tachidesk.util package ir.armor.tachidesk.util
/* 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 eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.net.URLEncoder import java.net.URLEncoder
/* 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/. */
class MangaDexHelper(private val mangaDexSource: HttpSource) { class MangaDexHelper(private val mangaDexSource: HttpSource) {
private fun clientBuilder(): OkHttpClient = clientBuilder(0) private fun clientBuilder(): OkHttpClient = clientBuilder(0)

View File

@ -0,0 +1,88 @@
package ir.armor.tachidesk.util
/* 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 eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.newCallWithProgress
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource
import ir.armor.tachidesk.database.table.ChapterTable
import ir.armor.tachidesk.database.table.MangaTable
import ir.armor.tachidesk.database.table.PageTable
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import java.io.File
import java.io.InputStream
import java.nio.file.Files
import java.nio.file.Paths
fun getTrueImageUrl(page: Page, source: HttpSource): String {
if (page.imageUrl == null) {
page.imageUrl = source.fetchImageUrl(page).toBlocking().first()!!
}
return page.imageUrl!!
}
fun getPageImage(mangaId: Int, chapterId: Int, index: Int): Pair<InputStream, String> {
val mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!! }
val source = getHttpSource(mangaEntry[MangaTable.sourceReference].value)
val chapterEntry = transaction { ChapterTable.select { ChapterTable.id eq chapterId }.firstOrNull()!! }
val pageEntry = transaction { PageTable.select { (PageTable.chapter eq chapterId) and (PageTable.index eq index) }.firstOrNull()!! }
val tachiPage = Page(
pageEntry[PageTable.index],
pageEntry[PageTable.url],
pageEntry[PageTable.imageUrl]
)
if (pageEntry[PageTable.imageUrl] == null) {
transaction {
PageTable.update({ (PageTable.chapter eq chapterId) and (PageTable.index eq index) }) {
it[imageUrl] = getTrueImageUrl(tachiPage, source)
}
}
}
val saveDir = getMangaDir(mangaId) + "/" + chapterEntry[ChapterTable.chapter_number]
File(saveDir).mkdirs()
var filePath = "$saveDir/$index."
val potentialCache = findFileNameStartingWith(saveDir, index.toString())
if (potentialCache != null) {
println("using cached page file for $index")
return Pair(
pathToInputStream(potentialCache),
"image/${potentialCache.substringAfter("$filePath")}"
)
}
// Note: line bellow somehow closes the okhttp reqest!
// val response = source.fetchImage(tachiPage).toBlocking().first()
// Note: also this which is the same
// val response = source.client.newCallWithProgress(source.imageRequest(tachiPage), tachiPage)
// .asObservableSuccess().toBlocking().first()
val response = source.client.newCall(source.imageRequest(tachiPage)).execute()
if (response.code == 200) {
val contentType = response.headers["content-type"]!!
filePath += contentType.substringAfter("image/")
Files.newOutputStream(Paths.get(filePath)).use { os ->
response.body!!.source().saveTo(os)
}
// writeStream(response.body!!.source(), filePath)
return Pair(
pathToInputStream(filePath),
contentType
)
} else {
throw Exception("request error! ${response.code}")
}
}

View File

@ -14,44 +14,36 @@ const style = {
backgroundColor: '#343a40', backgroundColor: '#343a40',
} as React.CSSProperties; } as React.CSSProperties;
interface IPage { const range = (n:number) => Array.from({ length: n }, (value, key) => key);
index: number
imageUrl: string
}
interface IData {
first: IChapter
second: IPage[]
}
export default function Reader() { export default function Reader() {
const { setTitle } = useContext(NavBarTitle); const { setTitle } = useContext(NavBarTitle);
const [pages, setPages] = useState<IPage[]>([]); const [pageCount, setPageCount] = useState<number>(-1);
const { chapterId, mangaId } = useParams<{chapterId: string, mangaId: string}>(); const { chapterId, mangaId } = useParams<{chapterId: string, mangaId: string}>();
useEffect(() => { useEffect(() => {
fetch(`http://127.0.0.1:4567/api/v1/manga/${mangaId}/chapter/${chapterId}`) fetch(`http://127.0.0.1:4567/api/v1/manga/${mangaId}/chapter/${chapterId}`)
.then((response) => response.json()) .then((response) => response.json())
.then((data:IData) => { .then((data:IChapter) => {
setTitle(data.first.name); setTitle(data.name);
setPages(data.second); setPageCount(data.pageCount);
}); });
}, []); }, []);
pages.sort((a, b) => (a.index - b.index)); if (pageCount === -1) {
return (
let mapped; <div style={style}>
if (pages.length === 0) { <h3>wait</h3>
mapped = <h3>wait</h3>;
} else {
mapped = pages.map(({ imageUrl }) => (
<div style={{ margin: '0 auto' }}>
<img src={imageUrl} alt="f" style={{ maxWidth: '100%' }} />
</div> </div>
)); );
} }
const mapped = range(pageCount).map((index) => (
<div style={{ margin: '0 auto' }}>
<img src={`http://127.0.0.1:4567/api/v1/manga/${mangaId}/chapter/${chapterId}/page/${index}`} alt="f" style={{ maxWidth: '100%' }} />
</div>
));
return ( return (
<div style={style}> <div style={style}>
{mapped} {mapped}

View File

@ -34,4 +34,5 @@ interface IChapter {
chapter_number: number chapter_number: number
scanlator: String scanlator: String
mangaId: number mangaId: number
pageCount: number
} }