From 077f34c922703a62b4144f43e1a73d3f2096e543 Mon Sep 17 00:00:00 2001
From: AudricV <74829229+AudricV@users.noreply.github.com>
Date: Sat, 5 Jul 2025 20:26:37 +0200
Subject: [PATCH 1/7] Add a YouTube DASH manifest parser to make live DASH
manifests usable
This is a hacky solution, a better one should be investigated and used.
---
.../helper/YoutubeDashLiveManifestParser.java | 68 +++++++++++++++++++
1 file changed, 68 insertions(+)
create mode 100644 app/src/main/java/org/schabi/newpipe/player/helper/YoutubeDashLiveManifestParser.java
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/YoutubeDashLiveManifestParser.java b/app/src/main/java/org/schabi/newpipe/player/helper/YoutubeDashLiveManifestParser.java
new file mode 100644
index 000000000..00f5de071
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/YoutubeDashLiveManifestParser.java
@@ -0,0 +1,68 @@
+package org.schabi.newpipe.player.helper;
+
+import android.net.Uri;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
+import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
+import com.google.android.exoplayer2.source.dash.manifest.Period;
+import com.google.android.exoplayer2.source.dash.manifest.ProgramInformation;
+import com.google.android.exoplayer2.source.dash.manifest.ServiceDescriptionElement;
+import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement;
+
+import java.util.List;
+
+/**
+ * A {@link DashManifestParser} fixing YouTube DASH manifests to allow starting playback from the
+ * newest period available instead of the earliest one in some cases.
+ *
+ *
+ * It changes the {@code availabilityStartTime} passed to a custom value doing the workaround.
+ * A better approach to fix the issue should be investigated and used in the future.
+ *
+ */
+public class YoutubeDashLiveManifestParser extends DashManifestParser {
+
+ // Result of Util.parseXsDateTime("1970-01-01T00:00:00Z")
+ private static final long AVAILABILITY_START_TIME_TO_USE = 0;
+
+ // There is no computation made with the availabilityStartTime value in the
+ // parseMediaPresentationDescription method itself, so we can just override methods called in
+ // this method using the workaround value
+ // Overriding parsePeriod does not seem to be needed
+
+ @SuppressWarnings("checkstyle:ParameterNumber")
+ @NonNull
+ @Override
+ protected DashManifest buildMediaPresentationDescription(
+ final long availabilityStartTime,
+ final long durationMs,
+ final long minBufferTimeMs,
+ final boolean dynamic,
+ final long minUpdateTimeMs,
+ final long timeShiftBufferDepthMs,
+ final long suggestedPresentationDelayMs,
+ final long publishTimeMs,
+ @Nullable final ProgramInformation programInformation,
+ @Nullable final UtcTimingElement utcTiming,
+ @Nullable final ServiceDescriptionElement serviceDescription,
+ @Nullable final Uri location,
+ @NonNull final List periods) {
+ return super.buildMediaPresentationDescription(
+ AVAILABILITY_START_TIME_TO_USE,
+ durationMs,
+ minBufferTimeMs,
+ dynamic,
+ minUpdateTimeMs,
+ timeShiftBufferDepthMs,
+ suggestedPresentationDelayMs,
+ publishTimeMs,
+ programInformation,
+ utcTiming,
+ serviceDescription,
+ location,
+ periods);
+ }
+}
From c670ad80ee563e670b274f7a3c137a318d176218 Mon Sep 17 00:00:00 2001
From: AudricV <74829229+AudricV@users.noreply.github.com>
Date: Sat, 5 Jul 2025 20:33:37 +0200
Subject: [PATCH 2/7] Use DASH first instead of HLS and YouTube's DASH parser
for lives
---
.../newpipe/player/helper/PlayerDataSource.java | 7 +++++++
.../newpipe/player/resolver/PlaybackResolver.java | 13 +++++++++----
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java
index 0530d56e9..506b643fe 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java
@@ -129,6 +129,13 @@ public class PlayerDataSource {
getDefaultDashChunkSourceFactory(cachelessDataSourceFactory),
cachelessDataSourceFactory);
}
+
+ public DashMediaSource.Factory getLiveYoutubeDashMediaSourceFactory() {
+ return new DashMediaSource.Factory(
+ getDefaultDashChunkSourceFactory(cachelessDataSourceFactory),
+ cachelessDataSourceFactory)
+ .setManifestParser(new YoutubeDashLiveManifestParser());
+ }
//endregion
diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java
index e204b8372..33a007597 100644
--- a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java
+++ b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java
@@ -201,12 +201,13 @@ public interface PlaybackResolver extends Resolver {
try {
final StreamInfoTag tag = StreamInfoTag.of(info);
- if (!info.getHlsUrl().isEmpty()) {
- return buildLiveMediaSource(dataSource, info.getHlsUrl(), C.CONTENT_TYPE_HLS, tag);
- } else if (!info.getDashMpdUrl().isEmpty()) {
+ if (!info.getDashMpdUrl().isEmpty()) {
return buildLiveMediaSource(
dataSource, info.getDashMpdUrl(), C.CONTENT_TYPE_DASH, tag);
}
+ if (!info.getHlsUrl().isEmpty()) {
+ return buildLiveMediaSource(dataSource, info.getHlsUrl(), C.CONTENT_TYPE_HLS, tag);
+ }
} catch (final Exception e) {
Log.w(TAG, "Error when generating live media source, falling back to standard sources",
e);
@@ -225,7 +226,11 @@ public interface PlaybackResolver extends Resolver {
factory = dataSource.getLiveSsMediaSourceFactory();
break;
case C.CONTENT_TYPE_DASH:
- factory = dataSource.getLiveDashMediaSourceFactory();
+ if (metadata.getServiceId() == ServiceList.YouTube.getServiceId()) {
+ factory = dataSource.getLiveYoutubeDashMediaSourceFactory();
+ } else {
+ factory = dataSource.getLiveDashMediaSourceFactory();
+ }
break;
case C.CONTENT_TYPE_HLS:
factory = dataSource.getLiveHlsMediaSourceFactory();
From 0578e7fde077ed34a84942d14ddc53f7c5866ab7 Mon Sep 17 00:00:00 2001
From: AudricV <74829229+AudricV@users.noreply.github.com>
Date: Sat, 5 Jul 2025 20:48:49 +0200
Subject: [PATCH 3/7] Rename useVideoSource to useVideoAndSubtitles in Player
As both subtitles and video tracks are disabled in this method, the
goal of this rename is to highlight disabling/enabled subtitles.
---
.../main/java/org/schabi/newpipe/player/Player.java | 11 ++++++-----
.../org/schabi/newpipe/player/ui/MainPlayerUi.java | 4 ++--
.../org/schabi/newpipe/player/ui/PopupPlayerUi.java | 4 ++--
3 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java
index 42f6cbf36..f3dfb0a91 100644
--- a/app/src/main/java/org/schabi/newpipe/player/Player.java
+++ b/app/src/main/java/org/schabi/newpipe/player/Player.java
@@ -2195,12 +2195,12 @@ public final class Player implements PlaybackListener, Listener {
}
}
- public void useVideoSource(final boolean videoEnabled) {
+ public void useVideoAndSubtitles(final boolean videoAndSubtitlesEnabled) {
if (playQueue == null || audioPlayerSelected()) {
return;
}
- isAudioOnly = !videoEnabled;
+ isAudioOnly = !videoAndSubtitlesEnabled;
getCurrentStreamInfo().ifPresentOrElse(info -> {
// In case we don't know the source type, fall back to either video-with-audio, or
@@ -2214,10 +2214,11 @@ public final class Player implements PlaybackListener, Listener {
setRecovery();
- // Disable or enable video and subtitles renderers depending of the videoEnabled value
+ // Disable or enable video and subtitles renderers depending of the
+ // videoAndSubtitlesEnabled value
trackSelector.setParameters(trackSelector.buildUponParameters()
- .setTrackTypeDisabled(C.TRACK_TYPE_TEXT, !videoEnabled)
- .setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, !videoEnabled));
+ .setTrackTypeDisabled(C.TRACK_TYPE_TEXT, !videoAndSubtitlesEnabled)
+ .setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, !videoAndSubtitlesEnabled));
}, () -> {
/*
The current metadata may be null sometimes (for e.g. when using an unstable connection
diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java
index bfcc82984..b21a387a9 100644
--- a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java
+++ b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java
@@ -331,7 +331,7 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh
} else if (VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED.equals(intent.getAction())) {
// Restore video source when user returns to the fragment
fragmentIsVisible = true;
- player.useVideoSource(true);
+ player.useVideoAndSubtitles(true);
// When a user returns from background, the system UI will always be shown even if
// controls are invisible: hide it in that case
@@ -370,7 +370,7 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh
if (player.isPlaying() || player.isLoading()) {
switch (getMinimizeOnExitAction(context)) {
case MINIMIZE_ON_EXIT_MODE_BACKGROUND:
- player.useVideoSource(false);
+ player.useVideoAndSubtitles(false);
break;
case MINIMIZE_ON_EXIT_MODE_POPUP:
getParentActivity().ifPresent(activity -> {
diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java
index 24b734fe0..10a982bf0 100644
--- a/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java
+++ b/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java
@@ -219,10 +219,10 @@ public final class PopupPlayerUi extends VideoPlayerUi {
} else if (player.isPlaying() || player.isLoading()) {
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
// Use only audio source when screen turns off while popup player is playing
- player.useVideoSource(false);
+ player.useVideoAndSubtitles(false);
} else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
// Restore video source when screen turns on and user was watching video in popup
- player.useVideoSource(true);
+ player.useVideoAndSubtitles(true);
}
}
}
From 4648cac9c6bd28c75350ea4f28ab257a6ba8b199 Mon Sep 17 00:00:00 2001
From: AudricV <74829229+AudricV@users.noreply.github.com>
Date: Thu, 4 Sep 2025 17:52:48 +0200
Subject: [PATCH 4/7] Allow changing video and text tracks state without stream
info
This allows disabling these track types when stream info has been not
loaded while the ExoPlayer instance is.
It is now possible to do so with the background player, in order to
disable fetching video and text tracks for manifest sources,
especially used for livestreams.
Also set the recovery first before reloading play queue manager in the
useVideoAndSubtitles method of the Player class.
---
.../org/schabi/newpipe/player/Player.java | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java
index f3dfb0a91..ef3a8b0fe 100644
--- a/app/src/main/java/org/schabi/newpipe/player/Player.java
+++ b/app/src/main/java/org/schabi/newpipe/player/Player.java
@@ -2196,7 +2196,7 @@ public final class Player implements PlaybackListener, Listener {
}
public void useVideoAndSubtitles(final boolean videoAndSubtitlesEnabled) {
- if (playQueue == null || audioPlayerSelected()) {
+ if (playQueue == null) {
return;
}
@@ -2208,17 +2208,11 @@ public final class Player implements PlaybackListener, Listener {
final SourceType sourceType = videoResolver.getStreamSourceType()
.orElse(SourceType.VIDEO_WITH_AUDIO_OR_AUDIO_ONLY);
+ setRecovery();
+
if (playQueueManagerReloadingNeeded(sourceType, info, getVideoRendererIndex())) {
reloadPlayQueueManager();
}
-
- setRecovery();
-
- // Disable or enable video and subtitles renderers depending of the
- // videoAndSubtitlesEnabled value
- trackSelector.setParameters(trackSelector.buildUponParameters()
- .setTrackTypeDisabled(C.TRACK_TYPE_TEXT, !videoAndSubtitlesEnabled)
- .setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, !videoAndSubtitlesEnabled));
}, () -> {
/*
The current metadata may be null sometimes (for e.g. when using an unstable connection
@@ -2227,9 +2221,15 @@ public final class Player implements PlaybackListener, Listener {
Reload the play queue manager in this case, which is the behavior when we don't know the
index of the video renderer or playQueueManagerReloadingNeeded returns true
*/
- reloadPlayQueueManager();
setRecovery();
+ reloadPlayQueueManager();
});
+
+ // Disable or enable video and subtitles renderers depending of the
+ // videoAndSubtitlesEnabled value
+ trackSelector.setParameters(trackSelector.buildUponParameters()
+ .setTrackTypeDisabled(C.TRACK_TYPE_TEXT, !videoAndSubtitlesEnabled)
+ .setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, !videoAndSubtitlesEnabled));
}
/**
From 1d8ea0181fc6a447153b0f1ffe4f27006bd1109a Mon Sep 17 00:00:00 2001
From: AudricV <74829229+AudricV@users.noreply.github.com>
Date: Thu, 4 Sep 2025 17:58:31 +0200
Subject: [PATCH 5/7] Disable fetching video and text tracks in background
player
This reduces data usage for manifest sources with demuxed audio and
video, such as livestreams, for non-HLS sources only due to an
ExoPlayer bug.
---
.../org/schabi/newpipe/player/Player.java | 18 ++++++++++++
.../newpipe/player/ui/BackgroundPlayerUi.java | 29 +++++++++++++++++++
.../newpipe/player/ui/MainPlayerUi.java | 4 +++
.../newpipe/player/ui/PopupPlayerUi.java | 8 +++++
4 files changed, 59 insertions(+)
create mode 100644 app/src/main/java/org/schabi/newpipe/player/ui/BackgroundPlayerUi.java
diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java
index ef3a8b0fe..b0858f2e3 100644
--- a/app/src/main/java/org/schabi/newpipe/player/Player.java
+++ b/app/src/main/java/org/schabi/newpipe/player/Player.java
@@ -114,6 +114,7 @@ import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver.SourceType;
+import org.schabi.newpipe.player.ui.BackgroundPlayerUi;
import org.schabi.newpipe.player.ui.MainPlayerUi;
import org.schabi.newpipe.player.ui.PlayerUi;
import org.schabi.newpipe.player.ui.PlayerUiList;
@@ -271,6 +272,7 @@ public final class Player implements PlaybackListener, Listener {
@NonNull
private final HistoryRecordManager recordManager;
+ private boolean screenOn = true;
/*//////////////////////////////////////////////////////////////////////////
// Constructor
@@ -592,14 +594,17 @@ public final class Player implements PlaybackListener, Listener {
switch (playerType) {
case MAIN:
UIs.destroyAll(PopupPlayerUi.class);
+ UIs.destroyAll(BackgroundPlayerUi.class);
UIs.addAndPrepare(new MainPlayerUi(this, binding));
break;
case POPUP:
UIs.destroyAll(MainPlayerUi.class);
+ UIs.destroyAll(BackgroundPlayerUi.class);
UIs.addAndPrepare(new PopupPlayerUi(this, binding));
break;
case AUDIO:
UIs.destroyAll(VideoPlayerUi.class);
+ UIs.addAndPrepare(new BackgroundPlayerUi(this));
break;
}
}
@@ -842,6 +847,12 @@ public final class Player implements PlaybackListener, Listener {
case ACTION_SHUFFLE:
toggleShuffleModeEnabled();
break;
+ case Intent.ACTION_SCREEN_OFF:
+ screenOn = false;
+ break;
+ case Intent.ACTION_SCREEN_ON:
+ screenOn = true;
+ break;
case Intent.ACTION_CONFIGURATION_CHANGED:
if (DEBUG) {
Log.d(TAG, "ACTION_CONFIGURATION_CHANGED received");
@@ -2462,4 +2473,11 @@ public final class Player implements PlaybackListener, Listener {
.orElse(RENDERER_UNAVAILABLE);
}
//endregion
+
+ /**
+ * @return whether the device screen is turned on.
+ */
+ public boolean isScreenOn() {
+ return screenOn;
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/BackgroundPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/BackgroundPlayerUi.java
new file mode 100644
index 000000000..9530033d1
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/player/ui/BackgroundPlayerUi.java
@@ -0,0 +1,29 @@
+package org.schabi.newpipe.player.ui;
+
+import androidx.annotation.NonNull;
+
+import org.schabi.newpipe.player.Player;
+
+/**
+ * This is not a real UI for the background player, it used to disable fetching video and text
+ * tracks with it.
+ *
+ *
+ * This allows reducing data usage for manifest sources with demuxed audio and video,
+ * such as livestreams.
+ *
+ */
+public class BackgroundPlayerUi extends PlayerUi {
+
+ public BackgroundPlayerUi(@NonNull final Player player) {
+ super(player);
+ }
+
+ @Override
+ public void initPlayback() {
+ super.initPlayback();
+
+ // Make sure to disable video and subtitles track types
+ player.useVideoAndSubtitles(false);
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java
index b21a387a9..868881782 100644
--- a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java
+++ b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java
@@ -216,6 +216,10 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh
playQueueAdapter = new PlayQueueAdapter(context,
Objects.requireNonNull(player.getPlayQueue()));
segmentAdapter = new StreamSegmentAdapter(getStreamSegmentListener());
+
+ // Make sure video and text tracks are enabled if the user is in the app, in the case user
+ // switched from background player to main player
+ player.useVideoAndSubtitles(fragmentIsVisible);
}
@Override
diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java
index 10a982bf0..b9c29c008 100644
--- a/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java
+++ b/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java
@@ -152,6 +152,14 @@ public final class PopupPlayerUi extends VideoPlayerUi {
windowManager.addView(closeOverlayBinding.getRoot(), closeOverlayLayoutParams);
}
+ @Override
+ public void initPlayback() {
+ super.initPlayback();
+ // Make sure video and text tracks are enabled if the screen is turned on (which should
+ // always be the case), in the case user switched from background player to popup player
+ player.useVideoAndSubtitles(player.isScreenOn());
+ }
+
@Override
protected void setupElementsVisibility() {
binding.fullScreenButton.setVisibility(View.VISIBLE);
From 216867c597c46fc62ebee371b28545557c2984fd Mon Sep 17 00:00:00 2001
From: Stypox
Date: Wed, 28 Jan 2026 02:20:33 +0100
Subject: [PATCH 6/7] Address review comments
---
app/src/main/java/org/schabi/newpipe/player/Player.java | 6 +++---
.../schabi/newpipe/player/resolver/PlaybackResolver.java | 2 ++
.../org/schabi/newpipe/player/ui/BackgroundPlayerUi.java | 4 ++--
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java
index b0858f2e3..b70441d51 100644
--- a/app/src/main/java/org/schabi/newpipe/player/Player.java
+++ b/app/src/main/java/org/schabi/newpipe/player/Player.java
@@ -2219,7 +2219,7 @@ public final class Player implements PlaybackListener, Listener {
final SourceType sourceType = videoResolver.getStreamSourceType()
.orElse(SourceType.VIDEO_WITH_AUDIO_OR_AUDIO_ONLY);
- setRecovery();
+ setRecovery(); // making sure to save playback position before reloadPlayQueueManager()
if (playQueueManagerReloadingNeeded(sourceType, info, getVideoRendererIndex())) {
reloadPlayQueueManager();
@@ -2227,12 +2227,12 @@ public final class Player implements PlaybackListener, Listener {
}, () -> {
/*
The current metadata may be null sometimes (for e.g. when using an unstable connection
- in livestreams) so we will be not able to execute the block below
+ in livestreams) so we will be not able to execute the block above
Reload the play queue manager in this case, which is the behavior when we don't know the
index of the video renderer or playQueueManagerReloadingNeeded returns true
*/
- setRecovery();
+ setRecovery(); // making sure to save playback position before reloadPlayQueueManager()
reloadPlayQueueManager();
});
diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java
index 33a007597..7dc80a958 100644
--- a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java
+++ b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java
@@ -201,6 +201,8 @@ public interface PlaybackResolver extends Resolver {
try {
final StreamInfoTag tag = StreamInfoTag.of(info);
+ // Prefer DASH over HLS because of an exoPlayer bug that causes the background player to
+ // also fetch the video stream even if it is supposed to just fetch the audio stream.
if (!info.getDashMpdUrl().isEmpty()) {
return buildLiveMediaSource(
dataSource, info.getDashMpdUrl(), C.CONTENT_TYPE_DASH, tag);
diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/BackgroundPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/BackgroundPlayerUi.java
index 9530033d1..4172df35e 100644
--- a/app/src/main/java/org/schabi/newpipe/player/ui/BackgroundPlayerUi.java
+++ b/app/src/main/java/org/schabi/newpipe/player/ui/BackgroundPlayerUi.java
@@ -5,8 +5,8 @@ import androidx.annotation.NonNull;
import org.schabi.newpipe.player.Player;
/**
- * This is not a real UI for the background player, it used to disable fetching video and text
- * tracks with it.
+ * This is not a "graphical" UI for the background player, but it is used to disable fetching video
+ * and text tracks with it.
*
*
* This allows reducing data usage for manifest sources with demuxed audio and video,
From c2723096ab88eae17c773b6e8f94161cc5fd03a3 Mon Sep 17 00:00:00 2001
From: Stypox
Date: Wed, 28 Jan 2026 02:39:56 +0100
Subject: [PATCH 7/7] Avoid rebuilding BackgroundPlayerUi if already in place
---
app/src/main/java/org/schabi/newpipe/player/Player.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java
index b70441d51..e18ead899 100644
--- a/app/src/main/java/org/schabi/newpipe/player/Player.java
+++ b/app/src/main/java/org/schabi/newpipe/player/Player.java
@@ -576,6 +576,7 @@ public final class Player implements PlaybackListener, Listener {
private void initUIsForCurrentPlayerType() {
if ((UIs.get(MainPlayerUi.class).isPresent() && playerType == PlayerType.MAIN)
+ || (UIs.get(BackgroundPlayerUi.class).isPresent() && playerType == PlayerType.AUDIO)
|| (UIs.get(PopupPlayerUi.class).isPresent() && playerType == PlayerType.POPUP)) {
// correct UI already in place
return;
@@ -603,7 +604,7 @@ public final class Player implements PlaybackListener, Listener {
UIs.addAndPrepare(new PopupPlayerUi(this, binding));
break;
case AUDIO:
- UIs.destroyAll(VideoPlayerUi.class);
+ UIs.destroyAll(VideoPlayerUi.class); // destroys both MainPlayerUi and PopupPlayerUi
UIs.addAndPrepare(new BackgroundPlayerUi(this));
break;
}