Skip to content

Commit

Permalink
finish extension
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed Nov 27, 2023
1 parent d402893 commit abac0ae
Show file tree
Hide file tree
Showing 10 changed files with 940 additions and 44 deletions.
9 changes: 9 additions & 0 deletions extension/background/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ kotlin {
implementation(parent?.project("base") ?: rootProject.project("extension:base"))
implementation(rootProject.project("model"))
implementation(rootProject.project("network"))

implementation(libs.ktor)
implementation(libs.ktor.js)
implementation(libs.ktor.content.negotiation)
implementation(libs.ktor.serialization.json)
}
}
}

tasks.build {
dependsOn(tasks.generateSekret)
}
24 changes: 15 additions & 9 deletions extension/background/sekret/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
plugins {
alias(libs.plugins.multiplatform)
alias(libs.plugins.multiplatform)
}
kotlin {
js(IR)
applyDefaultHierarchyTemplate()
sourceSets {
val commonMain by getting {
dependencies {
api("dev.datlag.sekret:sekret:0.2.0")
}
js(IR) {
binaries.executable()
browser()
nodejs()
}

applyDefaultHierarchyTemplate()

sourceSets {
val commonMain by getting {
dependencies {
api(libs.sekret)
}
}
}
}
}
82 changes: 75 additions & 7 deletions extension/background/src/jsMain/kotlin/background.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
import common.isNullOrEmpty
import de.jensklingenberg.ktorfit.ktorfitBuilder
import dev.datlag.burningseries.Sekret
import dev.datlag.burningseries.model.ExtensionMessage
import dev.datlag.burningseries.model.HosterScraping
import dev.datlag.burningseries.model.common.scopeCatching
import dev.datlag.burningseries.model.state.SaveAction
import dev.datlag.burningseries.model.state.SaveState
import dev.datlag.burningseries.network.Firestore
import dev.datlag.burningseries.network.JsonBase
import dev.datlag.burningseries.network.realm.RealmLoader
import dev.datlag.burningseries.network.state.SaveStateMachine
import dev.gitlive.firebase.Firebase
import dev.gitlive.firebase.FirebaseOptions
import dev.gitlive.firebase.firestore.firestore
import dev.gitlive.firebase.initialize
import io.ktor.client.*
import io.ktor.client.engine.js.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.promise
import kotlinx.serialization.json.Json

Expand All @@ -10,6 +29,47 @@ fun main() {
ignoreUnknownKeys = true
isLenient = true
}
val client = HttpClient(Js) {
developmentMode = false
followRedirects = true

install(ContentNegotiation) {
json(defaultJson)
}
}
val packageName = "dev.datlag.burningseries"
val ktorfit = ktorfitBuilder {
httpClient(client)
}
val jsonBaseKtor = ktorfit.build {
baseUrl("https://jsonbase.com/")
}
val firebaseKtor = ktorfit.build {
baseUrl("https://firestore.googleapis.com/v1/projects/${Sekret.firebaseProject(packageName)}/")
}
val app = Firebase.initialize(
context = null,
options = FirebaseOptions(
applicationId = Sekret.firebaseApplication(packageName),
apiKey = Sekret.firebaseApiKey(packageName),
projectId = Sekret.firebaseProject(packageName)
)
)
val store = Firebase.firestore(app)

val jsonBase = jsonBaseKtor.create<JsonBase>()
val firebase = firebaseKtor.create<Firestore>()

val activator = SaveStateMachine(
client = client,
jsonBase = jsonBase,
realmLoader = RealmLoader,
firestore = store,
firestoreApi = firebase
)
val activatorState = activator.state.flowOn(Dispatchers.Default).shareIn(GlobalScope, SharingStarted.Eagerly)

val memorySaveState = mutableMapOf<String, Boolean>()

browser.runtime.onMessage.addListener {
val msg = if (it.message.isNullOrEmpty()) {
Expand All @@ -33,15 +93,23 @@ fun main() {
}

return@addListener GlobalScope.promise {
if (message!!.set) {
return@promise save(message)
if (message!!.set && !message.url.isNullOrBlank()) {
activator.dispatch(SaveAction.Save(
HosterScraping(
href = message.href,
url = message.url!!
),
loadStream = false
))
val result = activatorState.first { state ->
state is SaveState.Success || state is SaveState.Error
}
return@promise (result is SaveState.Success).also { saved ->
memorySaveState[message.href] = saved
}
} else {
return@promise false
return@promise memorySaveState[message.href] ?: false
}
}
}
}

private suspend fun save(message: ExtensionMessage): Boolean {
return true
}
39 changes: 39 additions & 0 deletions extension/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,15 @@ kotlin {
tasks {
val extensionFolder = File(rootProject.layout.buildDirectory.get().asFile, "extension")
val resourcesFolder = File(projectDir, "src/jsMain/resources")
val releaseFolder = File(rootProject.layout.buildDirectory.get().asFile, "release/main/extension")
val commonShared = File(rootProject.project("app").project("shared").projectDir, "src/commonMain")
val iconsFolder = File(commonShared, "resources/MR/assets/png")
var firefox = false

val buildAndCopy = register("buildAndCopy") {
dependsOn(
project("content").tasks.build,
project("background").tasks.build,
assemble
)

Expand All @@ -45,6 +50,11 @@ tasks {
copy {
from(File(resourcesFolder, "manifest.json"))
into(extensionFolder)

from(iconsFolder) {
include("launcher_*.png")
into("icons")
}
}

copy {
Expand All @@ -66,11 +76,40 @@ tasks {
if (json.containsKey("version")) {
json["version"] = version
}
if (firefox) {
json["background"] = mapOf("scripts" to listOf("background.js"))
json["browser_specific_settings"] = mapOf("gecko" to mapOf("id" to "[email protected]"))

val permissions = ((json["permissions"] as? List<String>) ?: listOf("storage")).toMutableList()
permissions.remove("background")
permissions.add("activeTab")
json["permissions"] = permissions
}

File(extensionFolder, "manifest.json").writeText(JsonBuilder(json).toPrettyString())
}
}
}

register<Zip>("packChromium") {
firefox = false
dependsOn(buildAndCopy)

mkdir(releaseFolder)
from(extensionFolder)
archiveBaseName.set("Chromium-$version")
destinationDirectory.set(releaseFolder)
}
register<Zip>("packFirefox") {
firefox = true
dependsOn(buildAndCopy)

mkdir(releaseFolder)
from(extensionFolder)
archiveBaseName.set("Firefox-$version")
archiveExtension.set("xpi")
destinationDirectory.set(releaseFolder)
}
}

fun jsonObjectAsMap(data: Any?): Map<String, Any>? {
Expand Down
84 changes: 71 additions & 13 deletions extension/content/src/jsMain/kotlin/content.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,59 @@ fun main() {

if (document.getElementsByClassName("episodes").length <= 0) {
checkEpisode(href)
} else {
checkSeason(href)
}
}
}

private fun checkSeason(docHref: String) {
fun checkHoster(hoster: Element) {
hoster.getAttribute("href")?.let { href ->
entryExists(href) { exists ->
if (exists) {
val (background, content) = getThemeColor(docHref, href)
val style = buildString {
append("background-color: ${background.asHexColor()};")
append("color: ${content.asHexColor()};")
append("border: 3px solid ${background.asHexColor()};")
append("border-radius: 3px;")
}
hoster.setAttribute("style", style)
}
}
}
}

val episodes = document.getElementsByClassName("episodes")[0]?.getElementsByTagName("tr")
episodes.forEachNotNull { episode ->
val hosters = episode.getElementsByTagName("td")[2]?.getElementsByTagName("a")
hosters.forEachNotNull { hoster ->
checkHoster(hoster)
}
}
}

@OptIn(ExperimentalStdlibApi::class)
private fun checkEpisode(docHref: String) {
fun hosterApplyTheme(hoster: Element, href: String) {
val (background, content) = getThemeColor(docHref, href)
hoster.setAttribute("style", "background-color: ${background.asHexColor()}; color: ${content.asHexColor()}")
}

fun checkHoster(hoster: Element) {
if (hoster.hasClass("active")) {
return
}
hoster.getElementsByTagName("a")[0]?.getAttribute("href")?.let { href ->
entryExists(href) { exists ->
if (exists) {
hosterApplyTheme(hoster, href)
}
}
}
}

fun observeActivation(hoster: Element) {
if (!hoster.hasClass("active")) {
return
Expand Down Expand Up @@ -70,19 +117,7 @@ private fun checkEpisode(docHref: String) {
if (!url.isNullOrEmpty() && !href.isNullOrEmpty()) {
entrySave(href!!, url!!) { saved ->
if (saved) {
val isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches
val (fallbackBackground, fallbackContent) = if (isDarkMode) {
0xFF00497f.toInt() to 0xFFd2e4ff.toInt()
} else {
0xFFd2e4ff.toInt() to 0xFF001c37.toInt()
}
val scheme = themes[docHref]?.getScheme() ?: themes[href]?.getScheme()
val (background, content) = run {
(scheme?.primaryContainer ?: fallbackBackground) to (scheme?.onPrimaryContainer ?: fallbackContent)
}


hoster.setAttribute("style", "background-color: ${background.asHexColor()}; color: ${content.asHexColor()}")
hosterApplyTheme(hoster, href)
}
}
}
Expand All @@ -108,6 +143,7 @@ private fun checkEpisode(docHref: String) {
hosterArea.forEachNotNull { area ->
val hosters = area.getElementsByTagName("li")
hosters.forEachNotNull { hoster ->
checkHoster(hoster)
observeActivation(hoster)
}
}
Expand All @@ -127,6 +163,28 @@ private fun entrySave(href: String, url: String, callback: (Boolean) -> Unit) {
}
}

private fun entryExists(href: String, callback: (Boolean) -> Unit) {
browser.runtime.sendMessage(
message = json.encodeToString(ExtensionMessage(
set = false,
href = href
))
).collect {
callback((it as? Boolean) ?: false)
}
}

private fun getThemeColor(docHref: String, href: String): Pair<Int, Int> {
val isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches
val (fallbackBackground, fallbackContent) = if (isDarkMode) {
0xFF00497f.toInt() to 0xFFd2e4ff.toInt()
} else {
0xFFd2e4ff.toInt() to 0xFF001c37.toInt()
}
val scheme = themes[docHref]?.getScheme() ?: themes[href]?.getScheme()
return (scheme?.primaryContainer ?: fallbackBackground) to (scheme?.onPrimaryContainer ?: fallbackContent)
}

private fun Theme.getScheme(): Scheme {
val isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches
return if (isDarkMode) {
Expand Down
3 changes: 1 addition & 2 deletions extension/src/jsMain/resources/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
"version": "0.0.0",

"action": {
"default_icon": "icons/launcher_128.png",
"default_popup": "popup.html"
"default_icon": "icons/launcher_128.png"
},

"icons": {
Expand Down
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ parcelable = "1.2.0"
protobuf = "0.9.4"
protoc = "3.24.4"
realm = "1.12.0"
sekret = "0.2.0"
sekret = "0.2.1"
serialization-json = "1.6.0"
splashscreen = "1.0.1"
sqldelight = "2.0.0"
Expand Down Expand Up @@ -87,6 +87,7 @@ ktor = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
ktor-jvm = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktor" }
ktor-apple = { group = "io.ktor", name = "ktor-client-darwin", version.ref = "ktor" }
ktor-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktor" }
ktor-js = { group = "io.ktor", name = "ktor-client-js", version.ref = "ktor" }
ktor-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" }
ktor-serialization-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" }
ktorfit = { group = "de.jensklingenberg.ktorfit", name = "ktorfit-lib", version.ref = "ktorfit" }
Expand Down
Loading

0 comments on commit abac0ae

Please sign in to comment.