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

View file

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

View file

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