Add NineManga (#584) (#943) (#1049)

* Added NineManga (All)

* done latest, all and manga parse.

* Complete manga img parse

* Added TODO to change language conditions.

* Date corrections.

* Remove old icons.

* Override status and date functions in the different classes.
This commit is contained in:
Edgar Mejía 2019-04-21 21:17:56 -06:00 committed by Eugene
parent 5533dae52f
commit de52d6c215
3 changed files with 368 additions and 0 deletions

View File

@ -0,0 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
appName = 'Tachiyomi: NineManga'
pkgNameSuffix = "all.ninemanga"
extClass = '.NineMangaEs; .NineMangaBr; .NineMangaEn; .NineMangaRu; .NineMangaDe; .NineMangaIt; .NineMangaFr'
extVersionCode = 1
libVersion = '1.2'
}
apply from: "$rootDir/common.gradle"

View File

@ -0,0 +1,127 @@
package eu.kanade.tachiyomi.extension.all.ninemanga
import eu.kanade.tachiyomi.source.model.*
import okhttp3.Request
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
open class NineManga(override val name: String, override val baseUrl: String, override val lang: String) : ParsedHttpSource() {
override val supportsLatest: Boolean = true
private fun newHeaders() = super.headersBuilder()
.add("Accept-Language", "es-ES,es;q=0.9,en;q=0.8,gl;q=0.7")
.add("Host", baseUrl.substringAfterLast("/")) // like: es.ninemanga.com
.add("Connection", "keep-alive")
.add("Upgrade-Insecure-Requests", "1")
.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) Gecko/20100101 Firefox/60")
.build()
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/list/New-Update/", headers) // "$baseUrl/category/updated_$page.html"
override fun latestUpdatesSelector() = "ul.direlist > li"
override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
element.select("dl.bookinfo").let {
setUrlWithoutDomain(it.select("dd > a.bookname").attr("href") + "?waring=1") // To removes warning message and shows chapter list.
title = it.select("dd > a.bookname").first().text()
thumbnail_url = it.select("dt > a > img").attr("src")
}
}
override fun latestUpdatesNextPageSelector() = "ul.pageList > li:last-child > a.l"
override fun popularMangaRequest(page: Int) = GET("$baseUrl/category/index_$page.html", headers)
override fun popularMangaSelector() = latestUpdatesSelector()
override fun popularMangaFromElement(element: Element) = latestUpdatesFromElement(element)
override fun popularMangaNextPageSelector() = latestUpdatesNextPageSelector()
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
document.select("div.bookintro").let {
thumbnail_url = document.select("a.bookface > img").attr("src")
genre = document.select("li[itemprop=genre] > a").joinToString(", ") {
it.text()
}
author = it.select("a[itemprop=author]").text()
artist = ""
description = it.select("p[itemprop=description]").text().orEmpty()
status = parseStatus(it.select("a.red").first().text().orEmpty())
}
}
open fun parseStatus(status: String) = when {
status.contains("Ongoing") -> SManga.ONGOING
status.contains("Completed") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun chapterListSelector() = "ul.sub_vol_ul > li"
override fun chapterFromElement(element: Element) = SChapter.create().apply {
element.select("a.chapter_list_a").let {
name = it.text()
setUrlWithoutDomain(it.attr("href"))
}
date_upload = parseChapterDate(element.select("span").text())
}
open fun parseChapterDate(date: String): Long {
val dateWords = date.split(" ")
if (dateWords.size == 3) {
if(dateWords[1].contains(",")){
try {
return SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(date).time
} catch (e: ParseException) {
return 0L
}
}else {
val timeAgo = Integer.parseInt(dateWords[0])
return Calendar.getInstance().apply {
when (dateWords[1]) {
"minutes" -> Calendar.MINUTE
"hours" -> Calendar.HOUR
else -> null
}?.let {
add(it, -timeAgo)
}
}.timeInMillis
}
}
return 0L
}
override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, newHeaders())
override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply {
document.select("select#page").first().select("option").forEach {
add(Page(size, baseUrl + it.attr("value")))
}
}
override fun imageUrlRequest(page: Page) = GET(page.url, newHeaders())
override fun imageUrlParse(document: Document) = document.select("div.pic_box img.manga_pic").first().attr("src").orEmpty()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
return GET("$baseUrl/search/?name_sel=&wd=$query&author_sel=&author=&artist_sel=&artist=&category_id=&out_category_id=&completed_series=&page=$page.html", headers)
}
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
//TODO: Implement filters list.
// Array.from(document.querySelectorAll('.optionbox .typelist:nth-child(3) ul')).map(a => Array.from(a.querySelectorAll('li')).map(b => `Genre("${b.querySelector('label').innerText}", "${a.querySelector('li[cate_id]').getAttribute('cate_id')}")`)).join(',\n')
// http://es.ninemanga.com/search/?name_sel=contain&wd=&author_sel=contain&author=&artist_sel=contain&artist=&category_id=&out_category_id=&completed_series=either&type=high
}

View File

@ -0,0 +1,229 @@
package eu.kanade.tachiyomi.extension.all.ninemanga
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.source.model.SManga
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
class NineMangaFactory : SourceFactory {
override fun createSources(): List<Source> = getAllNineManga()
}
fun getAllNineManga(): List<Source> {
return listOf(
NineMangaEn(),
NineMangaEs(),
NineMangaBr(),
NineMangaRu(),
NineMangaDe(),
NineMangaIt(),
NineMangaFr()
)
}
class NineMangaEn : NineManga("NineMangaEn", "http://en.ninemanga.com", "en")
class NineMangaEs : NineManga("NineMangaEs", "http://es.ninemanga.com", "es") {
override fun parseStatus(status: String) = when {
status.contains("En curso") -> SManga.ONGOING
status.contains("Completado") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun parseChapterDate(date: String): Long {
val dateWords = date.split(" ")
if (dateWords.size == 3) {
if(dateWords[1].contains(",")){
try {
return SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(date).time
} catch (e: ParseException) {
return 0L
}
}else{
val timeAgo = Integer.parseInt(dateWords[0])
return Calendar.getInstance().apply {
when (dateWords[1]) {
"minutos" -> Calendar.MINUTE
"horas" -> Calendar.HOUR
else -> null
}?.let {
add(it, -timeAgo)
}
}.timeInMillis
}
}
return 0L
}
}
class NineMangaBr : NineManga("NineMangaBr", "http://br.ninemanga.com", "br") {
override fun parseStatus(status: String) = when {
status.contains("Em tradução") -> SManga.ONGOING
status.contains("Completo") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun parseChapterDate(date: String): Long {
val dateWords = date.split(" ")
if (dateWords.size == 3) {
if(dateWords[1].contains(",")){
try {
return SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(date).time
} catch (e: ParseException) {
return 0L
}
}else{
val timeAgo = Integer.parseInt(dateWords[0])
return Calendar.getInstance().apply {
when (dateWords[1]) {
"minutos" -> Calendar.MINUTE
"hora" -> Calendar.HOUR
else -> null
}?.let {
add(it, -timeAgo)
}
}.timeInMillis
}
}
return 0L
}
}
class NineMangaRu : NineManga("NineMangaRu", "http://ru.ninemanga.com", "ru") {
override fun parseStatus(status: String) = when {
// No Ongoing status
status.contains("завершенный") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun parseChapterDate(date: String): Long {
val dateWords = date.split(" ")
if (dateWords.size == 3) {
if(dateWords[1].contains(",")){
try {
return SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(date).time
} catch (e: ParseException) {
return 0L
}
}else{
val timeAgo = Integer.parseInt(dateWords[0])
return Calendar.getInstance().apply {
when (dateWords[1]) {
"минут" -> Calendar.MINUTE
"часа" -> Calendar.HOUR
else -> null
}?.let {
add(it, -timeAgo)
}
}.timeInMillis
}
}
return 0L
}
}
class NineMangaDe : NineManga("NineMangaDe", "http://de.ninemanga.com", "de") {
override fun parseStatus(status: String) = when {
status.contains("Laufende") -> SManga.ONGOING
status.contains("Abgeschlossen") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun parseChapterDate(date: String): Long {
val dateWords = date.split(" ")
if (dateWords.size == 3) {
try {
return SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(date).time
} catch (e: ParseException) {
return 0L
}
}
else if (dateWords.size == 2) { // Aleman
val timeAgo = Integer.parseInt(dateWords[0])
return Calendar.getInstance().apply {
when (dateWords[1]) {
"Stunden" -> Calendar.HOUR // Aleman - 2 palabras
else -> null
}?.let {
add(it, -timeAgo)
}
}.timeInMillis
}
return 0L
}
}
class NineMangaIt : NineManga("NineMangaIt", "http://it.ninemanga.com", "it") {
override fun parseStatus(status: String) = when {
status.contains("In corso") -> SManga.ONGOING
status.contains("Completato") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun parseChapterDate(date: String): Long {
val dateWords = date.split(" ")
if (dateWords.size == 3) {
if(!dateWords[1].contains(",")){
try {
return SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(date).time
} catch (e: ParseException) {
return 0L
}
}else{
val timeAgo = Integer.parseInt(dateWords[0])
return Calendar.getInstance().apply {
when (dateWords[1]) {
"minuti" -> Calendar.MINUTE
"ore" -> Calendar.HOUR
else -> null
}?.let {
add(it, -timeAgo)
}
}.timeInMillis
}
}
return 0L
}
}
class NineMangaFr : NineManga("NineMangaFr", "http://fr.ninemanga.com", "fr") {
override fun parseStatus(status: String) = when {
status.contains("En cours") -> SManga.ONGOING
status.contains("Complété") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun parseChapterDate(date: String): Long {
val dateWords = date.split(" ")
if (dateWords.size == 3) {
try {
return SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(date).time
} catch (e: ParseException) {
return 0L
}
}
else if(dateWords.size == 5) {
val timeAgo = Integer.parseInt(dateWords[3])
return Calendar.getInstance().apply {
when (dateWords[4]) {
"minutes" -> Calendar.MINUTE
"heures" -> Calendar.HOUR
else -> null
}?.let {
add(it, -timeAgo)
}
}.timeInMillis
}
return 0L
}
}