Add extension CommitStrip (#8878)

* the journey to a commitstrip ext begins

copies over skeleton code from reallifecomics and renames files and
folders to match new extension

also creates a sourcefactory since the site has both en and fr variants

* generate SManga entries

since the site is able to show comics by year, we'll create one SManga
entry for every year.

Also aware of siteLang since it has brings some changes

Archive years start from 2012 to current year

* update build.gradle with commitstrip details

uses CommitStripFactory

* add chapter list selector and parse it

comic entries don't really have a numbering in the website except for
their dates so we'll just keep our own numbering

needs to be reversed because the website shows the latest one first

need to add paging

* use better logo url

* parse chapter from Element

gets the url, regexes the url to find a date, and sets a name using a
selector on the chapter element

still didnt attempt paging yet btw

* use correct selector for chapter pages images

* fix package info

actually forgot abt the things at top
also made `siteLang` private because android studio recommended that

* actually use the correct logo link for thumbnail...

* use `lang` instead of `siteLang` at some places

`siteLang` is used when creating URLs and for extension stuff like
creating SManga object, it's better to use `lang` I feel.
In the end, it doesn't matter in this case because both are the same for
this extension.

* remote `private val` as per speculation

constructor parameter is never used as a property
and so `val` can be removed
this avoids more memory use due to unnecessary use
 - android studio

* Add ext app icon

The entire image assets placed in `res` folder. Made possible by their
actual logo (small version), which for some reason refused to work as
`SManga.thumbnail_url`

* linting

* adjust `baseUrl` usages

rather than having `siteLang` inside the `baseUrl`, better to have it
explicitly used everytime.

also fixes `thumbnail_url` because the link to the logo is broken when
you include `siteLang` in `baseUrl`

* create `manga.url` explicitly

since fetchChapterList will be overridden and its easier to have a full
url to make a client.newCall rather than setUrlWithoutDomain

* `thumbnail_url` doesn't need `siteLang`

and I forgot abt that

* add pagination for fetchChapterList

tried to reuse whatever I already had to support pages

might be slow because I do one extra request for Page 1 and some manga
have too many pages (like 11) each with 20 comics

* rewrite how pages are obtained

since I changed `manga.url`, that changes `chapter.url` and so that
implied a change to the page list as well.

* maybe optimize chapterList a bit

still a bit slow because some of them have a lot of pages but wanted to
reduce an extra request just to get total no of pages.

* just use your own links for the logo

can't get the logo to load from the link even though it worked. Might as
well get your own links to the logo. While I was searching, also found a
cool logo in FR so I kept that.

Both logos taken from the webcomics' Kickstarter and Ulule fundraisers

* Revert "maybe optimize chapterList a bit"

This reverts commit 672db52460cf597f28a80f24de3a7659f4c7a939.
because that didn't work. at all..

* remove unused variable
This commit is contained in:
nicki 2021-08-30 17:33:45 +05:30 committed by GitHub
parent e6e438e4bf
commit e7369b1b38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 227 additions and 0 deletions

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />

View File

@ -0,0 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'Commit Strip'
pkgNameSuffix = 'all.commitstrip'
extClass = '.CommitStripFactory'
extVersionCode = 1
libVersion = '1.2'
}
apply from: "$rootDir/common.gradle"

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>

View File

@ -0,0 +1,185 @@
package eu.kanade.tachiyomi.extension.all.commitstrip
import eu.kanade.tachiyomi.network.GET
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.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
abstract class CommitStrip(
override val lang: String,
private val siteLang: String
) : ParsedHttpSource() {
override val name = "Commit Strip"
override val baseUrl = "https://www.commitstrip.com"
override val supportsLatest = false
// Helper
private fun createManga(year: Int): SManga = SManga.create().apply {
url = "$baseUrl/$siteLang/$year"
title = "$name ($year)"
thumbnail_url = when (lang) {
"en" -> LOGO_EN
"fr" -> LOGO_FR
else -> LOGO_EN
}
author = when (lang) {
"en" -> AUTHOR_EN
"fr" -> AUTHOR_FR
else -> AUTHOR_EN
}
artist = ARTIST
status = if (year != currentYear) SManga.COMPLETED else SManga.ONGOING
description = when (lang) {
"en" -> "$SUMMARY_EN $NOTE $year"
"fr" -> "$SUMMARY_FR $NOTE $year"
else -> "$SUMMARY_EN $NOTE $year"
}
}
// Popular
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
// have one manga entry for each year
return (currentYear downTo 2012)
.map { createManga(it) }
.let { Observable.just(MangasPage(it, false))!! }
}
// Search
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
fetchPopularManga(1).map { mangaList ->
mangaList.copy(mangaList.mangas.filter { it.title.contains(query) })
}
// Details
override fun fetchMangaDetails(manga: SManga) = Observable.just(
manga.apply {
initialized = true
}
)!!
// Chapters
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
// create a new call to parse the no of pages in the site
// example responseString - Page 1 of 11
val responseString = client.newCall(GET("${manga.url}", headers)).execute().run {
asJsoup().select(".wp-pagenavi .pages").first().text()
}
// use regex to get the last number (i.e. 11 above)
val pages = Regex("\\d+").findAll(responseString).last().value.toInt()
return (1..pages).map {
val response = chapterListRequest(manga, it)
chapterListParse(response)
}.let { Observable.just(it.flatten()) }
}
private fun chapterListRequest(manga: SManga, page: Int): Response =
client.newCall(GET("${manga.url}/page/$page", headers)).execute().run {
if (!isSuccessful) {
close()
throw Exception("HTTP error $code")
}
this
}
override fun chapterListParse(response: Response): List<SChapter> {
return super.chapterListParse(response).reversed().distinct().mapIndexed { index, chapter ->
chapter.apply { chapter_number = index.toFloat() }
}
}
override fun chapterListSelector() = ".excerpt a"
override fun chapterFromElement(element: Element) = SChapter.create().apply {
url = element.attr("href")
// get the chapter date from the url
val date = Regex("\\d{4}\\/\\d{2}\\/\\d{2}").find(url)?.value
val parsedDate = SimpleDateFormat("yyyy/MM/dd", Locale.US).parse(date)
date_upload = parsedDate?.time ?: 0L
name = element.select("span").text()
}
// Page
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
return client.newCall(GET("${chapter.url}", headers)).execute().run {
asJsoup().select(".entry-content p img").attr("src")
}.let {
Observable.just(listOf(Page(0, "", it)))
}
}
// Unsupported
override fun pageListParse(document: Document): List<Page> = throw Exception("Not Used")
override fun imageUrlParse(document: Document) = throw Exception("Not used")
override fun popularMangaSelector() = throw Exception("Not used")
override fun searchMangaFromElement(element: Element) = throw Exception("Not used")
override fun searchMangaNextPageSelector() = throw Exception("Not used")
override fun searchMangaSelector() = throw Exception("Not used")
override fun popularMangaRequest(page: Int) = throw Exception("Not used")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw Exception("Not used")
override fun popularMangaNextPageSelector() = throw Exception("Not used")
override fun popularMangaFromElement(element: Element) = throw Exception("Not used")
override fun mangaDetailsParse(document: Document) = throw Exception("Not used")
override fun latestUpdatesNextPageSelector() = throw Exception("Not used")
override fun latestUpdatesFromElement(element: Element) = throw Exception("Not used")
override fun latestUpdatesRequest(page: Int) = throw Exception("Not used")
override fun latestUpdatesSelector() = throw Exception("Not used")
companion object {
private const val LOGO_EN = "https://i.imgur.com/HODJlt9.jpg"
private const val LOGO_FR = "https://i.imgur.com/I7ps9zS.jpg"
private const val AUTHOR_EN = "Mark Nightingale"
private const val AUTHOR_FR = "Thomas Gx"
private const val ARTIST = "Etienne Issartial"
private const val SUMMARY_EN = "The blog relating the daily life of web agency developers."
private const val SUMMARY_FR = "Le blog qui raconte la vie des codeurs"
private const val NOTE = "\n\nNote: This entry includes all the chapters published in"
private val currentYear by lazy {
Calendar.getInstance()[Calendar.YEAR]
}
}
}

View File

@ -0,0 +1,14 @@
package eu.kanade.tachiyomi.extension.all.commitstrip
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
class CommitStripFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(
CommitStripEnglish(),
CommitStripFrench(),
)
}
class CommitStripEnglish() : CommitStrip("en", "en")
class CommitStripFrench() : CommitStrip("fr", "fr")