From 5764e0df7cd3068a1e74a5a2c5a31a63c568bf23 Mon Sep 17 00:00:00 2001 From: Synced Synapse Date: Tue, 21 Nov 2023 12:07:43 +0000 Subject: [PATCH] Refactor PluginUrlUtils and clean addon preferences (#993) 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); } }