Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor PluginUrlUtils and clean addon preferences #993

Merged
merged 1 commit into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading