diff --git a/src/ko/jmana/build.gradle b/src/ko/jmana/build.gradle index 9b46c206f..d94553455 100644 --- a/src/ko/jmana/build.gradle +++ b/src/ko/jmana/build.gradle @@ -5,7 +5,7 @@ ext { appName = 'Tachiyomi: JMana' pkgNameSuffix = 'ko.jmana' extClass = '.JMana' - extVersionCode = 7 + extVersionCode = 8 libVersion = '1.2' } diff --git a/src/ko/jmana/src/eu/kanade/tachiyomi/extension/ko/jmana/JMana.kt b/src/ko/jmana/src/eu/kanade/tachiyomi/extension/ko/jmana/JMana.kt index 442923a2d..5b228be65 100644 --- a/src/ko/jmana/src/eu/kanade/tachiyomi/extension/ko/jmana/JMana.kt +++ b/src/ko/jmana/src/eu/kanade/tachiyomi/extension/ko/jmana/JMana.kt @@ -40,7 +40,7 @@ class JMana : ParsedHttpSource() { override fun popularMangaNextPageSelector() = "div.page > ul > li" // Do not add page parameter if page is 1 to prevent tracking. - override fun popularMangaRequest(page: Int) = GET("$baseUrl/comic_main_frame?page=${page - 1}") + override fun popularMangaRequest(page: Int) = GET("$baseUrl/comic_main_frame?tag=null&keyword=null&chosung=null&page=${page - 1}") override fun popularMangaParse(response: Response): MangasPage { val document = response.asJsoup() @@ -59,33 +59,41 @@ class JMana : ParsedHttpSource() { override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element) override fun searchMangaNextPageSelector() = popularMangaSelector() override fun searchMangaParse(response: Response) = popularMangaParse(response) - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/comic_main_frame?keyword=$query&page=${page - 1}") + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/?tag=null&keyword=$query&chosung=null&page=${page - 1}") override fun mangaDetailsParse(document: Document): SManga { - val info = document.select("div.leftM").first() - val authorText = info.select("div.comBtnArea a").text() - val titleDescription = info.select("li.row") + val descriptionElement = document.select(".media > .row > .media-body.col-9 > div") + val thumbnailUrl = document.select(".media > .row > .media-body.col-3 img.media-object-list").attr("src") val manga = SManga.create() - manga.title = titleDescription.first().text() - manga.description = titleDescription.last().text() - manga.author = authorText + descriptionElement + .map { it.text() } + .forEach { text -> + when { + DETAIL_TITLE in text -> manga.title = text.substringAfter(DETAIL_TITLE).trim() + DETAIL_AUTHOR in text -> manga.author = text.substringAfter(DETAIL_AUTHOR).trim() + DETAIL_GENRE in text -> manga.genre = text.substringAfter("장르 : [").substringBefore("]").trim() + DETAIL_DESCRIPTION in text -> text.substringAfter(DETAIL_DESCRIPTION).trim() + } + } + manga.thumbnail_url = thumbnailUrl manga.status = SManga.UNKNOWN return manga } - override fun chapterListSelector() = "div.contents > ul > li" + override fun chapterListSelector() = "div.section > .post > .post-content-list" override fun chapterFromElement(element: Element): SChapter { - val linkElement = element.select("a") + val linkElement = element.select(".entry-title a") val rawName = linkElement.text() + val chapterUrl = "${linkElement.attr("href")}?viewstyle=list".replace("book/", "book_frame/") val chapter = SChapter.create() - chapter.url = linkElement.attr("href").replace("book/", "book_frame/") + chapter.url = chapterUrl chapter.chapter_number = parseChapterNumber(rawName) chapter.name = rawName.trim() - chapter.date_upload = parseChapterDate(element.select("ul > li:not(.fcR)").last().text()) + chapter.date_upload = parseChapterDate(element.select("li.publish-date span").last().text()) return chapter } @@ -105,7 +113,7 @@ class JMana : ParsedHttpSource() { private fun parseChapterDate(date: String): Long { return try { - SimpleDateFormat("yyyy-MM-dd").parse(date).time + SimpleDateFormat("yyyy-MM-dd HH:mm").parse(date).time } catch (e: Exception) { e.printStackTrace() 0 @@ -146,4 +154,11 @@ class JMana : ParsedHttpSource() { override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("This method should not be called!") override fun getFilterList() = FilterList() + + companion object { + const val DETAIL_TITLE = "제목 : " + const val DETAIL_GENRE = "장르 : " + const val DETAIL_AUTHOR = "작가 : " + const val DETAIL_DESCRIPTION = "설명 : " + } } \ No newline at end of file diff --git a/src/ko/mangashowme/build.gradle b/src/ko/mangashowme/build.gradle index 8c680cd67..5ba078bdd 100644 --- a/src/ko/mangashowme/build.gradle +++ b/src/ko/mangashowme/build.gradle @@ -2,10 +2,10 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' ext { - appName = 'Tachiyomi: MangaShow.Me (ManaMoa)' + appName = 'Tachiyomi: ManaMoa (MangaShow.Me)' pkgNameSuffix = 'ko.mangashowme' - extClass = '.MangaShowMe' - extVersionCode = 11 + extClass = '.MSMFactory' + extVersionCode = 12 libVersion = '1.2' } diff --git a/src/ko/mangashowme/res/mipmap-hdpi/ic_launcher.png b/src/ko/mangashowme/res/mipmap-hdpi/ic_launcher.png index 7a313cf43..9a3757007 100644 Binary files a/src/ko/mangashowme/res/mipmap-hdpi/ic_launcher.png and b/src/ko/mangashowme/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/ko/mangashowme/res/mipmap-mdpi/ic_launcher.png b/src/ko/mangashowme/res/mipmap-mdpi/ic_launcher.png index e002c2507..86639e571 100644 Binary files a/src/ko/mangashowme/res/mipmap-mdpi/ic_launcher.png and b/src/ko/mangashowme/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/ko/mangashowme/res/mipmap-xhdpi/ic_launcher.png b/src/ko/mangashowme/res/mipmap-xhdpi/ic_launcher.png index 4664f167d..d6f545905 100644 Binary files a/src/ko/mangashowme/res/mipmap-xhdpi/ic_launcher.png and b/src/ko/mangashowme/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/ko/mangashowme/res/mipmap-xxhdpi/ic_launcher.png b/src/ko/mangashowme/res/mipmap-xxhdpi/ic_launcher.png index e34287f39..dccac2012 100644 Binary files a/src/ko/mangashowme/res/mipmap-xxhdpi/ic_launcher.png and b/src/ko/mangashowme/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/ko/mangashowme/res/mipmap-xxxhdpi/ic_launcher.png b/src/ko/mangashowme/res/mipmap-xxxhdpi/ic_launcher.png index 9a07e79b5..d8eaadfc0 100644 Binary files a/src/ko/mangashowme/res/mipmap-xxxhdpi/ic_launcher.png and b/src/ko/mangashowme/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/ko/mangashowme/res/web_hi_res_512.png b/src/ko/mangashowme/res/web_hi_res_512.png index 3c929d703..9357e66dd 100644 Binary files a/src/ko/mangashowme/res/web_hi_res_512.png and b/src/ko/mangashowme/res/web_hi_res_512.png differ diff --git a/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MSMFactory.kt b/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MSMFactory.kt new file mode 100644 index 000000000..c7305578f --- /dev/null +++ b/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MSMFactory.kt @@ -0,0 +1,44 @@ +package eu.kanade.tachiyomi.extension.ko.mangashowme + +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceFactory +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.online.HttpSource +import okhttp3.Response + + +/* + * Source Factory for before-Migration + * + * I will remove this and only use ManaMoa class after 1.2.15. + * This is just helper who uses =<1.2.11 before. + * + */ +class MSMFactory : SourceFactory { + override fun createSources(): List = listOf( + ManaMoa(), + MSMDeprecated() + ) +} + +class MSMDeprecated : HttpSource() { + override val name = "MangaShow.Me" + override val baseUrl: String = "https://Depricated._Need.Source.Migration.to.ManaMoa.net" + override val lang: String = "ko" + override val supportsLatest = false + + override fun chapterListParse(response: Response) = throw Exception(NEED_MIGRATION) + override fun popularMangaRequest(page: Int) = throw Exception(NEED_MIGRATION) + override fun popularMangaParse(response: Response) = throw Exception(NEED_MIGRATION) + override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw Exception(NEED_MIGRATION) + override fun searchMangaParse(response: Response) = throw Exception(NEED_MIGRATION) + override fun latestUpdatesRequest(page: Int) = throw Exception(NEED_MIGRATION) + override fun latestUpdatesParse(response: Response) = throw Exception(NEED_MIGRATION) + override fun mangaDetailsParse(response: Response) = throw Exception(NEED_MIGRATION) + override fun imageUrlParse(response: Response) = throw Exception(NEED_MIGRATION) + override fun pageListParse(response: Response) = throw Exception(NEED_MIGRATION) + + companion object { + const val NEED_MIGRATION = "Deprecated: Use 'ManaMoa' instead.\nSource migration is on 'My Library' -> three dots -> 'Source migration'" + } +} \ No newline at end of file diff --git a/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MSMImageDecoder.kt b/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MSMImageDecoder.kt index 4acf7d680..9bc5b8178 100644 --- a/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MSMImageDecoder.kt +++ b/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MSMImageDecoder.kt @@ -73,9 +73,10 @@ internal class ImageDecoderInterceptor : Interceptor { * * MIT License * + * * Copyright (c) 2019 junheah */ - private fun imageDecoder(input: Bitmap, chapter: Int, view_cnt: Int, half: Int = 0, _CX: Int = MangaShowMe.V1_CX, _CY: Int = MangaShowMe.V1_CY): Bitmap { + private fun imageDecoder(input: Bitmap, chapter: Int, view_cnt: Int, half: Int = 0, _CX: Int = ManaMoa.V1_CX, _CY: Int = ManaMoa.V1_CY): Bitmap { if (view_cnt == 0) return input val viewCnt = view_cnt / 10 var CX = _CX diff --git a/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MSMImageUrlHandler.kt b/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MSMImageUrlHandler.kt new file mode 100644 index 000000000..1d576e586 --- /dev/null +++ b/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MSMImageUrlHandler.kt @@ -0,0 +1,57 @@ +package eu.kanade.tachiyomi.extension.ko.mangashowme + +import okhttp3.Interceptor +import okhttp3.Response + +internal class ImageUrlHandlerInterceptor : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response = RequestHandler(chain).run() +} + +private class RequestHandler(val chain: Interceptor.Chain) { + val req = chain.request() + val origUrl = req.url().toString() + + fun run(): Response { + // only for image Request + if (req.header("ImageRequest") != "1") return chain.proceed(req) + + val secondUrl = req.header("SecondUrlToRequest") + + val res = getRequest(origUrl) + return if (!isSuccess(res) && secondUrl != null) { + getRequest(secondUrl) + } else res + } + + private fun isSuccess(res: Response): Boolean { + val length = res.header("content-length")?.toInt() ?: 0 + return !(!res.isSuccessful || length < 50000) + } + + private fun getRequest(url: String): Response = when { + "filecdn.xyz" in url || "chickencdn.info" in url + -> ownCDNRequestHandler(url) + else -> outsideRequestHandler(url) + } + + private fun ownCDNRequestHandler(url: String): Response { + val res = proceedRequest(url) + return if (!isSuccess(res)) { + proceedRequest(url.replace("img.", "s3.")) // s3 + } else res + } + + private fun outsideRequestHandler(url: String): Response { + val outUrl = url.substringBefore("?quick") + return proceedRequest(outUrl) + } + + private fun proceedRequest(url: String): Response = chain.proceed( + req.newBuilder()!! + .url(url) + .removeHeader("ImageRequest") + .removeHeader("ImageDecodeRequest") + .removeHeader("SecondUrlToRequest") + .build()!! + ) +} \ No newline at end of file diff --git a/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MangaShowMe.kt b/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MangaShowMe.kt index 5cf804b03..872756e87 100644 --- a/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MangaShowMe.kt +++ b/src/ko/mangashowme/src/eu/kanade/tachiyomi/extension/ko/mangashowme/MangaShowMe.kt @@ -26,13 +26,17 @@ import java.util.* import java.util.concurrent.TimeUnit /** - * MangaShow.Me Source + * ManaMoa Source + * + * Originally it was mangashow.me extension but they changed site structure widely. + * so I moved to new name for treating as new source. + * Users who uses =<1.2.11 need to migrate sources. starts 1.2.12 * * PS. There's no Popular section. It's just a list of manga. Also not latest updates. * `manga_list` returns latest 'added' manga. not a chapter updates. **/ -class MangaShowMe : ConfigurableSource, ParsedHttpSource() { - override val name = "MangaShow.Me" +class ManaMoa : ConfigurableSource, ParsedHttpSource() { + override val name = "ManaMoa" private val defaultBaseUrl = "https://manamoa3.net" override val baseUrl by lazy { getPrefBaseUrl() } override val lang: String = "ko" @@ -43,52 +47,7 @@ class MangaShowMe : ConfigurableSource, ParsedHttpSource() { .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .addInterceptor(ImageDecoderInterceptor()) - .addInterceptor { chain -> - val req = chain.request() - - // only for image Request - val isFileCdn = !req.url().host().contains(".filecdn.xyz") - if (!req.url().toString().endsWith("?quick")) return@addInterceptor chain.proceed(req) - - val secondUrl = req.header("SecondUrlToRequest") - - fun get(flag: Int = 0): Request { - val url = if (isFileCdn) { - when (flag) { - 1 -> req.url().toString().replace("img.", "s3.") - else -> req.url().toString() - } - } else { - when (flag) { - 1 -> secondUrl!! - 2 -> secondUrl!!.replace("img.", "s3.") - else -> req.url().toString().substringBefore("?quick") - } - } - - return req.newBuilder()!!.url(url) - .removeHeader("ImageDecodeRequest") - .removeHeader("SecondUrlToRequest") - .build()!! - } - - val res = chain.proceed(get()) - - if (isFileCdn) { - val length = res.header("content-length") - if (length == null || length.toInt() < 50000) { - chain.proceed(get(1)) // s3 - } else res - } else { - if (!res.isSuccessful && secondUrl != null) { - val fallbackRes = chain.proceed(get(1)) // img filecdn - val fallbackLength = fallbackRes.header("content-length") - if (fallbackLength == null || fallbackLength.toInt() < 50000) { - chain.proceed(get(2)) // s3 - } else fallbackRes - } else res - } - } + .addInterceptor(ImageUrlHandlerInterceptor()) .build()!! override fun popularMangaSelector() = "div.manga-list-gallery > div > div.post-row" @@ -98,7 +57,7 @@ class MangaShowMe : ConfigurableSource, ParsedHttpSource() { val titleElement = element.select(".manga-subject > a").first() val manga = SManga.create() - manga.url = urlTitleEscape(linkElement.attr("href")) + manga.url = linkElement.attr("href") manga.title = titleElement.text().trim() manga.thumbnail_url = urlFinder(element.select(".img-wrap-back").attr("style")) return manga @@ -244,21 +203,15 @@ class MangaShowMe : ConfigurableSource, ParsedHttpSource() { val decoder = ImageDecoder(element) - if (imageUrls.length() != imageUrls1.length()) { - (0 until imageUrls.length()) - .map { imageUrls.getString(it) } - .forEach { pages.add(Page(pages.size, decoder.request(it), "${it.substringBefore("!!")}?quick")) } - } else { - (0 until imageUrls1.length()) - .map { - imageUrls1.getString(it) + try { - "!!${imageUrls.getString(it)}?quick" - } catch (_: Exception) { - "" - } + (0 until imageUrls.length()) + .map { + imageUrls.getString(it) + try { + "!!${imageUrls1.getString(it)}?quick" + } catch (_: Exception) { + "" } - .forEach { pages.add(Page(pages.size, decoder.request(it), "${it.substringBefore("!!")}?quick")) } - } + } + .forEach { pages.add(Page(pages.size, decoder.request(it), "${it.substringBefore("!!")}?quick")) } } catch (e: Exception) { e.printStackTrace() } @@ -284,7 +237,7 @@ class MangaShowMe : ConfigurableSource, ParsedHttpSource() { builder.build()!! } catch (_: Exception) { headers - } + }.newBuilder()!!.add("ImageRequest", "1").build()!! return GET(page.imageUrl!!, requestHeaders) } @@ -306,14 +259,6 @@ class MangaShowMe : ConfigurableSource, ParsedHttpSource() { return style.substringAfter("background-image:url(").substringBefore(")") } - // Some title contains `&` and `#` which can cause a error. - private fun urlTitleEscape(title: String): String { - val url = title.split("&manga_name=") - return "${url[0]}&manga_name=" + - url[1].replace("&", "%26").replace("#", "%23") - } - - private val preferences: SharedPreferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } diff --git a/src/ko/navercomic/build.gradle b/src/ko/navercomic/build.gradle new file mode 100644 index 000000000..213049e04 --- /dev/null +++ b/src/ko/navercomic/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + appName = 'Tachiyomi: Naver Comic' + pkgNameSuffix = 'ko.navercomic' + extClass = '.NaverComicFactory' + extVersionCode = 1 + libVersion = '1.2' +} + +apply from: "$rootDir/common.gradle" diff --git a/src/ko/navercomic/res/mipmap-hdpi/ic_launcher.png b/src/ko/navercomic/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..f057cebfd Binary files /dev/null and b/src/ko/navercomic/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/ko/navercomic/res/mipmap-mdpi/ic_launcher.png b/src/ko/navercomic/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..1e514076b Binary files /dev/null and b/src/ko/navercomic/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/ko/navercomic/res/mipmap-xhdpi/ic_launcher.png b/src/ko/navercomic/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..dd227e4a8 Binary files /dev/null and b/src/ko/navercomic/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/ko/navercomic/res/mipmap-xxhdpi/ic_launcher.png b/src/ko/navercomic/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..39dd99919 Binary files /dev/null and b/src/ko/navercomic/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/ko/navercomic/res/mipmap-xxxhdpi/ic_launcher.png b/src/ko/navercomic/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..0c9abfb46 Binary files /dev/null and b/src/ko/navercomic/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/ko/navercomic/res/web_hi_res_512.png b/src/ko/navercomic/res/web_hi_res_512.png new file mode 100644 index 000000000..fbd67fedf Binary files /dev/null and b/src/ko/navercomic/res/web_hi_res_512.png differ diff --git a/src/ko/navercomic/src/eu/kanade/tachiyomi/extension/ko/navercomic/NaverComic.kt b/src/ko/navercomic/src/eu/kanade/tachiyomi/extension/ko/navercomic/NaverComic.kt new file mode 100644 index 000000000..2e62afb6d --- /dev/null +++ b/src/ko/navercomic/src/eu/kanade/tachiyomi/extension/ko/navercomic/NaverComic.kt @@ -0,0 +1,74 @@ +package eu.kanade.tachiyomi.extension.ko.navercomic + +import android.annotation.SuppressLint +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.Request +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat + +class NaverWebtoon : NaverComicBase("webtoon") { + override val name = "Naver Webtoon" + + override fun popularMangaRequest(page: Int) = GET("$baseUrl/$mType/weekday.nhn") + override fun popularMangaSelector() = ".list_area.daily_all .col ul > li" + override fun popularMangaNextPageSelector() = null + override fun popularMangaFromElement(element: Element): SManga { + val thumb = element.select("div.thumb img").first().attr("src") + val title = element.select("a.title").first() + + val manga = SManga.create() + manga.url = title.attr("href").substringBefore("&week") + manga.title = title.text().trim() + manga.thumbnail_url = thumb + return manga + } + + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/$mType/weekday.nhn?order=Update") + override fun latestUpdatesSelector() = ".list_area.daily_all .col.col_selected ul > li" + override fun latestUpdatesNextPageSelector() = null + override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element) +} + +class NaverBestChallenge : NaverComicChallengeBase("bestChallenge") { + override val name = "Naver Webtoon Best Challenge" + + override fun popularMangaRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn") + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn?m=main&order=Update") +} + +class NaverChallenge : NaverComicChallengeBase("challenge") { + override val name = "Naver Webtoon Challenge" + override fun popularMangaRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn") + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn?m=list&order=Update") + + // Need to override again because there's no mobile page. + override fun chapterPagedListRequest(manga: SManga, page: Int): Request { + return GET("$baseUrl${manga.url}&page=$page") + } + + override fun chapterListSelector() = ".viewList > tbody > tr:not([class])" + + override fun chapterFromElement(element: Element): SChapter { + val nameElement = element.select("td.title > a").first() + val rawName = nameElement.text().trim() + + val chapter = SChapter.create() + chapter.url = nameElement.attr("src") + chapter.chapter_number = parseChapterNumber(rawName) + chapter.name = rawName + chapter.date_upload = parseChapterDate(element.select("td.num").last().text().trim()) + return chapter + } + + @SuppressLint("SimpleDateFormat") + private fun parseChapterDate(date: String): Long { + return try { + SimpleDateFormat("yyyy.MM.dd").parse(date).time + } catch (e: Exception) { + e.printStackTrace() + 0 + } + } +} \ No newline at end of file diff --git a/src/ko/navercomic/src/eu/kanade/tachiyomi/extension/ko/navercomic/NaverComicBase.kt b/src/ko/navercomic/src/eu/kanade/tachiyomi/extension/ko/navercomic/NaverComicBase.kt new file mode 100644 index 000000000..2914c1e16 --- /dev/null +++ b/src/ko/navercomic/src/eu/kanade/tachiyomi/extension/ko/navercomic/NaverComicBase.kt @@ -0,0 +1,170 @@ +package eu.kanade.tachiyomi.extension.ko.navercomic + +import android.annotation.SuppressLint +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.asObservableSuccess +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import okhttp3.OkHttpClient +import okhttp3.Request +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import rx.Observable +import java.text.SimpleDateFormat + +abstract class NaverComicBase(protected val mType: String) : ParsedHttpSource() { + override val lang: String = "ko" + override val baseUrl: String = "https://comic.naver.com" + private val mobileUrl = "https://m.comic.naver.com" + override val supportsLatest = true + override val client: OkHttpClient = network.client + + private val mobileHeaders = super.headersBuilder() + .add("Referer", mobileUrl) + .build() + + override fun searchMangaSelector() = ".resultList > li h5 > a" + override fun searchMangaNextPageSelector() = ".paginate a.next" + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("search.nhn?m=$mType&keyword=$query&type=title&page=$page") + override fun searchMangaFromElement(element: Element): SManga { + val url = element.attr("href").substringBefore("&week").substringBefore("&listPage=") + val manga = SManga.create() + manga.url = url + manga.title = element.text().trim() + return manga + } + + override fun chapterListSelector() = "#ct > .toon_lst.lst2 > li > div a" + + // Need to override because the chapter list is paginated. + override fun fetchChapterList(manga: SManga): Observable> = fetchChapterList(manga, 1) + + private fun fetchChapterList(manga: SManga, page: Int, + pastChapters: List = emptyList()): Observable> { + val chapters = pastChapters.toMutableList() + fun isSamePage(list: List): Boolean = try { + chapters.last().url == list.last().url + } catch (_: Exception) { + false + } + + return fetchChapterListPage(manga, page) + .flatMap { + if (isSamePage(it)) { + Observable.just(chapters) + } else { + chapters += it + fetchChapterList(manga, page + 1, chapters) + } + } + } + + private fun fetchChapterListPage(manga: SManga, page: Int): Observable> { + return client.newCall(chapterPagedListRequest(manga, page)) + .asObservableSuccess() + .map { response -> + chapterListParse(response) + } + } + + override fun chapterListRequest(manga: SManga): Request { + return chapterPagedListRequest(manga, 1) + } + + open fun chapterPagedListRequest(manga: SManga, page: Int): Request { + return GET("$mobileUrl${manga.url}&page=$page", mobileHeaders) + } + + override fun chapterFromElement(element: Element): SChapter { + val rawName = element.select(".toon_name > strong").last().ownText() + val url = element.attr("href").substringBefore("&week").substringBefore("&listPage") + + val chapter = SChapter.create() + chapter.url = url + chapter.chapter_number = parseChapterNumber(rawName) + chapter.name = rawName + chapter.date_upload = parseChapterDate(element.select(".toon_detail_info .if1").last().text().trim()) + return chapter + } + + protected fun parseChapterNumber(name: String): Float { + try { + if (name.contains("[단편]")) return 1f + // `특별` means `Special`, so It can be buggy. so pad `편`(Chapter) to prevent false return + if (name.contains("번외") || name.contains("특별편")) return -2f + val regex = Regex("([0-9]+)(?:[-.]([0-9]+))?(?:화)") + val (ch_primal, ch_second) = regex.find(name)!!.destructured + return (ch_primal + if (ch_second.isBlank()) "" else ".$ch_second").toFloatOrNull() ?: -1f + } catch (e: Exception) { + e.printStackTrace() + return -1f + } + } + + @SuppressLint("SimpleDateFormat") + private fun parseChapterDate(date: String): Long { + return try { + SimpleDateFormat("YY.MM.dd").parse(date).time + } catch (e: Exception) { + e.printStackTrace() + 0 + } + } + + override fun mangaDetailsParse(document: Document): SManga { + val element = document.select(".comicinfo") + val titleElement = element.select(".detail > h2") + + val manga = SManga.create() + manga.title = titleElement.first().ownText().trim() + manga.author = titleElement.select("span").text().trim() + manga.description = document.select(".comicinfo > p").text().trim() + manga.thumbnail_url = element.select(".thumb > a > img").last().attr("src") + return manga + } + + + override fun pageListParse(document: Document): List { + val pages = mutableListOf() + try { + document.select(".wt_viewer img") + .map { + it.attr("src") + } + .forEach { + pages.add(Page(pages.size, "", it)) + } + } catch (e: Exception) { + e.printStackTrace() + } + + return pages + } + + //We are able to get the image URL directly from the page list + override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("This method should not be called!") + + override fun getFilterList() = FilterList() +} + +abstract class NaverComicChallengeBase(mType: String) : NaverComicBase(mType) { + override fun popularMangaSelector() = ".weekchallengeBox tbody td:not([class])" + override fun popularMangaNextPageSelector(): String? = ".paginate .page_wrap a.next" + override fun popularMangaFromElement(element: Element): SManga { + val thumb = element.select("a img").first().attr("src") + val title = element.select(".challengeTitle a").first() + + val manga = SManga.create() + manga.url = title.attr("href").substringBefore("&week") + manga.title = title.text().trim() + manga.thumbnail_url = thumb + return manga + } + + override fun latestUpdatesSelector() = popularMangaSelector() + override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector() + override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element) +} \ No newline at end of file diff --git a/src/ko/navercomic/src/eu/kanade/tachiyomi/extension/ko/navercomic/NaverComicFactory.kt b/src/ko/navercomic/src/eu/kanade/tachiyomi/extension/ko/navercomic/NaverComicFactory.kt new file mode 100644 index 000000000..35ed20d70 --- /dev/null +++ b/src/ko/navercomic/src/eu/kanade/tachiyomi/extension/ko/navercomic/NaverComicFactory.kt @@ -0,0 +1,12 @@ +package eu.kanade.tachiyomi.extension.ko.navercomic + +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceFactory + +class NaverComicFactory : SourceFactory { + override fun createSources(): List = listOf( + NaverWebtoon(), + NaverBestChallenge(), + NaverChallenge() + ) +} \ No newline at end of file diff --git a/src/ko/newtoki/build.gradle b/src/ko/newtoki/build.gradle index 46dc2f495..10bc902cd 100644 --- a/src/ko/newtoki/build.gradle +++ b/src/ko/newtoki/build.gradle @@ -5,8 +5,13 @@ ext { appName = 'Tachiyomi: NewToki' pkgNameSuffix = 'ko.newtoki' extClass = '.NewTokiFactory' - extVersionCode = 7 + extVersionCode = 8 libVersion = '1.2' } +dependencies { + compileOnly project(':preference-stub') + compileOnly 'com.github.inorichi.injekt:injekt-core:65b0440' +} + apply from: "$rootDir/common.gradle" diff --git a/src/ko/newtoki/src/eu/kanade/tachiyomi/extension/ko/newtoki/NewToki.kt b/src/ko/newtoki/src/eu/kanade/tachiyomi/extension/ko/newtoki/NewToki.kt index 904341eba..475334c9b 100644 --- a/src/ko/newtoki/src/eu/kanade/tachiyomi/extension/ko/newtoki/NewToki.kt +++ b/src/ko/newtoki/src/eu/kanade/tachiyomi/extension/ko/newtoki/NewToki.kt @@ -1,7 +1,14 @@ package eu.kanade.tachiyomi.extension.ko.newtoki import android.annotation.SuppressLint +import android.app.Application +import android.content.SharedPreferences +import android.support.v7.preference.EditTextPreference +import android.support.v7.preference.PreferenceScreen +import android.widget.Toast +import eu.kanade.tachiyomi.extension.BuildConfig import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.util.asJsoup @@ -10,13 +17,16 @@ import okhttp3.Request import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import java.text.SimpleDateFormat import java.util.* /** * NewToki Source **/ -open class NewToki(override val name: String, override val baseUrl: String, private val boardName: String) : ParsedHttpSource() { +open class NewToki(override val name: String, private val defaultBaseUrl: String, private val boardName: String) : ConfigurableSource, ParsedHttpSource() { + override val baseUrl by lazy { getPrefBaseUrl() } override val lang: String = "ko" override val supportsLatest = true override val client: OkHttpClient = network.cloudflareClient @@ -183,4 +193,41 @@ open class NewToki(override val name: String, override val baseUrl: String, priv override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("This method should not be called!") override fun getFilterList() = FilterList() + + private val preferences: SharedPreferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + val baseUrlPref = EditTextPreference(screen.context).apply { + key = BASE_URL_PREF_TITLE + title = BASE_URL_PREF_TITLE + summary = BASE_URL_PREF_SUMMARY + this.setDefaultValue(defaultBaseUrl) + dialogTitle = BASE_URL_PREF_TITLE + dialogMessage = "Default: $defaultBaseUrl" + + setOnPreferenceChangeListener { _, newValue -> + try { + val res = preferences.edit().putString(BASE_URL_PREF, newValue as String).commit() + Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show() + res + } catch (e: Exception) { + e.printStackTrace() + false + } + } + } + + screen.addPreference(baseUrlPref) + } + + private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, defaultBaseUrl) + + companion object { + private const val BASE_URL_PREF_TITLE = "Override BaseUrl" + private const val BASE_URL_PREF = "overrideBaseUrl_v${BuildConfig.VERSION_NAME}" + private const val BASE_URL_PREF_SUMMARY = "For temporary uses. Update extension will erase this setting." + private const val RESTART_TACHIYOMI = "Restart Tachiyomi to apply new setting." + } } \ No newline at end of file diff --git a/src/ko/newtoki/src/eu/kanade/tachiyomi/extension/ko/newtoki/NewTokiFactory.kt b/src/ko/newtoki/src/eu/kanade/tachiyomi/extension/ko/newtoki/NewTokiFactory.kt index c9552d547..20f3d7477 100644 --- a/src/ko/newtoki/src/eu/kanade/tachiyomi/extension/ko/newtoki/NewTokiFactory.kt +++ b/src/ko/newtoki/src/eu/kanade/tachiyomi/extension/ko/newtoki/NewTokiFactory.kt @@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.source.model.FilterList import okhttp3.HttpUrl import okhttp3.Request -private const val baseDomain = "newtoki10" +private const val baseDomain = "newtoki12" class NewTokiFactory : SourceFactory { override fun createSources(): List = listOf(