Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b764ad3
Drop some assumptions on how PlayerService is started and reused
Stypox Feb 15, 2025
5819546
Have PlayerService implement MediaBrowserServiceCompat
Stypox Feb 15, 2025
7d17468
Instantiate media session and connector in PlayerService
Stypox Feb 16, 2025
1e08cc8
Add MediaBrowserCommon with info item's and pages' IDs
Stypox Feb 16, 2025
9bb2c0b
Add getPlaylist(id) to RemotePlaylistManager
Stypox Feb 16, 2025
690b40d
Allow creating PlayQueue from ListInfo and index
Stypox Feb 16, 2025
5eabcb5
Add getThumbnailUrl() to PlaylistLocalItem interface
Stypox Feb 16, 2025
6cedd11
Add StreamHistoryEntry.toStreamInfoItem()
Stypox Feb 16, 2025
3fcac10
Add MediaBrowserPlaybackPreparer
Stypox Feb 16, 2025
4c88a19
Add MediaBrowserImpl
Stypox Feb 16, 2025
064e1d3
Use the media browser implementation in PlayerService
Stypox Feb 16, 2025
ec6612d
Call exoPlayer.prepare() on PlaybackPreparer.onPrepare()
Stypox Feb 16, 2025
dc62d21
Properly stop PlayerService
Stypox Feb 16, 2025
e5458bc
Properly handle item errors during media browser loading
Stypox Feb 16, 2025
1d98518
Fix loading remote playlists in media browser
Stypox Feb 16, 2025
6558794
Try to bind to PlayerService when MainActivity starts
Stypox Feb 18, 2025
126f4b0
Fix crash when closing video detail fragment
Stypox Feb 18, 2025
a7a7dc5
Handle player and player service separately
Stypox Feb 18, 2025
49b7194
Fix style and add comment about null player
Stypox Feb 24, 2025
b378931
Merge pull request #12104 from TeamNewPipe/update-npe
Stypox Mar 21, 2025
196c277
Merge pull request #12044 from TeamNewPipe/android-auto
Stypox Mar 21, 2025
6d6b73e
textview for download date added
malania02 Mar 22, 2025
536b78f
textview for download date added
malania02 Mar 22, 2025
63be322
Show download date
malania02 Mar 22, 2025
912f07a
Missing lines added
malania02 Mar 30, 2025
f39eda0
Fix for overlapping
malania02 Apr 9, 2025
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
8 changes: 8 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>

<activity
Expand Down Expand Up @@ -424,5 +427,10 @@
<meta-data
android:name="com.samsung.android.multidisplay.keep_process_alive"
android:value="true" />
<!-- Android Auto -->
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@mipmap/ic_launcher" />
</application>
</manifest>
7 changes: 6 additions & 1 deletion app/src/main/java/org/schabi/newpipe/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,8 @@ private void openMiniPlayerUponPlayerStarted() {
@Override
public void onReceive(final Context context, final Intent intent) {
if (Objects.equals(intent.getAction(),
VideoDetailFragment.ACTION_PLAYER_STARTED)) {
VideoDetailFragment.ACTION_PLAYER_STARTED)
&& PlayerHolder.getInstance().isPlayerOpen()) {
openMiniPlayerIfMissing();
// At this point the player is added 100%, we can unregister. Other actions
// are useless since the fragment will not be removed after that.
Expand All @@ -874,6 +875,10 @@ public void onReceive(final Context context, final Intent intent) {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED);
registerReceiver(broadcastReceiver, intentFilter);

// If the PlayerHolder is not bound yet, but the service is running, try to bind to it.
// Once the connection is established, the ACTION_PLAYER_STARTED will be sent.
PlayerHolder.getInstance().tryBindIfNeeded(this);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package org.schabi.newpipe.database.history.model
import androidx.room.ColumnInfo
import androidx.room.Embedded
import org.schabi.newpipe.database.stream.model.StreamEntity
import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.util.image.ImageStrategy
import java.time.OffsetDateTime

data class StreamHistoryEntry(
Expand All @@ -27,4 +29,17 @@ data class StreamHistoryEntry(
return this.streamEntity.uid == other.streamEntity.uid && streamId == other.streamId &&
accessDate.isEqual(other.accessDate)
}

fun toStreamInfoItem(): StreamInfoItem =
StreamInfoItem(
streamEntity.serviceId,
streamEntity.url,
streamEntity.title,
streamEntity.streamType,
).apply {
duration = streamEntity.duration
uploaderName = streamEntity.uploader
uploaderUrl = streamEntity.uploaderUrl
thumbnails = ImageStrategy.dbUrlToImageList(streamEntity.thumbnailUrl)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.schabi.newpipe.database.playlist;

import androidx.annotation.Nullable;

import org.schabi.newpipe.database.LocalItem;

public interface PlaylistLocalItem extends LocalItem {
Expand All @@ -10,4 +12,7 @@ public interface PlaylistLocalItem extends LocalItem {
long getUid();

void setDisplayIndex(long displayIndex);

@Nullable
String getThumbnailUrl();
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_STREAM_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL;

import androidx.annotation.Nullable;

public class PlaylistMetadataEntry implements PlaylistLocalItem {
public static final String PLAYLIST_STREAM_COUNT = "streamCount";

Expand Down Expand Up @@ -71,4 +73,10 @@ public long getUid() {
public void setDisplayIndex(final long displayIndex) {
this.displayIndex = displayIndex;
}

@Nullable
@Override
public String getThumbnailUrl() {
return thumbnailUrl;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public interface PlaylistRemoteDAO extends BasicDAO<PlaylistRemoteEntity> {

@Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE + " WHERE "
+ REMOTE_PLAYLIST_ID + " = :playlistId")
Flowable<List<PlaylistRemoteEntity>> getPlaylist(long playlistId);
Flowable<PlaylistRemoteEntity> getPlaylist(long playlistId);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐛 Correctness Issue

Breaking API Change: Return Type Modification.

Changing return type from Flowable<List> to Flowable will break code that expects a list structure.

Current Code (Diff):

- Flowable<PlaylistRemoteEntity> getPlaylist(long playlistId);
+ Flowable<List<PlaylistRemoteEntity>> getPlaylist(long playlistId);
📝 Committable suggestion

‼️ IMPORTANT
Trust, but verify! 🕵️ Please review this suggestion with the care of a code archaeologist - check that it perfectly replaces the highlighted code, preserves all lines, maintains proper indentation, and won't break anything in production. Your future self will thank you! 🚀

Suggested change
Flowable<PlaylistRemoteEntity> getPlaylist(long playlistId);
Flowable<List<PlaylistRemoteEntity>> getPlaylist(long playlistId);


@Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE + " WHERE "
+ REMOTE_PLAYLIST_URL + " = :url AND " + REMOTE_PLAYLIST_SERVICE_ID + " = :serviceId")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.text.TextUtils;

import androidx.annotation.Nullable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
Expand Down Expand Up @@ -134,6 +135,8 @@ public void setName(final String name) {
this.name = name;
}

@Nullable
@Override
public String getThumbnailUrl() {
return thumbnailUrl;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,14 @@ public final class VideoDetailFragment
// Service management
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onServiceConnected(final Player connectedPlayer,
final PlayerService connectedPlayerService,
final boolean playAfterConnect) {
player = connectedPlayer;
public void onServiceConnected(@NonNull final PlayerService connectedPlayerService) {
playerService = connectedPlayerService;
}

@Override
public void onPlayerConnected(@NonNull final Player connectedPlayer,
final boolean playAfterConnect) {
player = connectedPlayer;

// It will do nothing if the player is not in fullscreen mode
hideSystemUiIfNeeded();
Expand Down Expand Up @@ -272,11 +275,18 @@ && isAutoplayEnabled()
updateOverlayPlayQueueButtonVisibility();
}

@Override
public void onPlayerDisconnected() {
player = null;
// the binding could be null at this point, if the app is finishing
if (binding != null) {
restoreDefaultBrightness();
}
}

@Override
public void onServiceDisconnected() {
playerService = null;
player = null;
restoreDefaultBrightness();
}


Expand Down Expand Up @@ -1848,13 +1858,16 @@ public void onPlayerError(final PlaybackException error, final boolean isCatchab

@Override
public void onServiceStopped() {
setOverlayPlayPauseImage(false);
if (currentInfo != null) {
updateOverlayData(currentInfo.getName(),
currentInfo.getUploaderName(),
currentInfo.getThumbnails());
// the binding could be null at this point, if the app is finishing
if (binding != null) {
setOverlayPlayPauseImage(false);
if (currentInfo != null) {
updateOverlayData(currentInfo.getName(),
currentInfo.getUploaderName(),
currentInfo.getThumbnails());
}
updateOverlayPlayQueueButtonVisibility();
}
updateOverlayPlayQueueButtonVisibility();
}

@Override
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/org/schabi/newpipe/ktx/Bundle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,16 @@ import androidx.core.os.BundleCompat
inline fun <reified T : Parcelable> Bundle.parcelableArrayList(key: String?): ArrayList<T>? {
return BundleCompat.getParcelableArrayList(this, key, T::class.java)
}

fun Bundle?.toDebugString(): String {
if (this == null) {
return "null"
}
val string = StringBuilder("Bundle{")
for (key in this.keySet()) {
@Suppress("DEPRECATION") // we want this[key] to return items of any type
string.append(" ").append(key).append(" => ").append(this[key]).append(";")
}
string.append(" }")
return string.toString()
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public Flowable<List<PlaylistRemoteEntity>> getPlaylists() {
return playlistRemoteTable.getPlaylists().subscribeOn(Schedulers.io());
}

public Flowable<PlaylistRemoteEntity> getPlaylist(final long playlistId) {
return playlistRemoteTable.getPlaylist(playlistId).subscribeOn(Schedulers.io());
}

public Flowable<List<PlaylistRemoteEntity>> getPlaylist(final PlaylistInfo info) {
return playlistRemoteTable.getPlaylist(info.getServiceId(), info.getUrl())
.subscribeOn(Schedulers.io());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ protected void onDestroy() {
////////////////////////////////////////////////////////////////////////////

private void bind() {
// Note: this code should not really exist, and PlayerHolder should be used instead, but
// it will be rewritten when NewPlayer will replace the current player.
final Intent bindIntent = new Intent(this, PlayerService.class);
bindIntent.setAction(PlayerService.BIND_PLAYER_HOLDER_ACTION);
final boolean success = bindService(bindIntent, serviceConnection, BIND_AUTO_CREATE);
if (!success) {
unbindService(serviceConnection);
Expand Down Expand Up @@ -221,7 +224,7 @@ public void onServiceConnected(final ComponentName name, final IBinder service)
Log.d(TAG, "Player service is connected");

if (service instanceof PlayerService.LocalBinder) {
player = ((PlayerService.LocalBinder) service).getPlayer();
player = ((PlayerService.LocalBinder) service).getService().getPlayer();
}

if (player == null || player.getPlayQueue() == null || player.exoPlayerIsNull()) {
Expand Down
32 changes: 28 additions & 4 deletions app/src/main/java/org/schabi/newpipe/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.support.v4.media.session.MediaSessionCompat;
import android.util.Log;
import android.view.LayoutInflater;

Expand All @@ -71,6 +72,7 @@
import com.google.android.exoplayer2.Player.PositionInfo;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Tracks;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.text.CueGroup;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
Expand Down Expand Up @@ -269,7 +271,16 @@ public final class Player implements PlaybackListener, Listener {
//////////////////////////////////////////////////////////////////////////*/
//region Constructor

public Player(@NonNull final PlayerService service) {
/**
* @param service the service this player resides in
* @param mediaSession used to build the {@link MediaSessionPlayerUi}, lives in the service and
* could possibly be reused with multiple player instances
* @param sessionConnector used to build the {@link MediaSessionPlayerUi}, lives in the service
* and could possibly be reused with multiple player instances
*/
public Player(@NonNull final PlayerService service,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐛 Correctness Issue

Breaking Constructor Signature Change.

The Player constructor now requires two additional parameters which will break all existing code that instantiates this class.

Proposed Code:

    public Player(@NonNull final PlayerService service,
                   @NonNull final MediaSessionCompat mediaSession,
                   @NonNull final MediaSessionConnector sessionConnector) {

@NonNull final MediaSessionCompat mediaSession,
@NonNull final MediaSessionConnector sessionConnector) {
this.service = service;
context = service;
prefs = PreferenceManager.getDefaultSharedPreferences(context);
Expand Down Expand Up @@ -302,7 +313,7 @@ public Player(@NonNull final PlayerService service) {
// notification ui in the UIs list, since the notification depends on the media session in
// PlayerUi#initPlayer(), and UIs.call() guarantees UI order is preserved.
UIs = new PlayerUiList(
new MediaSessionPlayerUi(this),
new MediaSessionPlayerUi(this, mediaSession, sessionConnector),
new NotificationPlayerUi(this)
);
}
Expand Down Expand Up @@ -646,7 +657,7 @@ public void onPlaybackShutdown() {
Log.d(TAG, "onPlaybackShutdown() called");
}
// destroys the service, which in turn will destroy the player
service.stopService();
service.destroyPlayerAndStopService();
}

public void smoothStopForImmediateReusing() {
Expand Down Expand Up @@ -718,7 +729,7 @@ private void onBroadcastReceived(final Intent intent) {
pause();
break;
case ACTION_CLOSE:
service.stopService();
service.destroyPlayerAndStopService();
break;
case ACTION_PLAY_PAUSE:
playPause();
Expand Down Expand Up @@ -1375,6 +1386,19 @@ public void onRenderedFirstFrame() {
public void onCues(@NonNull final CueGroup cueGroup) {
UIs.call(playerUi -> playerUi.onCues(cueGroup.cues));
}

/**
* To be called when the {@code PlaybackPreparer} set in the {@link MediaSessionConnector}
* receives an {@code onPrepare()} call. This function allows restoring the default behavior
* that would happen if there was no playback preparer set, i.e. to just call
* {@code player.prepare()}. You can find the default behavior in `onPlay()` inside the
* {@link MediaSessionConnector} file.
*/
public void onPrepare() {
if (!exoPlayerIsNull()) {
simpleExoPlayer.prepare();
}
}
//endregion


Expand Down
Loading