diff --git a/app/src/main/java/com/zionhuang/music/App.kt b/app/src/main/java/com/zionhuang/music/App.kt
index 6fcfc0228..fe609d1a4 100644
--- a/app/src/main/java/com/zionhuang/music/App.kt
+++ b/app/src/main/java/com/zionhuang/music/App.kt
@@ -21,6 +21,7 @@ import com.zionhuang.music.constants.ProxyEnabledKey
import com.zionhuang.music.constants.ProxyTypeKey
import com.zionhuang.music.constants.ProxyUrlKey
import com.zionhuang.music.constants.SYSTEM_DEFAULT
+import com.zionhuang.music.constants.UseLoginForBrowse
import com.zionhuang.music.constants.VisitorDataKey
import com.zionhuang.music.extensions.toEnum
import com.zionhuang.music.extensions.toInetSocketAddress
@@ -71,6 +72,10 @@ class App : Application(), ImageLoaderFactory {
}
}
+ if (dataStore[UseLoginForBrowse] == true) {
+ YouTube.useLoginForBrowse = true
+ }
+
GlobalScope.launch {
dataStore.data
.map { it[VisitorDataKey] }
diff --git a/app/src/main/java/com/zionhuang/music/constants/PreferenceKeys.kt b/app/src/main/java/com/zionhuang/music/constants/PreferenceKeys.kt
index 09680e619..254889a26 100644
--- a/app/src/main/java/com/zionhuang/music/constants/PreferenceKeys.kt
+++ b/app/src/main/java/com/zionhuang/music/constants/PreferenceKeys.kt
@@ -50,6 +50,7 @@ val MaxSongCacheSizeKey = intPreferencesKey("maxSongCacheSize")
val PauseListenHistoryKey = booleanPreferencesKey("pauseListenHistory")
val PauseSearchHistoryKey = booleanPreferencesKey("pauseSearchHistory")
+val UseLoginForBrowse = booleanPreferencesKey("useLoginForBrowse")
val DisableScreenshotKey = booleanPreferencesKey("disableScreenshot")
val DiscordTokenKey = stringPreferencesKey("discordToken")
diff --git a/app/src/main/java/com/zionhuang/music/ui/screens/settings/PrivacySettings.kt b/app/src/main/java/com/zionhuang/music/ui/screens/settings/PrivacySettings.kt
index c2b025574..6e1a485b9 100644
--- a/app/src/main/java/com/zionhuang/music/ui/screens/settings/PrivacySettings.kt
+++ b/app/src/main/java/com/zionhuang/music/ui/screens/settings/PrivacySettings.kt
@@ -25,12 +25,14 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
+import com.zionhuang.innertube.YouTube
import com.zionhuang.music.LocalDatabase
import com.zionhuang.music.LocalPlayerAwareWindowInsets
import com.zionhuang.music.R
import com.zionhuang.music.constants.DisableScreenshotKey
import com.zionhuang.music.constants.PauseListenHistoryKey
import com.zionhuang.music.constants.PauseSearchHistoryKey
+import com.zionhuang.music.constants.UseLoginForBrowse
import com.zionhuang.music.ui.component.DefaultDialog
import com.zionhuang.music.ui.component.IconButton
import com.zionhuang.music.ui.component.PreferenceEntry
@@ -48,6 +50,7 @@ fun PrivacySettings(
val database = LocalDatabase.current
val (pauseListenHistory, onPauseListenHistoryChange) = rememberPreference(key = PauseListenHistoryKey, defaultValue = false)
val (pauseSearchHistory, onPauseSearchHistoryChange) = rememberPreference(key = PauseSearchHistoryKey, defaultValue = false)
+ val (useLoginForBrowse, onUseLoginForBrowseChange) = rememberPreference(key = UseLoginForBrowse, defaultValue = false)
val (disableScreenshot, onDisableScreenshotChange) = rememberPreference(key = DisableScreenshotKey, defaultValue = false)
var showClearListenHistoryDialog by remember { mutableStateOf(false) }
@@ -155,6 +158,21 @@ fun PrivacySettings(
onClick = { showClearSearchHistoryDialog = true }
)
+ PreferenceGroupTitle(
+ title = stringResource(R.string.account)
+ )
+
+ SwitchPreference(
+ title = { Text(stringResource(R.string.use_login_for_browse)) },
+ description = stringResource(R.string.use_login_for_browse_desc),
+ icon = { Icon(painterResource(R.drawable.person), null) },
+ checked = useLoginForBrowse,
+ onCheckedChange = {
+ YouTube.useLoginForBrowse = it
+ onUseLoginForBrowseChange(it)
+ }
+ )
+
PreferenceGroupTitle(
title = stringResource(R.string.misc)
)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 187474d75..fe88c7127 100755
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -299,6 +299,8 @@
Pause search history
Clear search history
Are you sure you want to clear all search history?
+ Use login for browsing content
+ This can influence what content you see and for example shows premium-only albums if you are logged in with a Premium account
Disable screenshot
When this option is on, screenshots and the app\'s view in Recents are disabled.
Enable LrcLib lyrics provider
diff --git a/innertube/src/main/java/com/zionhuang/innertube/InnerTube.kt b/innertube/src/main/java/com/zionhuang/innertube/InnerTube.kt
index b41ffba18..ae0775f1d 100644
--- a/innertube/src/main/java/com/zionhuang/innertube/InnerTube.kt
+++ b/innertube/src/main/java/com/zionhuang/innertube/InnerTube.kt
@@ -47,6 +47,8 @@ class InnerTube {
httpClient = createClient()
}
+ var useLoginForBrowse: Boolean = false
+
@OptIn(ExperimentalSerializationApi::class)
private fun createClient() = HttpClient(OkHttp) {
expectSuccess = true
@@ -107,7 +109,7 @@ class InnerTube {
params: String? = null,
continuation: String? = null,
) = httpClient.post("search") {
- ytClient(client)
+ ytClient(client, setLogin = useLoginForBrowse)
setBody(
SearchBody(
context = client.toContext(locale, visitorData),
@@ -154,7 +156,7 @@ class InnerTube {
continuation: String? = null,
setLogin: Boolean = false,
) = httpClient.post("browse") {
- ytClient(client, setLogin)
+ ytClient(client, setLogin = setLogin || useLoginForBrowse)
setBody(
BrowseBody(
context = client.toContext(locale, visitorData),
diff --git a/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt b/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt
index 4b386cfe0..797c3a940 100644
--- a/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt
+++ b/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt
@@ -82,6 +82,11 @@ object YouTube {
set(value) {
innerTube.proxy = value
}
+ var useLoginForBrowse: Boolean
+ get() = innerTube.useLoginForBrowse
+ set(value) {
+ innerTube.useLoginForBrowse = value
+ }
suspend fun searchSuggestions(query: String): Result = runCatching {
val response = innerTube.getSearchSuggestions(WEB_REMIX, query).body()
@@ -194,7 +199,6 @@ object YouTube {
response = innerTube.browse(
client = WEB_REMIX,
continuation = continuation,
- setLogin = true
).body()
songs += response.continuationContents?.musicPlaylistShelfContinuation?.contents?.mapNotNull {
AlbumPage.fromMusicResponsiveListItemRenderer(it.musicResponsiveListItemRenderer)
diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/Endpoint.kt b/innertube/src/main/java/com/zionhuang/innertube/models/Endpoint.kt
index a5ec77638..ff2ed8d63 100644
--- a/innertube/src/main/java/com/zionhuang/innertube/models/Endpoint.kt
+++ b/innertube/src/main/java/com/zionhuang/innertube/models/Endpoint.kt
@@ -2,6 +2,7 @@ package com.zionhuang.innertube.models
import com.zionhuang.innertube.models.BrowseEndpoint.BrowseEndpointContextSupportedConfigs.BrowseEndpointContextMusicConfig.Companion.MUSIC_PAGE_TYPE_ALBUM
import com.zionhuang.innertube.models.BrowseEndpoint.BrowseEndpointContextSupportedConfigs.BrowseEndpointContextMusicConfig.Companion.MUSIC_PAGE_TYPE_ARTIST
+import com.zionhuang.innertube.models.BrowseEndpoint.BrowseEndpointContextSupportedConfigs.BrowseEndpointContextMusicConfig.Companion.MUSIC_PAGE_TYPE_AUDIOBOOK
import com.zionhuang.innertube.models.BrowseEndpoint.BrowseEndpointContextSupportedConfigs.BrowseEndpointContextMusicConfig.Companion.MUSIC_PAGE_TYPE_PLAYLIST
import kotlinx.serialization.Serializable
@@ -43,7 +44,8 @@ data class BrowseEndpoint(
val isArtistEndpoint: Boolean
get() = browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType == MUSIC_PAGE_TYPE_ARTIST
val isAlbumEndpoint: Boolean
- get() = browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType == MUSIC_PAGE_TYPE_ALBUM
+ get() = browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType == MUSIC_PAGE_TYPE_ALBUM ||
+ browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType == MUSIC_PAGE_TYPE_AUDIOBOOK
val isPlaylistEndpoint: Boolean
get() = browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType == MUSIC_PAGE_TYPE_PLAYLIST
diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/NavigationEndpoint.kt b/innertube/src/main/java/com/zionhuang/innertube/models/NavigationEndpoint.kt
index 83e4f3351..9229f3e5e 100644
--- a/innertube/src/main/java/com/zionhuang/innertube/models/NavigationEndpoint.kt
+++ b/innertube/src/main/java/com/zionhuang/innertube/models/NavigationEndpoint.kt
@@ -18,4 +18,8 @@ data class NavigationEndpoint(
?: searchEndpoint
?: queueAddEndpoint
?: shareEntityEndpoint
+
+ val anyWatchEndpoint: WatchEndpoint?
+ get() = watchEndpoint
+ ?: watchPlaylistEndpoint
}
diff --git a/innertube/src/main/java/com/zionhuang/innertube/pages/ArtistItemsPage.kt b/innertube/src/main/java/com/zionhuang/innertube/pages/ArtistItemsPage.kt
index 61cc4f4ed..109ec5d04 100644
--- a/innertube/src/main/java/com/zionhuang/innertube/pages/ArtistItemsPage.kt
+++ b/innertube/src/main/java/com/zionhuang/innertube/pages/ArtistItemsPage.kt
@@ -54,7 +54,7 @@ data class ArtistItemsPage(
browseId = renderer.navigationEndpoint.browseEndpoint?.browseId ?: return null,
playlistId = renderer.thumbnailOverlay?.musicItemThumbnailOverlayRenderer
?.content?.musicPlayButtonRenderer?.playNavigationEndpoint
- ?.watchPlaylistEndpoint?.playlistId ?: return null,
+ ?.anyWatchEndpoint?.playlistId ?: return null,
title = renderer.title.runs?.firstOrNull()?.text ?: return null,
artists = null,
year = renderer.subtitle?.runs?.lastOrNull()?.text?.toIntOrNull(),
diff --git a/innertube/src/main/java/com/zionhuang/innertube/pages/ArtistPage.kt b/innertube/src/main/java/com/zionhuang/innertube/pages/ArtistPage.kt
index 8e5fffbaa..0b65e2686 100644
--- a/innertube/src/main/java/com/zionhuang/innertube/pages/ArtistPage.kt
+++ b/innertube/src/main/java/com/zionhuang/innertube/pages/ArtistPage.kt
@@ -123,7 +123,7 @@ data class ArtistPage(
browseId = renderer.navigationEndpoint.browseEndpoint?.browseId ?: return null,
playlistId = renderer.thumbnailOverlay?.musicItemThumbnailOverlayRenderer?.content
?.musicPlayButtonRenderer?.playNavigationEndpoint
- ?.watchPlaylistEndpoint?.playlistId ?: return null,
+ ?.anyWatchEndpoint?.playlistId ?: return null,
title = renderer.title.runs?.firstOrNull()?.text ?: return null,
artists = null,
year = renderer.subtitle?.runs?.lastOrNull()?.text?.toIntOrNull(),
diff --git a/innertube/src/main/java/com/zionhuang/innertube/pages/SearchPage.kt b/innertube/src/main/java/com/zionhuang/innertube/pages/SearchPage.kt
index 478a227ea..dc9dbafef 100644
--- a/innertube/src/main/java/com/zionhuang/innertube/pages/SearchPage.kt
+++ b/innertube/src/main/java/com/zionhuang/innertube/pages/SearchPage.kt
@@ -64,7 +64,7 @@ object SearchPage {
renderer.isAlbum -> {
AlbumItem(
browseId = renderer.navigationEndpoint?.browseEndpoint?.browseId ?: return null,
- playlistId = renderer.overlay?.musicItemThumbnailOverlayRenderer?.content?.musicPlayButtonRenderer?.playNavigationEndpoint?.watchPlaylistEndpoint?.playlistId ?: return null,
+ playlistId = renderer.overlay?.musicItemThumbnailOverlayRenderer?.content?.musicPlayButtonRenderer?.playNavigationEndpoint?.anyWatchEndpoint?.playlistId ?: return null,
title = renderer.flexColumns.firstOrNull()
?.musicResponsiveListItemFlexColumnRenderer?.text?.runs
?.firstOrNull()?.text ?: return null,
diff --git a/innertube/src/main/java/com/zionhuang/innertube/pages/SearchSummaryPage.kt b/innertube/src/main/java/com/zionhuang/innertube/pages/SearchSummaryPage.kt
index 53f9c7522..69386fee5 100644
--- a/innertube/src/main/java/com/zionhuang/innertube/pages/SearchSummaryPage.kt
+++ b/innertube/src/main/java/com/zionhuang/innertube/pages/SearchSummaryPage.kt
@@ -83,7 +83,7 @@ data class SearchSummaryPage(
renderer.onTap.browseEndpoint?.isAlbumEndpoint == true -> {
AlbumItem(
browseId = renderer.onTap.browseEndpoint.browseId,
- playlistId = renderer.buttons.firstOrNull()?.buttonRenderer?.command?.watchPlaylistEndpoint?.playlistId ?: return null,
+ playlistId = renderer.buttons.firstOrNull()?.buttonRenderer?.command?.anyWatchEndpoint?.playlistId ?: return null,
title = renderer.title.runs?.firstOrNull()?.text ?: return null,
artists = subtitle?.getOrNull(1)?.oddElements()?.map {
Artist(