Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion app/src/main/java/org/schabi/newpipe/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -1298,7 +1298,7 @@ public void onShuffleModeEnabledChanged(final boolean shuffleModeEnabled) {

if (playQueue != null) {
if (shuffleModeEnabled) {
playQueue.shuffle();
playQueue.shuffle(false);
} else {
playQueue.unshuffle();
}
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/org/schabi/newpipe/player/PlayerService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ class PlayerService : MediaBrowserServiceCompat() {
// a (dummy) foreground notification, otherwise we'd incur in
// "Context.startForegroundService() did not then call Service.startForeground()". Then
// we stop the service again.
Log.d(TAG, "onStartCommand() got a useless intent, closing the service");
NotificationUtil.startForegroundWithDummyNotification(this);
Log.d(TAG, "onStartCommand() got a useless intent, closing the service")
NotificationUtil.startForegroundWithDummyNotification(this)
return START_NOT_STICKY
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ internal const val ID_STREAM = "stream"
internal const val ID_PLAYLIST = "playlist"
internal const val ID_CHANNEL = "channel"

internal const val ID_SHUFFLE = "ID_SHUFFLE"

internal fun infoItemTypeToString(type: InfoType): String {
return when (type) {
InfoType.STREAM -> ID_STREAM
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,31 @@ class MediaBrowserImpl(
.build().toString()
}

private fun createShuffleAndPlayMediaItem(
isRemote: Boolean,
playlistId: Long
): MediaBrowserCompat.MediaItem {
val resources = context.resources

val builder = MediaDescriptionCompat.Builder()
.setMediaId(createMediaIdForPlaylistShuffle(isRemote, playlistId))
.setTitle(resources.getString(R.string.shuffle_and_play))

@DrawableRes val iconResId = R.drawable.ic_shuffle_white
builder.setIconUri(
Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(resources.getResourcePackageName(iconResId))
.appendPath(resources.getResourceTypeName(iconResId))
.appendPath(resources.getResourceEntryName(iconResId))
.build()
)

return MediaBrowserCompat.MediaItem(
builder.build(), MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
)
}

private fun createLocalPlaylistStreamMediaItem(
playlistId: Long,
item: PlaylistStreamEntry,
Expand Down Expand Up @@ -301,6 +326,15 @@ class MediaBrowserImpl(
.build().toString()
}

private fun createMediaIdForPlaylistShuffle(
isRemote: Boolean,
playlistId: Long,
): String {
return buildLocalPlaylistItemMediaId(isRemote, playlistId)
.appendPath(ID_SHUFFLE)
.build().toString()
}

private fun createMediaIdForInfoItem(item: InfoItem): String {
return buildInfoItemMediaId(item).build().toString()
}
Expand Down Expand Up @@ -346,7 +380,10 @@ class MediaBrowserImpl(
private fun populateLocalPlaylist(playlistId: Long): Single<List<MediaBrowserCompat.MediaItem>> {
val playlist = LocalPlaylistManager(database).getPlaylistStreams(playlistId).firstOrError()
return playlist.map { items ->
items.mapIndexed { index, item ->
val quickActions = if (items.isEmpty()) emptyList() else listOf(
createShuffleAndPlayMediaItem(false, playlistId)
)
quickActions + items.mapIndexed { index, item ->
createLocalPlaylistStreamMediaItem(playlistId, item, index)
}
}
Expand All @@ -356,9 +393,12 @@ class MediaBrowserImpl(
return RemotePlaylistManager(database).getPlaylist(playlistId).firstOrError()
.flatMap { ExtractorHelper.getPlaylistInfo(it.serviceId, it.url, false) }
.map {
val quickActions = if (it.relatedItems.isEmpty()) emptyList() else listOf(
createShuffleAndPlayMediaItem(true, playlistId)
)
// ignore it.errors, i.e. ignore errors about specific items, since there would
// be no way to show the error properly in Android Auto anyway
it.relatedItems.mapIndexed { index, item ->
quickActions + it.relatedItems.mapIndexed { index, item ->
createRemotePlaylistStreamMediaItem(playlistId, item, index)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,17 @@ class MediaBrowserPlaybackPreparer(
.map { info -> PlaylistPlayQueue(info, index) }
}

private fun extractShufflePlayQueue(isRemote: Boolean, playlistId: Long): Single<PlayQueue> {
return if (isRemote) {
extractRemotePlayQueue(playlistId, 0)
} else {
extractLocalPlayQueue(playlistId, 0)
}.map { playQueue ->
playQueue.shuffle(true)
playQueue
}
}

private fun extractPlayQueueFromMediaId(mediaId: String): Single<PlayQueue> {
try {
val mediaIdUri = mediaId.toUri()
Expand Down Expand Up @@ -184,6 +195,10 @@ class MediaBrowserPlaybackPreparer(
throw parseError(mediaId)
}
val playlistId = path[0].toLong()
if (ID_SHUFFLE == path[1]) {
return extractShufflePlayQueue(playlistType == ID_REMOTE, playlistId)
}

val index = path[1].toInt()
return if (playlistType == ID_LOCAL)
extractLocalPlayQueue(playlistId, index)
Expand Down
23 changes: 16 additions & 7 deletions app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.kt
Original file line number Diff line number Diff line change
Expand Up @@ -399,17 +399,21 @@ abstract class PlayQueue internal constructor(
/**
* Shuffles the current play queue
*
* This method first backs up the existing play queue and item being played. Then a newly
* shuffled play queue will be generated along with currently playing item placed at the
* beginning of the queue. This item will also be added to the history.
* This method first backs up the existing play queue. By default, the currently playing item
* is preserved at the beginning of the queue, with the remaining items shuffled.
* If [shuffleAll] is true, all items in the queue will be shuffled without preserving the
* currently playing item at the head of the queue.
*
* Will emit a [ReorderEvent] if shuffled.
* When the currently playing item is preserved, it will also be added to the history and will
* emit a [ReorderEvent] if the currently playing item position changes.
*
* @param shuffleAll whether to shuffle all items in the queue or preserve the currently
* playing item at the head
* @implNote Does nothing if the queue has a size <= 2 (the currently playing video must stay on
* top, so shuffling a size-2 list does nothing)
*/
@Synchronized
fun shuffle() {
fun shuffle(shuffleAll: Boolean = false) {
// Create a backup if it doesn't already exist
// Note: The backup-list has to be created at all cost (even when size <= 2).
// Otherwise it's not possible to enter shuffle-mode!
Expand All @@ -421,13 +425,18 @@ abstract class PlayQueue internal constructor(
return
}

if (shuffleAll) {
streams.shuffle()
return
}

val originalIndex = this.index
val currentItem = this.item
val currentItem = this.item!!

streams.shuffle()

// Move currentItem to the head of the queue
streams.remove(currentItem!!)
streams.remove(currentItem)
streams.add(0, currentItem)
queueIndex.set(0)

Expand Down
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_shuffle_white.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/white"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M10.59,9.17L5.41,4 4,5.41l5.17,5.17 1.42,-1.41zM14.5,4l2.04,2.04L4,18.59 5.41,20 17.96,7.46 20,9.5L20,4h-5.5zM14.83,13.41l-1.41,1.41 3.13,3.13L14.5,20L20,20v-5.5l-2.04,2.04 -3.13,-3.13z" />
</vector>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@
<string name="app_language_title">App language</string>
<string name="systems_language">System default</string>
<string name="remove_watched">Remove watched</string>
<string name="shuffle_and_play">Shuffle and play</string>
<string name="remove_watched_popup_title">Remove watched videos?</string>
<string name="remove_duplicates">Remove duplicates</string>
<string name="remove_duplicates_title">Remove duplicates?</string>
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ teamnewpipe-nanojson = "e9d656ddb49a412a5a0a5d5ef20ca7ef09549996"
# the corresponding commit hash, since JitPack sometimes deletes artifacts.
# If there’s already a git hash, just add more of it to the end (or remove a letter)
# to cause jitpack to regenerate the artifact.
teamnewpipe-newpipe-extractor = "0023b22095a2d62a60cdfc87f4b5cd85c8b266c3"
teamnewpipe-newpipe-extractor = "0023b22095a2d62a60cdfc87f4b5cd85c8b266c"
webkit = "1.9.0"
work = "2.10.0"

Expand Down