mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Option to load from server plugin
This commit is contained in:
parent
f02f66c506
commit
a27bb2a962
8 changed files with 104 additions and 5 deletions
|
|
@ -16,6 +16,7 @@ import com.github.damontecres.wholphin.util.EqualityMutableLiveData
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import org.jellyfin.sdk.Jellyfin
|
import org.jellyfin.sdk.Jellyfin
|
||||||
|
|
@ -54,6 +55,8 @@ class ServerRepository
|
||||||
val currentServer: LiveData<JellyfinServer?> get() = _current.map { it?.server }
|
val currentServer: LiveData<JellyfinServer?> get() = _current.map { it?.server }
|
||||||
val currentUser: LiveData<JellyfinUser?> get() = _current.map { it?.user }
|
val currentUser: LiveData<JellyfinUser?> get() = _current.map { it?.user }
|
||||||
|
|
||||||
|
val serverPluginInstalled = MutableStateFlow<Boolean>(false)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a server to the app database and updated the [ApiClient] to the server's URL
|
* Adds a server to the app database and updated the [ApiClient] to the server's URL
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ class HomeSettingsService
|
||||||
try {
|
try {
|
||||||
block.invoke()
|
block.invoke()
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.w(ex, "Error loading local settings")
|
Timber.w(ex, "Error loading settings")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,6 +213,7 @@ class HomeSettingsService
|
||||||
suspend fun loadCurrentSettings(userId: UUID) {
|
suspend fun loadCurrentSettings(userId: UUID) {
|
||||||
Timber.v("Getting setting for %s", userId)
|
Timber.v("Getting setting for %s", userId)
|
||||||
// User local then server/remote otherwise create a default
|
// User local then server/remote otherwise create a default
|
||||||
|
// TODO figure out priority order
|
||||||
val settings =
|
val settings =
|
||||||
tryLoad {
|
tryLoad {
|
||||||
serverPluginApi.fetchHomePageSettings()
|
serverPluginApi.fetchHomePageSettings()
|
||||||
|
|
|
||||||
|
|
@ -272,6 +272,7 @@ class UserSwitchListener
|
||||||
private val seerrServerDao: SeerrServerDao,
|
private val seerrServerDao: SeerrServerDao,
|
||||||
private val seerrApi: SeerrApi,
|
private val seerrApi: SeerrApi,
|
||||||
private val homeSettingsService: HomeSettingsService,
|
private val homeSettingsService: HomeSettingsService,
|
||||||
|
private val serverPluginApi: ServerPluginApi,
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
context as AppCompatActivity
|
context as AppCompatActivity
|
||||||
|
|
@ -289,8 +290,19 @@ class UserSwitchListener
|
||||||
|
|
||||||
private suspend fun switchUser(user: JellyfinUser) =
|
private suspend fun switchUser(user: JellyfinUser) =
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
// Check for home settings
|
// Check if plugin is installed, then for home settings
|
||||||
launchIO {
|
launchIO {
|
||||||
|
val serverPluginInstalled =
|
||||||
|
try {
|
||||||
|
serverPluginApi.public()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex, "Error checking for server plugin")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Timber.i("Server plugin installed: %s", serverPluginInstalled)
|
||||||
|
serverRepository.serverPluginInstalled.value = serverPluginInstalled
|
||||||
|
|
||||||
|
// Check for home settings
|
||||||
homeSettingsService.loadCurrentSettings(user.id)
|
homeSettingsService.loadCurrentSettings(user.id)
|
||||||
}
|
}
|
||||||
if (BuildConfig.DISCOVER_ENABLED) {
|
if (BuildConfig.DISCOVER_ENABLED) {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
import org.jellyfin.sdk.api.client.exception.ApiClientException
|
import org.jellyfin.sdk.api.client.exception.ApiClientException
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
@ -22,8 +23,24 @@ class ServerPluginApi
|
||||||
private fun createUrl(path: String): String? =
|
private fun createUrl(path: String): String? =
|
||||||
api.baseUrl?.let { if (it.endsWith("/")) "${it}wholphin/$path" else "$it/wholphin/$path" }
|
api.baseUrl?.let { if (it.endsWith("/")) "${it}wholphin/$path" else "$it/wholphin/$path" }
|
||||||
|
|
||||||
|
private val json =
|
||||||
|
Json {
|
||||||
|
ignoreUnknownKeys = false
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val HOME_CONFIG_PATH = "home"
|
private const val HOME_CONFIG_PATH = "homesettings"
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun public(): Boolean {
|
||||||
|
val url = createUrl("public") ?: return false
|
||||||
|
val request =
|
||||||
|
Request
|
||||||
|
.Builder()
|
||||||
|
.url(url)
|
||||||
|
.get()
|
||||||
|
.build()
|
||||||
|
return okHttpClient.newCall(request).execute().isSuccessful
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
|
@ -37,11 +54,12 @@ class ServerPluginApi
|
||||||
.build()
|
.build()
|
||||||
return okHttpClient.newCall(request).execute().use { res ->
|
return okHttpClient.newCall(request).execute().use { res ->
|
||||||
if (res.isSuccessful) {
|
if (res.isSuccessful) {
|
||||||
Json.decodeFromStream<HomePageSettings>(res.body.byteStream())
|
json.decodeFromStream<HomePageSettings>(res.body.byteStream())
|
||||||
} else if (res.code == 404) {
|
} else if (res.code == 404) {
|
||||||
|
Timber.w("fetchHomePageSettings returned 404")
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
throw ApiClientException(res.body.string())
|
throw ApiClientException(res.code.toString() + " " + res.body.string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ fun HomeSettingsGlobal(
|
||||||
onClickSave: () -> Unit,
|
onClickSave: () -> Unit,
|
||||||
onClickLoad: () -> Unit,
|
onClickLoad: () -> Unit,
|
||||||
onClickLoadWeb: () -> Unit,
|
onClickLoadWeb: () -> Unit,
|
||||||
|
serverPluginActive: Boolean,
|
||||||
|
onClickLoadPlugin: () -> Unit,
|
||||||
onClickReset: () -> Unit,
|
onClickReset: () -> Unit,
|
||||||
onClickViewNextUp: () -> Unit,
|
onClickViewNextUp: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
@ -166,6 +168,22 @@ fun HomeSettingsGlobal(
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (serverPluginActive) {
|
||||||
|
item {
|
||||||
|
HomeSettingsListItem(
|
||||||
|
selected = false,
|
||||||
|
headlineText = stringResource(R.string.load_from_server_plugin),
|
||||||
|
leadingContent = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.fa_download),
|
||||||
|
fontFamily = FontAwesome,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = onClickLoadPlugin,
|
||||||
|
modifier = Modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
item {
|
item {
|
||||||
HomeSettingsListItem(
|
HomeSettingsListItem(
|
||||||
selected = false,
|
selected = false,
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ fun HomeSettingsPage(
|
||||||
var showRemovedNextUpDialog by remember { mutableStateOf(false) }
|
var showRemovedNextUpDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val state by viewModel.state.collectAsState()
|
val state by viewModel.state.collectAsState()
|
||||||
|
val serverPluginActive by viewModel.serverPluginActive.collectAsState()
|
||||||
var position by rememberPosition(0, 0)
|
var position by rememberPosition(0, 0)
|
||||||
// TODO discover rows
|
// TODO discover rows
|
||||||
val discoverEnabled = false // by viewModel.discoverEnabled.collectAsState(false)
|
val discoverEnabled = false // by viewModel.discoverEnabled.collectAsState(false)
|
||||||
|
|
@ -274,6 +275,13 @@ fun HomeSettingsPage(
|
||||||
viewModel.loadFromRemote()
|
viewModel.loadFromRemote()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
serverPluginActive = serverPluginActive,
|
||||||
|
onClickLoadPlugin = {
|
||||||
|
showConfirmDialog =
|
||||||
|
ShowConfirm(R.string.overwrite_local_settings) {
|
||||||
|
viewModel.loadFromRemotePlugin()
|
||||||
|
}
|
||||||
|
},
|
||||||
onClickLoadWeb = {
|
onClickLoadWeb = {
|
||||||
showConfirmDialog =
|
showConfirmDialog =
|
||||||
ShowConfirm(R.string.overwrite_local_settings) {
|
ShowConfirm(R.string.overwrite_local_settings) {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import com.github.damontecres.wholphin.services.HomeRowConfigDisplay
|
||||||
import com.github.damontecres.wholphin.services.HomeSettingsService
|
import com.github.damontecres.wholphin.services.HomeSettingsService
|
||||||
import com.github.damontecres.wholphin.services.NavDrawerService
|
import com.github.damontecres.wholphin.services.NavDrawerService
|
||||||
import com.github.damontecres.wholphin.services.SeerrServerRepository
|
import com.github.damontecres.wholphin.services.SeerrServerRepository
|
||||||
|
import com.github.damontecres.wholphin.services.ServerPluginApi
|
||||||
import com.github.damontecres.wholphin.services.UnsupportedHomeSettingsVersionException
|
import com.github.damontecres.wholphin.services.UnsupportedHomeSettingsVersionException
|
||||||
import com.github.damontecres.wholphin.services.UserPreferencesService
|
import com.github.damontecres.wholphin.services.UserPreferencesService
|
||||||
import com.github.damontecres.wholphin.services.hilt.IoCoroutineScope
|
import com.github.damontecres.wholphin.services.hilt.IoCoroutineScope
|
||||||
|
|
@ -74,12 +75,15 @@ class HomeSettingsViewModel
|
||||||
private val navDrawerService: NavDrawerService,
|
private val navDrawerService: NavDrawerService,
|
||||||
private val backdropService: BackdropService,
|
private val backdropService: BackdropService,
|
||||||
private val seerrServerRepository: SeerrServerRepository,
|
private val seerrServerRepository: SeerrServerRepository,
|
||||||
|
private val serverPluginApi: ServerPluginApi,
|
||||||
val preferencesDataStore: DataStore<AppPreferences>,
|
val preferencesDataStore: DataStore<AppPreferences>,
|
||||||
@param:IoCoroutineScope private val ioScope: CoroutineScope,
|
@param:IoCoroutineScope private val ioScope: CoroutineScope,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _state = MutableStateFlow(HomePageSettingsState.EMPTY)
|
private val _state = MutableStateFlow(HomePageSettingsState.EMPTY)
|
||||||
val state: StateFlow<HomePageSettingsState> = _state
|
val state: StateFlow<HomePageSettingsState> = _state
|
||||||
|
|
||||||
|
val serverPluginActive get() = serverRepository.serverPluginInstalled
|
||||||
|
|
||||||
private var idCounter by Delegates.notNull<Int>()
|
private var idCounter by Delegates.notNull<Int>()
|
||||||
|
|
||||||
val discoverEnabled = seerrServerRepository.active
|
val discoverEnabled = seerrServerRepository.active
|
||||||
|
|
@ -767,6 +771,40 @@ class HomeSettingsViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loadFromRemotePlugin() {
|
||||||
|
viewModelScope.launchIO {
|
||||||
|
Timber.d("Loading home settings from server plugin")
|
||||||
|
try {
|
||||||
|
// TODO should this remove local settings?
|
||||||
|
// TODO how to set up priorities
|
||||||
|
_state.update { it.copy(loading = LoadingState.Loading) }
|
||||||
|
val result = serverPluginApi.fetchHomePageSettings()
|
||||||
|
if (result != null) {
|
||||||
|
Timber.v("Got remote settings")
|
||||||
|
val newRows =
|
||||||
|
result.rows.mapIndexed { index, config ->
|
||||||
|
homeSettingsService.resolve(index, config)
|
||||||
|
}
|
||||||
|
idCounter = newRows.maxOfOrNull { it.id }?.plus(1) ?: 0
|
||||||
|
_state.update {
|
||||||
|
it.copy(rows = newRows)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Timber.v("No server plugin settings")
|
||||||
|
showToast(context, "No server plugin settings found")
|
||||||
|
}
|
||||||
|
fetchRowData()
|
||||||
|
} catch (ex: UnsupportedHomeSettingsVersionException) {
|
||||||
|
// TODO
|
||||||
|
Timber.w(ex)
|
||||||
|
showToast(context, "Error: ${ex.localizedMessage}")
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex)
|
||||||
|
showToast(context, "Error: ${ex.localizedMessage}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class HomePageSettingsState(
|
data class HomePageSettingsState(
|
||||||
|
|
|
||||||
|
|
@ -774,6 +774,7 @@
|
||||||
<string name="skip_behavior_summary">For intros, credits, etc</string>
|
<string name="skip_behavior_summary">For intros, credits, etc</string>
|
||||||
<string name="play_with">Play with</string>
|
<string name="play_with">Play with</string>
|
||||||
<string name="transcoding">Transcoding</string>
|
<string name="transcoding">Transcoding</string>
|
||||||
|
<string name="load_from_server_plugin">Load default from server</string>
|
||||||
<array name="ass_subtitle_modes">
|
<array name="ass_subtitle_modes">
|
||||||
<item>@string/ass_subtitle_mode_libass</item>
|
<item>@string/ass_subtitle_mode_libass</item>
|
||||||
<item>@string/ass_subtitle_mode_exoplayer</item>
|
<item>@string/ass_subtitle_mode_exoplayer</item>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue