Skip to content
This repository has been archived by the owner on Oct 10, 2024. It is now read-only.

Remove songs from the queue by swiping #1163

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ android {
minSdk = 21
targetSdk = 33
versionCode = 20
versionName = "0.5.4"
versionName = "1.0.1"
}

splits {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ fun AlbumScreen(browseId: String) {
.collect { (currentAlbum, tabIndex) ->
album = currentAlbum

if (albumPage == null && (currentAlbum?.timestamp == null || tabIndex == 1)) {
if (albumPage == null && (currentAlbum?.timestamp == null || currentAlbum.title == null || tabIndex == 1)) {
withContext(Dispatchers.IO) {
Innertube.albumPage(BrowseBody(browseId = browseId))
?.onSuccess { currentAlbumPage ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.fadeIn
Expand All @@ -15,6 +17,9 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
Expand All @@ -25,6 +30,7 @@ import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
Expand All @@ -36,6 +42,7 @@ import androidx.compose.foundation.text.BasicText
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
Expand All @@ -44,10 +51,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
Expand Down Expand Up @@ -82,6 +91,8 @@ import it.vfsfitvnm.vimusic.utils.shuffleQueue
import it.vfsfitvnm.vimusic.utils.smoothScrollToTop
import it.vfsfitvnm.vimusic.utils.windows
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.math.roundToInt

@ExperimentalFoundationApi
@ExperimentalAnimationApi
Expand Down Expand Up @@ -181,6 +192,10 @@ fun Queue(

val musicBarsTransition = updateTransition(targetState = mediaItemIndex, label = "")

val deleteHistory = remember {
mutableStateListOf<String>()
}

Column {
Box(
modifier = Modifier
Expand All @@ -202,7 +217,7 @@ fun Queue(
key = { it.uid.hashCode() }
) { window ->
val isPlayingThisMediaItem = mediaItemIndex == window.firstPeriodIndex

val offsetX = remember { Animatable(Offset(0f, 0f), Offset.VectorConverter) }
SongItem(
song = window.mediaItem,
thumbnailSizePx = thumbnailSizePx,
Expand Down Expand Up @@ -283,6 +298,43 @@ fun Queue(
reorderingState = reorderingState,
index = window.firstPeriodIndex
)
.draggable(
orientation = Orientation.Horizontal,
state = rememberDraggableState(onDelta = { delta ->
if (isPlayingThisMediaItem) return@rememberDraggableState
runBlocking {
offsetX.snapTo(offsetX.value + Offset(delta, 0f))
}
}),
onDragStopped = { _ ->
if (offsetX.value.x >= 200.0f || offsetX.value.x <= -200.0f) {
val currentIndex = window.firstPeriodIndex
val mediaId = window.mediaItem.mediaId

if (deleteHistory.indexOf(mediaId) != -1) return@draggable
deleteHistory.add(mediaId)

var indexToDelete = currentIndex
for (i in 0 until currentIndex) {
if (deleteHistory.indexOf(windows.elementAt(i).mediaItem.mediaId) != -1) {
indexToDelete--
}
}

if (offsetX.value.x < 0) {
offsetX.animateTo(Offset(-1500.0f, offsetX.value.y))
} else {
offsetX.animateTo(Offset(1500.0f, offsetX.value.y))
}

binder.player.removeMediaItem(indexToDelete)
deleteHistory.removeFirst()
} else {
offsetX.animateTo(Offset(0f, offsetX.value.y))
}
}
)
.offset { IntOffset(offsetX.value.x.roundToInt(), 0) }
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ data class BrowseResponse(
@Serializable
data class Contents(
val singleColumnBrowseResultsRenderer: Tabs?,
val twoColumnBrowseResultsRenderer: TwoColResults?,
val sectionListRenderer: SectionListRenderer?,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ data class MusicShelfRenderer(
val bottomEndpoint: NavigationEndpoint?,
val contents: List<Content>?,
val continuations: List<Continuation>?,
val title: Runs?
val title: Runs?,
val thumbnail: ThumbnailRenderer?,
val subtitle: Runs?
) {
@Serializable
data class Content(
Expand All @@ -23,7 +25,7 @@ data class MusicShelfRenderer(
?: emptyList()) to
(musicResponsiveListItemRenderer
?.flexColumns
?.lastOrNull()
?.getOrNull(1)
?.musicResponsiveListItemFlexColumnRenderer
?.text
?.splitBySeparator()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ data class SectionListRenderer(
val musicCarouselShelfRenderer: MusicCarouselShelfRenderer?,
@JsonNames("musicPlaylistShelfRenderer")
val musicShelfRenderer: MusicShelfRenderer?,
val musicResponsiveHeaderRenderer: MusicShelfRenderer?,
val musicPlaylistShelfRenderer: MusicShelfRenderer?,
val gridRenderer: GridRenderer?,
val musicDescriptionShelfRenderer: MusicDescriptionShelfRenderer?,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package it.vfsfitvnm.innertube.models

import kotlinx.serialization.Serializable

@Serializable
data class TwoColResults(
val secondaryContents: SecondaryContents?,
val tabs: List<Tabs.Tab>?
) {
@Serializable
data class SecondaryContents(
val sectionListRenderer: SectionListRenderer?
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ suspend fun <T : Innertube.Item> Innertube.itemsPage(

itemsPageFromMusicShelRendererOrGridRenderer(
musicShelfRenderer = sectionListRendererContent
?.musicShelfRenderer,
?.musicPlaylistShelfRenderer,
gridRenderer = sectionListRendererContent
?.gridRenderer,
fromMusicResponsiveListItemRenderer = fromMusicResponsiveListItemRenderer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,31 @@ import it.vfsfitvnm.innertube.utils.runCatchingNonCancellable
suspend fun Innertube.playlistPage(body: BrowseBody) = runCatchingNonCancellable {
val response = client.post(browse) {
setBody(body)
mask("contents.singleColumnBrowseResultsRenderer.tabs.tabRenderer.content.sectionListRenderer.contents(musicPlaylistShelfRenderer(continuations,contents.$musicResponsiveListItemRendererMask),musicCarouselShelfRenderer.contents.$musicTwoRowItemRendererMask),header.musicDetailHeaderRenderer(title,subtitle,thumbnail),microformat")
mask("contents.twoColumnBrowseResultsRenderer(tabs.tabRenderer.content.sectionListRenderer.contents.musicResponsiveHeaderRenderer(title,subtitle,thumbnail),secondaryContents.sectionListRenderer.contents(musicPlaylistShelfRenderer(continuations,contents.$musicResponsiveListItemRendererMask),musicCarouselShelfRenderer.contents.$musicTwoRowItemRendererMask)),microformat")
}.body<BrowseResponse>()

val musicDetailHeaderRenderer = response
.header
?.musicDetailHeaderRenderer

val sectionListRendererContents = response
.contents
?.singleColumnBrowseResultsRenderer
?.twoColumnBrowseResultsRenderer
?.tabs
?.firstOrNull()
?.tabRenderer
?.content
?.sectionListRenderer
?.contents
?.firstOrNull()
?.musicResponsiveHeaderRenderer

val sectionListRendererContents = response
.contents
?.twoColumnBrowseResultsRenderer
?.secondaryContents
?.sectionListRenderer
?.contents

val musicShelfRenderer = sectionListRendererContents
?.firstOrNull()
?.musicShelfRenderer
?.musicPlaylistShelfRenderer

val musicCarouselShelfRenderer = sectionListRendererContents
?.getOrNull(1)
Expand Down