Add in app download functionality - #1404
Conversation
|
So I would call the PR in a beta state. Feature wise, I think it is already at a good point. Adding the view downloads button requires modifying the web app. If something is missing I can still add it, but otherwise next step would be the polish. |
|
Thank you very much for this PR, it looks very good, I have a few questions to ask you: 1 - This is to download media on the device and watch them locally without internet? |
Yes. Currently downloads are saved using Android's built-in DownloadManager. They are saved in for example the Downloads folder so one can playback the file using VLC. This PR adds the functionality to save the download to the app internal storage making playback from within the app possible.
Video files are usually already very well compressed through the used codecs, adding a conventional lossless compression such as zstandard would barely reduce the size. The next step after this PR could adding functionality to support downloads in different bitrates, enabling smaller downloads. |
|
While thinking about implementing the ability to download transcoded media (streams), I realized it would be better if ExoPlayer would handle downloading, since it natively supports HLS streams, making future efforts easier. So I rewrote the download logic to utilize ExoPlayers in-buildt download capabilities. Oh yeah this also makes regular video playback use the downloade / cached files if available. |
|
I would love to test this if you want to post some builds... I have a flight coming up. 😅 |
neBM
left a comment
There was a problem hiding this comment.
Much of the detekt issues blocking the PR seems like quick fixes except for one :/
Looks like we're the unlucky PR that needs that final param and hit the limit! 😆
I've thought of a potential solution but it's by no means perfect.
|
Commit #7ce600c fixes the HLS content source error. Problem was that the exoplayer cache cached videos when playing them back, this was not intended and let to complication. The commit makes the cache read-only. |
|
Commit #6db98b7 improves download notification when multiple downloads are in progress. This should fix all current outstanding problems. |
|
Would it be possible for someone to take a look at this PR and review it please? That'd be great ❤️ |
|
Sorry for taking so long to look into this. I'd like to get these changes in next. Would you mind rebasing this onto the latest master so that I can properly test and review it? |
|
I currently can't really test changes rn. So I can't that any guarantees if it still works. |
This comment was marked as off-topic.
This comment was marked as off-topic.
|
I have to be real, this feature is the most requested feature of the official Jellyfin project (https://features.jellyfin.org/?view=most-wanted). It is understandable that people lead busy lives, but if the maintainer is not able to look into this for almost a whole year, then maybe a solution needs to be found. It makes sense that there are many other Jellyfin Android apps with more features and better maintenance. |
|
@nielsvanvelzen would you be ok with an edge release with some of these long waiting PRs pulled in? A quarterly or sooner unstable release cadence with lower expected polish, separate from the default ship it when it is ready schedule would help keep outside developers who want to collaborate interested... |
|
@satmandu I agree with you that the current situation is not ideal. If someone is willing to help out with one or all of these tasks we can proceed with merging:
Polishing and fixing smaller issues can be done in separate pull requests. |
|
Sure I can look into this again. At least now I finished my semester abroad |
|
Removed merge conflict, will now make sure everything works. |
I get you, the situation totally sucks. I probably shouldn't even have left the project for so long, because getting back into it only got harder because of that. The reality is that I simply have no time to actively maintain this app right now, and it pains me since I also had a lot of plans (including unfinished work like the player UI rewrite). The little time I can spend on it every few weeks to months usually goes mostly into housekeeping and basic maintenance aka dependency updates and such, but that may be the wrong approach. I'm honestly fine with pulling in this PR mostly unreviewed at this point, considering multiple community members already worked on it.
That was already my plan when I last talked to Niels, but that was probably weeks if not months ago, again. Having a download feature in the app would be amazing, and merging this and other bigger PRs would then allow us to iterate on the existing state without risking to break other open PRs again and again. So yes, absolutely agree. Unfortunately, I really can't and don't want to promise anything right now. But I definitely hear and understand the frustration, and won't get in the way while trying to find a solution. |
|
Fixed cache not being properly used when downloading. Tested: |
| <string name="store_videos_in_internal_storage">Store videos in internal storage</string> | ||
| <string name="stored_videos_in_internal_storage_desc">Videos such as movies and TV shows will be stored in the app\'s internal storage. Allowing access from within the app.</string> | ||
| <string name="tv_show_desc">%1$s - S%2$dE%3$d</string> | ||
| <string name="downloading_desc">Downloading %1$d titles</string> |
Check warning
Code scanning / Android Lint
Potential Plurals
nielsvanvelzen
left a comment
There was a problem hiding this comment.
Approving (& merging) for the sake of progression. I have not reviewed or tested these changes, I just looked over it briefly.
As mentioned in my previous comment, any issues with these changes can be resolved in separate pull requests. If you're interested in helping out please do so! We'd appreciate any testers/reviewers & developers. For questions please visit our Android chat via Matrix or Discord (listed at jellyfin.org/contact)
|
Just one thing I noticed that when downloading at lot of episodes at the same time, the total download progress tends to reset, when an episode finishes downloading. Could look into it the next few days. |
| val customPrefs = displayPreferencesDto.customPrefs | ||
|
|
||
| displayPreferences = DisplayPreferences( | ||
| skipBackLength = customPrefs[Constants.DISPLAY_PREFERENCES_SKIP_BACK_LENGTH]?.toLongOrNull() | ||
| ?: Constants.DEFAULT_SEEK_TIME_MS, | ||
| skipForwardLength = customPrefs[Constants.DISPLAY_PREFERENCES_SKIP_FORWARD_LENGTH]?.toLongOrNull() | ||
| ?: Constants.DEFAULT_SEEK_TIME_MS, | ||
| ) | ||
| customPrefs = displayPreferencesDto.customPrefs | ||
| } catch (e: ApiClientException) { | ||
| Timber.e(e, "Failed to load display preferences") | ||
| Timber.e(e, "Failed to load display preferences from API") | ||
| } | ||
|
|
||
| displayPreferences = DisplayPreferences( | ||
| skipBackLength = customPrefs?.get(Constants.DISPLAY_PREFERENCES_SKIP_BACK_LENGTH)?.toLongOrNull() | ||
| ?: Constants.DEFAULT_SEEK_TIME_MS, | ||
| skipForwardLength = customPrefs?.get(Constants.DISPLAY_PREFERENCES_SKIP_FORWARD_LENGTH)?.toLongOrNull() | ||
| ?: Constants.DEFAULT_SEEK_TIME_MS, | ||
| ) |
There was a problem hiding this comment.
The default display preferences should already use these defaults, so I'm not sure these changes are necessary..



This PR internalizes the download process.
Changes
Downloads are stored inside the app directory including thumbnail and external subtitle files. MediaSource data is downloaded as well and stored inside the Room Database.
A new Downloads fragment is added to show available downloads. Downloads are played back using the Exoplayer.
What's left
This is not a complete list
Notes
There is still lots of work to do, but it is already working. I new to Android development, so I am really sorry if something is not correct. Furthermore I implement this functionality as part of my Master's degree, so even if for whatever reason this PR is rejected, I will still complete the project to my best ability. I sorry that I changed some ExoPlayer code even though, it is currently reworked, but due to time constraints, I could not wait.
Thanks for any feedback :)