Skip to content

Commit

Permalink
Refactor PluginUrlUtils and clean addon preferences (#993)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
SyncedSynapse authored Nov 21, 2023
1 parent 5e6f971 commit 5764e0d
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 162 deletions.
172 changes: 34 additions & 138 deletions app/src/main/java/org/xbmc/kore/ShareOpenActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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<Boolean>() {
@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)
Expand Down Expand Up @@ -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")) {
Expand All @@ -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;
Expand Down
127 changes: 124 additions & 3 deletions app/src/main/java/org/xbmc/kore/utils/PluginUrlUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,101 @@

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;

/**
* 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()) {
Expand All @@ -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);
Expand All @@ -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 = {
"^\\/(?<id>\\d+)$",
Expand All @@ -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;
}
}
Loading

0 comments on commit 5764e0d

Please sign in to comment.