leinelissen / jellyfin-audio-player

🎵 A gorgeous Jellyfin audio streaming app for iOS and Android
https://fintunes.app
MIT License
786 stars 29 forks source link

Subsonic API compatibility #252

Open justicecurcian opened 1 month ago

justicecurcian commented 1 month ago

I really like the project, however I was never able to host any media on jellyfin (it just never loads the metadata, I have all metadata filled in music, files are stored using heirarchy from docs, each album folder containing "cover.jpg").

I (and I believe many others) would love to see subsonic (navidrome) api compatibily.

I would like to help with this. I'm web react dev but I have never worked with react-native. I'm very shy about refactoring redux store without coordination from you.

leinelissen commented 1 month ago

Hi @justicecurcian. I would love to see this happen! Thanks for stepping up.

This should be doable and I'm more than happy to give you a couple pointers to get you on the way. The important thing to know is that the Redux store is the source for all data for the app, and we can probably leave it fully intact with a Subsonic integration. That means that if you'll implement all the functions to retrieve data from Subsonic, we should be mostly okay.

I suspect the following things would need to be resolved in order to make this work:

A UI for selecting type of back-end in onboarding This should be nothing more than a list that would then redirect to the right credential generator for that back-end. Just make something simple, I'll jazz it up afterwards.

A component that retrieves credentials specifically for Subsonic For Emby / Jellyfin, we show a login page WebView and then extract the credentials stored in localStorage. Ideally it opens a WebView, because that allows auto-filling credentials from a password manager. I have no idea how this would work for Subsonic, but I suspect a similar approach is feasible.

A more generic credential store As I imagine Subsonic has slightly different credentials, it probably makes sense to refactor the Redux settings state:

https://github.com/leinelissen/jellyfin-audio-player/blob/95d8748936e065dd8203610736750a7274d45718/src/store/settings/index.ts#L5-L17

To something like this:

export interface JellyfinCredentials {
    uri: string;
    user_id: string;
    access_token: string;
    device_id: string;
    type: 'jellyfin';
}

export interface EmbyCredentials {
    uri: string;
    user_id: string;
    access_token: string;
    device_id: string;
    type: 'emby';
}

export interface SubsonicCredentials {
    uri: string;
    type: 'subsonic';
    // whatever extra data you require...
}

interface State {
    credentials?: {
        uri: string;
        user_id: string;
        access_token: string;
        device_id: string;
        type: 'jellyfin' | 'emby';
    }
    bitrate: number;
    isOnboardingComplete: boolean;
    hasReceivedErrorReportingAlert: boolean;
    enablePlaybackReporting: boolean;
    colorScheme: ColorScheme;
}

Create functions for retrieving data The application always makes calls to Redux for fetching any sort of data from the back-end. So long as you've got functions that can respond to those calls. All potential fetching actions are specified in https://github.com/leinelissen/jellyfin-audio-player/blob/95d8748936e065dd8203610736750a7274d45718/src/store/music/actions.ts. You'll need functions that respons to all the calls prefixed with fetch, as well as searchAndFetchAlbums.

You'll see that right now, all function calls are forwarded to the src/utility/JellyfinApi folder, which currently does the heavy lifting. You can use those functions as inspiration for how to route the function calls, which arguments to accept and how to retrieve credentials from the Redux store.

Transform data As I would like to leave much of the schema for the store intact, you'll need to transform the data coming in from Subsonic to match the Redux store format. We currently store, tracks, albums and playlists, which resolve to the AlbumTrack, Album and Playlist types respectively, in https://github.com/leinelissen/jellyfin-audio-player/blob/95d8748936e065dd8203610736750a7274d45718/src/store/music/types.ts.

Any data coming back from your fetching functions should adhere to those types. You'll probably find there's a lot of attributes that have no equivalent data in Subsonic. That's okay. Just make those types nullable (add ? after the key) and implement some extra checks for components that use those attributes.

That's all I can think of right now, but I'm sure there will be other stuff that pops up. If you can take care of the things above, I can do the rest of the integration. I am more than willing to help you out along the way. Just create a Pull Request, send updates periodically and loop me in if you're stuck on some things. Together, we should be able to make this work.

Let me know if this is feasible for you. Lei

justicecurcian commented 1 month ago

Thanks for detailed reply!

Maybe we can create new button on onboarding screen, so it will be something like:

And on subsonic screen we can just enter server url, login and password. Login autocomplete won't work but it's one time operation, a user should be able to enter it easily, and when I tried jellyfin login my bitwarden couldn't autocomplete the password anyway.

Subsonic api authenticates using login and hashed password, not access token.

If this approach is ok for you I will start working on PR soon

leinelissen commented 1 month ago

Yeah, as far as the button goes, just put in the bare minimum, I will clean it up when the rest is done. As far as authentication goes, I would prefer a webview from a user experience perspective and so we don't have to store a password. Not sure if that's feasible with Subsonic, but I'll leave that up to you.

The rest is good to go. I look forward to seeing your progress!

justicecurcian commented 1 month ago

There is no WebView as far as I'm aware, also the password will be hashed so it's pretty safe