Skip to content

Conversation

rien333
Copy link
Contributor

@rien333 rien333 commented Aug 16, 2025

This PR enables sharing multiple audio/video/image files to Kore. Previously, sharing multiple files to Kore required a share action per file.

Use cases

My main use case is sharing ("streaming") whole albums from a music player app — previously, I had to queue every song one by one, which quickly got tiresome. An example of an app that can share whole albums is Auxio.

Additionally, I can see this PR being useful for creating slideshows from your camera roll.

Notable changes

Sorry for the long diff, but this change called for some refactoring. The real meat is in the first two commits, so its wise to start there. The rest are revertible cosmetic tweaks (e.g. renaming vars) and minor follow up fixes.

  • Moved URI-extraction logic into a dedicated function. This is a separate commit (the first one), so that it is easier to verify that this new function produces identical results.
    • This new function IMO makes the code easier to follow, and fixes some small stuff in the previous routine (e.g. a duplicate call to getData()), which frankly was a bit all over.
  • Adjusted HttpApp to make read permission for content:// URIs more persistent, using context.grantUriPermission. In my tests, this read permission is now only revoked when Kore is killed. Having this permission be more persistent is essential for the use case of this PR: without it, Kore would hit permission errors after just one song.
  • Renamed applicationContext to just context in HttpApp (see ca2aa4a). This aligns with actual usage. In fact, it's precisely because we pass the Activity context instead of the application context that the current code works (this blogpost discusses URI permissions in greater detail)
  • Some functions now expect lists of URIs/URLs, instead of singletons. In case of OpenSharedUrls, that proved really helpful: its onSuccess callback now signals "hey, I'm done with all the URLs", so that we avoid running finish() prematurely.

Discussion

  • Kore can now send mixed content to Kodi (e.g. slideshow with pictures and videos). Kore currently interprets these as videoplaylist, which matches the old behavior for unknown/odd intent types.
    • We could instead extract mimeypes from individual URIs, match them to playlist types, and then rewrite OpenSharedUrls such that it explicitly handles collections of URIs of different types. This seems overkill, though.
    • Or we could add a UNKNOWN playlist type (equal to -1), and handle that. There is precedent for an UNKNOWN type both in the API and Kodi's playlistTypes.h.
  • I tried a lot of patterns until I hit upon my current solution. For example, we share a lot functionality with LocalMediaFileListFragments, which I tried to extract into a shared class. I'm not sure why I eventually abandoned this approach (maybe content:// URIs were more difficult to convert to LocalFileLocation than I initially expected), but I'm happy to reconsider this or other approaches.
  • I can make a OpenSharedUrl (singular) action, if desired. But frankly, that code would see no use currently.

Future work

  • Kodi logs still show frequent HTTP errors. Likely harmless, but worth investigating.
  • Music shared in this way won't get any metadata, such as albumartist, release date, etc. That is a bummer if you use last.fm, and it also sucks cosmetically. IMO, this is Kodi's problem: from my test, ffmpeg is perfectly capable of extracting metadata from the first 0.1mb of a music file served over http. However, Kodi has been built with a 'local library'-first philosophy, so it's currently not not fully leveraging ffmpeg's capabilities here.

rien333 added 11 commits August 15, 2025 19:17
The new function should act identically, but is not conflacted with
URL conversion, and avoids a potential duplicate `intent.getData();` call.
the context being used here does not have to be the application
context. Shortening this name leads to more self-documenting
code (such as when context == some Activty, as it sometimes is), and
is much shorter, to boot.
before this, the music queue would _always_ be cleared, even if the
user choose the "Queue on Kodi" share action. This is hardly what you expect when you press a button
named "queue".

This also brings Kore's behavior more in line with similar
code (e.g. startPlaylistIfNoActivePlayers in LocalMediaFileListFragment).

Some of the language here was editied to be a bit less video-centric, as well.
I also think throwing around ! in your code makes it harder to read
This else is also hit when the intent contains mixed content. Not the
most elegant solution for mixed content, but the original behavior
of this block was clearly: "Don't know what to do? Must be a video I
guess". This retains that spirit, for better or worse.

Other, slightly overkill option is to extend the PLAYLISTID
types (Kodi's API recognizes -1, which Kodi code defines as 'NONE').
@@ -178,32 +195,6 @@ private Uri getUrlInsideIntent(Intent intent) {
return null;
}

private Uri getShareLocalUriOrHiddenUri(Intent intent) {
Copy link
Contributor Author

@rien333 rien333 Aug 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function has basically been "inlined" into the new extractUriFromIntent(), and is hence redundant.

I also don't really like how it casts to URLs, then back to URIs, and then later in the code, back to a URL again.

finish();
return;
}

String url = toPluginUrl(videoUri);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the video centric language was unneeded imo

@rien333 rien333 changed the title feat: allow users to share multiple files to Kore feat: allow sharing multiple files to Kore Aug 16, 2025
@rien333 rien333 changed the title feat: allow sharing multiple files to Kore feat: enable sharing multiple files to Kore Aug 16, 2025

if (contentUri == null) {
Bundle bundle = intent.getExtras();
contentUri = (Uri) bundle.get(Intent.EXTRA_STREAM);
Copy link
Contributor Author

@rien333 rien333 Aug 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed this to intent.getParcelableExtra(Intent.EXTRA_STREAM), because I think that is the preferred variant nowadays.

(source: https://developer.android.com/training/sharing/receive#handling-content)

This was the original convention in this file (tho not necessarily in
the codebase), probably to avoid overly long lines.

I'm happy to revert this change, and have all .get() calls not open a newline.
@rien333
Copy link
Contributor Author

rien333 commented Aug 22, 2025

I'll fix the conflicts in a bit!

(I've been using this for a while now, works well so far)

@SyncedSynapse
Copy link
Member

Looks good, thanks for this.

Squashing and merging as usual

@SyncedSynapse SyncedSynapse merged commit 6d634d2 into xbmc:master Sep 1, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants