add metadb support for crunchyroll
also remove gson snice it's unused now
This commit is contained in:
		| @ -45,6 +45,7 @@ import org.mosad.teapod.ui.activity.onboarding.OnboardingActivity | ||||
| import org.mosad.teapod.ui.activity.player.PlayerActivity | ||||
| import org.mosad.teapod.ui.components.LoginDialog | ||||
| import org.mosad.teapod.util.DataTypes | ||||
| import org.mosad.teapod.util.metadb.MetaDBController | ||||
| import java.util.* | ||||
| import kotlin.system.measureTimeMillis | ||||
|  | ||||
| @ -141,6 +142,9 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen | ||||
|             Preferences.load(this) | ||||
|             EncryptedPreferences.readCredentials(this) | ||||
|  | ||||
|             // load meta db at the start, it doesn't depend on any third party | ||||
|             val metaJob = initMetaDB() | ||||
|  | ||||
|             // always initialize the api token | ||||
|             Crunchyroll.initBasicApiToken() | ||||
|  | ||||
| @ -152,14 +156,17 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen | ||||
|             ) { | ||||
|                 showOnboarding() | ||||
|             } else { | ||||
|                 runBlocking { initCrunchyroll().joinAll() } | ||||
|                 runBlocking { | ||||
|                     initCrunchyroll().joinAll() | ||||
|                     metaJob.join() // meta loading should be done here | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         Log.i(classTag, "loading in $time ms") | ||||
|     } | ||||
|  | ||||
|     private fun initCrunchyroll(): List<Job> { | ||||
|         val scope = CoroutineScope(Dispatchers.IO + CoroutineName("InitialLoadingScope")) | ||||
|         val scope = CoroutineScope(Dispatchers.IO + CoroutineName("InitialCrunchyLoading")) | ||||
|         return listOf( | ||||
|             scope.launch { Crunchyroll.index() }, | ||||
|             scope.launch { Crunchyroll.account() }, | ||||
| @ -172,6 +179,11 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     private fun initMetaDB(): Job { | ||||
|         val scope = CoroutineScope(Dispatchers.IO + CoroutineName("InitialMetaDBLoading")) | ||||
|         return scope.launch { MetaDBController.list() } | ||||
|     } | ||||
|  | ||||
|     private fun showLoginDialog() { | ||||
|         LoginDialog(this, false).positiveButton { | ||||
|             EncryptedPreferences.saveCredentials(login, password, context) | ||||
|  | ||||
| @ -107,8 +107,6 @@ class AboutFragment : Fragment() { | ||||
|                 "https://github.com/material-components/material-components-android", License.APACHE2), | ||||
|             ThirdPartyComponent("ExoPlayer", "2014 - 2020", "The Android Open Source Project", | ||||
|                 "https://github.com/google/ExoPlayer", License.APACHE2), | ||||
|             ThirdPartyComponent("Gson", "2008", "Google Inc.", | ||||
|                 "https://github.com/google/gson", License.APACHE2), | ||||
|             ThirdPartyComponent("Material design icons", "2020", "Google Inc.", | ||||
|                 "https://github.com/google/material-design-icons", License.APACHE2), | ||||
|             ThirdPartyComponent("Material Dialogs", "", "Aidan Follestad", | ||||
|  | ||||
| @ -8,7 +8,7 @@ import kotlinx.coroutines.launch | ||||
| import org.mosad.teapod.parser.crunchyroll.* | ||||
| import org.mosad.teapod.preferences.Preferences | ||||
| import org.mosad.teapod.util.DataTypes.MediaType | ||||
| import org.mosad.teapod.util.Meta | ||||
| import org.mosad.teapod.util.metadb.Meta | ||||
| import org.mosad.teapod.util.tmdb.* | ||||
|  | ||||
| /** | ||||
| @ -59,7 +59,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic | ||||
|         ).joinAll() | ||||
| //        println("series: $seriesCrunchy") | ||||
| //        println("seasons: $seasonsCrunchy") | ||||
|         println(upNextSeries) | ||||
| //        println(upNextSeries) | ||||
|  | ||||
|         // load the preferred season (preferred language, language per season, not per stream) | ||||
|         currentSeasonCrunchy = seasonsCrunchy.getPreferredSeason(Preferences.preferredLocale) | ||||
|  | ||||
| @ -425,7 +425,6 @@ class PlayerActivity : AppCompatActivity() { | ||||
|             val seekTime = (it.openingStart + it.openingDuration) - model.player.currentPosition | ||||
|             model.seekToOffset(seekTime) | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|  | ||||
| @ -46,8 +46,10 @@ import org.mosad.teapod.parser.crunchyroll.NoneEpisode | ||||
| import org.mosad.teapod.parser.crunchyroll.NoneEpisodes | ||||
| import org.mosad.teapod.parser.crunchyroll.NonePlayback | ||||
| import org.mosad.teapod.preferences.Preferences | ||||
| import org.mosad.teapod.util.EpisodeMeta | ||||
| import org.mosad.teapod.util.tmdb.TMDBTVSeason | ||||
| import org.mosad.teapod.util.metadb.EpisodeMeta | ||||
| import org.mosad.teapod.util.metadb.Meta | ||||
| import org.mosad.teapod.util.metadb.MetaDBController | ||||
| import org.mosad.teapod.util.metadb.TVShowMeta | ||||
| import java.util.* | ||||
|  | ||||
| /** | ||||
| @ -56,6 +58,7 @@ import java.util.* | ||||
|  * the next episode will be update and the callback is handled. | ||||
|  */ | ||||
| class PlayerViewModel(application: Application) : AndroidViewModel(application) { | ||||
|     private val classTag = javaClass.name | ||||
|  | ||||
|     val player = SimpleExoPlayer.Builder(application).build() | ||||
|     private val dataSourceFactory = DefaultDataSourceFactory(application, Util.getUserAgent(application, "Teapod")) | ||||
| @ -65,13 +68,12 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) | ||||
|     private var currentPlayhead: Long = 0 | ||||
|  | ||||
|     // tmdb/meta data | ||||
|     // TODO meta data currently not implemented for cr | ||||
| //    var mediaMeta: Meta? = null | ||||
| //        internal set | ||||
|     var tmdbTVSeason: TMDBTVSeason? =null | ||||
|     var mediaMeta: Meta? = null | ||||
|         internal set | ||||
|     var currentEpisodeMeta: EpisodeMeta? = null | ||||
|         internal set | ||||
| //    var tmdbTVSeason: TMDBTVSeason? =null | ||||
| //        internal set | ||||
|  | ||||
|     // crunchyroll episodes/playback | ||||
|     var episodes = NoneEpisodes | ||||
| @ -108,7 +110,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) | ||||
|         mediaSession.release() | ||||
|         player.release() | ||||
|  | ||||
|         Log.d(javaClass.name, "Released player") | ||||
|         Log.d(classTag, "Released player") | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @ -124,22 +126,12 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) | ||||
|  | ||||
|     fun loadMediaAsync(seasonId: String, episodeId: String) = viewModelScope.launch { | ||||
|         episodes = Crunchyroll.episodes(seasonId) | ||||
|         mediaMeta = loadMediaMeta(episodes.items.first().seriesId) | ||||
|  | ||||
|         Log.d(classTag, "meta: $mediaMeta") | ||||
|  | ||||
|         setCurrentEpisode(episodeId) | ||||
|         playCurrentMedia(currentPlayhead) | ||||
|  | ||||
|         // TODO reimplement for cr | ||||
|         // run async as it should be loaded by the time the episodes a | ||||
| //        viewModelScope.launch { | ||||
| //            // get tmdb season info, if metaDB knows the tv show | ||||
| //            if (media.type == DataTypes.MediaType.TVSHOW && mediaMeta != null) { | ||||
| //                val tvShowMeta = mediaMeta as TVShowMeta | ||||
| //                tmdbTVSeason = TMDBApiController().getTVSeasonDetails(tvShowMeta.tmdbId, tvShowMeta.tmdbSeasonNumber) | ||||
| //            } | ||||
| //        } | ||||
| // | ||||
| //        currentEpisodeMeta = getEpisodeMetaByAoDMediaId(currentEpisodeAoD.mediaId) | ||||
| //        currentLanguage = currentEpisodeAoD.getPreferredStream(preferredLanguage).language | ||||
|     } | ||||
|  | ||||
|     fun setLanguage(language: Locale) { | ||||
| @ -174,6 +166,15 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) | ||||
|             episode.id == episodeId | ||||
|         } ?: NoneEpisode | ||||
|  | ||||
|         // update current episode meta | ||||
|         currentEpisodeMeta = if (mediaMeta is TVShowMeta && currentEpisode.episodeNumber != null) { | ||||
|             (mediaMeta as TVShowMeta) | ||||
|                 .seasons[currentEpisode.seasonNumber - 1] | ||||
|                 .episodes[currentEpisode.episodeNumber!! - 1] | ||||
|         } else { | ||||
|             null | ||||
|         } | ||||
|  | ||||
|         // update player gui (title, next ep button) after currentEpisode has changed | ||||
|         currentEpisodeChangedListener.forEach { it() } | ||||
|  | ||||
| @ -195,9 +196,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) | ||||
|                 } | ||||
|             ) | ||||
|         } | ||||
|         println("loaded playback ${currentEpisode.playback}") | ||||
|  | ||||
|         // TODO update metadata and language (it should not be needed to update the language here!) | ||||
|         Log.i(classTag, "playback: ${currentEpisode.playback}") | ||||
|  | ||||
|         if (startPlayback) { | ||||
|             playCurrentMedia() | ||||
| @ -227,7 +226,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) | ||||
|                 currentPlayback.streams.adaptive_hls.entries.first().value.url | ||||
|             } | ||||
|         } | ||||
|         println("stream url: $url") | ||||
|         Log.d(classTag, "stream url: $url") | ||||
|  | ||||
|         // create the media source object | ||||
|         val mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource( | ||||
| @ -266,25 +265,9 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) | ||||
|         return episodes.items.lastOrNull()?.id == currentEpisode.id | ||||
|     } | ||||
|  | ||||
|     // TODO reimplement for cr | ||||
| //    fun getEpisodeMetaByAoDMediaId(aodMediaId: Int): EpisodeMeta? { | ||||
| //        val meta = mediaMeta | ||||
| //        return if (meta is TVShowMeta) { | ||||
| //            meta.episodes.firstOrNull { it.aodMediaId == aodMediaId } | ||||
| //        } else { | ||||
| //            null | ||||
| //        } | ||||
| //    } | ||||
| // | ||||
| //    private suspend fun loadMediaMeta(aodId: Int): Meta? { | ||||
| //        return if (media.type == DataTypes.MediaType.TVSHOW) { | ||||
| //            MetaDBController().getTVShowMetadata(aodId) | ||||
| //        } else { | ||||
| //            null | ||||
| //        } | ||||
| // | ||||
| //        return null | ||||
| //    } | ||||
|     private suspend fun loadMediaMeta(crSeriesId: String): Meta? { | ||||
|         return MetaDBController.getTVShowMetadata(crSeriesId) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the playhead of the current episode, if currentPosition > 1000ms. | ||||
|  | ||||
| @ -28,7 +28,7 @@ class EpisodesListPlayer @JvmOverloads constructor( | ||||
|         } | ||||
|  | ||||
|         model?.let { | ||||
|             adapterRecEpisodes = PlayerEpisodeItemAdapter(model.episodes, model.tmdbTVSeason?.episodes) | ||||
|             adapterRecEpisodes = PlayerEpisodeItemAdapter(model.episodes) | ||||
|             adapterRecEpisodes.onImageClick = {_, episodeId -> | ||||
|                 (this.parent as ViewGroup).removeView(this) | ||||
|                 model.setCurrentEpisode(episodeId, startPlayback = true) | ||||
|  | ||||
| @ -1,159 +0,0 @@ | ||||
| /** | ||||
|  * Teapod | ||||
|  * | ||||
|  * Copyright 2020-2022  <seil0@mosad.xyz> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU 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 General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||||
|  * MA 02110-1301, USA. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| package org.mosad.teapod.util | ||||
|  | ||||
| import android.util.Log | ||||
| import com.google.gson.Gson | ||||
| import com.google.gson.annotations.SerializedName | ||||
| import kotlinx.coroutines.* | ||||
| import java.io.FileNotFoundException | ||||
| import java.net.URL | ||||
|  | ||||
| /** | ||||
|  * TODO remove gson usage | ||||
|  */ | ||||
| class MetaDBController { | ||||
|  | ||||
|     companion object { | ||||
|         private const val repoUrl = "https://gitlab.com/Seil0/teapodmetadb/-/raw/main/aod/" | ||||
|  | ||||
|         var mediaList = MediaList(listOf()) | ||||
|         private var metaCacheList = arrayListOf<Meta>() | ||||
|  | ||||
|         @Suppress("BlockingMethodInNonBlockingContext") | ||||
|         suspend fun list() = withContext(Dispatchers.IO) { | ||||
|             val url = URL("$repoUrl/list.json") | ||||
|             val json = url.readText() | ||||
|  | ||||
|             mediaList = Gson().fromJson(json, MediaList::class.java) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the meta data for a movie from MetaDB | ||||
|      * @param aodId The AoD id of the media | ||||
|      * @return A meta movie object, or null if not found | ||||
|      */ | ||||
|     suspend fun getMovieMetadata(aodId: Int): MovieMeta? { | ||||
|         return metaCacheList.firstOrNull { | ||||
|             it.aodId == aodId | ||||
|         } as MovieMeta? ?: getMovieMetadataFromDB(aodId) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the meta data for a tv show from MetaDB | ||||
|      * @param aodId The AoD id of the media | ||||
|      * @return A meta tv show object, or null if not found | ||||
|      */ | ||||
|     suspend fun getTVShowMetadata(aodId: Int): TVShowMeta? { | ||||
|         return metaCacheList.firstOrNull { | ||||
|             it.aodId == aodId | ||||
|         } as TVShowMeta? ?: getTVShowMetadataFromDB(aodId) | ||||
|     } | ||||
|  | ||||
|     @Suppress("BlockingMethodInNonBlockingContext") | ||||
|     private suspend fun getMovieMetadataFromDB(aodId: Int): MovieMeta? = withContext(Dispatchers.IO) { | ||||
|         val url = URL("$repoUrl/movie/$aodId/media.json") | ||||
|         return@withContext try { | ||||
|             val json = url.readText() | ||||
|             val meta = Gson().fromJson(json, MovieMeta::class.java) | ||||
|             metaCacheList.add(meta) | ||||
|  | ||||
|             meta | ||||
|         } catch (ex: FileNotFoundException) { | ||||
|             Log.w(javaClass.name, "Waring: The requested file was not found. Requested ID: $aodId", ex) | ||||
|             null | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Suppress("BlockingMethodInNonBlockingContext") | ||||
|     private suspend fun getTVShowMetadataFromDB(aodId: Int): TVShowMeta? = withContext(Dispatchers.IO) { | ||||
|         val url = URL("$repoUrl/tv/$aodId/media.json") | ||||
|         return@withContext try { | ||||
|             val json = url.readText() | ||||
|             val meta = Gson().fromJson(json, TVShowMeta::class.java) | ||||
|             metaCacheList.add(meta) | ||||
|  | ||||
|             meta | ||||
|         } catch (ex: FileNotFoundException) { | ||||
|             Log.w(javaClass.name, "Waring: The requested file was not found. Requested ID: $aodId", ex) | ||||
|             null | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| // class representing the media list json object | ||||
| data class MediaList( | ||||
|     val media: List<Int> | ||||
| ) | ||||
|  | ||||
| // abstract class used for meta data objects (tv, movie) | ||||
| abstract class Meta { | ||||
|     abstract val id: Int | ||||
|     abstract val aodId: Int | ||||
|     abstract val tmdbId: Int | ||||
| } | ||||
|  | ||||
| // class representing the movie json object | ||||
| data class MovieMeta( | ||||
|     override val id: Int, | ||||
|     @SerializedName("aod_id") | ||||
|     override val aodId: Int, | ||||
|     @SerializedName("tmdb_id") | ||||
|     override val tmdbId: Int | ||||
| ): Meta() | ||||
|  | ||||
| // class representing the tv show json object | ||||
| data class TVShowMeta( | ||||
|     override val id: Int, | ||||
|     @SerializedName("aod_id") | ||||
|     override val aodId: Int, | ||||
|     @SerializedName("tmdb_id") | ||||
|     override val tmdbId: Int, | ||||
|     @SerializedName("tmdb_season_id") | ||||
|     val tmdbSeasonId: Int, | ||||
|     @SerializedName("tmdb_season_number") | ||||
|     val tmdbSeasonNumber: Int, | ||||
|     @SerializedName("episodes") | ||||
|     val episodes: List<EpisodeMeta> | ||||
| ): Meta() | ||||
|  | ||||
| // class used in TVShowMeta, part of the tv show json object | ||||
| data class EpisodeMeta( | ||||
|     val id: Int, | ||||
|     @SerializedName("aod_media_id") | ||||
|     val aodMediaId: Int, | ||||
|     @SerializedName("tmdb_id") | ||||
|     val tmdbId: Int, | ||||
|     @SerializedName("tmdb_number") | ||||
|     val tmdbNumber: Int, | ||||
|     @SerializedName("opening_start") | ||||
|     val openingStart: Long, | ||||
|     @SerializedName("opening_duration") | ||||
|     val openingDuration: Long, | ||||
|     @SerializedName("ending_start") | ||||
|     val endingStart: Long, | ||||
|     @SerializedName("ending_duration") | ||||
|     val endingDuration: Long | ||||
| ) | ||||
| @ -12,7 +12,7 @@ import org.mosad.teapod.databinding.ItemEpisodePlayerBinding | ||||
| import org.mosad.teapod.parser.crunchyroll.Episodes | ||||
| import org.mosad.teapod.util.tmdb.TMDBTVEpisode | ||||
|  | ||||
| class PlayerEpisodeItemAdapter(private val episodes: Episodes, private val tmdbEpisodes: List<TMDBTVEpisode>?) : RecyclerView.Adapter<PlayerEpisodeItemAdapter.EpisodeViewHolder>() { | ||||
| class PlayerEpisodeItemAdapter(private val episodes: Episodes) : RecyclerView.Adapter<PlayerEpisodeItemAdapter.EpisodeViewHolder>() { | ||||
|  | ||||
|     var onImageClick: ((seasonId: String, episodeId: String) -> Unit)? = null | ||||
|     var currentSelected: Int = -1 // -1, since position should never be < 0 | ||||
| @ -39,8 +39,6 @@ class PlayerEpisodeItemAdapter(private val episodes: Episodes, private val tmdbE | ||||
|         holder.binding.textEpisodeTitle2.text = titleText | ||||
|         holder.binding.textEpisodeDesc2.text = if (ep.description.isNotEmpty()) { | ||||
|             ep.description | ||||
|         } else if (tmdbEpisodes != null && position < tmdbEpisodes.size){ | ||||
|             tmdbEpisodes[position].overview | ||||
|         } else { | ||||
|             "" | ||||
|         } | ||||
|  | ||||
							
								
								
									
										57
									
								
								app/src/main/java/org/mosad/teapod/util/metadb/DatTypes.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								app/src/main/java/org/mosad/teapod/util/metadb/DatTypes.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | ||||
| package org.mosad.teapod.util.metadb | ||||
|  | ||||
| import kotlinx.serialization.SerialName | ||||
| import kotlinx.serialization.Serializable | ||||
|  | ||||
| // class representing the media list json object | ||||
| @Serializable | ||||
| data class MediaList( | ||||
|     @SerialName("media") val media: List<String> | ||||
| ) | ||||
|  | ||||
| // abstract class used for meta data objects (tv, movie) | ||||
| abstract class Meta { | ||||
|     abstract val id: Int | ||||
|     abstract val tmdbId: Int | ||||
|     abstract val crSeriesId: String | ||||
| } | ||||
|  | ||||
| // class representing the movie json object | ||||
| @Serializable | ||||
| data class MovieMeta( | ||||
|     @SerialName("id") override val id: Int, | ||||
|     @SerialName("tmdb_id") override val tmdbId: Int, | ||||
|     @SerialName("cr_series_id") override val crSeriesId: String, | ||||
| ): Meta() | ||||
|  | ||||
| // class representing the tv show json object | ||||
| @Serializable | ||||
| data class TVShowMeta( | ||||
|     @SerialName("id") override val id: Int, | ||||
|     @SerialName("tmdb_id") override val tmdbId: Int, | ||||
|     @SerialName("cr_series_id") override val crSeriesId: String, | ||||
|     @SerialName("seasons") val seasons: List<SeasonMeta>, | ||||
| ): Meta() | ||||
|  | ||||
| // class used in TVShowMeta, part of the tv show json object | ||||
| @Serializable | ||||
| data class SeasonMeta( | ||||
|     @SerialName("id") val id: Int, | ||||
|     @SerialName("tmdb_season_id") val tmdbSeasonId: Int, | ||||
|     @SerialName("tmdb_season_number") val tmdbSeasonNumber: Int, | ||||
|     @SerialName("cr_season_ids") val crSeasonIds: List<String>, | ||||
|     @SerialName("episodes") val episodes: List<EpisodeMeta>, | ||||
| ) | ||||
|  | ||||
| // class used in TVShowMeta, part of the tv show json object | ||||
| @Serializable | ||||
| data class EpisodeMeta( | ||||
|     @SerialName("id") val id: Int, | ||||
|     @SerialName("tmdb_episode_id") val tmdbEpisodeId: Int, | ||||
|     @SerialName("tmdb_episode_number") val tmdbEpisodeNumber: Int, | ||||
|     @SerialName("cr_episode_ids") val crEpisodeIds: List<String>, | ||||
|     @SerialName("opening_start") val openingStart: Long, | ||||
|     @SerialName("opening_duration") val openingDuration: Long, | ||||
|     @SerialName("ending_start") val endingStart: Long, | ||||
|     @SerialName("ending_duration") val endingDuration: Long | ||||
| ) | ||||
| @ -0,0 +1,88 @@ | ||||
| /** | ||||
|  * Teapod | ||||
|  * | ||||
|  * Copyright 2020-2022  <seil0@mosad.xyz> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU 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 General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||||
|  * MA 02110-1301, USA. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| package org.mosad.teapod.util.metadb | ||||
|  | ||||
| import android.util.Log | ||||
| import io.ktor.client.* | ||||
| import io.ktor.client.features.* | ||||
| import io.ktor.client.features.json.* | ||||
| import io.ktor.client.features.json.serializer.* | ||||
| import io.ktor.client.request.* | ||||
| import io.ktor.http.* | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.withContext | ||||
| import kotlinx.serialization.decodeFromString | ||||
| import kotlinx.serialization.json.Json | ||||
|  | ||||
| object MetaDBController { | ||||
|     private val TAG = javaClass.name | ||||
|  | ||||
|     private const val repoUrl = "https://gitlab.com/Seil0/teapodmetadb/-/raw/main/crunchy/" | ||||
|  | ||||
|     private val client = HttpClient { | ||||
|         install(JsonFeature) { | ||||
|             serializer = KotlinxSerializer(Json) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private var mediaList = MediaList(listOf()) | ||||
|     private var metaCacheList = arrayListOf<Meta>() | ||||
|  | ||||
|     suspend fun list() = withContext(Dispatchers.IO) { | ||||
|         val raw: String = client.get("$repoUrl/list.json") | ||||
|         mediaList = Json.decodeFromString(raw) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the meta data for a movie from MetaDB | ||||
|      * @param crSeriesId The crunchyroll media id | ||||
|      * @return A meta object, or null if not found | ||||
|      */ | ||||
|     suspend fun getTVShowMetadata(crSeriesId: String): TVShowMeta? { | ||||
|         return if (mediaList.media.contains(crSeriesId)) { | ||||
|             metaCacheList.firstOrNull { | ||||
|                 it.crSeriesId == crSeriesId | ||||
|             } as TVShowMeta? ?: getTVShowMetadataFromDB(crSeriesId) | ||||
|         } else { | ||||
|             null | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private suspend fun getTVShowMetadataFromDB(crSeriesId: String): TVShowMeta? = withContext(Dispatchers.IO) { | ||||
|         return@withContext try { | ||||
|             val raw: String = client.get("$repoUrl/tv/$crSeriesId/media.json") | ||||
|             val meta: TVShowMeta = Json.decodeFromString(raw) | ||||
|             metaCacheList.add(meta) | ||||
|  | ||||
|             meta | ||||
|         } catch (ex: ClientRequestException) { | ||||
|             when (ex.response.status) { | ||||
|                 HttpStatusCode.NotFound -> Log.w(TAG, "The requested file was not found. Series ID: $crSeriesId", ex) | ||||
|                 else -> Log.e(TAG, "Error while requesting meta data. Series ID: $crSeriesId", ex) | ||||
|             } | ||||
|  | ||||
|             null // todo return none object | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user