mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-08-05 19:59:51 +00:00
Compare commits
8 Commits
7f55ee64fb
...
6d06c69078
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d06c69078 | ||
|
|
fe0ebb47fe | ||
|
|
14ee4f6a4c | ||
|
|
4bc487782f | ||
|
|
160f1f318f | ||
|
|
ec672d5f84 | ||
|
|
6c9c762b87 | ||
|
|
3d341f341c |
13
.github/apache_header.txt
vendored
13
.github/apache_header.txt
vendored
@ -1,13 +0,0 @@
|
||||
Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
16
.github/dependabot.yml
vendored
16
.github/dependabot.yml
vendored
@ -1,16 +0,0 @@
|
||||
version: 2
|
||||
|
||||
enable-beta-ecosystems: true
|
||||
|
||||
updates:
|
||||
- package-ecosystem: "pub"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: "dev"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: "dev"
|
||||
@ -17,7 +17,11 @@
|
||||
|
||||
package dev.krtirtho.spotube
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import org.koin.core.context.GlobalContext
|
||||
|
||||
class AndroidPlatform : Platform {
|
||||
override val name: String = "Android ${Build.VERSION.SDK_INT}"
|
||||
@ -25,3 +29,11 @@ class AndroidPlatform : Platform {
|
||||
}
|
||||
|
||||
actual fun getPlatform(): Platform = AndroidPlatform()
|
||||
|
||||
actual fun openUrlInBrowser(url: String) {
|
||||
val context = GlobalContext.get().get<Context>()
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
}
|
||||
@ -17,8 +17,20 @@
|
||||
|
||||
package dev.krtirtho.spotube.core.webview
|
||||
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebStorage
|
||||
import android.webkit.WebView
|
||||
import io.github.kdroidfilter.webview.web.WebViewState
|
||||
|
||||
actual fun platformWebviewConfig(webView: WebViewState) {
|
||||
webView.webView?.nativeWebView?.settings?.domStorageEnabled = true
|
||||
actual fun platformWebviewConfig(webView: WebViewState, pluginId: String?) {
|
||||
val nativeWebView = webView.webView?.nativeWebView as? WebView ?: return
|
||||
nativeWebView.settings.domStorageEnabled = true
|
||||
}
|
||||
|
||||
actual suspend fun platformClearWebviewData(pluginId: String?) {
|
||||
if (pluginId == null) return
|
||||
|
||||
CookieManager.getInstance().removeAllCookies(null)
|
||||
CookieManager.getInstance().flush()
|
||||
WebStorage.getInstance().deleteAllData()
|
||||
}
|
||||
@ -151,6 +151,7 @@
|
||||
<string name="plugin_action_logout">Logout</string>
|
||||
<string name="plugin_section_url_title">Download from URL</string>
|
||||
<string name="plugin_section_file_title">Install from file</string>
|
||||
<string name="plugin_section_install">Install</string>
|
||||
|
||||
<string name="plugin_permissions_author_version">• %1$s</string>
|
||||
<string name="plugin_permissions_requested_title">Permissions requested</string>
|
||||
|
||||
@ -28,6 +28,8 @@ interface Platform {
|
||||
|
||||
expect fun getPlatform(): Platform
|
||||
|
||||
expect fun openUrlInBrowser(url: String)
|
||||
|
||||
fun Platform.isDesktop(): Boolean {
|
||||
return type == PlatformType.Windows ||
|
||||
type == PlatformType.Linux ||
|
||||
|
||||
@ -53,6 +53,7 @@ import dev.krtirtho.spotube.modules.library.playlist.LibraryPlaylistsViewModel
|
||||
import dev.krtirtho.spotube.modules.lyrics.LyricsViewModel
|
||||
import dev.krtirtho.spotube.modules.playlist.PlaylistRepository
|
||||
import dev.krtirtho.spotube.modules.playlist.PlaylistViewModel
|
||||
import dev.krtirtho.spotube.modules.plugin.PluginDiscoverViewModel
|
||||
import dev.krtirtho.spotube.modules.plugin.PluginManager
|
||||
import dev.krtirtho.spotube.modules.plugin.PluginProvider
|
||||
import dev.krtirtho.spotube.modules.saved_tracks.SavedTracksRepository
|
||||
@ -108,6 +109,7 @@ val sharedModules = module {
|
||||
|
||||
// Plugin system
|
||||
singleOf(::PluginManager) { bind<PluginProvider>() }
|
||||
viewModelOf(::PluginDiscoverViewModel)
|
||||
|
||||
// Settings
|
||||
singleOf(::SettingsRepository)
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.spotube.core.extras
|
||||
|
||||
fun String.kebabToTitleCase(): String {
|
||||
return this.split("-").joinToString(" ") { it.replaceFirstChar { char -> char.uppercase() } }
|
||||
}
|
||||
@ -38,6 +38,8 @@ class WebViewController(val navigationCommands: NavigationCommands): KoinCompone
|
||||
private var cookieManager: CookieManager? = null
|
||||
private val urlFlow = MutableStateFlow("")
|
||||
private val webViewCreated = MutableSharedFlow<Unit>(replay = 1)
|
||||
var currentPluginId: String? = null
|
||||
private set
|
||||
|
||||
suspend fun getCookies(url: String): List<Cookie> {
|
||||
if (cookieManager == null) {
|
||||
@ -104,19 +106,21 @@ class WebViewController(val navigationCommands: NavigationCommands): KoinCompone
|
||||
webViewNavigator = null
|
||||
}
|
||||
|
||||
fun navigateTo(url: String) {
|
||||
fun navigateTo(url: String, pluginId: String) {
|
||||
if (this.content != null) {
|
||||
throw IllegalStateException("WebView is already open. Please close the current WebView before navigating to a new URL.")
|
||||
}
|
||||
this.currentPluginId = pluginId
|
||||
this.content = url
|
||||
this.isHtmlContent = false
|
||||
navigationCommands.navigateTo(Routes.WebView)
|
||||
}
|
||||
|
||||
fun navigateToHTML(html: String) {
|
||||
fun navigateToHTML(html: String, pluginId: String) {
|
||||
if (this.content != null) {
|
||||
throw IllegalStateException("WebView is already open. Please close the current WebView before navigating to a new URL.")
|
||||
}
|
||||
this.currentPluginId = pluginId
|
||||
this.content = html
|
||||
this.isHtmlContent = true
|
||||
navigationCommands.navigateTo(Routes.WebView)
|
||||
@ -138,12 +142,15 @@ class WebViewController(val navigationCommands: NavigationCommands): KoinCompone
|
||||
return completer.await()
|
||||
}
|
||||
|
||||
suspend fun clearData() {
|
||||
suspend fun clearData(pluginId: String? = null) {
|
||||
cookieManager?.removeAllCookies()
|
||||
val targetPluginId = pluginId ?: currentPluginId
|
||||
platformClearWebviewData(targetPluginId)
|
||||
cookieManager = null
|
||||
content = null
|
||||
isHtmlContent = false
|
||||
webViewNavigator = null
|
||||
currentPluginId = null
|
||||
}
|
||||
|
||||
val urlChangedFlow = urlFlow.asStateFlow()
|
||||
|
||||
@ -19,4 +19,6 @@ package dev.krtirtho.spotube.core.webview
|
||||
|
||||
import io.github.kdroidfilter.webview.web.WebViewState
|
||||
|
||||
expect fun platformWebviewConfig(webView: WebViewState)
|
||||
expect fun platformWebviewConfig(webView: WebViewState, pluginId: String?)
|
||||
|
||||
expect suspend fun platformClearWebviewData(pluginId: String?)
|
||||
|
||||
@ -19,7 +19,6 @@ package dev.krtirtho.spotube.core.webview
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@ -40,30 +39,25 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.github.kdroidfilter.webview.jsbridge.IJsMessageHandler
|
||||
import io.github.kdroidfilter.webview.jsbridge.JsMessage
|
||||
import io.github.kdroidfilter.webview.jsbridge.rememberWebViewJsBridge
|
||||
import io.github.kdroidfilter.webview.web.WebView
|
||||
import io.github.kdroidfilter.webview.web.rememberWebViewNavigator
|
||||
import io.github.kdroidfilter.webview.web.WebViewState
|
||||
import io.github.kdroidfilter.webview.web.WebViewNavigator
|
||||
import compose.icons.FeatherIcons
|
||||
import compose.icons.feathericons.ChevronLeft
|
||||
import compose.icons.feathericons.ChevronRight
|
||||
import compose.icons.feathericons.X
|
||||
import dev.krtirtho.spotube.core.tools.user_agents.UserAgents
|
||||
import kotlinx.coroutines.launch
|
||||
import io.github.kdroidfilter.webview.jsbridge.IJsMessageHandler
|
||||
import io.github.kdroidfilter.webview.jsbridge.JsMessage
|
||||
import io.github.kdroidfilter.webview.jsbridge.rememberWebViewJsBridge
|
||||
import io.github.kdroidfilter.webview.web.WebView
|
||||
import io.github.kdroidfilter.webview.web.WebViewNavigator
|
||||
import io.github.kdroidfilter.webview.web.WebViewState
|
||||
import io.github.kdroidfilter.webview.web.rememberWebViewNavigator
|
||||
|
||||
class PostMessageHandler(
|
||||
private val onMessageReceived: (String) -> Unit = {}
|
||||
@ -98,7 +92,7 @@ fun PlatformWebViewScreen(webViewController: WebViewController) {
|
||||
)
|
||||
}.apply {
|
||||
this.content = webViewController.getWebContent()
|
||||
platformWebviewConfig(this)
|
||||
platformWebviewConfig(this, webViewController.currentPluginId)
|
||||
}
|
||||
|
||||
val navigator = rememberWebViewNavigator()
|
||||
|
||||
@ -141,7 +141,7 @@ open class ZiplinePluginService(
|
||||
}
|
||||
|
||||
private val realHttpClientAPI = RealHttpClientAPI()
|
||||
private val realWebViewAPI = RealWebViewAPI(scope, webViewController)
|
||||
private val realWebViewAPI = RealWebViewAPI(scope, webViewController, pluginInfo.id)
|
||||
|
||||
private val persistedStorageAPI = RealPersistedStorageAPI(pluginInfo)
|
||||
private val cryptoAPI = RealCryptoAPI(scope.coroutineContext)
|
||||
|
||||
@ -22,26 +22,24 @@ import dev.krtirtho.plugin_interfaces.host_apis.WebViewAPI
|
||||
import dev.krtirtho.spotube.core.webview.WebViewController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class RealWebViewAPI(
|
||||
private val scope: CoroutineScope,
|
||||
private val webViewController: WebViewController,
|
||||
private val pluginId: String,
|
||||
) : WebViewAPI {
|
||||
override fun navigateTo(url: String) {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
webViewController.navigateTo(url)
|
||||
webViewController.navigateTo(url, pluginId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun navigateToHTML(html: String) {
|
||||
scope.launch {
|
||||
webViewController.navigateToHTML(html)
|
||||
webViewController.navigateToHTML(html, pluginId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
package dev.krtirtho.spotube.modules.blacklist
|
||||
|
||||
@ -27,7 +27,11 @@ val NEWPIPE_YOUTUBE_BUILT_IN_PLUGIN = PluginEntry(
|
||||
PluginCapability.NETWORK_REQUESTS,
|
||||
PluginCapability.PERSISTENT_STORAGE
|
||||
),
|
||||
abilities = listOf(PluginAbility.AUDIO)
|
||||
abilities = listOf(PluginAbility.AUDIO),
|
||||
contact = "",
|
||||
repository = "",
|
||||
bugs = "",
|
||||
license = "",
|
||||
)
|
||||
val LRCLIB_BUILT_IN_PLUGIN = PluginEntry(
|
||||
name = "LRCLib Lyrics",
|
||||
@ -38,7 +42,11 @@ val LRCLIB_BUILT_IN_PLUGIN = PluginEntry(
|
||||
capabilities = listOf(
|
||||
PluginCapability.NETWORK_REQUESTS,
|
||||
),
|
||||
abilities = listOf(PluginAbility.LYRICS)
|
||||
abilities = listOf(PluginAbility.LYRICS),
|
||||
contact = "",
|
||||
repository = "",
|
||||
bugs = "",
|
||||
license = "",
|
||||
)
|
||||
val BUILT_IN_PLUGINS = listOf(
|
||||
NEWPIPE_YOUTUBE_BUILT_IN_PLUGIN,
|
||||
|
||||
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.spotube.modules.plugin
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.headers
|
||||
import io.ktor.client.request.parameter
|
||||
import io.ktor.http.append
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Serializable
|
||||
data class GitHubRepoSearchResponse(
|
||||
@SerialName("total_count") val totalCount: Int,
|
||||
@SerialName("incomplete_results") val incompleteResults: Boolean,
|
||||
val items: List<GitHubRepo>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GitHubRepo(
|
||||
val id: Long,
|
||||
@SerialName("full_name") val fullName: String,
|
||||
val description: String? = null,
|
||||
@SerialName("stargazers_count") val stargazersCount: Int = 0,
|
||||
@SerialName("html_url") val htmlUrl: String,
|
||||
val owner: GitHubOwner,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GitHubOwner(
|
||||
val login: String,
|
||||
@SerialName("avatar_url") val avatarUrl: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GitHubRelease(
|
||||
@SerialName("tag_name") val tagName: String,
|
||||
val name: String? = null,
|
||||
val body: String? = null,
|
||||
val assets: List<GitHubAsset>,
|
||||
@SerialName("html_url") val htmlUrl: String,
|
||||
val prerelease: Boolean = false,
|
||||
val draft: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GitHubAsset(
|
||||
val name: String,
|
||||
@SerialName("browser_download_url") val browserDownloadUrl: String,
|
||||
)
|
||||
|
||||
class GitHubPluginRepository {
|
||||
private val httpClient = HttpClient {
|
||||
install(ContentNegotiation) {
|
||||
json(Json { ignoreUnknownKeys = true })
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun searchSpotubePlugins(page: Int = 1, perPage: Int = 30): GitHubRepoSearchResponse {
|
||||
return httpClient.get("https://api.github.com/search/repositories") {
|
||||
headers {
|
||||
append("Accept", "application/vnd.github+json")
|
||||
append("X-GitHub-Api-Version", "2022-11-28")
|
||||
}
|
||||
parameter("q", "topic:spotube-zipline-plugin")
|
||||
parameter("sort", "stars")
|
||||
parameter("order", "desc")
|
||||
parameter("page", page)
|
||||
parameter("per_page", perPage)
|
||||
}.body()
|
||||
}
|
||||
|
||||
suspend fun getLatestReleaseSmplugUrl(owner: String, repo: String): String? {
|
||||
return try {
|
||||
val release: GitHubRelease =
|
||||
httpClient.get("https://api.github.com/repos/$owner/$repo/releases/latest") {
|
||||
headers {
|
||||
append("Accept", "application/vnd.github+json")
|
||||
append("X-GitHub-Api-Version", "2022-11-28")
|
||||
}
|
||||
}.body()
|
||||
release.assets.firstOrNull { it.name.endsWith(".smplug") }?.browserDownloadUrl
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getReleases(owner: String, repo: String, perPage: Int = 30): List<GitHubRelease> {
|
||||
return try {
|
||||
httpClient.get("https://api.github.com/repos/$owner/$repo/releases") {
|
||||
headers {
|
||||
append("Accept", "application/vnd.github+json")
|
||||
append("X-GitHub-Api-Version", "2022-11-28")
|
||||
}
|
||||
parameter("per_page", perPage)
|
||||
}.body()
|
||||
} catch (_: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun close() {
|
||||
httpClient.close()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.spotube.modules.plugin
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dev.krtirtho.spotube.core.di.injectLogger
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
|
||||
data class PluginDiscoverState(
|
||||
val repos: List<GitHubRepo> = emptyList(),
|
||||
val currentPage: Int = 1,
|
||||
val hasMore: Boolean = true,
|
||||
val isLoading: Boolean = false,
|
||||
val isLoadingMore: Boolean = false,
|
||||
val error: String? = null,
|
||||
val installingRepoId: Long? = null,
|
||||
val isInitialLoaded: Boolean = false,
|
||||
)
|
||||
|
||||
class PluginDiscoverViewModel(
|
||||
private val pluginManager: PluginManager,
|
||||
) : ViewModel(), KoinComponent {
|
||||
|
||||
private val logger by injectLogger<PluginDiscoverViewModel>()
|
||||
private val gitHubRepo = GitHubPluginRepository()
|
||||
|
||||
private val _allRepos = mutableListOf<GitHubRepo>()
|
||||
private val _paginationInfo = PaginationInfo()
|
||||
|
||||
private val _state = MutableStateFlow(PluginDiscoverState())
|
||||
val state: StateFlow<PluginDiscoverState> = _state.asStateFlow()
|
||||
|
||||
private class PaginationInfo(
|
||||
var currentPage: Int = 1,
|
||||
var hasMore: Boolean = true,
|
||||
var totalCount: Int = 0,
|
||||
)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
pluginManager.state.collect { pluginState ->
|
||||
if (pluginState is PluginManagerStates.Data) {
|
||||
val installedUrls = pluginState.plugins.mapNotNull { it.repository.takeIf { r -> r.isNotBlank() } }.toSet()
|
||||
_state.update {
|
||||
it.copy(repos = _allRepos.filter { repo -> repo.htmlUrl !in installedUrls })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loadFirstPage()
|
||||
}
|
||||
|
||||
private fun filterInstalled(repos: List<GitHubRepo>): List<GitHubRepo> {
|
||||
val pluginState = pluginManager.state.value
|
||||
if (pluginState !is PluginManagerStates.Data) return repos
|
||||
val installedUrls = pluginState.plugins.mapNotNull { it.repository.takeIf { r -> r.isNotBlank() } }.toSet()
|
||||
return repos.filter { it.htmlUrl !in installedUrls }
|
||||
}
|
||||
|
||||
private fun loadFirstPage() {
|
||||
viewModelScope.launch {
|
||||
_state.update { it.copy(isLoading = true, error = null) }
|
||||
runCatching {
|
||||
gitHubRepo.searchSpotubePlugins(page = 1)
|
||||
}.onSuccess { response ->
|
||||
_allRepos.clear()
|
||||
_allRepos.addAll(response.items)
|
||||
_paginationInfo.currentPage = 1
|
||||
_paginationInfo.totalCount = response.totalCount
|
||||
_paginationInfo.hasMore = _allRepos.size < response.totalCount
|
||||
_state.update {
|
||||
it.copy(
|
||||
repos = filterInstalled(response.items),
|
||||
currentPage = 1,
|
||||
hasMore = _paginationInfo.hasMore,
|
||||
isLoading = false,
|
||||
isInitialLoaded = true,
|
||||
)
|
||||
}
|
||||
}.onFailure { e ->
|
||||
logger.e(e) { "Failed to load plugins" }
|
||||
_state.update {
|
||||
it.copy(
|
||||
isLoading = false,
|
||||
error = e.message,
|
||||
isInitialLoaded = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadNextPage() {
|
||||
val current = _state.value
|
||||
if (current.isLoadingMore || !current.hasMore) return
|
||||
viewModelScope.launch {
|
||||
val nextPage = _paginationInfo.currentPage + 1
|
||||
_state.update { it.copy(isLoadingMore = true, error = null) }
|
||||
runCatching {
|
||||
gitHubRepo.searchSpotubePlugins(page = nextPage)
|
||||
}.onSuccess { response ->
|
||||
_allRepos.addAll(response.items)
|
||||
_paginationInfo.currentPage = nextPage
|
||||
_paginationInfo.hasMore = _allRepos.size < response.totalCount
|
||||
_state.update {
|
||||
it.copy(
|
||||
repos = filterInstalled(_allRepos),
|
||||
currentPage = nextPage,
|
||||
hasMore = _paginationInfo.hasMore,
|
||||
isLoadingMore = false,
|
||||
)
|
||||
}
|
||||
}.onFailure { e ->
|
||||
logger.e(e) { "Failed to load more plugins" }
|
||||
_state.update { it.copy(isLoadingMore = false, error = e.message) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun installPlugin(repo: GitHubRepo) {
|
||||
if (_state.value.installingRepoId != null) return
|
||||
_state.update { it.copy(installingRepoId = repo.id, error = null) }
|
||||
viewModelScope.launch {
|
||||
runCatching {
|
||||
val parts = repo.fullName.split("/")
|
||||
val url = gitHubRepo.getLatestReleaseSmplugUrl(parts[0], parts[1])
|
||||
?: throw IllegalStateException("No .smplug asset found in latest release")
|
||||
pluginManager.addPluginFromURL(url)
|
||||
}.onFailure { e ->
|
||||
logger.e(e) { "Failed to install plugin" }
|
||||
_state.update { it.copy(error = e.message) }
|
||||
}
|
||||
_state.update { it.copy(installingRepoId = null) }
|
||||
}
|
||||
}
|
||||
|
||||
fun installPluginFromUrl(url: String, repoId: Long) {
|
||||
if (_state.value.installingRepoId != null) return
|
||||
_state.update { it.copy(installingRepoId = repoId, error = null) }
|
||||
viewModelScope.launch {
|
||||
runCatching {
|
||||
pluginManager.addPluginFromURL(url)
|
||||
}.onFailure { e ->
|
||||
logger.e(e) { "Failed to install plugin" }
|
||||
_state.update { it.copy(error = e.message) }
|
||||
}
|
||||
_state.update { it.copy(installingRepoId = null) }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getReleases(owner: String, repo: String): List<GitHubRelease> {
|
||||
return gitHubRepo.getReleases(owner, repo)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
gitHubRepo.close()
|
||||
super.onCleared()
|
||||
}
|
||||
}
|
||||
@ -80,6 +80,7 @@ class PluginManager(
|
||||
}
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate + pluginExceptionHandler)
|
||||
private val pluginsDir = "${paths.getApplicationDataDirPath()}/plugins".toPath()
|
||||
val pluginsDirPath: Path get() = pluginsDir
|
||||
private val httpClient = HttpClient()
|
||||
|
||||
|
||||
@ -324,6 +325,15 @@ class PluginManager(
|
||||
throw IllegalArgumentException("Invalid plugin.json format: ${e.message}")
|
||||
}
|
||||
|
||||
// Preserve logo.png before deleting temp dir so the permission dialog can show it
|
||||
val logoPngPath = tempDir / "logo.png".toPath()
|
||||
if (FileSystem.SYSTEM.exists(logoPngPath)) {
|
||||
val logoDir = pluginsDirPath / pluginEntry.id.toPath()
|
||||
if (!FileSystem.SYSTEM.exists(logoDir)) FileSystem.SYSTEM.createDirectories(logoDir)
|
||||
val destLogo = logoDir / "logo.png".toPath()
|
||||
FileSystem.SYSTEM.copy(logoPngPath, destLogo)
|
||||
}
|
||||
|
||||
pendingPlugin.value = buildPendingPlugin(pluginEntry, bytes)
|
||||
} catch (e: Exception) {
|
||||
throw Exception("Failed to read plugin: ${e.message}", e)
|
||||
|
||||
@ -18,20 +18,28 @@
|
||||
package dev.krtirtho.spotube.modules.plugin
|
||||
|
||||
import com.goncalossilva.murmurhash.MurmurHash3
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
enum class PluginCapability {
|
||||
@SerialName("persistent_storage")
|
||||
PERSISTENT_STORAGE,
|
||||
@SerialName("network_requests")
|
||||
NETWORK_REQUESTS,
|
||||
@SerialName("webview")
|
||||
WEBVIEW
|
||||
}
|
||||
|
||||
//Set naming strategy to snake_case for better interoperability with JavaScript plugins
|
||||
@Serializable
|
||||
enum class PluginAbility {
|
||||
@SerialName("metadata")
|
||||
METADATA,
|
||||
@SerialName("audio")
|
||||
AUDIO,
|
||||
@SerialName("lyrics")
|
||||
LYRICS,
|
||||
@SerialName("scrobble")
|
||||
SCROBBLE,
|
||||
}
|
||||
|
||||
@ -43,7 +51,11 @@ data class PluginEntry(
|
||||
val description: String,
|
||||
val author: String,
|
||||
val capabilities: List<PluginCapability>,
|
||||
val abilities: List<PluginAbility>
|
||||
val abilities: List<PluginAbility>,
|
||||
val contact: String,
|
||||
val repository: String,
|
||||
val bugs: String,
|
||||
val license: String,
|
||||
) {
|
||||
@Suppress("REDUNDANT_CALL_OF_CONVERSION_METHOD")
|
||||
val id: String = MurmurHash3().hash32x86("$name:$author".encodeToByteArray())
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
package dev.krtirtho.spotube.modules.plugin
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -30,6 +31,8 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@ -41,6 +44,7 @@ import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@ -50,15 +54,24 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.compose.LocalPlatformContext
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.request.crossfade
|
||||
import dev.krtirtho.spotube.PlatformType
|
||||
import dev.krtirtho.spotube.core.extras.kebabToTitleCase
|
||||
import dev.krtirtho.spotube.core.ui.base.Card
|
||||
import dev.krtirtho.spotube.core.ui.base.OutlineButton
|
||||
import dev.krtirtho.spotube.core.ui.base.PrimaryButton
|
||||
import dev.krtirtho.spotube.core.ui.base.SecondaryButton
|
||||
import dev.krtirtho.spotube.core.ui.base.SecondaryIconButton
|
||||
import dev.krtirtho.spotube.core.ui.base.TextField
|
||||
import dev.krtirtho.spotube.core.ui.base.ThemedDialog
|
||||
import dev.krtirtho.spotube.core.ui.component.AdaptiveDialogBottomSheet
|
||||
import dev.krtirtho.spotube.core.ui.component.AdaptiveDropdownBottomSheet
|
||||
import dev.krtirtho.spotube.core.ui.component.AdaptiveMenuItem
|
||||
@ -67,15 +80,21 @@ import dev.krtirtho.spotube.core.ui.component.HeaderDisplayMode
|
||||
import dev.krtirtho.spotube.core.webview.WebViewController
|
||||
import dev.krtirtho.spotube.getPlatform
|
||||
import dev.krtirtho.spotube.modules.plugin.components.PluginCard
|
||||
import dev.krtirtho.spotube.modules.plugin.components.PluginInstallDialog
|
||||
import dev.krtirtho.spotube.modules.plugin.components.PluginPermissionDialog
|
||||
import dev.krtirtho.spotube.modules.shell.LocalAppShellBottomInset
|
||||
import dev.krtirtho.spotube.openUrlInBrowser
|
||||
import dev.krtirtho.spotube.resources.iconsax.CarbonGithubLogo
|
||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxAdd
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxArrowDown4
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxBox
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxCheckCircle
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxDocumentText
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxEdit
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxExportArrowBulk
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxGlobe
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxHeart
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxImportArrow2Bulk
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxLink
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxMusic
|
||||
@ -86,8 +105,13 @@ import io.github.vinceglb.filekit.dialogs.compose.rememberFilePickerLauncher
|
||||
import io.github.vinceglb.filekit.readBytes
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import net.swiftzer.semver.SemVer
|
||||
import okio.FileSystem
|
||||
import okio.Path.Companion.toPath
|
||||
import okio.SYSTEM
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.koin.compose.koinInject
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import spotube.composeapp.generated.resources.Res
|
||||
import spotube.composeapp.generated.resources.plugin_action_download
|
||||
import spotube.composeapp.generated.resources.plugin_action_install_from_file
|
||||
@ -103,6 +127,7 @@ import spotube.composeapp.generated.resources.plugin_installed_plural
|
||||
import spotube.composeapp.generated.resources.plugin_installed_singular
|
||||
import spotube.composeapp.generated.resources.plugin_screen_title
|
||||
import spotube.composeapp.generated.resources.plugin_section_file_title
|
||||
import spotube.composeapp.generated.resources.plugin_section_install
|
||||
import spotube.composeapp.generated.resources.plugin_section_url_title
|
||||
import spotube.composeapp.generated.resources.plugin_url_placeholder
|
||||
import spotube.composeapp.generated.resources.settings_plugins_ability_audio
|
||||
@ -116,6 +141,10 @@ import spotube.composeapp.generated.resources.settings_plugins_no_plugins
|
||||
import spotube.composeapp.generated.resources.settings_plugins_no_selection
|
||||
import spotube.composeapp.generated.resources.settings_plugins_plugin_content_description
|
||||
|
||||
private val OFFICIAL_PLUGIN_OWNERS = setOf("KRTirtho", "team-spotube")
|
||||
|
||||
private val VERIFIED_PLUGIN_OWNERS = setOf<String>()
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PluginScreen(
|
||||
@ -134,6 +163,18 @@ fun PluginScreen(
|
||||
var isLoadingUrl by remember { mutableStateOf(false) }
|
||||
var showInstallSheet by remember { mutableStateOf(false) }
|
||||
|
||||
val discoverViewModel: PluginDiscoverViewModel = koinViewModel()
|
||||
val discoverState by discoverViewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
var showPluginInfo by remember { mutableStateOf<PluginEntry?>(null) }
|
||||
var showPluginSupport by remember { mutableStateOf<PluginEntry?>(null) }
|
||||
var supportText by remember { mutableStateOf<String?>(null) }
|
||||
var isLoadingSupport by remember { mutableStateOf(false) }
|
||||
|
||||
var installDialogRepo by remember { mutableStateOf<GitHubRepo?>(null) }
|
||||
var releases by remember { mutableStateOf<List<GitHubRelease>>(emptyList()) }
|
||||
var isLoadingReleases by remember { mutableStateOf(false) }
|
||||
|
||||
val pleaseEnterUrl = stringResource(Res.string.plugin_error_enter_url)
|
||||
val urlSchemeError = stringResource(Res.string.plugin_error_url_scheme)
|
||||
val downloadFailed = stringResource(Res.string.plugin_error_download_failed)
|
||||
@ -173,12 +214,17 @@ fun PluginScreen(
|
||||
}
|
||||
|
||||
pendingPlugin?.let { pending ->
|
||||
val logoPath = remember(pending.entry.id) {
|
||||
val path = pluginManager.pluginsDirPath / pending.entry.id.toPath() / "logo.png".toPath()
|
||||
if (FileSystem.SYSTEM.exists(path)) path else null
|
||||
}
|
||||
PluginPermissionDialog(
|
||||
pluginInfo = pending.entry,
|
||||
title = pending.title,
|
||||
message = pending.message,
|
||||
confirmLabel = pending.confirmLabel,
|
||||
existingPlugin = pending.existingEntry,
|
||||
logoPath = logoPath,
|
||||
onConfirm = if (pending.kind != PluginManager.InstallPromptKind.INFO && pending.confirmLabel != null) {
|
||||
{ pluginManager.confirmInstall() }
|
||||
} else {
|
||||
@ -188,6 +234,160 @@ fun PluginScreen(
|
||||
)
|
||||
}
|
||||
|
||||
showPluginInfo?.let { plugin ->
|
||||
val logoPath = remember(plugin.id) {
|
||||
val path = pluginManager.pluginsDirPath / plugin.id.toPath() / "logo.png".toPath()
|
||||
if (FileSystem.SYSTEM.exists(path)) path else null
|
||||
}
|
||||
ThemedDialog(
|
||||
onDismissRequest = { showPluginInfo = null },
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(RoundedCornerShape(12.dp)),
|
||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.12f)
|
||||
) {
|
||||
if (logoPath != null) {
|
||||
val platformContext = LocalPlatformContext.current
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(platformContext)
|
||||
.data(logoPath.toString())
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = plugin.name,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
} else {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Icon(
|
||||
Iconsax.IconsaxBox,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Text(
|
||||
plugin.name,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
PrimaryButton(onClick = { showPluginInfo = null }) {
|
||||
Text("Close")
|
||||
}
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
if (plugin.description.isNotBlank()) {
|
||||
Text(
|
||||
plugin.description,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
|
||||
DetailRow("Version", plugin.version)
|
||||
DetailRow("API Version", plugin.apiVersion)
|
||||
DetailRow("Author", plugin.author)
|
||||
if (plugin.license.isNotBlank()) {
|
||||
DetailRow("License", plugin.license)
|
||||
}
|
||||
|
||||
if (plugin.capabilities.isNotEmpty()) {
|
||||
DetailChipsRow("Capabilities", plugin.capabilities.map { it.name })
|
||||
}
|
||||
|
||||
if (plugin.abilities.isNotEmpty()) {
|
||||
DetailChipsRow("Abilities", plugin.abilities.map { ability ->
|
||||
when (ability) {
|
||||
PluginAbility.METADATA -> "Metadata"
|
||||
PluginAbility.AUDIO -> "Audio"
|
||||
PluginAbility.LYRICS -> "Lyrics"
|
||||
PluginAbility.SCROBBLE -> "Scrobble"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (plugin.repository.isNotBlank()) {
|
||||
ClickableDetailRow(label = "Repository", value = plugin.repository) {
|
||||
openUrlInBrowser(plugin.repository)
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin.contact.isNotBlank()) {
|
||||
DetailRow("Contact", plugin.contact)
|
||||
}
|
||||
|
||||
if (plugin.bugs.isNotBlank()) {
|
||||
ClickableDetailRow(label = "Report Bugs", value = plugin.bugs) {
|
||||
openUrlInBrowser(plugin.bugs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showPluginSupport?.let { plugin ->
|
||||
ThemedDialog(
|
||||
onDismissRequest = { showPluginSupport = null; supportText = null },
|
||||
title = {
|
||||
Text(
|
||||
"Support ${plugin.name}",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
PrimaryButton(onClick = { showPluginSupport = null; supportText = null }) {
|
||||
Text("Close")
|
||||
}
|
||||
}
|
||||
) {
|
||||
if (isLoadingSupport) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 24.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
supportText ?: "",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
installDialogRepo?.let { repo ->
|
||||
PluginInstallDialog(
|
||||
repo = repo,
|
||||
releases = releases,
|
||||
isLoadingReleases = isLoadingReleases,
|
||||
onDismiss = { installDialogRepo = null },
|
||||
onInstall = { release ->
|
||||
installDialogRepo = null
|
||||
val smplugUrl = release.assets.firstOrNull { it.name.endsWith(".smplug") }?.browserDownloadUrl
|
||||
if (smplugUrl != null) {
|
||||
discoverViewModel.installPluginFromUrl(smplugUrl, repo.id)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (showInstallSheet) {
|
||||
AdaptiveDialogBottomSheet(
|
||||
onDismiss = { showInstallSheet = false },
|
||||
@ -301,7 +501,9 @@ fun PluginScreen(
|
||||
.fillMaxSize()
|
||||
.padding(innerPadding)
|
||||
) {
|
||||
val discoverListState = rememberLazyListState()
|
||||
LazyColumn(
|
||||
state = discoverListState,
|
||||
modifier = Modifier.widthIn(max = 1280.dp).align(Alignment.TopCenter),
|
||||
contentPadding = PaddingValues(
|
||||
start = 12.dp,
|
||||
@ -487,6 +689,12 @@ fun PluginScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val logoPath = remember(plugin.id) {
|
||||
val path =
|
||||
pluginManager.pluginsDirPath / plugin.id.toPath() / "logo.png".toPath()
|
||||
if (FileSystem.SYSTEM.exists(path)) path else null
|
||||
}
|
||||
|
||||
PluginCard(
|
||||
plugin = plugin,
|
||||
isSelected = isSelected,
|
||||
@ -494,6 +702,24 @@ fun PluginScreen(
|
||||
scope.launch { pluginManager.removePlugin(plugin) }
|
||||
},
|
||||
isLoggedIn = isLoggedIn,
|
||||
logoPath = logoPath,
|
||||
onInfo = { showPluginInfo = plugin },
|
||||
onSupport = if (selectedService != null) {
|
||||
{
|
||||
isLoadingSupport = true
|
||||
scope.launch {
|
||||
showPluginSupport = plugin
|
||||
val version = SemVer.parse(plugin.version)
|
||||
selectedService.use {
|
||||
supportText =
|
||||
coreAPI.supportMarkdownText(version)
|
||||
}
|
||||
isLoadingSupport = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onLogin = if (requiresAuth && selectedService != null) {
|
||||
{
|
||||
pluginManager.launchTask {
|
||||
@ -509,7 +735,7 @@ fun PluginScreen(
|
||||
selectedService.use { coreAPI.logout() }
|
||||
}
|
||||
// should clear webview data after logout
|
||||
scope.launch { webviewController.clearData() }
|
||||
scope.launch { webviewController.clearData(plugin.id) }
|
||||
}
|
||||
} else {
|
||||
null
|
||||
@ -520,8 +746,340 @@ fun PluginScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Discover plugins ─────────────────────────
|
||||
if (discoverState.isLoading || discoverState.repos.isNotEmpty()) {
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 4.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
Iconsax.IconsaxGlobe,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(
|
||||
"Discover Plugins",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
}
|
||||
}
|
||||
items(
|
||||
discoverState.repos,
|
||||
key = { it.id }
|
||||
) { repo ->
|
||||
val isOfficial = repo.owner.login in OFFICIAL_PLUGIN_OWNERS
|
||||
val isVerified = repo.owner.login in VERIFIED_PLUGIN_OWNERS
|
||||
val isInstalling = discoverState.installingRepoId == repo.id
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
val platformContext = LocalPlatformContext.current
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(platformContext)
|
||||
.data(repo.owner.avatarUrl)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = repo.owner.login,
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
Text(
|
||||
repo.fullName.split("/")
|
||||
.last()
|
||||
.replace("spotube-plugin-", "")
|
||||
.kebabToTitleCase(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f, fill = false)
|
||||
)
|
||||
if (isOfficial) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
color = MaterialTheme.colorScheme.primary.copy(
|
||||
alpha = 0.15f
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
"Official",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.padding(
|
||||
horizontal = 5.dp,
|
||||
vertical = 1.dp
|
||||
)
|
||||
)
|
||||
}
|
||||
} else if (isVerified) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
color = Color(0xFF4CAF50).copy(alpha = 0.15f)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = 5.dp,
|
||||
vertical = 1.dp
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
2.dp
|
||||
)
|
||||
) {
|
||||
Icon(
|
||||
Iconsax.IconsaxCheckCircle,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(10.dp),
|
||||
tint = Color(0xFF4CAF50)
|
||||
)
|
||||
Text(
|
||||
"Verified",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = Color(0xFF4CAF50)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!repo.description.isNullOrBlank()) {
|
||||
Text(
|
||||
repo.description,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
repo.owner.login,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(3.dp)
|
||||
) {
|
||||
Icon(
|
||||
Iconsax.IconsaxHeart,
|
||||
contentDescription = "Github Stars",
|
||||
modifier = Modifier.size(11.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
repo.stargazersCount.toString(),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
Surface(
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
modifier = Modifier.clickable {
|
||||
openUrlInBrowser(repo.htmlUrl)
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = 5.dp,
|
||||
vertical = 2.dp
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
3.dp
|
||||
)
|
||||
) {
|
||||
Icon(
|
||||
Iconsax.CarbonGithubLogo,
|
||||
contentDescription = "Github Repository URL",
|
||||
modifier = Modifier.size(10.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
"github.com",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SecondaryButton(
|
||||
onClick = {
|
||||
installDialogRepo = repo
|
||||
isLoadingReleases = true
|
||||
releases = emptyList()
|
||||
scope.launch {
|
||||
val parts = repo.fullName.split("/")
|
||||
releases = discoverViewModel.getReleases(parts[0], parts[1])
|
||||
isLoadingReleases = false
|
||||
}
|
||||
},
|
||||
enabled = !isInstalling
|
||||
) {
|
||||
if (isInstalling) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(16.dp),
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
Iconsax.IconsaxAdd,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
Text(stringResource(Res.string.plugin_section_install))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (discoverState.isLoadingMore) {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator(modifier = Modifier.size(24.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (discoverState.error != null) {
|
||||
item {
|
||||
Text(
|
||||
discoverState.error ?: "",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.padding(
|
||||
horizontal = 4.dp,
|
||||
vertical = 8.dp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val density = LocalDensity.current
|
||||
val shouldLoadMore = remember(density) {
|
||||
derivedStateOf {
|
||||
val totalItems = discoverListState.layoutInfo.totalItemsCount
|
||||
val lastVisibleIndex =
|
||||
discoverListState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
|
||||
?: 0
|
||||
totalItems > 0 && lastVisibleIndex >= totalItems - 3
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(shouldLoadMore.value) {
|
||||
if (shouldLoadMore.value) {
|
||||
discoverViewModel.loadNextPage()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DetailRow(label: String, value: String) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
"$label:",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.widthIn(min = 90.dp)
|
||||
)
|
||||
Text(
|
||||
value,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ClickableDetailRow(label: String, value: String, onClick: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().clickable(onClick = onClick),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
"$label:",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.widthIn(min = 90.dp)
|
||||
)
|
||||
Text(
|
||||
value,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DetailChipsRow(label: String, chips: List<String>) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
"$label:",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.widthIn(min = 90.dp)
|
||||
)
|
||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
chips.forEach { chip ->
|
||||
Surface(
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.6f)
|
||||
) {
|
||||
Text(
|
||||
chip,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
@ -38,6 +39,11 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.compose.LocalPlatformContext
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.request.crossfade
|
||||
import okio.Path
|
||||
import dev.krtirtho.spotube.core.ui.base.GhostIconButton
|
||||
import dev.krtirtho.spotube.core.ui.base.OutlineButton
|
||||
import dev.krtirtho.spotube.modules.plugin.BUILT_IN_PLUGINS
|
||||
@ -46,6 +52,8 @@ import dev.krtirtho.spotube.modules.plugin.PluginEntry
|
||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxBox
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxCheckSquare
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxInformation
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxHeart
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxTag
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxTrash
|
||||
import dev.krtirtho.spotube.resources.iconsax.User
|
||||
@ -70,6 +78,9 @@ internal fun PluginCard(
|
||||
isLoggedIn: Boolean,
|
||||
onLogin: (() -> Unit)? = null,
|
||||
onLogout: (() -> Unit)? = null,
|
||||
onInfo: (() -> Unit)? = null,
|
||||
onSupport: (() -> Unit)? = null,
|
||||
logoPath: Path? = null,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@ -85,6 +96,17 @@ internal fun PluginCard(
|
||||
.clip(RoundedCornerShape(10.dp)),
|
||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f)
|
||||
) {
|
||||
if (logoPath != null) {
|
||||
val platformContext = LocalPlatformContext.current
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(platformContext)
|
||||
.data(logoPath.toString())
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = plugin.name,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
} else {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Icon(
|
||||
Iconsax.IconsaxBox,
|
||||
@ -94,6 +116,7 @@ internal fun PluginCard(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Text content
|
||||
Column(
|
||||
@ -215,10 +238,31 @@ internal fun PluginCard(
|
||||
Column(
|
||||
modifier = Modifier.align(Alignment.Bottom),
|
||||
horizontalAlignment = Alignment.End,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (onInfo != null) {
|
||||
GhostIconButton(onClick = onInfo) {
|
||||
Icon(
|
||||
Iconsax.IconsaxInformation,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (onSupport != null) {
|
||||
GhostIconButton(onClick = onSupport) {
|
||||
Icon(
|
||||
Iconsax.IconsaxHeart,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (plugin in BUILT_IN_PLUGINS) {
|
||||
// Built-in plugins cannot be removed
|
||||
Text(
|
||||
stringResource(Res.string.plugin_state_builtin),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
@ -239,6 +283,7 @@ internal fun PluginCard(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val authAction = when {
|
||||
isLoggedIn && onLogout != null -> onLogout to Res.string.plugin_action_logout
|
||||
|
||||
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.spotube.modules.plugin.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MenuAnchorType
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.compose.LocalPlatformContext
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.request.crossfade
|
||||
import dev.krtirtho.spotube.core.ui.base.PrimaryButton
|
||||
import dev.krtirtho.spotube.core.ui.base.ThemedDialog
|
||||
import dev.krtirtho.spotube.modules.plugin.GitHubRepo
|
||||
import dev.krtirtho.spotube.modules.plugin.GitHubRelease
|
||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxBox
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PluginInstallDialog(
|
||||
repo: GitHubRepo,
|
||||
releases: List<GitHubRelease>,
|
||||
isLoadingReleases: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
onInstall: (GitHubRelease) -> Unit,
|
||||
) {
|
||||
var selectedRelease by remember { mutableStateOf<GitHubRelease?>(null) }
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(releases) {
|
||||
if (selectedRelease == null && releases.isNotEmpty()) {
|
||||
selectedRelease = releases.first()
|
||||
}
|
||||
}
|
||||
|
||||
ThemedDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(RoundedCornerShape(12.dp)),
|
||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.12f)
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
val platformContext = LocalPlatformContext.current
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(platformContext)
|
||||
.data(repo.owner.avatarUrl)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = repo.owner.login,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
repo.fullName,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
if (!repo.description.isNullOrBlank()) {
|
||||
Text(
|
||||
repo.description,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
PrimaryButton(
|
||||
onClick = { selectedRelease?.let { onInstall(it) } },
|
||||
enabled = selectedRelease != null && !isLoadingReleases
|
||||
) {
|
||||
Text("Install")
|
||||
}
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
if (isLoadingReleases) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth().height(120.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator(modifier = Modifier.size(32.dp))
|
||||
}
|
||||
} else if (releases.isEmpty()) {
|
||||
Text(
|
||||
"No releases found",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
"Select Release",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = { expanded = it }
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.menuAnchor(MenuAnchorType.PrimaryNotEditable),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
color = MaterialTheme.colorScheme.surfaceVariant
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
selectedRelease?.tagName ?: "Select a release",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
|
||||
}
|
||||
}
|
||||
|
||||
ExposedDropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false }
|
||||
) {
|
||||
releases.forEach { release ->
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Column {
|
||||
Text(
|
||||
release.tagName,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = if (release == selectedRelease) FontWeight.SemiBold else FontWeight.Normal
|
||||
)
|
||||
if (release.prerelease) {
|
||||
Text(
|
||||
"Pre-release",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
selectedRelease = release
|
||||
expanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectedRelease?.let { release ->
|
||||
if (!release.body.isNullOrBlank()) {
|
||||
Text(
|
||||
"Release Notes",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = 200.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(12.dp)
|
||||
) {
|
||||
Text(
|
||||
release.body,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
@ -38,6 +39,11 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.compose.LocalPlatformContext
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.request.crossfade
|
||||
import okio.Path
|
||||
import dev.krtirtho.spotube.core.ui.base.Card
|
||||
import dev.krtirtho.spotube.core.ui.base.OutlineButton
|
||||
import dev.krtirtho.spotube.core.ui.base.PrimaryButton
|
||||
@ -76,6 +82,7 @@ fun PluginPermissionDialog(
|
||||
message: String,
|
||||
confirmLabel: String?,
|
||||
existingPlugin: PluginEntry? = null,
|
||||
logoPath: Path? = null,
|
||||
onConfirm: (() -> Unit)? = null,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
@ -93,6 +100,17 @@ fun PluginPermissionDialog(
|
||||
.background(MaterialTheme.colorScheme.primary.copy(alpha = 0.12f)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (logoPath != null) {
|
||||
val platformContext = LocalPlatformContext.current
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(platformContext)
|
||||
.data(logoPath.toString())
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = pluginInfo.name,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Iconsax.IconsaxBoxAdd,
|
||||
contentDescription = null,
|
||||
@ -100,6 +118,7 @@ fun PluginPermissionDialog(
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
title,
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
package dev.krtirtho.spotube.resources.iconsax
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
val Iconsax.CarbonGithubLogo: ImageVector
|
||||
get() {
|
||||
if (_CarbonGithubLogo != null) {
|
||||
return _CarbonGithubLogo!!
|
||||
}
|
||||
_CarbonGithubLogo = ImageVector.Builder(
|
||||
name = "CarbonGithubLogo",
|
||||
defaultWidth = 24.dp,
|
||||
defaultHeight = 24.dp,
|
||||
viewportWidth = 32f,
|
||||
viewportHeight = 32f
|
||||
).apply {
|
||||
path(
|
||||
fill = SolidColor(Color.Black),
|
||||
stroke = SolidColor(Color.Black),
|
||||
strokeLineWidth = 1f
|
||||
) {
|
||||
moveTo(16f, 2f)
|
||||
arcToRelative(14f, 14f, 0f, isMoreThanHalf = false, isPositiveArc = false, -4.43f, 27.28f)
|
||||
curveToRelative(0.7f, 0.13f, 1f, -0.3f, 1f, -0.67f)
|
||||
reflectiveCurveToRelative(0f, -1.21f, 0f, -2.38f)
|
||||
curveToRelative(-3.89f, 0.84f, -4.71f, -1.88f, -4.71f, -1.88f)
|
||||
arcTo(3.71f, 3.71f, 0f, isMoreThanHalf = false, isPositiveArc = false, 6.24f, 22.3f)
|
||||
curveToRelative(-1.27f, -0.86f, 0.1f, -0.85f, 0.1f, -0.85f)
|
||||
arcTo(2.94f, 2.94f, 0f, isMoreThanHalf = false, isPositiveArc = true, 8.48f, 22.9f)
|
||||
arcToRelative(3f, 3f, 0f, isMoreThanHalf = false, isPositiveArc = false, 4.08f, 1.16f)
|
||||
arcToRelative(2.93f, 2.93f, 0f, isMoreThanHalf = false, isPositiveArc = true, 0.88f, -1.87f)
|
||||
curveToRelative(-3.1f, -0.36f, -6.37f, -1.56f, -6.37f, -6.92f)
|
||||
arcToRelative(5.4f, 5.4f, 0f, isMoreThanHalf = false, isPositiveArc = true, 1.44f, -3.76f)
|
||||
arcToRelative(5f, 5f, 0f, isMoreThanHalf = false, isPositiveArc = true, 0.14f, -3.7f)
|
||||
reflectiveCurveToRelative(1.17f, -0.38f, 3.85f, 1.43f)
|
||||
arcToRelative(13.3f, 13.3f, 0f, isMoreThanHalf = false, isPositiveArc = true, 7f, 0f)
|
||||
curveToRelative(2.67f, -1.81f, 3.84f, -1.43f, 3.84f, -1.43f)
|
||||
arcToRelative(5f, 5f, 0f, isMoreThanHalf = false, isPositiveArc = true, 0.14f, 3.7f)
|
||||
arcToRelative(5.4f, 5.4f, 0f, isMoreThanHalf = false, isPositiveArc = true, 1.44f, 3.76f)
|
||||
curveToRelative(0f, 5.38f, -3.27f, 6.56f, -6.39f, 6.91f)
|
||||
arcToRelative(3.33f, 3.33f, 0f, isMoreThanHalf = false, isPositiveArc = true, 0.95f, 2.59f)
|
||||
curveToRelative(0f, 1.87f, 0f, 3.38f, 0f, 3.84f)
|
||||
reflectiveCurveToRelative(0.25f, 0.81f, 1f, 0.67f)
|
||||
arcTo(14f, 14f, 0f, isMoreThanHalf = false, isPositiveArc = false, 16f, 2f)
|
||||
close()
|
||||
}
|
||||
}.build()
|
||||
|
||||
return _CarbonGithubLogo!!
|
||||
}
|
||||
|
||||
@Suppress("ObjectPropertyName")
|
||||
private var _CarbonGithubLogo: ImageVector? = null
|
||||
@ -0,0 +1,97 @@
|
||||
package dev.krtirtho.spotube.resources.iconsax
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.PathData
|
||||
import androidx.compose.ui.graphics.vector.group
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
val Iconsax.IconsaxInformation: ImageVector
|
||||
get() {
|
||||
if (_IconsaxInformation != null) {
|
||||
return _IconsaxInformation!!
|
||||
}
|
||||
_IconsaxInformation = ImageVector.Builder(
|
||||
name = "IconsaxInformation",
|
||||
defaultWidth = 24.dp,
|
||||
defaultHeight = 24.dp,
|
||||
viewportWidth = 24f,
|
||||
viewportHeight = 24f
|
||||
).apply {
|
||||
group(
|
||||
clipPathData = PathData {
|
||||
moveTo(0f, 0f)
|
||||
horizontalLineToRelative(24f)
|
||||
verticalLineToRelative(24f)
|
||||
horizontalLineToRelative(-24f)
|
||||
close()
|
||||
}
|
||||
) {
|
||||
path(
|
||||
fill = SolidColor(Color.White),
|
||||
fillAlpha = 0.4f,
|
||||
strokeAlpha = 0.4f
|
||||
) {
|
||||
moveTo(10.75f, 2.45f)
|
||||
curveTo(11.45f, 1.86f, 12.58f, 1.86f, 13.26f, 2.45f)
|
||||
lineTo(14.84f, 3.8f)
|
||||
curveTo(15.14f, 4.05f, 15.71f, 4.26f, 16.11f, 4.26f)
|
||||
horizontalLineTo(17.81f)
|
||||
curveTo(18.87f, 4.26f, 19.74f, 5.13f, 19.74f, 6.19f)
|
||||
verticalLineTo(7.89f)
|
||||
curveTo(19.74f, 8.29f, 19.95f, 8.85f, 20.2f, 9.15f)
|
||||
lineTo(21.55f, 10.73f)
|
||||
curveTo(22.14f, 11.43f, 22.14f, 12.56f, 21.55f, 13.24f)
|
||||
lineTo(20.2f, 14.82f)
|
||||
curveTo(19.95f, 15.12f, 19.74f, 15.68f, 19.74f, 16.08f)
|
||||
verticalLineTo(17.78f)
|
||||
curveTo(19.74f, 18.84f, 18.87f, 19.71f, 17.81f, 19.71f)
|
||||
horizontalLineTo(16.11f)
|
||||
curveTo(15.71f, 19.71f, 15.15f, 19.92f, 14.85f, 20.17f)
|
||||
lineTo(13.27f, 21.52f)
|
||||
curveTo(12.57f, 22.11f, 11.44f, 22.11f, 10.76f, 21.52f)
|
||||
lineTo(9.18f, 20.17f)
|
||||
curveTo(8.88f, 19.92f, 8.31f, 19.71f, 7.92f, 19.71f)
|
||||
horizontalLineTo(6.17f)
|
||||
curveTo(5.11f, 19.71f, 4.24f, 18.84f, 4.24f, 17.78f)
|
||||
verticalLineTo(16.07f)
|
||||
curveTo(4.24f, 15.68f, 4.04f, 15.11f, 3.79f, 14.82f)
|
||||
lineTo(2.44f, 13.23f)
|
||||
curveTo(1.86f, 12.54f, 1.86f, 11.42f, 2.44f, 10.73f)
|
||||
lineTo(3.79f, 9.14f)
|
||||
curveTo(4.04f, 8.84f, 4.24f, 8.28f, 4.24f, 7.89f)
|
||||
verticalLineTo(6.2f)
|
||||
curveTo(4.24f, 5.14f, 5.11f, 4.27f, 6.17f, 4.27f)
|
||||
horizontalLineTo(7.9f)
|
||||
curveTo(8.3f, 4.27f, 8.86f, 4.06f, 9.16f, 3.81f)
|
||||
lineTo(10.75f, 2.45f)
|
||||
close()
|
||||
}
|
||||
path(fill = SolidColor(Color.White)) {
|
||||
moveTo(12f, 16.869f)
|
||||
curveTo(11.45f, 16.869f, 11f, 16.419f, 11f, 15.869f)
|
||||
curveTo(11f, 15.319f, 11.44f, 14.869f, 12f, 14.869f)
|
||||
curveTo(12.55f, 14.869f, 13f, 15.319f, 13f, 15.869f)
|
||||
curveTo(13f, 16.419f, 12.56f, 16.869f, 12f, 16.869f)
|
||||
close()
|
||||
}
|
||||
path(fill = SolidColor(Color.White)) {
|
||||
moveTo(12f, 13.721f)
|
||||
curveTo(11.59f, 13.721f, 11.25f, 13.381f, 11.25f, 12.971f)
|
||||
verticalLineTo(8.131f)
|
||||
curveTo(11.25f, 7.721f, 11.59f, 7.381f, 12f, 7.381f)
|
||||
curveTo(12.41f, 7.381f, 12.75f, 7.721f, 12.75f, 8.131f)
|
||||
verticalLineTo(12.961f)
|
||||
curveTo(12.75f, 13.381f, 12.42f, 13.721f, 12f, 13.721f)
|
||||
close()
|
||||
}
|
||||
}
|
||||
}.build()
|
||||
|
||||
return _IconsaxInformation!!
|
||||
}
|
||||
|
||||
@Suppress("ObjectPropertyName")
|
||||
private var _IconsaxInformation: ImageVector? = null
|
||||
138
composeApp/src/commonMain/rust/discord_rpc.rs
Normal file
138
composeApp/src/commonMain/rust/discord_rpc.rs
Normal file
@ -0,0 +1,138 @@
|
||||
use discord_rich_presence::activity::{Activity, ActivityType, Assets, Timestamps};
|
||||
use discord_rich_presence::{DiscordIpc, DiscordIpcClient};
|
||||
use std::sync::Mutex;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[derive(Debug, uniffi::Error)]
|
||||
pub enum DiscordRpcError {
|
||||
ConnectionError { reason: String },
|
||||
UpdateError { reason: String },
|
||||
NotConnected,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DiscordRpcError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DiscordRpcError::ConnectionError { reason } => write!(f, "Connection error: {}", reason),
|
||||
DiscordRpcError::UpdateError { reason } => write!(f, "Update error: {}", reason),
|
||||
DiscordRpcError::NotConnected => write!(f, "Not connected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct DiscordRpcClient {
|
||||
client_id: String,
|
||||
client: Mutex<Option<DiscordIpcClient>>,
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl DiscordRpcClient {
|
||||
#[uniffi::constructor]
|
||||
pub fn new(client_id: String) -> Self {
|
||||
Self {
|
||||
client_id,
|
||||
client: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect(&self) -> Result<(), DiscordRpcError> {
|
||||
let mut client_guard = self.client.lock().unwrap();
|
||||
if client_guard.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut client = DiscordIpcClient::new(&self.client_id);
|
||||
|
||||
client
|
||||
.connect()
|
||||
.map_err(|e| DiscordRpcError::ConnectionError { reason: e.to_string() })?;
|
||||
|
||||
*client_guard = Some(client);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn disconnect(&self) -> Result<(), DiscordRpcError> {
|
||||
let mut client_guard = self.client.lock().unwrap();
|
||||
if let Some(mut client) = client_guard.take() {
|
||||
client
|
||||
.close()
|
||||
.map_err(|e| DiscordRpcError::ConnectionError { reason: e.to_string() })?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_presence(
|
||||
&self,
|
||||
title: String,
|
||||
artist: String,
|
||||
album: String,
|
||||
cover_url: String,
|
||||
position_ms: i64,
|
||||
duration_ms: i64,
|
||||
) -> Result<(), DiscordRpcError> {
|
||||
let mut client_guard = self.client.lock().unwrap();
|
||||
let client = client_guard
|
||||
.as_mut()
|
||||
.ok_or(DiscordRpcError::NotConnected)?;
|
||||
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as i64;
|
||||
|
||||
let start = now - (position_ms / 1000);
|
||||
let end = start + (duration_ms / 1000);
|
||||
|
||||
let details = if title.is_empty() {
|
||||
"Unknown Track".to_string()
|
||||
} else {
|
||||
title
|
||||
};
|
||||
|
||||
let state = if artist.is_empty() {
|
||||
"Unknown Artist".to_string()
|
||||
} else {
|
||||
format!("by {}", artist)
|
||||
};
|
||||
|
||||
let mut activity = Activity::new()
|
||||
.details(&details)
|
||||
.state(&state)
|
||||
.timestamps(Timestamps::new().start(start).end(end))
|
||||
.activity_type(ActivityType::Listening);
|
||||
|
||||
if !album.is_empty() {
|
||||
let mut assets = Assets::new().large_text(&album);
|
||||
if !cover_url.is_empty() {
|
||||
assets = assets.large_image(&cover_url);
|
||||
}
|
||||
activity = activity.assets(assets);
|
||||
} else if !cover_url.is_empty() {
|
||||
activity = activity.assets(Assets::new().large_image(&cover_url));
|
||||
}
|
||||
|
||||
client
|
||||
.set_activity(activity)
|
||||
.map_err(|e| DiscordRpcError::UpdateError { reason: e.to_string() })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear_presence(&self) -> Result<(), DiscordRpcError> {
|
||||
let mut client_guard = self.client.lock().unwrap();
|
||||
let client = client_guard
|
||||
.as_mut()
|
||||
.ok_or(DiscordRpcError::NotConnected)?;
|
||||
|
||||
client
|
||||
.clear_activity()
|
||||
.map_err(|e| DiscordRpcError::UpdateError { reason: e.to_string() })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_connected(&self) -> bool {
|
||||
self.client.lock().unwrap().is_some()
|
||||
}
|
||||
}
|
||||
@ -1,298 +1,7 @@
|
||||
use discord_rich_presence::activity::{Activity, ActivityType, Assets, Timestamps};
|
||||
use discord_rich_presence::{DiscordIpc, DiscordIpcClient};
|
||||
use lofty::config::WriteOptions;
|
||||
use lofty::file::{AudioFile, FileType, TaggedFileExt};
|
||||
use lofty::picture::Picture;
|
||||
use lofty::probe::Probe;
|
||||
use lofty::tag::{Accessor, Tag, TagExt};
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
mod metadata;
|
||||
mod discord_rpc;
|
||||
|
||||
#[derive(uniffi::Record)]
|
||||
pub struct AudioMetadata {
|
||||
pub title: Option<String>,
|
||||
pub artists: Vec<String>,
|
||||
pub album: Option<String>,
|
||||
pub duration_ms: i64,
|
||||
pub track_number: Option<u32>,
|
||||
pub disc_number: Option<u32>,
|
||||
pub cover_bytes: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, uniffi::Error)]
|
||||
pub enum AudioTagError {
|
||||
FileNotFound,
|
||||
ReadError { reason: String },
|
||||
WriteError { reason: String },
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AudioTagError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AudioTagError::FileNotFound => write!(f, "File not found"),
|
||||
AudioTagError::ReadError { reason } => write!(f, "Read error: {}", reason),
|
||||
AudioTagError::WriteError { reason } => write!(f, "Write error: {}", reason),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn read_audio_metadata(file_path: String) -> Result<AudioMetadata, AudioTagError> {
|
||||
let path = Path::new(&file_path);
|
||||
if !path.exists() {
|
||||
return Err(AudioTagError::FileNotFound);
|
||||
}
|
||||
|
||||
let tagged_file = Probe::open(path)
|
||||
.map_err(|e| AudioTagError::ReadError { reason: e.to_string() })?
|
||||
.read()
|
||||
.map_err(|e| AudioTagError::ReadError { reason: e.to_string() })?;
|
||||
|
||||
let duration = tagged_file.properties().duration();
|
||||
let duration_ms = duration.as_millis() as i64;
|
||||
|
||||
let tag = tagged_file.primary_tag().or_else(|| tagged_file.first_tag());
|
||||
|
||||
let mut title = None;
|
||||
let mut artists = Vec::new();
|
||||
let mut album = None;
|
||||
let mut track_number = None;
|
||||
let mut disc_number = None;
|
||||
let mut cover_bytes = None;
|
||||
|
||||
if let Some(tag) = tag {
|
||||
title = tag.title().map(|s| s.to_string());
|
||||
|
||||
if let Some(artist) = tag.artist() {
|
||||
artists = artist
|
||||
.split(';')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
}
|
||||
|
||||
album = tag.album().map(|s| s.to_string());
|
||||
track_number = tag.track();
|
||||
disc_number = tag.disk();
|
||||
|
||||
if let Some(pic) = tag.pictures().first() {
|
||||
cover_bytes = Some(pic.data().to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(AudioMetadata {
|
||||
title,
|
||||
artists,
|
||||
album,
|
||||
duration_ms,
|
||||
track_number,
|
||||
disc_number,
|
||||
cover_bytes,
|
||||
})
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn write_audio_metadata(
|
||||
file_path: String,
|
||||
title: String,
|
||||
artists: String,
|
||||
album: Option<String>,
|
||||
track_number: Option<i32>,
|
||||
disc_number: Option<i32>,
|
||||
cover_bytes: Option<Vec<u8>>,
|
||||
) -> Result<(), AudioTagError> {
|
||||
let path = Path::new(&file_path);
|
||||
if !path.exists() {
|
||||
return Err(AudioTagError::FileNotFound);
|
||||
}
|
||||
|
||||
let mut tagged_file = Probe::open(path)
|
||||
.map_err(|e| AudioTagError::ReadError { reason: e.to_string() })?
|
||||
.read()
|
||||
.map_err(|e| AudioTagError::ReadError { reason: e.to_string() })?;
|
||||
|
||||
let file_type = tagged_file.file_type();
|
||||
|
||||
if tagged_file.primary_tag().is_none() && tagged_file.first_tag().is_none() {
|
||||
let new_tag = match file_type {
|
||||
FileType::Mpeg => Tag::new(lofty::tag::TagType::Id3v2),
|
||||
FileType::Mp4 => Tag::new(lofty::tag::TagType::Mp4Ilst),
|
||||
FileType::Flac => Tag::new(lofty::tag::TagType::VorbisComments),
|
||||
FileType::Opus | FileType::Vorbis => Tag::new(lofty::tag::TagType::VorbisComments),
|
||||
_ => Tag::new(lofty::tag::TagType::Id3v2),
|
||||
};
|
||||
tagged_file.insert_tag(new_tag);
|
||||
}
|
||||
|
||||
let tag = if tagged_file.primary_tag().is_some() {
|
||||
tagged_file.primary_tag_mut().unwrap()
|
||||
} else if tagged_file.first_tag().is_some() {
|
||||
tagged_file.first_tag_mut().unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
tag.set_title(title);
|
||||
tag.set_artist(artists);
|
||||
|
||||
if let Some(album_name) = album {
|
||||
tag.set_album(album_name);
|
||||
}
|
||||
|
||||
if let Some(num) = track_number {
|
||||
tag.set_track(num as u32);
|
||||
}
|
||||
|
||||
if let Some(num) = disc_number {
|
||||
tag.set_disk(num as u32);
|
||||
}
|
||||
|
||||
if let Some(bytes) = cover_bytes {
|
||||
if !bytes.is_empty() {
|
||||
let picture = Picture::unchecked(bytes).build();
|
||||
tag.push_picture(picture);
|
||||
}
|
||||
}
|
||||
|
||||
tag.save_to_path(path, WriteOptions::default())
|
||||
.map_err(|e| AudioTagError::WriteError { reason: e.to_string() })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, uniffi::Error)]
|
||||
pub enum DiscordRpcError {
|
||||
ConnectionError { reason: String },
|
||||
UpdateError { reason: String },
|
||||
NotConnected,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DiscordRpcError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DiscordRpcError::ConnectionError { reason } => write!(f, "Connection error: {}", reason),
|
||||
DiscordRpcError::UpdateError { reason } => write!(f, "Update error: {}", reason),
|
||||
DiscordRpcError::NotConnected => write!(f, "Not connected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct DiscordRpcClient {
|
||||
client_id: String,
|
||||
client: Mutex<Option<DiscordIpcClient>>,
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl DiscordRpcClient {
|
||||
#[uniffi::constructor]
|
||||
pub fn new(client_id: String) -> Self {
|
||||
Self {
|
||||
client_id,
|
||||
client: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect(&self) -> Result<(), DiscordRpcError> {
|
||||
let mut client_guard = self.client.lock().unwrap();
|
||||
if client_guard.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut client = DiscordIpcClient::new(&self.client_id);
|
||||
|
||||
client
|
||||
.connect()
|
||||
.map_err(|e| DiscordRpcError::ConnectionError { reason: e.to_string() })?;
|
||||
|
||||
*client_guard = Some(client);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn disconnect(&self) -> Result<(), DiscordRpcError> {
|
||||
let mut client_guard = self.client.lock().unwrap();
|
||||
if let Some(mut client) = client_guard.take() {
|
||||
client
|
||||
.close()
|
||||
.map_err(|e| DiscordRpcError::ConnectionError { reason: e.to_string() })?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_presence(
|
||||
&self,
|
||||
title: String,
|
||||
artist: String,
|
||||
album: String,
|
||||
cover_url: String,
|
||||
position_ms: i64,
|
||||
duration_ms: i64,
|
||||
) -> Result<(), DiscordRpcError> {
|
||||
let mut client_guard = self.client.lock().unwrap();
|
||||
let client = client_guard
|
||||
.as_mut()
|
||||
.ok_or(DiscordRpcError::NotConnected)?;
|
||||
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as i64;
|
||||
|
||||
let start = now - (position_ms / 1000);
|
||||
let end = start + (duration_ms / 1000);
|
||||
|
||||
let details = if title.is_empty() {
|
||||
"Unknown Track".to_string()
|
||||
} else {
|
||||
title
|
||||
};
|
||||
|
||||
let state = if artist.is_empty() {
|
||||
"Unknown Artist".to_string()
|
||||
} else {
|
||||
format!("by {}", artist)
|
||||
};
|
||||
|
||||
let mut activity = Activity::new()
|
||||
.details(&details)
|
||||
.state(&state)
|
||||
.timestamps(Timestamps::new().start(start).end(end))
|
||||
.activity_type(ActivityType::Listening);
|
||||
|
||||
if !album.is_empty() {
|
||||
let mut assets = Assets::new().large_text(&album);
|
||||
if !cover_url.is_empty() {
|
||||
assets = assets.large_image(&cover_url);
|
||||
}
|
||||
activity = activity.assets(assets);
|
||||
} else if !cover_url.is_empty() {
|
||||
activity = activity.assets(Assets::new().large_image(&cover_url));
|
||||
}
|
||||
|
||||
client
|
||||
.set_activity(activity)
|
||||
.map_err(|e| DiscordRpcError::UpdateError { reason: e.to_string() })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear_presence(&self) -> Result<(), DiscordRpcError> {
|
||||
let mut client_guard = self.client.lock().unwrap();
|
||||
let client = client_guard
|
||||
.as_mut()
|
||||
.ok_or(DiscordRpcError::NotConnected)?;
|
||||
|
||||
client
|
||||
.clear_activity()
|
||||
.map_err(|e| DiscordRpcError::UpdateError { reason: e.to_string() })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_connected(&self) -> bool {
|
||||
self.client.lock().unwrap().is_some()
|
||||
}
|
||||
}
|
||||
pub use metadata::*;
|
||||
pub use discord_rpc::*;
|
||||
|
||||
uniffi::setup_scaffolding!();
|
||||
|
||||
158
composeApp/src/commonMain/rust/metadata.rs
Normal file
158
composeApp/src/commonMain/rust/metadata.rs
Normal file
@ -0,0 +1,158 @@
|
||||
use lofty::config::WriteOptions;
|
||||
use lofty::file::{AudioFile, FileType, TaggedFileExt};
|
||||
use lofty::picture::Picture;
|
||||
use lofty::probe::Probe;
|
||||
use lofty::tag::{Accessor, Tag, TagExt};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(uniffi::Record)]
|
||||
pub struct AudioMetadata {
|
||||
pub title: Option<String>,
|
||||
pub artists: Vec<String>,
|
||||
pub album: Option<String>,
|
||||
pub duration_ms: i64,
|
||||
pub track_number: Option<u32>,
|
||||
pub disc_number: Option<u32>,
|
||||
pub cover_bytes: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, uniffi::Error)]
|
||||
pub enum AudioTagError {
|
||||
FileNotFound,
|
||||
ReadError { reason: String },
|
||||
WriteError { reason: String },
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AudioTagError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AudioTagError::FileNotFound => write!(f, "File not found"),
|
||||
AudioTagError::ReadError { reason } => write!(f, "Read error: {}", reason),
|
||||
AudioTagError::WriteError { reason } => write!(f, "Write error: {}", reason),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
pub fn read_audio_metadata(file_path: String) -> Result<AudioMetadata, AudioTagError> {
|
||||
let path = Path::new(&file_path);
|
||||
if !path.exists() {
|
||||
return Err(AudioTagError::FileNotFound);
|
||||
}
|
||||
|
||||
let tagged_file = Probe::open(path)
|
||||
.map_err(|e| AudioTagError::ReadError { reason: e.to_string() })?
|
||||
.read()
|
||||
.map_err(|e| AudioTagError::ReadError { reason: e.to_string() })?;
|
||||
|
||||
let duration = tagged_file.properties().duration();
|
||||
let duration_ms = duration.as_millis() as i64;
|
||||
|
||||
let tag = tagged_file.primary_tag().or_else(|| tagged_file.first_tag());
|
||||
|
||||
let mut title = None;
|
||||
let mut artists = Vec::new();
|
||||
let mut album = None;
|
||||
let mut track_number = None;
|
||||
let mut disc_number = None;
|
||||
let mut cover_bytes = None;
|
||||
|
||||
if let Some(tag) = tag {
|
||||
title = tag.title().map(|s| s.to_string());
|
||||
|
||||
if let Some(artist) = tag.artist() {
|
||||
artists = artist
|
||||
.split(';')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
}
|
||||
|
||||
album = tag.album().map(|s| s.to_string());
|
||||
track_number = tag.track();
|
||||
disc_number = tag.disk();
|
||||
|
||||
if let Some(pic) = tag.pictures().first() {
|
||||
cover_bytes = Some(pic.data().to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(AudioMetadata {
|
||||
title,
|
||||
artists,
|
||||
album,
|
||||
duration_ms,
|
||||
track_number,
|
||||
disc_number,
|
||||
cover_bytes,
|
||||
})
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
pub fn write_audio_metadata(
|
||||
file_path: String,
|
||||
title: String,
|
||||
artists: String,
|
||||
album: Option<String>,
|
||||
track_number: Option<i32>,
|
||||
disc_number: Option<i32>,
|
||||
cover_bytes: Option<Vec<u8>>,
|
||||
) -> Result<(), AudioTagError> {
|
||||
let path = Path::new(&file_path);
|
||||
if !path.exists() {
|
||||
return Err(AudioTagError::FileNotFound);
|
||||
}
|
||||
|
||||
let mut tagged_file = Probe::open(path)
|
||||
.map_err(|e| AudioTagError::ReadError { reason: e.to_string() })?
|
||||
.read()
|
||||
.map_err(|e| AudioTagError::ReadError { reason: e.to_string() })?;
|
||||
|
||||
let file_type = tagged_file.file_type();
|
||||
|
||||
if tagged_file.primary_tag().is_none() && tagged_file.first_tag().is_none() {
|
||||
let new_tag = match file_type {
|
||||
FileType::Mpeg => Tag::new(lofty::tag::TagType::Id3v2),
|
||||
FileType::Mp4 => Tag::new(lofty::tag::TagType::Mp4Ilst),
|
||||
FileType::Flac => Tag::new(lofty::tag::TagType::VorbisComments),
|
||||
FileType::Opus | FileType::Vorbis => Tag::new(lofty::tag::TagType::VorbisComments),
|
||||
_ => Tag::new(lofty::tag::TagType::Id3v2),
|
||||
};
|
||||
tagged_file.insert_tag(new_tag);
|
||||
}
|
||||
|
||||
let tag = if tagged_file.primary_tag().is_some() {
|
||||
tagged_file.primary_tag_mut().unwrap()
|
||||
} else if tagged_file.first_tag().is_some() {
|
||||
tagged_file.first_tag_mut().unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
tag.set_title(title);
|
||||
tag.set_artist(artists);
|
||||
|
||||
if let Some(album_name) = album {
|
||||
tag.set_album(album_name);
|
||||
}
|
||||
|
||||
if let Some(num) = track_number {
|
||||
tag.set_track(num as u32);
|
||||
}
|
||||
|
||||
if let Some(num) = disc_number {
|
||||
tag.set_disk(num as u32);
|
||||
}
|
||||
|
||||
if let Some(bytes) = cover_bytes {
|
||||
if !bytes.is_empty() {
|
||||
let picture = Picture::unchecked(bytes).build();
|
||||
tag.push_picture(picture);
|
||||
}
|
||||
}
|
||||
|
||||
tag.save_to_path(path, WriteOptions::default())
|
||||
.map_err(|e| AudioTagError::WriteError { reason: e.to_string() })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -17,6 +17,8 @@
|
||||
|
||||
package dev.krtirtho.spotube
|
||||
|
||||
import platform.Foundation.NSURL
|
||||
import platform.UIKit.UIApplication
|
||||
import platform.UIKit.UIDevice
|
||||
|
||||
class IOSPlatform: Platform {
|
||||
@ -25,3 +27,8 @@ class IOSPlatform: Platform {
|
||||
}
|
||||
|
||||
actual fun getPlatform(): Platform = IOSPlatform()
|
||||
|
||||
actual fun openUrlInBrowser(url: String) {
|
||||
val nsUrl = NSURL.URLWithString(url) ?: return
|
||||
UIApplication.sharedApplication.openURL(nsUrl)
|
||||
}
|
||||
@ -18,6 +18,24 @@
|
||||
package dev.krtirtho.spotube.core.webview
|
||||
|
||||
import io.github.kdroidfilter.webview.web.WebViewState
|
||||
import kotlinx.cinterop.ExperimentalForeignApi
|
||||
import platform.Foundation.NSDate
|
||||
import platform.Foundation.NSHTTPCookieStorage
|
||||
import platform.Foundation.distantPast
|
||||
import platform.WebKit.WKWebsiteDataStore
|
||||
import platform.WebKit.WKWebsiteDataTypeCookies
|
||||
import platform.WebKit.WKWebsiteDataTypeLocalStorage
|
||||
|
||||
actual fun platformWebviewConfig(webView: WebViewState) {
|
||||
actual fun platformWebviewConfig(webView: WebViewState, pluginId: String?) {
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalForeignApi::class)
|
||||
actual suspend fun platformClearWebviewData(pluginId: String?) {
|
||||
if (pluginId == null) return
|
||||
|
||||
val dataStore = WKWebsiteDataStore.defaultDataStore()
|
||||
val dataTypes = setOf(WKWebsiteDataTypeCookies, WKWebsiteDataTypeLocalStorage)
|
||||
dataStore.removeDataOfTypes(dataTypes, NSDate.distantPast) {}
|
||||
|
||||
NSHTTPCookieStorage.sharedHTTPCookieStorage.removeCookiesSinceDate(NSDate.distantPast)
|
||||
}
|
||||
@ -17,6 +17,9 @@
|
||||
|
||||
package dev.krtirtho.spotube
|
||||
|
||||
import java.awt.Desktop
|
||||
import java.net.URI
|
||||
|
||||
class JVMPlatform : Platform {
|
||||
override val name: String = "Java ${System.getProperty("java.version")}"
|
||||
override val type: PlatformType = System.getProperty("os.name").let { osName ->
|
||||
@ -34,3 +37,7 @@ class JVMPlatform : Platform {
|
||||
}
|
||||
|
||||
actual fun getPlatform(): Platform = JVMPlatform()
|
||||
|
||||
actual fun openUrlInBrowser(url: String) {
|
||||
Desktop.getDesktop().browse(URI(url))
|
||||
}
|
||||
@ -19,13 +19,24 @@ package dev.krtirtho.spotube.core.webview
|
||||
|
||||
import dev.krtirtho.spotube.core.paths.Paths
|
||||
import io.github.kdroidfilter.webview.web.WebViewState
|
||||
import io.github.vinceglb.filekit.utils.div
|
||||
import io.github.vinceglb.filekit.utils.toPath
|
||||
import okio.FileSystem
|
||||
import okio.Path.Companion.toPath
|
||||
import org.koin.core.context.GlobalContext
|
||||
|
||||
actual fun platformWebviewConfig(webView: WebViewState) {
|
||||
actual fun platformWebviewConfig(webView: WebViewState, pluginId: String?) {
|
||||
val paths = GlobalContext.get().get<Paths>()
|
||||
|
||||
webView.webSettings.desktopWebSettings.dataDirectory =
|
||||
(paths.getApplicationCacheDirPath().toPath() / "webview_data").toString()
|
||||
val baseDir = "${paths.getApplicationCacheDirPath()}/webview_data".toPath()
|
||||
val dataDir = if (pluginId != null) baseDir / pluginId else baseDir
|
||||
webView.webSettings.desktopWebSettings.dataDirectory = dataDir.toString()
|
||||
}
|
||||
|
||||
actual suspend fun platformClearWebviewData(pluginId: String?) {
|
||||
if (pluginId == null) return
|
||||
val paths = GlobalContext.get().get<Paths>()
|
||||
val dataDirStr = "${paths.getApplicationCacheDirPath()}/webview_data/$pluginId"
|
||||
val dataDir = dataDirStr.toPath()
|
||||
if (FileSystem.SYSTEM.exists(dataDir)) {
|
||||
FileSystem.SYSTEM.deleteRecursively(dataDir)
|
||||
}
|
||||
}
|
||||
@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.audio
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.core
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.lyrics
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.metadata
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.metadata
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.metadata
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.metadata
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.metadata
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.metadata
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.metadata
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.metadata
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.js_plugin_example.plugin_apis.scrobble
|
||||
|
||||
@ -1,31 +1,23 @@
|
||||
# Copyright 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
# Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
pre-commit:
|
||||
parallel: true
|
||||
commands:
|
||||
# 1. Protect the AGPL Core (Exclude the Apache libraries)
|
||||
agpl-headers:
|
||||
glob: "*.{kt,kts,xml}"
|
||||
exclude: "(js_plugin_example|plugin_interfaces)/"
|
||||
glob: "*.{kt,kts,xml,yaml,yml}"
|
||||
run: addlicense -f .github/agpl_header.txt {staged_files}
|
||||
stage_fixed: true
|
||||
|
||||
# 2. Protect the Apache Libraries (Only include those folders)
|
||||
apache-headers:
|
||||
glob: "(js_plugin_example|plugin_interfaces)/**/*.{kt,kts,xml}"
|
||||
run: addlicense -f .github/apache_header.txt {staged_files}
|
||||
stage_fixed: true
|
||||
@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.core
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.core.browser_apis
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.logger
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.core
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.core.browser_apis
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.logger
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.spotor
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.spotor
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.spotor
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.spotor
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.spotor
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.spotor
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.spotor
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.spotor
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.host_apis
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.host_apis
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.host_apis
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.host_apis
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.host_apis
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.audio
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.audio
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.core
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.core
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.lyrics
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.lyrics
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.album
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.album
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.artist
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.artist
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.browse
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.common
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.playlist
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.playlist
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.search
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.search
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.track
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.track
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.user
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.metadata.user
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.scrobble
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.plugin_apis.scrobble
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.core
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.core.browser_apis
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.logger
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.core
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.core.browser_apis
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.core.browser_apis
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.logger
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.core
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.core.browser_apis
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package dev.krtirtho.plugin_interfaces.extras.logger
|
||||
|
||||
Loading…
Reference in New Issue
Block a user