2
0
milan jurkulák 2 жил өмнө
parent
commit
2c557da62f
32 өөрчлөгдсөн 670 нэмэгдсэн , 185 устгасан
  1. 3 0
      .gitignore
  2. 12 2
      app/build.gradle.kts
  3. 4 5
      app/src/main/AndroidManifest.xml
  4. 5 5
      app/src/main/kotlin/org/mjdev/tvapp/module/ProvideModule.kt
  5. 0 1
      app/src/main/kotlin/org/mjdev/tvapp/sync/syncers/GithubMoviesSyncer.kt
  6. 7 0
      app/src/main/kotlin/org/mjdev/tvapp/ui/screens/LoadingScreen.kt
  7. 6 6
      app/src/main/kotlin/org/mjdev/tvapp/viewmodel/DetailViewModel.kt
  8. 5 5
      app/src/main/kotlin/org/mjdev/tvapp/viewmodel/IPTVViewModel.kt
  9. 11 0
      build.gradle.kts
  10. 64 1
      buildSrc/build.gradle.kts
  11. 0 2
      buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/BuildTypeExt.kt
  12. 7 1
      buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/HashMapExt.kt
  13. 14 0
      buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/ProjectExt.kt
  14. 1 1
      buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/TestedExtension.kt
  15. 45 15
      buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/AppPlugin.kt
  16. 12 17
      buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/LibPlugin.kt
  17. 8 31
      buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/config/AppConfig.kt
  18. 36 0
      buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/config/base/BuilConfigs.kt
  19. 2 2
      buildSrc/src/main/kotlin/org/mjdev/gradle/tasks/InAppInfoCreateTask.kt
  20. 0 2
      buildSrc/src/main/kotlin/org/mjdev/gradle/tasks/ReleaseNotesCleanTask.kt
  21. 156 0
      buildSrc/src/main/kotlin/org/mjdev/gradle/tasks/WebServiceCreateTask.kt
  22. 0 1
      buildSrc/src/main/kotlin/org/mjdev/gradle/tasks/ZipReleaseClearTask.kt
  23. 93 71
      config/detekt.yml
  24. 14 5
      gradle/libs.versions.toml
  25. 2 2
      tvlib/build.gradle.kts
  26. 0 0
      web/.nomedia
  27. 0 10
      web/index.html
  28. 17 0
      web/old/app.js
  29. 44 0
      web/old/install
  30. BIN
      web/old/public/favicon.png
  31. 83 0
      web/old/public/index.html
  32. 19 0
      web/old/uninstall

+ 3 - 0
.gitignore

@@ -51,3 +51,6 @@ google-services.json
 *.hprof!/release.zip
 !/documentation/release-notes-*.md
 /reports/
+!/web/app.nginx
+!/web/app.service
+!/web/package.json

+ 12 - 2
app/build.gradle.kts

@@ -11,11 +11,16 @@ plugins {
 }
 
 appConfig {
-    autoCorrectCode = false
+    autoCorrectCode = true
     createDocumentation = false
-    createReleaseNotes = true
+    createReleaseNotes = false
     createZipRelease = false
 
+    createWebApp = false
+    // todo
+    createWebSiteFromGit = false
+
+    // todo
     default {
 //        buildConfigString(
 //            "IPTV_API_URL" to "https://iptv-org.github.io/api/",
@@ -28,16 +33,21 @@ appConfig {
 //        )
     }
 
+    // todo
     debug {
+//        println("creating test bt config field")
+//        buildConfigField("String" , "TEST", "\"TEST\"")
 //        stringRes("app_name", "TVApp-Debug")
 //        addSyncProviderAuthString("sync_auth", "sync")
     }
 
+    // todo
     release {
 //        stringRes("app_name", "TVApp")
 //        addSyncProviderAuthString("sync_auth", "sync")
     }
 
+    // todo
     minified {
 //        stringRes("app_name", "TVApp-Minified")
 //        addSyncProviderAuthString("sync_auth", "sync")

+ 4 - 5
app/src/main/AndroidManifest.xml

@@ -112,11 +112,10 @@
             android:theme="@style/Theme.TvApp"
             android:windowLayoutInDisplayCutoutMode="default">
 
-            <!--            <intent-filter>-->
-            <!--                <action android:name="android.intent.action.MAIN" />-->
-
-            <!--                <category android:name="android.intent.category.LAUNCHER" />-->
-            <!--            </intent-filter>-->
+            <!--<intent-filter>-->
+            <!--<action android:name="android.intent.action.MAIN" />-->
+            <!--<category android:name="android.intent.category.LAUNCHER" />-->
+            <!--</intent-filter>-->
 
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />

+ 5 - 5
app/src/main/kotlin/org/mjdev/tvapp/module/ProvideModule.kt

@@ -1,9 +1,9 @@
 /*
- * Copyright (c) Milan Jurkulák 2023.
- * Contact:
- * e: mimoccc@gmail.com
- * e: mj@mjdev.org
- * w: https://mjdev.org
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
  */
 
 @file:Suppress("PrivatePropertyName")

+ 0 - 1
app/src/main/kotlin/org/mjdev/tvapp/sync/syncers/GithubMoviesSyncer.kt

@@ -8,7 +8,6 @@
 
 package org.mjdev.tvapp.sync.syncers
 
-import androidx.compose.runtime.MutableState
 import org.mjdev.tvapp.data.local.Media
 import org.mjdev.tvapp.sync.SyncAdapter
 import org.mjdev.tvapp.sync.base.SyncItems

+ 7 - 0
app/src/main/kotlin/org/mjdev/tvapp/ui/screens/LoadingScreen.kt

@@ -22,18 +22,25 @@ import org.mjdev.tvlib.screen.Screen
 
 class LoadingScreen : Screen() {
 
+//    private val appInfo = ApplicationInfo()
+
     override val title = R.string.msg_loading
     override val menuIcon: ImageVector get() = Icons.Filled.Tv
     override val immersive: Boolean = true
     override val showOnce: Boolean = true
 
+//    @OptIn(ExperimentalTvMaterial3Api::class)
     @Previews
     @Composable
     override fun Content() {
         Box(
             modifier = Modifier.fillMaxSize(),
+//                .background(Color.Black, RectangleShape),
             contentAlignment = Alignment.Center,
         ) {
+//            Text(
+//                text = appInfo.getInfo()
+//            )
         }
     }
 

+ 6 - 6
app/src/main/kotlin/org/mjdev/tvapp/viewmodel/DetailViewModel.kt

@@ -1,9 +1,9 @@
 /*
- * Copyright (c) Milan Jurkulák 2023.
- * Contact:
- * e: mimoccc@gmail.com
- * e: mj@mjdev.org
- * w: https://mjdev.org
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
  */
 
 @file:Suppress("unused")
@@ -77,4 +77,4 @@ class DetailViewModel @Inject constructor() : BaseViewModel() {
 
     }
 
-}
+}

+ 5 - 5
app/src/main/kotlin/org/mjdev/tvapp/viewmodel/IPTVViewModel.kt

@@ -1,9 +1,9 @@
 /*
- * Copyright (c) Milan Jurkulák 2023.
- * Contact:
- * e: mimoccc@gmail.com
- * e: mj@mjdev.org
- * w: https://mjdev.org
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
  */
 
 @file:Suppress("unused")

+ 11 - 0
build.gradle.kts

@@ -10,6 +10,10 @@ plugins {
     alias(libs.plugins.android.dagger.hilt) apply false
     alias(libs.plugins.devtools.google.ksp) apply false
     alias(libs.plugins.gradle.ktlint) apply false
+//    alias(libs.plugins.gradle.markdown) apply false
+//    alias(libs.plugins.gradle.os.packages) apply false
+//    alias(libs.plugins.gradle.versions)apply false
+//    alias(libs.plugins.gradle.catalogs.update) apply false
 }
 
 buildscript {
@@ -19,6 +23,7 @@ buildscript {
         maven("https://mvn.dailymotion.com/repository/releases/")
         google()
     }
+
     dependencies {
         classpath(libs.gradle)
         classpath(libs.gradle.api)
@@ -29,6 +34,12 @@ buildscript {
         classpath(libs.gradle.paparazzi.plugin)
         classpath(libs.gradle.kotlinter)
         classpath(libs.gradle.dokka.plugin)
+//        classpath(libs.gradle.markdown.plugin)
+//        classpath(libs.gradle.ospackage.plugin)
+    }
+
+    configurations.classpath {
+        resolutionStrategy.activateDependencyLocking()
     }
 }
 

+ 64 - 1
buildSrc/build.gradle.kts

@@ -57,6 +57,68 @@ plugins {
 //    }
 //}
 
+//ospackage {
+//    packageName = 'foo'
+//    version = '1.2.3'
+//    release = '1'
+//    arch = I386
+//    os = LINUX
+//
+//    installUtils file('scripts/rpm/utils.sh')
+//    preInstall file('scripts/rpm/preInstall.sh')
+//    postInstall file('scripts/rpm/postInstall.sh')
+//    preUninstall 'touch /tmp/myfile'
+//    postUninstall file('scripts/rpm/postUninstall.sh')
+//
+//    requires('qux')
+//
+//    into '/opt/foo'
+//
+//    from(jar.outputs.files) {
+//        into 'lib'
+//    }
+//    from(configurations.runtime) {
+//        into 'lib'
+//    }
+//    from('lib') {
+//        into 'lib'
+//    }
+//    from('scripts') {
+//        into 'bin'
+//        exclude 'database'
+//        fileMode = 0550
+//    }
+//    from('src/main/resources') {
+//        fileType CONFIG | NOREPLACE
+//                into 'conf'
+//    }
+//    from('home') {
+//        // Creating directory entries (or not) in the RPM is normally left up to redline-rpm library.
+//        // Use this to explicitly create an entry -- for setting directory fileMode on system directories.
+//        createDirectoryEntry = true
+//        fileMode = 0500
+//        into 'home'
+//    }
+//    from('endorsed') {
+//        // Will tell redline-rpm not to auto create directories, which
+//        // is sometimes necessary to avoid rpm directory conflicts
+//        addParentDirs = false
+//        into '/usr/share/tomcat/endorsed'
+//    }
+//
+//}
+//
+//buildRpm {
+//    requires('bar', '2.2', GREATER | EQUAL)
+//    requires('baz', '1.0.1', LESS)
+//    link('/etc/init.d/foo’, '/opt/foo/bin/foo.init')
+//}
+//
+//buildDeb {
+//    requires('bat', '1.0.1')
+//    link('/etc/init.d/foo', '/opt/foo/bin/foo.upstart')
+//}
+
 dependencies {
     implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
     implementation(gradleApi())
@@ -67,10 +129,11 @@ dependencies {
     implementation(libs.gradle.kotlin.plugin)
     implementation(libs.gradle.detekt.plugin)
     implementation(libs.gradle.dokka.plugin)
+//    implementation(libs.gradle.markdown.plugin)
     implementation(libs.gradle.kotlinter)
     implementation(libs.jsoup)
     implementation(libs.kotlin.stdlib.jdk8)
-    implementation(libs.detekt.formatting)
+//    implementation(libs.detekt.formatting)
     implementation(libs.poet.java)
     implementation(libs.poet.kotlin)
     implementation(libs.eclipse.jgit)

+ 0 - 2
buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/BuildTypeExt.kt

@@ -12,8 +12,6 @@ package org.mjdev.gradle.extensions
 
 import com.android.build.api.dsl.ApplicationVariantDimension
 
-typealias BuildTypeFnc = ApplicationVariantDimension.() -> Unit
-
 fun ApplicationVariantDimension.buildConfigBoolean(
     vararg fields: Pair<String, Boolean>
 ) {

+ 7 - 1
buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/HashMapExt.kt

@@ -16,4 +16,10 @@ fun <T, V> HashMap<T, V>.onKeyExists(
     if (this.containsKey(key)) {
         block.invoke(key, this[key])
     }
-}
+}
+
+fun <K, V> Map<K, V>.mapToString(): String = map { kv ->
+    val sdKey = kv.key.toString().replace("\"", "")
+    val sdValue = kv.value.toString().replace("\"", "")
+    "\t\"$sdKey\": \"$sdValue\""
+}.joinToString(",\n") + "\n"

+ 14 - 0
buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/ProjectExt.kt

@@ -35,6 +35,8 @@ import org.gradle.kotlin.dsl.withType
 import org.jetbrains.dokka.gradle.DokkaTask
 import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.mjdev.gradle.plugin.config.base.BuildConfigs
+//import org.kordamp.gradle.plugin.markdown.tasks.MarkdownToHtmlTask
 import java.io.File
 import java.io.FileInputStream
 import java.util.Properties
@@ -264,6 +266,10 @@ fun Project.dokkaTask(scoped: DokkaTask.() -> Unit) {
     tasks.withType<DokkaTask>().first().also { t -> scoped(t) }
 }
 
+//fun Project.markDownToHtmlTask(scoped: MarkdownToHtmlTask.() -> Unit) {
+//    tasks.withType<MarkdownToHtmlTask>().first().also { t -> scoped(t) }
+//}
+
 fun Project.registerSources(outDir: File, variant: BaseVariant) {
     if (outDir.absolutePath.contains("generated/source")) {
         project.kotlinExtension.sourceSets.findByName(variant.name)?.apply {
@@ -315,6 +321,14 @@ fun Project.fileTree(directory: String, inc: String, exc: String): List<File> {
 inline fun <reified T> Project.runConfigured(crossinline function: T.() -> Unit) {
     afterEvaluate {
         val config = project.extension<T>()
+        if (config is BuildConfigs) {
+//            project.androidExtension.buildTypes.forEach { bt ->
+//                println("> Configuring build : ${bt.name}")
+//                val btConfig = config.buildTypes[bt.name.lowercase()]
+//                println ("> Config: $btConfig")
+//                btConfig?.invoke(bt)
+//            }
+        }
         function(config)
     }
 }

+ 1 - 1
buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/TestedExtension.kt

@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) Milan Jurkulák 2023.
+ *  Copyright (c) Milan Jurkulák 2024.
  *  Contact:
  *  e: mimoccc@gmail.com
  *  e: mj@mjdev.org

+ 45 - 15
buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/AppPlugin.kt

@@ -13,6 +13,7 @@ import io.gitlab.arturbosch.detekt.DetektPlugin
 import io.gitlab.arturbosch.detekt.extensions.DetektExtension
 import org.gradle.api.JavaVersion
 import org.gradle.api.Project
+import org.gradle.api.artifacts.dsl.LockMode
 import org.gradle.kotlin.dsl.apply
 import org.gradle.kotlin.dsl.configure
 import org.gradle.kotlin.dsl.dependencies
@@ -52,9 +53,13 @@ import org.mjdev.gradle.extensions.runConfigured
 import org.mjdev.gradle.plugin.config.AppConfig
 import org.mjdev.gradle.tasks.ReleaseNotesCleanTask
 import org.mjdev.gradle.tasks.ReleaseNotesCreateTask
+import org.mjdev.gradle.tasks.WebServiceCreateTask
 import org.mjdev.gradle.tasks.ZipReleaseClearTask
 import org.mjdev.gradle.tasks.ZipReleaseCreateTask
 
+//import org.kordamp.gradle.plugin.markdown.MarkdownPlugin
+//import org.mjdev.gradle.extensions.markDownToHtmlTask
+
 @Suppress("UnstableApiUsage")
 class AppPlugin : BasePlugin() {
     private val configFieldName = "appConfig"
@@ -83,7 +88,6 @@ class AppPlugin : BasePlugin() {
     override fun Project.work() {
         extension<AppConfig>(configFieldName)
         loadPropertiesFile(versionPropertiesFile)
-//        apply(plugin = "version-catalog")
         apply(plugin = "com.android.application")
         apply(plugin = "kotlin-android")
         apply(plugin = "kotlin-kapt")
@@ -92,7 +96,8 @@ class AppPlugin : BasePlugin() {
         apply(plugin = "com.google.dagger.hilt.android")
         apply(plugin = "dagger.hilt.android.plugin")
         apply(plugin = "io.objectbox")
-        apply("org.jetbrains.dokka")
+        apply(plugin = "org.jetbrains.dokka")
+//        apply(MarkdownPlugin::class)
         apply(DetektPlugin::class)
         apply(KotlinterPlugin::class)
         registerTask<ReleaseNotesCleanTask> {
@@ -160,7 +165,9 @@ class AppPlugin : BasePlugin() {
                     isShrinkResources = false
                     isCrunchPngs = false
                     signingConfig = signingConfigs[name]
+                    // todo : move
                     stringRes("app_name", "TVApp-Debug")
+                    // todo : move
                     addSyncProviderAuthString(
                         "$applicationId$applicationIdSuffix",
                         "sync_auth",
@@ -245,6 +252,39 @@ class AppPlugin : BasePlugin() {
                 if (createDocumentation)
                     runAfterAssembleTask()
             }
+//            markDownToHtmlTask {
+//                sourceDir = rootDir
+//                outputDir = rootDir.resolve("web")
+//                hardwraps = true
+//                autoLinks = true
+//                abbreviations = true
+//                definitionLists = true
+//                smartQuotes = true
+//                smartPunctuation = true
+//                smart = true
+//                fencedCodeBlocks = true
+//                tables = true
+//                all = true
+//                removeHtml = true
+//                removeTables = true
+//                baseUri = ""
+//                customizePegdown = {}
+//                customizeRemark = {}
+//                if(createWebSiteFromGit) {
+//                    runAfterAssembleTask()
+//                }
+//            }
+            if (createWebApp) {
+                registerTask<WebServiceCreateTask> {
+                    domain = "localhost"
+                    servicePort = 2222
+                    serviceName = project.name
+                    serviceVersion = project.versionName
+                    serviceCompany = "mjdev"
+                    serviceLicense = ""
+                    runAfterCleanTask()
+                }
+            }
             val rnTask = registerTask<ReleaseNotesCreateTask> {
                 if (createReleaseNotes)
                     runAfterAssembleTask()
@@ -265,15 +305,6 @@ class AppPlugin : BasePlugin() {
                 ignoreFailures = ignoreCodeFailures
                 reporters = arrayOf("checkstyle", "plain")
             }
-// todo
-//            configurations.getByName("releaseRuntimeClasspath") {
-//                resolutionStrategy.activateDependencyLocking()
-//            }
-// todo
-//            configurations.getByName("debugRuntimeClasspath") {
-//                resolutionStrategy.activateDependencyLocking()
-//            }
-// todo
 //            if (appConfig.renameApkOutputByAppID) {
 //                applicationVariants.all {
 //                    outputs.map {
@@ -285,10 +316,9 @@ class AppPlugin : BasePlugin() {
 //                }
 //            }
         }
-// todo
-//        dependencyLocking {
-//            lockMode.set(LockMode.STRICT)
-//        }
+        dependencyLocking {
+            lockMode.set(LockMode.STRICT)
+        }
         dependencies {
             implementation(project(mapOf("path" to ":tvlib")))
             // compose

+ 12 - 17
buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/LibPlugin.kt

@@ -13,6 +13,7 @@ import io.gitlab.arturbosch.detekt.DetektPlugin
 import io.gitlab.arturbosch.detekt.extensions.DetektExtension
 import org.gradle.api.JavaVersion
 import org.gradle.api.Project
+import org.gradle.api.artifacts.dsl.LockMode
 import org.gradle.kotlin.dsl.apply
 import org.gradle.kotlin.dsl.configure
 import org.gradle.kotlin.dsl.dependencies
@@ -39,7 +40,6 @@ import org.mjdev.gradle.extensions.int
 import org.mjdev.gradle.extensions.string
 import org.mjdev.gradle.extensions.projectName
 import org.mjdev.gradle.extensions.runConfigured
-import org.mjdev.gradle.plugin.config.AppConfig
 
 @Suppress("UnstableApiUsage")
 class LibPlugin : BasePlugin() {
@@ -63,7 +63,6 @@ class LibPlugin : BasePlugin() {
 
     override fun Project.work() {
         extension<LibConfig>(configFieldName)
-        apply(plugin ="version-catalog")
         apply(plugin = "kotlin-android")
         apply(plugin = "com.android.library")
         apply(plugin = "kotlin-kapt")
@@ -86,11 +85,6 @@ class LibPlugin : BasePlugin() {
                 // todo : move
                 targetCompatibility = projectJavaVersion
             }
-            defaultConfig {
-                // todo : move
-                minSdk = libs.versions.minSdk.int
-                testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
-            }
             buildFeatures {
                 compose = true
                 buildConfig = true
@@ -99,6 +93,11 @@ class LibPlugin : BasePlugin() {
                 // todo : move
                 kotlinCompilerExtensionVersion = libs.versions.kotlin.compiler.version.string
             }
+            defaultConfig {
+                // todo : move
+                minSdk = libs.versions.minSdk.int
+                testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+            }
             packaging {
                 resources {
                     excludes.addAll(projectExcludes)
@@ -124,7 +123,9 @@ class LibPlugin : BasePlugin() {
                 }
             }
             sourceSets {
-                getByName("main") { jniLibs.srcDirs() }
+                getByName("main") {
+                    jniLibs.srcDirs()
+                }
             }
             lint {
                 checkAllWarnings = true
@@ -184,16 +185,10 @@ class LibPlugin : BasePlugin() {
                 ignoreFailures = ignoreCodeFailures
                 reporters = arrayOf("checkstyle", "plain")
             }
-//            configurations.getByName("releaseRuntimeClasspath") {
-//                resolutionStrategy.activateDependencyLocking()
-//            }
-//            configurations.getByName("debugRuntimeClasspath") {
-//                resolutionStrategy.activateDependencyLocking()
-//            }
         }
-//        dependencyLocking {
-//            lockMode.set(LockMode.STRICT)
-//        }
+        dependencyLocking {
+            lockMode.set(LockMode.STRICT)
+        }
         dependencies {
             // core
             implementation(libs.androidx.ktx)

+ 8 - 31
buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/config/AppConfig.kt

@@ -8,11 +8,10 @@
 
 package org.mjdev.gradle.plugin.config
 
-import org.mjdev.gradle.extensions.BuildTypeFnc
+import org.mjdev.gradle.plugin.config.base.BuildConfigs
 
-open class AppConfig {
-
-    private val buildTypes = mutableMapOf<String, BuildTypeFnc>()
+@Suppress("unused")
+open class AppConfig : BuildConfigs() {
 
     open var autoCorrectCode = false
     open var ignoreCodeFailures = true
@@ -21,37 +20,15 @@ open class AppConfig {
     open var failOnDocumentationWarning = false
     open var createReleaseNotes = false
     open var createZipRelease = false
-    open var renameApkOutputByAppID = false
 
-//    open var createInfoClass = false
-//    open var buildTypeInLauncherIcon = false
+    open var createWebApp = false
+    open var createWebSiteFromGit = false
+    open var renameApkOutputByAppID = false
+    open var createInfoClass = false
+    open var launcherIconByBuildType = false
 
     open var codeReportsDir = "reports/app"
     open var documentationDir = "documentation/app"
     open var detectConfigFile = "config/detekt.yml"
 
-    fun default (action:BuildTypeFnc) {
-        buildTypes.put("debug", action)
-    }
-
-    fun debug(action: BuildTypeFnc) {
-        buildTypes.put("debug", action)
-    }
-
-    fun release(action: BuildTypeFnc) {
-        buildTypes.put("release", action)
-    }
-
-    fun minified(action: BuildTypeFnc) {
-        buildTypes.put("minified", action)
-    }
-
-    fun test(action: BuildTypeFnc) {
-        buildTypes.put("test", action)
-    }
-
-    fun publish(action: BuildTypeFnc) {
-        buildTypes.put("test", action)
-    }
-
 }

+ 36 - 0
buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/config/base/BuilConfigs.kt

@@ -0,0 +1,36 @@
+/*
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package org.mjdev.gradle.plugin.config.base
+
+import com.android.build.api.dsl.BuildType
+
+typealias BuildTypeFnc = BuildType.() -> Unit
+
+@Suppress("MemberVisibilityCanBePrivate")
+abstract class BuildConfigs {
+
+    val buildTypes = mutableMapOf<String, BuildTypeFnc>()
+
+    fun create(name: String, action: BuildTypeFnc) {
+        buildTypes[name] = action
+    }
+
+    fun default(action: BuildTypeFnc) = create("default", action)
+
+    fun debug(action: BuildTypeFnc) = create("debug", action)
+
+    fun release(action: BuildTypeFnc) = create("release", action)
+
+    fun minified(action: BuildTypeFnc) = create("minified", action)
+
+    fun test(action: BuildTypeFnc) = create("test", action)
+
+    fun publish(action: BuildTypeFnc) = create("publish", action)
+
+}

+ 2 - 2
buildSrc/src/main/kotlin/org/mjdev/gradle/tasks/InAppInfoCreateTask.kt

@@ -10,7 +10,7 @@ package org.mjdev.gradle.tasks
 
 import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.TaskAction
-import org.mjdev.gradle.extensions.packageName
+import org.mjdev.gradle.extensions.applicationId
 import org.mjdev.gradle.extensions.createFile
 import org.mjdev.gradle.extensions.srcOutputDirectory
 import java.io.File
@@ -36,7 +36,7 @@ open class InAppInfoCreateTask : BaseTask() {
         registerSourceDirectory(srcOutputDir)
         createFile(
             name = "ApplicationInfo",
-            packageName = project.packageName
+            packageName = project.applicationId
         ) {
             createClass(
                 name = "ApplicationInfo"

+ 0 - 2
buildSrc/src/main/kotlin/org/mjdev/gradle/tasks/ReleaseNotesCleanTask.kt

@@ -10,7 +10,6 @@ package org.mjdev.gradle.tasks
 
 import org.gradle.api.tasks.TaskAction
 import org.mjdev.gradle.extensions.file
-import org.mjdev.gradle.extensions.projectName
 import org.mjdev.gradle.extensions.projectVersion
 import java.io.File
 import org.mjdev.gradle.base.BaseTask
@@ -36,7 +35,6 @@ open class ReleaseNotesCleanTask : BaseTask() {
 
     @TaskAction
     fun taskAction() {
-        println("> Clearing release notes") // todo
         outputFiles.forEach { file ->
             if (file.isDirectory) {
                 file.deleteRecursively()

+ 156 - 0
buildSrc/src/main/kotlin/org/mjdev/gradle/tasks/WebServiceCreateTask.kt

@@ -0,0 +1,156 @@
+/*
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package org.mjdev.gradle.tasks
+
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.TaskAction
+import org.mjdev.gradle.base.BaseTask
+import org.mjdev.gradle.extensions.file
+import org.mjdev.gradle.extensions.writeText
+import org.mjdev.gradle.extensions.mapToString
+import java.io.File
+import java.util.Date
+
+@Suppress("DEPRECATION")
+open class WebServiceCreateTask : BaseTask() {
+
+    @Input
+    var domain: String = "mjdev.org"
+
+    @Input
+    var servicePort: Int = 0
+
+    @Input
+    var serviceName: String = "tvapp"
+
+    @Input
+    var serviceVersion: String = "1.0.0"
+
+    @Input
+    val serviceAuthor: String = "Milan Jurkulák"
+
+    @Input
+    var serviceCompany: String = "mjdev"
+
+    @Input
+    var serviceLicense: String = ""
+
+    @Input
+    val serviceDescription: String = "$serviceName application website"
+
+    @Input
+    val serviceDependencies = mutableMapOf(
+        "ejs" to "^3.1.9",
+        "express" to "^4.18.2",
+        "express-static" to "^1.2.6",
+    )
+
+    @Input
+    val serviceDevDependencies = mutableMapOf(
+        "nodemon" to "^2.0.7",
+    )
+
+    private val letsEncryptDir = "/etc/letsencrypt/live"
+    private val fullchainPem = "fullchain.pem"
+    private val privkeyPem = "privkey.pem"
+    private val packageFileName = "package.json"
+    private val appName = "app"
+
+    private val systemdFile
+        get() = "$serviceName.service"
+    private val nginxFileName
+        get() = "$serviceName.nginx"
+    private val appFileName
+        get() = "$appName.js"
+    private val serviceUrl
+        get() = "http://localhost:$servicePort"
+    private val serverUrl
+        get() = "$serviceName.$domain"
+    private val sslCertificate
+        get() = "$letsEncryptDir/$serverUrl/$fullchainPem"
+    private val sslCertificateKey
+        get() = "$letsEncryptDir/$serverUrl/$privkeyPem"
+
+    private val webDir: File
+        get() = project.rootProject.file("web")
+
+    private val nginxFileContent
+        get() = "server {\n" +
+                "    server_name $serverUrl;\n" +
+                "    listen [::]:443 ssl;\n" +
+                "    listen 443 ssl;\n" +
+                "    ssl_certificate $sslCertificate;\n" +
+                "    ssl_certificate_key $sslCertificateKey;\n" +
+                "    include /etc/letsencrypt/options-ssl-nginx.conf;\n" +
+                "    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;\n" +
+                "    location / {\n" +
+                "        proxy_pass $serviceUrl;\n" +
+                "        proxy_http_version 1.1;\n" +
+                "        proxy_set_header Upgrade \$http_upgrade;\n" +
+                "        proxy_set_header Connection 'upgrade';\n" +
+                "        proxy_set_header Host \$host;\n" +
+                "        proxy_cache_bypass \$http_upgrade;\n" +
+                "    }\n" +
+                "}"
+
+    private val packageJsonFile
+        get() = "{\n" +
+                "  \"name\": \"$serviceName\",\n" +
+                "  \"version\": \"$serviceVersion\",\n" +
+                "  \"description\": \"$serviceDescription\",\n" +
+                "  \"main\": \"$appFileName\",\n" +
+                "  \"scripts\": {\n" +
+                "    \"start\": \"export PORT=$servicePort && node $appName\",\n" +
+                "    \"dev\": \"nodemon\",\n" +
+                "    \"build\": \"npm install && npm run build\",\n" +
+                "    \"serve\": \"node $appName\",\n" +
+                "    \"release\": \"npm install && npm run build && export PORT=$servicePort && node $appName\"\n" +
+                "  },\n" +
+                "  \"author\": \"$serviceAuthor $serviceCompany (c) ${Date().year}\",\n" +
+                "  \"license\": \"$serviceLicense\",\n" +
+                "  \"dependencies\": {\n" +
+                serviceDependencies.mapToString() +
+                "  },\n" +
+                "  \"devDependencies\": {\n" +
+                serviceDevDependencies.mapToString() +
+                "  }\n" +
+                "}"
+
+    private val systemdFileContent
+        get() = "[Unit]\n" +
+                "Description=$serviceName web service in node js\n" +
+                "After=syslog.target network.target remote-fs.target nss-lookup.target\n" +
+                "\n" +
+                "[Service]\n" +
+                "Type=simple\n" +
+                "PIDFile=/run/$serviceName.pid\n" +
+                "WorkingDirectory=/opt/services/$serviceName\n" +
+                "ExecStart=\"export PORT=$servicePort && npm run start &\"\n" +
+                "ExecReload=/bin/kill -s HUP \$MAINPID\n" +
+                "ExecStop=/bin/kill -s QUIT \$MAINPID\n" +
+                "PrivateTmp=true\n" +
+                "NonBlocking=true\n" +
+                "Restart=on-failure\n" +
+                "\n" +
+                "[Install]\n" +
+                "WantedBy=multi-user.target"
+
+    init {
+        group = "mjdev"
+        description = "This task create web page files, for the project."
+        outputs.upToDateWhen { false }
+    }
+
+    @TaskAction
+    fun taskAction() {
+        webDir.file(systemdFile).writeText(systemdFileContent)
+        webDir.file(nginxFileName).writeText(nginxFileContent)
+        webDir.file(packageFileName).writeText(packageJsonFile)
+    }
+}

+ 0 - 1
buildSrc/src/main/kotlin/org/mjdev/gradle/tasks/ZipReleaseClearTask.kt

@@ -25,7 +25,6 @@ open class ZipReleaseClearTask : BaseTask() {
 
     @TaskAction
     fun taskAction() {
-        println("> Clearing release zip archive") // todo
         outputFiles.forEach { file ->
             if (file.isDirectory) {
                 file.deleteRecursively()

+ 93 - 71
config/detekt.yml

@@ -17,13 +17,13 @@ processors:
   active: true
   exclude:
     - 'DetektProgressListener'
-  # - 'KtFileCountProcessor'
-  # - 'PackageCountProcessor'
-  # - 'ClassCountProcessor'
-  # - 'FunctionCountProcessor'
-  # - 'PropertyCountProcessor'
-  # - 'ProjectComplexityProcessor'
-  # - 'ProjectCognitiveComplexityProcessor'
+    - 'KtFileCountProcessor'
+    - 'PackageCountProcessor'
+    - 'ClassCountProcessor'
+    - 'FunctionCountProcessor'
+    - 'PropertyCountProcessor'
+    - 'ProjectComplexityProcessor'
+    - 'ProjectCognitiveComplexityProcessor'
   # - 'ProjectLLOCProcessor'
   # - 'ProjectCLOCProcessor'
   # - 'ProjectLOCProcessor'
@@ -34,19 +34,19 @@ console-reports:
   active: true
   exclude:
     - 'ProjectStatisticsReport'
-    - 'ComplexityReport'
+#    - 'ComplexityReport'
     - 'NotificationReport'
     - 'FindingsReport'
     - 'FileBasedFindingsReport'
-  #  - 'LiteFindingsReport'
+    - 'LiteFindingsReport'
 
 output-reports:
   active: true
   exclude:
-  # - 'TxtOutputReport'
-  # - 'XmlOutputReport'
-  # - 'HtmlOutputReport'
-  # - 'MdOutputReport'
+    - 'TxtOutputReport'
+    - 'XmlOutputReport'
+    - 'HtmlOutputReport'
+    #- 'MdOutputReport'
 
 comments:
   active: true
@@ -95,22 +95,22 @@ complexity:
     threshold: 20
     includeStaticDeclarations: false
     includePrivateDeclarations: false
-  ComplexMethod:
-    active: true
-    threshold: 30
-    ignoreSingleWhenExpression: false
-    ignoreSimpleWhenEntries: false
-    ignoreNestingFunctions: false
-    nestingFunctions:
-      - 'also'
-      - 'apply'
-      - 'forEach'
-      - 'isNotNull'
-      - 'ifNull'
-      - 'let'
-      - 'run'
-      - 'use'
-      - 'with'
+#  ComplexMethod:
+#    active: true
+#    threshold: 30
+#    ignoreSingleWhenExpression: false
+#    ignoreSimpleWhenEntries: false
+#    ignoreNestingFunctions: false
+#    nestingFunctions:
+#      - 'also'
+#      - 'apply'
+#      - 'forEach'
+#      - 'isNotNull'
+#      - 'ifNull'
+#      - 'let'
+#      - 'run'
+#      - 'use'
+#      - 'with'
   LabeledExpression:
     active: true
     ignoredLabels: []
@@ -165,7 +165,7 @@ complexity:
     thresholdInEnums: 35
     ignoreDeprecated: false
     ignorePrivate: false
-    ignoreOverridden: false
+#    ignoreOverridden: false
 
 coroutines:
   active: true
@@ -179,6 +179,7 @@ coroutines:
       - 'Unconfined'
   RedundantSuspendModifier:
     active: true
+    autoCorrect: true
   SleepInsteadOfDelay:
     active: true
   SuspendFunWithCoroutineScopeReceiver:
@@ -205,7 +206,7 @@ empty-blocks:
     active: true
   EmptyFunctionBlock:
     active: true
-    ignoreOverridden: false
+#    ignoreOverridden: false
   EmptyIfBlock:
     active: true
   EmptyInitBlock:
@@ -298,7 +299,7 @@ naming:
   BooleanPropertyNaming:
     active: true
     allowedPattern: '^(is|has|are)'
-    ignoreOverridden: true
+#    ignoreOverridden: true
   ClassNaming:
     active: true
     classPattern: '[A-Z][a-zA-Z0-9]*'
@@ -307,7 +308,7 @@ naming:
     parameterPattern: '[a-z][A-Za-z0-9]*'
     privateParameterPattern: '[a-z][A-Za-z0-9]*'
     excludeClassPattern: '$^'
-    ignoreOverridden: true
+#    ignoreOverridden: true
   EnumNaming:
     active: true
     enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
@@ -326,12 +327,12 @@ naming:
     excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
     functionPattern: '[a-zA-Z][a-zA-Z0-9]*' # Non-default for Jetpack Compose
     excludeClassPattern: '$^'
-    ignoreOverridden: true
+#    ignoreOverridden: true
   FunctionParameterNaming:
     active: true
     parameterPattern: '[a-z][A-Za-z0-9]*'
     excludeClassPattern: '$^'
-    ignoreOverridden: true
+#    ignoreOverridden: true
   InvalidPackageDeclaration:
     active: true
     rootPackage: ''
@@ -344,7 +345,7 @@ naming:
     mustBeFirst: true
   MemberNameEqualsClassName:
     active: true
-    ignoreOverridden: true
+#    ignoreOverridden: true
   NoNameShadowing:
     active: true
   NonBooleanPropertyPrefixedWithIs:
@@ -373,7 +374,7 @@ naming:
     variablePattern: '[a-z][A-Za-z0-9]*'
     privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
     excludeClassPattern: '$^'
-    ignoreOverridden: true
+#    ignoreOverridden: true
 
 performance:
   active: true
@@ -381,6 +382,7 @@ performance:
     active: true
   CouldBeSequence:
     active: true
+    autoCorrect: true
     threshold: 3
   ForEachOnRange:
     active: true
@@ -414,8 +416,8 @@ potential-bugs:
       - 'java.util.HashSet'
       - 'java.util.LinkedHashMap'
       - 'java.util.HashMap'
-  DuplicateCaseInWhenExpression:
-    active: true
+#  DuplicateCaseInWhenExpression:
+#    active: true
   ElseCaseInsteadOfExhaustiveWhen:
     active: true
   EqualsAlwaysReturnsTrueOrFalse:
@@ -430,7 +432,8 @@ potential-bugs:
     active: true
   IgnoredReturnValue:
     active: true
-    restrictToAnnotatedMethods: true
+#    restrictToAnnotatedMethods: true
+    restrictToConfig: true
     returnValueAnnotations:
       - '*.CheckResult'
       - '*.CheckReturnValue'
@@ -457,15 +460,15 @@ potential-bugs:
   MissingPackageDeclaration:
     active: true
     excludes: ['**/*.kts']
-  MissingWhenCase:
-    active: true
-    allowElseExpression: true
+#  MissingWhenCase:
+#    active: true
+#    allowElseExpression: true
   NullCheckOnMutableProperty:
     active: true
   NullableToStringCall:
     active: true
-  RedundantElseInWhen:
-    active: true
+#  RedundantElseInWhen:
+#    active: true
   UnconditionalJumpStatementInLoop:
     active: true
   UnnecessaryNotNullOperator:
@@ -492,6 +495,7 @@ style:
   active: true
   CanBeNonNullable:
     active: true
+    autoCorrect: true
   CascadingCallWrapping:
     active: true
     includeElvis: true
@@ -501,9 +505,11 @@ style:
     active: true
   DataClassContainsFunctions:
     active: true
-    conversionFunctionPrefix: 'to'
+    conversionFunctionPrefix:
+      - 'to'
   DataClassShouldBeImmutable:
     active: true
+    autoCorrect: true
   DestructuringDeclarationWithTooManyEntries:
     active: true
     maxDestructuringEntries: 3
@@ -520,12 +526,12 @@ style:
     includeLineWrapping: false
   ForbiddenComment:
     active: true
-    values:
-      - 'FIXME:'
-      - 'STOPSHIP:'
-      - 'TODO:'
+#    values:
+#      - 'FIXME:'
+#      - 'STOPSHIP:'
+#      - 'TODO:'
     allowedPatterns: ''
-    customMessage: ''
+#    customMessage: ''
   ForbiddenImport:
     active: true
     imports: []
@@ -535,30 +541,33 @@ style:
     methods:
       - 'kotlin.io.print'
       - 'kotlin.io.println'
-  ForbiddenPublicDataClass:
-    active: true
-    excludes: ['**']
-    ignorePackages:
-      - '*.internal'
-      - '*.internal.*'
+#  ForbiddenPublicDataClass:
+#    active: true
+#    excludes: ['**']
+#    ignorePackages:
+#      - '*.internal'
+#      - '*.internal.*'
   ForbiddenSuppress:
     active: true
     rules: []
   ForbiddenVoid:
     active: true
-    ignoreOverridden: false
+#    ignoreOverridden: false
     ignoreUsageInGenerics: false
   FunctionOnlyReturningConstant:
     active: true
     ignoreOverridableFunction: true
     ignoreActualFunction: true
-    excludedFunctions: ''
-  LibraryCodeMustSpecifyReturnType:
-    active: true
-    excludes: ['**']
-  LibraryEntitiesShouldNotBePublic:
-    active: true
-    excludes: ['**']
+    excludedFunctions:
+      - ''
+#  LibraryCodeMustSpecifyReturnType:
+#    active: true
+#    autoCorrect: true
+#    excludes: ['**']
+#  LibraryEntitiesShouldNotBePublic:
+#    active: true
+#    autoCorrect: true
+#    excludes: ['**']
   LoopWithTooManyJumpStatements:
     active: true
     maxJumpCount: 1
@@ -580,8 +589,9 @@ style:
     ignoreEnums: false
     ignoreRanges: false
     ignoreExtensionFunctions: true
-  MandatoryBracesIfStatements:
-    active: true
+#  MandatoryBracesIfStatements:
+#    active: true
+#    autoCorrect: true
   MandatoryBracesLoops:
     active: true
   MaxChainedCallsOnSameLine:
@@ -595,6 +605,7 @@ style:
     excludeCommentStatements: false
   MayBeConst:
     active: true
+    autoCorrect: true
   ModifierOrder:
     active: true
   MultilineLambdaItParameter:
@@ -603,8 +614,10 @@ style:
     active: true
   NewLineAtEndOfFile:
     active: true
+    autoCorrect: true
   NoTabs:
     active: true
+    autoCorrect: true
   NullableBooleanCheck:
     active: true
   ObjectLiteralToLambda:
@@ -613,8 +626,8 @@ style:
     active: true
   OptionalUnit:
     active: true
-  OptionalWhenBraces:
-    active: false # Disabled because I personally feel braces helps readability
+#  OptionalWhenBraces:
+#    active: false # Disabled because I personally feel braces helps readability
   PreferToOverPairSyntax:
     active: true
   ProtectedMemberInFinalClass:
@@ -625,10 +638,12 @@ style:
     active: true
   RedundantVisibilityModifierRule:
     active: true
+    autoCorrect: true
   ReturnCount:
     active: true
     max: 3
-    excludedFunctions: 'equals'
+    excludedFunctions:
+      - 'equals'
     excludeLabeled: false
     excludeReturnFromLambda: true
     excludeGuardClauses: false
@@ -666,10 +681,12 @@ style:
     active: true
   UnnecessaryParentheses:
     active: true
+    autoCorrect: true
   UntilInsteadOfRangeTo:
     active: true
   UnusedImports:
     active: true
+    autoCorrect: true
   UnusedPrivateClass:
     active: true
   UnusedPrivateMember:
@@ -692,10 +709,13 @@ style:
     active: true
   UseIfInsteadOfWhen:
     active: true
+    autoCorrect: true
   UseIsNullOrEmpty:
     active: true
+    autoCorrect: true
   UseOrEmpty:
     active: true
+    autoCorrect: true
   UseRequire:
     active: true
   UseRequireNotNull:
@@ -706,9 +726,11 @@ style:
     active: true
   VarCouldBeVal:
     active: true
-    ignoreLateinitVar: false
+    autoCorrect: true
+    ignoreLateinitVar: true
   WildcardImport:
     active: true
+    autoCorrect: true
     excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
     excludeImports:
       - 'java.util.*'

+ 14 - 5
gradle/libs.versions.toml

@@ -1,7 +1,9 @@
 [versions]
 app-namespace = "org.mjdev.tvapp"
+gradleOspackagePlugin = "8.4.1"
 lib-namespace = "org.mjdev.tvlib"
 #
+markdownGradlePlugin = "2.2.0"
 minSdk = "21"
 compileSdk = "34"
 targetSdk = "34"
@@ -56,7 +58,7 @@ kotlin-gradle-plugin = "1.9.22"
 kotlin-stdlib-jdk8 = "1.9.22"
 kotlinpoet = "1.13.2"
 kotlinter-gradle = "3.16.0"
-ktoml= "0.5.1"
+ktoml = "0.5.1"
 mockito-kotlin = "5.2.1"
 moshi-kotlin = "1.12.0"
 objectbox-gradle-plugin = "3.7.1"
@@ -166,9 +168,10 @@ dagger-android-support = { module = "com.google.dagger:dagger-android-support",
 dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" }
 dagger-hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "dagger" }
 dagger-hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "dagger" }
+# ktoml
+#ktoml-core = { module = "com.akuleshov7:ktoml-core", version.ref = "ktoml" }
+#ktoml-file = { module = "com.akuleshov7:ktoml-file", version.ref = "ktoml" }
 # moshi
-ktoml-core = { module = "com.akuleshov7:ktoml-core", version.ref = "ktoml" }
-ktoml-file = { module = "com.akuleshov7:ktoml-file", version.ref = "ktoml" }
 moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
 moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi-kotlin" }
 moshi-kotlin-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }
@@ -179,14 +182,14 @@ landscapist-palette = { module = "com.github.skydoves:landscapist-palette", vers
 landscapist-placeholder = { module = "com.github.skydoves:landscapist-placeholder", version.ref = "landscapist-glide" }
 landscapist-transformation = { module = "com.github.skydoves:landscapist-transformation", version.ref = "landscapist-glide" }
 # okhttp
-okhttp3 = { group = "com.squareup.okhttp3", name = "okhttp" , version.ref = "okhttp" }
+okhttp3 = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
 okhttp3-bom = { group = "com.squareup.okhttp3", name = "okhttp-bom", version.ref = "okhttp3-bom" }
 okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp3-logging-interceptor" }
 #
 dependency-analysis-gradle-plugin = { module = "gradle.plugin.com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependency-analysis-gradle-plugin" }
 detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt-formatting" }
 google-api-services-androidpublisher = { module = "com.google.apis:google-api-services-androidpublisher", version.ref = "google-api-services-androidpublisher" }
-poet-java= { module = "com.squareup:javapoet", version.ref = "javapoet" }
+poet-java = { module = "com.squareup:javapoet", version.ref = "javapoet" }
 poet-kotlin = { module = "com.squareup:kotlinpoet", version.ref = "kotlinpoet" }
 korim-jvm = { module = "com.soywiz.korlibs.korim:korim-jvm", version.ref = "korim-jvm" }
 eclipse-jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version.ref = "org-eclipse-jgit" }
@@ -210,6 +213,8 @@ gradle-detekt-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plu
 gradle-paparazzi-plugin = { module = "app.cash.paparazzi:paparazzi-gradle-plugin", version.ref = "paparazzi-gradle-plugin" }
 gradle-kotlinter = { module = "org.jmailen.gradle:kotlinter-gradle", version.ref = "kotlinter-gradle" }
 gradle-dokka-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka-gradle-plugin" }
+gradle-markdown-plugin = { module = "org.kordamp.gradle:markdown-gradle-plugin", version.ref = "markdownGradlePlugin" }
+gradle-ospackage-plugin = { module = "com.netflix.nebula:gradle-ospackage-plugin", version.ref = "gradleOspackagePlugin" }
 #classpath("org.jetbrains.kotlin:kotlin-scripting-jsr223:1.9.22")
 #classpath("org.jetbrains.kotlin:kotlin-scripting-common:1.9.22")
 #classpath("org.jetbrains.kotlin:kotlin-compiler-embeddable:1.9.22")
@@ -226,5 +231,9 @@ android-library = { id = "com.android.library", version = "8.3.2" }
 android-dagger-hilt = { id = "com.google.dagger.hilt.android", version = "2.47" }
 devtools-google-ksp = { id = "com.google.devtools.ksp", version = "1.9.22-1.0.17" }
 gradle-ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "11.3.2" }
+gradle-markdown = { id = "org.kordamp.gradle.markdown", version = "2.2.0" }
+gradle-os-packages = { id = "nebula.ospackage", version = "9.1.1" }
+gradle-versions = { id = "com.github.ben-manes.versions", version = "0.41.0" }
+gradle-catalogs-update = { id = "nl.littlerobots.version-catalog-update", version = "0.8.4" }
 # todo ktorfit https://foso.github.io/Ktorfit/
 gradle-ktorfit = { id = "de.jensklingenberg.ktorfit", version = "1.10.2" }

+ 2 - 2
tvlib/build.gradle.kts

@@ -11,7 +11,7 @@ plugins {
 }
 
 libConfig {
-    autoCorrectCode = false
-    createDocumentation = false
+    autoCorrectCode = true
+    createDocumentation = true
     minify = false
 }

+ 0 - 0
web/.nomedia


+ 0 - 10
web/index.html

@@ -1,10 +0,0 @@
-<!--
-  ~  Copyright (c) Milan Jurkulák 2024.
-  ~  Contact:
-  ~  e: mimoccc@gmail.com
-  ~  e: mj@mjdev.org
-  ~  w: https://mjdev.org
-  -->
-
-<html>
-</html>

+ 17 - 0
web/old/app.js

@@ -0,0 +1,17 @@
+//--------------------------------------------------------------------------------------------------
+// base service app
+//--------------------------------------------------------------------------------------------------
+let path = require('path');
+let express = require('express');
+let serve   = require('express-static');
+//--------------------------------------------------------------------------------------------------
+let server_port = process.env.PORT | 8888;
+let app = express();
+//--------------------------------------------------------------------------------------------------
+// static content
+//--------------------------------------------------------------------------------------------------
+app.use(serve(__dirname + '/public'));
+app.listen(server_port, () => { 
+   console.log('App listening on port ' + server_port + '.');
+});
+//--------------------------------------------------------------------------------------------------

+ 44 - 0
web/old/install

@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+#  Copyright (c) Milan Jurkulák 2024.
+#  Contact:
+#  e: mimoccc@gmail.com
+#  e: mj@mjdev.org
+#  w: https://mjdev.org
+#
+service_name="app"
+# store actual dir
+pwd=$(pwd)
+# sw needed
+sudo apt-get install certbot nginx-full nodejs npm ufw yarn
+# stop service
+sudo systemctl stop "service-$service_name"
+sudo systemctl disable "service-$service_name"
+# remove old
+sudo rm -rf "/etc/systemd/system/service-$service_name.service"
+sudo rm -rf "/etc/nginx/sites-available/service-$service_name.nginx"
+sudo rm -rf "/etc/nginx/sites-enabled/service-$service_name.nginx"
+# reload services
+sudo systemctl daemon-reload
+sudo systemctl reload nginx
+# copy things
+sudo mkdir -p "/opt/services/$service_name"
+sudo cp -rf * "/opt/services/$service_name"
+# link things
+sudo ln -s "/opt/services/$service_name/$service_name.service" "/etc/systemd/system/service-$service_name.service"
+sudo ln -s "/opt/services/$service_name/$service_name.nginx" "/etc/nginx/sites-available/service-$service_name.nginx"
+sudo ln -s "/opt/services/$service_name/$service_name.nginx" "/etc/nginx/sites-enabled/service-$service_name.nginx"
+# go to directory
+cd "/opt/services/$service_name" || exit
+# build
+sudo npm install
+sudo npm audit fix
+sudo npm upgrade
+#sudo npm run build
+# go back
+cd "$pwd" || exit
+# start services
+sudo systemctl daemon-reload
+sudo systemctl enable "service-$service_name"
+sudo systemctl start "service-$service_name"
+sudo systemctl reload nginx

BIN
web/old/public/favicon.png


+ 83 - 0
web/old/public/index.html

@@ -0,0 +1,83 @@
+<html lang="en">
+<head>
+    <title>mjdev - software alive</title>
+    <link rel="shortcut icon" href="favicon.png">
+    <style>
+        body {
+          background : #282a42;
+          color      : #d1d1e6;
+          padding    : 0;
+          margin     : 0;
+        }
+        div.logo {
+          margin : 24px;
+          height : 26px;
+        }
+        div.left_menu {
+          display    : block;
+          float      : left;
+          width      : 256px;
+          height     : 100%;
+        }
+        div.bottom {
+          position: absolute;
+          bottom : 24px;
+          left : 24px;
+        }
+        .logo_text {
+          margin: 0;
+          letter-spacing: 0.15px;
+          font-family: Sans-Serif,Roboto,Arial;
+          font-weight: 500;
+          font-size: 1.125rem;
+          line-height: 1.6;
+          color: rgba(234, 234, 255, 0.87);
+          margin-left: 0.5rem;
+          line-height: 1;
+          font-weight: 700;
+          font-size: 1.5rem !important;
+        }
+    </style>
+</head>
+<body>
+<div class="left_menu">
+    <div class="logo">
+        <h6 class="logo_text">
+            <svg width="47" fill="none" height="26" viewBox="0 0 268 150"
+                 xmlns="http://www.w3.org/2000/svg">
+                <rect rx="25.1443" width="50.2886" height="143.953" fill="#666CFF"
+                      transform="matrix(-0.865206 0.501417 0.498585 0.866841 195.571 0)"></rect>
+                <rect rx="25.1443" width="50.2886" height="143.953" fill-opacity="0.4"
+                      fill="url(#paint0_linear_7821_79167)"
+                      transform="matrix(-0.865206 0.501417 0.498585 0.866841 196.084 0)"></rect>
+                <rect rx="25.1443" width="50.2886" height="143.953" fill="#666CFF"
+                      transform="matrix(0.865206 0.501417 -0.498585 0.866841 173.147 0)"></rect>
+                <rect rx="25.1443" width="50.2886" height="143.953" fill="#666CFF"
+                      transform="matrix(-0.865206 0.501417 0.498585 0.866841 94.1973 0)"></rect>
+                <rect rx="25.1443" width="50.2886" height="143.953" fill-opacity="0.4"
+                      fill="url(#paint1_linear_7821_79167)"
+                      transform="matrix(-0.865206 0.501417 0.498585 0.866841 94.1973 0)"></rect>
+                <rect rx="25.1443" width="50.2886" height="143.953" fill="#666CFF"
+                      transform="matrix(0.865206 0.501417 -0.498585 0.866841 71.7728 0)"></rect>
+                <defs>
+                    <linearGradient y1="0" x1="25.1443" x2="25.1443" y2="143.953"
+                                    id="paint0_linear_7821_79167" gradientUnits="userSpaceOnUse">
+                        <stop></stop>
+                        <stop offset="1" stop-opacity="0"></stop>
+                    </linearGradient>
+                    <linearGradient y1="0" x1="25.1443" x2="25.1443" y2="143.953"
+                                    id="paint1_linear_7821_79167" gradientUnits="userSpaceOnUse">
+                        <stop></stop>
+                        <stop offset="1" stop-opacity="0"></stop>
+                    </linearGradient>
+                </defs>
+            </svg>
+            mjdev
+        </h6>
+    </div>
+</div>
+<div class="bottom">
+    <h6 class="logo_text"></h6>
+</div>
+</body>
+</html>

+ 19 - 0
web/old/uninstall

@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+#  Copyright (c) Milan Jurkulák 2024.
+#  Contact:
+#  e: mimoccc@gmail.com
+#  e: mj@mjdev.org
+#  w: https://mjdev.org
+#
+service_name="app"
+# stop service
+sudo systemctl stop "service-$service_name"
+sudo systemctl disable "service-$service_name"
+# remove old
+sudo rm -rf "/etc/systemd/system/service-$service_name.service"
+sudo rm -rf "/etc/nginx/sites-available/service-$service_name.nginx"
+sudo rm -rf "/etc/nginx/sites-enabled/service-$service_name.nginx"
+# reload services
+sudo systemctl daemon-reload
+sudo systemctl reload nginx