mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	handling timestamp links in comments
This commit is contained in:
		| @@ -36,7 +36,6 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException; | |||||||
| import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.extractor.stream.VideoStream; | import org.schabi.newpipe.extractor.stream.VideoStream; | ||||||
| import org.schabi.newpipe.player.helper.PlayerHelper; |  | ||||||
| import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; | import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; | ||||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; | import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; | ||||||
| @@ -81,10 +80,13 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|     protected int selectedPreviously = -1; |     protected int selectedPreviously = -1; | ||||||
|  |  | ||||||
|     protected String currentUrl; |     protected String currentUrl; | ||||||
|  |     protected boolean internalRoute = false; | ||||||
|     protected final CompositeDisposable disposables = new CompositeDisposable(); |     protected final CompositeDisposable disposables = new CompositeDisposable(); | ||||||
|  |  | ||||||
|     private boolean selectionIsDownload = false; |     private boolean selectionIsDownload = false; | ||||||
|  |  | ||||||
|  |     public static final String internalRouteKey = "internalRoute"; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |     protected void onCreate(Bundle savedInstanceState) { | ||||||
|         super.onCreate(savedInstanceState); |         super.onCreate(savedInstanceState); | ||||||
| @@ -99,6 +101,8 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         internalRoute = getIntent().getBooleanExtra(internalRouteKey, false); | ||||||
|  |  | ||||||
|         setTheme(ThemeHelper.isLightThemeSelected(this) |         setTheme(ThemeHelper.isLightThemeSelected(this) | ||||||
|                 ? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark); |                 ? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark); | ||||||
|     } |     } | ||||||
| @@ -383,8 +387,10 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|                     .subscribeOn(Schedulers.io()) |                     .subscribeOn(Schedulers.io()) | ||||||
|                     .observeOn(AndroidSchedulers.mainThread()) |                     .observeOn(AndroidSchedulers.mainThread()) | ||||||
|                     .subscribe(intent -> { |                     .subscribe(intent -> { | ||||||
|                         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |                         if(!internalRoute){ | ||||||
|                         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); |                             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||||
|  |                             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); | ||||||
|  |                         } | ||||||
|                         startActivity(intent); |                         startActivity(intent); | ||||||
|  |  | ||||||
|                         finish(); |                         finish(); | ||||||
|   | |||||||
| @@ -15,6 +15,9 @@ import org.schabi.newpipe.util.CommentTextOnTouchListener; | |||||||
| import org.schabi.newpipe.util.ImageDisplayConstants; | import org.schabi.newpipe.util.ImageDisplayConstants; | ||||||
| import org.schabi.newpipe.util.NavigationHelper; | import org.schabi.newpipe.util.NavigationHelper; | ||||||
|  |  | ||||||
|  | import java.util.regex.Matcher; | ||||||
|  | import java.util.regex.Pattern; | ||||||
|  |  | ||||||
| import de.hdodenhof.circleimageview.CircleImageView; | import de.hdodenhof.circleimageview.CircleImageView; | ||||||
|  |  | ||||||
| public class CommentsMiniInfoItemHolder extends InfoItemHolder { | public class CommentsMiniInfoItemHolder extends InfoItemHolder { | ||||||
| @@ -28,7 +31,23 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { | |||||||
|     private static final int commentExpandedLines = 1000; |     private static final int commentExpandedLines = 1000; | ||||||
|  |  | ||||||
|     private String commentText; |     private String commentText; | ||||||
|     private boolean containsLinks = false; |     private String streamUrl; | ||||||
|  |  | ||||||
|  |     private static final Pattern pattern = Pattern.compile("(\\d+:)?(\\d+)?:(\\d+)"); | ||||||
|  |  | ||||||
|  |     private final Linkify.TransformFilter timestampLink = new Linkify.TransformFilter() { | ||||||
|  |         @Override | ||||||
|  |         public String transformUrl(Matcher match, String url) { | ||||||
|  |             int timestamp = 0; | ||||||
|  |             String hours = match.group(1); | ||||||
|  |             String minutes = match.group(2); | ||||||
|  |             String seconds = match.group(3); | ||||||
|  |             if(hours != null) timestamp += (Integer.parseInt(hours.replace(":", ""))*3600); | ||||||
|  |             if(minutes != null) timestamp += (Integer.parseInt(minutes.replace(":", ""))*60); | ||||||
|  |             if(seconds != null) timestamp += (Integer.parseInt(seconds)); | ||||||
|  |             return streamUrl + url.replace(match.group(0), "&t=" + String.valueOf(timestamp)); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     CommentsMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) { |     CommentsMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) { | ||||||
|         super(infoItemBuilder, layoutId, parent); |         super(infoItemBuilder, layoutId, parent); | ||||||
| @@ -70,10 +89,12 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |         streamUrl = item.getUrl(); | ||||||
|  |  | ||||||
|         itemContentView.setMaxLines(commentDefaultLines); |         itemContentView.setMaxLines(commentDefaultLines); | ||||||
|         commentText = item.getCommentText(); |         commentText = item.getCommentText(); | ||||||
|         itemContentView.setText(commentText); |         itemContentView.setText(commentText); | ||||||
|         containsLinks = linkify(); |         linkify(); | ||||||
|         itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE); |         itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE); | ||||||
|  |  | ||||||
|         if(itemContentView.getLineCount() == 0){ |         if(itemContentView.getLineCount() == 0){ | ||||||
| @@ -100,7 +121,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { | |||||||
|             int endOfLastLine = itemContentView.getLayout().getLineEnd(commentDefaultLines - 1); |             int endOfLastLine = itemContentView.getLayout().getLineEnd(commentDefaultLines - 1); | ||||||
|             String newVal = itemContentView.getText().subSequence(0, endOfLastLine - 3) + "..."; |             String newVal = itemContentView.getText().subSequence(0, endOfLastLine - 3) + "..."; | ||||||
|             itemContentView.setText(newVal); |             itemContentView.setText(newVal); | ||||||
|             if(containsLinks) linkify(); |             linkify(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -115,12 +136,12 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { | |||||||
|     private void expand() { |     private void expand() { | ||||||
|         itemContentView.setMaxLines(commentExpandedLines); |         itemContentView.setMaxLines(commentExpandedLines); | ||||||
|         itemContentView.setText(commentText); |         itemContentView.setText(commentText); | ||||||
|         if(containsLinks) linkify(); |         linkify(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private boolean linkify(){ |     private void linkify(){ | ||||||
|         boolean res = Linkify.addLinks(itemContentView, Linkify.WEB_URLS); |         Linkify.addLinks(itemContentView, Linkify.WEB_URLS); | ||||||
|  |         Linkify.addLinks(itemContentView, pattern, null, null, timestampLink); | ||||||
|         itemContentView.setMovementMethod(null); |         itemContentView.setMovementMethod(null); | ||||||
|         return res; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,18 +1,38 @@ | |||||||
| 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; | ||||||
| import android.text.Spanned; | import android.text.Spanned; | ||||||
| import android.text.style.ClickableSpan; | import android.text.style.ClickableSpan; | ||||||
|  | import android.text.style.URLSpan; | ||||||
| import android.view.MotionEvent; | 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.Single; | ||||||
|  | import io.reactivex.android.schedulers.AndroidSchedulers; | ||||||
|  | import io.reactivex.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 timestampPattern = Pattern.compile(".*&t=(\\d+)"); | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public boolean onTouch(View v, MotionEvent event) { |     public boolean onTouch(View v, MotionEvent event) { | ||||||
|         if(!(v instanceof TextView)){ |         if(!(v instanceof TextView)){ | ||||||
| @@ -45,7 +65,11 @@ public class CommentTextOnTouchListener implements View.OnTouchListener { | |||||||
|  |  | ||||||
|                 if (link.length != 0) { |                 if (link.length != 0) { | ||||||
|                     if (action == MotionEvent.ACTION_UP) { |                     if (action == MotionEvent.ACTION_UP) { | ||||||
|                         link[0].onClick(widget); |                         boolean handled = false; | ||||||
|  |                         if(link[0] instanceof URLSpan){ | ||||||
|  |                             handled = handleUrl(v.getContext(), (URLSpan) link[0]); | ||||||
|  |                         } | ||||||
|  |                         if(!handled) link[0].onClick(widget); | ||||||
|                     } else if (action == MotionEvent.ACTION_DOWN) { |                     } else if (action == MotionEvent.ACTION_DOWN) { | ||||||
|                         Selection.setSelection(buffer, |                         Selection.setSelection(buffer, | ||||||
|                                 buffer.getSpanStart(link[0]), |                                 buffer.getSpanStart(link[0]), | ||||||
| @@ -59,4 +83,46 @@ public class CommentTextOnTouchListener implements View.OnTouchListener { | |||||||
|  |  | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private boolean handleUrl(Context context, URLSpan urlSpan) { | ||||||
|  |         String url = urlSpan.getURL(); | ||||||
|  |         StreamingService service; | ||||||
|  |         StreamingService.LinkType linkType; | ||||||
|  |         try { | ||||||
|  |             service = NewPipe.getServiceByUrl(url); | ||||||
|  |             linkType = service.getLinkTypeByUrl(url); | ||||||
|  |         } catch (ExtractionException e) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         if(linkType == StreamingService.LinkType.NONE){ | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         Matcher matcher = timestampPattern.matcher(url); | ||||||
|  |         if(linkType == StreamingService.LinkType.STREAM && matcher.matches()){ | ||||||
|  |             int seconds = Integer.parseInt(matcher.group(1)); | ||||||
|  |             return playOnPopup(context, url, service, seconds); | ||||||
|  |         }else{ | ||||||
|  |             NavigationHelper.openRouterActivity(context, url); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private boolean playOnPopup(Context context, String url, StreamingService service, int seconds) { | ||||||
|  |         LinkHandlerFactory factory = service.getStreamLHFactory(); | ||||||
|  |         String cleanUrl = null; | ||||||
|  |         try { | ||||||
|  |             cleanUrl = factory.getUrl(factory.getId(url)); | ||||||
|  |         } catch (ParsingException e) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         Single single = ExtractorHelper.getStreamInfo(service.getServiceId(), cleanUrl, false); | ||||||
|  |         single.subscribeOn(Schedulers.io()) | ||||||
|  |                 .observeOn(AndroidSchedulers.mainThread()) | ||||||
|  |                 .subscribe(info -> { | ||||||
|  |                     PlayQueue playQueue = new SinglePlayQueue((StreamInfo) info); | ||||||
|  |                     ((StreamInfo) info).setStartPosition(seconds); | ||||||
|  |                     NavigationHelper.enqueueOnPopupPlayer(context, playQueue, true); | ||||||
|  |                 }); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ import com.nostra13.universalimageloader.core.ImageLoader; | |||||||
|  |  | ||||||
| import org.schabi.newpipe.MainActivity; | import org.schabi.newpipe.MainActivity; | ||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
|  | import org.schabi.newpipe.RouterActivity; | ||||||
| import org.schabi.newpipe.about.AboutActivity; | import org.schabi.newpipe.about.AboutActivity; | ||||||
| import org.schabi.newpipe.download.DownloadActivity; | import org.schabi.newpipe.download.DownloadActivity; | ||||||
| import org.schabi.newpipe.extractor.NewPipe; | import org.schabi.newpipe.extractor.NewPipe; | ||||||
| @@ -34,11 +35,11 @@ import org.schabi.newpipe.fragments.MainFragment; | |||||||
| import org.schabi.newpipe.fragments.detail.VideoDetailFragment; | import org.schabi.newpipe.fragments.detail.VideoDetailFragment; | ||||||
| import org.schabi.newpipe.fragments.list.channel.ChannelFragment; | import org.schabi.newpipe.fragments.list.channel.ChannelFragment; | ||||||
| import org.schabi.newpipe.fragments.list.comments.CommentsFragment; | import org.schabi.newpipe.fragments.list.comments.CommentsFragment; | ||||||
| import org.schabi.newpipe.local.bookmark.BookmarkFragment; |  | ||||||
| import org.schabi.newpipe.local.feed.FeedFragment; |  | ||||||
| import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; | import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; | ||||||
| import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; | import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; | ||||||
| import org.schabi.newpipe.fragments.list.search.SearchFragment; | import org.schabi.newpipe.fragments.list.search.SearchFragment; | ||||||
|  | import org.schabi.newpipe.local.bookmark.BookmarkFragment; | ||||||
|  | import org.schabi.newpipe.local.feed.FeedFragment; | ||||||
| import org.schabi.newpipe.local.history.StatisticsPlaylistFragment; | import org.schabi.newpipe.local.history.StatisticsPlaylistFragment; | ||||||
| import org.schabi.newpipe.local.playlist.LocalPlaylistFragment; | import org.schabi.newpipe.local.playlist.LocalPlaylistFragment; | ||||||
| import org.schabi.newpipe.local.subscription.SubscriptionFragment; | import org.schabi.newpipe.local.subscription.SubscriptionFragment; | ||||||
| @@ -422,6 +423,13 @@ public class NavigationHelper { | |||||||
|         context.startActivity(mIntent); |         context.startActivity(mIntent); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static void openRouterActivity(Context context, String url) { | ||||||
|  |         Intent mIntent = new Intent(context, RouterActivity.class); | ||||||
|  |         mIntent.setData(Uri.parse(url)); | ||||||
|  |         mIntent.putExtra(RouterActivity.internalRouteKey, true); | ||||||
|  |         context.startActivity(mIntent); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public static void openAbout(Context context) { |     public static void openAbout(Context context) { | ||||||
|         Intent intent = new Intent(context, AboutActivity.class); |         Intent intent = new Intent(context, AboutActivity.class); | ||||||
|         context.startActivity(intent); |         context.startActivity(intent); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Ritvik Saraf
					Ritvik Saraf