Add new multi-src: zerotheme (#9544)
This commit is contained in:
		
							parent
							
								
									1980853506
								
							
						
					
					
						commit
						4050e42337
					
				
							
								
								
									
										5
									
								
								lib-multisrc/zerotheme/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								lib-multisrc/zerotheme/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| plugins { | ||||
|     id("lib-multisrc") | ||||
| } | ||||
| 
 | ||||
| baseVersionCode = 1 | ||||
| @ -0,0 +1,83 @@ | ||||
| package eu.kanade.tachiyomi.multisrc.zerotheme | ||||
| 
 | ||||
| import eu.kanade.tachiyomi.network.GET | ||||
| import eu.kanade.tachiyomi.network.interceptor.rateLimit | ||||
| 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.online.HttpSource | ||||
| import eu.kanade.tachiyomi.util.asJsoup | ||||
| import keiyoushi.utils.parseAs | ||||
| import okhttp3.HttpUrl.Companion.toHttpUrl | ||||
| import okhttp3.Request | ||||
| import okhttp3.Response | ||||
| 
 | ||||
| abstract class ZeroTheme( | ||||
|     override val name: String, | ||||
|     override val baseUrl: String, | ||||
|     override val lang: String, | ||||
| ) : HttpSource() { | ||||
| 
 | ||||
|     override val supportsLatest: Boolean = true | ||||
| 
 | ||||
|     override val client = network.cloudflareClient.newBuilder() | ||||
|         .rateLimit(2) | ||||
|         .build() | ||||
| 
 | ||||
|     open val cdnUrl: String = "https://cdn.${baseUrl.substringAfterLast("/")}" | ||||
| 
 | ||||
|     open val imageLocation: String = "images" | ||||
| 
 | ||||
|     private val sourceLocation: String get() = "$cdnUrl/$imageLocation" | ||||
| 
 | ||||
|     // =========================== Popular ================================ | ||||
| 
 | ||||
|     override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", FilterList()) | ||||
| 
 | ||||
|     override fun popularMangaParse(response: Response) = searchMangaParse(response) | ||||
| 
 | ||||
|     // =========================== Latest =================================== | ||||
| 
 | ||||
|     override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers) | ||||
| 
 | ||||
|     override fun latestUpdatesParse(response: Response): MangasPage = | ||||
|         MangasPage(response.toDto<LatestDto>().toSMangaList(sourceLocation), hasNextPage = false) | ||||
| 
 | ||||
|     // =========================== Search ================================= | ||||
| 
 | ||||
|     override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { | ||||
|         val url = "$baseUrl/api/search".toHttpUrl().newBuilder() | ||||
|             .addQueryParameter("q", query) | ||||
|             .addQueryParameter("page", page.toString()) | ||||
|             .build() | ||||
|         return GET(url, headers) | ||||
|     } | ||||
| 
 | ||||
|     override fun searchMangaParse(response: Response): MangasPage { | ||||
|         val dto = response.parseAs<SearchDto>() | ||||
|         val mangas = dto.mangas.map { it.toSManga(sourceLocation) } | ||||
|         return MangasPage(mangas, hasNextPage = dto.hasNextPage()) | ||||
|     } | ||||
| 
 | ||||
|     // =========================== Details ================================= | ||||
| 
 | ||||
|     override fun mangaDetailsParse(response: Response) = response.toDto<MangaDetailsDto>().toSManga(sourceLocation) | ||||
| 
 | ||||
|     // =========================== Chapter ================================= | ||||
| 
 | ||||
|     override fun chapterListParse(response: Response) = response.toDto<MangaDetailsDto>().toSChapterList() | ||||
| 
 | ||||
|     // =========================== Pages =================================== | ||||
| 
 | ||||
|     override fun pageListParse(response: Response): List<Page> = | ||||
|         response.toDto<PageDto>().toPageList(sourceLocation) | ||||
| 
 | ||||
|     override fun imageUrlParse(response: Response) = "" | ||||
| 
 | ||||
|     // =========================== Utilities =============================== | ||||
| 
 | ||||
|     inline fun <reified T> Response.toDto(): T { | ||||
|         val jsonString = asJsoup().selectFirst("[data-page]")!!.attr("data-page") | ||||
|         return jsonString.parseAs<T>() | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,139 @@ | ||||
| package eu.kanade.tachiyomi.multisrc.zerotheme | ||||
| 
 | ||||
| import eu.kanade.tachiyomi.source.model.Page | ||||
| import eu.kanade.tachiyomi.source.model.SChapter | ||||
| import eu.kanade.tachiyomi.source.model.SManga | ||||
| import keiyoushi.utils.tryParse | ||||
| import kotlinx.serialization.SerialName | ||||
| import kotlinx.serialization.Serializable | ||||
| import kotlinx.serialization.json.JsonNames | ||||
| import org.jsoup.Jsoup | ||||
| import java.text.SimpleDateFormat | ||||
| import java.util.Locale | ||||
| 
 | ||||
| @Serializable | ||||
| class Props<T>( | ||||
|     @JsonNames("comic_infos", "chapter", "new_chapters") | ||||
|     val content: T, | ||||
| ) | ||||
| 
 | ||||
| @Serializable | ||||
| class LatestDto( | ||||
|     private val props: Props<List<Comic>>, | ||||
| ) { | ||||
|     fun toSMangaList(srcPath: String) = props.content.map { it.comic.toSManga(srcPath) } | ||||
| 
 | ||||
|     @Serializable | ||||
|     class Comic( | ||||
|         val comic: MangaDto, | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| @Serializable | ||||
| class MangaDetailsDto( | ||||
|     private val props: Props<MangaDto>, | ||||
| ) { | ||||
|     fun toSManga(srcPath: String) = props.content.toSManga(srcPath) | ||||
|     fun toSChapterList() = props.content.chapters!!.map { it.toSChapter() } | ||||
| } | ||||
| 
 | ||||
| @Serializable | ||||
| class PageDto( | ||||
|     val props: Props<ChapterWrapper>, | ||||
| ) { | ||||
|     fun toPageList(srcPath: String): List<Page> { | ||||
|         return props.content.chapter.pages | ||||
|             .filter { it.pathSegment.contains("xml").not() } | ||||
|             .mapIndexed { index, path -> | ||||
|                 Page(index, imageUrl = "$srcPath/${path.pathSegment}") | ||||
|             } | ||||
|     } | ||||
| 
 | ||||
|     @Serializable | ||||
|     class ChapterWrapper( | ||||
|         val chapter: Chapter, | ||||
|     ) | ||||
| 
 | ||||
|     @Serializable | ||||
|     class Chapter( | ||||
|         val pages: List<Image>, | ||||
|     ) | ||||
| 
 | ||||
|     @Serializable | ||||
|     class Image( | ||||
|         @SerialName("page_path") | ||||
|         val pathSegment: String, | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| @Serializable | ||||
| class SearchDto( | ||||
|     @SerialName("comics") | ||||
|     private val page: PageDto, | ||||
| ) { | ||||
| 
 | ||||
|     val mangas: List<MangaDto> get() = page.data | ||||
| 
 | ||||
|     fun hasNextPage() = page.currentPage < page.lastPage | ||||
| 
 | ||||
|     @Serializable | ||||
|     class PageDto( | ||||
|         val `data`: List<MangaDto>, | ||||
|         @SerialName("last_page") | ||||
|         val lastPage: Int = 0, | ||||
|         @SerialName("current_page") | ||||
|         val currentPage: Int = 0, | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| @Serializable | ||||
| class MangaDto( | ||||
|     val title: String, | ||||
|     val description: String?, | ||||
|     @SerialName("cover") | ||||
|     val thumbnailUrl: String?, | ||||
|     val slug: String, | ||||
|     val status: List<ValueDto>? = emptyList(), | ||||
|     val genres: List<ValueDto>? = emptyList(), | ||||
|     val chapters: List<ChapterDto>? = emptyList(), | ||||
| ) { | ||||
| 
 | ||||
|     fun toSManga(srcPath: String) = SManga.create().apply { | ||||
|         title = this@MangaDto.title | ||||
|         description = this@MangaDto.description?.let { Jsoup.parseBodyFragment(it).text() } | ||||
|         this.thumbnail_url = thumbnailUrl?.let { "$srcPath/$it" } | ||||
| 
 | ||||
|         status = when (this@MangaDto.status?.firstOrNull()?.name?.lowercase()) { | ||||
|             "em andamento" -> SManga.ONGOING | ||||
|             else -> SManga.UNKNOWN | ||||
|         } | ||||
|         genre = genres?.joinToString { it.name } | ||||
|         url = "/comic/$slug" | ||||
|     } | ||||
| 
 | ||||
|     @Serializable | ||||
|     class ValueDto( | ||||
|         val name: String, | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| @Serializable | ||||
| class ChapterDto( | ||||
|     @SerialName("chapter_number") | ||||
|     val number: Float, | ||||
|     @SerialName("chapter_path") | ||||
|     val path: String, | ||||
|     @SerialName("created_at") | ||||
|     val createdAt: String, | ||||
| ) { | ||||
|     fun toSChapter() = SChapter.create().apply { | ||||
|         name = number.toString() | ||||
|         chapter_number = number | ||||
|         date_upload = dateFormat.tryParse(createdAt) | ||||
|         url = "/chapter/$path" | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT) | ||||
|     } | ||||
| } | ||||
| @ -1,9 +1,9 @@ | ||||
| ext { | ||||
|     extName = 'Ego Toons' | ||||
|     extClass = '.EgoToons' | ||||
|     themePkg = 'yuyu' | ||||
|     themePkg = 'zerotheme' | ||||
|     baseUrl = 'https://egotoons.com' | ||||
|     overrideVersionCode = 1 | ||||
|     overrideVersionCode = 2 | ||||
|     isNsfw = true | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,18 +1,18 @@ | ||||
| package eu.kanade.tachiyomi.extension.pt.egotoons | ||||
| 
 | ||||
| import eu.kanade.tachiyomi.multisrc.yuyu.YuYu | ||||
| import eu.kanade.tachiyomi.multisrc.zerotheme.ZeroTheme | ||||
| import eu.kanade.tachiyomi.network.interceptor.rateLimit | ||||
| 
 | ||||
| class EgoToons : YuYu( | ||||
| class EgoToons : ZeroTheme( | ||||
|     "Ego Toons", | ||||
|     "https://egotoons.com", | ||||
|     "pt-BR", | ||||
| ) { | ||||
| 
 | ||||
|     override fun headersBuilder() = super.headersBuilder() | ||||
|         .set("Accept-Encoding", "") | ||||
|     override val versionId = 2 | ||||
| 
 | ||||
|     override val client = super.client.newBuilder() | ||||
|         .rateLimit(2) | ||||
|         .build() | ||||
| 
 | ||||
|     override val imageLocation = "image-db" | ||||
| } | ||||
|  | ||||
							
								
								
									
										10
									
								
								src/pt/lertoons/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/pt/lertoons/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| ext { | ||||
|     extName = 'Ler Toons' | ||||
|     extClass = '.LerToons' | ||||
|     themePkg = 'zerotheme' | ||||
|     baseUrl = 'https://lertoons.com' | ||||
|     overrideVersionCode = 0 | ||||
|     isNsfw = true | ||||
| } | ||||
| 
 | ||||
| apply from: "$rootDir/common.gradle" | ||||
							
								
								
									
										
											BIN
										
									
								
								src/pt/lertoons/res/mipmap-hdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/pt/lertoons/res/mipmap-hdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pt/lertoons/res/mipmap-mdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/pt/lertoons/res/mipmap-mdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pt/lertoons/res/mipmap-xhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/pt/lertoons/res/mipmap-xhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pt/lertoons/res/mipmap-xxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/pt/lertoons/res/mipmap-xxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 6.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/pt/lertoons/res/mipmap-xxxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/pt/lertoons/res/mipmap-xxxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 8.3 KiB | 
| @ -0,0 +1,16 @@ | ||||
| package eu.kanade.tachiyomi.extension.pt.lertoons | ||||
| 
 | ||||
| import eu.kanade.tachiyomi.multisrc.zerotheme.ZeroTheme | ||||
| import eu.kanade.tachiyomi.network.interceptor.rateLimit | ||||
| 
 | ||||
| class LerToons : ZeroTheme( | ||||
|     "Ler Toons", | ||||
|     "https://lertoons.com", | ||||
|     "pt-BR", | ||||
| ) { | ||||
|     override val versionId = 3 | ||||
| 
 | ||||
|     override val client = network.cloudflareClient.newBuilder() | ||||
|         .rateLimit(2) | ||||
|         .build() | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Chopper
						Chopper