mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-12-23 16:40:32 +00:00
Open recognized timestamps in the description of contents in the popup player
This commit adds support of opening recognized timestamps in the popup player instead of starting an intent which opens the YouTube website with the video timestamp.
This commit is contained in:
parent
06d10cf9aa
commit
4031777606
@ -1,6 +1,5 @@
|
|||||||
package org.schabi.newpipe.util;
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.text.Layout;
|
import android.text.Layout;
|
||||||
import android.text.Selection;
|
import android.text.Selection;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
@ -11,27 +10,9 @@ import android.view.MotionEvent;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|
||||||
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
|
||||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.rxjava3.core.Single;
|
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
|
||||||
|
|
||||||
public class CommentTextOnTouchListener implements View.OnTouchListener {
|
public class CommentTextOnTouchListener implements View.OnTouchListener {
|
||||||
public static final CommentTextOnTouchListener INSTANCE = new CommentTextOnTouchListener();
|
public static final CommentTextOnTouchListener INSTANCE = new CommentTextOnTouchListener();
|
||||||
|
|
||||||
private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("(.*)#timestamp=(\\d+)");
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(final View v, final MotionEvent event) {
|
public boolean onTouch(final View v, final MotionEvent event) {
|
||||||
if (!(v instanceof TextView)) {
|
if (!(v instanceof TextView)) {
|
||||||
@ -66,7 +47,8 @@ public class CommentTextOnTouchListener implements View.OnTouchListener {
|
|||||||
if (action == MotionEvent.ACTION_UP) {
|
if (action == MotionEvent.ACTION_UP) {
|
||||||
boolean handled = false;
|
boolean handled = false;
|
||||||
if (link[0] instanceof URLSpan) {
|
if (link[0] instanceof URLSpan) {
|
||||||
handled = handleUrl(v.getContext(), (URLSpan) link[0]);
|
handled = URLHandler.handleUrl(v.getContext(),
|
||||||
|
((URLSpan) link[0]).getURL(), 1);
|
||||||
}
|
}
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
ShareUtils.openUrlInBrowser(v.getContext(),
|
ShareUtils.openUrlInBrowser(v.getContext(),
|
||||||
@ -83,52 +65,4 @@ public class CommentTextOnTouchListener implements View.OnTouchListener {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleUrl(final Context context, final URLSpan urlSpan) {
|
|
||||||
String url = urlSpan.getURL();
|
|
||||||
int seconds = -1;
|
|
||||||
final Matcher matcher = TIMESTAMP_PATTERN.matcher(url);
|
|
||||||
if (matcher.matches()) {
|
|
||||||
url = matcher.group(1);
|
|
||||||
seconds = Integer.parseInt(matcher.group(2));
|
|
||||||
}
|
|
||||||
final StreamingService service;
|
|
||||||
final StreamingService.LinkType linkType;
|
|
||||||
try {
|
|
||||||
service = NewPipe.getServiceByUrl(url);
|
|
||||||
linkType = service.getLinkTypeByUrl(url);
|
|
||||||
} catch (final ExtractionException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (linkType == StreamingService.LinkType.NONE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (linkType == StreamingService.LinkType.STREAM && seconds != -1) {
|
|
||||||
return playOnPopup(context, url, service, seconds);
|
|
||||||
} else {
|
|
||||||
NavigationHelper.openRouterActivity(context, url);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean playOnPopup(final Context context, final String url,
|
|
||||||
final StreamingService service, final int seconds) {
|
|
||||||
final LinkHandlerFactory factory = service.getStreamLHFactory();
|
|
||||||
final String cleanUrl;
|
|
||||||
try {
|
|
||||||
cleanUrl = factory.getUrl(factory.getId(url));
|
|
||||||
} catch (final ParsingException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final Single single
|
|
||||||
= ExtractorHelper.getStreamInfo(service.getServiceId(), cleanUrl, false);
|
|
||||||
single.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(info -> {
|
|
||||||
final PlayQueue playQueue
|
|
||||||
= new SinglePlayQueue((StreamInfo) info, seconds * 1000);
|
|
||||||
NavigationHelper.playOnPopupPlayer(context, playQueue, false);
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,9 @@ public final class TextLinkifier {
|
|||||||
for (final URLSpan span : urls) {
|
for (final URLSpan span : urls) {
|
||||||
final ClickableSpan clickableSpan = new ClickableSpan() {
|
final ClickableSpan clickableSpan = new ClickableSpan() {
|
||||||
public void onClick(@NonNull final View view) {
|
public void onClick(@NonNull final View view) {
|
||||||
ShareUtils.openUrlInBrowser(context, span.getURL(), false);
|
if (!URLHandler.handleUrl(context, span.getURL(), 0)) {
|
||||||
|
ShareUtils.openUrlInBrowser(context, span.getURL(), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
113
app/src/main/java/org/schabi/newpipe/util/URLHandler.java
Normal file
113
app/src/main/java/org/schabi/newpipe/util/URLHandler.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
|
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.rxjava3.core.Single;
|
||||||
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
|
|
||||||
|
public final class URLHandler {
|
||||||
|
|
||||||
|
private URLHandler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an URL can be handled in NewPipe.
|
||||||
|
* <p>
|
||||||
|
* This method will check if the provided url can be handled in NewPipe or not. If this is a
|
||||||
|
* service URL with a timestamp, the popup player will be opened.
|
||||||
|
* <p>
|
||||||
|
* The timestamp param accepts two integers, corresponding to two timestamps types:
|
||||||
|
* 0 for {@code &t=} (used for timestamps in descriptions),
|
||||||
|
* 1 for {@code #timestamp=} (used for timestamps in comments).
|
||||||
|
* Any other value of this integer will return false.
|
||||||
|
*
|
||||||
|
* @param context the context to be used
|
||||||
|
* @param url the URL to check if it can be handled
|
||||||
|
* @param timestampType the type of timestamp
|
||||||
|
* @return true if the URL can be handled by NewPipe, false if it cannot
|
||||||
|
*/
|
||||||
|
public static boolean handleUrl(final Context context, final String url, final int timestampType) {
|
||||||
|
String matchedUrl = "";
|
||||||
|
int seconds = -1;
|
||||||
|
final Pattern TIMESTAMP_PATTERN;
|
||||||
|
|
||||||
|
if (timestampType == 0) {
|
||||||
|
TIMESTAMP_PATTERN = Pattern.compile("(.*)&t=(\\d+)");
|
||||||
|
} else if (timestampType == 1) {
|
||||||
|
TIMESTAMP_PATTERN = Pattern.compile("(.*)#timestamp=(\\d+)");
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Matcher matcher = TIMESTAMP_PATTERN.matcher(url);
|
||||||
|
if (matcher.matches()) {
|
||||||
|
matchedUrl = matcher.group(1);
|
||||||
|
seconds = Integer.parseInt(matcher.group(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
final StreamingService service;
|
||||||
|
final StreamingService.LinkType linkType;
|
||||||
|
|
||||||
|
try {
|
||||||
|
service = NewPipe.getServiceByUrl(matchedUrl);
|
||||||
|
linkType = service.getLinkTypeByUrl(matchedUrl);
|
||||||
|
} catch (final ExtractionException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linkType == StreamingService.LinkType.NONE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (linkType == StreamingService.LinkType.STREAM && seconds != -1) {
|
||||||
|
return playOnPopup(context, matchedUrl, service, seconds);
|
||||||
|
} else {
|
||||||
|
NavigationHelper.openRouterActivity(context, matchedUrl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play a content in the floating player.
|
||||||
|
*
|
||||||
|
* @param context the context to be used
|
||||||
|
* @param url the URL of the content
|
||||||
|
* @param service the service of the content
|
||||||
|
* @param seconds the position in seconds at which the floating player will start
|
||||||
|
* @return true if the playback of the content has successfully started or false if not
|
||||||
|
*/
|
||||||
|
private static boolean playOnPopup(final Context context, final String url,
|
||||||
|
final StreamingService service, final int seconds) {
|
||||||
|
final LinkHandlerFactory factory = service.getStreamLHFactory();
|
||||||
|
final String cleanUrl;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cleanUrl = factory.getUrl(factory.getId(url));
|
||||||
|
} catch (final ParsingException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Single single
|
||||||
|
= ExtractorHelper.getStreamInfo(service.getServiceId(), cleanUrl, false);
|
||||||
|
single.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(info -> {
|
||||||
|
final PlayQueue playQueue
|
||||||
|
= new SinglePlayQueue((StreamInfo) info, seconds * 1000);
|
||||||
|
NavigationHelper.playOnPopupPlayer(context, playQueue, false);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user