changed when video segments are queried

this should fix issues with the background/pop-up player and download dialog
This commit is contained in:
polymorphicshade 2021-02-20 23:55:32 -07:00
parent ea39cb868f
commit 8011192dd3
6 changed files with 116 additions and 92 deletions

View file

@ -101,7 +101,6 @@ import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ShareUtils;
import org.schabi.newpipe.util.SponsorBlockUtils; import org.schabi.newpipe.util.SponsorBlockUtils;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.util.VideoSegment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
@ -160,8 +159,12 @@ public final class VideoDetailFragment
private boolean showRelatedStreams; private boolean showRelatedStreams;
private boolean showDescription; private boolean showDescription;
private String selectedTabTag; private String selectedTabTag;
@AttrRes @NonNull final List<Integer> tabIcons = new ArrayList<>(); @AttrRes
@StringRes @NonNull final List<Integer> tabContentDescriptions = new ArrayList<>(); @NonNull
final List<Integer> tabIcons = new ArrayList<>();
@StringRes
@NonNull
final List<Integer> tabContentDescriptions = new ArrayList<>();
private boolean tabSettingsChanged = false; private boolean tabSettingsChanged = false;
private int lastAppBarVerticalOffset = Integer.MAX_VALUE; // prevents useless updates private int lastAppBarVerticalOffset = Integer.MAX_VALUE; // prevents useless updates
@ -187,12 +190,13 @@ public final class VideoDetailFragment
private final CompositeDisposable disposables = new CompositeDisposable(); private final CompositeDisposable disposables = new CompositeDisposable();
@Nullable @Nullable
private Disposable positionSubscriber = null; private Disposable positionSubscriber = null;
@Nullable
private Disposable videoSegmentsSubscriber = null;
private List<VideoStream> sortedVideoStreams; private List<VideoStream> sortedVideoStreams;
private int selectedVideoStreamIndex = -1; private int selectedVideoStreamIndex = -1;
private BottomSheetBehavior<FrameLayout> bottomSheetBehavior; private BottomSheetBehavior<FrameLayout> bottomSheetBehavior;
private BroadcastReceiver broadcastReceiver; private BroadcastReceiver broadcastReceiver;
private VideoSegment[] videoSegments;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Views // Views
@ -379,6 +383,9 @@ public final class VideoDetailFragment
if (positionSubscriber != null) { if (positionSubscriber != null) {
positionSubscriber.dispose(); positionSubscriber.dispose();
} }
if (videoSegmentsSubscriber != null) {
videoSegmentsSubscriber.dispose();
}
if (currentWorker != null) { if (currentWorker != null) {
currentWorker.dispose(); currentWorker.dispose();
} }
@ -887,43 +894,7 @@ public final class VideoDetailFragment
private void runWorker(final boolean forceLoad, final boolean addToBackStack) { private void runWorker(final boolean forceLoad, final boolean addToBackStack) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
final String apiUrl = prefs.getString(getContext()
.getString(R.string.sponsor_block_api_url_key), null);
final boolean isSponsorBlockEnabled = prefs.getBoolean(getContext()
.getString(R.string.sponsor_block_enable_key), false);
final boolean includeSponsorCategory = prefs.getBoolean(getContext()
.getString(R.string.sponsor_block_category_sponsor_key), false);
final boolean includeIntroCategory = prefs.getBoolean(getContext()
.getString(R.string.sponsor_block_category_intro_key), false);
final boolean includeOutroCategory = prefs.getBoolean(getContext()
.getString(R.string.sponsor_block_category_outro_key), false);
final boolean includeInteractionCategory = prefs.getBoolean(getContext()
.getString(R.string.sponsor_block_category_interaction_key), false);
final boolean includeSelfPromoCategory = prefs.getBoolean(getContext()
.getString(R.string.sponsor_block_category_self_promo_key), false);
final boolean includeMusicCategory = prefs.getBoolean(getContext()
.getString(R.string.sponsor_block_category_non_music_key), false);
currentWorker = ExtractorHelper.getStreamInfo(serviceId, url, forceLoad) currentWorker = ExtractorHelper.getStreamInfo(serviceId, url, forceLoad)
.flatMap(streamInfo -> Single.fromCallable(() -> {
if (isSponsorBlockEnabled
&& streamInfo.getUrl().startsWith("https://www.youtube.com")
&& apiUrl != null
&& !apiUrl.isEmpty()) {
this.videoSegments = SponsorBlockUtils.getYouTubeVideoSegments(
apiUrl,
streamInfo.getId(),
includeSponsorCategory,
includeIntroCategory,
includeOutroCategory,
includeInteractionCategory,
includeSelfPromoCategory,
includeMusicCategory);
}
return streamInfo;
}))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> { .subscribe(result -> {
@ -1182,7 +1153,6 @@ public final class VideoDetailFragment
playerService.getView().setVisibility(View.GONE); playerService.getView().setVisibility(View.GONE);
} }
addVideoPlayerView(); addVideoPlayerView();
playerService.setVideoSegments(videoSegments);
final Intent playerIntent = NavigationHelper final Intent playerIntent = NavigationHelper
.getPlayerIntent(requireContext(), MainPlayer.class, queue, true, autoPlayEnabled); .getPlayerIntent(requireContext(), MainPlayer.class, queue, true, autoPlayEnabled);
@ -1634,15 +1604,28 @@ public final class VideoDetailFragment
} }
public void openDownloadDialog() { public void openDownloadDialog() {
videoSegmentsSubscriber = Single.fromCallable(() -> {
try { try {
final DownloadDialog downloadDialog = DownloadDialog.newInstance(currentInfo); return SponsorBlockUtils.getYouTubeVideoSegments(getContext(), currentInfo);
} catch (final Exception e) {
// TODO: handle
return null;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(videoSegments -> {
try {
final DownloadDialog downloadDialog =
DownloadDialog.newInstance(currentInfo);
downloadDialog.setVideoStreams(sortedVideoStreams); downloadDialog.setVideoStreams(sortedVideoStreams);
downloadDialog.setAudioStreams(currentInfo.getAudioStreams()); downloadDialog.setAudioStreams(currentInfo.getAudioStreams());
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex); downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
downloadDialog.setSubtitleStreams(currentInfo.getSubtitles()); downloadDialog.setSubtitleStreams(currentInfo.getSubtitles());
downloadDialog.setVideoSegments(videoSegments); downloadDialog.setVideoSegments(videoSegments);
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog"); downloadDialog.show(
activity.getSupportFragmentManager(), "downloadDialog");
} catch (final Exception e) { } catch (final Exception e) {
final ErrorInfo info = ErrorInfo.make(UserAction.UI_ERROR, final ErrorInfo info = ErrorInfo.make(UserAction.UI_ERROR,
ServiceList.all() ServiceList.all()
@ -1657,6 +1640,7 @@ public final class VideoDetailFragment
activity.getClass(), activity.getClass(),
activity.findViewById(android.R.id.content), info); activity.findViewById(android.R.id.content), info);
} }
});
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////

View file

@ -37,7 +37,6 @@ import androidx.core.content.ContextCompat;
import org.schabi.newpipe.App; import org.schabi.newpipe.App;
import org.schabi.newpipe.databinding.PlayerBinding; import org.schabi.newpipe.databinding.PlayerBinding;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.util.VideoSegment;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
@ -244,10 +243,6 @@ public final class MainPlayer extends Service {
} }
} }
public void setVideoSegments(final VideoSegment[] videoSegments) {
player.setVideoSegments(videoSegments);
}
public class LocalBinder extends Binder { public class LocalBinder extends Binder {

View file

@ -227,6 +227,7 @@ public final class Player implements
public static final String SELECT_ON_APPEND = "select_on_append"; public static final String SELECT_ON_APPEND = "select_on_append";
public static final String PLAYER_TYPE = "player_type"; public static final String PLAYER_TYPE = "player_type";
public static final String IS_MUTED = "is_muted"; public static final String IS_MUTED = "is_muted";
public static final String VIDEO_SEGMENTS = "video_segments";
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Time constants // Time constants
@ -381,7 +382,6 @@ public final class Player implements
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// SponsorBlock // SponsorBlock
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private VideoSegment[] videoSegments;
private SponsorBlockMode sponsorBlockMode = SponsorBlockMode.DISABLED; private SponsorBlockMode sponsorBlockMode = SponsorBlockMode.DISABLED;
@ -842,7 +842,7 @@ public final class Player implements
} }
if (playQueue != null) { if (playQueue != null) {
playQueueManager = new MediaSourceManager(this, playQueue); playQueueManager = new MediaSourceManager(context, this, playQueue);
} }
} }
@ -4223,13 +4223,6 @@ public final class Player implements
// SponsorBlock // SponsorBlock
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
//region //region
public VideoSegment[] getVideoSegments() {
return videoSegments;
}
public void setVideoSegments(final VideoSegment[] videoSegments) {
this.videoSegments = videoSegments;
}
public void onBlockingSponsorsButtonClicked() { public void onBlockingSponsorsButtonClicked() {
if (DEBUG) { if (DEBUG) {
@ -4263,6 +4256,7 @@ public final class Player implements
} }
public VideoSegment getSkippableSegment(final int progress) { public VideoSegment getSkippableSegment(final int progress) {
final VideoSegment[] videoSegments = currentItem.getVideoSegments();
if (videoSegments == null) { if (videoSegments == null) {
return null; return null;
} }
@ -4285,7 +4279,7 @@ public final class Player implements
private void markSegments() { private void markSegments() {
binding.playbackSeekBar.clearMarkers(); binding.playbackSeekBar.clearMarkers();
final VideoSegment[] segments = getVideoSegments(); final VideoSegment[] segments = currentItem.getVideoSegments();
if (segments == null || segments.length == 0) { if (segments == null || segments.length == 0) {
return; return;

View file

@ -1,5 +1,6 @@
package org.schabi.newpipe.player.playback; package org.schabi.newpipe.player.playback;
import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
@ -23,6 +24,7 @@ import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent;
import org.schabi.newpipe.player.playqueue.events.RemoveEvent; import org.schabi.newpipe.player.playqueue.events.RemoveEvent;
import org.schabi.newpipe.player.playqueue.events.ReorderEvent; import org.schabi.newpipe.player.playqueue.events.ReorderEvent;
import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.SponsorBlockUtils;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -69,6 +71,8 @@ public class MediaSourceManager {
*/ */
private static final int MAXIMUM_LOADER_SIZE = WINDOW_SIZE * 2 + 1; private static final int MAXIMUM_LOADER_SIZE = WINDOW_SIZE * 2 + 1;
@NonNull
private final Context context;
@NonNull @NonNull
private final PlaybackListener playbackListener; private final PlaybackListener playbackListener;
@NonNull @NonNull
@ -125,14 +129,16 @@ public class MediaSourceManager {
private final Handler removeMediaSourceHandler = new Handler(); private final Handler removeMediaSourceHandler = new Handler();
public MediaSourceManager(@NonNull final PlaybackListener listener, public MediaSourceManager(@NonNull final Context context,
@NonNull final PlaybackListener listener,
@NonNull final PlayQueue playQueue) { @NonNull final PlayQueue playQueue) {
this(listener, playQueue, 400L, this(context, listener, playQueue, 400L,
/*playbackNearEndGapMillis=*/TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS), /*playbackNearEndGapMillis=*/TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS),
/*progressUpdateIntervalMillis*/TimeUnit.MILLISECONDS.convert(2, TimeUnit.SECONDS)); /*progressUpdateIntervalMillis*/TimeUnit.MILLISECONDS.convert(2, TimeUnit.SECONDS));
} }
private MediaSourceManager(@NonNull final PlaybackListener listener, private MediaSourceManager(@NonNull final Context context,
@NonNull final PlaybackListener listener,
@NonNull final PlayQueue playQueue, @NonNull final PlayQueue playQueue,
final long loadDebounceMillis, final long loadDebounceMillis,
final long playbackNearEndGapMillis, final long playbackNearEndGapMillis,
@ -146,6 +152,7 @@ public class MediaSourceManager {
+ " ms] for them to be useful."); + " ms] for them to be useful.");
} }
this.context = context;
this.playbackListener = listener; this.playbackListener = listener;
this.playQueue = playQueue; this.playQueue = playQueue;
@ -428,6 +435,9 @@ public class MediaSourceManager {
final long expiration = System.currentTimeMillis() final long expiration = System.currentTimeMillis()
+ ServiceHelper.getCacheExpirationMillis(streamInfo.getServiceId()); + ServiceHelper.getCacheExpirationMillis(streamInfo.getServiceId());
stream.setVideoSegments(SponsorBlockUtils.getYouTubeVideoSegments(context, streamInfo));
return new LoadedMediaSource(source, stream, expiration); return new LoadedMediaSource(source, stream, expiration);
}).onErrorReturn(throwable -> new FailedMediaSource(stream, }).onErrorReturn(throwable -> new FailedMediaSource(stream,
new StreamInfoLoadException(throwable))); new StreamInfoLoadException(throwable)));

View file

@ -7,6 +7,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.VideoSegment;
import java.io.Serializable; import java.io.Serializable;
@ -35,6 +36,8 @@ public class PlayQueueItem implements Serializable {
private long recoveryPosition; private long recoveryPosition;
private Throwable error; private Throwable error;
private VideoSegment[] videoSegments;
PlayQueueItem(@NonNull final StreamInfo info) { PlayQueueItem(@NonNull final StreamInfo info) {
this(info.getName(), info.getUrl(), info.getServiceId(), info.getDuration(), this(info.getName(), info.getUrl(), info.getServiceId(), info.getDuration(),
info.getThumbnailUrl(), info.getUploaderName(), info.getStreamType()); info.getThumbnailUrl(), info.getUploaderName(), info.getStreamType());
@ -142,4 +145,12 @@ public class PlayQueueItem implements Serializable {
public void setAutoQueued(final boolean autoQueued) { public void setAutoQueued(final boolean autoQueued) {
isAutoQueued = autoQueued; isAutoQueued = autoQueued;
} }
public VideoSegment[] getVideoSegments() {
return videoSegments;
}
public void setVideoSegments(final VideoSegment[] videoSegments) {
this.videoSegments = videoSegments;
}
} }

View file

@ -2,10 +2,13 @@ package org.schabi.newpipe.util;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.preference.PreferenceManager;
import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParser;
@ -13,6 +16,8 @@ import com.grack.nanojson.JsonParser;
import org.schabi.newpipe.App; import org.schabi.newpipe.App;
import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.DownloaderImpl;
import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
@ -29,15 +34,40 @@ public final class SponsorBlockUtils {
} }
@SuppressWarnings("CheckStyle") @SuppressWarnings("CheckStyle")
public static VideoSegment[] getYouTubeVideoSegments(final String apiUrl, public static VideoSegment[] getYouTubeVideoSegments(final Context context,
final String videoId, final StreamInfo streamInfo)
final boolean includeSponsorCategory,
final boolean includeIntroCategory,
final boolean includeOutroCategory,
final boolean includeInteractionCategory,
final boolean includeSelfPromoCategory,
final boolean includeMusicCategory)
throws UnsupportedEncodingException { throws UnsupportedEncodingException {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final boolean isSponsorBlockEnabled = prefs.getBoolean(context
.getString(R.string.sponsor_block_enable_key), false);
if (!isSponsorBlockEnabled) {
return null;
}
final String apiUrl = prefs.getString(context
.getString(R.string.sponsor_block_api_url_key), null);
if (!streamInfo.getUrl().startsWith("https://www.youtube.com")
|| apiUrl == null
|| apiUrl.isEmpty()) {
return null;
}
final boolean includeSponsorCategory = prefs.getBoolean(context
.getString(R.string.sponsor_block_category_sponsor_key), false);
final boolean includeIntroCategory = prefs.getBoolean(context
.getString(R.string.sponsor_block_category_intro_key), false);
final boolean includeOutroCategory = prefs.getBoolean(context
.getString(R.string.sponsor_block_category_outro_key), false);
final boolean includeInteractionCategory = prefs.getBoolean(context
.getString(R.string.sponsor_block_category_interaction_key), false);
final boolean includeSelfPromoCategory = prefs.getBoolean(context
.getString(R.string.sponsor_block_category_self_promo_key), false);
final boolean includeMusicCategory = prefs.getBoolean(context
.getString(R.string.sponsor_block_category_non_music_key), false);
final ArrayList<String> categoryParamList = new ArrayList<>(); final ArrayList<String> categoryParamList = new ArrayList<>();
if (includeSponsorCategory) { if (includeSponsorCategory) {
@ -66,7 +96,7 @@ public final class SponsorBlockUtils {
String categoryParams = "[\"" + TextUtils.join("\",\"", categoryParamList) + "\"]"; String categoryParams = "[\"" + TextUtils.join("\",\"", categoryParamList) + "\"]";
categoryParams = URLEncoder.encode(categoryParams, "utf-8"); categoryParams = URLEncoder.encode(categoryParams, "utf-8");
final String videoIdHash = toSha256(videoId); final String videoIdHash = toSha256(streamInfo.getId());
if (videoIdHash == null) { if (videoIdHash == null) {
return null; return null;
@ -106,7 +136,7 @@ public final class SponsorBlockUtils {
final JsonObject jObj1 = (JsonObject) obj1; final JsonObject jObj1 = (JsonObject) obj1;
final String responseVideoId = jObj1.getString("videoID"); final String responseVideoId = jObj1.getString("videoID");
if (!responseVideoId.equals(videoId)) { if (!responseVideoId.equals(streamInfo.getId())) {
continue; continue;
} }