Coder Social home page Coder Social logo

jellyfin / jellyfin-sdk-kotlin Goto Github PK

View Code? Open in Web Editor NEW
73.0 9.0 36.0 103.95 MB

Kotlin SDK for Jellyfin, supporting Android and JVM targets

Home Page: https://kotlin-sdk.jellyfin.org

License: GNU Lesser General Public License v3.0

Kotlin 100.00%
android java kotlin jellyfin openapi3 ktor sdk jvm

jellyfin-sdk-kotlin's Introduction

Jellyfin Kotlin SDK

Part of the Jellyfin Project


Logo Banner

LGPL 3.0 license Current Release Maven Central Release
Donate Chat on Matrix Join our Subreddit Release RSS Feed Master Commits RSS Feed


The Kotlin SDK for Jellyfin implements the Jellyfin API to easily access servers. It is currently available for the JVM and Android. Developer documentation is available at kotlin-sdk.jellyfin.org.

Contributing

We welcome contributions to the SDK. Open an issue or ask in our official chats if you plan to make bigger changes.

To validate binary compatibility we use the Binary compatibility validator tool from the Kotlin team. When creating pull requests the api files need to be updated. Use the apiDump Gradle task to generate the api files. Add the changes from this command to a separate commit to make the review process easier.

Testing

The SDK includes two example projects, the kotlin-cli and java-cli, to test various larger functions like server discovery. Besides that we use unit tests to test smaller components, these can be executed with the allTests Gradle task. We recommend adding new tests for changes to the code.

Testing in app

It is also possible to test a new version of the SDK in your own app. Use the publishToMavenLocal Gradle task to publish the SDK to your local system, afterwards you can add mavenLocal() as repository and use the latest-SNAPSHOT version for the SDK. This process is simplified in our official apps by adding an option to the gradle.properties file.

jellyfin-sdk-kotlin's People

Contributors

1337joe avatar alialiusefi avatar andreasac avatar andreasgb avatar anthonylavado avatar bendardenne avatar dependabot[bot] avatar dkanada avatar ebr11 avatar erayan avatar ferferga avatar givimad avatar gradle-update-robot avatar h1dden-da3m0n avatar jarnedemeulemeester avatar jellyfin-bot avatar joshuaboniface avatar justaman avatar lukepulverenti avatar maxr1998 avatar nielsvanvelzen avatar redshirtmb avatar renovate-bot avatar renovate[bot] avatar snazy2000 avatar thornbill avatar yash-garg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jellyfin-sdk-kotlin's Issues

Publish to JCenter

This should be published to JCenter (or similar) so it can be included as a maven or gradle dependency.

Follow index redirect in discovery

Sometimes people use a baseurl and only have a redirect on the index page (e.g. jellyfin.local -> jellyfin.local/jellyfin). When the user inputs jellyfin.local we should send a HEAD request and see if there is any redirect. If the redirect is the same origin but with a subpath we should add that to the candidates.

This should allow easier setup in Jellyfin apps. Behavior can be tested with our own demo server which redirects to the /stable path.

Use Kotlin Multiplatform and merge platform-android with core

Moving the structure of the library to Kotlin Mutliplatform allows us to add the Android specific code to the core library. This would make it easier to use the library as users don't need to include the Android platform separately. It also makes it easier for us to support different platforms like JavaScript, WASM, Native or iOS in the future.

I already tested this in #187 and it might be a relatively easy change.

NoClassDefFoundError on API 23

When UserApi.authenticateUserByName is called on API 23 then it crashes with

W/System.err: java.lang.NoClassDefFoundError: Failed resolution of: Ljava/time/ZoneId;
W/System.err:     at org.jellyfin.sdk.model.serializer.DateTimeSerializer.<init>(DateTimeSerializer.kt:16)
W/System.err:     at org.jellyfin.sdk.model.api.UserDto$$serializer.deserialize(UserDto.kt:27)
W/System.err:     at org.jellyfin.sdk.model.api.UserDto$$serializer.deserialize(UserDto.kt:27)
W/System.err:     at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:59)
W/System.err:     at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:38)
W/System.err:     at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
W/System.err:     at kotlinx.serialization.encoding.AbstractDecoder.decodeNullableSerializableElement(AbstractDecoder.kt:79)
W/System.err:     at org.jellyfin.sdk.model.api.AuthenticationResult$$serializer.deserialize(AuthenticationResult.kt:12)
W/System.err:     at org.jellyfin.sdk.model.api.AuthenticationResult$$serializer.deserialize(AuthenticationResult.kt:12)
W/System.err:     at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:59)
W/System.err:     at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:38)
W/System.err:     at kotlinx.serialization.json.Json.decodeFromString(Json.kt:100)
W/System.err:     at org.jellyfin.sdk.api.operations.UserApi.authenticateUserByName(UserApi.kt:299)
W/System.err:     at org.jellyfin.sdk.api.operations.UserApi$authenticateUserByName$1.invokeSuspend(UserApi.kt)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:191)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:147)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:15)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:93)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:191)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:147)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:15)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:93)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:191)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:147)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:15)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:93)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:191)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:147)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:15)
W/System.err:     at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:93)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
W/System.err:     at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
W/System.err:     at android.os.Handler.handleCallback(Handler.java:739)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err:     at android.os.Looper.loop(Looper.java:148)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5417)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
W/System.err: Caused by: java.lang.ClassNotFoundException: Didn't find class "java.time.ZoneId" on path: DexPathList[[zip file "/data/app/com.shalva97.jellylist.debug-2/base.apk"],nativeLibraryDirectories=[/data/app/com.shalva97.jellylist.debug-2/lib/x86_64, /vendor/lib64, /system/lib64]]
W/System.err:     at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
W/System.err: 	... 43 more
W/System.err: 	Suppressed: java.lang.ClassNotFoundException: java.time.ZoneId
W/System.err:     at java.lang.Class.classForName(Native Method)
W/System.err:     at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
W/System.err:     at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
W/System.err: 		... 44 more
W/System.err: 	Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available

Add data classes for requests with a lot of parameters

Some endpoints have a lot of (query-)parameters. Sometimes the values of these parameters change based on some logic. Right now you'll need to write separate function calls or create variables to later pass to the operation function.

A solution for this annoyance would be to add data classes that can be used instead of a big parameter list. They would be an addition to the existing functions, not replace them.

A (simple) example for the "getAudioStreamUrl" operation in the audio api:

// Before
val streamUrl = api.audioApi.getAudioStreamUrl(
	itemId = itemId,
	mediaSourceId = mediaSource.id,
	deviceId = api.deviceInfo.id,
)

// After
var audioStreamRequest = GetAudioStreamRequest(
	itemId = itemId,
	mediaSourceId = mediaSource.id,
	deviceId = api.deviceInfo.id,
)
// Imagine a lot of logic here instead of just this single line
if (someOtherMediaSourceId != null) audioStreamRequest = audioStreamRequest.copy(mediaSourceId = someOtherMediaSourceId)
val streamUrl = api.audioApi.getAudioStreamUrl(audioStreamRequest)

The requests should be the exact same as the request parameters but in a data class instead of function parameters.

Usage for sdk.version=local

Hi,

I'm trying to use a local edited copy of the SDK for testing purposes. But when I changed the sdk.version in the gradle.properties from "default" to "local" in jellyfin-android, then include the SDK output library that was created, jellyfin-sdk-kotlin\jellyfin-platform-android\build\outputs\aar\jellyfin-platform-android-debug.aar, I was getting this error when debugging jellyfin-android:

Execution failed for task ':app:dataBindingMergeDependencyArtifactsProprietaryDebug'.
> Could not resolve all files for configuration ':app:proprietaryDebugCompileClasspath'.
   > Could not find org.jellyfin.sdk:jellyfin-platform-android:latest-SNAPSHOT.
     Required by:
         project :app

Possible solution:
 - Declare repository providing the artifact, see the documentation at https://docs.gradle.org/current/userguide/declaring_repositories.html

I'm lost at the possible solution provided. Pardon my ignorance but how do I include a local copy of jellyfin SDK for testing in jellyfin-android? Thank you in advance.

Validate generated sources with GitHub actions

Pull requests that change files in any of the kotlin-generated directories or the generator module should trigger a GitHub workflow that runs the generator and validates the sources in the pull request match. If a difference is found it should fail the task.

This will help testing the integrity of "Update OpenAPI spec" merge requests.

No assets are uploaded to the GitHub release

Unfortunately not everything worked in the 0.7.1 release. The assets were not uploaded. Not a major issue but it should be fixed for the next release.

Logs

In case the Azure logs get too old and will be deleted.

2020-08-18T15:58:47.2723873Z ##[section]Starting: GitHub Upload
2020-08-18T15:58:47.2738850Z ==============================================================================
2020-08-18T15:58:47.2739452Z Task         : GitHub Release
2020-08-18T15:58:47.2739881Z Description  : Create, edit, or delete a GitHub release
2020-08-18T15:58:47.2740288Z Version      : 0.160.5
2020-08-18T15:58:47.2740640Z Author       : Microsoft Corporation
2020-08-18T15:58:47.2741691Z Help         : https://aka.ms/AA5vv5o
2020-08-18T15:58:47.2742474Z ==============================================================================
2020-08-18T15:58:47.4668852Z 9bde9970-f931-4741-b0e3-f9f2bbcbf4ae exists true
2020-08-18T15:58:47.5062106Z Computing changes made in this release...
2020-08-18T15:58:47.7352006Z Fetching the latest published release...
2020-08-18T15:58:47.9899384Z Found the latest published release: https://github.com/jellyfin/jellyfin-apiclient-java/releases/tag/v0.7.1
2020-08-18T15:58:48.2483200Z Fetching the list of commits since the last published release...
2020-08-18T15:58:48.5968714Z No changes were found. The provided target commit is the same as the commit of the last published release.
2020-08-18T15:58:48.5972148Z Release notes file: /home/vsts/work/1/s is a directory and not a file.
2020-08-18T15:58:48.5973273Z Fetching the release for tag: v0.7.1
2020-08-18T15:58:48.9265051Z Found a release for tag: v0.7.1
2020-08-18T15:58:48.9272950Z Editing the release with tag: v0.7.1
2020-08-18T15:58:49.2010463Z Uploading assets...
2020-08-18T15:58:49.2016049Z Searching for file(s) matching '**/distributions/*-${JELLYFIN_VERSION}.zip'.
2020-08-18T15:58:49.3337046Z No files found matching '**/distributions/*-${JELLYFIN_VERSION}.zip'. Nothing to upload.
2020-08-18T15:58:49.3338629Z Searching for file(s) matching '**/libs/*-${JELLYFIN_VERSION}.jar'.
2020-08-18T15:58:49.4329634Z No files found matching '**/libs/*-${JELLYFIN_VERSION}.jar'. Nothing to upload.
2020-08-18T15:58:49.4331135Z Searching for file(s) matching '**/libs/*-${JELLYFIN_VERSION}-sources.jar'.
2020-08-18T15:58:49.5278786Z No files found matching '**/libs/*-${JELLYFIN_VERSION}-sources.jar'. Nothing to upload.
2020-08-18T15:58:49.5280259Z All assets uploaded successfully.
2020-08-18T15:58:49.5281914Z Release edited successfully https://github.com/jellyfin/jellyfin-apiclient-java/releases/tag/v0.7.1
2020-08-18T15:58:49.5322867Z ##[section]Finishing: GitHub Upload

Logs (after #88)

2020-08-19T07:33:23.5116508Z ##[section]Starting: GitHub Upload
2020-08-19T07:33:23.5129081Z ==============================================================================
2020-08-19T07:33:23.5129845Z Task         : GitHub Release
2020-08-19T07:33:23.5130467Z Description  : Create, edit, or delete a GitHub release
2020-08-19T07:33:23.5130888Z Version      : 0.160.5
2020-08-19T07:33:23.5131588Z Author       : Microsoft Corporation
2020-08-19T07:33:23.5132025Z Help         : https://aka.ms/AA5vv5o
2020-08-19T07:33:23.5133224Z ==============================================================================
2020-08-19T07:33:23.7313368Z 9bde9970-f931-4741-b0e3-f9f2bbcbf4ae exists true
2020-08-19T07:33:23.7802548Z Computing changes made in this release...
2020-08-19T07:33:23.9567074Z Fetching the latest published release...
2020-08-19T07:33:24.1384291Z Found the latest published release: https://github.com/jellyfin/jellyfin-apiclient-java/releases/tag/v0.7.2
2020-08-19T07:33:24.3440201Z Fetching the list of commits since the last published release...
2020-08-19T07:33:24.6088660Z No changes were found. The provided target commit is the same as the commit of the last published release.
2020-08-19T07:33:24.6091549Z Release notes file: /home/vsts/work/1/s is a directory and not a file.
2020-08-19T07:33:24.6101088Z Fetching the release for tag: v0.7.2
2020-08-19T07:33:24.8060860Z Found a release for tag: v0.7.2
2020-08-19T07:33:24.8061699Z Editing the release with tag: v0.7.2
2020-08-19T07:33:25.0480932Z Uploading assets...
2020-08-19T07:33:25.0488543Z Searching for file(s) matching '/home/vsts/work/1/s/**/distributions/*-${JELLYFIN_VERSION}.zip'.
2020-08-19T07:33:25.1717647Z No files found matching '/home/vsts/work/1/s/**/distributions/*-${JELLYFIN_VERSION}.zip'. Nothing to upload.
2020-08-19T07:33:25.1719290Z Searching for file(s) matching '/home/vsts/work/1/s/**/libs/*-${JELLYFIN_VERSION}.jar'.
2020-08-19T07:33:25.2674781Z No files found matching '/home/vsts/work/1/s/**/libs/*-${JELLYFIN_VERSION}.jar'. Nothing to upload.
2020-08-19T07:33:25.2675919Z Searching for file(s) matching '/home/vsts/work/1/s/**/libs/*-${JELLYFIN_VERSION}-sources.jar'.
2020-08-19T07:33:25.3548400Z No files found matching '/home/vsts/work/1/s/**/libs/*-${JELLYFIN_VERSION}-sources.jar'. Nothing to upload.
2020-08-19T07:33:25.3549354Z All assets uploaded successfully.
2020-08-19T07:33:25.3550407Z Release edited successfully https://github.com/jellyfin/jellyfin-apiclient-java/releases/tag/v0.7.2
2020-08-19T07:33:25.3588737Z ##[section]Finishing: GitHub Upload

ApiClient does not respect custom HTTP client timeout

Describe the bug

I am setting the timeout to 60 seconds in the following manner:

private const val HTTP_CLIENT_TIMEOUT_IN_MS = 60000L

@Singleton
@Provides
internal fun providesKtorClient(jellyfin: Jellyfin): KtorClient {
    val clientOptions = HttpClientOptions(followRedirects = true, timeout = HTTP_CLIENT_TIMEOUT_IN_MS)
    return jellyfin.createApi(baseUrl = null, httpClientOptions = clientOptions)
}

However I have noticed that some requests time out after 6-10 seconds rather than 60 seconds.

Actual result
This is what the timeout log looks like:
Socket timeout has expired [url=https://somedummyurl.com/Users/a8888-cca3-4c0c-8668-a05fd9cb0b9f/Items/Latest?limit=10&groupItems=true, socket_timeout=unknown] ms

Notice the socket_timeout is unknown.

Expected result
Request will time out after custom timeout period (60 seconds in this case).

Add function to determine best server from a list of candidates

a function that takes the list of candidate server addresses and returns which one is valid

It would be nice to have a function that extends the functionality of the function that will "normalize" a server string and return a list of candidates by connecting to the servers in the list and returning the "best". The best server will always prefer https over http but otherwise returns the first in the list that responds to a public system info.

JsonDecodingException: TranscodeReasons is not a string

Getting this error, using the latest Jellyfin version (10.8.5) and the latest sdk version (1.3.6).
Seems like it expects TranscodeReasons to be a String?

kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 5217: Expected beginning of the string, but got [ at path: $[0].TranscodingInfo.TranscodeReasons
JSON input: .....hannels":6,"TranscodeReasons":["ContainerBitrateExceedsLimit.....
	at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24) ~[bundleFile:?]
	at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:32) ~[bundleFile:?]
	at kotlinx.serialization.json.internal.AbstractJsonLexer.fail(AbstractJsonLexer.kt:528) ~[bundleFile:?]

LocalDateTime can't serialized to json

I try to use ItemUpdateApi#updateItem(itemId: UUID, data: BaseItemDto)

but an error occurred

{
   "type":"https://tools.ietf.org/html/rfc7231#section-6.5.1",
   "title":"One or more validation errors occurred.",
   "status":400,
   "traceId":"00-33dd05cfdb47ed4088bf0ae90b8b86da-8b9f9586acc83544-00",
   "errors":{
      "request":[
         "The request field is required."
      ],
      "$.DateCreated":[
         "The JSON value could not be converted to System.DateTime. Path: $ | LineNumber: 0 | BytePositionInLine: 52. Path: $.DateCreated | LineNumber: 0 | BytePositionInLine: 268."
      ]
   }
}

Add RecommendedServerIssue for connection problems

Right now connection issues are emitted as MissingSystemInfo, with the throwable pointing to an ApiClientException with a cause containing the actual exception emitted from OkHttp.

This can be made better/easier.

I'd suggest creating a new issue type "NoConnection". It should contain the throwable and an enum explaining what went wrong with the connection: bad SSL, no internet etc.

Apiclient only works with 10.7 or up

The next Jellyfin version, 10.7, adds new properties that are not-null. Because of this those types are not-null in the apiclient too. This will cause issues with JSON deserialization when using the apiclient with 10.6<=.

Example:
A new property in PublicSystemInfo, StartupWizardCompleted (boolean, not null) was added. If we now use getPublicSystemInfo in SystemApi it will fail for servers not emitting that field because we can't set it to a default value.

[Feature Request] include link in the source to the corresponding jf api website page for a given model

It's sometimes unclear where to find more info about a class or field using https://api.jellyfin.org or https://jellyfin.org/docs/plugin-api or by googling.
Is it possible for the generator to automatically add a url in the source as a comment?
example:

/*
** https://jellyfin.org/docs/plugin-api/MediaBrowser.Model.Dto.MediaSourceInfo.html
*/

public class MediaSourceInfo
{
...

afaik the generator doesn't use the plugin-api, but I used it in this example since I can't find MediaSourceInfo on api.jellyfin.org

Migrate from JCenter to Maven Central

Unfortunately JCenter is closing in a few months. We need to publish the apiclient somewhere else before this happens. Additionally all our Java based projects (the Android apps) need to switch to different repositories for all dependencies.

I think our best bet is to wait a bit and see what other projects in the java ecosystem do before we act.

See the jfrog blog article for some more information:

https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/

Follow HTTP redirect when discovering servers

I'm following up on an issue I described in jellyfin/jellyfin-android#949 (comment). I've an HTTP redirect set from a custom domain to the Jellyfin server. Works great on browsers with both HTTP and HTTPS, even on LG webos app. The android client, however, refuses to follow the redirect URL and instead lists the tried candidates:

Tried 5 candidates for input, without success.

Unable to reach server:
- https://subdomain.domain.com
- https://subdomain.domain.com:8096
- https://subdomain.domain.com:8920
- http://subdomain.domain.com
- http://subdomain.domain.com:8096

Could it be possible to allow redirected URLs (301/302) to be acceptable as server URLs instead of outright rejecting them? I believe it could be fixed by adjusting the acceptable response code here: https://github.com/jellyfin/jellyfin-sdk-kotlin/blob/master/jellyfin-core/src/commonMain/kotlin/org/jellyfin/sdk/discovery/RecommendedServerDiscovery.kt#L125.

how to understand the docs for Jellyfin API

Its been a while I am trying to create a Jellyfin Android Client for a sideproject. Made log-in work, but fetching media and playing it seems a lot harder. Not in a technical way but in a API way...

At first I started from here https://api.jellyfin.org which is just a reference, it has almost no explanation. So how do I know what is what?

For example, what is difference between Libraries and UserLibraries? how to get the list of media, movies, download links?

I have been debuging Findroid for a while and it is a pain. On their app it just works and on mine it throws exceptions...

Automatically update OpenAPI spec

When a new Jellyfin server version is released the OpenAPI specification is published at https://repo.jellyfin.org/releases/openapi/. Right now updating the SDK requires running the updateApiSpecStable gradle task to download the JSON file and run the generator to create the generated sources.

It would be amazing if we could automate this process by creating a workflow that runs every day/week/? and compares the latest stable specification with the one in the repository. If any changes are found it should update the generated sources, push it to a new branch and create a pull request. We can then validate this PR manually to see if everything works correctly, and make changes to it if required.

Make KtorClient private and depend on ApiClient in generated code

Although we do use the ApiClient interface in most places it doesn't include the request, get, post and delete functions. This is because we use inline functions with reified types, which are not supported when using interfaces. Preferably we'd fix this by passing the class as parameters instead of using generics but when I looked into this it didn't look like a possibility.

Separating the KtorClient (implementation) from the ApiClient (interface) it would allow the usage of custom HTTP clients and make it easier to change it if we decide to drop Ktor at some point.

Discovery crashes with invalid port

When a user is dumb and makes a typo and the HTTP port is out of range the discovery stuff throws an exception and the Android TV app crashes... I was the dumb user.

Stack Trace:

java.lang.IllegalArgumentException: port must be between 0 and 65535, or 0 if not set
	at io.ktor.http.Url.<init>(URLBuilder.kt:144)
	at io.ktor.http.URLBuilder.build(URLBuilder.kt:98)
	at org.jellyfin.sdk.discovery.AddressCandidateHelper.<init>(AddressCandidateHelper.kt:55)
	at org.jellyfin.sdk.discovery.DiscoveryService.getAddressCandidates(DiscoveryService.kt:27)
	at org.jellyfin.androidtv.auth.repository.ServerRepositoryImpl$addServer$1.invokeSuspend(ServerRepository.kt:90)
	at org.jellyfin.androidtv.auth.repository.ServerRepositoryImpl$addServer$1.invoke(Unknown Source:8)
	at org.jellyfin.androidtv.auth.repository.ServerRepositoryImpl$addServer$1.invoke(Unknown Source:4)
	at kotlinx.coroutines.flow.SafeFlow.collectSafely(Builders.kt:61)
	at kotlinx.coroutines.flow.AbstractFlow.collect(Flow.kt:230)
	at org.jellyfin.androidtv.ui.startup.ServerAddViewModel$addServer$1.invokeSuspend(ServerAddViewModel.kt:19)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:367)
	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
	at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
	at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
	at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
	at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1)
	at org.jellyfin.androidtv.ui.startup.ServerAddViewModel.addServer(ServerAddViewModel.kt:18)
	at org.jellyfin.androidtv.ui.startup.fragment.ServerAddFragment.submitAddress(ServerAddFragment.kt:107)
	at org.jellyfin.androidtv.ui.startup.fragment.ServerAddFragment.onCreateView$lambda-4$lambda-3(ServerAddFragment.kt:50)
	at org.jellyfin.androidtv.ui.startup.fragment.ServerAddFragment.$r8$lambda$uSCCu62gCVZN90XjWAl5IwN7O5U(Unknown Source:0)
	at org.jellyfin.androidtv.ui.startup.fragment.ServerAddFragment$$ExternalSyntheticLambda0.onClick(Unknown Source:2)
	at android.view.View.performClick(View.java:6597)
	at android.view.View.performClickInternal(View.java:6574)
	at android.view.View.access$3100(View.java:778)
	at android.view.View$PerformClick.run(View.java:25885)
	at android.os.Handler.handleCallback(Handler.java:873)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loop(Looper.java:193)
	at android.app.ActivityThread.main(ActivityThread.java:6669)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@9106673, Dispatchers.Main.immediate]

Log lines before exception:

08-13 07:35:18.707 D/ServerRepositoryImpl$addServer( 4635): Adding server http://192.168.1.10:80961
08-13 07:35:18.707 D/AddressCandidateHelper( 4635): Input is http://192.168.1.10:80961

SSL errors not caught

Regression from #446, SSL errors are not caught. Ideally we should wrap them in a subclass of ApiClientException. This issue only happens for badly configured servers.

Stacktrace from jellyfin/jellyfin-androidtv#1920.

javax.net.ssl.SSLHandshakeException: Handshake failed
	at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:286)
	at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:379)
	at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337)
	at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209)
	at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226)
	at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
	at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
	at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
	at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
	at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
	at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
	at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
	at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
	at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
	at java.lang.Thread.run(Thread.java:764)
	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@b5b80bf, Dispatchers.Main.immediate]
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x941ce4c8: Failure in SSL library, usually a protocol error
error:100000f7:SSL routines:OPENSSL_internal:WRONG_VERSION_NUMBER (external/boringssl/src/ssl/tls_record.cc:242 0xa33aef0b:0x00000000)
	at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
	at com.android.org.conscrypt.NativeSsl.doHandshake(NativeSsl.java:375)
	at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:224)
	... 20 more

Send Message is not getting received on android

I am new to Jellyfin and while playing around I saw the send message feature for messaging active sessions. I tried it and found that I am not seeing messages on my android. I am able to send messages from it, but not receive. No errors show on either side, it just never pops up anywhere.

Publishing automation

When #54 is merged we can publish the apiclient to Bintray which is nice. What would be really nice however is this process being automated.

We should add a new task to the Azure pipeline that will run the publishDefaultPublicationToBintrayRepository Gradle job with the environment variables specified in #54 set. This way we can automagically publish to Bintray.

Another thing that we could probably do (however, I did not test it) is publishing the master branch with the version set as SNAPSHOT.

Add linting in CI

The current Gradle setup includes the Android linter (automatically added for Android projects) and Detekt (manually added) for linting. Right now both are unused in the GitHub actions.

I'd like to automatically lint the codebase on pull requests and report any new issues using annotations or in a comment. My previous attempt (#163) didn't work out like I wanted so I'll need to rethink how to approach this issue.

Update the README

  • Some badges don't work
  • The sample code is outdated.
  • Should add a link to the sample(s)
  • Should add a blurb about our own use in the Android / Android TV apps as example implementations

Add retry logic when requests fail due too network issues

Especially on mobile devices the network can sometimes drop or have other issues. It would be nice if the SDK could automatically retry network requests when a network issue occurs. We need to make sure a retry is not attempted when only the response fails. The server could have mutated something already and retrying could cause issues. (I think this is also true for GET requests).

The behavior for retrying should be configurable in the HttpClientOptions.

TranscodingInfo.TranscodeReasons incorrectly generated

Server:
https://github.com/jellyfin/jellyfin/blob/master/MediaBrowser.Model/Session/TranscodingInfo.cs#L32

OpenAPI:

"TranscodeReasons": {
	"enum": [
		"ContainerNotSupported",
		"VideoCodecNotSupported",
		"AudioCodecNotSupported",
		"SubtitleCodecNotSupported",
		"AudioIsExternal",
		"SecondaryAudioNotSupported",
		"VideoProfileNotSupported",
		"VideoLevelNotSupported",
		"VideoResolutionNotSupported",
		"VideoBitDepthNotSupported",
		"VideoFramerateNotSupported",
		"RefFramesNotSupported",
		"AnamorphicVideoNotSupported",
		"InterlacedVideoNotSupported",
		"AudioChannelsNotSupported",
		"AudioProfileNotSupported",
		"AudioSampleRateNotSupported",
		"AudioBitDepthNotSupported",
		"ContainerBitrateExceedsLimit",
		"VideoBitrateNotSupported",
		"AudioBitrateNotSupported",
		"UnknownVideoStreamInfo",
		"UnknownAudioStreamInfo",
		"DirectPlayError",
		"VideoRangeTypeNotSupported"
	],
	"type": "string"
}

Generated:

@SerialName("TranscodeReasons")
public val transcodeReasons: String,

Expected generated:

@SerialName("TranscodeReasons")
public val transcodeReasons: Collection<TranscodeReason>,

Add WebSockets

The new API generated from the OpenAPI spec does not contain the WebSocket logic. This needs to be added manually to replace the current WebSockets. Ideally using Ktor.

Is permission READ_EXTERNAL_STORAGE required?

First of all thank you so much for making this library, it allowed me to create Findroid ๐Ÿ˜„

While looking at the permissions I saw that READ_EXTERNAL_STORAGE is requested.
But is this really required? I removed it from the manifest and everything still seems to work.

According to the docs this is only required when you want to read all contents from external storage and I don't think this library does that. But maybe I'm wrong.
https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE

Publish Dokka documentation

We already add Dokka documentation to the artifacts on Maven Central (docs are required). It would be nice to publish them somewhere more accessible. Something like developers.jellyfin.org/sdk-kotlin/1.0.0/.

Add default values for booleans and integers

Right now we set the defaultValue to null when a property is nullable. But the openapi document contains a few properties of type boolean or integer with defaults. We should use these defaults.

When a property has both a default value and nullability we should prefer the default value above the default null value.

Testing with Robolectric

Hi, wanted to try this library and also added Robolectric.

Anyways this line returns null and the test crashes

val id = Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)

Is Robolectric supported? anyways my usecase is that I can play around with jellyfin-sdk-kotlin faster than with debugging/starting random Activities with random buttons. I could try with Instrumentation test, but idk... I dont like those

Rename the repository to jellyfin-sdk-kotlin

We've had a discussion about renaming this project before. A few names came up like "jellyfin-sdk-kotlin" and "jellyfin-apiclient-kotlin".

Since the project is entirely written in Kotlin now I think it's better to not use the term "java" in the name. I'd like to start using Kotlin Multiplatform at some point which allows us to target not only the JVM but also JavaScript, WebAssembly and iOS (just to name a few).

I'd rather rename the repository before releasing 1.0.0, although this only matters for the metadata because our package and group use "org.jellyfin.apiclient".

The new readme in #193 already uses the new name ๐Ÿ˜‰

edit (2021-03-19): the initial plan (written above) was to rename to "apiclient" but we decided "sdk" makes more sense. See my comment below for the new plan.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.