refactor: Simplify multisrc structure (#2942)

This commit is contained in:
Claudemirovsky 2024-03-04 07:29:16 -03:00 committed by GitHub
parent 0064c5658d
commit 42159cc40d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
418 changed files with 672 additions and 1078 deletions

View file

@ -1,64 +1,12 @@
#!/bin/bash
versionStr="extVersionCode ="
multisrcVersionStr="overrideVersionCode ="
versionStr="VersionCode ="
bumpedFiles=""
# cut -d "=" -f 2 -> string.split("=")[1]
# "extVersionCode = 6" -> ["extVersionCode ", " 6"] -> " 6" -> "6"
getValue() { cut -d "=" -f 2 | cut -d " " -f 2;}
getVersion() {
if [[ $1 =~ ^multisrc/ ]]; then
# We are going to be piped, so no file specified, just read from stdin.
grep -Po "$multisrcVersionStr \d+" | getValue
else
grep "$versionStr" "$1" | getValue
fi
}
# expected input: multisrc/overrides/<theme>/<override>/....
# if override is default, then it will bump all overrides.
bumpMultisrcVersion() {
local overridePath=$1
# Prevents bumping extensions multiple times.
# Ex: When a theme uses a extractor per default, but one extension(override)
# also uses another, so if both libs are modifyed, such extension will be
# bumped only once instead of two times.
if [[ $bumpedFiles =~( |^)$overridePath( |$) ]]; then
return 0
fi
bumpedFiles+="$overridePath "
# Bump all extensions from a multisrc that implements a lib by default
if [[ $overridePath =~ .*/default/.* ]]; then
local themeBase=$(echo $overridePath | cut -d/ -f-3)
for file in $(ls $themeBase | grep -v default); do
bumpMultisrcVersion $themeBase/$file/
done
else
local theme=$(echo $overridePath | cut -d/ -f3)
local themePath="multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/$theme"
local sourceName=$(echo $overridePath | cut -d/ -f4)
local generator=$(echo $themePath/*Generator.kt)
bumpedFiles+="$generator " # Needed to commit the changes
local sourceLine=$(grep "Lang(" $generator | grep -i $sourceName)
local oldVersion=$(echo $sourceLine | getVersion $generator)
local newVersionStr="$multisrcVersionStr $((oldVersion + 1))"
if [[ $sourceLine =~ .*$multisrcVersionStr.* ]]; then
# if the override already have a "overrideVersionCode" param at
# the generator, then just bump it
local newSourceLine=${sourceLine/$multisrcVersionStr $oldVersion/$newVersionStr}
else
# else, add overrideVersionCode param to its line on the generator
local newSourceLine=${sourceLine/)/, $newVersionStr)}
fi
echo -e "\nmultisrc $sourceName($theme): $oldVersion -> $((oldVersion + 1))\n"
sed -i "s@$sourceLine@$newSourceLine@g" $generator
fi
}
bumpVersion() {
@ -70,13 +18,24 @@ bumpVersion() {
sed -i "s/$versionStr $oldVersion/$versionStr $newVersion/" $file
}
findAndBump() {
for lib in $@; do
for file in $(grep -l -R ":lib:$lib" --include "build.gradle" --include "additional.gradle"); do
bumpLibMultisrcVersion() {
local themeName=$(echo $1 | grep -Eo "lib-multisrc/\w+" | cut -c 14-)
for file in $(grep -l -R "themePkg = '$themeName'" --include build.gradle src/); do
# prevent bumping the same extension multiple times
if [[ ! $bumpedFiles =~ ( |^)$file( |$) ]]; then
if [[ $file =~ ^multisrc ]]; then
bumpMultisrcVersion ${file/additional.gradle/}
bumpedFiles+="$file "
bumpVersion $file
fi
done
}
findAndBump() {
for lib in $@; do
for file in $(grep -l -R ":lib:$lib" --include "build.gradle" --include "build.gradle.kts" src/ lib-multisrc/); do
# prevent bumping the same extension multiple times
if [[ ! $bumpedFiles =~ ( |^)$file( |$) ]]; then
if [[ $file =~ ^lib-multisrc ]]; then
bumpLibMultisrcVersion ${file/build.gradle.kts/}
else
bumpedFiles+="$file "
bumpVersion $file

View file

@ -21,65 +21,19 @@ jobs:
runs-on: ubuntu-latest
outputs:
individualMatrix: ${{ steps.generate-matrices.outputs.individualMatrix }}
multisrcMatrix: ${{ steps.generate-matrices.outputs.multisrcMatrix }}
isIndividualChanged: ${{ steps.parse-changed-files.outputs.isIndividualChanged }}
isMultisrcChanged: ${{ steps.parse-changed-files.outputs.isMultisrcChanged }}
env:
CI_MODULE_GEN: true
steps:
- name: Clone repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@27152f6fa06a6b8062ef7195c795692e51fc2c81 # v2
- name: Set up JDK
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4
with:
java-version: 17
distribution: temurin
- id: get-changed-files
name: Get changed files
uses: Ana06/get-changed-files@e0c398b7065a8d84700c471b6afc4116d1ba4e96 # v2.2.0
- id: parse-changed-files
name: Parse changed files
run: |
isIndividualChanged=0
isMultisrcChanged=0
for changedFile in ${{ steps.get-changed-files.outputs.all }}; do
if [[ ${changedFile} == src/* ]]; then
isIndividualChanged=1
elif [[ ${changedFile} == multisrc/* ]]; then
isMultisrcChanged=1
elif [[ ${changedFile} == .github/workflows/issue_moderator.yml ]]; then
true
elif [[ ${changedFile} == *.md ]]; then
true
else
isIndividualChanged=1
isMultisrcChanged=1
break
fi
done
echo "isIndividualChanged=$isIndividualChanged" >> $GITHUB_OUTPUT
echo "isMultisrcChanged=$isMultisrcChanged" >> $GITHUB_OUTPUT
- name: Set up Gradle
uses: gradle/actions/setup-gradle@ec92e829475ac0c2315ea8f9eced72db85bb337a # v3
- name: Generate multisrc sources
if: ${{ steps.parse-changed-files.outputs.isMultisrcChanged == '1' }}
run: ./gradlew :multisrc:generateExtensions
uses: gradle/wrapper-validation-action@a494d935f4b56874c4a5a87d19af7afcf3a163d0 # v2
- name: Get number of modules
run: |
set -x
./gradlew -q projects | grep '.*extensions\:\(individual\|multisrc\)\:.*\:.*' > projects.txt
projects=(src/*/*)
echo "NUM_INDIVIDUAL_MODULES=$(cat projects.txt | grep '.*\:individual\:.*' | wc -l)" >> $GITHUB_ENV
echo "NUM_MULTISRC_MODULES=$(cat projects.txt | grep '.*\:multisrc\:.*' | wc -l)" >> $GITHUB_ENV
echo "NUM_INDIVIDUAL_MODULES=${#projects[@]}" >> $GITHUB_ENV
- id: generate-matrices
name: Create output matrices
@ -87,55 +41,17 @@ jobs:
with:
script: |
const numIndividualModules = process.env.NUM_INDIVIDUAL_MODULES;
const numMultisrcModules = process.env.NUM_MULTISRC_MODULES;
const chunkSize = process.env.CI_CHUNK_SIZE;
const numIndividualChunks = Math.ceil(numIndividualModules / chunkSize);
const numMultisrcChunks = Math.ceil(numMultisrcModules / chunkSize);
console.log(`Individual modules: ${numIndividualModules} (${numIndividualChunks} chunks of ${chunkSize})`);
console.log(`Multi-source modules: ${numMultisrcModules} (${numMultisrcChunks} chunks of ${chunkSize})`);
core.setOutput('individualMatrix', { 'chunk': [...Array(numIndividualChunks).keys()] });
core.setOutput('multisrcMatrix', { 'chunk': [...Array(numMultisrcChunks).keys()] });
build_multisrc:
name: Build multisrc modules
needs: prepare
if: ${{ needs.prepare.outputs.isMultisrcChanged == '1' }}
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.prepare.outputs.multisrcMatrix) }}
steps:
- name: Checkout PR
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: Set up JDK
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4
with:
java-version: 17
distribution: temurin
- name: Set up Gradle
uses: gradle/actions/setup-gradle@ec92e829475ac0c2315ea8f9eced72db85bb337a # v3
with:
cache-read-only: true
- name: Generate sources from the multi-source library
env:
CI_MODULE_GEN: "true"
run: ./gradlew :multisrc:generateExtensions
- name: Build extensions (chunk ${{ matrix.chunk }})
env:
CI_MULTISRC: "true"
CI_CHUNK_NUM: ${{ matrix.chunk }}
run: ./gradlew assembleDebug
build_individual:
name: Build individual modules
needs: prepare
if: ${{ needs.prepare.outputs.isIndividualChanged == '1' }}
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.prepare.outputs.individualMatrix) }}
@ -150,12 +66,11 @@ jobs:
distribution: temurin
- name: Set up Gradle
uses: gradle/actions/setup-gradle@ec92e829475ac0c2315ea8f9eced72db85bb337a # v3
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3
with:
cache-read-only: true
- name: Build extensions (chunk ${{ matrix.chunk }})
env:
CI_MULTISRC: "false"
CI_CHUNK_NUM: ${{ matrix.chunk }}
run: ./gradlew assembleDebug
run: ./gradlew -p src assembleDebug

View file

@ -24,9 +24,6 @@ jobs:
runs-on: ubuntu-latest
outputs:
individualMatrix: ${{ steps.generate-matrices.outputs.individualMatrix }}
multisrcMatrix: ${{ steps.generate-matrices.outputs.multisrcMatrix }}
env:
CI_MODULE_GEN: true
steps:
- name: Clone repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
@ -58,27 +55,14 @@ jobs:
./.github/scripts/bump-versions.sh ${{ steps.modified-libs.outputs.all_changed_files }}
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@27152f6fa06a6b8062ef7195c795692e51fc2c81 # v2
- name: Set up JDK
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4
with:
java-version: 17
distribution: temurin
- name: Set up Gradle
uses: gradle/actions/setup-gradle@ec92e829475ac0c2315ea8f9eced72db85bb337a # v3
- name: Generate multisrc sources
run: ./gradlew :multisrc:generateExtensions
uses: gradle/wrapper-validation-action@a494d935f4b56874c4a5a87d19af7afcf3a163d0 # v2
- name: Get number of modules
run: |
set -x
./gradlew -q projects | grep '.*extensions\:\(individual\|multisrc\)\:.*\:.*' > projects.txt
projects=(src/*/*)
echo "NUM_INDIVIDUAL_MODULES=$(cat projects.txt | grep '.*\:individual\:.*' | wc -l)" >> $GITHUB_ENV
echo "NUM_MULTISRC_MODULES=$(cat projects.txt | grep '.*\:multisrc\:.*' | wc -l)" >> $GITHUB_ENV
echo "NUM_INDIVIDUAL_MODULES=${#projects[@]}" >> $GITHUB_ENV
- id: generate-matrices
name: Create output matrices
@ -86,67 +70,13 @@ jobs:
with:
script: |
const numIndividualModules = process.env.NUM_INDIVIDUAL_MODULES;
const numMultisrcModules = process.env.NUM_MULTISRC_MODULES;
const chunkSize = process.env.CI_CHUNK_SIZE;
const numIndividualChunks = Math.ceil(numIndividualModules / chunkSize);
const numMultisrcChunks = Math.ceil(numMultisrcModules / chunkSize);
console.log(`Individual modules: ${numIndividualModules} (${numIndividualChunks} chunks of ${chunkSize})`);
console.log(`Multi-source modules: ${numMultisrcModules} (${numMultisrcChunks} chunks of ${chunkSize})`);
core.setOutput('individualMatrix', { 'chunk': [...Array(numIndividualChunks).keys()] });
core.setOutput('multisrcMatrix', { 'chunk': [...Array(numMultisrcChunks).keys()] });
build_multisrc:
name: Build multisrc modules
needs: prepare
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.prepare.outputs.multisrcMatrix) }}
steps:
- name: Checkout master branch
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
ref: master
- name: Set up JDK
uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4
with:
java-version: 17
distribution: temurin
- name: Prepare signing key
run: |
echo ${{ secrets.SIGNING_KEY }} | base64 -d > signingkey.jks
- name: Set up Gradle
uses: gradle/actions/setup-gradle@ec92e829475ac0c2315ea8f9eced72db85bb337a # v3
- name: Generate sources from the multi-source library
env:
CI_MODULE_GEN: "true"
run: ./gradlew :multisrc:generateExtensions
- name: Build extensions (chunk ${{ matrix.chunk }})
env:
CI_MULTISRC: "true"
CI_CHUNK_NUM: ${{ matrix.chunk }}
ALIAS: ${{ secrets.ALIAS }}
KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: ./gradlew assembleRelease
- name: Upload APKs (chunk ${{ matrix.chunk }})
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4
if: "github.repository == 'aniyomiorg/aniyomi-extensions'"
with:
name: "multisrc-apks-${{ matrix.chunk }}"
path: "**/*.apk"
retention-days: 1
- name: Clean up CI files
run: rm signingkey.jks
build_individual:
name: Build individual modules
@ -171,19 +101,18 @@ jobs:
echo ${{ secrets.SIGNING_KEY }} | base64 -d > signingkey.jks
- name: Set up Gradle
uses: gradle/actions/setup-gradle@ec92e829475ac0c2315ea8f9eced72db85bb337a # v3
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3
- name: Build extensions (chunk ${{ matrix.chunk }})
env:
CI_MULTISRC: "false"
CI_CHUNK_NUM: ${{ matrix.chunk }}
ALIAS: ${{ secrets.ALIAS }}
KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: ./gradlew assembleRelease
run: ./gradlew -p src assembleRelease
- name: Upload APKs (chunk ${{ matrix.chunk }})
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4
if: "github.repository == 'aniyomiorg/aniyomi-extensions'"
with:
name: "individual-apks-${{ matrix.chunk }}"
@ -196,13 +125,12 @@ jobs:
publish_repo:
name: Publish repo
needs:
- build_multisrc
- build_individual
if: "github.repository == 'aniyomiorg/aniyomi-extensions'"
runs-on: ubuntu-latest
steps:
- name: Download APK artifacts
uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4
with:
path: ~/apk-artifacts

View file

@ -1,11 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="AnimeStreamGenerator" type="JetRunConfigurationType" nameIsGenerated="true">
<module name="aniyomi-extensions.multisrc.main" />
<option name="MAIN_CLASS_NAME" value="eu.kanade.tachiyomi.multisrc.animestream.AnimeStreamGenerator" />
<method v="2">
<option name="Make" enabled="true" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktFormat" externalProjectPath="$PROJECT_DIR$/multisrc" vmOptions="" scriptParameters="-Ptheme=animestream" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktLint" externalProjectPath="$PROJECT_DIR$/multisrc" vmOptions="" scriptParameters="-Ptheme=animestream" />
</method>
</configuration>
</component>

View file

@ -1,11 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="DooPlayGenerator" type="JetRunConfigurationType" nameIsGenerated="true">
<module name="aniyomi-extensions.multisrc.main" />
<option name="MAIN_CLASS_NAME" value="eu.kanade.tachiyomi.multisrc.dooplay.DooPlayGenerator" />
<method v="2">
<option name="Make" enabled="true" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktFormat" externalProjectPath="$PROJECT_DIR$/multisrc" vmOptions="" scriptParameters="-Ptheme=dooplay" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktLint" externalProjectPath="$PROJECT_DIR$/multisrc" vmOptions="" scriptParameters="-Ptheme=dooplay" />
</method>
</configuration>
</component>

View file

@ -1,11 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="DopeFlixGenerator" type="JetRunConfigurationType" nameIsGenerated="true">
<module name="aniyomi-extensions.multisrc.main" />
<option name="MAIN_CLASS_NAME" value="eu.kanade.tachiyomi.multisrc.dopeflix.DopeFlixGenerator" />
<method v="2">
<option name="Make" enabled="true" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktFormat" externalProjectPath="$PROJECT_DIR$/multisrc" vmOptions="" scriptParameters="-Ptheme=dopeflix" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktLint" externalProjectPath="$PROJECT_DIR$/multisrc" vmOptions="" scriptParameters="-Ptheme=dopeflix" />
</method>
</configuration>
</component>

View file

@ -1,11 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ZoroThemeGenerator" type="JetRunConfigurationType" nameIsGenerated="true">
<module name="aniyomi-extensions.multisrc.main" />
<option name="MAIN_CLASS_NAME" value="eu.kanade.tachiyomi.multisrc.zorotheme.ZoroThemeGenerator" />
<method v="2">
<option name="Make" enabled="true" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktFormat" externalProjectPath="$PROJECT_DIR$/multisrc" vmOptions="" scriptParameters="-Ptheme=zorotheme" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktLint" externalProjectPath="$PROJECT_DIR$/multisrc" vmOptions="" scriptParameters="-Ptheme=zorotheme" />
</method>
</configuration>
</component>

View file

@ -1,16 +0,0 @@
plugins {
`kotlin-dsl`
}
dependencies {
implementation(libs.gradle.agp)
implementation(libs.gradle.kotlin)
implementation(libs.gradle.kotlin.serialization)
// Workaround: https://github.com/gradle/gradle/issues/15383#issuecomment-779893192
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
}
kotlin {
// To access AndroidConfig
sourceSets.getByName("main").kotlin.srcDir("../buildSrc/src/main/kotlin")
}

View file

@ -1,12 +0,0 @@
import org.gradle.accessors.dm.LibrariesForLibs
plugins {
`java-library`
kotlin("jvm")
}
val libs = the<LibrariesForLibs>()
dependencies {
compileOnly(libs.kotlin.stdlib)
}

View file

@ -1,12 +1,15 @@
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.kotlinter) apply false
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory.asFile.get())
buildscript {
repositories {
mavenCentral()
google()
maven(url = "https://plugins.gradle.org/m2/")
}
dependencies {
classpath(libs.gradle.agp)
classpath(libs.gradle.kotlin)
classpath(libs.gradle.kotlin.serialization)
classpath(libs.gradle.kotlinter)
}
}
allprojects {

2
buildSrc/.gitignore vendored
View file

@ -1,2 +0,0 @@
.gradle/
build/

View file

@ -2,6 +2,9 @@ plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
dependencies {
implementation(libs.gradle.agp)
implementation(libs.gradle.kotlin)
implementation(libs.gradle.kotlin.serialization)
implementation(libs.gradle.kotlinter)
}

View file

@ -0,0 +1,6 @@
import org.gradle.api.plugins.ExtensionAware
import org.gradle.kotlin.dsl.extra
var ExtensionAware.baseVersionCode: Int
get() = extra.get("baseVersionCode") as Int
set(value) = extra.set("baseVersionCode", value)

View file

@ -1,5 +1,3 @@
import org.gradle.accessors.dm.LibrariesForLibs
plugins {
id("com.android.library")
kotlin("android")
@ -16,8 +14,11 @@ android {
namespace = "eu.kanade.tachiyomi.lib.${name.replace("-", "")}"
}
val libs = the<LibrariesForLibs>()
dependencies {
compileOnly(libs.bundles.common)
}
versionCatalogs
.named("libs")
.findBundle("common")
.ifPresent { common ->
dependencies {
compileOnly(common)
}
}

View file

@ -0,0 +1,13 @@
plugins {
`java-library`
kotlin("jvm")
}
versionCatalogs
.named("libs")
.findLibrary("kotlin-stdlib")
.ifPresent { stdlib ->
dependencies {
compileOnly(stdlib)
}
}

View file

@ -0,0 +1,55 @@
plugins {
id("com.android.library")
kotlin("android")
id("kotlinx-serialization")
id("org.jmailen.kotlinter")
}
android {
compileSdk = AndroidConfig.compileSdk
defaultConfig {
minSdk = AndroidConfig.minSdk
}
namespace = "eu.kanade.tachiyomi.multisrc.${project.name}"
sourceSets {
named("main") {
manifest.srcFile("AndroidManifest.xml")
java.setSrcDirs(listOf("src"))
res.setSrcDirs(listOf("res"))
assets.setSrcDirs(listOf("assets"))
}
}
buildFeatures {
resValues = false
shaders = false
}
kotlinOptions {
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
}
}
versionCatalogs
.named("libs")
.findBundle("common")
.ifPresent { common ->
dependencies {
compileOnly(common)
}
}
tasks {
preBuild {
dependsOn(lintKotlin)
}
if (System.getenv("CI") != "true") {
lintKotlin {
dependsOn(formatKotlin)
}
}
}

View file

@ -8,6 +8,9 @@ assert !ext.has("libVersion")
assert extName.chars().max().asInt < 0x180 : "Extension name should be romanized"
Project theme = ext.has("themePkg") ? project(":lib-multisrc:$themePkg") : null
if (theme != null) evaluationDependsOn(theme.path)
android {
compileSdkVersion AndroidConfig.compileSdk
namespace AndroidConfig.namespace
@ -30,8 +33,8 @@ android {
minSdkVersion AndroidConfig.minSdk
targetSdkVersion AndroidConfig.targetSdk
applicationIdSuffix project.parent.name + "." + project.name
versionCode extVersionCode
versionName "14.$extVersionCode"
versionCode theme == null ? extVersionCode : theme.baseVersionCode + overrideVersionCode
versionName "14.$versionCode"
base {
archivesName = "aniyomi-$applicationIdSuffix-v$versionName"
}
@ -39,8 +42,19 @@ android {
manifestPlaceholders = [
appName : "Aniyomi: $extName",
extClass: extClass,
nsfw: project.ext.find("isNsfw") ? 1 : 0,
nsfw : project.ext.find("isNsfw") ? 1 : 0,
]
String baseUrl = project.ext.find("baseUrl") ?: ""
if (theme != null && !baseUrl.isEmpty()) {
def split = baseUrl.split("://")
assert split.length == 2
def path = split[1].split("/")
manifestPlaceholders += [
SOURCEHOST : path[0],
SOURCESCHEME: split[0],
]
}
}
signingConfigs {
@ -84,6 +98,7 @@ android {
}
dependencies {
if (theme != null) implementation(theme) // Overrides core launcher icons
implementation(project(":core"))
compileOnly(libs.bundles.common)
}
@ -113,4 +128,6 @@ tasks.register("writeManifestFile") {
}
preBuild.dependsOn(writeManifestFile, lintKotlin)
lintKotlin.dependsOn(formatKotlin)
if (System.getenv("CI") != "true") {
lintKotlin.dependsOn(formatKotlin)
}

View file

@ -8,6 +8,7 @@ serialization_version = "1.5.1"
gradle-agp = { module = "com.android.tools.build:gradle", version.ref = "agp_version" }
gradle-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin_version" }
gradle-kotlin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin_version" }
gradle-kotlinter = { module = "org.jmailen.gradle:kotlinter-gradle", version = "3.15.0" }
aniyomi-lib = { module = "com.github.aniyomiorg:extensions-lib", version = "14" }
@ -19,7 +20,6 @@ coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", ve
coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines_version" }
injekt-core = { module = "com.github.inorichi.injekt:injekt-core", version = "65b0440" }
rxandroid = { module = "io.reactivex:rxandroid", version = "1.2.1" }
rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" }
jsoup = { module = "org.jsoup:jsoup", version = "1.16.1" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0-alpha.11" }
@ -27,11 +27,3 @@ quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" }
[bundles]
common = ["kotlin-stdlib", "injekt-core", "rxjava", "kotlin-protobuf", "kotlin-json", "jsoup", "okhttp", "aniyomi-lib", "quickjs", "coroutines-core", "coroutines-android"]
reactivex = ["rxandroid"]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp_version" }
android-library = { id = "com.android.library", version.ref = "agp_version" }
kotlinter = { id = "org.jmailen.kotlinter", version = "3.15.0" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin_version" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin_version" }

View file

@ -0,0 +1,5 @@
plugins {
id("lib-multisrc")
}
baseVersionCode = 2

View file

@ -0,0 +1,5 @@
plugins {
id("lib-multisrc")
}
baseVersionCode = 1

View file

@ -0,0 +1,5 @@
plugins {
id("lib-multisrc")
}
baseVersionCode = 1

View file

@ -0,0 +1,11 @@
plugins {
id("lib-multisrc")
}
baseVersionCode = 19
dependencies {
api(project(":lib:dood-extractor"))
api(project(":lib:cryptoaes"))
api(project(":lib:playlist-utils"))
}

View file

@ -0,0 +1,10 @@
plugins {
id("lib-multisrc")
}
baseVersionCode = 1
dependencies {
api(project(":lib:megacloud-extractor"))
api(project(":lib:streamtape-extractor"))
}

View file

@ -1,86 +0,0 @@
plugins {
id("com.android.library")
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
}
android {
compileSdk = AndroidConfig.compileSdk
namespace = AndroidConfig.multisrcNamespace
defaultConfig {
minSdk = 29
}
kotlinOptions {
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
}
}
configurations {
compileOnly {
isCanBeResolved = true
}
}
dependencies {
compileOnly(libs.bundles.common)
// Implements all shared-extractors on the extensions generator
// Note that this does not mean that generated sources are going to
// implement them too; this is just to be able to compile and generate sources.
rootProject.subprojects
.filter { it.path.startsWith(":lib:") }
.forEach(::implementation)
}
tasks {
register<JavaExec>("generateExtensions") {
classpath = configurations.compileOnly.get() +
configurations.androidApis.get() + // android.jar path
layout.buildDirectory.files("intermediates/aar_main_jar/debug/classes.jar") // jar made from this module
// Default generator class, generates extensions for all themes.
mainClass.set("generator.GeneratorMainKt")
// Only generate extensions from a specified theme.
if (project.hasProperty("theme")) {
val theme = project.property("theme")
val themeDir = file("src/main/java/eu/kanade/tachiyomi/multisrc/$theme")
if (themeDir.isDirectory) {
val className = themeDir.list()!!
.first { it.endsWith("Generator.kt") }
.removeSuffix(".kt")
mainClass.set("eu.kanade.tachiyomi.multisrc.$theme.$className")
}
}
workingDir = workingDir.parentFile // project root
errorOutput = System.out // for GitHub workflow commands
if (!logger.isInfoEnabled) {
standardOutput = org.gradle.internal.io.NullOutputStream.INSTANCE
}
dependsOn("ktFormat", "ktLint", "assembleDebug")
}
register<org.jmailen.gradle.kotlinter.tasks.LintTask>("ktLint") {
if (project.hasProperty("theme")) {
val theme = project.property("theme")
source(files("src/main/java/eu/kanade/tachiyomi/multisrc/$theme", "overrides/$theme"))
return@register
}
source(files("src", "overrides"))
}
register<org.jmailen.gradle.kotlinter.tasks.FormatTask>("ktFormat") {
if (project.hasProperty("theme")) {
val theme = project.property("theme")
source(files("src/main/java/eu/kanade/tachiyomi/multisrc/$theme", "overrides/$theme"))
return@register
}
source(files("src", "overrides"))
}
}

View file

@ -1,4 +0,0 @@
dependencies {
implementation(project(":lib:okru-extractor"))
implementation(project(":lib:googledrive-extractor"))
}

View file

@ -1,5 +0,0 @@
dependencies {
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
}

View file

@ -1,5 +0,0 @@
dependencies {
implementation(project(':lib:filemoon-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
}

View file

@ -1,5 +0,0 @@
dependencies {
implementation(project(":lib:okru-extractor"))
implementation(project(":lib:streamtape-extractor"))
implementation(project(":lib:sendvid-extractor"))
}

View file

@ -1,5 +0,0 @@
dependencies {
implementation(project(":lib:dailymotion-extractor"))
implementation(project(":lib:streamwish-extractor"))
implementation(project(":lib:playlist-utils"))
}

View file

@ -1,5 +0,0 @@
dependencies {
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:googledrive-extractor'))
implementation(project(':lib:sibnet-extractor'))
}

View file

@ -1,4 +0,0 @@
dependencies {
implementation(project(':lib:dailymotion-extractor'))
implementation(project(':lib:playlist-utils'))
}

View file

@ -1,5 +0,0 @@
dependencies {
implementation(project(":lib:dailymotion-extractor"))
implementation(project(":lib:mp4upload-extractor"))
implementation(project(":lib:streamwish-extractor"))
}

View file

@ -1,5 +0,0 @@
dependencies {
implementation(project(':lib:dailymotion-extractor'))
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:streamwish-extractor'))
}

View file

@ -1,4 +0,0 @@
dependencies {
implementation(project(":lib:gdriveplayer-extractor"))
implementation(project(":lib:unpacker"))
}

View file

@ -1,4 +0,0 @@
dependencies {
implementation(project(":lib:unpacker"))
implementation(project(":lib:playlist-utils"))
}

View file

@ -1,3 +0,0 @@
dependencies {
implementation(project(":lib:blogger-extractor"))
}

View file

@ -1,3 +0,0 @@
dependencies {
implementation(project(":lib:chillx-extractor"))
}

View file

@ -1,3 +0,0 @@
dependencies {
implementation(project(":lib:playlist-utils"))
}

View file

@ -1,5 +0,0 @@
dependencies {
implementation(project(":lib:playlist-utils"))
implementation(project(":lib:blogger-extractor"))
implementation("dev.datlag.jsunpacker:jsunpacker:1.0.1")
}

View file

@ -1,4 +0,0 @@
dependencies {
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
implementation(project(':lib:playlist-utils'))
}

View file

@ -1,4 +0,0 @@
dependencies {
implementation(project(':lib:voe-extractor'))
implementation(project(':lib:dood-extractor'))
}

View file

@ -1,4 +0,0 @@
dependencies {
implementation(project(':lib:streamhidevid-extractor'))
}

View file

@ -1,3 +0,0 @@
dependencies {
implementation(project(":lib:playlist-utils"))
}

View file

@ -1,5 +0,0 @@
dependencies {
implementation(project(":lib:dood-extractor"))
implementation(project(":lib:cryptoaes"))
implementation(project(":lib:playlist-utils"))
}

View file

@ -1,4 +0,0 @@
dependencies {
implementation(project(":lib:megacloud-extractor"))
implementation(project(':lib:streamtape-extractor'))
}

View file

@ -1,37 +0,0 @@
package eu.kanade.tachiyomi.multisrc.animestream
import generator.ThemeSourceData.SingleLang
import generator.ThemeSourceGenerator
class AnimeStreamGenerator : ThemeSourceGenerator {
override val themePkg = "animestream"
override val themeClass = "AnimeStream"
override val baseVersionCode = 2
override val sources = listOf(
SingleLang("AnimeBalkan", "https://animebalkan.org", "sr", isNsfw = false, overrideVersionCode = 1),
SingleLang("AnimeIndo", "https://animeindo.skin", "id", isNsfw = false, overrideVersionCode = 10),
SingleLang("AnimeKhor", "https://animekhor.xyz", "en", isNsfw = false, overrideVersionCode = 3),
SingleLang("Animenosub", "https://animenosub.com", "en", isNsfw = true, overrideVersionCode = 4),
SingleLang("AnimeXin", "https://animexin.vip", "all", isNsfw = false, overrideVersionCode = 8),
SingleLang("AnimeYT.es", "https://animeyt.es", "es", isNsfw = false, className = "AnimeYTES", pkgName = "animeytes", overrideVersionCode = 3),
SingleLang("Tiodonghua.com", "https://anime.tiodonghua.com", "es", isNsfw = false, className = "Tiodonghua", pkgName = "tiodonghua", overrideVersionCode = 2),
SingleLang("AsyaAnimeleri", "https://asyaanimeleri.com", "tr", isNsfw = false, overrideVersionCode = 3),
SingleLang("ChineseAnime", "https://chineseanime.top", "all", isNsfw = false, overrideVersionCode = 4),
SingleLang("desu-online", "https://desu-online.pl", "pl", className = "DesuOnline", isNsfw = false, overrideVersionCode = 4),
SingleLang("DonghuaStream", "https://donghuastream.co.in", "en", isNsfw = false, overrideVersionCode = 3),
SingleLang("LMAnime", "https://lmanime.com", "all", isNsfw = false, overrideVersionCode = 6),
SingleLang("LuciferDonghua", "https://luciferdonghua.in", "en", isNsfw = false, overrideVersionCode = 4),
SingleLang("MiniOppai", "https://minioppai.org", "id", isNsfw = true, overrideVersionCode = 4),
SingleLang("MyKdrama", "https://mykdrama.co", "fr", isNsfw = false),
SingleLang("RineCloud", "https://rine.cloud", "pt-BR", isNsfw = false, overrideVersionCode = 6),
SingleLang("TRAnimeCI", "https://tranimaci.com", "tr", isNsfw = false, overrideVersionCode = 1),
)
companion object {
@JvmStatic
fun main(args: Array<String>) = AnimeStreamGenerator().createAll()
}
}

View file

@ -1,22 +0,0 @@
package eu.kanade.tachiyomi.multisrc.datalifeengine
import generator.ThemeSourceData.SingleLang
import generator.ThemeSourceGenerator
class DataLifeEngineGenerator : ThemeSourceGenerator {
override val themePkg = "datalifeengine"
override val themeClass = "DataLifeEngine"
override val baseVersionCode = 1
override val sources = listOf(
SingleLang("Wiflix", "https://wiflix.voto", "fr", overrideVersionCode = 4),
SingleLang("French Anime", "https://french-anime.com", "fr", overrideVersionCode = 5),
)
companion object {
@JvmStatic
fun main(args: Array<String>) = DataLifeEngineGenerator().createAll()
}
}

View file

@ -1,35 +0,0 @@
package eu.kanade.tachiyomi.multisrc.dooplay
import generator.ThemeSourceData.SingleLang
import generator.ThemeSourceGenerator
class DooPlayGenerator : ThemeSourceGenerator {
override val themePkg = "dooplay"
override val themeClass = "Dooplay"
override val baseVersionCode = 1
override val sources = listOf(
SingleLang("AnimeOnline.Ninja", "https://ww3.animeonline.ninja", "es", className = "AnimeOnlineNinja", isNsfw = false, overrideVersionCode = 36),
SingleLang("AnimesOnline", "https://animesonline.nz", "pt-BR", isNsfw = false, overrideVersionCode = 8, pkgName = "animesgratis"),
SingleLang("AnimePlayer", "https://animeplayer.com.br", "pt-BR", isNsfw = true, overrideVersionCode = 2),
SingleLang("AnimeSAGA", "https://www.animesaga.in", "hi", isNsfw = false, overrideVersionCode = 9),
SingleLang("AnimesFox BR", "https://animesfox.net", "pt-BR", isNsfw = false, overrideVersionCode = 2),
SingleLang("Animes House", "https://animeshouse.net", "pt-BR", isNsfw = false, overrideVersionCode = 9),
SingleLang("Cinemathek", "https://cinemathek.net", "de", isNsfw = true, overrideVersionCode = 19),
SingleLang("GoAnimes", "https://goanimes.net", "pt-BR", isNsfw = true, overrideVersionCode = 9),
SingleLang("JetAnime", "https://ssl.jetanimes.com", "fr", isNsfw = false, overrideVersionCode = 3),
SingleLang("Kinoking", "https://kinoking.cc", "de", isNsfw = false, overrideVersionCode = 20),
SingleLang("Multimovies", "https://multimovies.live", "en", isNsfw = false, overrideVersionCode = 15),
SingleLang("Pi Fansubs", "https://pifansubs.club", "pt-BR", isNsfw = true, overrideVersionCode = 18),
SingleLang("Pobreflix", "https://pobreflix.biz", "pt-BR", isNsfw = true, overrideVersionCode = 5),
SingleLang("UniqueStream", "https://uniquestream.net", "en", isNsfw = false, overrideVersionCode = 2),
SingleLang("VoirCartoon", "https://voircartoon.com", "fr", isNsfw = true, overrideVersionCode = 1),
)
companion object {
@JvmStatic
fun main(args: Array<String>) = DooPlayGenerator().createAll()
}
}

View file

@ -1,22 +0,0 @@
package eu.kanade.tachiyomi.multisrc.dopeflix
import generator.ThemeSourceData.SingleLang
import generator.ThemeSourceGenerator
class DopeFlixGenerator : ThemeSourceGenerator {
override val themePkg = "dopeflix"
override val themeClass = "DopeFlix"
override val baseVersionCode = 19
override val sources = listOf(
SingleLang("DopeBox", "https://dopebox.to", "en", isNsfw = false, overrideVersionCode = 6),
SingleLang("SFlix", "https://sflix.to", "en", isNsfw = false, overrideVersionCode = 5),
)
companion object {
@JvmStatic
fun main(args: Array<String>) = DopeFlixGenerator().createAll()
}
}

View file

@ -1,22 +0,0 @@
package eu.kanade.tachiyomi.multisrc.zorotheme
import generator.ThemeSourceData.SingleLang
import generator.ThemeSourceGenerator
class ZoroThemeGenerator : ThemeSourceGenerator {
override val themePkg = "zorotheme"
override val themeClass = "ZoroTheme"
override val baseVersionCode = 1
override val sources = listOf(
SingleLang("AniWatch", "https://aniwatch.to", "en", isNsfw = false, pkgName = "zoro", overrideVersionCode = 39),
SingleLang("Kaido", "https://kaido.to", "en", isNsfw = false, overrideVersionCode = 6),
)
companion object {
@JvmStatic
fun main(args: Array<String>) = ZoroThemeGenerator().createAll()
}
}

View file

@ -1,29 +0,0 @@
package generator
import java.io.File
/**
* Finds and calls all `ThemeSourceGenerator`s
*/
@Suppress("unused_parameter")
fun main(args: Array<String>) {
val userDir = System.getProperty("user.dir")!!
val sourcesDirPath = "$userDir/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc"
val sourcesDir = File(sourcesDirPath)
// find all theme packages
sourcesDir.list()!!
.filter { File(sourcesDir, it).isDirectory }
.forEach { themeSource ->
// Find all XxxGenerator.kt files and invoke main from them
File("$sourcesDirPath/$themeSource").list()!!
.filter { it.endsWith("Generator.kt") }
.mapNotNull { generatorClass ->
// Find Java class and extract method lists
Class.forName("eu/kanade/tachiyomi/multisrc/$themeSource/$generatorClass".replace("/", ".").substringBefore(".kt"))
.methods
.find { it.name == "main" }
}
.forEach { it.invoke(null, emptyArray<String>()) }
}
}

View file

@ -1,50 +0,0 @@
package generator
import java.io.File
/**
* Finds all themes and creates an Intellij Idea run configuration for their generators
* Should be run after creation/deletion of each theme
*/
@Suppress("unused_parameter")
fun main(args: Array<String>) {
val userDir = System.getProperty("user.dir")!!
val sourcesDirPath = "$userDir/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc"
val sourcesDir = File(sourcesDirPath)
// cleanup from past runs
File("$userDir/.run").apply {
if (exists()) {
deleteRecursively()
}
mkdirs()
}
// find all theme packages
sourcesDir.list()!!
.filter { File(sourcesDir, it).isDirectory }
.forEach { themeSource ->
// Find all XxxGenerator.kt files
File("$sourcesDirPath/$themeSource").list()!!
.filter { it.endsWith("Generator.kt") }
.map { it.substringBefore(".kt") }
.forEach { generatorClass ->
val file = File("$userDir/.run/$generatorClass.run.xml")
val intellijConfStr = """
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="$generatorClass" type="JetRunConfigurationType" nameIsGenerated="true">
<module name="aniyomi-extensions.multisrc.main" />
<option name="MAIN_CLASS_NAME" value="eu.kanade.tachiyomi.multisrc.$themeSource.$generatorClass" />
<method v="2">
<option name="Make" enabled="true" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktFormat" externalProjectPath="${'$'}PROJECT_DIR${'$'}/multisrc" vmOptions="" scriptParameters="-Ptheme=$themeSource" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktLint" externalProjectPath="${'$'}PROJECT_DIR${'$'}/multisrc" vmOptions="" scriptParameters="-Ptheme=$themeSource" />
</method>
</configuration>
</component>
""".trimIndent()
file.writeText(intellijConfStr)
file.appendText("\n")
}
}
}

View file

@ -1,284 +0,0 @@
package generator
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import java.io.File
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.util.Locale
/**
* This is meant to be used in place of a factory extension, specifically for what would be a multi-source extension.
* A multi-lang (but not multi-source) extension should still be made as a factory extension.
* Use a generator for initial setup of a theme source or when all of the inheritors need a version bump.
* Source list (val sources) should be kept up to date.
*/
interface ThemeSourceGenerator {
/**
* The class that the sources inherit from.
*/
val themeClass: String
/**
* The package that contains themeClass.
*/
val themePkg: String
/**
* Base theme version, starts with 1 and should be increased when based theme class changes
*/
val baseVersionCode: Int
/**
* The list of sources to be created or updated.
*/
val sources: List<ThemeSourceData>
fun createAll() {
val userDir = System.getProperty("user.dir")!!
sources.forEach { createGradleProject(it, themePkg, themeClass, baseVersionCode, userDir) }
}
companion object {
private fun pkgNameSuffix(source: ThemeSourceData, separator: String): String {
return if (source is ThemeSourceData.SingleLang) {
listOf(source.lang.substringBefore("-"), source.pkgName).joinToString(separator)
} else {
listOf("all", source.pkgName).joinToString(separator)
}
}
private fun themeSuffix(themePkg: String, separator: String): String {
return listOf("eu", "kanade", "tachiyomi", "multisrc", themePkg).joinToString(separator)
}
private fun writeGradle(gradle: File, source: ThemeSourceData, baseVersionCode: Int, defaultAdditionalGradlePath: String, additionalGradleOverridePath: String) {
fun File.readTextOrEmptyString(): String = if (exists()) readText(Charsets.UTF_8) else ""
val defaultAdditionalGradleText = File(defaultAdditionalGradlePath).readTextOrEmptyString()
val additionalGradleOverrideText = File(additionalGradleOverridePath).readTextOrEmptyString()
val placeholders = mapOf(
"SOURCEHOST" to source.baseUrl.toHttpUrlOrNull()?.host,
"SOURCESCHEME" to source.baseUrl.toHttpUrlOrNull()?.scheme,
)
val placeholdersStr = placeholders
.filter { it.value != null }
.map { "${" ".repeat(12)}${it.key}: \"${it.value}\"" }
.joinToString(",\n")
gradle.writeText(
"""
|// THIS FILE IS AUTO-GENERATED; DO NOT EDIT
|ext {
| extName = '${source.name}'
| extClass = '.${source.className}'
| extVersionCode = ${baseVersionCode + source.overrideVersionCode + MULTISRC_LIBRARY_VERSION}
| ${if (source.isNsfw) "isNsfw = true\n" else ""}
|}
|
|apply from: "${'$'}rootDir/common.gradle"
|
|$defaultAdditionalGradleText
|$additionalGradleOverrideText
|
|android {
| defaultConfig {
| manifestPlaceholders += [
|$placeholdersStr
| ]
| }
|}
""".trimMargin(),
)
}
private fun writeAndroidManifest(androidManifestFile: File, manifestOverridesPath: String, defaultAndroidManifestPath: String) {
val androidManifestOverride = File(manifestOverridesPath)
val defaultAndroidManifest = File(defaultAndroidManifestPath)
if (androidManifestOverride.exists()) {
androidManifestOverride.copyTo(androidManifestFile)
} else if (defaultAndroidManifest.exists()) {
defaultAndroidManifest.copyTo(androidManifestFile)
}
}
fun createGradleProject(source: ThemeSourceData, themePkg: String, themeClass: String, baseVersionCode: Int, userDir: String) {
// userDir = aniyomi-extensions project root path
val projectRootPath = "$userDir/generated-src/${pkgNameSuffix(source, "/")}"
val projectSrcPath = "$projectRootPath/src/eu/kanade/tachiyomi/animeextension/${pkgNameSuffix(source, "/")}"
val overridesPath = "$userDir/multisrc/overrides/$themePkg/${source.pkgName}"
val defaultResPath = "$userDir/multisrc/overrides/$themePkg/default/res"
val defaultAndroidManifestPath = "$userDir/multisrc/overrides/$themePkg/default/AndroidManifest.xml"
val defaultAdditionalGradlePath = "$userDir/multisrc/overrides/$themePkg/default/additional.gradle"
val resOverridePath = "$overridesPath/res"
val srcOverridePath = "$overridesPath/src"
val manifestOverridePath = "$overridesPath/AndroidManifest.xml"
val additionalGradleOverridePath = "$overridesPath/additional.gradle"
val projectGradleFile = File("$projectRootPath/build.gradle")
val projectAndroidManifestFile = File("$projectRootPath/AndroidManifest.xml")
File(projectRootPath).let { projectRootFile ->
println("Generating $source")
// remove everything from past runs
projectRootFile.deleteRecursively()
projectRootFile.mkdirs()
writeGradle(projectGradleFile, source, baseVersionCode, defaultAdditionalGradlePath, additionalGradleOverridePath)
writeAndroidManifest(projectAndroidManifestFile, manifestOverridePath, defaultAndroidManifestPath)
writeSourceClasses(projectSrcPath, srcOverridePath, source, themePkg, themeClass)
copyThemeClasses(userDir, themePkg, projectRootPath)
copyThemeReadmes(userDir, themePkg, overridesPath, projectRootPath)
copyResFiles(resOverridePath, defaultResPath, projectRootPath)
}
}
private fun copyThemeReadmes(userDir: String, themePkg: String, overridesPath: String, projectRootPath: String) {
val sourcePath = "$userDir/multisrc/src/main/java/${themeSuffix(themePkg, "/")}"
File(projectRootPath).mkdirs()
listOf(sourcePath, overridesPath).forEach { path ->
File(path).list()
?.filter { it.endsWith("README.md") || it.endsWith("CHANGELOG.md") }
?.forEach {
Files.copy(
File("$path/$it").toPath(),
File("$projectRootPath/$it").toPath(),
StandardCopyOption.REPLACE_EXISTING,
)
}
}
}
private fun copyThemeClasses(userDir: String, themePkg: String, projectRootPath: String) {
val themeSrcPath = "$userDir/multisrc/src/main/java/${themeSuffix(themePkg, "/")}"
.replace("/", File.separator)
val themeDestPath = "$projectRootPath/src/${themeSuffix(themePkg, "/")}"
File(themeDestPath).mkdirs()
File(themeSrcPath).walk()
.map { it.toString().replace(themeSrcPath, "") }
.filter { it.endsWith(".kt") && !it.endsWith("Generator.kt") && !it.endsWith("Gen.kt") }
.forEach {
File("$themeSrcPath/$it").copyTo(
File("$themeDestPath/$it"),
true, // REPLACE_EXISTING
)
}
}
private fun copyResFiles(resOverridePath: String, defaultResPath: String, projectRootPath: String): Any {
// check if res override exists if not copy default res
val resOverride = File(resOverridePath)
return if (resOverride.exists()) {
resOverride.copyRecursively(File("$projectRootPath/res"))
} else {
File(defaultResPath).let { defaultResFile ->
if (defaultResFile.exists()) defaultResFile.copyRecursively(File("$projectRootPath/res"))
}
}
}
private fun writeSourceClasses(projectSrcPath: String, srcOverridePath: String, source: ThemeSourceData, themePkg: String, themeClass: String) {
val projectSrcFile = File(projectSrcPath)
projectSrcFile.mkdirs()
val srcOverrideFile = File(srcOverridePath)
if (srcOverrideFile.exists()) {
srcOverrideFile.copyRecursively(projectSrcFile)
} else {
writeSourceClass(projectSrcFile, source, themePkg, themeClass)
}
}
private fun writeSourceClass(classPath: File, source: ThemeSourceData, themePkg: String, themeClass: String) {
fun factoryClassText() = when (source) {
is ThemeSourceData.SingleLang -> {
"""class ${source.className} : $themeClass("${source.sourceName}", "${source.baseUrl}", "${source.lang}")"""
}
is ThemeSourceData.MultiLang -> {
val sourceClasses = source.langs.map { lang ->
"""$themeClass("${source.sourceName}", "${source.baseUrl}", "$lang")"""
}
"""
|class ${source.className} : AnimeSourceFactory {
| override fun createSources() = listOf(
| ${sourceClasses.joinToString(",\n")}
| )
|}
""".trimMargin()
}
}
File("$classPath/${source.className}.kt").writeText(
"""
|/* ktlint-disable */
|// THIS FILE IS AUTO-GENERATED; DO NOT EDIT
|package eu.kanade.tachiyomi.animeextension.${pkgNameSuffix(source, ".")}
|
|import eu.kanade.tachiyomi.multisrc.$themePkg.$themeClass
|${if (source is ThemeSourceData.MultiLang) "import eu.kanade.tachiyomi.animesource.AnimeSourceFactory" else ""}
|
|${factoryClassText()}
""".trimMargin(),
)
}
}
}
sealed class ThemeSourceData {
abstract val name: String
abstract val baseUrl: String
abstract val isNsfw: Boolean
abstract val className: String
abstract val pkgName: String
/**
* Override it if for some reason the name attribute inside the source class
* should be different from the extension name. Useful in cases where the
* extension name should be romanized and the source name should be the one
* in the source language. Defaults to the extension name if not specified.
*/
abstract val sourceName: String
/**
* overrideVersionCode defaults to 0, if a source changes their source override code or
* a previous existing source suddenly needs source code overrides, overrideVersionCode
* should be increased.
* When a new source is added with overrides, overrideVersionCode should still be set to 0
*
* Note: source code overrides are located in "multisrc/overrides/src/<themeName>/<sourceName>"
*/
abstract val overrideVersionCode: Int
data class SingleLang(
override val name: String,
override val baseUrl: String,
val lang: String,
override val isNsfw: Boolean = false,
override val className: String = name.replace(" ", ""),
override val pkgName: String = className.lowercase(Locale.ENGLISH),
override val sourceName: String = name,
override val overrideVersionCode: Int = 0,
) : ThemeSourceData()
data class MultiLang(
override val name: String,
override val baseUrl: String,
val langs: List<String>,
override val isNsfw: Boolean = false,
override val className: String = name.replace(" ", "") + "Factory",
override val pkgName: String = className.substringBefore("Factory").lowercase(Locale.ENGLISH),
override val sourceName: String = name,
override val overrideVersionCode: Int = 0,
) : ThemeSourceData()
}
/**
* This variable should be increased when the multisrc library changes in a way that prompts global extension upgrade
*/
const val MULTISRC_LIBRARY_VERSION = 0

View file

@ -1,5 +1,6 @@
dependencyResolutionManagement {
repositories {
gradlePluginPortal()
mavenCentral()
google()
maven(url = "https://jitpack.io")

View file

@ -1,92 +1,42 @@
apply(from = "repositories.gradle.kts")
pluginManagement {
includeBuild("build-plugins")
}
include(":core")
// Load all modules under /lib
File(rootDir, "lib").eachDir { include("lib:${it.name}") }
// Fix deprecation warnings with Gradle 8.5+.
// See https://docs.gradle.org/8.5/userguide/upgrading_version_8.html#deprecated_missing_project_directory
listOf(
":extensions" to "$rootDir/gradle", // Temporary workaround.
":extensions:individual" to "$rootDir/src",
":extensions:multisrc" to "$rootDir/generated-src",
).forEach { (name, path) ->
val projectDir = file(path)
if (projectDir.exists()) {
include(name)
project(name).projectDir = projectDir
}
}
// Load all modules under /lib-multisrc
File(rootDir, "lib-multisrc").eachDir { include("lib-multisrc:${it.name}") }
if (System.getenv("CI") == null || System.getenv("CI_MODULE_GEN") == "true") {
if (System.getenv("CI") != "true") {
// Local development (full project build)
include(":multisrc")
project(":multisrc").projectDir = File("multisrc")
/**
* Add or remove modules to load as needed for local development here.
* To generate multisrc extensions first, run the `:multisrc:generateExtensions` task first.
*/
loadAllIndividualExtensions()
loadAllGeneratedMultisrcExtensions()
// loadIndividualExtension("all", "jellyfin")
// loadGeneratedMultisrcExtension("en", "aniwatch")
} else {
// Running in CI (GitHub Actions)
val isMultisrc = System.getenv("CI_MULTISRC") == "true"
val chunkSize = System.getenv("CI_CHUNK_SIZE").toInt()
val chunk = System.getenv("CI_CHUNK_NUM").toInt()
if (isMultisrc) {
include(":multisrc")
project(":multisrc").projectDir = File("multisrc")
// Loads generated extensions from multisrc
File(rootDir, "generated-src").getChunk(chunk, chunkSize)?.forEach {
loadGeneratedMultisrcExtension(it.parentFile.name, it.name, log = true)
}
} else {
// Loads individual extensions
File(rootDir, "src").getChunk(chunk, chunkSize)?.forEach {
loadIndividualExtension(it.parentFile.name, it.name, log = true)
}
loadIndividualExtension(it.parentFile.name, it.name)
}
}
fun loadAllIndividualExtensions() {
File(rootDir, "src").eachDir { lang ->
lang.eachDir { extension ->
loadIndividualExtension(lang.name, extension.name)
File(rootDir, "src").eachDir { dir ->
dir.eachDir { subdir ->
loadIndividualExtension(dir.name, subdir.name)
}
}
}
fun loadAllGeneratedMultisrcExtensions() {
File(rootDir, "generated-src").eachDir { lang ->
lang.eachDir { extension ->
loadGeneratedMultisrcExtension(lang.name, extension.name)
}
}
}
fun loadIndividualExtension(lang: String, name: String, log: Boolean = false) {
val projectName = ":extensions:individual:$lang:$name"
if (log) println(projectName)
include(projectName)
project(projectName).projectDir = File("src/$lang/$name")
}
fun loadGeneratedMultisrcExtension(lang: String, name: String, log: Boolean = false) {
val projectName = ":extensions:multisrc:$lang:$name"
if (log) println(projectName)
include(projectName)
project(projectName).projectDir = File("generated-src/$lang/$name")
fun loadIndividualExtension(lang: String, name: String) {
include("src:$lang:$name")
}
fun File.getChunk(chunk: Int, chunkSize: Int): List<File>? {
@ -102,5 +52,10 @@ fun File.getChunk(chunk: Int, chunkSize: Int): List<File>? {
}
fun File.eachDir(block: (File) -> Unit) {
listFiles()?.filter { it.isDirectory }?.forEach { block(it) }
val files = listFiles() ?: return
for (file in files) {
if (file.isDirectory && file.name != ".gradle" && file.name != "build") {
block(file)
}
}
}

View file

@ -1,3 +1,13 @@
ext {
extName = 'AnimeXin'
extClass = '.AnimeXin'
themePkg = 'animestream'
baseUrl = 'https://animexin.vip'
overrideVersionCode = 8
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:dailymotion-extractor'))
implementation(project(':lib:okru-extractor'))

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

View file

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View file

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -0,0 +1,15 @@
ext {
extName = 'ChineseAnime'
extClass = '.ChineseAnime'
themePkg = 'animestream'
baseUrl = 'https://chineseanime.top'
overrideVersionCode = 4
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:dailymotion-extractor"))
implementation(project(":lib:streamwish-extractor"))
implementation(project(":lib:playlist-utils"))
}

View file

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View file

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 207 KiB

View file

@ -0,0 +1,15 @@
ext {
extName = 'LMAnime'
extClass = '.LMAnime'
themePkg = 'animestream'
baseUrl = 'https://lmanime.com'
overrideVersionCode = 6
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:dailymotion-extractor"))
implementation(project(":lib:mp4upload-extractor"))
implementation(project(":lib:streamwish-extractor"))
}

View file

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 5 KiB

View file

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,3 +1,14 @@
ext {
extName = 'Cinemathek'
extClass = '.Cinemathek'
themePkg = 'dooplay'
baseUrl = 'https://cinemathek.net'
overrideVersionCode = 19
isNsfw = true
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:filemoon-extractor'))
implementation(project(':lib:dood-extractor'))

View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Some files were not shown because too many files have changed in this diff Show more