diff --git a/app/build.gradle b/app/build.gradle index 17bca730f..8152bb2c0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,12 +34,12 @@ android { publishNonDefault true defaultConfig { - applicationId "eu.kanade.tachiyomi" + applicationId "eu.kanade.tachiyomi.eh" minSdkVersion 16 targetSdkVersion 23 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - versionCode 218 - versionName "Tachiyomi-EH-2.18 (Update 18)" + versionCode 2180 + versionName "2.18.0" buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\"" buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\"" @@ -51,13 +51,11 @@ android { buildTypes { debug { - applicationIdSuffix ".eh" minifyEnabled false shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { - applicationIdSuffix ".eh" minifyEnabled false shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt deleted file mode 100644 index 0fe674241..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt +++ /dev/null @@ -1,267 +0,0 @@ -package eu.kanade.tachiyomi.data.source.online.english - -import android.content.Context -import android.net.Uri -import android.text.Html -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.network.GET -import eu.kanade.tachiyomi.data.network.POST -import eu.kanade.tachiyomi.data.network.asObservable -import eu.kanade.tachiyomi.data.source.EN -import eu.kanade.tachiyomi.data.source.Language -import eu.kanade.tachiyomi.data.source.model.MangasPage -import eu.kanade.tachiyomi.data.source.model.Page -import eu.kanade.tachiyomi.data.source.online.LoginSource -import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource -import eu.kanade.tachiyomi.util.asJsoup -import eu.kanade.tachiyomi.util.selectText -import okhttp3.FormBody -import okhttp3.Request -import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import rx.Observable -import java.net.URI -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.* -import java.util.regex.Pattern - -class Batoto(context: Context, override val id: Int) : ParsedOnlineSource(context), LoginSource { - - override val name = "Batoto" - - override val baseUrl = "http://bato.to" - - override val lang: Language get() = EN - - private val datePattern = Pattern.compile("(\\d+|A|An)\\s+(.*?)s? ago.*") - - private val dateFields = HashMap().apply { - put("second", Calendar.SECOND) - put("minute", Calendar.MINUTE) - put("hour", Calendar.HOUR) - put("day", Calendar.DATE) - put("week", Calendar.WEEK_OF_YEAR) - put("month", Calendar.MONTH) - put("year", Calendar.YEAR) - } - - private val staffNotice = Pattern.compile("=+Batoto Staff Notice=+([^=]+)==+", Pattern.CASE_INSENSITIVE) - - override fun headersBuilder() = super.headersBuilder() - .add("Cookie", "lang_option=English") - - private val pageHeaders = super.headersBuilder() - .add("Referer", "http://bato.to/reader") - .build() - - override fun popularMangaInitialUrl() = "$baseUrl/search_ajax?order_cond=views&order=desc&p=1" - - override fun popularMangaParse(response: Response, page: MangasPage) { - val document = response.asJsoup() - for (element in document.select(popularMangaSelector())) { - Manga.create(id).apply { - popularMangaFromElement(element, this) - page.mangas.add(this) - } - } - - page.nextPageUrl = document.select(popularMangaNextPageSelector()).first()?.let { - "$baseUrl/search_ajax?order_cond=views&order=desc&p=${page.page + 1}" - } - } - - override fun popularMangaSelector() = "tr:has(a)" - - override fun popularMangaFromElement(element: Element, manga: Manga) { - element.select("a[href^=http://bato.to]").first().let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.text().trim() - } - } - - override fun popularMangaNextPageSelector() = "#show_more_row" - - override fun searchMangaInitialUrl(query: String) = "$baseUrl/search_ajax?name=${Uri.encode(query)}&p=1" - - override fun searchMangaParse(response: Response, page: MangasPage, query: String) { - val document = response.asJsoup() - for (element in document.select(searchMangaSelector())) { - Manga.create(id).apply { - searchMangaFromElement(element, this) - page.mangas.add(this) - } - } - - page.nextPageUrl = document.select(searchMangaNextPageSelector()).first()?.let { - "$baseUrl/search_ajax?name=${Uri.encode(query)}&p=${page.page + 1}" - } - } - - override fun searchMangaSelector() = popularMangaSelector() - - override fun searchMangaFromElement(element: Element, manga: Manga) { - popularMangaFromElement(element, manga) - } - - override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() - - override fun mangaDetailsRequest(manga: Manga): Request { - val mangaId = manga.url.substringAfterLast("r") - return GET("$baseUrl/comic_pop?id=$mangaId", headers) - } - - override fun mangaDetailsParse(document: Document, manga: Manga) { - val tbody = document.select("tbody").first() - val artistElement = tbody.select("tr:contains(Author/Artist:)").first() - - manga.author = artistElement.selectText("td:eq(1)") - manga.artist = artistElement.selectText("td:eq(2)") ?: manga.author - manga.description = tbody.selectText("tr:contains(Description:) > td:eq(1)") - manga.thumbnail_url = document.select("img[src^=http://img.bato.to/forums/uploads/]").first()?.attr("src") - manga.status = parseStatus(document.selectText("tr:contains(Status:) > td:eq(1)")) - manga.genre = tbody.select("tr:contains(Genres:) img").map { it.attr("alt") }.joinToString(", ") - } - - private fun parseStatus(status: String?) = when (status) { - "Ongoing" -> Manga.ONGOING - "Complete" -> Manga.COMPLETED - else -> Manga.UNKNOWN - } - - override fun chapterListParse(response: Response, chapters: MutableList) { - val body = response.body().string() - val matcher = staffNotice.matcher(body) - if (matcher.find()) { - val notice = Html.fromHtml(matcher.group(1)).toString().trim() - throw Exception(notice) - } - - val document = response.asJsoup(body) - - for (element in document.select(chapterListSelector())) { - Chapter.create().apply { - chapterFromElement(element, this) - chapters.add(this) - } - } - } - - override fun chapterListSelector() = "tr.row.lang_English.chapter_row" - - override fun chapterFromElement(element: Element, chapter: Chapter) { - val urlElement = element.select("a[href^=http://bato.to/reader").first() - - chapter.setUrlWithoutDomain(urlElement.attr("href")) - chapter.name = urlElement.text() - chapter.date_upload = element.select("td").getOrNull(4)?.let { - parseDateFromElement(it) - } ?: 0 - } - - private fun parseDateFromElement(dateElement: Element): Long { - val dateAsString = dateElement.text() - - val date: Date - try { - date = SimpleDateFormat("dd MMMMM yyyy - hh:mm a", Locale.ENGLISH).parse(dateAsString) - } catch (e: ParseException) { - val m = datePattern.matcher(dateAsString) - - if (m.matches()) { - val number = m.group(1) - val amount = if (number.contains("A")) 1 else Integer.parseInt(m.group(1)) - val unit = m.group(2) - - date = Calendar.getInstance().apply { - add(dateFields[unit]!!, -amount) - }.time - } else { - return 0 - } - } - - return date.time - } - - override fun pageListRequest(chapter: Chapter): Request { - val id = chapter.url.substringAfterLast("#") - return GET("$baseUrl/areader?id=$id&p=1", pageHeaders) - } - - override fun pageListParse(document: Document, pages: MutableList) { - val selectElement = document.select("#page_select").first() - if (selectElement != null) { - for ((i, element) in selectElement.select("option").withIndex()) { - pages.add(Page(i, element.attr("value"))) - } - pages.getOrNull(0)?.imageUrl = imageUrlParse(document) - } else { - // For webtoons in one page - for ((i, element) in document.select("div > img").withIndex()) { - pages.add(Page(i, "", element.attr("src"))) - } - } - } - - override fun imageUrlRequest(page: Page): Request { - val pageUrl = page.url - val start = pageUrl.indexOf("#") + 1 - val end = pageUrl.indexOf("_", start) - val id = pageUrl.substring(start, end) - return GET("$baseUrl/areader?id=$id&p=${pageUrl.substring(end+1)}", pageHeaders) - } - - override fun imageUrlParse(document: Document): String { - return document.select("#comic_page").first().attr("src") - } - - override fun login(username: String, password: String) = - client.newCall(GET("$baseUrl/forums/index.php?app=core&module=global§ion=login", headers)) - .asObservable() - .flatMap { doLogin(it, username, password) } - .map { isAuthenticationSuccessful(it) } - - private fun doLogin(response: Response, username: String, password: String): Observable { - val doc = response.asJsoup() - val form = doc.select("#login").first() - val url = form.attr("action") - val authKey = form.select("input[name=auth_key]").first() - - val payload = FormBody.Builder().apply { - add(authKey.attr("name"), authKey.attr("value")) - add("ips_username", username) - add("ips_password", password) - add("invisible", "1") - add("rememberMe", "1") - }.build() - - return client.newCall(POST(url, headers, payload)).asObservable() - } - - override fun isAuthenticationSuccessful(response: Response) = - response.priorResponse() != null && response.priorResponse().code() == 302 - - override fun isLogged(): Boolean { - return network.cookies.get(URI(baseUrl)).any { it.name() == "pass_hash" } - } - - override fun fetchChapterList(manga: Manga): Observable> { - if (!isLogged()) { - val username = preferences.sourceUsername(this) - val password = preferences.sourcePassword(this) - - if (username.isNullOrEmpty() || password.isNullOrEmpty()) { - return Observable.error(Exception("User not logged")) - } else { - return login(username, password).flatMap { super.fetchChapterList(manga) } - } - - } else { - return super.fetchChapterList(manga) - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Kissmanga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Kissmanga.kt deleted file mode 100644 index 163712425..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Kissmanga.kt +++ /dev/null @@ -1,118 +0,0 @@ -package eu.kanade.tachiyomi.data.source.online.english - -import android.content.Context -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.network.GET -import eu.kanade.tachiyomi.data.network.POST -import eu.kanade.tachiyomi.data.source.EN -import eu.kanade.tachiyomi.data.source.Language -import eu.kanade.tachiyomi.data.source.model.MangasPage -import eu.kanade.tachiyomi.data.source.model.Page -import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource -import okhttp3.FormBody -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.text.SimpleDateFormat -import java.util.regex.Pattern - -class Kissmanga(context: Context, override val id: Int) : ParsedOnlineSource(context) { - - override val name = "Kissmanga" - - override val baseUrl = "http://kissmanga.com" - - override val lang: Language get() = EN - - override val client: OkHttpClient get() = network.cloudflareClient - - override fun popularMangaInitialUrl() = "$baseUrl/MangaList/MostPopular" - - override fun popularMangaSelector() = "table.listing tr:gt(1)" - - override fun popularMangaFromElement(element: Element, manga: Manga) { - element.select("td a:eq(0)").first().let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.text() - } - } - - override fun popularMangaNextPageSelector() = "li > a:contains(› Next)" - - override fun searchMangaRequest(page: MangasPage, query: String): Request { - if (page.page == 1) { - page.url = searchMangaInitialUrl(query) - } - - val form = FormBody.Builder().apply { - add("authorArtist", "") - add("mangaName", query) - add("status", "") - add("genres", "") - }.build() - - return POST(page.url, headers, form) - } - - override fun searchMangaInitialUrl(query: String) = "$baseUrl/AdvanceSearch" - - override fun searchMangaSelector() = popularMangaSelector() - - override fun searchMangaFromElement(element: Element, manga: Manga) { - popularMangaFromElement(element, manga) - } - - override fun searchMangaNextPageSelector() = null - - override fun mangaDetailsParse(document: Document, manga: Manga) { - val infoElement = document.select("div.barContent").first() - - manga.author = infoElement.select("p:has(span:contains(Author:)) > a").first()?.text() - manga.genre = infoElement.select("p:has(span:contains(Genres:)) > *:gt(0)").text() - manga.description = infoElement.select("p:has(span:contains(Summary:)) ~ p").text() - manga.status = infoElement.select("p:has(span:contains(Status:))").first()?.text().orEmpty().let { parseStatus(it)} - manga.thumbnail_url = document.select(".rightBox:eq(0) img").first()?.attr("src") - } - - fun parseStatus(status: String) = when { - status.contains("Ongoing") -> Manga.ONGOING - status.contains("Completed") -> Manga.COMPLETED - else -> Manga.UNKNOWN - } - - override fun chapterListSelector() = "table.listing tr:gt(1)" - - override fun chapterFromElement(element: Element, chapter: Chapter) { - val urlElement = element.select("a").first() - - chapter.setUrlWithoutDomain(urlElement.attr("href")) - chapter.name = urlElement.text() - chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let { - SimpleDateFormat("MM/dd/yyyy").parse(it).time - } ?: 0 - } - - override fun pageListRequest(chapter: Chapter) = POST(baseUrl + chapter.url, headers) - - override fun pageListParse(response: Response, pages: MutableList) { - //language=RegExp - val p = Pattern.compile("""lstImages.push\("(.+?)"""") - val m = p.matcher(response.body().string()) - - var i = 0 - while (m.find()) { - pages.add(Page(i++, "", m.group(1))) - } - } - - // Not used - override fun pageListParse(document: Document, pages: MutableList) {} - - override fun imageUrlRequest(page: Page) = GET(page.url) - - override fun imageUrlParse(document: Document) = "" - -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.kt deleted file mode 100644 index 17d9f1045..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.kt +++ /dev/null @@ -1,121 +0,0 @@ -package eu.kanade.tachiyomi.data.source.online.english - -import android.content.Context -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.source.EN -import eu.kanade.tachiyomi.data.source.Language -import eu.kanade.tachiyomi.data.source.model.Page -import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource -import eu.kanade.tachiyomi.util.asJsoup -import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.* - -class Mangafox(context: Context, override val id: Int) : ParsedOnlineSource(context) { - - override val name = "Mangafox" - - override val baseUrl = "http://mangafox.me" - - override val lang: Language get() = EN - - override fun popularMangaInitialUrl() = "$baseUrl/directory/" - - override fun popularMangaSelector() = "div#mangalist > ul.list > li" - - override fun popularMangaFromElement(element: Element, manga: Manga) { - element.select("a.title").first().let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.text() - } - } - - override fun popularMangaNextPageSelector() = "a:has(span.next)" - - override fun searchMangaInitialUrl(query: String) = - "$baseUrl/search.php?name_method=cw&advopts=1&order=za&sort=views&name=$query&page=1" - - override fun searchMangaSelector() = "table#listing > tbody > tr:gt(0)" - - override fun searchMangaFromElement(element: Element, manga: Manga) { - element.select("a.series_preview").first().let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.text() - } - } - - override fun searchMangaNextPageSelector() = "a:has(span.next)" - - override fun mangaDetailsParse(document: Document, manga: Manga) { - val infoElement = document.select("div#title").first() - val rowElement = infoElement.select("table > tbody > tr:eq(1)").first() - val sideInfoElement = document.select("#series_info").first() - - manga.author = rowElement.select("td:eq(1)").first()?.text() - manga.artist = rowElement.select("td:eq(2)").first()?.text() - manga.genre = rowElement.select("td:eq(3)").first()?.text() - manga.description = infoElement.select("p.summary").first()?.text() - manga.status = sideInfoElement.select(".data").first()?.text().orEmpty().let { parseStatus(it) } - manga.thumbnail_url = sideInfoElement.select("div.cover > img").first()?.attr("src") - } - - private fun parseStatus(status: String) = when { - status.contains("Ongoing") -> Manga.ONGOING - status.contains("Completed") -> Manga.COMPLETED - else -> Manga.UNKNOWN - } - - override fun chapterListSelector() = "div#chapters li div" - - override fun chapterFromElement(element: Element, chapter: Chapter) { - val urlElement = element.select("a.tips").first() - - chapter.setUrlWithoutDomain(urlElement.attr("href")) - chapter.name = urlElement.text() - chapter.date_upload = element.select("span.date").first()?.text()?.let { parseChapterDate(it) } ?: 0 - } - - private fun parseChapterDate(date: String): Long { - return if ("Today" in date || " ago" in date) { - Calendar.getInstance().apply { - set(Calendar.HOUR_OF_DAY, 0) - set(Calendar.MINUTE, 0) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - }.timeInMillis - } else if ("Yesterday" in date) { - Calendar.getInstance().apply { - add(Calendar.DATE, -1) - set(Calendar.HOUR_OF_DAY, 0) - set(Calendar.MINUTE, 0) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - }.timeInMillis - } else { - try { - SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(date).time - } catch (e: ParseException) { - 0L - } - } - } - - override fun pageListParse(response: Response, pages: MutableList) { - val document = response.asJsoup() - - val url = response.request().url().toString().substringBeforeLast('/') - document.select("select.m").first().select("option:not([value=0])").forEach { - pages.add(Page(pages.size, "$url/${it.attr("value")}.html")) - } - } - - // Not used, overrides parent. - override fun pageListParse(document: Document, pages: MutableList) {} - - override fun imageUrlParse(document: Document) = document.getElementById("image").attr("src") - -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.kt deleted file mode 100644 index dd63a5d13..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.kt +++ /dev/null @@ -1,113 +0,0 @@ -package eu.kanade.tachiyomi.data.source.online.english - -import android.content.Context -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.source.EN -import eu.kanade.tachiyomi.data.source.Language -import eu.kanade.tachiyomi.data.source.model.Page -import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.* - -class Mangahere(context: Context, override val id: Int) : ParsedOnlineSource(context) { - - override val name = "Mangahere" - - override val baseUrl = "http://www.mangahere.co" - - override val lang: Language get() = EN - - override fun popularMangaInitialUrl() = "$baseUrl/directory/" - - override fun popularMangaSelector() = "div.directory_list > ul > li" - - override fun popularMangaFromElement(element: Element, manga: Manga) { - element.select("div.title > a").first().let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.text() - } - } - - override fun popularMangaNextPageSelector() = "div.next-page > a.next" - - override fun searchMangaInitialUrl(query: String) = - "$baseUrl/search.php?name=$query&page=1&sort=views&order=za" - - override fun searchMangaSelector() = "div.result_search > dl:has(dt)" - - override fun searchMangaFromElement(element: Element, manga: Manga) { - element.select("a.manga_info").first().let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.text() - } - } - - override fun searchMangaNextPageSelector() = "div.next-page > a.next" - - override fun mangaDetailsParse(document: Document, manga: Manga) { - val detailElement = document.select(".manga_detail_top").first() - val infoElement = detailElement.select(".detail_topText").first() - - manga.author = infoElement.select("a[href^=http://www.mangahere.co/author/]").first()?.text() - manga.artist = infoElement.select("a[href^=http://www.mangahere.co/artist/]").first()?.text() - manga.genre = infoElement.select("li:eq(3)").first()?.text()?.substringAfter("Genre(s):") - manga.description = infoElement.select("#show").first()?.text()?.substringBeforeLast("Show less") - manga.status = infoElement.select("li:eq(6)").first()?.text().orEmpty().let { parseStatus(it) } - manga.thumbnail_url = detailElement.select("img.img").first()?.attr("src") - } - - private fun parseStatus(status: String) = when { - status.contains("Ongoing") -> Manga.ONGOING - status.contains("Completed") -> Manga.COMPLETED - else -> Manga.UNKNOWN - } - - override fun chapterListSelector() = ".detail_list > ul:not([class]) > li" - - override fun chapterFromElement(element: Element, chapter: Chapter) { - val urlElement = element.select("a").first() - - chapter.setUrlWithoutDomain(urlElement.attr("href")) - chapter.name = urlElement.text() - chapter.date_upload = element.select("span.right").first()?.text()?.let { parseChapterDate(it) } ?: 0 - } - - private fun parseChapterDate(date: String): Long { - return if ("Today" in date) { - Calendar.getInstance().apply { - set(Calendar.HOUR_OF_DAY, 0) - set(Calendar.MINUTE, 0) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - }.timeInMillis - } else if ("Yesterday" in date) { - Calendar.getInstance().apply { - add(Calendar.DATE, -1) - set(Calendar.HOUR_OF_DAY, 0) - set(Calendar.MINUTE, 0) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - }.timeInMillis - } else { - try { - SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(date).time - } catch (e: ParseException) { - 0L - } - } - } - - override fun pageListParse(document: Document, pages: MutableList) { - document.select("select.wid60").first()?.getElementsByTag("option")?.forEach { - pages.add(Page(pages.size, it.attr("value"))) - } - pages.getOrNull(0)?.imageUrl = imageUrlParse(document) - } - - override fun imageUrlParse(document: Document) = document.getElementById("image").attr("src") - -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangasee.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangasee.kt deleted file mode 100644 index 697f1b165..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangasee.kt +++ /dev/null @@ -1,125 +0,0 @@ -package eu.kanade.tachiyomi.data.source.online.english - -import android.content.Context -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.source.EN -import eu.kanade.tachiyomi.data.source.Language -import eu.kanade.tachiyomi.data.source.model.Page -import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource -import eu.kanade.tachiyomi.util.asJsoup -import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.util.* -import java.util.regex.Pattern - -class Mangasee(context: Context, override val id: Int) : ParsedOnlineSource(context) { - - override val name = "Mangasee" - - override val baseUrl = "http://www.mangasee.co" - - override val lang: Language get() = EN - - private val datePattern = Pattern.compile("(\\d+)\\s+(.*?)s? ago.*") - - private val dateFields = HashMap().apply { - put("second", Calendar.SECOND) - put("minute", Calendar.MINUTE) - put("hour", Calendar.HOUR) - put("day", Calendar.DATE) - put("week", Calendar.WEEK_OF_YEAR) - put("month", Calendar.MONTH) - put("year", Calendar.YEAR) - } - - override fun popularMangaInitialUrl() = "$baseUrl/search_result.php?Action=Yes&order=popularity&numResultPerPage=20&sort=desc" - - override fun popularMangaSelector() = "div.well > table > tbody > tr" - - override fun popularMangaFromElement(element: Element, manga: Manga) { - element.select("td > h2 > a").first().let { - manga.setUrlWithoutDomain("/${it.attr("href")}") - manga.title = it.text() - } - } - - override fun popularMangaNextPageSelector() = "ul.pagination > li > a:contains(Next)" - - override fun searchMangaInitialUrl(query: String) = - "$baseUrl/advanced-search/result.php?sortBy=alphabet&direction=ASC&textOnly=no&resPerPage=20&page=1&seriesName=$query" - - override fun searchMangaSelector() = "div.row > div > div > div > h1" - - override fun searchMangaFromElement(element: Element, manga: Manga) { - element.select("a").first().let { - manga.setUrlWithoutDomain("/${it.attr("href")}") - manga.title = it.text() - } - } - - override fun searchMangaNextPageSelector() = "ul.pagination > li > a:contains(Next)" - - override fun mangaDetailsParse(document: Document, manga: Manga) { - val detailElement = document.select("div.well > div.row").first() - - manga.author = detailElement.select("a[href^=../search_result.php?author_name=]").first()?.text() - manga.genre = detailElement.select("div > div.row > div:has(b:contains(Genre:)) > a").map { it.text() }.joinToString() - manga.description = detailElement.select("strong:contains(Description:) + div").first()?.text() - manga.status = detailElement.select("div > div.row > div:has(b:contains(Scanlation Status:))").first()?.text().orEmpty().let { parseStatus(it) } - manga.thumbnail_url = detailElement.select("div > img").first()?.absUrl("src") - } - - private fun parseStatus(status: String) = when { - status.contains("Ongoing") -> Manga.ONGOING - status.contains("Completed") -> Manga.COMPLETED - else -> Manga.UNKNOWN - } - - override fun chapterListSelector() = "div.row > div > div.row:has(a.chapter_link[alt])" - - override fun chapterFromElement(element: Element, chapter: Chapter) { - val urlElement = element.select("a").first() - - chapter.setUrlWithoutDomain("/${urlElement.attr("href")}") - chapter.name = urlElement.text() - chapter.date_upload = element.select("span").first()?.text()?.let { parseChapterDate(it) } ?: 0 - } - - private fun parseChapterDate(dateAsString: String): Long { - val m = datePattern.matcher(dateAsString) - - if (m.matches()) { - val amount = Integer.parseInt(m.group(1)) - val unit = m.group(2) - - return Calendar.getInstance().apply { - add(dateFields[unit]!!, -amount) - }.time.time - } else { - return 0 - } - } - - override fun pageListParse(response: Response, pages: MutableList) { - val document = response.asJsoup() - val url = response.request().url().toString().substringBeforeLast('/') - - val series = document.select("input[name=series]").first().attr("value") - val chapter = document.select("input[name=chapter]").first().attr("value") - val index = document.select("input[name=index]").first().attr("value") - - document.select("select[name=page] > option").forEach { - pages.add(Page(pages.size, "$url/?series=$series&chapter=$chapter&index=$index&page=${pages.size + 1}")) - } - pages.getOrNull(0)?.imageUrl = imageUrlParse(document) - } - - // Not used, overrides parent. - override fun pageListParse(document: Document, pages: MutableList) { - } - - override fun imageUrlParse(document: Document) = document.select("div > a > img").attr("src") - -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Readmangatoday.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Readmangatoday.kt deleted file mode 100644 index 7c89ada96..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Readmangatoday.kt +++ /dev/null @@ -1,130 +0,0 @@ -package eu.kanade.tachiyomi.data.source.online.english - -import android.content.Context -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.network.POST -import eu.kanade.tachiyomi.data.source.EN -import eu.kanade.tachiyomi.data.source.Language -import eu.kanade.tachiyomi.data.source.model.MangasPage -import eu.kanade.tachiyomi.data.source.model.Page -import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource -import okhttp3.OkHttpClient -import okhttp3.Request -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.util.* - -class Readmangatoday(context: Context, override val id: Int) : ParsedOnlineSource(context) { - - override val name = "ReadMangaToday" - - override val baseUrl = "http://www.readmanga.today" - - override val lang: Language get() = EN - - override val client: OkHttpClient get() = network.cloudflareClient - - override fun popularMangaInitialUrl() = "$baseUrl/hot-manga/" - - override fun popularMangaSelector() = "div.hot-manga > div.style-list > div.box" - - override fun popularMangaFromElement(element: Element, manga: Manga) { - element.select("div.title > h2 > a").first().let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.attr("title") - } - } - - override fun popularMangaNextPageSelector() = "div.hot-manga > ul.pagination > li > a:contains(»)" - - override fun searchMangaInitialUrl(query: String) = - "$baseUrl/search" - - - override fun searchMangaRequest(page: MangasPage, query: String): Request { - if (page.page == 1) { - page.url = searchMangaInitialUrl(query) - } - - var builder = okhttp3.FormBody.Builder() - builder.add("query", query) - - return POST(page.url, headers, builder.build()) - } - - override fun searchMangaSelector() = "div.content-list > div.style-list > div.box" - - override fun searchMangaFromElement(element: Element, manga: Manga) { - element.select("div.title > h2 > a").first().let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.attr("title") - } - } - - override fun searchMangaNextPageSelector() = "div.next-page > a.next" - - override fun mangaDetailsParse(document: Document, manga: Manga) { - val detailElement = document.select("div.movie-meta").first() - - manga.author = document.select("ul.cast-list li.director > ul a").first()?.text() - manga.artist = document.select("ul.cast-list li:not(.director) > ul a").first()?.text() - manga.genre = detailElement.select("dl.dl-horizontal > dd:eq(5)").first()?.text() - manga.description = detailElement.select("li.movie-detail").first()?.text() - manga.status = detailElement.select("dl.dl-horizontal > dd:eq(3)").first()?.text().orEmpty().let { parseStatus(it) } - manga.thumbnail_url = detailElement.select("img.img-responsive").first()?.attr("src") - } - - private fun parseStatus(status: String) = when { - status.contains("Ongoing") -> Manga.ONGOING - status.contains("Completed") -> Manga.COMPLETED - else -> Manga.UNKNOWN - } - - override fun chapterListSelector() = "ul.chp_lst > li" - - override fun chapterFromElement(element: Element, chapter: Chapter) { - val urlElement = element.select("a").first() - - chapter.setUrlWithoutDomain(urlElement.attr("href")) - chapter.name = urlElement.select("span.val").text() - chapter.date_upload = element.select("span.dte").first()?.text()?.let { parseChapterDate(it) } ?: 0 - } - - private fun parseChapterDate(date: String): Long { - val dateWords : List = date.split(" ") - - if (dateWords.size == 3) { - val timeAgo = Integer.parseInt(dateWords[0]) - var date : Calendar = Calendar.getInstance() - - if (dateWords[1].contains("Minute")) { - date.add(Calendar.MINUTE, - timeAgo) - } else if (dateWords[1].contains("Hour")) { - date.add(Calendar.HOUR_OF_DAY, - timeAgo) - } else if (dateWords[1].contains("Day")) { - date.add(Calendar.DAY_OF_YEAR, -timeAgo) - } else if (dateWords[1].contains("Week")) { - date.add(Calendar.WEEK_OF_YEAR, -timeAgo) - } else if (dateWords[1].contains("Month")) { - date.add(Calendar.MONTH, -timeAgo) - } else if (dateWords[1].contains("Year")) { - date.add(Calendar.YEAR, -timeAgo) - } - - return date.getTimeInMillis() - } - - return 0L - } - - override fun pageListParse(document: Document, pages: MutableList) { - document.select("ul.list-switcher-2 > li > select.jump-menu").first().getElementsByTag("option").forEach { - pages.add(Page(pages.size, it.attr("value"))) - } - pages.getOrNull(0)?.imageUrl = imageUrlParse(document) - } - - override fun imageUrlParse(document: Document) = document.select("img.img-responsive-2").first().attr("src") - -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/german/WieManga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/german/WieManga.kt deleted file mode 100644 index d58e5af55..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/german/WieManga.kt +++ /dev/null @@ -1,97 +0,0 @@ -package eu.kanade.tachiyomi.data.source.online.german - -import android.content.Context -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.source.DE -import eu.kanade.tachiyomi.data.source.Language -import eu.kanade.tachiyomi.data.source.model.Page -import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource -import eu.kanade.tachiyomi.util.asJsoup -import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.text.SimpleDateFormat - -class WieManga(context: Context, override val id: Int) : ParsedOnlineSource(context) { - - override val name = "Wie Manga!" - - override val baseUrl = "http://www.wiemanga.com" - - override val lang: Language get() = DE - - override fun popularMangaInitialUrl() = "$baseUrl/list/Hot-Book/" - - override fun popularMangaSelector() = ".booklist td > div" - - override fun popularMangaFromElement(element: Element, manga: Manga) { - val image = element.select("dt img") - val title = element.select("dd a:first-child") - - manga.setUrlWithoutDomain(title.attr("href")) - manga.title = title.text() - manga.thumbnail_url = image.attr("src") - } - - override fun popularMangaNextPageSelector() = null - - override fun searchMangaInitialUrl(query: String) = "$baseUrl/search/?wd=$query" - - override fun searchMangaSelector() = ".searchresult td > div" - - override fun searchMangaFromElement(element: Element, manga: Manga) { - val image = element.select(".resultimg img") - val title = element.select(".resultbookname") - - manga.setUrlWithoutDomain(title.attr("href")) - manga.title = title.text() - manga.thumbnail_url = image.attr("src") - } - - override fun searchMangaNextPageSelector() = ".pagetor a.l" - - override fun mangaDetailsParse(document: Document, manga: Manga) { - val imageElement = document.select(".bookmessgae tr > td:nth-child(1)").first() - val infoElement = document.select(".bookmessgae tr > td:nth-child(2)").first() - - manga.author = infoElement.select("dd:nth-of-type(2) a").first()?.text() - manga.artist = infoElement.select("dd:nth-of-type(3) a").first()?.text() - manga.description = infoElement.select("dl > dt:last-child").first()?.text()?.replaceFirst("Beschreibung", "") - manga.thumbnail_url = imageElement.select("img").first()?.attr("src") - - if (manga.author == "RSS") - manga.author = null - - if (manga.artist == "RSS") - manga.artist = null - } - - override fun chapterListSelector() = ".chapterlist tr:not(:first-child)" - - override fun chapterFromElement(element: Element, chapter: Chapter) { - val urlElement = element.select(".col1 a").first() - val dateElement = element.select(".col3 a").first() - - chapter.setUrlWithoutDomain(urlElement.attr("href")) - chapter.name = urlElement.text() - chapter.date_upload = dateElement?.text()?.let { parseChapterDate(it) } ?: 0 - } - - private fun parseChapterDate(date: String): Long { - return SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(date).time - } - - override fun pageListParse(response: Response, pages: MutableList) { - val document = response.asJsoup() - - document.select("select#page").first().select("option").forEach { - pages.add(Page(pages.size, it.attr("value"))) - } - } - - override fun pageListParse(document: Document, pages: MutableList) {} - - override fun imageUrlParse(document: Document) = document.select("img#comicpic").first().attr("src") - -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mangachan.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mangachan.kt deleted file mode 100644 index b93cc0867..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mangachan.kt +++ /dev/null @@ -1,94 +0,0 @@ -package eu.kanade.tachiyomi.data.source.online.russian - -import android.content.Context -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.source.Language -import eu.kanade.tachiyomi.data.source.RU -import eu.kanade.tachiyomi.data.source.model.Page -import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource -import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.text.SimpleDateFormat -import java.util.* - -class Mangachan(context: Context, override val id: Int) : ParsedOnlineSource(context) { - - override val name = "Mangachan" - - override val baseUrl = "http://mangachan.me" - - override val lang: Language get() = RU - - override fun popularMangaInitialUrl() = "$baseUrl/mostfavorites" - - override fun searchMangaInitialUrl(query: String) = "$baseUrl/?do=search&subaction=search&story=$query" - - override fun popularMangaSelector() = "div.content_row" - - override fun popularMangaFromElement(element: Element, manga: Manga) { - element.select("h2 > a").first().let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.text() - } - } - - override fun popularMangaNextPageSelector() = "a:contains(Вперед)" - - override fun searchMangaSelector() = popularMangaSelector() - - override fun searchMangaFromElement(element: Element, manga: Manga) { - popularMangaFromElement(element, manga) - } - - override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() - - override fun mangaDetailsParse(document: Document, manga: Manga) { - val infoElement = document.select("table.mangatitle").first() - val descElement = document.select("div#description").first() - val imgElement = document.select("img#cover").first() - - manga.author = infoElement.select("tr:eq(2) > td:eq(1)").text() - manga.genre = infoElement.select("tr:eq(5) > td:eq(1)").text() - manga.status = parseStatus(infoElement.select("tr:eq(4) > td:eq(1)").text()) - manga.description = descElement.textNodes().first().text() - manga.thumbnail_url = baseUrl + imgElement.attr("src") - } - - private fun parseStatus(element: String): Int { - when { - element.contains("перевод завершен") -> return Manga.COMPLETED - element.contains("перевод продолжается") -> return Manga.ONGOING - else -> return Manga.UNKNOWN - } - } - - override fun chapterListSelector() = "table.table_cha tr:gt(1)" - - override fun chapterFromElement(element: Element, chapter: Chapter) { - val urlElement = element.select("a").first() - - chapter.setUrlWithoutDomain(urlElement.attr("href")) - chapter.name = urlElement.text() - chapter.date_upload = element.select("div.date").first()?.text()?.let { - SimpleDateFormat("yyyy-MM-dd", Locale.US).parse(it).time - } ?: 0 - } - - override fun pageListParse(response: Response, pages: MutableList) { - val html = response.body().string() - val beginIndex = html.indexOf("fullimg\":[") + 10 - val endIndex = html.indexOf(",]", beginIndex) - val trimmedHtml = html.substring(beginIndex, endIndex).replace("\"", "") - val pageUrls = trimmedHtml.split(',') - - for ((i, url) in pageUrls.withIndex()) { - pages.add(Page(i, "", url)) - } - } - - override fun pageListParse(document: Document, pages: MutableList) { } - - override fun imageUrlParse(document: Document) = "" -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mintmanga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mintmanga.kt deleted file mode 100644 index 25abf0082..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mintmanga.kt +++ /dev/null @@ -1,102 +0,0 @@ -package eu.kanade.tachiyomi.data.source.online.russian - -import android.content.Context -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.source.Language -import eu.kanade.tachiyomi.data.source.RU -import eu.kanade.tachiyomi.data.source.model.Page -import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource -import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.text.SimpleDateFormat -import java.util.* -import java.util.regex.Pattern - -class Mintmanga(context: Context, override val id: Int) : ParsedOnlineSource(context) { - - override val name = "Mintmanga" - - override val baseUrl = "http://mintmanga.com" - - override val lang: Language get() = RU - - override fun popularMangaInitialUrl() = "$baseUrl/list?sortType=rate" - - override fun searchMangaInitialUrl(query: String) = "$baseUrl/search?q=$query" - - override fun popularMangaSelector() = "div.desc" - - override fun popularMangaFromElement(element: Element, manga: Manga) { - element.select("h3 > a").first().let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.attr("title") - } - } - - override fun popularMangaNextPageSelector() = "a.nextLink" - - override fun searchMangaSelector() = popularMangaSelector() - - override fun searchMangaFromElement(element: Element, manga: Manga) { - popularMangaFromElement(element, manga) - } - - override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() - - override fun mangaDetailsParse(document: Document, manga: Manga) { - val infoElement = document.select("div.leftContent").first() - - manga.author = infoElement.select("span.elem_author").first()?.text() - manga.genre = infoElement.select("span.elem_genre").text().replace(" ,", ",") - manga.description = infoElement.select("div.manga-description").text() - manga.status = parseStatus(infoElement.html()) - manga.thumbnail_url = infoElement.select("img").attr("data-full") - } - - private fun parseStatus(element: String): Int { - when { - element.contains("

Запрещена публикация произведения по копирайту

") -> return Manga.LICENSED - element.contains("

Сингл") || element.contains("Перевод: завершен") -> return Manga.COMPLETED - element.contains("Перевод: продолжается") -> return Manga.ONGOING - else -> return Manga.UNKNOWN - } - } - - override fun chapterListSelector() = "div.chapters-link tbody tr" - - override fun chapterFromElement(element: Element, chapter: Chapter) { - val urlElement = element.select("a").first() - - chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mature=1") - chapter.name = urlElement.text().replace(" новое", "") - chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let { - SimpleDateFormat("dd/MM/yy", Locale.US).parse(it).time - } ?: 0 - } - - override fun parseChapterNumber(chapter: Chapter) { - chapter.chapter_number = -2f - } - - override fun pageListParse(response: Response, pages: MutableList) { - val html = response.body().string() - val beginIndex = html.indexOf("rm_h.init( [") - val endIndex = html.indexOf("], 0, false);", beginIndex) - val trimmedHtml = html.substring(beginIndex, endIndex) - - val p = Pattern.compile("'.+?','.+?',\".+?\"") - val m = p.matcher(trimmedHtml) - - var i = 0 - while (m.find()) { - val urlParts = m.group().replace("[\"\']+".toRegex(), "").split(',') - pages.add(Page(i++, "", urlParts[1] + urlParts[0] + urlParts[2])) - } - } - - override fun pageListParse(document: Document, pages: MutableList) { } - - override fun imageUrlParse(document: Document) = "" -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Readmanga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Readmanga.kt deleted file mode 100644 index 7bb6cc50f..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Readmanga.kt +++ /dev/null @@ -1,102 +0,0 @@ -package eu.kanade.tachiyomi.data.source.online.russian - -import android.content.Context -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.source.Language -import eu.kanade.tachiyomi.data.source.RU -import eu.kanade.tachiyomi.data.source.model.Page -import eu.kanade.tachiyomi.data.source.online.ParsedOnlineSource -import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.text.SimpleDateFormat -import java.util.* -import java.util.regex.Pattern - -class Readmanga(context: Context, override val id: Int) : ParsedOnlineSource(context) { - - override val name = "Readmanga" - - override val baseUrl = "http://readmanga.me" - - override val lang: Language get() = RU - - override fun popularMangaInitialUrl() = "$baseUrl/list?sortType=rate" - - override fun searchMangaInitialUrl(query: String) = "$baseUrl/search?q=$query" - - override fun popularMangaSelector() = "div.desc" - - override fun popularMangaFromElement(element: Element, manga: Manga) { - element.select("h3 > a").first().let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.attr("title") - } - } - - override fun popularMangaNextPageSelector() = "a.nextLink" - - override fun searchMangaSelector() = popularMangaSelector() - - override fun searchMangaFromElement(element: Element, manga: Manga) { - popularMangaFromElement(element, manga) - } - - override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() - - override fun mangaDetailsParse(document: Document, manga: Manga) { - val infoElement = document.select("div.leftContent").first() - - manga.author = infoElement.select("span.elem_author").first()?.text() - manga.genre = infoElement.select("span.elem_genre").text().replace(" ,", ",") - manga.description = infoElement.select("div.manga-description").text() - manga.status = parseStatus(infoElement.html()) - manga.thumbnail_url = infoElement.select("img").attr("data-full") - } - - private fun parseStatus(element: String): Int { - when { - element.contains("

Запрещена публикация произведения по копирайту

") -> return Manga.LICENSED - element.contains("

Сингл") || element.contains("Перевод: завершен") -> return Manga.COMPLETED - element.contains("Перевод: продолжается") -> return Manga.ONGOING - else -> return Manga.UNKNOWN - } - } - - override fun chapterListSelector() = "div.chapters-link tbody tr" - - override fun chapterFromElement(element: Element, chapter: Chapter) { - val urlElement = element.select("a").first() - - chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mature=1") - chapter.name = urlElement.text().replace(" новое", "") - chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let { - SimpleDateFormat("dd/MM/yy", Locale.US).parse(it).time - } ?: 0 - } - - override fun parseChapterNumber(chapter: Chapter) { - chapter.chapter_number = -2f - } - - override fun pageListParse(response: Response, pages: MutableList) { - val html = response.body().string() - val beginIndex = html.indexOf("rm_h.init( [") - val endIndex = html.indexOf("], 0, false);", beginIndex) - val trimmedHtml = html.substring(beginIndex, endIndex) - - val p = Pattern.compile("'.+?','.+?',\".+?\"") - val m = p.matcher(trimmedHtml) - - var i = 0 - while (m.find()) { - val urlParts = m.group().replace("[\"\']+".toRegex(), "").split(',') - pages.add(Page(i++, "", urlParts[1] + urlParts[0] + urlParts[2])) - } - } - - override fun pageListParse(document: Document, pages: MutableList) { } - - override fun imageUrlParse(document: Document) = "" -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListDialogFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListDialogFragment.kt deleted file mode 100644 index 9cccdcb8e..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListDialogFragment.kt +++ /dev/null @@ -1,124 +0,0 @@ -package eu.kanade.tachiyomi.ui.manga.myanimelist - -import android.app.Dialog -import android.os.Bundle -import android.support.v4.app.DialogFragment -import android.view.View -import com.afollestad.materialdialogs.MaterialDialog -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.MangaSync -import eu.kanade.tachiyomi.widget.SimpleTextWatcher -import kotlinx.android.synthetic.main.dialog_myanimelist_search.view.* -import rx.Subscription -import rx.android.schedulers.AndroidSchedulers -import rx.subjects.PublishSubject -import java.util.concurrent.TimeUnit - -class MyAnimeListDialogFragment : DialogFragment() { - - companion object { - - fun newInstance(): MyAnimeListDialogFragment { - return MyAnimeListDialogFragment() - } - } - - private lateinit var v: View - - lateinit var adapter: MyAnimeListSearchAdapter - private set - - lateinit var querySubject: PublishSubject - private set - - private var selectedItem: MangaSync? = null - - private var searchSubscription: Subscription? = null - - override fun onCreateDialog(savedState: Bundle?): Dialog { - val dialog = MaterialDialog.Builder(activity) - .customView(R.layout.dialog_myanimelist_search, false) - .positiveText(android.R.string.ok) - .negativeText(android.R.string.cancel) - .onPositive { dialog1, which -> onPositiveButtonClick() } - .build() - - onViewCreated(dialog.view, savedState) - - return dialog - } - - override fun onViewCreated(view: View, savedState: Bundle?) { - v = view - - // Create adapter - adapter = MyAnimeListSearchAdapter(activity) - view.myanimelist_search_results.adapter = adapter - - // Set listeners - view.myanimelist_search_results.setOnItemClickListener { parent, viewList, position, id -> - selectedItem = adapter.getItem(position) - } - - // Do an initial search based on the manga's title - if (savedState == null) { - val title = presenter.manga.title - view.myanimelist_search_field.append(title) - search(title) - } - - querySubject = PublishSubject.create() - - view.myanimelist_search_field.addTextChangedListener(object : SimpleTextWatcher() { - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - querySubject.onNext(s.toString()) - } - }) - } - - override fun onResume() { - super.onResume() - - // Listen to text changes - searchSubscription = querySubject.debounce(1, TimeUnit.SECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { search(it) } - } - - override fun onPause() { - searchSubscription?.unsubscribe() - super.onPause() - } - - private fun onPositiveButtonClick() { - presenter.registerManga(selectedItem) - } - - private fun search(query: String) { - if (!query.isNullOrEmpty()) { - v.myanimelist_search_results.visibility = View.GONE - v.progress.visibility = View.VISIBLE - presenter.searchManga(query) - } - } - - fun onSearchResults(results: List) { - selectedItem = null - v.progress.visibility = View.GONE - v.myanimelist_search_results.visibility = View.VISIBLE - adapter.setItems(results) - } - - fun onSearchResultsError() { - v.progress.visibility = View.GONE - v.myanimelist_search_results.visibility = View.VISIBLE - adapter.clear() - } - - val malFragment: MyAnimeListFragment - get() = parentFragment as MyAnimeListFragment - - val presenter: MyAnimeListPresenter - get() = malFragment.presenter - -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListFragment.kt deleted file mode 100644 index 170a8ffb9..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListFragment.kt +++ /dev/null @@ -1,177 +0,0 @@ -package eu.kanade.tachiyomi.ui.manga.myanimelist - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.NumberPicker -import com.afollestad.materialdialogs.MaterialDialog -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.MangaSync -import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment -import eu.kanade.tachiyomi.util.toast -import kotlinx.android.synthetic.main.card_myanimelist_personal.* -import kotlinx.android.synthetic.main.fragment_myanimelist.* -import nucleus.factory.RequiresPresenter -import java.text.DecimalFormat - -@RequiresPresenter(MyAnimeListPresenter::class) -class MyAnimeListFragment : BaseRxFragment() { - - companion object { - fun newInstance(): MyAnimeListFragment { - return MyAnimeListFragment() - } - } - - private var dialog: MyAnimeListDialogFragment? = null - - private val decimalFormat = DecimalFormat("#.##") - - private val SEARCH_FRAGMENT_TAG = "mal_search" - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_myanimelist, container, false) - } - - override fun onViewCreated(view: View, savedState: Bundle?) { - swipe_refresh.isEnabled = false - swipe_refresh.setOnRefreshListener { presenter.refresh() } - myanimelist_title_layout.setOnClickListener { onTitleClick() } - myanimelist_status_layout.setOnClickListener { onStatusClick() } - myanimelist_chapters_layout.setOnClickListener { onChaptersClick() } - myanimelist_score_layout.setOnClickListener { onScoreClick() } - } - - @Suppress("DEPRECATION") - fun setMangaSync(mangaSync: MangaSync?) { - swipe_refresh.isEnabled = mangaSync != null - mangaSync?.let { - myanimelist_title.setTextAppearance(context, R.style.TextAppearance_Regular_Body1_Secondary) - myanimelist_title.setAllCaps(false) - myanimelist_title.text = it.title - myanimelist_chapters.text = if (it.total_chapters > 0) - "${it.last_chapter_read}/${it.total_chapters}" else "${it.last_chapter_read}/-" - myanimelist_score.text = if (it.score == 0f) "-" else decimalFormat.format(it.score) - myanimelist_status.text = presenter.myAnimeList.getStatus(it.status) - } ?: run { - myanimelist_title.setTextAppearance(context, R.style.TextAppearance_Medium_Button) - myanimelist_title.setText(R.string.action_edit) - myanimelist_chapters.text = "" - myanimelist_score.text = "" - myanimelist_status.text = "" - } - - } - - fun onRefreshDone() { - swipe_refresh.isRefreshing = false - } - - fun onRefreshError(error: Throwable) { - swipe_refresh.isRefreshing = false - context.toast(error.message) - } - - fun setSearchResults(results: List) { - findSearchFragmentIfNeeded() - - dialog?.onSearchResults(results) - } - - fun setSearchResultsError(error: Throwable) { - findSearchFragmentIfNeeded() - context.toast(error.message) - - dialog?.onSearchResultsError() - } - - private fun findSearchFragmentIfNeeded() { - if (dialog == null) { - dialog = childFragmentManager.findFragmentByTag(SEARCH_FRAGMENT_TAG) as MyAnimeListDialogFragment - } - } - - fun onTitleClick() { - if (dialog == null) { - dialog = MyAnimeListDialogFragment.newInstance() - } - - presenter.restartSearch() - dialog?.show(childFragmentManager, SEARCH_FRAGMENT_TAG) - } - - fun onStatusClick() { - if (presenter.mangaSync == null) - return - - MaterialDialog.Builder(activity) - .title(R.string.status) - .items(presenter.getAllStatus()) - .itemsCallbackSingleChoice(presenter.getIndexFromStatus(), { dialog, view, i, charSequence -> - presenter.setStatus(i) - myanimelist_status.text = "..." - true - }) - .show() - } - - fun onChaptersClick() { - if (presenter.mangaSync == null) - return - - val dialog = MaterialDialog.Builder(activity) - .title(R.string.chapters) - .customView(R.layout.dialog_myanimelist_chapters, false) - .positiveText(android.R.string.ok) - .negativeText(android.R.string.cancel) - .onPositive { d, action -> - val view = d.customView - if (view != null) { - val np = view.findViewById(R.id.chapters_picker) as NumberPicker - np.clearFocus() - presenter.setLastChapterRead(np.value) - myanimelist_chapters.text = "..." - } - } - .show() - - val view = dialog.customView - if (view != null) { - val np = view.findViewById(R.id.chapters_picker) as NumberPicker - // Set initial value - np.value = presenter.mangaSync!!.last_chapter_read - // Don't allow to go from 0 to 9999 - np.wrapSelectorWheel = false - } - } - - fun onScoreClick() { - if (presenter.mangaSync == null) - return - - val dialog = MaterialDialog.Builder(activity) - .title(R.string.score) - .customView(R.layout.dialog_myanimelist_score, false) - .positiveText(android.R.string.ok) - .negativeText(android.R.string.cancel) - .onPositive { d, action -> - val view = d.customView - if (view != null) { - val np = view.findViewById(R.id.score_picker) as NumberPicker - np.clearFocus() - presenter.setScore(np.value) - myanimelist_score.text = "..." - } - } - .show() - - val view = dialog.customView - if (view != null) { - val np = view.findViewById(R.id.score_picker) as NumberPicker - // Set initial value - np.value = presenter.mangaSync!!.score.toInt() - } - } - -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListPresenter.kt deleted file mode 100644 index f9d26b33a..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListPresenter.kt +++ /dev/null @@ -1,174 +0,0 @@ -package eu.kanade.tachiyomi.ui.manga.myanimelist - -import android.os.Bundle -import com.pushtorefresh.storio.sqlite.operations.put.PutResult -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.DatabaseHelper -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.MangaSync -import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager -import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter -import eu.kanade.tachiyomi.ui.manga.MangaEvent -import eu.kanade.tachiyomi.util.SharedData -import eu.kanade.tachiyomi.util.toast -import rx.Observable -import rx.android.schedulers.AndroidSchedulers -import rx.schedulers.Schedulers -import timber.log.Timber -import uy.kohesive.injekt.injectLazy - -class MyAnimeListPresenter : BasePresenter() { - - val db: DatabaseHelper by injectLazy() - val syncManager: MangaSyncManager by injectLazy() - - val myAnimeList by lazy { syncManager.myAnimeList } - - lateinit var manga: Manga - private set - - var mangaSync: MangaSync? = null - private set - - private var query: String? = null - - private val GET_MANGA_SYNC = 1 - private val GET_SEARCH_RESULTS = 2 - private val REFRESH = 3 - - private val PREFIX_MY = "my:" - - override fun onCreate(savedState: Bundle?) { - super.onCreate(savedState) - - startableLatestCache(GET_MANGA_SYNC, - { db.getMangaSync(manga, myAnimeList).asRxObservable() - .doOnNext { mangaSync = it } - .observeOn(AndroidSchedulers.mainThread()) }, - { view, mangaSync -> view.setMangaSync(mangaSync) }) - - startableLatestCache(GET_SEARCH_RESULTS, - { getSearchResultsObservable() }, - { view, results -> view.setSearchResults(results) }, - { view, error -> view.setSearchResultsError(error) }) - - startableFirst(REFRESH, - { getRefreshObservable() }, - { view, result -> view.onRefreshDone() }, - { view, error -> view.onRefreshError(error) }) - - manga = SharedData.get(MangaEvent::class.java)?.manga ?: return - start(GET_MANGA_SYNC) - } - - fun getSearchResultsObservable(): Observable> { - return query?.let { query -> - val observable: Observable> - if (query.startsWith(PREFIX_MY)) { - val realQuery = query.substring(PREFIX_MY.length).toLowerCase().trim() - observable = myAnimeList.getList() - .flatMap { Observable.from(it) } - .filter { it.title.toLowerCase().contains(realQuery) } - .toList() - } else { - observable = myAnimeList.search(query) - } - observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) - } ?: Observable.error(Exception("Null query")) - - } - - fun getRefreshObservable(): Observable { - return mangaSync?.let { mangaSync -> - myAnimeList.getList() - .map { myList -> - myList.find { it.remote_id == mangaSync.remote_id }?.let { - mangaSync.copyPersonalFrom(it) - mangaSync.total_chapters = it.total_chapters - mangaSync - } ?: throw Exception("Could not find manga") - } - .flatMap { db.insertMangaSync(it).asRxObservable() } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - } ?: Observable.error(Exception("Not found")) - } - - private fun updateRemote() { - mangaSync?.let { mangaSync -> - add(myAnimeList.update(mangaSync) - .subscribeOn(Schedulers.io()) - .flatMap { db.insertMangaSync(mangaSync).asRxObservable() } - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ next -> }, - { error -> - Timber.e(error, error.message) - // Restart on error to set old values - start(GET_MANGA_SYNC) - })) - } - } - - fun searchManga(query: String) { - if (query.isNullOrEmpty() || query == this.query) - return - - this.query = query - start(GET_SEARCH_RESULTS) - } - - fun restartSearch() { - query = null - stop(GET_SEARCH_RESULTS) - } - - fun registerManga(sync: MangaSync?) { - if (sync != null) { - sync.manga_id = manga.id!! - add(myAnimeList.bind(sync) - .flatMap { db.insertMangaSync(sync).asRxObservable() } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ }, - { error -> context.toast(error.message) })) - } else { - db.deleteMangaSyncForManga(manga).executeAsBlocking() - } - } - - fun getAllStatus(): List { - return listOf(context.getString(R.string.reading), - context.getString(R.string.completed), - context.getString(R.string.on_hold), - context.getString(R.string.dropped), - context.getString(R.string.plan_to_read)) - } - - fun getIndexFromStatus(): Int { - return mangaSync?.let { mangaSync -> - if (mangaSync.status == 6) 4 else mangaSync.status - 1 - } ?: 0 - } - - fun setStatus(index: Int) { - mangaSync?.status = if (index == 4) 6 else index + 1 - updateRemote() - } - - fun setScore(score: Int) { - mangaSync?.score = score.toFloat() - updateRemote() - } - - fun setLastChapterRead(chapterNumber: Int) { - mangaSync?.last_chapter_read = chapterNumber - updateRemote() - } - - fun refresh() { - if (mangaSync != null) { - start(REFRESH) - } - } - -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListSearchAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListSearchAdapter.kt deleted file mode 100644 index 8ce5207d1..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListSearchAdapter.kt +++ /dev/null @@ -1,46 +0,0 @@ -package eu.kanade.tachiyomi.ui.manga.myanimelist - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import android.widget.ArrayAdapter -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.MangaSync -import eu.kanade.tachiyomi.util.inflate -import kotlinx.android.synthetic.main.dialog_myanimelist_search_item.view.* -import java.util.* - -class MyAnimeListSearchAdapter(context: Context) : - ArrayAdapter(context, R.layout.dialog_myanimelist_search_item, ArrayList()) { - - override fun getView(position: Int, view: View?, parent: ViewGroup): View { - var v = view - // Get the data item for this position - val sync = getItem(position) - // Check if an existing view is being reused, otherwise inflate the view - val holder: SearchViewHolder // view lookup cache stored in tag - if (v == null) { - v = parent.inflate(R.layout.dialog_myanimelist_search_item) - holder = SearchViewHolder(v) - v.tag = holder - } else { - holder = v.tag as SearchViewHolder - } - holder.onSetValues(sync) - return v - } - - fun setItems(syncs: List) { - setNotifyOnChange(false) - clear() - addAll(syncs) - notifyDataSetChanged() - } - - class SearchViewHolder(private val view: View) { - - fun onSetValues(sync: MangaSync) { - view.myanimelist_result_title.text = sync.title - } - } -}