From 66bb6fa213e941a5f54c62790d53e49471f615ab Mon Sep 17 00:00:00 2001 From: Synced Synapse Date: Tue, 21 Nov 2023 11:57:13 +0000 Subject: [PATCH] Refactor PluginUrlUtils and clean addon preferences Move code from ShareOpenActivity to PluginUtlUtils to keep all code dealing with Plugin URL translation in the same place Tweak preferences screen to show Addon settings in a more logical place --- .../java/org/xbmc/kore/ShareOpenActivity.java | 172 ++++-------------- .../org/xbmc/kore/utils/PluginUrlUtils.java | 127 ++++++++++++- app/src/main/res/values/strings.xml | 4 +- app/src/main/res/xml/preferences.xml | 28 +-- .../xbmc/kore/utils/PluginUrlUtilsTest.java | 10 +- 5 files changed, 179 insertions(+), 162 deletions(-) diff --git a/app/src/main/java/org/xbmc/kore/ShareOpenActivity.java b/app/src/main/java/org/xbmc/kore/ShareOpenActivity.java index 47014b377..d3c090aa2 100644 --- a/app/src/main/java/org/xbmc/kore/ShareOpenActivity.java +++ b/app/src/main/java/org/xbmc/kore/ShareOpenActivity.java @@ -10,11 +10,9 @@ import android.webkit.MimeTypeMap; import android.widget.Toast; -import androidx.annotation.Nullable; import androidx.core.content.pm.ShortcutManagerCompat; import androidx.preference.PreferenceManager; -import org.xbmc.kore.host.HostConnection; import org.xbmc.kore.host.HostInfo; import org.xbmc.kore.host.HostManager; import org.xbmc.kore.host.actions.OpenSharedUrl; @@ -26,11 +24,8 @@ import org.xbmc.kore.utils.PluginUrlUtils; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -98,7 +93,7 @@ protected void handleStartIntent(Intent intent, boolean queue) { } Uri videoUri; - if (action.equals(Intent.ACTION_SEND) && intentType.equals("text/plain")) { + if (action.equals(Intent.ACTION_SEND) && intentType != null && intentType.equals("text/plain")) { // Get the URI, which is stored in Extras videoUri = getPlainTextUri(intent.getStringExtra(Intent.EXTRA_TEXT)); } else { @@ -142,21 +137,22 @@ protected void handleStartIntent(Intent intent, boolean queue) { final Context context = this; new OpenSharedUrl(this, url, title, text, queue, playlistType) .execute(hostManager.getConnection(), - new ApiCallback() { - @Override - public void onSuccess(Boolean wasAlreadyPlaying) { - String msg = queue && wasAlreadyPlaying ? getString(R.string.item_added_to_playlist) : getString(R.string.item_sent_to_kodi); - Toast.makeText(context, msg, Toast.LENGTH_SHORT) - .show(); - } - - @Override - public void onError(int errorCode, String description) { - LogUtils.LOGE(TAG, "Share failed: " + description); - Toast.makeText(context, description, Toast.LENGTH_SHORT) - .show(); - } - }, new Handler(Looper.getMainLooper())); + new ApiCallback<>() { + @Override + public void onSuccess(Boolean wasAlreadyPlaying) { + String msg = queue && wasAlreadyPlaying ? getString(R.string.item_added_to_playlist) + : getString(R.string.item_sent_to_kodi); + Toast.makeText(context, msg, Toast.LENGTH_SHORT) + .show(); + } + + @Override + public void onError(int errorCode, String description) { + LogUtils.LOGE(TAG, "Share failed: " + description); + Toast.makeText(context, description, Toast.LENGTH_SHORT) + .show(); + } + }, new Handler(Looper.getMainLooper())); // Don't display Kore after queueing from another app, otherwise start the remote if (!queue) @@ -244,40 +240,33 @@ private String toPluginUrl(Uri playuri) { String host = playuri.getHost(); String extension = MimeTypeMap.getFileExtensionFromUrl(playuri.toString()); String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + + if (host == null) + return null; + boolean alwaysSendToKodi = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()) .getBoolean(Settings.KEY_PREF_ALWAYS_SENDTOKODI_ADDON, Settings.DEFAULT_PREF_ALWAYS_SENDTOKODI_ADDON); + if (!alwaysSendToKodi) { if (host.endsWith("youtube.com") || host.endsWith("youtu.be")) { - return toYouTubePluginUrl(playuri); + String preferredYouTubeAddonId = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()) + .getString(Settings.KEY_PREF_YOUTUBE_ADDON_ID, Settings.DEFAULT_PREF_YOUTUBE_ADDON_ID); + if (preferredYouTubeAddonId.equals("plugin.video.invidious")) { + return PluginUrlUtils.toInvidiousYouTubePluginUrl(playuri); + } else { + return PluginUrlUtils.toDefaultYouTubePluginUrl(playuri); + } } else if (host.endsWith("vimeo.com")) { - return PluginUrlUtils.toPluginUrlVimeo(playuri); + return PluginUrlUtils.toVimeoPluginUrl(playuri); } else if (host.endsWith("svtplay.se")) { - try { - Pattern pattern = Pattern.compile( - "^(?:https?://)?(?:www\\.)?svtplay\\.se/video/(\\w+/.*)", - Pattern.CASE_INSENSITIVE); - Matcher matcher = pattern.matcher(playuri.toString()); - if (matcher.matches()) { - return "plugin://plugin.video.svtplay/?id=%2Fvideo%2F" - + URLEncoder.encode(matcher.group(1), StandardCharsets.UTF_8.name()) + "&mode=video"; - } - } catch (UnsupportedEncodingException e) { - LogUtils.LOGD(TAG, "Unsuported Encoding Exception: " + e); - return null; - } + return PluginUrlUtils.toSvtPlayPluginUrl(playuri); } else if (host.endsWith("soundcloud.com")) { - try { - return "plugin://plugin.audio.soundcloud/play/?url=" - + URLEncoder.encode(playuri.toString(), StandardCharsets.UTF_8.name()); - } catch (UnsupportedEncodingException e) { - LogUtils.LOGD(TAG, "Unsuported Encoding Exception: " + e); - return null; - } + return PluginUrlUtils.toSoundCloudPluginUrl(playuri); } else if (host.endsWith("twitch.tv")) { - return PluginUrlUtils.toPluginUrlTwitch(playuri); + return PluginUrlUtils.toTwitchPluginUrl(playuri); } else if (PluginUrlUtils.isHostArte(host)) { - return PluginUrlUtils.toPluginUrlArte(playuri); + return PluginUrlUtils.toArtePluginUrl(playuri); } } if (host.startsWith("app.primevideo.com")) { @@ -297,99 +286,6 @@ private String toPluginUrl(Uri playuri) { return null; } - /** - * Converts a YouTube url to a Kodi plugin URL. - * - * @param playuri some URL for YouTube - * @return plugin URL - */ - @Nullable - private String toYouTubePluginUrl(Uri playuri) { - String preferredYouTubeAddonId = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()) - .getString(Settings.KEY_PREF_YOUTUBE_ADDON_ID, Settings.DEFAULT_PREF_YOUTUBE_ADDON_ID); - - if (preferredYouTubeAddonId.equals("plugin.video.invidious")) { - return toInvidiousYouTubePluginUrl(playuri); - } else { - return toDefaultYouTubePluginUrl(playuri); - } - } - - /** - * Converts a YouTube url to an URL for the default YouTube add-on (plugin.video.youtube) - * - * @param playuri some URL for YouTube - * @return plugin URL - */ - @Nullable - private String toDefaultYouTubePluginUrl(Uri playuri) { - String host = playuri.getHost(); - - if (host.endsWith("youtube.com")) { - String videoId = playuri.getQueryParameter("v"); - String playlistId = playuri.getQueryParameter("list"); - Uri.Builder pluginUri = new Uri.Builder() - .scheme("plugin") - .authority("plugin.video.youtube") - .path("play/"); - boolean valid = false; - if (videoId != null) { - valid = true; - pluginUri.appendQueryParameter("video_id", videoId); - } - if (playlistId != null) { - valid = true; - pluginUri.appendQueryParameter("playlist_id", playlistId) - .appendQueryParameter("order", "default"); - } - if (valid) { - return pluginUri.build().toString(); - } - } else if (host.endsWith("youtu.be")) { - return "plugin://plugin.video.youtube/play/?video_id=" - + playuri.getLastPathSegment(); - } - - return null; - } - - /** - * Converts a YouTube url to an URL for the Invidious YouTube add-on (plugin.video.invidious) - * - * @param playuri some URL for YouTube - * @return plugin URL - */ - @Nullable - private String toInvidiousYouTubePluginUrl(Uri playuri) { - String host = playuri.getHost(); - - Uri.Builder pluginUri = new Uri.Builder() - .scheme("plugin") - .authority("plugin.video.invidious") - .path("/") - .appendQueryParameter("action", "play_video"); - - String videoIdParameterKey = "video_id"; - - String videoId; - if (host.endsWith("youtube.com")) { - videoId = playuri.getQueryParameter("v"); - } else if (host.endsWith("youtu.be")) { - videoId = playuri.getLastPathSegment(); - } else { - return null; - } - - if (videoId == null) { - return null; - } - - return pluginUri - .appendQueryParameter(videoIdParameterKey, videoId) - .build() - .toString(); - } - boolean isMediaFile(String mimeType) { if (mimeType == null) { return false; diff --git a/app/src/main/java/org/xbmc/kore/utils/PluginUrlUtils.java b/app/src/main/java/org/xbmc/kore/utils/PluginUrlUtils.java index cc19fd9f2..002abda59 100644 --- a/app/src/main/java/org/xbmc/kore/utils/PluginUrlUtils.java +++ b/app/src/main/java/org/xbmc/kore/utils/PluginUrlUtils.java @@ -17,6 +17,11 @@ import android.net.Uri; +import androidx.annotation.Nullable; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -24,11 +29,89 @@ * Misc util methods for use with plugin URL */ public class PluginUrlUtils { + private static final String TAG = LogUtils.makeLogTag(PluginUrlUtils.class); + + /** + * Converts a YouTube url to an URL for the default YouTube add-on (plugin.video.youtube) + * + * @param playUri some URL for YouTube + * @return plugin URL + */ + @Nullable + public static String toDefaultYouTubePluginUrl(Uri playUri) { + String host = playUri.getHost(); + + if (host.endsWith("youtube.com")) { + String videoId = playUri.getQueryParameter("v"); + String playlistId = playUri.getQueryParameter("list"); + Uri.Builder pluginUri = new Uri.Builder() + .scheme("plugin") + .authority("plugin.video.youtube") + .path("play/"); + boolean valid = false; + if (videoId != null) { + valid = true; + pluginUri.appendQueryParameter("video_id", videoId); + } + if (playlistId != null) { + valid = true; + pluginUri.appendQueryParameter("playlist_id", playlistId) + .appendQueryParameter("order", "default"); + } + if (valid) { + return pluginUri.build().toString(); + } + } else if (host.endsWith("youtu.be")) { + return "plugin://plugin.video.youtube/play/?video_id=" + + playUri.getLastPathSegment(); + } + + return null; + } + + /** + * Converts a YouTube url to an URL for the Invidious YouTube add-on (plugin.video.invidious) + * + * @param playUri some URL for YouTube + * @return plugin URL + */ + @Nullable + public static String toInvidiousYouTubePluginUrl(Uri playUri) { + String host = playUri.getHost(); + + Uri.Builder pluginUri = new Uri.Builder() + .scheme("plugin") + .authority("plugin.video.invidious") + .path("/") + .appendQueryParameter("action", "play_video"); + + String videoIdParameterKey = "video_id"; + + String videoId; + if (host.endsWith("youtube.com")) { + videoId = playUri.getQueryParameter("v"); + } else if (host.endsWith("youtu.be")) { + videoId = playUri.getLastPathSegment(); + } else { + return null; + } + + if (videoId == null) { + return null; + } + + return pluginUri + .appendQueryParameter(videoIdParameterKey, videoId) + .build() + .toString(); + } + + public static boolean isHostArte(String host) { return host.equals("www.arte.tv"); } - public static String toPluginUrlArte(Uri playUri) { + public static String toArtePluginUrl(Uri playUri) { Pattern pattern = Pattern.compile("^https://www.arte.tv/[a-z]{2}/videos/([0-9]{6}-[0-9]{3}-[A-Z])/.*$"); Matcher matcher = pattern.matcher(playUri.toString()); if (matcher.matches()) { @@ -39,7 +122,7 @@ public static String toPluginUrlArte(Uri playUri) { return null; } - public static String toPluginUrlTwitch(Uri playUri) { + public static String toTwitchPluginUrl(Uri playUri) { Matcher twitchStreamMatcher = Pattern.compile("twitch\\.tv/(\\w+)$").matcher(playUri.toString()); if (twitchStreamMatcher.find()) { return "plugin://plugin.video.twitch/?mode=play&channel_name=" + twitchStreamMatcher.group(1); @@ -51,7 +134,7 @@ public static String toPluginUrlTwitch(Uri playUri) { return null; } - public static String toPluginUrlVimeo(Uri playUri) { + public static String toVimeoPluginUrl(Uri playUri) { String route = playUri.getPath(); String[] routePatterns = { "^\\/(?\\d+)$", @@ -74,4 +157,42 @@ public static String toPluginUrlVimeo(Uri playUri) { return null; } + + /** + * Converts a SvtPlay uri to an uri for the the respective plugin + * + * @param playUri some URL for svtplay + * @return plugin URI + */ + public static String toSvtPlayPluginUrl(Uri playUri) { + try { + Pattern pattern = Pattern.compile( + "^(?:https?://)?(?:www\\.)?svtplay\\.se/video/(\\w+/.*)", + Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(playUri.toString()); + if (matcher.matches()) { + return "plugin://plugin.video.svtplay/?id=%2Fvideo%2F" + + URLEncoder.encode(matcher.group(1), StandardCharsets.UTF_8.name()) + "&mode=video"; + } + } catch (UnsupportedEncodingException e) { + LogUtils.LOGD(TAG, "Unsuported Encoding Exception: " + e); + } + return null; + } + + /** + * Converts a Soundcloud uri to an uri for the the respective plugin + * + * @param playUri some URL for soundcloud + * @return plugin URI + */ + public static String toSoundCloudPluginUrl(Uri playUri) { + try { + return "plugin://plugin.audio.soundcloud/play/?url=" + + URLEncoder.encode(playUri.toString(), StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + LogUtils.LOGD(TAG, "Unsuported Encoding Exception: " + e); + } + return null; + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3379465af..0f6ab429c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -494,7 +494,7 @@ Preferred YouTube addon - YouTube (plugin.video.youtube) - Invidious (plugin.video.invidious) + YouTube + Invidious \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index edbda0dbc..c4d734fb7 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -53,20 +53,6 @@ android:defaultValue="@array/default_values_remote_bar_items" app:singleLineTitle="false"/> - - - - @@ -130,6 +116,20 @@ android:defaultValue="@array/default_values_download_media_items" app:singleLineTitle="false"/> + + + + diff --git a/app/src/test/java/org/xbmc/kore/utils/PluginUrlUtilsTest.java b/app/src/test/java/org/xbmc/kore/utils/PluginUrlUtilsTest.java index 80926ba49..7eac399a7 100644 --- a/app/src/test/java/org/xbmc/kore/utils/PluginUrlUtilsTest.java +++ b/app/src/test/java/org/xbmc/kore/utils/PluginUrlUtilsTest.java @@ -40,7 +40,7 @@ public void isHostArte() throws Exception { @Test public void toPluginUrlArte() throws Exception { Uri playUri = Uri.parse("https://www.arte.tv/fr/videos/084692-000-A/mongolie-le-reve-d-une-jeune-nomade/"); - String pluginUrl = PluginUrlUtils.toPluginUrlArte(playUri); + String pluginUrl = PluginUrlUtils.toArtePluginUrl(playUri); assertNotNull(pluginUrl); assertEquals("plugin://plugin.video.arteplussept/play/SHOW/084692-000-A", pluginUrl); } @@ -48,19 +48,19 @@ public void toPluginUrlArte() throws Exception { @Test public void toPluginUrlVimeo() throws Exception { Uri playUriDefault = Uri.parse("https://vimeo.com/12345"); - String pluginUrlDefault = PluginUrlUtils.toPluginUrlVimeo(playUriDefault); + String pluginUrlDefault = PluginUrlUtils.toVimeoPluginUrl(playUriDefault); assertEquals("plugin://plugin.video.vimeo/play/?video_id=12345", pluginUrlDefault); Uri playUriChannel = Uri.parse("https://vimeo.com/channels/staffpicks/654321"); - String pluginUrlChannel = PluginUrlUtils.toPluginUrlVimeo(playUriChannel); + String pluginUrlChannel = PluginUrlUtils.toVimeoPluginUrl(playUriChannel); assertEquals("plugin://plugin.video.vimeo/play/?video_id=654321", pluginUrlChannel); Uri playUriShowcase = Uri.parse("https://vimeo.com/showcase/123/video/1234567"); - String pluginUrlShowcase = PluginUrlUtils.toPluginUrlVimeo(playUriShowcase); + String pluginUrlShowcase = PluginUrlUtils.toVimeoPluginUrl(playUriShowcase); assertEquals("plugin://plugin.video.vimeo/play/?video_id=1234567", pluginUrlShowcase); Uri playUriUnlisted = Uri.parse("https://vimeo.com/1234/hash"); - String pluginUrlUnlisted = PluginUrlUtils.toPluginUrlVimeo(playUriUnlisted); + String pluginUrlUnlisted = PluginUrlUtils.toVimeoPluginUrl(playUriUnlisted); assertEquals("plugin://plugin.video.vimeo/play/?video_id=1234:hash", pluginUrlUnlisted); } }