lint: klint standard

This commit is contained in:
nomadics9 2024-07-19 05:01:09 +03:00
parent 062781a43d
commit 633ee6b8c4
3 changed files with 636 additions and 438 deletions

View file

@ -30,7 +30,6 @@ import org.jellyfin.sdk.model.api.EncodingContext
import org.jellyfin.sdk.model.api.MediaStreamType import org.jellyfin.sdk.model.api.MediaStreamType
import java.io.File import java.io.File
import java.util.UUID import java.util.UUID
import kotlin.Exception
import kotlin.math.ceil import kotlin.math.ceil
import dev.jdtech.jellyfin.core.R as CoreR import dev.jdtech.jellyfin.core.R as CoreR
@ -48,9 +47,11 @@ class DownloaderImpl(
storageIndex: Int, storageIndex: Int,
): Pair<Long, UiText?> { ): Pair<Long, UiText?> {
try { try {
val source = jellyfinRepository.getMediaSources(item.id, true).first { it.id == sourceId } val source =
jellyfinRepository.getMediaSources(item.id, true).first { it.id == sourceId }
val intro = jellyfinRepository.getIntroTimestamps(item.id) val intro = jellyfinRepository.getIntroTimestamps(item.id)
val trickplayInfo = if (item is FindroidSources) { val trickplayInfo =
if (item is FindroidSources) {
item.trickplayInfo?.get(sourceId) item.trickplayInfo?.get(sourceId)
} else { } else {
null null
@ -85,9 +86,12 @@ class DownloaderImpl(
database.insertIntro(intro.toIntroDto(item.id)) database.insertIntro(intro.toIntroDto(item.id))
} }
if (appPreferences.downloadQuality != "Original") { if (appPreferences.downloadQuality != "Original") {
downloadEmbeddedMediaStreams(item, source,storageIndex) downloadEmbeddedMediaStreams(item, source, storageIndex)
val transcodingUrl =getTranscodedUrl(item.id,appPreferences.downloadQuality!!) val transcodingUrl =
val request = DownloadManager.Request(transcodingUrl) getTranscodedUrl(item.id, appPreferences.downloadQuality!!)
val request =
DownloadManager
.Request(transcodingUrl)
.setTitle(item.name) .setTitle(item.name)
.setAllowedOverMetered(appPreferences.downloadOverMobileData) .setAllowedOverMetered(appPreferences.downloadOverMobileData)
.setAllowedOverRoaming(appPreferences.downloadWhenRoaming) .setAllowedOverRoaming(appPreferences.downloadWhenRoaming)
@ -96,8 +100,10 @@ class DownloaderImpl(
val downloadId = downloadManager.enqueue(request) val downloadId = downloadManager.enqueue(request)
database.setSourceDownloadId(source.id, downloadId) database.setSourceDownloadId(source.id, downloadId)
return Pair(downloadId, null) return Pair(downloadId, null)
}else { } else {
val request = DownloadManager.Request(source.path.toUri()) val request =
DownloadManager
.Request(source.path.toUri())
.setTitle(item.name) .setTitle(item.name)
.setAllowedOverMetered(appPreferences.downloadOverMobileData) .setAllowedOverMetered(appPreferences.downloadOverMobileData)
.setAllowedOverRoaming(appPreferences.downloadWhenRoaming) .setAllowedOverRoaming(appPreferences.downloadWhenRoaming)
@ -111,7 +117,8 @@ class DownloaderImpl(
is FindroidEpisode -> { is FindroidEpisode -> {
database.insertShow( database.insertShow(
jellyfinRepository.getShow(item.seriesId) jellyfinRepository
.getShow(item.seriesId)
.toFindroidShowDto(appPreferences.currentServer!!), .toFindroidShowDto(appPreferences.currentServer!!),
) )
database.insertSeason( database.insertSeason(
@ -128,9 +135,12 @@ class DownloaderImpl(
database.insertIntro(intro.toIntroDto(item.id)) database.insertIntro(intro.toIntroDto(item.id))
} }
if (appPreferences.downloadQuality != "Original") { if (appPreferences.downloadQuality != "Original") {
downloadEmbeddedMediaStreams(item, source,storageIndex) downloadEmbeddedMediaStreams(item, source, storageIndex)
val transcodingUrl = getTranscodedUrl(item.id, appPreferences.downloadQuality!!) val transcodingUrl =
val request = DownloadManager.Request(transcodingUrl) getTranscodedUrl(item.id, appPreferences.downloadQuality!!)
val request =
DownloadManager
.Request(transcodingUrl)
.setTitle(item.name) .setTitle(item.name)
.setAllowedOverMetered(appPreferences.downloadOverMobileData) .setAllowedOverMetered(appPreferences.downloadOverMobileData)
.setAllowedOverRoaming(appPreferences.downloadWhenRoaming) .setAllowedOverRoaming(appPreferences.downloadWhenRoaming)
@ -139,8 +149,10 @@ class DownloaderImpl(
val downloadId = downloadManager.enqueue(request) val downloadId = downloadManager.enqueue(request)
database.setSourceDownloadId(source.id, downloadId) database.setSourceDownloadId(source.id, downloadId)
return Pair(downloadId, null) return Pair(downloadId, null)
}else { } else {
val request = DownloadManager.Request(source.path.toUri()) val request =
DownloadManager
.Request(source.path.toUri())
.setTitle(item.name) .setTitle(item.name)
.setAllowedOverMetered(appPreferences.downloadOverMobileData) .setAllowedOverMetered(appPreferences.downloadOverMobileData)
.setAllowedOverRoaming(appPreferences.downloadWhenRoaming) .setAllowedOverRoaming(appPreferences.downloadWhenRoaming)
@ -157,24 +169,41 @@ class DownloaderImpl(
try { try {
val source = jellyfinRepository.getMediaSources(item.id).first { it.id == sourceId } val source = jellyfinRepository.getMediaSources(item.id).first { it.id == sourceId }
deleteItem(item, source) deleteItem(item, source)
} catch (_: Exception) {} } catch (_: Exception) {
}
return Pair(-1, if (e.message != null) UiText.DynamicString(e.message!!) else UiText.StringResource(CoreR.string.unknown_error)) return Pair(
-1,
if (e.message != null) {
UiText.DynamicString(e.message!!)
} else {
UiText.StringResource(
CoreR.string.unknown_error,
)
},
)
} }
} }
override suspend fun cancelDownload(item: FindroidItem, source: FindroidSource) { override suspend fun cancelDownload(
item: FindroidItem,
source: FindroidSource,
) {
if (source.downloadId != null) { if (source.downloadId != null) {
downloadManager.remove(source.downloadId!!) downloadManager.remove(source.downloadId!!)
} }
deleteItem(item, source) deleteItem(item, source)
} }
override suspend fun deleteItem(item: FindroidItem, source: FindroidSource) { override suspend fun deleteItem(
item: FindroidItem,
source: FindroidSource,
) {
when (item) { when (item) {
is FindroidMovie -> { is FindroidMovie -> {
database.deleteMovie(item.id) database.deleteMovie(item.id)
} }
is FindroidEpisode -> { is FindroidEpisode -> {
database.deleteEpisode(item.id) database.deleteEpisode(item.id)
val remainingEpisodes = database.getEpisodesBySeasonId(item.seasonId) val remainingEpisodes = database.getEpisodesBySeasonId(item.seasonId)
@ -212,23 +241,29 @@ class DownloaderImpl(
if (downloadId == null) { if (downloadId == null) {
return Pair(downloadStatus, progress) return Pair(downloadStatus, progress)
} }
val query = DownloadManager.Query() val query =
DownloadManager
.Query()
.setFilterById(downloadId) .setFilterById(downloadId)
val cursor = downloadManager.query(query) val cursor = downloadManager.query(query)
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
downloadStatus = cursor.getInt( downloadStatus =
cursor.getInt(
cursor.getColumnIndexOrThrow( cursor.getColumnIndexOrThrow(
DownloadManager.COLUMN_STATUS, DownloadManager.COLUMN_STATUS,
), ),
) )
when (downloadStatus) { when (downloadStatus) {
DownloadManager.STATUS_RUNNING -> { DownloadManager.STATUS_RUNNING -> {
val totalBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)) val totalBytes =
cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
if (totalBytes > 0) { if (totalBytes > 0) {
val downloadedBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)) val downloadedBytes =
cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
progress = downloadedBytes.times(100).div(totalBytes).toInt() progress = downloadedBytes.times(100).div(totalBytes).toInt()
} }
} }
DownloadManager.STATUS_SUCCESSFUL -> { DownloadManager.STATUS_SUCCESSFUL -> {
progress = 100 progress = 100
} }
@ -247,9 +282,23 @@ class DownloaderImpl(
val storageLocation = context.getExternalFilesDirs(null)[storageIndex] val storageLocation = context.getExternalFilesDirs(null)[storageIndex]
for (mediaStream in source.mediaStreams.filter { it.isExternal }) { for (mediaStream in source.mediaStreams.filter { it.isExternal }) {
val id = UUID.randomUUID() val id = UUID.randomUUID()
val streamPath = Uri.fromFile(File(storageLocation, "downloads/${item.id}.${source.id}.$id.download")) val streamPath =
database.insertMediaStream(mediaStream.toFindroidMediaStreamDto(id, source.id, streamPath.path.orEmpty())) Uri.fromFile(
val request = DownloadManager.Request(Uri.parse(mediaStream.path)) File(
storageLocation,
"downloads/${item.id}.${source.id}.$id.download",
),
)
database.insertMediaStream(
mediaStream.toFindroidMediaStreamDto(
id,
source.id,
streamPath.path.orEmpty(),
),
)
val request =
DownloadManager
.Request(Uri.parse(mediaStream.path))
.setTitle(mediaStream.title) .setTitle(mediaStream.title)
.setAllowedOverMetered(appPreferences.downloadOverMobileData) .setAllowedOverMetered(appPreferences.downloadOverMobileData)
.setAllowedOverRoaming(appPreferences.downloadWhenRoaming) .setAllowedOverRoaming(appPreferences.downloadWhenRoaming)
@ -263,30 +312,34 @@ class DownloaderImpl(
private fun downloadEmbeddedMediaStreams( private fun downloadEmbeddedMediaStreams(
item: FindroidItem, item: FindroidItem,
source: FindroidSource, source: FindroidSource,
storageIndex: Int = 0 storageIndex: Int = 0,
) { ) {
val storageLocation = context.getExternalFilesDirs(null)[storageIndex] val storageLocation = context.getExternalFilesDirs(null)[storageIndex]
val subtitleStreams = source.mediaStreams.filter { !it.isExternal && it.type == MediaStreamType.SUBTITLE && it.path != null } val subtitleStreams =
source.mediaStreams.filter { !it.isExternal && it.type == MediaStreamType.SUBTITLE && it.path != null }
for (mediaStream in subtitleStreams) { for (mediaStream in subtitleStreams) {
var deliveryUrl = mediaStream.path!! var deliveryUrl = mediaStream.path!!
if (mediaStream.codec == "webvtt") { if (mediaStream.codec == "webvtt") {
deliveryUrl = deliveryUrl.replace("Stream.srt", "Stream.vtt") deliveryUrl = deliveryUrl.replace("Stream.srt", "Stream.vtt")
} }
val id = UUID.randomUUID() val id = UUID.randomUUID()
val streamPath = Uri.fromFile( val streamPath =
Uri.fromFile(
File( File(
storageLocation, storageLocation,
"downloads/${item.id}.${source.id}.$id.download" "downloads/${item.id}.${source.id}.$id.download",
) ),
) )
database.insertMediaStream( database.insertMediaStream(
mediaStream.toFindroidMediaStreamDto( mediaStream.toFindroidMediaStreamDto(
id, id,
source.id, source.id,
streamPath.path.orEmpty() streamPath.path.orEmpty(),
),
) )
) val request =
val request = DownloadManager.Request(Uri.parse(deliveryUrl)) DownloadManager
.Request(Uri.parse(deliveryUrl))
.setTitle(mediaStream.title) .setTitle(mediaStream.title)
.setAllowedOverMetered(appPreferences.downloadOverMobileData) .setAllowedOverMetered(appPreferences.downloadOverMobileData)
.setAllowedOverRoaming(appPreferences.downloadWhenRoaming) .setAllowedOverRoaming(appPreferences.downloadWhenRoaming)
@ -298,16 +351,21 @@ class DownloaderImpl(
} }
} }
private suspend fun downloadTrickplayData( private suspend fun downloadTrickplayData(
itemId: UUID, itemId: UUID,
sourceId: String, sourceId: String,
trickplayInfo: FindroidTrickplayInfo, trickplayInfo: FindroidTrickplayInfo,
) { ) {
val maxIndex = ceil(trickplayInfo.thumbnailCount.toDouble().div(trickplayInfo.tileWidth * trickplayInfo.tileHeight)).toInt() val maxIndex =
ceil(
trickplayInfo.thumbnailCount
.toDouble()
.div(trickplayInfo.tileWidth * trickplayInfo.tileHeight),
).toInt()
val byteArrays = mutableListOf<ByteArray>() val byteArrays = mutableListOf<ByteArray>()
for (i in 0..maxIndex) { for (i in 0..maxIndex) {
jellyfinRepository.getTrickplayData( jellyfinRepository
.getTrickplayData(
itemId, itemId,
trickplayInfo.width, trickplayInfo.width,
i, i,
@ -333,8 +391,12 @@ class DownloaderImpl(
} }
} }
private suspend fun getTranscodedUrl(itemId: UUID, quality: String): Uri? { private suspend fun getTranscodedUrl(
val maxBitrate = when (quality) { itemId: UUID,
quality: String,
): Uri? {
val maxBitrate =
when (quality) {
"720p" -> 2000000 // 2 Mbps "720p" -> 2000000 // 2 Mbps
"480p" -> 1000000 // 1 Mbps "480p" -> 1000000 // 1 Mbps
"360p" -> 800000 // 800Kbps "360p" -> 800000 // 800Kbps
@ -342,13 +404,25 @@ class DownloaderImpl(
} }
return try { return try {
val deviceProfile =
val deviceProfile = jellyfinRepository.buildDeviceProfile(maxBitrate,"mkv", EncodingContext.STATIC) jellyfinRepository.buildDeviceProfile(maxBitrate, "mkv", EncodingContext.STATIC)
val playbackInfo = jellyfinRepository.getPostedPlaybackInfo(itemId,false,deviceProfile,maxBitrate) val playbackInfo =
val mediaSourceId = playbackInfo.content.mediaSources.firstOrNull()?.id!! jellyfinRepository.getPostedPlaybackInfo(itemId, false, deviceProfile, maxBitrate)
val mediaSourceId =
playbackInfo.content.mediaSources
.firstOrNull()
?.id!!
val playSessionId = playbackInfo.content.playSessionId!! val playSessionId = playbackInfo.content.playSessionId!!
val deviceId = jellyfinRepository.getDeviceId() val deviceId = jellyfinRepository.getDeviceId()
val downloadUrl = jellyfinRepository.getVideoStreambyContainerUrl(itemId, deviceId, mediaSourceId, playSessionId, maxBitrate, "ts") val downloadUrl =
jellyfinRepository.getVideoStreambyContainerUrl(
itemId,
deviceId,
mediaSourceId,
playSessionId,
maxBitrate,
"ts",
)
val transcodeUri = buildTranscodeUri(downloadUrl, maxBitrate, quality) val transcodeUri = buildTranscodeUri(downloadUrl, maxBitrate, quality)
transcodeUri transcodeUri
@ -361,15 +435,18 @@ class DownloaderImpl(
private fun buildTranscodeUri( private fun buildTranscodeUri(
transcodingUrl: String, transcodingUrl: String,
maxBitrate: Int, maxBitrate: Int,
quality: String quality: String,
): Uri { ): Uri {
val resolution = when (quality) { val resolution =
when (quality) {
"720p" -> "720" "720p" -> "720"
"480p" -> "480" "480p" -> "480"
"360p" -> "360" "360p" -> "360"
else -> "720" else -> "720"
} }
return Uri.parse(transcodingUrl).buildUpon() return Uri
.parse(transcodingUrl)
.buildUpon()
.appendQueryParameter("MaxVideoHeight", resolution) .appendQueryParameter("MaxVideoHeight", resolution)
.appendQueryParameter("MaxVideoBitRate", maxBitrate.toString()) .appendQueryParameter("MaxVideoBitRate", maxBitrate.toString())
.appendQueryParameter("subtitleMethod", "External") .appendQueryParameter("subtitleMethod", "External")

View file

@ -68,53 +68,68 @@ class JellyfinRepositoryImpl(
private val database: ServerDatabaseDao, private val database: ServerDatabaseDao,
private val appPreferences: AppPreferences, private val appPreferences: AppPreferences,
) : JellyfinRepository { ) : JellyfinRepository {
override suspend fun getPublicSystemInfo(): PublicSystemInfo = withContext(Dispatchers.IO) { override suspend fun getPublicSystemInfo(): PublicSystemInfo =
withContext(Dispatchers.IO) {
jellyfinApi.systemApi.getPublicSystemInfo().content jellyfinApi.systemApi.getPublicSystemInfo().content
} }
override suspend fun getUserViews(): List<BaseItemDto> = withContext(Dispatchers.IO) { override suspend fun getUserViews(): List<BaseItemDto> =
jellyfinApi.viewsApi.getUserViews(jellyfinApi.userId!!).content.items.orEmpty() withContext(Dispatchers.IO) {
jellyfinApi.viewsApi
.getUserViews(jellyfinApi.userId!!)
.content.items
.orEmpty()
} }
override suspend fun getItem(itemId: UUID): BaseItemDto = withContext(Dispatchers.IO) { override suspend fun getItem(itemId: UUID): BaseItemDto =
withContext(Dispatchers.IO) {
jellyfinApi.userLibraryApi.getItem(itemId, jellyfinApi.userId!!).content jellyfinApi.userLibraryApi.getItem(itemId, jellyfinApi.userId!!).content
} }
override suspend fun getEpisode(itemId: UUID): FindroidEpisode = override suspend fun getEpisode(itemId: UUID): FindroidEpisode =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
jellyfinApi.userLibraryApi.getItem( jellyfinApi.userLibraryApi
.getItem(
itemId, itemId,
jellyfinApi.userId!!, jellyfinApi.userId!!,
).content.toFindroidEpisode(this@JellyfinRepositoryImpl, database)!! ).content
.toFindroidEpisode(this@JellyfinRepositoryImpl, database)!!
} }
override suspend fun getMovie(itemId: UUID): FindroidMovie = override suspend fun getMovie(itemId: UUID): FindroidMovie =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
jellyfinApi.userLibraryApi.getItem( jellyfinApi.userLibraryApi
.getItem(
itemId, itemId,
jellyfinApi.userId!!, jellyfinApi.userId!!,
).content.toFindroidMovie(this@JellyfinRepositoryImpl, database) ).content
.toFindroidMovie(this@JellyfinRepositoryImpl, database)
} }
override suspend fun getShow(itemId: UUID): FindroidShow = override suspend fun getShow(itemId: UUID): FindroidShow =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
jellyfinApi.userLibraryApi.getItem( jellyfinApi.userLibraryApi
.getItem(
itemId, itemId,
jellyfinApi.userId!!, jellyfinApi.userId!!,
).content.toFindroidShow(this@JellyfinRepositoryImpl) ).content
.toFindroidShow(this@JellyfinRepositoryImpl)
} }
override suspend fun getSeason(itemId: UUID): FindroidSeason = override suspend fun getSeason(itemId: UUID): FindroidSeason =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
jellyfinApi.userLibraryApi.getItem( jellyfinApi.userLibraryApi
.getItem(
itemId, itemId,
jellyfinApi.userId!!, jellyfinApi.userId!!,
).content.toFindroidSeason(this@JellyfinRepositoryImpl) ).content
.toFindroidSeason(this@JellyfinRepositoryImpl)
} }
override suspend fun getLibraries(): List<FindroidCollection> = override suspend fun getLibraries(): List<FindroidCollection> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
jellyfinApi.itemsApi.getItems( jellyfinApi.itemsApi
.getItems(
jellyfinApi.userId!!, jellyfinApi.userId!!,
).content.items ).content.items
.orEmpty() .orEmpty()
@ -131,7 +146,8 @@ class JellyfinRepositoryImpl(
limit: Int?, limit: Int?,
): List<FindroidItem> = ): List<FindroidItem> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
jellyfinApi.itemsApi.getItems( jellyfinApi.itemsApi
.getItems(
jellyfinApi.userId!!, jellyfinApi.userId!!,
parentId = parentId, parentId = parentId,
includeItemTypes = includeTypes, includeItemTypes = includeTypes,
@ -151,9 +167,10 @@ class JellyfinRepositoryImpl(
recursive: Boolean, recursive: Boolean,
sortBy: SortBy, sortBy: SortBy,
sortOrder: SortOrder, sortOrder: SortOrder,
): Flow<PagingData<FindroidItem>> { ): Flow<PagingData<FindroidItem>> =
return Pager( Pager(
config = PagingConfig( config =
PagingConfig(
pageSize = 10, pageSize = 10,
maxSize = 100, maxSize = 100,
enablePlaceholders = false, enablePlaceholders = false,
@ -169,14 +186,15 @@ class JellyfinRepositoryImpl(
) )
}, },
).flow ).flow
}
override suspend fun getPersonItems( override suspend fun getPersonItems(
personIds: List<UUID>, personIds: List<UUID>,
includeTypes: List<BaseItemKind>?, includeTypes: List<BaseItemKind>?,
recursive: Boolean, recursive: Boolean,
): List<FindroidItem> = withContext(Dispatchers.IO) { ): List<FindroidItem> =
jellyfinApi.itemsApi.getItems( withContext(Dispatchers.IO) {
jellyfinApi.itemsApi
.getItems(
jellyfinApi.userId!!, jellyfinApi.userId!!,
personIds = personIds, personIds = personIds,
includeItemTypes = includeTypes, includeItemTypes = includeTypes,
@ -190,10 +208,12 @@ class JellyfinRepositoryImpl(
override suspend fun getFavoriteItems(): List<FindroidItem> = override suspend fun getFavoriteItems(): List<FindroidItem> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
jellyfinApi.itemsApi.getItems( jellyfinApi.itemsApi
.getItems(
jellyfinApi.userId!!, jellyfinApi.userId!!,
filters = listOf(ItemFilter.IS_FAVORITE), filters = listOf(ItemFilter.IS_FAVORITE),
includeItemTypes = listOf( includeItemTypes =
listOf(
BaseItemKind.MOVIE, BaseItemKind.MOVIE,
BaseItemKind.SERIES, BaseItemKind.SERIES,
BaseItemKind.EPISODE, BaseItemKind.EPISODE,
@ -206,10 +226,12 @@ class JellyfinRepositoryImpl(
override suspend fun getSearchItems(searchQuery: String): List<FindroidItem> = override suspend fun getSearchItems(searchQuery: String): List<FindroidItem> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
jellyfinApi.itemsApi.getItems( jellyfinApi.itemsApi
.getItems(
jellyfinApi.userId!!, jellyfinApi.userId!!,
searchTerm = searchQuery, searchTerm = searchQuery,
includeItemTypes = listOf( includeItemTypes =
listOf(
BaseItemKind.MOVIE, BaseItemKind.MOVIE,
BaseItemKind.SERIES, BaseItemKind.SERIES,
BaseItemKind.EPISODE, BaseItemKind.EPISODE,
@ -221,12 +243,15 @@ class JellyfinRepositoryImpl(
} }
override suspend fun getResumeItems(): List<FindroidItem> { override suspend fun getResumeItems(): List<FindroidItem> {
val items = withContext(Dispatchers.IO) { val items =
jellyfinApi.itemsApi.getResumeItems( withContext(Dispatchers.IO) {
jellyfinApi.itemsApi
.getResumeItems(
jellyfinApi.userId!!, jellyfinApi.userId!!,
limit = 12, limit = 12,
includeItemTypes = listOf(BaseItemKind.MOVIE, BaseItemKind.EPISODE), includeItemTypes = listOf(BaseItemKind.MOVIE, BaseItemKind.EPISODE),
).content.items.orEmpty() ).content.items
.orEmpty()
} }
return items.mapNotNull { return items.mapNotNull {
it.toFindroidItem(this, database) it.toFindroidItem(this, database)
@ -234,8 +259,10 @@ class JellyfinRepositoryImpl(
} }
override suspend fun getLatestMedia(parentId: UUID): List<FindroidItem> { override suspend fun getLatestMedia(parentId: UUID): List<FindroidItem> {
val items = withContext(Dispatchers.IO) { val items =
jellyfinApi.userLibraryApi.getLatestMedia( withContext(Dispatchers.IO) {
jellyfinApi.userLibraryApi
.getLatestMedia(
jellyfinApi.userId!!, jellyfinApi.userId!!,
parentId = parentId, parentId = parentId,
limit = 16, limit = 16,
@ -246,10 +273,15 @@ class JellyfinRepositoryImpl(
} }
} }
override suspend fun getSeasons(seriesId: UUID, offline: Boolean): List<FindroidSeason> = override suspend fun getSeasons(
seriesId: UUID,
offline: Boolean,
): List<FindroidSeason> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
if (!offline) { if (!offline) {
jellyfinApi.showsApi.getSeasons(seriesId, jellyfinApi.userId!!).content.items jellyfinApi.showsApi
.getSeasons(seriesId, jellyfinApi.userId!!)
.content.items
.orEmpty() .orEmpty()
.map { it.toFindroidSeason(this@JellyfinRepositoryImpl) } .map { it.toFindroidSeason(this@JellyfinRepositoryImpl) }
} else { } else {
@ -259,7 +291,8 @@ class JellyfinRepositoryImpl(
override suspend fun getNextUp(seriesId: UUID?): List<FindroidEpisode> = override suspend fun getNextUp(seriesId: UUID?): List<FindroidEpisode> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
jellyfinApi.showsApi.getNextUp( jellyfinApi.showsApi
.getNextUp(
jellyfinApi.userId!!, jellyfinApi.userId!!,
limit = 24, limit = 24,
seriesId = seriesId, seriesId = seriesId,
@ -279,7 +312,8 @@ class JellyfinRepositoryImpl(
): List<FindroidEpisode> = ): List<FindroidEpisode> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
if (!offline) { if (!offline) {
jellyfinApi.showsApi.getEpisodes( jellyfinApi.showsApi
.getEpisodes(
seriesId, seriesId,
jellyfinApi.userId!!, jellyfinApi.userId!!,
seasonId = seasonId, seasonId = seasonId,
@ -294,33 +328,41 @@ class JellyfinRepositoryImpl(
} }
} }
override suspend fun getMediaSources(itemId: UUID, includePath: Boolean): List<FindroidSource> = override suspend fun getMediaSources(
itemId: UUID,
includePath: Boolean,
): List<FindroidSource> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val sources = mutableListOf<FindroidSource>() val sources = mutableListOf<FindroidSource>()
sources.addAll( sources.addAll(
jellyfinApi.mediaInfoApi.getPostedPlaybackInfo( jellyfinApi.mediaInfoApi
.getPostedPlaybackInfo(
itemId, itemId,
PlaybackInfoDto( PlaybackInfoDto(
userId = jellyfinApi.userId!!, userId = jellyfinApi.userId!!,
deviceProfile = DeviceProfile( deviceProfile =
DeviceProfile(
name = "Direct play all", name = "Direct play all",
maxStaticBitrate = 1_000_000_000, maxStaticBitrate = 1_000_000_000,
maxStreamingBitrate = 1_000_000_000, maxStreamingBitrate = 1_000_000_000,
codecProfiles = emptyList(), codecProfiles = emptyList(),
containerProfiles = emptyList(), containerProfiles = emptyList(),
directPlayProfiles = listOf( directPlayProfiles =
listOf(
DirectPlayProfile(type = DlnaProfileType.VIDEO), DirectPlayProfile(type = DlnaProfileType.VIDEO),
DirectPlayProfile(type = DlnaProfileType.AUDIO), DirectPlayProfile(type = DlnaProfileType.AUDIO),
), ),
transcodingProfiles = emptyList(), transcodingProfiles = emptyList(),
subtitleProfiles = listOf( subtitleProfiles =
listOf(
SubtitleProfile("srt", SubtitleDeliveryMethod.EXTERNAL), SubtitleProfile("srt", SubtitleDeliveryMethod.EXTERNAL),
SubtitleProfile("ass", SubtitleDeliveryMethod.EXTERNAL), SubtitleProfile("ass", SubtitleDeliveryMethod.EXTERNAL),
), ),
), ),
maxStreamingBitrate = 1_000_000_000, maxStreamingBitrate = 1_000_000_000,
), ),
).content.mediaSources.map { ).content.mediaSources
.map {
it.toFindroidSource( it.toFindroidSource(
this@JellyfinRepositoryImpl, this@JellyfinRepositoryImpl,
itemId, itemId,
@ -334,14 +376,18 @@ class JellyfinRepositoryImpl(
sources sources
} }
override suspend fun getStreamUrl(itemId: UUID, mediaSourceId: String, playSessionId: String?): String = override suspend fun getStreamUrl(
itemId: UUID,
mediaSourceId: String,
playSessionId: String?,
): String =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
try { try {
jellyfinApi.videosApi.getVideoStreamUrl( jellyfinApi.videosApi.getVideoStreamUrl(
itemId, itemId,
static = true, static = true,
mediaSourceId = mediaSourceId, mediaSourceId = mediaSourceId,
playSessionId = playSessionId playSessionId = playSessionId,
) )
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
@ -362,7 +408,8 @@ class JellyfinRepositoryImpl(
pathParameters["itemId"] = itemId pathParameters["itemId"] = itemId
try { try {
return@withContext jellyfinApi.api.get<Intro>( return@withContext jellyfinApi.api
.get<Intro>(
"/Episode/{itemId}/IntroTimestamps/v1", "/Episode/{itemId}/IntroTimestamps/v1",
pathParameters, pathParameters,
).content ).content
@ -371,7 +418,11 @@ class JellyfinRepositoryImpl(
} }
} }
override suspend fun getTrickplayData(itemId: UUID, width: Int, index: Int): ByteArray? = override suspend fun getTrickplayData(
itemId: UUID,
width: Int,
index: Int,
): ByteArray? =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
try { try {
try { try {
@ -379,9 +430,13 @@ class JellyfinRepositoryImpl(
if (sources != null) { if (sources != null) {
return@withContext File(sources.first(), index.toString()).readBytes() return@withContext File(sources.first(), index.toString()).readBytes()
} }
} catch (_: Exception) { } } catch (_: Exception) {
}
return@withContext jellyfinApi.trickplayApi.getTrickplayTileImage(itemId, width, index).content.toByteArray() return@withContext jellyfinApi.trickplayApi
.getTrickplayTileImage(itemId, width, index)
.content
.toByteArray()
} catch (e: Exception) { } catch (e: Exception) {
return@withContext null return@withContext null
} }
@ -392,7 +447,8 @@ class JellyfinRepositoryImpl(
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
jellyfinApi.sessionApi.postCapabilities( jellyfinApi.sessionApi.postCapabilities(
playableMediaTypes = listOf(MediaType.VIDEO), playableMediaTypes = listOf(MediaType.VIDEO),
supportedCommands = listOf( supportedCommands =
listOf(
GeneralCommandType.VOLUME_UP, GeneralCommandType.VOLUME_UP,
GeneralCommandType.VOLUME_DOWN, GeneralCommandType.VOLUME_DOWN,
GeneralCommandType.TOGGLE_MUTE, GeneralCommandType.TOGGLE_MUTE,
@ -528,57 +584,66 @@ class JellyfinRepositoryImpl(
} }
} }
override suspend fun getUserConfiguration(): UserConfiguration = withContext(Dispatchers.IO) { override suspend fun getUserConfiguration(): UserConfiguration =
jellyfinApi.userApi.getCurrentUser().content.configuration!! withContext(Dispatchers.IO) {
jellyfinApi.userApi
.getCurrentUser()
.content.configuration!!
} }
override suspend fun getDownloads(): List<FindroidItem> = override suspend fun getDownloads(): List<FindroidItem> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val items = mutableListOf<FindroidItem>() val items = mutableListOf<FindroidItem>()
items.addAll( items.addAll(
database.getMoviesByServerId(appPreferences.currentServer!!) database
.getMoviesByServerId(appPreferences.currentServer!!)
.map { it.toFindroidMovie(database, jellyfinApi.userId!!) }, .map { it.toFindroidMovie(database, jellyfinApi.userId!!) },
) )
items.addAll( items.addAll(
database.getShowsByServerId(appPreferences.currentServer!!) database
.getShowsByServerId(appPreferences.currentServer!!)
.map { it.toFindroidShow(database, jellyfinApi.userId!!) }, .map { it.toFindroidShow(database, jellyfinApi.userId!!) },
) )
items items
} }
override fun getUserId(): UUID { override fun getUserId(): UUID = jellyfinApi.userId!!
return jellyfinApi.userId!!
}
override suspend fun getVideoTranscodeBitRate(transcodeResolution: Int): Pair<Int, Int> =
override suspend fun getVideoTranscodeBitRate(transcodeResolution: Int): Pair<Int, Int> { when (transcodeResolution) {
return when (transcodeResolution) {
1080 -> 8000000 to 384000 // Adjusted for personal can be other values 1080 -> 8000000 to 384000 // Adjusted for personal can be other values
720 -> 2000000 to 384000 // 720p 720 -> 2000000 to 384000 // 720p
480 -> 1000000 to 384000 // 480p 480 -> 1000000 to 384000 // 480p
360 -> 800000 to 128000 // 360p 360 -> 800000 to 128000 // 360p
else -> 12000000 to 384000 // its adaptive but setting max here else -> 12000000 to 384000 // its adaptive but setting max here
} }
}
override suspend fun buildDeviceProfile(maxBitrate: Int, container: String, context: EncodingContext): DeviceProfile { override suspend fun buildDeviceProfile(
val deviceProfile = ClientCapabilitiesDto( maxBitrate: Int,
container: String,
context: EncodingContext,
): DeviceProfile {
val deviceProfile =
ClientCapabilitiesDto(
supportedCommands = emptyList(), supportedCommands = emptyList(),
playableMediaTypes = emptyList(), playableMediaTypes = emptyList(),
supportsMediaControl = true, supportsMediaControl = true,
supportsPersistentIdentifier = true, supportsPersistentIdentifier = true,
deviceProfile = DeviceProfile( deviceProfile =
DeviceProfile(
name = "AnanasUser", name = "AnanasUser",
id = getUserId().toString(), id = getUserId().toString(),
maxStaticBitrate = maxBitrate, maxStaticBitrate = maxBitrate,
maxStreamingBitrate = maxBitrate, maxStreamingBitrate = maxBitrate,
codecProfiles = emptyList(), codecProfiles = emptyList(),
containerProfiles = listOf(), containerProfiles = listOf(),
directPlayProfiles = listOf( directPlayProfiles =
listOf(
DirectPlayProfile(type = DlnaProfileType.VIDEO), DirectPlayProfile(type = DlnaProfileType.VIDEO),
DirectPlayProfile(type = DlnaProfileType.AUDIO), DirectPlayProfile(type = DlnaProfileType.AUDIO),
), ),
transcodingProfiles = listOf( transcodingProfiles =
listOf(
TranscodingProfile( TranscodingProfile(
container = container, container = container,
context = context, context = context,
@ -586,20 +651,22 @@ class JellyfinRepositoryImpl(
audioCodec = "aac,ac3,eac3", audioCodec = "aac,ac3,eac3",
videoCodec = "hevc,h264", videoCodec = "hevc,h264",
type = DlnaProfileType.VIDEO, type = DlnaProfileType.VIDEO,
conditions = listOf( conditions =
listOf(
ProfileCondition( ProfileCondition(
condition = ProfileConditionType.LESS_THAN_EQUAL, condition = ProfileConditionType.LESS_THAN_EQUAL,
property = ProfileConditionValue.VIDEO_BITRATE, property = ProfileConditionValue.VIDEO_BITRATE,
value = "8000000", value = "8000000",
isRequired = true, isRequired = true,
) ),
), ),
copyTimestamps = true, copyTimestamps = true,
enableSubtitlesInManifest = true, enableSubtitlesInManifest = true,
transcodeSeekInfo = TranscodeSeekInfo.AUTO, transcodeSeekInfo = TranscodeSeekInfo.AUTO,
), ),
), ),
subtitleProfiles = listOf( subtitleProfiles =
listOf(
SubtitleProfile("srt", SubtitleDeliveryMethod.EXTERNAL), SubtitleProfile("srt", SubtitleDeliveryMethod.EXTERNAL),
SubtitleProfile("ass", SubtitleDeliveryMethod.EXTERNAL), SubtitleProfile("ass", SubtitleDeliveryMethod.EXTERNAL),
SubtitleProfile("sub", SubtitleDeliveryMethod.EXTERNAL), SubtitleProfile("sub", SubtitleDeliveryMethod.EXTERNAL),
@ -607,16 +674,21 @@ class JellyfinRepositoryImpl(
SubtitleProfile("ssa", SubtitleDeliveryMethod.EXTERNAL), SubtitleProfile("ssa", SubtitleDeliveryMethod.EXTERNAL),
SubtitleProfile("pgs", SubtitleDeliveryMethod.EXTERNAL), SubtitleProfile("pgs", SubtitleDeliveryMethod.EXTERNAL),
SubtitleProfile("dvb_teletext", SubtitleDeliveryMethod.EXTERNAL), SubtitleProfile("dvb_teletext", SubtitleDeliveryMethod.EXTERNAL),
SubtitleProfile("dvd_subtitle", SubtitleDeliveryMethod.EXTERNAL) SubtitleProfile("dvd_subtitle", SubtitleDeliveryMethod.EXTERNAL),
),
), ),
)
) )
return deviceProfile.deviceProfile!! return deviceProfile.deviceProfile!!
} }
override suspend fun getPostedPlaybackInfo(
override suspend fun getPostedPlaybackInfo(itemId: UUID ,enableDirectStream: Boolean ,deviceProfile: DeviceProfile ,maxBitrate: Int): Response<PlaybackInfoResponse> { itemId: UUID,
val playbackInfo = jellyfinApi.mediaInfoApi.getPostedPlaybackInfo( enableDirectStream: Boolean,
deviceProfile: DeviceProfile,
maxBitrate: Int,
): Response<PlaybackInfoResponse> {
val playbackInfo =
jellyfinApi.mediaInfoApi.getPostedPlaybackInfo(
itemId = itemId, itemId = itemId,
PlaybackInfoDto( PlaybackInfoDto(
userId = jellyfinApi.userId!!, userId = jellyfinApi.userId!!,
@ -628,13 +700,21 @@ class JellyfinRepositoryImpl(
allowAudioStreamCopy = true, allowAudioStreamCopy = true,
allowVideoStreamCopy = true, allowVideoStreamCopy = true,
maxStreamingBitrate = maxBitrate, maxStreamingBitrate = maxBitrate,
) ),
) )
return playbackInfo return playbackInfo
} }
override suspend fun getVideoStreambyContainerUrl(itemId: UUID, deviceId: String, mediaSourceId: String, playSessionId: String, videoBitrate: Int, container: String): String { override suspend fun getVideoStreambyContainerUrl(
val url = jellyfinApi.videosApi.getVideoStreamByContainerUrl( itemId: UUID,
deviceId: String,
mediaSourceId: String,
playSessionId: String,
videoBitrate: Int,
container: String,
): String {
val url =
jellyfinApi.videosApi.getVideoStreamByContainerUrl(
itemId, itemId,
static = false, static = false,
deviceId = deviceId, deviceId = deviceId,
@ -647,14 +727,21 @@ class JellyfinRepositoryImpl(
container = container, container = container,
startTimeTicks = 0, startTimeTicks = 0,
copyTimestamps = true, copyTimestamps = true,
subtitleMethod = SubtitleDeliveryMethod.EXTERNAL subtitleMethod = SubtitleDeliveryMethod.EXTERNAL,
) )
return url return url
} }
override suspend fun getTranscodedVideoStream(itemId: UUID, deviceId: String, mediaSourceId: String, playSessionId: String, videoBitrate: Int): String { override suspend fun getTranscodedVideoStream(
itemId: UUID,
deviceId: String,
mediaSourceId: String,
playSessionId: String,
videoBitrate: Int,
): String {
val isAuto = videoBitrate == 12000000 val isAuto = videoBitrate == 12000000
val url = if (!isAuto) { val url =
if (!isAuto) {
jellyfinApi.api.dynamicHlsApi.getMasterHlsVideoPlaylistUrl( jellyfinApi.api.dynamicHlsApi.getMasterHlsVideoPlaylistUrl(
itemId, itemId,
static = false, static = false,
@ -663,7 +750,7 @@ class JellyfinRepositoryImpl(
playSessionId = playSessionId, playSessionId = playSessionId,
videoBitRate = videoBitrate, videoBitRate = videoBitrate,
enableAdaptiveBitrateStreaming = false, enableAdaptiveBitrateStreaming = false,
audioBitRate = 384000, //could also be passed with audioBitrate but i preferred not as its not much data anyways audioBitRate = 384000, // could also be passed with audioBitrate but i preferred not as its not much data anyways
videoCodec = "hevc,h264", videoCodec = "hevc,h264",
audioCodec = "aac,ac3,eac3", audioCodec = "aac,ac3,eac3",
startTimeTicks = 0, startTimeTicks = 0,
@ -694,20 +781,18 @@ class JellyfinRepositoryImpl(
return url return url
} }
override suspend fun getDeviceId(): String { override suspend fun getDeviceId(): String {
val devices = jellyfinApi.devicesApi.getDevices(getUserId()) val devices = jellyfinApi.devicesApi.getDevices(getUserId())
return devices.content.items?.firstOrNull()?.id!! return devices.content.items
?.firstOrNull()
?.id!!
} }
override suspend fun stopEncodingProcess(playSessionId: String) { override suspend fun stopEncodingProcess(playSessionId: String) {
val deviceId = getDeviceId() val deviceId = getDeviceId()
jellyfinApi.api.hlsSegmentApi.stopEncodingProcess( jellyfinApi.api.hlsSegmentApi.stopEncodingProcess(
deviceId = deviceId, deviceId = deviceId,
playSessionId = playSessionId playSessionId = playSessionId,
) )
} }
} }

View file

@ -42,14 +42,9 @@ class JellyfinRepositoryOfflineImpl(
private val database: ServerDatabaseDao, private val database: ServerDatabaseDao,
private val appPreferences: AppPreferences, private val appPreferences: AppPreferences,
) : JellyfinRepository { ) : JellyfinRepository {
override suspend fun getPublicSystemInfo(): PublicSystemInfo = throw Exception("System info not available in offline mode")
override suspend fun getPublicSystemInfo(): PublicSystemInfo { override suspend fun getUserViews(): List<BaseItemDto> = emptyList()
throw Exception("System info not available in offline mode")
}
override suspend fun getUserViews(): List<BaseItemDto> {
return emptyList()
}
override suspend fun getItem(itemId: UUID): BaseItemDto { override suspend fun getItem(itemId: UUID): BaseItemDto {
TODO("Not yet implemented") TODO("Not yet implemented")
@ -113,36 +108,67 @@ class JellyfinRepositoryOfflineImpl(
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override suspend fun getSearchItems(searchQuery: String): List<FindroidItem> { override suspend fun getSearchItems(searchQuery: String): List<FindroidItem> =
return withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val movies = database.searchMovies(appPreferences.currentServer!!, searchQuery).map { it.toFindroidMovie(database, jellyfinApi.userId!!) } val movies =
val shows = database.searchShows(appPreferences.currentServer!!, searchQuery).map { it.toFindroidShow(database, jellyfinApi.userId!!) } database.searchMovies(appPreferences.currentServer!!, searchQuery).map {
val episodes = database.searchEpisodes(appPreferences.currentServer!!, searchQuery).map { it.toFindroidEpisode(database, jellyfinApi.userId!!) } it.toFindroidMovie(
database,
@Suppress("ktlint:standard:max-line-length")
jellyfinApi.userId!!,
)
}
val shows =
database
.searchShows(
appPreferences.currentServer!!,
searchQuery,
).map { it.toFindroidShow(database, jellyfinApi.userId!!) }
val episodes =
database.searchEpisodes(appPreferences.currentServer!!, searchQuery).map {
it.toFindroidEpisode(database, jellyfinApi.userId!!)
}
movies + shows + episodes movies + shows + episodes
} }
}
override suspend fun getResumeItems(): List<FindroidItem> { override suspend fun getResumeItems(): List<FindroidItem> =
return withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val movies = database.getMoviesByServerId(appPreferences.currentServer!!).map { it.toFindroidMovie(database, jellyfinApi.userId!!) }.filter { it.playbackPositionTicks > 0 } val movies =
val episodes = database.getEpisodesByServerId(appPreferences.currentServer!!).map { it.toFindroidEpisode(database, jellyfinApi.userId!!) }.filter { it.playbackPositionTicks > 0 } database
.getMoviesByServerId(
appPreferences.currentServer!!,
).map { it.toFindroidMovie(database, jellyfinApi.userId!!) }
.filter {
it.playbackPositionTicks >
0
}
val episodes =
database
.getEpisodesByServerId(
appPreferences.currentServer!!,
).map { it.toFindroidEpisode(database, jellyfinApi.userId!!) }
.filter {
it.playbackPositionTicks >
0
}
movies + episodes movies + episodes
} }
}
override suspend fun getLatestMedia(parentId: UUID): List<FindroidItem> { override suspend fun getLatestMedia(parentId: UUID): List<FindroidItem> = emptyList()
return emptyList()
}
override suspend fun getSeasons(seriesId: UUID, offline: Boolean): List<FindroidSeason> = override suspend fun getSeasons(
seriesId: UUID,
offline: Boolean,
): List<FindroidSeason> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
database.getSeasonsByShowId(seriesId).map { it.toFindroidSeason(database, jellyfinApi.userId!!) } database.getSeasonsByShowId(seriesId).map { it.toFindroidSeason(database, jellyfinApi.userId!!) }
} }
override suspend fun getNextUp(seriesId: UUID?): List<FindroidEpisode> { override suspend fun getNextUp(seriesId: UUID?): List<FindroidEpisode> =
return withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val result = mutableListOf<FindroidEpisode>() val result = mutableListOf<FindroidEpisode>()
val shows = database.getShowsByServerId(appPreferences.currentServer!!).filter { val shows =
database.getShowsByServerId(appPreferences.currentServer!!).filter {
if (seriesId != null) it.id == seriesId else true if (seriesId != null) it.id == seriesId else true
} }
for (show in shows) { for (show in shows) {
@ -156,7 +182,6 @@ class JellyfinRepositoryOfflineImpl(
} }
result.filter { it.playbackPositionTicks == 0L } result.filter { it.playbackPositionTicks == 0L }
} }
}
override suspend fun getEpisodes( override suspend fun getEpisodes(
seriesId: UUID, seriesId: UUID,
@ -172,12 +197,19 @@ class JellyfinRepositoryOfflineImpl(
items items
} }
override suspend fun getMediaSources(itemId: UUID, includePath: Boolean): List<FindroidSource> = override suspend fun getMediaSources(
itemId: UUID,
includePath: Boolean,
): List<FindroidSource> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
database.getSources(itemId).map { it.toFindroidSource(database) } database.getSources(itemId).map { it.toFindroidSource(database) }
} }
override suspend fun getStreamUrl(itemId: UUID, mediaSourceId: String, playSessionId: String?): String { override suspend fun getStreamUrl(
itemId: UUID,
mediaSourceId: String,
playSessionId: String?,
): String {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
@ -186,7 +218,11 @@ class JellyfinRepositoryOfflineImpl(
database.getIntro(itemId)?.toIntro() database.getIntro(itemId)?.toIntro()
} }
override suspend fun getTrickplayData(itemId: UUID, width: Int, index: Int): ByteArray? = override suspend fun getTrickplayData(
itemId: UUID,
width: Int,
index: Int,
): ByteArray? =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
try { try {
val sources = File(context.filesDir, "trickplay/$itemId").listFiles() ?: return@withContext null val sources = File(context.filesDir, "trickplay/$itemId").listFiles() ?: return@withContext null
@ -200,7 +236,11 @@ class JellyfinRepositoryOfflineImpl(
override suspend fun postPlaybackStart(itemId: UUID) {} override suspend fun postPlaybackStart(itemId: UUID) {}
override suspend fun postPlaybackStop(itemId: UUID, positionTicks: Long, playedPercentage: Int) { override suspend fun postPlaybackStop(
itemId: UUID,
positionTicks: Long,
playedPercentage: Int,
) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
when { when {
playedPercentage < 10 -> { playedPercentage < 10 -> {
@ -260,35 +300,31 @@ class JellyfinRepositoryOfflineImpl(
} }
} }
override fun getBaseUrl(): String { override fun getBaseUrl(): String = ""
return ""
}
override suspend fun updateDeviceName(name: String) { override suspend fun updateDeviceName(name: String) {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override suspend fun getUserConfiguration(): UserConfiguration? { override suspend fun getUserConfiguration(): UserConfiguration? = null
return null
}
override suspend fun getDownloads(): List<FindroidItem> = override suspend fun getDownloads(): List<FindroidItem> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val items = mutableListOf<FindroidItem>() val items = mutableListOf<FindroidItem>()
items.addAll( items.addAll(
database.getMoviesByServerId(appPreferences.currentServer!!) database
.getMoviesByServerId(appPreferences.currentServer!!)
.map { it.toFindroidMovie(database, jellyfinApi.userId!!) }, .map { it.toFindroidMovie(database, jellyfinApi.userId!!) },
) )
items.addAll( items.addAll(
database.getShowsByServerId(appPreferences.currentServer!!) database
.getShowsByServerId(appPreferences.currentServer!!)
.map { it.toFindroidShow(database, jellyfinApi.userId!!) }, .map { it.toFindroidShow(database, jellyfinApi.userId!!) },
) )
items items
} }
override fun getUserId(): UUID { override fun getUserId(): UUID = jellyfinApi.userId!!
return jellyfinApi.userId!!
}
override suspend fun getDeviceId(): String { override suspend fun getDeviceId(): String {
TODO("Not yet implemented") TODO("Not yet implemented")
@ -301,7 +337,7 @@ class JellyfinRepositoryOfflineImpl(
override suspend fun buildDeviceProfile( override suspend fun buildDeviceProfile(
maxBitrate: Int, maxBitrate: Int,
container: String, container: String,
context: EncodingContext context: EncodingContext,
): DeviceProfile { ): DeviceProfile {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
@ -312,7 +348,7 @@ class JellyfinRepositoryOfflineImpl(
mediaSourceId: String, mediaSourceId: String,
playSessionId: String, playSessionId: String,
videoBitrate: Int, videoBitrate: Int,
container: String container: String,
): String { ): String {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
@ -322,7 +358,7 @@ class JellyfinRepositoryOfflineImpl(
deviceId: String, deviceId: String,
mediaSourceId: String, mediaSourceId: String,
playSessionId: String, playSessionId: String,
videoBitrate: Int videoBitrate: Int,
): String { ): String {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
@ -331,7 +367,7 @@ class JellyfinRepositoryOfflineImpl(
itemId: UUID, itemId: UUID,
enableDirectStream: Boolean, enableDirectStream: Boolean,
deviceProfile: DeviceProfile, deviceProfile: DeviceProfile,
maxBitrate: Int maxBitrate: Int,
): Response<PlaybackInfoResponse> { ): Response<PlaybackInfoResponse> {
TODO("Not yet implemented") TODO("Not yet implemented")
} }