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(