Manhwa latino (#9692)
* First commit Mahnwa-Latino Extension. * manhwa-latino: Implementing find function TODO: only works with text, filter not implemented yet * manhwa-latino Add Parser to finde the information of Manhwa-Latino The parser 'ManhwaLatinoSiteParser.kt' make the whole magic to find the information of the Website. Mudularize The code and adding documentation * manhwa-latino: Adding Logos der Extension TODO: I am not to happy, i will check it later. * manhwa-latino: Adding Tags to Genre Combobox * manhwa-latino: Adding Headers to prevent error 404 The headers are necesary to prevent error 403 by downloading images. * manhwa-latino: Tags addded into Manga Description Page Status from Manga readed from Tags * manhwa-latino: Modularize Code * manhwa-latino: Adding Uploaddate for Chapters * manhwa-latino: Bug to get Chapter Number fixed * manhwa-latino: Logo 0.2 * manhwa-latino: Versionb 1.2.10 Adding Comments to ManhwaLatinoSiteParser * manhwa-latino: Remove logo_model directory * manhwa-latino: Show Seconds after Release a new Chapter Co-authored-by: Luis Beroiza <luisalberto.beroizaosses@intern.osp-dd.de>
This commit is contained in:
parent
c5262ebb59
commit
5695e7e470
2
src/es/mahnwalatino/AndroidManifest.xml
Normal file
2
src/es/mahnwalatino/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="eu.kanade.tachiyomi.extension" />
|
12
src/es/mahnwalatino/build.gradle
Normal file
12
src/es/mahnwalatino/build.gradle
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
|
ext {
|
||||||
|
extName = 'Manhwa-Latino'
|
||||||
|
pkgNameSuffix = 'es.manhwalatino'
|
||||||
|
extClass = '.ManhwaLatino'
|
||||||
|
extVersionCode = 11
|
||||||
|
containsNsfw = true
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
BIN
src/es/mahnwalatino/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/es/mahnwalatino/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
BIN
src/es/mahnwalatino/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/es/mahnwalatino/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
src/es/mahnwalatino/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/es/mahnwalatino/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
src/es/mahnwalatino/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/es/mahnwalatino/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
src/es/mahnwalatino/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/es/mahnwalatino/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
BIN
src/es/mahnwalatino/res/web_hi_res_512.png
Normal file
BIN
src/es/mahnwalatino/res/web_hi_res_512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 160 KiB |
@ -0,0 +1,279 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.es.manhwalatino
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.extension.es.manhwalatino.filters.GenreTagFilter
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
|
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 eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import okhttp3.Headers
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
|
||||||
|
class ManhwaLatino : ParsedHttpSource() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the source.
|
||||||
|
*/
|
||||||
|
override val name = "Manhwa-Latino"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base url of the website without the trailing slash, like: http://mysite.com
|
||||||
|
*/
|
||||||
|
override val baseUrl = "https://manhwa-latino.com"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser for Mainsite or Genre Site
|
||||||
|
*/
|
||||||
|
val manhwaLatinoSiteParser = ManhwaLatinoSiteParser(baseUrl)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An ISO 639-1 compliant language code (two letters in lower case).
|
||||||
|
*/
|
||||||
|
override val lang = "es"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the source has support for latest updates.
|
||||||
|
*/
|
||||||
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User Agent for this wWebsite
|
||||||
|
*/
|
||||||
|
private val userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
|
||||||
|
"(KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"
|
||||||
|
|
||||||
|
override fun headersBuilder(): Headers.Builder {
|
||||||
|
return Headers.Builder()
|
||||||
|
.add("User-Agent", userAgent)
|
||||||
|
.add("Referer", "$baseUrl/")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Jsoup selector that returns a list of [Element] corresponding to each manga.
|
||||||
|
*/
|
||||||
|
override fun popularMangaSelector(): String {
|
||||||
|
return manhwaLatinoSiteParser.popularMangaSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Jsoup selector that returns a list of [Element] corresponding to each manga.
|
||||||
|
*/
|
||||||
|
override fun latestUpdatesSelector(): String {
|
||||||
|
return manhwaLatinoSiteParser.latestUpdatesSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Jsoup selector that returns a list of [Element] corresponding to each manga.
|
||||||
|
*/
|
||||||
|
override fun searchMangaSelector(): String {
|
||||||
|
return manhwaLatinoSiteParser.searchMangaSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Jsoup selector that returns a list of [Element] corresponding to each chapter.
|
||||||
|
*/
|
||||||
|
override fun chapterListSelector() =
|
||||||
|
throw Exception("Not Used")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Jsoup selector that returns the <a> tag linking to the next page, or null if
|
||||||
|
* there's no next page.
|
||||||
|
*/
|
||||||
|
override fun popularMangaNextPageSelector(): String {
|
||||||
|
return manhwaLatinoSiteParser.popularMangaNextPageSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Jsoup selector that returns the <a> tag linking to the next page, or null if
|
||||||
|
* there's no next page.
|
||||||
|
*/
|
||||||
|
override fun latestUpdatesNextPageSelector(): String {
|
||||||
|
return manhwaLatinoSiteParser.latestUpdatesNextPageSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Jsoup selector that returns the <a> tag linking to the next page, or null if
|
||||||
|
* there's no next page.
|
||||||
|
*/
|
||||||
|
override fun searchMangaNextPageSelector(): String {
|
||||||
|
return manhwaLatinoSiteParser.searchMangaNextPageSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request for the popular manga given the page.
|
||||||
|
*
|
||||||
|
* @param page the page number to retrieve.
|
||||||
|
*/
|
||||||
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
|
return GET("$baseUrl/page/$page/", headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request for latest manga given the page.
|
||||||
|
*
|
||||||
|
* @param page the page number to retrieve.
|
||||||
|
*/
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request {
|
||||||
|
return GET("$baseUrl/page/$page/", headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the response from the site and returns a [MangasPage] object.
|
||||||
|
*
|
||||||
|
* @param response the response from the site.
|
||||||
|
*/
|
||||||
|
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||||
|
val document = response.asJsoup()
|
||||||
|
val mangas = document.select(latestUpdatesSelector()).map { latestUpdatesFromElement(it) }
|
||||||
|
return MangasPage(mangas, manhwaLatinoSiteParser.latestUpdatesHasNextPages())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a manga from the given [element]. Most sites only show the title and the url, it's
|
||||||
|
* totally fine to fill only those two values.
|
||||||
|
*
|
||||||
|
* @param element an element obtained from [latestUpdatesSelector].
|
||||||
|
*/
|
||||||
|
override fun latestUpdatesFromElement(element: Element): SManga {
|
||||||
|
return manhwaLatinoSiteParser.getMangaFromLastTranslatedSlide(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request for the search manga given the page.
|
||||||
|
*
|
||||||
|
* @param page the page number to retrieve.
|
||||||
|
* @param query the search query.
|
||||||
|
* @param filters the list of filters to apply.
|
||||||
|
*/
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
val uri = manhwaLatinoSiteParser.searchMangaRequest(page, query, filters)
|
||||||
|
return GET(uri.toString(), headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the response from the site and returns a [MangasPage] object.
|
||||||
|
*
|
||||||
|
* @param response the response from the site.
|
||||||
|
*/
|
||||||
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
|
return manhwaLatinoSiteParser.searchMangaParse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Returns the request for the details of a manga. Override only if it's needed to change the
|
||||||
|
// * url, send different headers or request method like POST.
|
||||||
|
// *
|
||||||
|
// * @param manga the manga to be updated.
|
||||||
|
// */
|
||||||
|
// override fun mangaDetailsRequest(manga: SManga) = GET(baseUrl + manga.url, headers)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request for updating the chapter list. Override only if it's needed to override
|
||||||
|
* the url, send different headers or request method like POST.
|
||||||
|
*
|
||||||
|
* @param manga the manga to look for chapters.
|
||||||
|
*/
|
||||||
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
|
return GET(baseUrl + manga.url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a manga from the given [element]. Most sites only show the title and the url, it's
|
||||||
|
* totally fine to fill only those two values.
|
||||||
|
*
|
||||||
|
* @param element an element obtained from [popularMangaSelector].
|
||||||
|
*/
|
||||||
|
override fun popularMangaFromElement(element: Element) = mangaFromElement(element)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a manga from the given [element]. Most sites only show the title and the url, it's
|
||||||
|
* totally fine to fill only those two values.
|
||||||
|
*
|
||||||
|
* @param element an element obtained from [searchMangaSelector].
|
||||||
|
*/
|
||||||
|
override fun searchMangaFromElement(element: Element) = mangaFromElement(element)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a manga from the given [element]. Most sites only show the title and the url, it's
|
||||||
|
* totally fine to fill only those two values.
|
||||||
|
*
|
||||||
|
* @param element an element obtained from [searchMangaSelector].
|
||||||
|
*/
|
||||||
|
private fun mangaFromElement(element: Element): SManga {
|
||||||
|
return manhwaLatinoSiteParser.getMangaFromList(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the response from the site and returns a list of chapters.
|
||||||
|
*
|
||||||
|
* @param response the response from the site.
|
||||||
|
*/
|
||||||
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
|
return manhwaLatinoSiteParser.getChapterListParse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a chapter from the given element.
|
||||||
|
*
|
||||||
|
* @param element an element obtained from [chapterListSelector].
|
||||||
|
*/
|
||||||
|
override fun chapterFromElement(element: Element) = throw Exception("Not used")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the details of the manga from the given [document].
|
||||||
|
*
|
||||||
|
* @param document the parsed document.
|
||||||
|
*/
|
||||||
|
override fun mangaDetailsParse(document: Document): SManga {
|
||||||
|
return manhwaLatinoSiteParser.getMangaDetails(document)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request for getting the page list. Override only if it's needed to override the
|
||||||
|
* url, send different headers or request method like POST.
|
||||||
|
* (Request to Webseite with comic)
|
||||||
|
*
|
||||||
|
* @param chapter the chapter whose page list has to be fetched.
|
||||||
|
*/
|
||||||
|
override fun pageListRequest(chapter: SChapter): Request {
|
||||||
|
return GET(baseUrl + chapter.url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the response from the site and returns the page list.
|
||||||
|
* (Parse the comic pages from the website with the chapter)
|
||||||
|
*
|
||||||
|
* @param response the response from the site.
|
||||||
|
*/
|
||||||
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
|
return manhwaLatinoSiteParser.getPageListParse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a page list from the given document.
|
||||||
|
*
|
||||||
|
* @param document the parsed document.
|
||||||
|
*/
|
||||||
|
override fun pageListParse(document: Document) = throw Exception("Not Used")
|
||||||
|
|
||||||
|
override fun imageUrlParse(document: Document) = throw Exception("Not Used")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of filters for the source.
|
||||||
|
*/
|
||||||
|
override fun getFilterList() = FilterList(
|
||||||
|
Filter.Header("NOTA: ¡La búsqueda de títulos no funciona!"), // "Title search not working"
|
||||||
|
Filter.Separator(),
|
||||||
|
GenreTagFilter(),
|
||||||
|
// LetterFilter(),
|
||||||
|
// StatusFilter(),
|
||||||
|
// SortFilter()
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,294 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.es.manhwalatino
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import eu.kanade.tachiyomi.extension.es.manhwalatino.filters.UriFilter
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
|
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.util.asJsoup
|
||||||
|
import okhttp3.Response
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class ManhwaLatinoSiteParser(private val baseUrl: String) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: ADD SEARCH_TAG
|
||||||
|
*/
|
||||||
|
enum class SearchType {
|
||||||
|
SEARCH_FREE, SEARCH_FILTER
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of search ( FREE, FILTER)
|
||||||
|
*/
|
||||||
|
var searchType = SearchType.SEARCH_FREE
|
||||||
|
|
||||||
|
private val urlHTMLSelector: String = "a"
|
||||||
|
private val titleHTMLSelector: String = "h3"
|
||||||
|
private val thumbnailUrlMangaListHTMLSelector: String = "div.item-thumb.c-image-hover img"
|
||||||
|
private val authorHTMLSelector: String = "div.author-content"
|
||||||
|
private val artistHTMLSelector: String = "div.artist-content"
|
||||||
|
private val descriptionHTMLSelector: String = "div.summary__content.show-more p"
|
||||||
|
private val genreHTMLSelector: String = "div.genres-content a"
|
||||||
|
private val statusHTMLSelector: String =
|
||||||
|
"div.summary_content div.post-status div.post-content_item div.summary-content"
|
||||||
|
private val thumbnailUrlMangaDetailsHTMLSelector: String = "div.summary_image img"
|
||||||
|
private val tagsHTMLSelector: String = "div.tags-content a"
|
||||||
|
private val searchSiteMangasHTMLSelector = "div.c-tabs-item__content"
|
||||||
|
private val genreSiteMangasHTMLSelector = "div.page-item-detail.manga"
|
||||||
|
private val latestUpdatesSelectorUrl = "div.slider__thumb_item a"
|
||||||
|
private val latestUpdatesSelectorThumbnailUrl = "div.slider__thumb_item a img"
|
||||||
|
private val latestUpdatesSelectorTitle = "div.slider__content h4"
|
||||||
|
private val chapterListParseSelector = "li.wp-manga-chapter"
|
||||||
|
private val chapterLinkParser = "a"
|
||||||
|
private val chapterReleaseDateLinkParser = "span.chapter-release-date a"
|
||||||
|
private val chapterReleaseDateIParser = "span.chapter-release-date i"
|
||||||
|
private val pageListParseSelector = "div.page-break.no-gaps img"
|
||||||
|
|
||||||
|
val searchMangaNextPageSelector = "link[rel=next]"
|
||||||
|
val latestUpdatesSelector = "div.slider__item"
|
||||||
|
|
||||||
|
val popularMangaSelector = "div.page-item-detail.manga"
|
||||||
|
val searchMangaSelector = "div.page-item-detail.manga"
|
||||||
|
val popularMangaNextPageSelector = "a.nextpostslink"
|
||||||
|
val latestUpdatesNextPageSelector = "div[role=navigation] a.last"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Latest Updates are in a Slider, this Methods get a Manga from the slide
|
||||||
|
*/
|
||||||
|
fun getMangaFromLastTranslatedSlide(element: Element): SManga {
|
||||||
|
val manga = SManga.create()
|
||||||
|
manga.url =
|
||||||
|
getUrlWithoutDomain(element.select(latestUpdatesSelectorUrl).first().attr("abs:href"))
|
||||||
|
manga.title = element.select(latestUpdatesSelectorTitle).text().trim()
|
||||||
|
manga.thumbnail_url = element.select(latestUpdatesSelectorThumbnailUrl).attr("abs:data-src")
|
||||||
|
return manga
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Latest Updates has only one site
|
||||||
|
*/
|
||||||
|
fun latestUpdatesHasNextPages() = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get eine Liste mit Mangas from Search Site
|
||||||
|
*/
|
||||||
|
fun getMangasFromSearchSite(document: Document): List<SManga> {
|
||||||
|
return document.select(searchSiteMangasHTMLSelector).map { getMangaFromList(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get eine Liste mit Mangas from Genre Site
|
||||||
|
*/
|
||||||
|
fun getMangasFromGenreSite(document: Document): List<SManga> {
|
||||||
|
return document.select(genreSiteMangasHTMLSelector).map { getMangaFromList(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse The Information from Mangas From Search or Genre Site
|
||||||
|
* Title, Address and thumbnail_url
|
||||||
|
*/
|
||||||
|
fun getMangaFromList(element: Element): SManga {
|
||||||
|
val manga = SManga.create()
|
||||||
|
manga.url = getUrlWithoutDomain(element.select(urlHTMLSelector).first().attr("abs:href"))
|
||||||
|
manga.title = element.select(titleHTMLSelector).text().trim()
|
||||||
|
manga.thumbnail_url = element.select(thumbnailUrlMangaListHTMLSelector).attr("abs:data-src")
|
||||||
|
return manga
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get The Details of a Manga Main Website
|
||||||
|
* Description, genre, tags, picture (thumbnail_url)
|
||||||
|
* status...
|
||||||
|
*/
|
||||||
|
fun getMangaDetails(document: Document): SManga {
|
||||||
|
val manga = SManga.create()
|
||||||
|
|
||||||
|
val descriptionList = document.select(descriptionHTMLSelector).map { it.text() }
|
||||||
|
val author = document.select(authorHTMLSelector).text()
|
||||||
|
val artist = document.select(artistHTMLSelector).text()
|
||||||
|
|
||||||
|
val genrelist = document.select(genreHTMLSelector).map { it.text() }
|
||||||
|
val tagList = document.select(tagsHTMLSelector).map { it.text() }
|
||||||
|
val genreTagList = genrelist + tagList
|
||||||
|
|
||||||
|
manga.thumbnail_url =
|
||||||
|
document.select(thumbnailUrlMangaDetailsHTMLSelector).attr("abs:data-src")
|
||||||
|
manga.description = descriptionList.joinToString("\n")
|
||||||
|
manga.author = if (author.isBlank()) "Autor Desconocido" else author
|
||||||
|
manga.artist = artist
|
||||||
|
manga.genre = genreTagList.joinToString(", ")
|
||||||
|
manga.status = findMangaStatus(tagList, document)
|
||||||
|
return manga
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findMangaStatus(tagList: List<String>, document: Document): Int {
|
||||||
|
return if (tagList.contains("Fin")) {
|
||||||
|
SManga.COMPLETED
|
||||||
|
} else {
|
||||||
|
when (document.select(statusHTMLSelector)?.first()?.text()?.trim()) {
|
||||||
|
"Publicandose" -> SManga.ONGOING
|
||||||
|
else -> SManga.UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the response from the site and returns a list of chapters.
|
||||||
|
*
|
||||||
|
* @param response the response from the site.
|
||||||
|
*/
|
||||||
|
fun getChapterListParse(response: Response): List<SChapter> {
|
||||||
|
return response.asJsoup().select(chapterListParseSelector).map { element ->
|
||||||
|
// Link to the Chapter with the info (address and chapter title)
|
||||||
|
val chapterInfo = element.select(chapterLinkParser)
|
||||||
|
// Chaptername
|
||||||
|
val chapterName = chapterInfo.text().trim()
|
||||||
|
// release date came as text with format dd/mm/yyyy from a link or <i>dd/mm/yyyy</i>
|
||||||
|
val chapterReleaseDate = getChapterReleaseDate(element)
|
||||||
|
SChapter.create().apply {
|
||||||
|
name = chapterName
|
||||||
|
chapter_number = getChapterNumber(chapterName)
|
||||||
|
url = getUrlWithoutDomain(chapterInfo.attr("abs:href"))
|
||||||
|
date_upload = parseChapterReleaseDate(chapterReleaseDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of Chapter from Chaptername
|
||||||
|
*/
|
||||||
|
private fun getChapterNumber(chapterName: String): Float =
|
||||||
|
Regex("""\d+""").find(chapterName)?.value.toString().trim().toFloat()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get The String with the information about the Release date of the Chapter
|
||||||
|
*/
|
||||||
|
private fun getChapterReleaseDate(element: Element): String {
|
||||||
|
val chapterReleaseDateLink = element.select(chapterReleaseDateLinkParser).attr("title")
|
||||||
|
val chapterReleaseDateI = element.select(chapterReleaseDateIParser).text()
|
||||||
|
return when {
|
||||||
|
chapterReleaseDateLink.isNotEmpty() -> chapterReleaseDateLink
|
||||||
|
chapterReleaseDateI.isNotEmpty() -> chapterReleaseDateI
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform String with the Date of Release into Long format
|
||||||
|
*/
|
||||||
|
private fun parseChapterReleaseDate(releaseDateStr: String): Long {
|
||||||
|
val regExSecs = Regex("""hace\s+(\d+)\s+segundos?""")
|
||||||
|
val regExMins = Regex("""hace\s+(\d+)\s+mins?""")
|
||||||
|
val regExHours = Regex("""hace\s+(\d+)\s+horas?""")
|
||||||
|
val regExDays = Regex("""hace\s+(\d+)\s+días?""")
|
||||||
|
val regExDate = Regex("""\d+/\d+/\d+""")
|
||||||
|
|
||||||
|
return when {
|
||||||
|
regExSecs.containsMatchIn(releaseDateStr) ->
|
||||||
|
getReleaseTime(releaseDateStr, Calendar.SECOND)
|
||||||
|
|
||||||
|
regExMins.containsMatchIn(releaseDateStr) ->
|
||||||
|
getReleaseTime(releaseDateStr, Calendar.MINUTE)
|
||||||
|
|
||||||
|
regExHours.containsMatchIn(releaseDateStr) ->
|
||||||
|
getReleaseTime(releaseDateStr, Calendar.HOUR)
|
||||||
|
|
||||||
|
regExDays.containsMatchIn(releaseDateStr) ->
|
||||||
|
getReleaseTime(releaseDateStr, Calendar.DAY_OF_YEAR)
|
||||||
|
|
||||||
|
regExDate.containsMatchIn(releaseDateStr) ->
|
||||||
|
SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).parse(releaseDateStr).time
|
||||||
|
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the Release time from a Text String
|
||||||
|
* Format of the String "hace\s+\d+\s(segundo|minuto|hora|dia)s?"
|
||||||
|
*/
|
||||||
|
private fun getReleaseTime(releaseDateStr: String, timeType: Int): Long {
|
||||||
|
val releaseTimeAgo = Regex("""\d+""").find(releaseDateStr)?.value.toString().toInt()
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
calendar.add(timeType, -releaseTimeAgo)
|
||||||
|
return calendar.timeInMillis
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the response from the site and returns the page list.
|
||||||
|
* (Parse the comic pages from the website with the chapter)
|
||||||
|
*
|
||||||
|
* @param response the response from the site.
|
||||||
|
*/
|
||||||
|
fun getPageListParse(response: Response): List<Page> {
|
||||||
|
val list =
|
||||||
|
response.asJsoup().select(pageListParseSelector).mapIndexed { index, imgElement ->
|
||||||
|
Page(index, "", imgElement.attr("abs:data-src"))
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request for the search manga given the page.
|
||||||
|
*
|
||||||
|
* @param page the page number to retrieve.
|
||||||
|
* @param query the search query.
|
||||||
|
* @param filters the list of filters to apply.
|
||||||
|
*/
|
||||||
|
fun searchMangaRequest(page: Int, query: String, filters: FilterList): Uri.Builder {
|
||||||
|
val uri = Uri.parse(baseUrl).buildUpon()
|
||||||
|
if (query.isNotBlank()) {
|
||||||
|
searchType = SearchType.SEARCH_FREE
|
||||||
|
uri.appendQueryParameter("s", query)
|
||||||
|
.appendQueryParameter("post_type", "wp-manga")
|
||||||
|
} else {
|
||||||
|
searchType = SearchType.SEARCH_FILTER
|
||||||
|
// Append uri filters
|
||||||
|
filters.forEach {
|
||||||
|
if (it is UriFilter)
|
||||||
|
it.addToUri(uri)
|
||||||
|
}
|
||||||
|
uri.appendPath("page").appendPath(page.toString())
|
||||||
|
}
|
||||||
|
return uri
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the response from the site and returns a [MangasPage] object.
|
||||||
|
*
|
||||||
|
* @param response the response from the site.
|
||||||
|
*/
|
||||||
|
fun searchMangaParse(response: Response): MangasPage {
|
||||||
|
val document = response.asJsoup()
|
||||||
|
val hasNextPages = hasNextPages(document)
|
||||||
|
val mangas: List<SManga>
|
||||||
|
|
||||||
|
when (searchType) {
|
||||||
|
SearchType.SEARCH_FREE ->
|
||||||
|
mangas = getMangasFromSearchSite(document)
|
||||||
|
SearchType.SEARCH_FILTER ->
|
||||||
|
mangas = getMangasFromGenreSite(document)
|
||||||
|
}
|
||||||
|
|
||||||
|
return MangasPage(mangas, hasNextPages)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there ir another page to show
|
||||||
|
*/
|
||||||
|
fun hasNextPages(document: Document): Boolean {
|
||||||
|
return !document.select(searchMangaNextPageSelector).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Address url without the base url.
|
||||||
|
*/
|
||||||
|
protected fun getUrlWithoutDomain(url: String) = url.substringAfter(baseUrl)
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.es.manhwalatino.filters
|
||||||
|
|
||||||
|
class GenreTagFilter : UriPartFilter(
|
||||||
|
"Género o Tag",
|
||||||
|
"filtro",
|
||||||
|
arrayOf(
|
||||||
|
Pair("manga-genre/manga", "Todas"),
|
||||||
|
Pair("manga-genre/accion", "Acción"),
|
||||||
|
Pair("manga-genre/adulto", "Adulto"),
|
||||||
|
Pair("manga-genre/aventura", "Aventura"),
|
||||||
|
Pair("manga-genre/bondage", "Bondage"),
|
||||||
|
Pair("manga-genre/cambio-de-pareja", "Cambio de pareja"),
|
||||||
|
Pair("manga-genre/chantaje", "Chantaje"),
|
||||||
|
Pair("manga-genre/ciencia-ficcion", "Ciencia Ficción"),
|
||||||
|
Pair("manga-genre/comedia", "Comedia"),
|
||||||
|
Pair("manga-genre/doujinshi", "Doujinshi"),
|
||||||
|
Pair("manga-genre/drama", "Drama"),
|
||||||
|
Pair("manga-tag/lunes", "Dia Publicación Lunes"),
|
||||||
|
Pair("manga-tag/martes", "Dia Publicación Martes"),
|
||||||
|
Pair("manga-tag/miercoles", "Dia Publicación Miércoles"),
|
||||||
|
Pair("manga-tag/jueves", "Dia Publicación Jueves"),
|
||||||
|
Pair("manga-tag/viernes", "Dia Publicación Viernes"),
|
||||||
|
Pair("manga-tag/sabado", "Dia Publicación Sábado"),
|
||||||
|
Pair("manga-tag/domingo", "Dia Publicación Domingo"),
|
||||||
|
Pair("manga-genre/ecchi", "Ecchi"),
|
||||||
|
Pair("manga-tag/espanol", "Español"),
|
||||||
|
Pair("manga-genre/exhibicion", "Exhibición"),
|
||||||
|
Pair("manga-genre/familia", "Familia"),
|
||||||
|
Pair("manga-genre/fantasia", "Fantasia"),
|
||||||
|
Pair("manga-tag/fin", "Finalizado"),
|
||||||
|
Pair("manga-genre/harem", "Harem"),
|
||||||
|
Pair("manga-genre/manga", "Manga"),
|
||||||
|
Pair("manga-genre/manhua", "Manhua"),
|
||||||
|
Pair("manga-genre/manhwa", "Manhwa"),
|
||||||
|
Pair("manga-genre/misterio", "Misterio"),
|
||||||
|
Pair("manga-genre/ntr", "Ntr"),
|
||||||
|
Pair("manga-genre/obsenidad", "Obsenidad"),
|
||||||
|
Pair("manga-genre/relato vida", "Relato vida"),
|
||||||
|
Pair("manga-genre/romance", "Romance"),
|
||||||
|
Pair("manga-genre/sangre", "Sangre"),
|
||||||
|
Pair("manga-genre/sexo-forzado", "Sexo forzado"),
|
||||||
|
Pair("manga-genre/sometimiento", "Sometimiento"),
|
||||||
|
Pair("manga-genre/tragedia", "Tragedia"),
|
||||||
|
Pair("manga-genre/venganza", "Venganza"),
|
||||||
|
Pair("manga-genre/vida-escolar", "Vida Escolar"),
|
||||||
|
Pair("manga-genre/webtoon", "Webtoon")
|
||||||
|
)
|
||||||
|
)
|
@ -0,0 +1,10 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.es.manhwalatino.filters
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a filter that is able to modify a URI.
|
||||||
|
*/
|
||||||
|
interface UriFilter {
|
||||||
|
fun addToUri(uri: Uri.Builder)
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.es.manhwalatino.filters
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that creates a select filter. Each entry in the dropdown has a name and a display name.
|
||||||
|
* If an entry is selected it is appended as a query parameter onto the end of the URI.
|
||||||
|
* If `firstIsUnspecified` is set to true, if the first entry is selected, nothing will be appended on the the URI.
|
||||||
|
*/
|
||||||
|
// vals: <name, display>
|
||||||
|
open class UriPartFilter(
|
||||||
|
displayName: String,
|
||||||
|
private val uriParam: String,
|
||||||
|
private val vals: Array<Pair<String, String>>,
|
||||||
|
private val firstIsUnspecified: Boolean = true,
|
||||||
|
defaultValue: Int = 0
|
||||||
|
) :
|
||||||
|
Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray(), defaultValue),
|
||||||
|
UriFilter {
|
||||||
|
override fun addToUri(uri: Uri.Builder) {
|
||||||
|
|
||||||
|
if (state != 0 || !firstIsUnspecified) {
|
||||||
|
val filter = vals[state].first
|
||||||
|
uri.appendPath(filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user