Skip to content

Commit

Permalink
refactor(tmdb): introduce new catalog system for providers
Browse files Browse the repository at this point in the history
Refactored code, focusing on the TMDB model, to introduce a new catalog system for custom providers. This allows developers and users to configure and dynamically choose which films appear in the app.

- Reconfigured provider system to support new changes/features and have their own catalogs
- TMDB remains the main source of metadata/films provider
- Added initial unit test for TMDB repository
- This commit is depressing af

This refactoring lays the groundwork for more flexible content management and will be followed by additional unit tests to reduce build times and ensure code quality.
  • Loading branch information
rhenwinch committed Jun 20, 2024
1 parent 93d847f commit 2ecd1dd
Show file tree
Hide file tree
Showing 177 changed files with 2,655 additions and 1,937 deletions.
24 changes: 24 additions & 0 deletions .github/create_provider_stubs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

DIR="build/stubs"
FILE="$DIR/AndroidManifest.xml"

rm -rf $DIR

./gradlew generateStubsJar

mkdir -p $DIR

cat <<EOL > $FILE
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.flixclusive" >
<uses-sdk android:minSdkVersion="21" />
</manifest>
EOL

cd $DIR || exit
zip provider-stubs.aar *
echo "[AAR]> Stubs have been generated"
10 changes: 5 additions & 5 deletions .github/workflows/Build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ jobs:
with:
arguments: assembleRelease bundleReleaseClassesToCompileJar

- name: Generate class stubs
uses: gradle/gradle-command-action@v2
with:
arguments: generateStubsJar
- name: Generate provider stubs
run: |
chmod +x .github/create_provider_stubs.sh
.github/create_provider_stubs.sh
- name: Sign release APK
uses: r0adkll/sign-android-release@v1
Expand Down Expand Up @@ -110,7 +110,7 @@ jobs:
| release | ${{ env.RELEASE_SHA }} |
files: |
flixclusive-release.apk
app/build/classes.jar
build/stubs/provider-stubs.aar
draft: false
prerelease: true
env:
Expand Down
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class GlobalCrashHandler(
try {
val softwareInfo = getSoftwareInfo()
val errorMessage = exception.stackTraceToString()
errorLog(errorMessage)
errorLog(exception)
errorLog(softwareInfo)

val intent = Intent(context, CrashActivity::class.java).apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ import com.flixclusive.feature.mobile.watchlist.destinations.WatchlistScreenDest
import com.flixclusive.feature.splashScreen.SplashScreenNavigator
import com.flixclusive.gradle.entities.ProviderData
import com.flixclusive.gradle.entities.Repository
import com.flixclusive.model.configuration.CategoryItem
import com.flixclusive.model.tmdb.category.Category
import com.flixclusive.model.tmdb.Film
import com.flixclusive.model.tmdb.Genre
import com.flixclusive.model.tmdb.TMDBEpisode
import com.flixclusive.model.tmdb.common.tv.Episode
import com.flixclusive.util.navGraph
import com.flixclusive.util.navigateIfResumed
import com.ramcosta.composedestinations.dynamic.within
Expand All @@ -55,7 +55,7 @@ internal class MobileAppNavigator(
navController.navigateIfResumed(SearchExpandedScreenDestination within destination.navGraph())
}

override fun openSeeAllScreen(item: CategoryItem) {
override fun openSeeAllScreen(item: Category) {
navController.navigateIfResumed(SeeAllScreenDestination(item = item) within destination.navGraph())
}

Expand Down Expand Up @@ -125,7 +125,7 @@ internal class MobileAppNavigator(

override fun onEpisodeChange(
film: Film,
episodeToPlay: TMDBEpisode
episodeToPlay: Episode
) {
navController.navigate(
PlayerScreenDestination(
Expand Down
40 changes: 19 additions & 21 deletions app/src/main/kotlin/com/flixclusive/mobile/MobileAppViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.flixclusive.core.ui.mobile.KeyEventHandler
import com.flixclusive.core.util.common.resource.Resource
import com.flixclusive.core.util.film.FilmType
import com.flixclusive.data.configuration.AppConfigurationManager
import com.flixclusive.data.util.InternetMonitor
import com.flixclusive.data.watch_history.WatchHistoryRepository
Expand All @@ -18,9 +17,8 @@ import com.flixclusive.model.database.toWatchlistItem
import com.flixclusive.model.provider.SourceData
import com.flixclusive.model.provider.SourceDataState
import com.flixclusive.model.tmdb.Film
import com.flixclusive.model.tmdb.Movie
import com.flixclusive.model.tmdb.TMDBEpisode
import com.flixclusive.model.tmdb.TvShow
import com.flixclusive.model.tmdb.FilmDetails
import com.flixclusive.model.tmdb.common.tv.Episode
import com.flixclusive.model.tmdb.toFilmInstance
import com.flixclusive.provider.util.FlixclusiveWebView
import dagger.hilt.android.lifecycle.HiltViewModel
Expand Down Expand Up @@ -66,14 +64,14 @@ internal class MobileAppViewModel @Inject constructor(
private val _uiState = MutableStateFlow(MobileAppUiState())
val uiState: StateFlow<MobileAppUiState> = _uiState.asStateFlow()

private val _episodeToPlay = MutableStateFlow<TMDBEpisode?>(null)
private val _episodeToPlay = MutableStateFlow<Episode?>(null)
val episodeToPlay = _episodeToPlay.asStateFlow()

private val _filmToPreview = MutableStateFlow<Film?>(null)
val filmToPreview = _filmToPreview.asStateFlow()

val loadedSourceData: SourceData?
get() = _filmToPreview.value?.id?.let {
get() = _filmToPreview.value?.identifier?.let {
sourceLinksProvider.getLinks(
filmId = it,
episode = _episodeToPlay.value,
Expand All @@ -82,7 +80,7 @@ internal class MobileAppViewModel @Inject constructor(

fun initializeConfigsIfNull() {
configurationManager.run {
if(homeCategoriesConfig == null || searchCategoriesConfig == null || appConfig == null) {
if(homeCategoriesData == null || searchCategoriesData == null || appConfig == null) {
initialize()
}
}
Expand All @@ -93,8 +91,8 @@ internal class MobileAppViewModel @Inject constructor(
return

onFilmLongClickJob = viewModelScope.launch {
val isInWatchlist = watchlistRepository.getWatchlistItemById(film.id) != null
val isInWatchHistory = watchHistoryRepository.getWatchHistoryItemById(film.id) != null
val isInWatchlist = watchlistRepository.getWatchlistItemById(film.identifier) != null
val isInWatchHistory = watchHistoryRepository.getWatchHistoryItemById(film.identifier) != null

_filmToPreview.update { film }
_uiState.update {
Expand Down Expand Up @@ -122,7 +120,7 @@ internal class MobileAppViewModel @Inject constructor(
_filmToPreview.value?.let { film ->
val isInWatchlist = _uiState.value.isLongClickedFilmInWatchlist
if(isInWatchlist) {
watchlistRepository.removeById(film.id)
watchlistRepository.removeById(film.identifier)
} else {
watchlistRepository.insert(film.toWatchlistItem())
}
Expand All @@ -147,15 +145,15 @@ internal class MobileAppViewModel @Inject constructor(
it.copy(isLongClickedFilmInWatchHistory = false)
}

watchHistoryRepository.deleteById(film.id)
watchHistoryRepository.deleteById(film.identifier)
}
}
}
}

fun onPlayClick(
film: Film? = null,
episode: TMDBEpisode? = null,
episode: Episode? = null,
runWebView: (FlixclusiveWebView) -> Unit,
) {
if(onPlayClickJob?.isActive == true)
Expand All @@ -166,22 +164,22 @@ internal class MobileAppViewModel @Inject constructor(

var filmToShow = film ?: _filmToPreview.value ?: return@launch

val response = filmToShow.run {
when {
filmType == FilmType.MOVIE && this !is Movie || filmType == FilmType.TV_SHOW && this !is TvShow -> {
filmProviderUseCase(id, filmType)
}
else -> Resource.Success(this)
val response = when {
filmToShow !is FilmDetails -> {
filmProviderUseCase(partiallyDetailedFilm = filmToShow)
}
else -> Resource.Success(filmToShow)
}

val errorFetchingFilm = SourceDataState.Error(UtilR.string.film_data_fetch_failed)
if(response !is Resource.Success) {
return@launch updateVideoDataDialogState(SourceDataState.Error(UtilR.string.film_data_fetch_failed))
return@launch updateVideoDataDialogState(errorFetchingFilm)
}

filmToShow = response.data ?: return@launch updateVideoDataDialogState(SourceDataState.Unavailable())
filmToShow = response.data
?: return@launch updateVideoDataDialogState(errorFetchingFilm)

val watchHistoryItem = watchHistoryRepository.getWatchHistoryItemById(filmToShow.id)
val watchHistoryItem = watchHistoryRepository.getWatchHistoryItemById(filmToShow.identifier)
?.copy(film = filmToShow.toFilmInstance())
?.also { item ->
viewModelScope.launch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ fun FilmPreviewBottomSheet(
)

Text(
text = "${formatRating(film.rating).asString()} | ${film.dateReleased}",
text = "${formatRating(film.rating).asString()} | ${film.parsedReleaseDate}",
style = MaterialTheme.typography.labelMedium,
fontWeight = FontWeight.Normal
)
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/kotlin/com/flixclusive/util/NavigationHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import com.flixclusive.ROOT
import com.flixclusive.mobile.MobileAppNavigator
import com.flixclusive.mobile.MobileNavGraphs
import com.flixclusive.model.tmdb.Film
import com.flixclusive.model.tmdb.TMDBEpisode
import com.flixclusive.model.tmdb.common.tv.Episode
import com.flixclusive.tv.AppTvNavigator
import com.flixclusive.tv.TvNavGraphs
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
Expand Down Expand Up @@ -100,7 +100,7 @@ internal fun AppNavHost(
isTv: Boolean = false,
closeApp: () -> Unit,
previewFilm: (Film) -> Unit = {},
play: (Film, TMDBEpisode?) -> Unit = { _, _ -> },
play: (Film, Episode?) -> Unit = { _, _ -> },
) {
DestinationsNavHost(
engine = rememberAnimatedNavHostEngine(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class AndroidApplicationConventionPlugin : Plugin<Project> {
apply("com.android.application")
apply("org.jetbrains.kotlin.android")
apply("org.jetbrains.kotlin.plugin.serialization")
apply("org.jetbrains.dokka")
}

extensions.configure<ApplicationExtension> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
apply("com.android.library")
apply("org.jetbrains.kotlin.android")
apply("org.jetbrains.kotlin.plugin.serialization")
apply("org.jetbrains.dokka")
}

extensions.configure<LibraryExtension> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class HiltConventionPlugin : Plugin<Project> {

dependencies {
add("implementation", libs.findLibrary("hilt.android").get())
add("implementation", libs.findLibrary("hilt.testing").get())
add("ksp", libs.findLibrary("hilt.compiler").get())
}
}
Expand Down
37 changes: 30 additions & 7 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,45 @@ plugins {
alias(libs.plugins.hilt) apply false
alias(libs.plugins.ksp) apply false
alias(libs.plugins.room) apply false
alias(libs.plugins.dokka) apply false
id("com.osacky.doctor") version "0.9.1"
}

// Generate the stubs jar for the providers-system.
// Must only be run after the task: bundleReleaseClassesToCompileJar or build.
// Generate a mf FAT AHH JAR!
tasks.register<Jar>("fatJar") {
archiveBaseName.set("provider-stubs")
archiveClassifier.set("sources")
destinationDirectory.set(File("build/stubs"))

subprojects.forEach { project ->
if (project.subprojects.size == 0) {
val projectPath = "." + project.path.replace(":", "/")
from("$projectPath/src/main/kotlin", "$projectPath/src/main/java")
}
}
}

/**
*
* Generate the stubs jar for the providers-system.
*
* Must only be run after the tasks:
* - assembleRelease; and
* - bundleReleaseClassesToCompileJar.
* */
tasks.register<Jar>("generateStubsJar") {
archiveBaseName.set("classes")
archiveClassifier.set("")
destinationDirectory.set(File("app/build"))
destinationDirectory.set(File("build/stubs"))
dependsOn("fatJar")

subprojects.forEach { project ->
if (project.subprojects.size == 0) {
val projectPath = "." + project.path.replace(":", "/")
val appJar = File("${projectPath}/build/intermediates/compile_app_classes_jar/release/classes.jar")
val classesJar = File("${projectPath}/build/intermediates/compile_app_classes_jar/release/classes.jar")

if (appJar.exists()) {
from(zipTree(appJar)) {
if (classesJar.exists()) {
from(zipTree(classesJar)) {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
}
Expand All @@ -51,4 +73,5 @@ tasks.register<Jar>("generateStubsJar") {
}
}
}
}
}

Loading

0 comments on commit 2ecd1dd

Please sign in to comment.