fix(all/jellyfin): Fix subtitles, pages, and some small code refactoring (#2103)
Co-authored-by: jmir1 <jhmiramon@gmail.com>
This commit is contained in:
@ -5,8 +5,8 @@ apply plugin: 'kotlinx-serialization'
|
|||||||
ext {
|
ext {
|
||||||
extName = 'Jellyfin'
|
extName = 'Jellyfin'
|
||||||
pkgNameSuffix = 'all.jellyfin'
|
pkgNameSuffix = 'all.jellyfin'
|
||||||
extClass = '.Jellyfin'
|
extClass = '.JellyfinFactory'
|
||||||
extVersionCode = 8
|
extVersionCode = 9
|
||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ import eu.kanade.tachiyomi.network.asObservableSuccess
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.Dns
|
import okhttp3.Dns
|
||||||
@ -38,9 +37,9 @@ import uy.kohesive.injekt.injectLazy
|
|||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
class Jellyfin(private val suffix: String) : ConfigurableAnimeSource, AnimeHttpSource() {
|
||||||
|
|
||||||
override val name = "Jellyfin"
|
override val name = "Jellyfin$suffix"
|
||||||
|
|
||||||
override val lang = "all"
|
override val lang = "all"
|
||||||
|
|
||||||
@ -108,23 +107,21 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun popularAnimeRequest(page: Int): Request {
|
override fun popularAnimeRequest(page: Int): Request {
|
||||||
if (parentId.isEmpty()) {
|
require(parentId.isNotEmpty()) { "Select library in the extension settings." }
|
||||||
throw Exception("Select library in the extension settings.")
|
|
||||||
}
|
|
||||||
val startIndex = (page - 1) * 20
|
val startIndex = (page - 1) * 20
|
||||||
|
|
||||||
val url = "$baseUrl/Users/$userId/Items".toHttpUrl().newBuilder()
|
val url = "$baseUrl/Users/$userId/Items".toHttpUrl().newBuilder().apply {
|
||||||
|
addQueryParameter("api_key", apiKey)
|
||||||
url.addQueryParameter("api_key", apiKey)
|
addQueryParameter("StartIndex", startIndex.toString())
|
||||||
url.addQueryParameter("StartIndex", startIndex.toString())
|
addQueryParameter("Limit", "20")
|
||||||
url.addQueryParameter("Limit", "20")
|
addQueryParameter("Recursive", "true")
|
||||||
url.addQueryParameter("Recursive", "true")
|
addQueryParameter("SortBy", "SortName")
|
||||||
url.addQueryParameter("SortBy", "SortName")
|
addQueryParameter("SortOrder", "Ascending")
|
||||||
url.addQueryParameter("SortOrder", "Ascending")
|
addQueryParameter("includeItemTypes", "Movie,Season,BoxSet")
|
||||||
url.addQueryParameter("includeItemTypes", "Movie,Series,Season,BoxSet")
|
addQueryParameter("ImageTypeLimit", "1")
|
||||||
url.addQueryParameter("ImageTypeLimit", "1")
|
addQueryParameter("ParentId", parentId)
|
||||||
url.addQueryParameter("ParentId", parentId)
|
addQueryParameter("EnableImageTypes", "Primary")
|
||||||
url.addQueryParameter("EnableImageTypes", "Primary")
|
}
|
||||||
|
|
||||||
return GET(url.toString())
|
return GET(url.toString())
|
||||||
}
|
}
|
||||||
@ -150,24 +147,21 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int): Request {
|
override fun latestUpdatesRequest(page: Int): Request {
|
||||||
if (parentId.isEmpty()) {
|
require(parentId.isNotEmpty()) { "Select library in the extension settings." }
|
||||||
throw Exception("Select library in the extension settings.")
|
|
||||||
}
|
|
||||||
|
|
||||||
val startIndex = (page - 1) * 20
|
val startIndex = (page - 1) * 20
|
||||||
|
|
||||||
val url = "$baseUrl/Users/$userId/Items".toHttpUrl().newBuilder()
|
val url = "$baseUrl/Users/$userId/Items".toHttpUrl().newBuilder().apply {
|
||||||
|
addQueryParameter("api_key", apiKey)
|
||||||
url.addQueryParameter("api_key", apiKey)
|
addQueryParameter("StartIndex", startIndex.toString())
|
||||||
url.addQueryParameter("StartIndex", startIndex.toString())
|
addQueryParameter("Limit", "20")
|
||||||
url.addQueryParameter("Limit", "20")
|
addQueryParameter("Recursive", "true")
|
||||||
url.addQueryParameter("Recursive", "true")
|
addQueryParameter("SortBy", "DateCreated,SortName")
|
||||||
url.addQueryParameter("SortBy", "DateCreated,SortName")
|
addQueryParameter("SortOrder", "Descending")
|
||||||
url.addQueryParameter("SortOrder", "Descending")
|
addQueryParameter("includeItemTypes", "Movie,Season,BoxSet")
|
||||||
url.addQueryParameter("includeItemTypes", "Movie,Series,Season,BoxSet")
|
addQueryParameter("ImageTypeLimit", "1")
|
||||||
url.addQueryParameter("ImageTypeLimit", "1")
|
addQueryParameter("ParentId", parentId)
|
||||||
url.addQueryParameter("ParentId", parentId)
|
addQueryParameter("EnableImageTypes", "Primary")
|
||||||
url.addQueryParameter("EnableImageTypes", "Primary")
|
}
|
||||||
|
|
||||||
return GET(url.toString())
|
return GET(url.toString())
|
||||||
}
|
}
|
||||||
@ -181,51 +175,45 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun searchAnimeParse(response: Response) = throw Exception("Not used")
|
override fun searchAnimeParse(response: Response) = throw Exception("Not used")
|
||||||
|
|
||||||
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> {
|
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> {
|
||||||
if (parentId.isEmpty()) {
|
require(parentId.isNotEmpty()) { "Select library in the extension settings." }
|
||||||
throw Exception("Select library in the extension settings.")
|
|
||||||
}
|
|
||||||
|
|
||||||
val animeList = mutableListOf<SAnime>()
|
|
||||||
val startIndex = (page - 1) * 5
|
val startIndex = (page - 1) * 5
|
||||||
|
|
||||||
val url = "$baseUrl/Users/$userId/Items".toHttpUrl().newBuilder()
|
val url = "$baseUrl/Users/$userId/Items".toHttpUrl().newBuilder().apply {
|
||||||
|
addQueryParameter("api_key", apiKey)
|
||||||
|
addQueryParameter("StartIndex", startIndex.toString())
|
||||||
|
addQueryParameter("Limit", "5")
|
||||||
|
addQueryParameter("Recursive", "true")
|
||||||
|
addQueryParameter("SortBy", "SortName")
|
||||||
|
addQueryParameter("SortOrder", "Ascending")
|
||||||
|
addQueryParameter("includeItemTypes", "Movie,Season,BoxSet")
|
||||||
|
addQueryParameter("ImageTypeLimit", "1")
|
||||||
|
addQueryParameter("EnableImageTypes", "Primary")
|
||||||
|
addQueryParameter("ParentId", parentId)
|
||||||
|
addQueryParameter("SearchTerm", query)
|
||||||
|
}
|
||||||
|
|
||||||
url.addQueryParameter("api_key", apiKey)
|
val items = client.newCall(
|
||||||
url.addQueryParameter("StartIndex", startIndex.toString())
|
|
||||||
url.addQueryParameter("Limit", "5")
|
|
||||||
url.addQueryParameter("Recursive", "true")
|
|
||||||
url.addQueryParameter("SortBy", "SortName")
|
|
||||||
url.addQueryParameter("SortOrder", "Ascending")
|
|
||||||
url.addQueryParameter("includeItemTypes", "Movie,Series,BoxSet")
|
|
||||||
url.addQueryParameter("ImageTypeLimit", "1")
|
|
||||||
url.addQueryParameter("EnableImageTypes", "Primary")
|
|
||||||
url.addQueryParameter("ParentId", parentId)
|
|
||||||
url.addQueryParameter("SearchTerm", query)
|
|
||||||
|
|
||||||
val response = client.newCall(
|
|
||||||
GET(url.build().toString(), headers = headers),
|
GET(url.build().toString(), headers = headers),
|
||||||
).execute()
|
).execute().parseAs<ItemsResponse>()
|
||||||
val items = json.decodeFromString<ItemsResponse>(response.body.string())
|
|
||||||
items.Items.forEach {
|
val animeList = items.Items.flatMap {
|
||||||
animeList.addAll(
|
getAnimeFromId(it.Id)
|
||||||
getAnimeFromId(it.Id),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Observable.just(AnimesPage(animeList, 5 * page < items.TotalRecordCount))
|
return Observable.just(AnimesPage(animeList, 5 * page < items.TotalRecordCount))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAnimeFromId(id: String): List<SAnime> {
|
private fun getAnimeFromId(id: String): List<SAnime> {
|
||||||
val url = "$baseUrl/Users/$userId/Items".toHttpUrl().newBuilder()
|
val url = "$baseUrl/Users/$userId/Items".toHttpUrl().newBuilder().apply {
|
||||||
|
addQueryParameter("api_key", apiKey)
|
||||||
url.addQueryParameter("api_key", apiKey)
|
addQueryParameter("Recursive", "true")
|
||||||
url.addQueryParameter("Recursive", "true")
|
addQueryParameter("SortBy", "SortName")
|
||||||
url.addQueryParameter("SortBy", "SortName")
|
addQueryParameter("SortOrder", "Ascending")
|
||||||
url.addQueryParameter("SortOrder", "Ascending")
|
addQueryParameter("includeItemTypes", "Movie,Series,Season")
|
||||||
url.addQueryParameter("includeItemTypes", "Movie,Series,Season")
|
addQueryParameter("ImageTypeLimit", "1")
|
||||||
url.addQueryParameter("ImageTypeLimit", "1")
|
addQueryParameter("EnableImageTypes", "Primary")
|
||||||
url.addQueryParameter("EnableImageTypes", "Primary")
|
addQueryParameter("ParentId", id)
|
||||||
url.addQueryParameter("ParentId", id)
|
}
|
||||||
|
|
||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
GET(url.build().toString()),
|
GET(url.build().toString()),
|
||||||
@ -244,22 +232,22 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
mediaId.seasonId
|
mediaId.seasonId
|
||||||
}
|
}
|
||||||
|
|
||||||
val url = "$baseUrl/Users/$userId/Items/$infoId".toHttpUrl().newBuilder()
|
val url = "$baseUrl/Users/$userId/Items/$infoId".toHttpUrl().newBuilder().apply {
|
||||||
|
addQueryParameter("api_key", apiKey)
|
||||||
url.addQueryParameter("api_key", apiKey)
|
addQueryParameter("fields", "Studios")
|
||||||
url.addQueryParameter("fields", "Studios")
|
}
|
||||||
|
|
||||||
return GET(url.toString())
|
return GET(url.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun animeDetailsParse(response: Response): SAnime {
|
override fun animeDetailsParse(response: Response): SAnime {
|
||||||
val info = json.decodeFromString<ItemsResponse.Item>(response.body.string())
|
val info = response.parseAs<ItemsResponse.Item>()
|
||||||
|
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
|
|
||||||
if (info.Genres != null) anime.genre = info.Genres.joinToString(", ")
|
if (info.Genres != null) anime.genre = info.Genres.joinToString(", ")
|
||||||
|
|
||||||
if (info.Studios != null && info.Studios.isNotEmpty()) {
|
if (!info.Studios.isNullOrEmpty()) {
|
||||||
anime.author = info.Studios.mapNotNull { it.Name }.joinToString(", ")
|
anime.author = info.Studios.mapNotNull { it.Name }.joinToString(", ")
|
||||||
} else if (info.SeriesStudio != null) anime.author = info.SeriesStudio
|
} else if (info.SeriesStudio != null) anime.author = info.SeriesStudio
|
||||||
|
|
||||||
@ -297,13 +285,15 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||||
val episodeList = if (response.request.url.toString().startsWith("$baseUrl/Users/")) {
|
val episodeList = if (response.request.url.toString().startsWith("$baseUrl/Users/")) {
|
||||||
val parsed = json.decodeFromString<ItemsResponse.Item>(response.body.string())
|
val parsed = json.decodeFromString<ItemsResponse.Item>(response.body.string())
|
||||||
val episode = SEpisode.create()
|
listOf(
|
||||||
episode.episode_number = 1.0F
|
SEpisode.create().apply {
|
||||||
episode.name = "Movie ${parsed.Name}"
|
setUrlWithoutDomain(response.request.url.toString())
|
||||||
episode.setUrlWithoutDomain(response.request.url.toString().substringAfter(baseUrl))
|
name = "Movie ${parsed.Name}"
|
||||||
listOf(episode)
|
episode_number = 1.0F
|
||||||
|
},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
val parsed = json.decodeFromString<ItemsResponse>(response.body.string())
|
val parsed = response.parseAs<ItemsResponse>()
|
||||||
|
|
||||||
parsed.Items.map { ep ->
|
parsed.Items.map { ep ->
|
||||||
|
|
||||||
@ -333,14 +323,14 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
override fun videoListParse(response: Response): List<Video> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
val videoList = mutableListOf<Video>()
|
val videoList = mutableListOf<Video>()
|
||||||
val id = json.decodeFromString<ItemsResponse.Item>(response.body.string()).Id
|
val id = response.parseAs<ItemsResponse.Item>().Id
|
||||||
|
|
||||||
val sessionResponse = client.newCall(
|
val parsed = client.newCall(
|
||||||
GET("$baseUrl/Items/$id/PlaybackInfo?userId=$userId&api_key=$apiKey"),
|
GET("$baseUrl/Items/$id/PlaybackInfo?userId=$userId&api_key=$apiKey"),
|
||||||
).execute()
|
).execute().parseAs<SessionResponse>()
|
||||||
val parsed = json.decodeFromString<SessionResponse>(sessionResponse.body.string())
|
|
||||||
|
|
||||||
val subtitleList = mutableListOf<Track>()
|
val subtitleList = mutableListOf<Track>()
|
||||||
|
val externalSubtitleList = mutableListOf<Track>()
|
||||||
|
|
||||||
val prefSub = preferences.getString(JFConstants.PREF_SUB_KEY, "eng")!!
|
val prefSub = preferences.getString(JFConstants.PREF_SUB_KEY, "eng")!!
|
||||||
val prefAudio = preferences.getString(JFConstants.PREF_AUDIO_KEY, "jpn")!!
|
val prefAudio = preferences.getString(JFConstants.PREF_AUDIO_KEY, "jpn")!!
|
||||||
@ -359,20 +349,23 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
if (media.Language == prefSub) {
|
if (media.Language == prefSub) {
|
||||||
try {
|
try {
|
||||||
if (media.IsExternal) {
|
if (media.IsExternal) {
|
||||||
subtitleList.add(0, Track(subUrl, media.DisplayTitle!!))
|
externalSubtitleList.add(0, Track(subUrl, media.DisplayTitle!!))
|
||||||
}
|
}
|
||||||
|
subtitleList.add(0, Track(subUrl, media.DisplayTitle!!))
|
||||||
} catch (e: Error) {
|
} catch (e: Error) {
|
||||||
subIndex = media.Index
|
subIndex = media.Index
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (media.IsExternal) {
|
if (media.IsExternal) {
|
||||||
subtitleList.add(Track(subUrl, media.DisplayTitle!!))
|
externalSubtitleList.add(Track(subUrl, media.DisplayTitle!!))
|
||||||
}
|
}
|
||||||
|
subtitleList.add(Track(subUrl, media.DisplayTitle!!))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (media.IsExternal) {
|
if (media.IsExternal) {
|
||||||
subtitleList.add(Track(subUrl, media.DisplayTitle!!))
|
externalSubtitleList.add(Track(subUrl, media.DisplayTitle!!))
|
||||||
}
|
}
|
||||||
|
subtitleList.add(Track(subUrl, media.DisplayTitle!!))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (media.Language != null && media.Language == prefSub) {
|
if (media.Language != null && media.Language == prefSub) {
|
||||||
@ -396,44 +389,44 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
JFConstants.QUALITIES_LIST.forEach { quality ->
|
JFConstants.QUALITIES_LIST.forEach { quality ->
|
||||||
if (width < quality.width && height < quality.height) {
|
if (width < quality.width && height < quality.height) {
|
||||||
val url = "$baseUrl/Videos/$id/stream?static=True&api_key=$apiKey"
|
val url = "$baseUrl/Videos/$id/stream?static=True&api_key=$apiKey"
|
||||||
videoList.add(Video(url, "Best", url, subtitleTracks = subtitleList))
|
videoList.add(Video(url, "Source", url, subtitleTracks = externalSubtitleList))
|
||||||
|
|
||||||
return videoList.reversed()
|
return videoList.reversed()
|
||||||
} else {
|
} else {
|
||||||
val url = "$baseUrl/videos/$id/main.m3u8".toHttpUrl().newBuilder()
|
val url = "$baseUrl/videos/$id/main.m3u8".toHttpUrl().newBuilder().apply {
|
||||||
|
addQueryParameter("api_key", apiKey)
|
||||||
url.addQueryParameter("api_key", apiKey)
|
addQueryParameter("VideoCodec", "h264")
|
||||||
url.addQueryParameter("VideoCodec", "h264")
|
addQueryParameter("AudioCodec", "aac,mp3")
|
||||||
url.addQueryParameter("AudioCodec", "aac,mp3")
|
addQueryParameter("AudioStreamIndex", audioIndex.toString())
|
||||||
url.addQueryParameter("AudioStreamIndex", audioIndex.toString())
|
subIndex?.let { addQueryParameter("SubtitleStreamIndex", it.toString()) }
|
||||||
subIndex?.let { url.addQueryParameter("SubtitleStreamIndex", it.toString()) }
|
addQueryParameter("VideoCodec", "h264")
|
||||||
url.addQueryParameter("VideoCodec", "h264")
|
addQueryParameter("VideoCodec", "h264")
|
||||||
url.addQueryParameter("VideoCodec", "h264")
|
addQueryParameter(
|
||||||
url.addQueryParameter(
|
|
||||||
"VideoBitrate",
|
"VideoBitrate",
|
||||||
quality.videoBitrate.toString(),
|
quality.videoBitrate.toString(),
|
||||||
)
|
)
|
||||||
url.addQueryParameter(
|
addQueryParameter(
|
||||||
"AudioBitrate",
|
"AudioBitrate",
|
||||||
quality.audioBitrate.toString(),
|
quality.audioBitrate.toString(),
|
||||||
)
|
)
|
||||||
url.addQueryParameter("PlaySessionId", parsed.PlaySessionId)
|
addQueryParameter("PlaySessionId", parsed.PlaySessionId)
|
||||||
url.addQueryParameter("TranscodingMaxAudioChannels", "6")
|
addQueryParameter("TranscodingMaxAudioChannels", "6")
|
||||||
url.addQueryParameter("RequireAvc", "false")
|
addQueryParameter("RequireAvc", "false")
|
||||||
url.addQueryParameter("SegmentContainer", "ts")
|
addQueryParameter("SegmentContainer", "ts")
|
||||||
url.addQueryParameter("MinSegments", "1")
|
addQueryParameter("MinSegments", "1")
|
||||||
url.addQueryParameter("BreakOnNonKeyFrames", "true")
|
addQueryParameter("BreakOnNonKeyFrames", "true")
|
||||||
url.addQueryParameter("h264-profile", "high,main,baseline,constrainedbaseline")
|
addQueryParameter("h264-profile", "high,main,baseline,constrainedbaseline")
|
||||||
url.addQueryParameter("h264-level", "51")
|
addQueryParameter("h264-level", "51")
|
||||||
url.addQueryParameter("h264-deinterlace", "true")
|
addQueryParameter("h264-deinterlace", "true")
|
||||||
url.addQueryParameter("TranscodeReasons", "VideoCodecNotSupported,AudioCodecNotSupported,ContainerBitrateExceedsLimit")
|
addQueryParameter("TranscodeReasons", "VideoCodecNotSupported,AudioCodecNotSupported,ContainerBitrateExceedsLimit")
|
||||||
|
}
|
||||||
|
|
||||||
videoList.add(Video(url.toString(), quality.description, url.toString(), subtitleTracks = subtitleList))
|
videoList.add(Video(url.toString(), quality.description, url.toString(), subtitleTracks = subtitleList))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val url = "$baseUrl/Videos/$id/stream?static=True&api_key=$apiKey"
|
val url = "$baseUrl/Videos/$id/stream?static=True&api_key=$apiKey"
|
||||||
videoList.add(Video(url, "Best", url))
|
videoList.add(Video(url, "Source", url, subtitleTracks = externalSubtitleList))
|
||||||
|
|
||||||
return videoList.reversed()
|
return videoList.reversed()
|
||||||
}
|
}
|
||||||
@ -441,10 +434,9 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
// ============================= Utilities ==============================
|
// ============================= Utilities ==============================
|
||||||
|
|
||||||
private fun animeParse(response: Response, page: Int): AnimesPage {
|
private fun animeParse(response: Response, page: Int): AnimesPage {
|
||||||
val items = json.decodeFromString<ItemsResponse>(response.body.string())
|
val items = response.parseAs<ItemsResponse>()
|
||||||
val animesList = mutableListOf<SAnime>()
|
|
||||||
|
|
||||||
items.Items.forEach { item ->
|
val animeList = items.Items.flatMap { item ->
|
||||||
val anime = SAnime.create()
|
val anime = SAnime.create()
|
||||||
|
|
||||||
when (item.Type) {
|
when (item.Type) {
|
||||||
@ -469,7 +461,7 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
if (item.ImageTags.Primary == null) {
|
if (item.ImageTags.Primary == null) {
|
||||||
anime.thumbnail_url = "$baseUrl/Items/${item.SeriesId}/Images/Primary?api_key=$apiKey"
|
anime.thumbnail_url = "$baseUrl/Items/${item.SeriesId}/Images/Primary?api_key=$apiKey"
|
||||||
}
|
}
|
||||||
animesList.add(anime)
|
listOf(anime)
|
||||||
}
|
}
|
||||||
"Movie" -> {
|
"Movie" -> {
|
||||||
anime.title = item.Name
|
anime.title = item.Name
|
||||||
@ -481,38 +473,41 @@ class Jellyfin : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
item.Id,
|
item.Id,
|
||||||
).toJsonString(),
|
).toJsonString(),
|
||||||
)
|
)
|
||||||
animesList.add(anime)
|
listOf(anime)
|
||||||
}
|
}
|
||||||
"BoxSet" -> {
|
"BoxSet" -> {
|
||||||
val url = "$baseUrl/Users/$userId/Items".toHttpUrl().newBuilder()
|
val url = "$baseUrl/Users/$userId/Items".toHttpUrl().newBuilder().apply {
|
||||||
|
addQueryParameter("api_key", apiKey)
|
||||||
url.addQueryParameter("api_key", apiKey)
|
addQueryParameter("Recursive", "true")
|
||||||
url.addQueryParameter("Recursive", "true")
|
addQueryParameter("SortBy", "SortName")
|
||||||
url.addQueryParameter("SortBy", "SortName")
|
addQueryParameter("SortOrder", "Ascending")
|
||||||
url.addQueryParameter("SortOrder", "Ascending")
|
addQueryParameter("includeItemTypes", "Movie,Series,Season")
|
||||||
url.addQueryParameter("includeItemTypes", "Movie,Series,Season")
|
addQueryParameter("ImageTypeLimit", "1")
|
||||||
url.addQueryParameter("ImageTypeLimit", "1")
|
addQueryParameter("ParentId", item.Id)
|
||||||
url.addQueryParameter("ParentId", item.Id)
|
addQueryParameter("EnableImageTypes", "Primary")
|
||||||
url.addQueryParameter("EnableImageTypes", "Primary")
|
}
|
||||||
|
|
||||||
val response = client.newCall(
|
val response = client.newCall(
|
||||||
GET(url.build().toString(), headers = headers),
|
GET(url.build().toString(), headers = headers),
|
||||||
).execute()
|
).execute()
|
||||||
animesList.addAll(animeParse(response, page).animes)
|
animeParse(response, page).animes
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
return@forEach
|
|
||||||
}
|
}
|
||||||
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return AnimesPage(animesList, 20 * page < items.TotalRecordCount)
|
return AnimesPage(animeList, 20 * page < items.TotalRecordCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LinkData.toJsonString(): String {
|
private fun LinkData.toJsonString(): String {
|
||||||
return json.encodeToString(this)
|
return json.encodeToString(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun <reified T> Response.parseAs(transform: (String) -> String = { it }): T {
|
||||||
|
val responseBody = use { transform(it.body.string()) }
|
||||||
|
return json.decodeFromString(responseBody)
|
||||||
|
}
|
||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
val mediaLibPref = medialibPreference(screen)
|
val mediaLibPref = medialibPreference(screen)
|
||||||
screen.addPreference(
|
screen.addPreference(
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package eu.kanade.tachiyomi.animeextension.all.jellyfin
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.animesource.AnimeSourceFactory
|
||||||
|
|
||||||
|
class JellyfinFactory : AnimeSourceFactory {
|
||||||
|
override fun createSources() = listOf(
|
||||||
|
Jellyfin(""),
|
||||||
|
Jellyfin(" (2)"),
|
||||||
|
Jellyfin(" (3)"),
|
||||||
|
)
|
||||||
|
}
|
Reference in New Issue
Block a user