I´m gonna join on this Devember challange too this months.
Don´t know if I´ll get anywhere with it. But I would sure hope so!
Project Details
Short Description
Sync your Music library from a JRiver instance somewhere in your local Network, including Playlists, threw your local WIFI. In the best case this would work completely without user interaction. At least when you´re in your known home network. I don´t want it to be looking for the server when I´m miles away from home. I would like it very much if music just gets copied over the moment I add it to my JRiver library. So I have it with me when I leave the house without the possibility of me forgetting about having to plug my phone into the computer. It´s also important for me that the program does not unnessesarily resync files. The first time might take forever (it´s WIFI). But once you have your library synced it should be much MUCH quicker as you´ll usually have only 1 album or song to sync and nothing more.
Project Name: JDrop (??)
I´m thinking maybe “JDrop” as in dropping the JRiver into your phone “drop by drop” or something like that. You have any better ideas? I´ll just call it that for now. Can always change it.
Platform
- Android
Witch version I don´t know yet. At least Android 9 lul. I´ll experiment with lower versions once I have it working to begin with.
Programming Language
- Kontlin
Maybe some Java, but planning to stick with Kotlin
What I got so far?
Mostly theorizing what I´m actually gonna do or can achieve.
JRiver hosts a local API on your Server. Can´t really show you the docs as the docs are also hosted locally and not in some wiki on the web. There is a wiki and it tells you open http://localhost:52199/MCWS/v1/doc and replace localhost with whatever.
Useful JRiver API calls
Those are some of the API methods that I found, witch I will likely be using to retrieve the music.
Get the Files
Files
Search
Perform a database search for files.
Parameters:
Query: The search string (empty returns full library) (default: )
Action: The action to perform with the files (MPL: return MPL playlist; Play: plays files; Save: saves the files (as a playlist in the library, etc.); Serialize: return serialized file array (basically a list of file keys); M3U: saves the list as an m3u). (default: mpl)
Shuffle: Set to 1 to shuffle the files. (default: )
ActiveFile: A file key to set as active (used as the file that playback starts with, etc.). (default: -1)
ActiveFileOnly: Set to 1 to trim the returned files to only contain the active file. (default: )
PlayMode: Play mode flags delimited by commas (Add: adds to end of playlist; NextToPlay: adds files in the next to play position). (default: )
Fields: The fields to include in an MPL (use empty to include all fields). (default: )
NoLocalFilenames: Set to 1 to filter out local filenames from MPL output (since they might be meaningless to a server). (default: )
PlayDoctor: Set to 1 to change the files to a Play Doctor generated playlist using these files as a seed. (default: )
SaveMode: Playlist: playlist (overwrites existing; returns ID) (default: )
SaveName: A backslash delimited path used with the action 'Save'. (default: )
NoUI: Set to one to put the player in no UI mode. (default: )
Zone: The zone the command is targetted for. (default: -1)
ZoneType: The type of value provided in 'Zone' (ID: zone id; Index: zone index; Name: zone name). (default: ID)
Response:
Examples:
Click here
File
GetFile
Get the contents of a file in the database.
Parameters:
File: The key of the file. (default: -1)
FileType: The type of value provided in 'File' (Key: file key; Filename: filename of file). (default: Key)
Helper: Allows getting sidecar / helper files (used internally). (default: )
Conversion: The conversion settings to use. (default: )
Quality: The conversion quality to use (low, medium, high, etc.). (default: )
Resolution: The resolution of the target device (allows making better conversion decisions). (default: )
AndroidVersion: The Android version of the target device (if applicable). (default: )
Prepare: Set to 1 to prepare the file (useful when waiting for video conversion, etc.). (default: )
Playback: 0: Downloading (not real-time playback); 1: Real-time playback with update of playback statistics, Scrobbling, etc.; 2: Real-time playback, no playback statistics handling. (default: )
Start: The start position for playback. This is normally seconds (decimal supported), but usage can vary based on playback type. (default: )
MimeType: The mime type to use in the response (leave blank for default mime type). (default: )
HLS: Use HTTP Live Streaming. (default: )
Context: The context used to access the file (used for HTTP Live Streaming). (default: )
Response:
PercentPrepared: The integer progress percentage of a file preparation operation, such as transcoding.
Examples:
Click here
The search function will return all the files in the currently selected library if no search string is passed to it. With the GetFile method it is possible to download that file.
Potential problem: Audio files may or may not be compatible with Android. Especially since some of my library are m4a´s from itunes / ipod times (apples version of mp3´ish, but does not have to be mp3´ish, could be lossless too, but I know mine are not).
Get the Playlists
Playlists
List
Gets a list of all playlists.
Parameters:
Group: Only return playlists within this group. (default: )
IncludeMediaTypes: Return the media types of files in the playlist (comma separated list). Only valid for regular playlists, not smartlists. (default: )
Response:
Examples:
Get list of all playlists.
Get list of all playlists within the 'Smartlists' group.
Get list of all playlists and return the media types.
Playlist
Files
Gets the files of a playlist.
Parameters:
Playlist: The playlist the command is targetted for. (default: )
PlaylistType: The type of value provided in 'Playlist' (ID: playlist id; Path: playlist path). (default: ID)
Action: The action to perform with the files (MPL: return MPL playlist; Play: plays files; Save: saves the files (as a playlist in the library, etc.); Serialize: return serialized file array (basically a list of file keys); M3U: saves the list as an m3u). (default: mpl)
Shuffle: Set to 1 to shuffle the files. (default: )
ActiveFile: A file key to set as active (used as the file that playback starts with, etc.). (default: -1)
ActiveFileOnly: Set to 1 to trim the returned files to only contain the active file. (default: )
PlayMode: Play mode flags delimited by commas (Add: adds to end of playlist; NextToPlay: adds files in the next to play position). (default: )
Fields: The fields to include in an MPL (use empty to include all fields). (default: )
NoLocalFilenames: Set to 1 to filter out local filenames from MPL output (since they might be meaningless to a server). (default: )
PlayDoctor: Set to 1 to change the files to a Play Doctor generated playlist using these files as a seed. (default: )
SaveMode: Playlist: playlist (overwrites existing; returns ID) (default: )
SaveName: A backslash delimited path used with the action 'Save'. (default: )
NoUI: Set to one to put the player in no UI mode. (default: )
Zone: The zone the command is targetted for. (default: -1)
ZoneType: The type of value provided in 'Zone' (ID: zone id; Index: zone index; Name: zone name). (default: ID)
Response:
Examples:
Click here
Those two methods I can use to get the playlists.
One big thing that I have to figure out is how not to break any of the paths for music that has non-conventional artist or album names (since paths are limited in different ways on Android than they are on Windows or Linux). It´s actually likely I will just name the music on the phone with the ID it has in the JRiver library, with sub-folders for each library (dodging the problem essentially :)). As far as managing the playlists on android there is a system wide playlist sqllite database that would work with any music player that didn´t invent their own way to do it.
Things that I will be mostly ignoring for the first working build
- Sync Multiple JRiver Libraries (I have one… I would expect most people to have one), but I will make the folder structure in a way that this would theoretically work too. But a problem with that is multiple instances of the same song could exist in different libraries and Android music players won´t really care much about that. Say you sync yours and the library of a (boy/girl)friend or relative that lives with you. I´d say it´s probably not exactly the most practical thing ever, unless I´m also able to exclude duplicates reliably.
- Selective syncing - I will just sync everything without exceptions. For me this is fine as my entire music library is 26.5GB (and that´s 2263 files…). So honestly I have about 59gb free space on my phone right now with all the music coppied to it. For me it´s no problem. But it might make it unuseable for many others if it ever comes to that.
- Support for old Android versions (will be handled once I have something that works for me)
- UI Design. Won´t do much for the start. Just 3 text boxes for server ip / username / password and a sync button. This does not HAVE TO BE a great looking program to start out with. The UI isn´t important to the functionality. So I will absolutely half ass the UI until I have working program. Clean code / code-design is way more important than pretty UI here.
- Selective playlist syncing. For the start I won´t bother with this either. But there are a lot of automatic playlists. Some of those I have disabled in the GUI, but they are still alive and well in the JRiver database. So… I still eventually want to not have them synced to my phone. But for the very first build I won´t even be syncing playlists to begin with. One step at a time.
- Convert lossless to mp3´s to save space. Thankfully JRiver actually has a built in mechanism for doing that and keep track of the duplicated entries. Won´t be doing that either at the start. But deffinitely a thing on the list that I want to include eventually. This is genually more useful than excluding files entierly imo.
What do I plan to do with it once it´s done?
I don´t know what I´ll do with it yet once I´m “finished”. For starters. I´m building it because I personally want it. After that the possibilities are open. I could put it into the playstore, make a public github repo, or both. Or I could gift it (maybe even sell it?) to JRiver themselves. Well see. For now I gotta make it. For now I hope I can make it work for me and everything else comes after.