diff --git a/src/all/commitstrip/AndroidManifest.xml b/src/all/commitstrip/AndroidManifest.xml
new file mode 100644
index 000000000..30deb7f79
--- /dev/null
+++ b/src/all/commitstrip/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/src/all/commitstrip/build.gradle b/src/all/commitstrip/build.gradle
new file mode 100644
index 000000000..edf02ce39
--- /dev/null
+++ b/src/all/commitstrip/build.gradle
@@ -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"
diff --git a/src/all/commitstrip/res/mipmap-anydpi-v26/ic_launcher.xml b/src/all/commitstrip/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..036d09bc5
--- /dev/null
+++ b/src/all/commitstrip/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/all/commitstrip/res/mipmap-anydpi-v26/ic_launcher_round.xml b/src/all/commitstrip/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..036d09bc5
--- /dev/null
+++ b/src/all/commitstrip/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/all/commitstrip/res/mipmap-hdpi/ic_launcher.png b/src/all/commitstrip/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..50c30a115
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/all/commitstrip/res/mipmap-hdpi/ic_launcher_foreground.png b/src/all/commitstrip/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..04a9df908
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/src/all/commitstrip/res/mipmap-hdpi/ic_launcher_round.png b/src/all/commitstrip/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..36f58997a
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/src/all/commitstrip/res/mipmap-mdpi/ic_launcher.png b/src/all/commitstrip/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..9f687b68d
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/all/commitstrip/res/mipmap-mdpi/ic_launcher_foreground.png b/src/all/commitstrip/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..69e3d06e0
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/src/all/commitstrip/res/mipmap-mdpi/ic_launcher_round.png b/src/all/commitstrip/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..83ddf95dc
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/src/all/commitstrip/res/mipmap-xhdpi/ic_launcher.png b/src/all/commitstrip/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..e62422674
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/all/commitstrip/res/mipmap-xhdpi/ic_launcher_foreground.png b/src/all/commitstrip/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..08491121d
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/src/all/commitstrip/res/mipmap-xhdpi/ic_launcher_round.png b/src/all/commitstrip/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..83cbee17c
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/src/all/commitstrip/res/mipmap-xxhdpi/ic_launcher.png b/src/all/commitstrip/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..194804739
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/all/commitstrip/res/mipmap-xxhdpi/ic_launcher_foreground.png b/src/all/commitstrip/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..53a952b41
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/src/all/commitstrip/res/mipmap-xxhdpi/ic_launcher_round.png b/src/all/commitstrip/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..2274067a5
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/src/all/commitstrip/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/commitstrip/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..90fa1a557
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/all/commitstrip/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/src/all/commitstrip/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..b881c366d
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/src/all/commitstrip/res/mipmap-xxxhdpi/ic_launcher_round.png b/src/all/commitstrip/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..c1ad23122
Binary files /dev/null and b/src/all/commitstrip/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/src/all/commitstrip/res/values/ic_launcher_background.xml b/src/all/commitstrip/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000..c5d5899fd
--- /dev/null
+++ b/src/all/commitstrip/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #FFFFFF
+
\ No newline at end of file
diff --git a/src/all/commitstrip/src/eu/kanade/tachiyomi/extension/all/commitstrip/CommitStrip.kt b/src/all/commitstrip/src/eu/kanade/tachiyomi/extension/all/commitstrip/CommitStrip.kt
new file mode 100644
index 000000000..18421b52d
--- /dev/null
+++ b/src/all/commitstrip/src/eu/kanade/tachiyomi/extension/all/commitstrip/CommitStrip.kt
@@ -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 {
+ // 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 =
+ 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> {
+ // 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 {
+ 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> {
+ 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 = 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]
+ }
+ }
+}
diff --git a/src/all/commitstrip/src/eu/kanade/tachiyomi/extension/all/commitstrip/CommitStripFactory.kt b/src/all/commitstrip/src/eu/kanade/tachiyomi/extension/all/commitstrip/CommitStripFactory.kt
new file mode 100644
index 000000000..332f0e198
--- /dev/null
+++ b/src/all/commitstrip/src/eu/kanade/tachiyomi/extension/all/commitstrip/CommitStripFactory.kt
@@ -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