├── .gitattributes
├── .github
└── workflows
│ └── android.yml
├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── CMakeLists.txt
├── agconnect-services.json
├── build.gradle.kts
├── foss
│ └── release
│ │ └── output-metadata.json
├── full
│ └── release
│ │ ├── app-full-release.aab
│ │ └── output-metadata.json
├── google-services.json
├── proguard-rules.pro
├── schemas
│ └── com.zionhuang.music.db.InternalDatabase
│ │ ├── 1.json
│ │ ├── 10.json
│ │ ├── 11.json
│ │ ├── 12.json
│ │ ├── 2.json
│ │ ├── 3.json
│ │ ├── 4.json
│ │ ├── 5.json
│ │ ├── 6.json
│ │ ├── 7.json
│ │ ├── 8.json
│ │ └── 9.json
└── src
│ ├── debug
│ └── res
│ │ ├── values
│ │ └── app_name.xml
│ │ └── xml-v25
│ │ └── shortcuts.xml
│ ├── foss
│ └── java
│ │ └── com
│ │ └── zionhuang
│ │ └── music
│ │ └── utils
│ │ ├── TranslationHelper.kt
│ │ └── Utils.kt
│ ├── full
│ └── java
│ │ └── com
│ │ └── zionhuang
│ │ └── music
│ │ └── utils
│ │ ├── TranslationHelper.kt
│ │ └── Utils.kt
│ └── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-playstore.png
│ ├── java
│ └── com
│ │ └── zionhuang
│ │ └── music
│ │ ├── App.kt
│ │ ├── MainActivity.kt
│ │ ├── SplashActivity.kt
│ │ ├── constants
│ │ ├── Constants.kt
│ │ ├── Dimensions.kt
│ │ ├── MediaSessionConstants.kt
│ │ ├── PreferenceKeys.kt
│ │ └── StatPeriod.kt
│ │ ├── db
│ │ ├── Converters.kt
│ │ ├── DatabaseDao.kt
│ │ ├── MusicDatabase.kt
│ │ └── entities
│ │ │ ├── Album.kt
│ │ │ ├── AlbumArtistMap.kt
│ │ │ ├── AlbumEntity.kt
│ │ │ ├── AlbumWithSongs.kt
│ │ │ ├── Artist.kt
│ │ │ ├── ArtistEntity.kt
│ │ │ ├── Event.kt
│ │ │ ├── EventWithSong.kt
│ │ │ ├── FormatEntity.kt
│ │ │ ├── LocalItem.kt
│ │ │ ├── LyricsEntity.kt
│ │ │ ├── Playlist.kt
│ │ │ ├── PlaylistEntity.kt
│ │ │ ├── PlaylistSong.kt
│ │ │ ├── PlaylistSongMap.kt
│ │ │ ├── PlaylistSongMapPreview.kt
│ │ │ ├── RelatedSongMap.kt
│ │ │ ├── SearchHistory.kt
│ │ │ ├── Song.kt
│ │ │ ├── SongAlbumMap.kt
│ │ │ ├── SongArtistMap.kt
│ │ │ ├── SongEntity.kt
│ │ │ ├── SortedSongAlbumMap.kt
│ │ │ └── SortedSongArtistMap.kt
│ │ ├── di
│ │ └── AppModule.kt
│ │ ├── extensions
│ │ ├── CoroutineExt.kt
│ │ ├── FileExt.kt
│ │ ├── ListExt.kt
│ │ ├── MediaItemExt.kt
│ │ ├── PlayerExt.kt
│ │ ├── StringExt.kt
│ │ └── UtilExt.kt
│ │ ├── lyrics
│ │ ├── KuGouLyricsProvider.kt
│ │ ├── LyricsEntry.kt
│ │ ├── LyricsHelper.kt
│ │ ├── LyricsProvider.kt
│ │ ├── LyricsUtils.kt
│ │ ├── YouTubeLyricsProvider.kt
│ │ └── YouTubeSubtitleLyricsProvider.kt
│ │ ├── models
│ │ ├── ItemsPage.kt
│ │ ├── MediaMetadata.kt
│ │ └── PersistQueue.kt
│ │ ├── playback
│ │ ├── DownloadUtil.kt
│ │ ├── ExoDownloadService.kt
│ │ ├── MediaLibrarySessionCallback.kt
│ │ ├── MusicService.kt
│ │ ├── PlayerConnection.kt
│ │ ├── SleepTimer.kt
│ │ └── queues
│ │ │ ├── EmptyQueue.kt
│ │ │ ├── ListQueue.kt
│ │ │ ├── Queue.kt
│ │ │ ├── YouTubeAlbumRadio.kt
│ │ │ └── YouTubeQueue.kt
│ │ ├── ui
│ │ ├── component
│ │ │ ├── AutoResizeText.kt
│ │ │ ├── BigSeekBar.kt
│ │ │ ├── BottomSheet.kt
│ │ │ ├── BottomSheetMenu.kt
│ │ │ ├── ChipsRow.kt
│ │ │ ├── Dialog.kt
│ │ │ ├── EmptyPlaceholder.kt
│ │ │ ├── GridMenu.kt
│ │ │ ├── HideOnScrollFAB.kt
│ │ │ ├── IconButton.kt
│ │ │ ├── Items.kt
│ │ │ ├── Lyrics.kt
│ │ │ ├── NavigationTile.kt
│ │ │ ├── NavigationTitle.kt
│ │ │ ├── PlayingIndicator.kt
│ │ │ ├── Preference.kt
│ │ │ ├── SearchBar.kt
│ │ │ ├── SortHeader.kt
│ │ │ └── shimmer
│ │ │ │ ├── ButtonPlaceholder.kt
│ │ │ │ ├── GridItemPlaceholder.kt
│ │ │ │ ├── ListItemPlaceholder.kt
│ │ │ │ ├── ShimmerHost.kt
│ │ │ │ └── TextPlaceholder.kt
│ │ ├── menu
│ │ │ ├── AddToPlaylistDialog.kt
│ │ │ ├── AlbumMenu.kt
│ │ │ ├── ArtistMenu.kt
│ │ │ ├── LyricsMenu.kt
│ │ │ ├── PlayerMenu.kt
│ │ │ ├── PlaylistMenu.kt
│ │ │ ├── SongMenu.kt
│ │ │ ├── YouTubeAlbumMenu.kt
│ │ │ ├── YouTubeArtistMenu.kt
│ │ │ ├── YouTubePlaylistMenu.kt
│ │ │ └── YouTubeSongMenu.kt
│ │ ├── player
│ │ │ ├── MiniPlayer.kt
│ │ │ ├── PlaybackError.kt
│ │ │ ├── Player.kt
│ │ │ ├── Queue.kt
│ │ │ └── Thumbnail.kt
│ │ ├── screens
│ │ │ ├── AccountScreen.kt
│ │ │ ├── AlbumScreen.kt
│ │ │ ├── HistoryScreen.kt
│ │ │ ├── HomeScreen.kt
│ │ │ ├── LoginScreen.kt
│ │ │ ├── MoodAndGenresScreen.kt
│ │ │ ├── NewReleaseScreen.kt
│ │ │ ├── Screens.kt
│ │ │ ├── StatsScreen.kt
│ │ │ ├── YouTubeBrowseScreen.kt
│ │ │ ├── artist
│ │ │ │ ├── ArtistItemsScreen.kt
│ │ │ │ ├── ArtistScreen.kt
│ │ │ │ └── ArtistSongsScreen.kt
│ │ │ ├── library
│ │ │ │ ├── LibraryAlbumsScreen.kt
│ │ │ │ ├── LibraryArtistsScreen.kt
│ │ │ │ ├── LibraryPlaylistsScreen.kt
│ │ │ │ └── LibrarySongsScreen.kt
│ │ │ ├── playlist
│ │ │ │ ├── LocalPlaylistScreen.kt
│ │ │ │ └── OnlinePlaylistScreen.kt
│ │ │ ├── search
│ │ │ │ ├── LocalSearchScreen.kt
│ │ │ │ ├── OnlineSearchResult.kt
│ │ │ │ └── OnlineSearchScreen.kt
│ │ │ └── settings
│ │ │ │ ├── AboutScreen.kt
│ │ │ │ ├── AppearanceSettings.kt
│ │ │ │ ├── BackupAndRestore.kt
│ │ │ │ ├── ContentSettings.kt
│ │ │ │ ├── PlayerSettings.kt
│ │ │ │ ├── PrivacySettings.kt
│ │ │ │ ├── SettingsScreen.kt
│ │ │ │ └── StorageSettings.kt
│ │ ├── theme
│ │ │ └── Theme.kt
│ │ └── utils
│ │ │ ├── AppBar.kt
│ │ │ ├── FadingEdge.kt
│ │ │ ├── HorizontalPager.kt
│ │ │ ├── LazyGridSnapLayoutInfoProvider.kt
│ │ │ ├── NavControllerUtils.kt
│ │ │ ├── ScrollUtils.kt
│ │ │ ├── ShapeUtils.kt
│ │ │ ├── StringUtils.kt
│ │ │ └── YouTubeUtils.kt
│ │ ├── utils
│ │ ├── CoilBitmapLoader.kt
│ │ ├── ComposeDebugUtils.kt
│ │ ├── DataStore.kt
│ │ └── StringUtils.kt
│ │ └── viewmodels
│ │ ├── AccountViewModel.kt
│ │ ├── AlbumViewModel.kt
│ │ ├── ArtistItemsViewModel.kt
│ │ ├── ArtistViewModel.kt
│ │ ├── BackupRestoreViewModel.kt
│ │ ├── HistoryViewModel.kt
│ │ ├── HomeViewModel.kt
│ │ ├── LibraryViewModels.kt
│ │ ├── LocalPlaylistViewModel.kt
│ │ ├── LocalSearchViewModel.kt
│ │ ├── LyricsMenuViewModel.kt
│ │ ├── MoodAndGenresViewModel.kt
│ │ ├── NewReleaseViewModel.kt
│ │ ├── OnlinePlaylistViewModel.kt
│ │ ├── OnlineSearchSuggestionViewModel.kt
│ │ ├── OnlineSearchViewModel.kt
│ │ ├── StatsViewModel.kt
│ │ └── YouTubeBrowseViewModel.kt
│ └── res
│ ├── drawable-hdpi
│ ├── ic_stat_onesignal_default.png
│ └── splash_image.png
│ ├── drawable-mdpi
│ ├── ic_stat_onesignal_default.png
│ └── splash_image.png
│ ├── drawable-xhdpi
│ ├── ic_stat_onesignal_default.png
│ └── splash_image.png
│ ├── drawable-xxhdpi
│ ├── ic_stat_onesignal_default.png
│ └── splash_image.png
│ ├── drawable-xxxhdpi
│ ├── ic_stat_onesignal_default.png
│ └── splash_image.png
│ ├── drawable
│ ├── add.xml
│ ├── album.xml
│ ├── app_logo.png
│ ├── arrow_back.xml
│ ├── arrow_downward.xml
│ ├── arrow_forward.xml
│ ├── arrow_top_left.xml
│ ├── arrow_upward.xml
│ ├── artist.xml
│ ├── backup.xml
│ ├── bedtime.xml
│ ├── bookmark.xml
│ ├── bookmark_filled.xml
│ ├── buymeacoffee.xml
│ ├── cached.xml
│ ├── casino.xml
│ ├── clear_all.xml
│ ├── close.xml
│ ├── contrast.xml
│ ├── dark_mode.xml
│ ├── delete.xml
│ ├── discover_tune.xml
│ ├── download.xml
│ ├── drag_handle.xml
│ ├── edit.xml
│ ├── equalizer.xml
│ ├── error.xml
│ ├── expand_less.xml
│ ├── expand_more.xml
│ ├── explicit.xml
│ ├── favorite.xml
│ ├── favorite_border.xml
│ ├── github.xml
│ ├── graphic_eq.xml
│ ├── grid_view.xml
│ ├── history.xml
│ ├── home.xml
│ ├── ic_launcher_background.xml
│ ├── ic_launcher_foreground.xml
│ ├── ic_launcher_round.png
│ ├── info.xml
│ ├── input.xml
│ ├── language.xml
│ ├── launcher_foreground.xml
│ ├── launcher_monochrome.xml
│ ├── liberapay.xml
│ ├── library_add.xml
│ ├── library_add_check.xml
│ ├── library_music.xml
│ ├── list.xml
│ ├── location_on.xml
│ ├── lock.xml
│ ├── lock_open.xml
│ ├── lyrics.xml
│ ├── manage_search.xml
│ ├── mood.xml
│ ├── more_horiz.xml
│ ├── more_vert.xml
│ ├── music_note.xml
│ ├── navigate_next.xml
│ ├── offline.xml
│ ├── palette.xml
│ ├── pause.xml
│ ├── person.xml
│ ├── play.xml
│ ├── playlist_add.xml
│ ├── playlist_play.xml
│ ├── queue_music.xml
│ ├── radio.xml
│ ├── radio_button_checked.xml
│ ├── radio_button_unchecked.xml
│ ├── remove.xml
│ ├── repeat.xml
│ ├── repeat_on.xml
│ ├── repeat_one.xml
│ ├── repeat_one_on.xml
│ ├── replay.xml
│ ├── restore.xml
│ ├── search.xml
│ ├── security.xml
│ ├── settings.xml
│ ├── share.xml
│ ├── shortcut_albums.xml
│ ├── shortcut_playlists.xml
│ ├── shortcut_search.xml
│ ├── shortcut_songs.xml
│ ├── shuffle.xml
│ ├── shuffle_on.xml
│ ├── skip_next.xml
│ ├── skip_previous.xml
│ ├── slow_motion_video.xml
│ ├── small_icon.xml
│ ├── splash_image.png
│ ├── storage.xml
│ ├── sync.xml
│ ├── tab.xml
│ ├── translate.xml
│ ├── trending_up.xml
│ ├── tune.xml
│ ├── update.xml
│ └── volume_up.xml
│ ├── font
│ ├── amsterdam.ttf
│ ├── arvo.ttf
│ ├── bod.TTF
│ ├── edutas.ttf
│ ├── kenia.ttf
│ ├── oswald.ttf
│ ├── raleway1.ttf
│ ├── raleway2.ttf
│ ├── roboto1.ttf
│ ├── roboto2.ttf
│ └── teko.ttf
│ ├── layout
│ └── activity_splash.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── values-DE
│ └── strings.xml
│ ├── values-be
│ └── strings.xml
│ ├── values-bn-rIN
│ └── strings.xml
│ ├── values-cs
│ └── strings.xml
│ ├── values-es
│ └── strings.xml
│ ├── values-fa-rIR
│ └── strings.xml
│ ├── values-fi-rFI
│ └── strings.xml
│ ├── values-fr-rFR
│ └── strings.xml
│ ├── values-hu
│ └── strings.xml
│ ├── values-id
│ └── strings.xml
│ ├── values-it
│ └── strings.xml
│ ├── values-ja-rJP
│ └── strings.xml
│ ├── values-ko-rKR
│ └── strings.xml
│ ├── values-ml-rIN
│ └── strings.xml
│ ├── values-nl
│ └── strings.xml
│ ├── values-or-rIN
│ └── strings.xml
│ ├── values-pa
│ └── strings.xml
│ ├── values-pl
│ └── strings.xml
│ ├── values-pt-rBR
│ └── strings.xml
│ ├── values-ru-rRU
│ └── strings.xml
│ ├── values-sv-rSE
│ └── strings.xml
│ ├── values-tr
│ └── strings.xml
│ ├── values-uk-rUA
│ └── strings.xml
│ ├── values-vi
│ └── strings.xml
│ ├── values-zh-rCN
│ └── strings.xml
│ ├── values-zh-rTW
│ └── strings.xml
│ ├── values
│ ├── app_name.xml
│ ├── ic_launcher_background.xml
│ ├── strings.xml
│ ├── styles.xml
│ └── values.xml
│ ├── xml-v25
│ └── shortcuts.xml
│ └── xml
│ ├── automotive_app_desc.xml
│ └── provider_paths.xml
├── assets
├── buymeacoffee.png
└── liberapay.png
├── build.gradle.kts
├── crowdin.yml
├── fastlane
└── metadata
│ └── android
│ ├── app_banner_youmusic.jpg
│ ├── en-US
│ ├── changelogs
│ │ ├── 10.txt
│ │ ├── 11.txt
│ │ ├── 12.txt
│ │ ├── 13.txt
│ │ ├── 14.txt
│ │ ├── 15.txt
│ │ ├── 16.txt
│ │ ├── 17.txt
│ │ ├── 18.txt
│ │ ├── 19.txt
│ │ ├── 5.txt
│ │ ├── 6.txt
│ │ ├── 7.txt
│ │ ├── 8.txt
│ │ └── 9.txt
│ ├── full_description.txt
│ ├── images
│ │ ├── icon.png
│ │ └── phoneScreenshots
│ │ │ ├── en1.png
│ │ │ ├── en2.png
│ │ │ ├── en3.png
│ │ │ ├── en4.png
│ │ │ ├── en5.png
│ │ │ ├── en6.png
│ │ │ ├── en7.png
│ │ │ └── en8.png
│ ├── short_description.txt
│ └── title.txt
│ ├── es
│ ├── full_description.txt
│ └── short_description.txt
│ ├── it
│ ├── full_description.txt
│ ├── short_description.txt
│ └── title.txt
│ ├── ja
│ ├── full_description.txt
│ └── short_description.txt
│ ├── ko
│ ├── full_description.txt
│ └── short_description.txt
│ ├── pa
│ ├── full_description.txt
│ └── short_description.txt
│ ├── ru-RU
│ ├── full_description.txt
│ ├── short_description.txt
│ └── title.txt
│ ├── tr
│ ├── full_description.txt
│ ├── images
│ │ ├── icon.png
│ │ └── phoneScreenshots
│ │ │ ├── tr1.png
│ │ │ ├── tr2.png
│ │ │ ├── tr3.png
│ │ │ ├── tr4.png
│ │ │ ├── tr5.png
│ │ │ ├── tr6.png
│ │ │ ├── tr7.png
│ │ │ └── tr8.png
│ └── short_description.txt
│ └── uk-UA
│ ├── full_description.txt
│ ├── short_description.txt
│ └── title.txt
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── innertube
├── .gitignore
├── build.gradle.kts
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── zionhuang
│ │ └── innertube
│ │ ├── InnerTube.kt
│ │ ├── YouTube.kt
│ │ ├── encoder
│ │ └── BrotliEncoder.kt
│ │ ├── models
│ │ ├── AccountInfo.kt
│ │ ├── AutomixPreviewVideoRenderer.kt
│ │ ├── Badges.kt
│ │ ├── Button.kt
│ │ ├── Context.kt
│ │ ├── Continuation.kt
│ │ ├── Endpoint.kt
│ │ ├── GridRenderer.kt
│ │ ├── Icon.kt
│ │ ├── Menu.kt
│ │ ├── MusicCardShelfRenderer.kt
│ │ ├── MusicCarouselShelfRenderer.kt
│ │ ├── MusicDescriptionShelfRenderer.kt
│ │ ├── MusicNavigationButtonRenderer.kt
│ │ ├── MusicPlaylistShelfRenderer.kt
│ │ ├── MusicQueueRenderer.kt
│ │ ├── MusicResponsiveListItemRenderer.kt
│ │ ├── MusicShelfRenderer.kt
│ │ ├── MusicTwoRowItemRenderer.kt
│ │ ├── NavigationEndpoint.kt
│ │ ├── PlaylistPanelRenderer.kt
│ │ ├── PlaylistPanelVideoRenderer.kt
│ │ ├── ResponseContext.kt
│ │ ├── Runs.kt
│ │ ├── SearchSuggestions.kt
│ │ ├── SearchSuggestionsSectionRenderer.kt
│ │ ├── SectionListRenderer.kt
│ │ ├── Tabs.kt
│ │ ├── ThumbnailRenderer.kt
│ │ ├── Thumbnails.kt
│ │ ├── YTItem.kt
│ │ ├── YouTubeClient.kt
│ │ ├── YouTubeLocale.kt
│ │ ├── body
│ │ │ ├── AccountMenuBody.kt
│ │ │ ├── BrowseBody.kt
│ │ │ ├── GetQueueBody.kt
│ │ │ ├── GetSearchSuggestionsBody.kt
│ │ │ ├── GetTranscriptBody.kt
│ │ │ ├── NextBody.kt
│ │ │ ├── PlayerBody.kt
│ │ │ └── SearchBody.kt
│ │ └── response
│ │ │ ├── AccountMenuResponse.kt
│ │ │ ├── BrowseResponse.kt
│ │ │ ├── GetQueueResponse.kt
│ │ │ ├── GetSearchSuggestionsResponse.kt
│ │ │ ├── GetTranscriptResponse.kt
│ │ │ ├── NextResponse.kt
│ │ │ ├── PipedResponse.kt
│ │ │ ├── PlayerResponse.kt
│ │ │ └── SearchResponse.kt
│ │ ├── pages
│ │ ├── AlbumPage.kt
│ │ ├── ArtistItemsContinuationPage.kt
│ │ ├── ArtistItemsPage.kt
│ │ ├── ArtistPage.kt
│ │ ├── BrowseResult.kt
│ │ ├── ExplorePage.kt
│ │ ├── MoodAndGenres.kt
│ │ ├── NewReleaseAlbumPage.kt
│ │ ├── NextPage.kt
│ │ ├── PlaylistContinuationPage.kt
│ │ ├── PlaylistPage.kt
│ │ ├── RelatedPage.kt
│ │ ├── SearchPage.kt
│ │ ├── SearchSuggestionPage.kt
│ │ └── SearchSummaryPage.kt
│ │ └── utils
│ │ └── Utils.kt
│ └── test
│ └── java
│ └── com
│ └── zionhuang
│ └── innertube
│ └── YouTubeTest.kt
├── kugou
├── .gitignore
├── build.gradle.kts
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── zionhuang
│ │ └── kugou
│ │ ├── KuGou.kt
│ │ └── models
│ │ ├── DownloadLyricsResponse.kt
│ │ ├── SearchLyricsResponse.kt
│ │ └── SearchSongResponse.kt
│ └── test
│ └── java
│ └── Test.kt
├── lint.xml
├── material-color-utilities
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── com
│ └── google
│ └── material
│ └── color
│ ├── blend
│ └── Blend.java
│ ├── contrast
│ └── Contrast.java
│ ├── dislike
│ └── DislikeAnalyzer.java
│ ├── dynamiccolor
│ ├── DynamicColor.java
│ ├── ToneDeltaConstraint.java
│ └── TonePolarity.java
│ ├── hct
│ ├── Cam16.java
│ ├── Hct.java
│ ├── HctSolver.java
│ └── ViewingConditions.java
│ ├── palettes
│ ├── CorePalette.java
│ └── TonalPalette.java
│ ├── quantize
│ ├── PointProvider.java
│ ├── PointProviderLab.java
│ ├── Quantizer.java
│ ├── QuantizerCelebi.java
│ ├── QuantizerMap.java
│ ├── QuantizerResult.java
│ ├── QuantizerWsmeans.java
│ └── QuantizerWu.java
│ ├── scheme
│ ├── DynamicScheme.java
│ ├── Scheme.java
│ ├── SchemeExpressive.java
│ ├── SchemeFidelity.java
│ ├── SchemeMonochrome.java
│ ├── SchemeNeutral.java
│ ├── SchemeTonalSpot.java
│ ├── SchemeVibrant.java
│ └── Variant.java
│ ├── score
│ └── Score.java
│ ├── temperature
│ └── TemperatureCache.java
│ └── utils
│ ├── ColorUtils.java
│ ├── MathUtils.java
│ └── StringUtils.java
└── settings.gradle.kts
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 | - name: set up JDK 11
17 | uses: actions/setup-java@v3
18 | with:
19 | java-version: '11'
20 | distribution: 'temurin'
21 | cache: gradle
22 |
23 | - name: Grant execute permission for gradlew
24 | run: chmod +x gradlew
25 | - name: Build with Gradle
26 | run: ./gradlew build
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Gradle files
2 | .gradle/
3 | build/
4 |
5 | # Local configuration file (sdk path, etc)
6 | local.properties
7 |
8 | # Log/OS Files
9 | *.log
10 |
11 | # Android Studio generated files and folders
12 | captures/
13 | .externalNativeBuild/
14 | .cxx/
15 | *.apk
16 | output.json
17 |
18 | # IntelliJ
19 | *.iml
20 | .idea/
21 |
22 | # Keystore files
23 | *.jks
24 | *.keystore
25 |
26 | # Google Services (e.g. APIs or Firebase)
27 | google-services.json
28 |
29 | # Android Profiling
30 | *.hprof
31 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/CMakeLists.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/CMakeLists.txt
--------------------------------------------------------------------------------
/app/foss/release/output-metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "artifactType": {
4 | "type": "APK",
5 | "kind": "Directory"
6 | },
7 | "applicationId": "com.ozsoft.youmusic",
8 | "variantName": "fossRelease",
9 | "elements": [
10 | {
11 | "type": "SINGLE",
12 | "filters": [],
13 | "attributes": [],
14 | "versionCode": 13,
15 | "versionName": "1.0.5",
16 | "outputFile": "app-foss-release.apk"
17 | }
18 | ],
19 | "elementType": "File"
20 | }
--------------------------------------------------------------------------------
/app/full/release/app-full-release.aab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/full/release/app-full-release.aab
--------------------------------------------------------------------------------
/app/full/release/output-metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "artifactType": {
4 | "type": "APK",
5 | "kind": "Directory"
6 | },
7 | "applicationId": "com.ozsoft.youmusic",
8 | "variantName": "fullRelease",
9 | "elements": [
10 | {
11 | "type": "SINGLE",
12 | "filters": [],
13 | "attributes": [],
14 | "versionCode": 14,
15 | "versionName": "1.0.7",
16 | "outputFile": "app-full-release.apk"
17 | }
18 | ],
19 | "elementType": "File"
20 | }
--------------------------------------------------------------------------------
/app/src/debug/res/values/app_name.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | YouMusic
4 | 107606197596
5 |
--------------------------------------------------------------------------------
/app/src/foss/java/com/zionhuang/music/utils/TranslationHelper.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.utils
2 |
3 | import com.zionhuang.music.db.entities.LyricsEntity
4 |
5 | object TranslationHelper {
6 | suspend fun translate(lyrics: LyricsEntity): LyricsEntity = lyrics
7 | suspend fun clearModels() {}
8 | }
--------------------------------------------------------------------------------
/app/src/foss/java/com/zionhuang/music/utils/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.utils
2 |
3 | import com.zionhuang.music.MainActivity
4 | import java.lang.Exception
5 |
6 | fun MainActivity.setupRemoteConfig() {}
7 |
8 | fun reportException(throwable: Throwable) {
9 | throwable.printStackTrace()
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/full/java/com/zionhuang/music/utils/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.utils
2 |
3 | import com.google.firebase.crashlytics.ktx.crashlytics
4 | import com.google.firebase.ktx.Firebase
5 | import com.google.firebase.remoteconfig.ConfigUpdate
6 | import com.google.firebase.remoteconfig.ConfigUpdateListener
7 | import com.google.firebase.remoteconfig.FirebaseRemoteConfigException
8 | import com.google.firebase.remoteconfig.ktx.remoteConfig
9 | import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
10 | import com.zionhuang.music.MainActivity
11 | import kotlin.time.Duration.Companion.hours
12 |
13 | fun MainActivity.setupRemoteConfig() {
14 | val remoteConfig = Firebase.remoteConfig
15 | remoteConfig.setConfigSettingsAsync(remoteConfigSettings {
16 | minimumFetchIntervalInSeconds = 6.hours.inWholeSeconds
17 | })
18 | remoteConfig.fetchAndActivate()
19 | .addOnCompleteListener(this) { task ->
20 | if (task.isSuccessful) {
21 | latestVersion = remoteConfig.getLong("latest_version")
22 | }
23 | }
24 | remoteConfig.addOnConfigUpdateListener(object : ConfigUpdateListener {
25 | override fun onError(error: FirebaseRemoteConfigException) {}
26 | override fun onUpdate(configUpdate: ConfigUpdate) {
27 | remoteConfig.activate().addOnCompleteListener {
28 | latestVersion = remoteConfig.getLong("latest_version")
29 | }
30 | }
31 | })
32 | }
33 |
34 | fun reportException(throwable: Throwable) {
35 | Firebase.crashlytics.recordException(throwable)
36 | throwable.printStackTrace()
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/SplashActivity.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import androidx.appcompat.app.AppCompatActivity
7 |
8 | @SuppressLint("CustomSplashScreen")
9 | class SplashActivity: AppCompatActivity() {
10 | private var activityStarted = false
11 |
12 | override fun onCreate(savedInstanceState: Bundle?) {
13 | super.onCreate(savedInstanceState)
14 | setContentView(R.layout.activity_splash)
15 | activityStarted = true
16 | val timerThread: Thread = object : Thread() {
17 | override fun run() {
18 | try {
19 | sleep(3000)
20 | } catch (e: InterruptedException) {
21 | e.printStackTrace()
22 | } finally {
23 | if (activityStarted && intent != null && intent.flags and Intent.FLAG_ACTIVITY_REORDER_TO_FRONT !== 0) {
24 | finish()
25 | } else {
26 | val intent = Intent(this@SplashActivity, MainActivity::class.java)
27 | startActivity(intent)
28 | }
29 | }
30 | }
31 | }
32 | timerThread.start()
33 | }
34 |
35 | override fun onPause() {
36 | super.onPause()
37 | finish()
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/constants/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.constants
2 |
3 | object Constants {
4 | const val GITHUB_URL = "https://github.com/TeamYouDown/YouMusic"
5 | const val YOU_MUSIC_URL = "https://youdown.net"
6 | const val GITHUB_ISSUE_URL = "https://github.com/TeamYouDown/YouMusic/issues"
7 | const val TEXT_NAME = "YouMusic"
8 | const val TEXT_COMPANY = "TeamYouDown"
9 | const val ONESIGNAL_CODE = "f82119dc-c269-406b-98cd-f2075e581005"
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/constants/Dimensions.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.constants
2 |
3 | import androidx.compose.animation.core.Spring
4 | import androidx.compose.animation.core.spring
5 | import androidx.compose.ui.unit.Dp
6 | import androidx.compose.ui.unit.dp
7 |
8 | const val CONTENT_TYPE_HEADER = 0
9 | const val CONTENT_TYPE_LIST = 1
10 | const val CONTENT_TYPE_SONG = 2
11 | const val CONTENT_TYPE_ARTIST = 3
12 | const val CONTENT_TYPE_ALBUM = 4
13 | const val CONTENT_TYPE_PLAYLIST = 5
14 |
15 | val NavigationBarHeight = 80.dp
16 | val MiniPlayerHeight = 64.dp
17 | val QueuePeekHeight = 64.dp
18 | val AppBarHeight = 64.dp
19 |
20 | val ListItemHeight = 64.dp
21 | val SuggestionItemHeight = 56.dp
22 | val SearchFilterHeight = 48.dp
23 | val ListThumbnailSize = 48.dp
24 | val GridThumbnailHeight = 128.dp
25 | val AlbumThumbnailSize = 144.dp
26 |
27 | val ThumbnailCornerRadius = 6.dp
28 |
29 | val PlayerHorizontalPadding = 32.dp
30 |
31 | val NavigationBarAnimationSpec = spring(stiffness = Spring.StiffnessMediumLow)
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/constants/MediaSessionConstants.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.constants
2 |
3 | import android.os.Bundle
4 | import androidx.media3.session.SessionCommand
5 |
6 | object MediaSessionConstants {
7 | const val ACTION_TOGGLE_LIBRARY = "TOGGLE_LIBRARY"
8 | const val ACTION_TOGGLE_LIKE = "TOGGLE_LIKE"
9 | const val ACTION_TOGGLE_SHUFFLE = "TOGGLE_SHUFFLE"
10 | const val ACTION_TOGGLE_REPEAT_MODE = "TOGGLE_REPEAT_MODE"
11 | val CommandToggleLibrary = SessionCommand(ACTION_TOGGLE_LIBRARY, Bundle.EMPTY)
12 | val CommandToggleLike = SessionCommand(ACTION_TOGGLE_LIKE, Bundle.EMPTY)
13 | val CommandToggleShuffle = SessionCommand(ACTION_TOGGLE_SHUFFLE, Bundle.EMPTY)
14 | val CommandToggleRepeatMode = SessionCommand(ACTION_TOGGLE_REPEAT_MODE, Bundle.EMPTY)
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/constants/StatPeriod.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.constants
2 |
3 | import java.time.LocalDateTime
4 | import java.time.ZoneOffset
5 |
6 | enum class StatPeriod {
7 | `1_WEEK`, `1_MONTH`, `3_MONTH`, `6_MONTH`, `1_YEAR`, ALL;
8 |
9 | fun toTimeMillis(): Long =
10 | when (this) {
11 | `1_WEEK` -> LocalDateTime.now().minusWeeks(1).toInstant(ZoneOffset.UTC).toEpochMilli()
12 | `1_MONTH` -> LocalDateTime.now().minusMonths(1).toInstant(ZoneOffset.UTC).toEpochMilli()
13 | `3_MONTH` -> LocalDateTime.now().minusMonths(3).toInstant(ZoneOffset.UTC).toEpochMilli()
14 | `6_MONTH` -> LocalDateTime.now().minusMonths(6).toInstant(ZoneOffset.UTC).toEpochMilli()
15 | `1_YEAR` -> LocalDateTime.now().minusMonths(12).toInstant(ZoneOffset.UTC).toEpochMilli()
16 | ALL -> 0
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/Converters.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db
2 |
3 | import androidx.room.TypeConverter
4 | import java.time.Instant
5 | import java.time.LocalDateTime
6 | import java.time.ZoneOffset
7 |
8 | class Converters {
9 | @TypeConverter
10 | fun fromTimestamp(value: Long?): LocalDateTime? =
11 | if (value != null) LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC)
12 | else null
13 |
14 | @TypeConverter
15 | fun dateToTimestamp(date: LocalDateTime?): Long? =
16 | date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli()
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/Album.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.compose.runtime.Immutable
4 | import androidx.room.Embedded
5 | import androidx.room.Junction
6 | import androidx.room.Relation
7 |
8 | @Immutable
9 | data class Album(
10 | @Embedded
11 | val album: AlbumEntity,
12 | @Relation(
13 | entity = ArtistEntity::class,
14 | entityColumn = "id",
15 | parentColumn = "id",
16 | associateBy = Junction(
17 | value = AlbumArtistMap::class,
18 | parentColumn = "albumId",
19 | entityColumn = "artistId"
20 | )
21 | )
22 | val artists: List,
23 | ) : LocalItem() {
24 | override val id: String
25 | get() = album.id
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/AlbumArtistMap.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.ForeignKey
6 |
7 | @Entity(
8 | tableName = "album_artist_map",
9 | primaryKeys = ["albumId", "artistId"],
10 | foreignKeys = [
11 | ForeignKey(
12 | entity = AlbumEntity::class,
13 | parentColumns = ["id"],
14 | childColumns = ["albumId"],
15 | onDelete = ForeignKey.CASCADE),
16 | ForeignKey(
17 | entity = ArtistEntity::class,
18 | parentColumns = ["id"],
19 | childColumns = ["artistId"],
20 | onDelete = ForeignKey.CASCADE
21 | )
22 | ]
23 | )
24 | data class AlbumArtistMap(
25 | @ColumnInfo(index = true) val albumId: String,
26 | @ColumnInfo(index = true) val artistId: String,
27 | val order: Int,
28 | )
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/AlbumEntity.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.compose.runtime.Immutable
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import java.time.LocalDateTime
7 |
8 | @Immutable
9 | @Entity(tableName = "album")
10 | data class AlbumEntity(
11 | @PrimaryKey val id: String,
12 | val title: String,
13 | val year: Int? = null,
14 | val thumbnailUrl: String? = null,
15 | val themeColor: Int? = null,
16 | val songCount: Int,
17 | val duration: Int,
18 | val lastUpdateTime: LocalDateTime = LocalDateTime.now(),
19 | val bookmarkedAt: LocalDateTime? = null,
20 | ) {
21 | fun toggleLike() = copy(
22 | bookmarkedAt = if (bookmarkedAt != null) null else LocalDateTime.now()
23 | )
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/AlbumWithSongs.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.compose.runtime.Immutable
4 | import androidx.room.Embedded
5 | import androidx.room.Junction
6 | import androidx.room.Relation
7 |
8 | @Immutable
9 | data class AlbumWithSongs(
10 | @Embedded
11 | val album: AlbumEntity,
12 | @Relation(
13 | entity = ArtistEntity::class,
14 | entityColumn = "id",
15 | parentColumn = "id",
16 | associateBy = Junction(
17 | value = AlbumArtistMap::class,
18 | parentColumn = "albumId",
19 | entityColumn = "artistId"
20 | )
21 | )
22 | val artists: List,
23 | @Relation(
24 | entity = SongEntity::class,
25 | entityColumn = "id",
26 | parentColumn = "id",
27 | associateBy = Junction(
28 | value = SortedSongAlbumMap::class,
29 | parentColumn = "albumId",
30 | entityColumn = "songId"
31 | )
32 | )
33 | val songs: List,
34 | )
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/Artist.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.compose.runtime.Immutable
4 | import androidx.room.Embedded
5 |
6 | @Immutable
7 | data class Artist(
8 | @Embedded
9 | val artist: ArtistEntity,
10 | val songCount: Int,
11 | ) : LocalItem() {
12 | override val id: String
13 | get() = artist.id
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/ArtistEntity.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.compose.runtime.Immutable
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import org.apache.commons.lang3.RandomStringUtils
7 | import java.time.LocalDateTime
8 |
9 | @Immutable
10 | @Entity(tableName = "artist")
11 | data class ArtistEntity(
12 | @PrimaryKey val id: String,
13 | val name: String,
14 | val thumbnailUrl: String? = null,
15 | val lastUpdateTime: LocalDateTime = LocalDateTime.now(),
16 | val bookmarkedAt: LocalDateTime? = null,
17 | ) {
18 | val isYouTubeArtist: Boolean
19 | get() = id.startsWith("UC")
20 |
21 | val isLocalArtist: Boolean
22 | get() = id.startsWith("LA")
23 |
24 | fun toggleLike() = copy(
25 | bookmarkedAt = if (bookmarkedAt != null) null else LocalDateTime.now()
26 | )
27 |
28 | companion object {
29 | fun generateArtistId() = "LA" + RandomStringUtils.random(8, true, false)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/Event.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.compose.runtime.Immutable
4 | import androidx.room.ColumnInfo
5 | import androidx.room.Entity
6 | import androidx.room.ForeignKey
7 | import androidx.room.PrimaryKey
8 | import java.time.LocalDateTime
9 |
10 | @Immutable
11 | @Entity(
12 | tableName = "event",
13 | foreignKeys = [
14 | ForeignKey(
15 | entity = SongEntity::class,
16 | parentColumns = ["id"],
17 | childColumns = ["songId"],
18 | onDelete = ForeignKey.CASCADE
19 | )
20 | ]
21 | )
22 | data class Event(
23 | @PrimaryKey(autoGenerate = true) val id: Long = 0,
24 | @ColumnInfo(index = true) val songId: String,
25 | val timestamp: LocalDateTime,
26 | val playTime: Long,
27 | )
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/EventWithSong.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.compose.runtime.Immutable
4 | import androidx.room.Embedded
5 | import androidx.room.Relation
6 |
7 | @Immutable
8 | data class EventWithSong(
9 | @Embedded
10 | val event: Event,
11 | @Relation(
12 | entity = SongEntity::class,
13 | parentColumn = "songId",
14 | entityColumn = "id"
15 | )
16 | val song: Song,
17 | )
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/FormatEntity.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity(tableName = "format")
7 | data class FormatEntity(
8 | @PrimaryKey val id: String,
9 | val itag: Int,
10 | val mimeType: String,
11 | val codecs: String,
12 | val bitrate: Int,
13 | val sampleRate: Int?,
14 | val contentLength: Long,
15 | val loudnessDb: Double?,
16 | )
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/LocalItem.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | sealed class LocalItem {
4 | abstract val id: String
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/LyricsEntity.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity(tableName = "lyrics")
7 | data class LyricsEntity(
8 | @PrimaryKey val id: String,
9 | val lyrics: String,
10 | ) {
11 | companion object {
12 | const val LYRICS_NOT_FOUND = "LYRICS_NOT_FOUND"
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/Playlist.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.compose.runtime.Immutable
4 | import androidx.room.Embedded
5 | import androidx.room.Junction
6 | import androidx.room.Relation
7 |
8 | @Immutable
9 | data class Playlist(
10 | @Embedded
11 | val playlist: PlaylistEntity,
12 | val songCount: Int,
13 | @Relation(
14 | entity = SongEntity::class,
15 | entityColumn = "id",
16 | parentColumn = "id",
17 | projection = ["thumbnailUrl"],
18 | associateBy = Junction(
19 | value = PlaylistSongMapPreview::class,
20 | parentColumn = "playlistId",
21 | entityColumn = "songId"
22 | )
23 | )
24 | val thumbnails: List,
25 | ) : LocalItem() {
26 | override val id: String
27 | get() = playlist.id
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/PlaylistEntity.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.compose.runtime.Immutable
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import org.apache.commons.lang3.RandomStringUtils
7 |
8 | @Immutable
9 | @Entity(tableName = "playlist")
10 | data class PlaylistEntity(
11 | @PrimaryKey val id: String = generatePlaylistId(),
12 | val name: String,
13 | val browseId: String? = null,
14 | ) {
15 | companion object {
16 | const val LIKED_PLAYLIST_ID = "LP_LIKED"
17 | const val DOWNLOADED_PLAYLIST_ID = "LP_DOWNLOADED"
18 |
19 | fun generatePlaylistId() = "LP" + RandomStringUtils.random(8, true, false)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/PlaylistSong.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.Embedded
4 | import androidx.room.Relation
5 |
6 | data class PlaylistSong(
7 | @Embedded val map: PlaylistSongMap,
8 | @Relation(
9 | parentColumn = "songId",
10 | entityColumn = "id",
11 | entity = SongEntity::class
12 | )
13 | val song: Song,
14 | )
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/PlaylistSongMap.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.ForeignKey
6 | import androidx.room.PrimaryKey
7 |
8 | @Entity(
9 | tableName = "playlist_song_map",
10 | foreignKeys = [
11 | ForeignKey(
12 | entity = PlaylistEntity::class,
13 | parentColumns = ["id"],
14 | childColumns = ["playlistId"],
15 | onDelete = ForeignKey.CASCADE
16 | ),
17 | ForeignKey(
18 | entity = SongEntity::class,
19 | parentColumns = ["id"],
20 | childColumns = ["songId"],
21 | onDelete = ForeignKey.CASCADE)
22 | ]
23 | )
24 | data class PlaylistSongMap(
25 | @PrimaryKey(autoGenerate = true) val id: Int = 0,
26 | @ColumnInfo(index = true) val playlistId: String,
27 | @ColumnInfo(index = true) val songId: String,
28 | val position: Int = 0,
29 | )
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/PlaylistSongMapPreview.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.DatabaseView
5 |
6 | @DatabaseView(
7 | viewName = "playlist_song_map_preview",
8 | value = "SELECT * FROM playlist_song_map WHERE position <= 3 ORDER BY position")
9 | data class PlaylistSongMapPreview(
10 | @ColumnInfo(index = true) val playlistId: String,
11 | @ColumnInfo(index = true) val songId: String,
12 | val idInPlaylist: Int = 0,
13 | )
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/RelatedSongMap.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.ForeignKey
6 | import androidx.room.PrimaryKey
7 |
8 | @Entity(
9 | tableName = "related_song_map",
10 | foreignKeys = [
11 | ForeignKey(
12 | entity = SongEntity::class,
13 | parentColumns = ["id"],
14 | childColumns = ["songId"],
15 | onDelete = ForeignKey.CASCADE
16 | ),
17 | ForeignKey(
18 | entity = SongEntity::class,
19 | parentColumns = ["id"],
20 | childColumns = ["relatedSongId"],
21 | onDelete = ForeignKey.CASCADE
22 | )
23 | ]
24 | )
25 | data class RelatedSongMap(
26 | @PrimaryKey(autoGenerate = true) val id: Long = 0,
27 | @ColumnInfo(index = true) val songId: String,
28 | @ColumnInfo(index = true) val relatedSongId: String,
29 | )
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/SearchHistory.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.Entity
4 | import androidx.room.Index
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity(
8 | tableName = "search_history",
9 | indices = [Index(
10 | value = ["query"],
11 | unique = true
12 | )]
13 | )
14 | data class SearchHistory(
15 | @PrimaryKey(autoGenerate = true) val id: Long = 0,
16 | val query: String,
17 | )
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/Song.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.compose.runtime.Immutable
4 | import androidx.room.Embedded
5 | import androidx.room.Junction
6 | import androidx.room.Relation
7 |
8 | @Immutable
9 | data class Song @JvmOverloads constructor(
10 | @Embedded val song: SongEntity,
11 | @Relation(
12 | entity = ArtistEntity::class,
13 | entityColumn = "id",
14 | parentColumn = "id",
15 | associateBy = Junction(
16 | value = SortedSongArtistMap::class,
17 | parentColumn = "songId",
18 | entityColumn = "artistId"
19 | )
20 | )
21 | val artists: List,
22 | @Relation(
23 | entity = AlbumEntity::class,
24 | entityColumn = "id",
25 | parentColumn = "id",
26 | associateBy = Junction(
27 | value = SongAlbumMap::class,
28 | parentColumn = "songId",
29 | entityColumn = "albumId"
30 | )
31 | )
32 | val album: AlbumEntity? = null,
33 | ) : LocalItem() {
34 | override val id: String
35 | get() = song.id
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/SongAlbumMap.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.ForeignKey
6 |
7 | @Entity(
8 | tableName = "song_album_map",
9 | primaryKeys = ["songId", "albumId"],
10 | foreignKeys = [
11 | ForeignKey(
12 | entity = SongEntity::class,
13 | parentColumns = ["id"],
14 | childColumns = ["songId"],
15 | onDelete = ForeignKey.CASCADE),
16 | ForeignKey(
17 | entity = AlbumEntity::class,
18 | parentColumns = ["id"],
19 | childColumns = ["albumId"],
20 | onDelete = ForeignKey.CASCADE
21 | )
22 | ]
23 | )
24 | data class SongAlbumMap(
25 | @ColumnInfo(index = true) val songId: String,
26 | @ColumnInfo(index = true) val albumId: String,
27 | val index: Int,
28 | )
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/SongArtistMap.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.ForeignKey
6 |
7 | @Entity(
8 | tableName = "song_artist_map",
9 | primaryKeys = ["songId", "artistId"],
10 | foreignKeys = [
11 | ForeignKey(
12 | entity = SongEntity::class,
13 | parentColumns = ["id"],
14 | childColumns = ["songId"],
15 | onDelete = ForeignKey.CASCADE),
16 | ForeignKey(
17 | entity = ArtistEntity::class,
18 | parentColumns = ["id"],
19 | childColumns = ["artistId"],
20 | onDelete = ForeignKey.CASCADE
21 | )
22 | ]
23 | )
24 | data class SongArtistMap(
25 | @ColumnInfo(index = true) val songId: String,
26 | @ColumnInfo(index = true) val artistId: String,
27 | val position: Int,
28 | )
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/SongEntity.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.compose.runtime.Immutable
4 | import androidx.room.Entity
5 | import androidx.room.Index
6 | import androidx.room.PrimaryKey
7 | import java.time.LocalDateTime
8 |
9 | @Immutable
10 | @Entity(
11 | tableName = "song",
12 | indices = [
13 | Index(
14 | value = ["albumId"]
15 | )
16 | ]
17 | )
18 | data class SongEntity(
19 | @PrimaryKey val id: String,
20 | val title: String,
21 | val duration: Int = -1, // in seconds
22 | val thumbnailUrl: String? = null,
23 | val albumId: String? = null,
24 | val albumName: String? = null,
25 | val liked: Boolean = false,
26 | val totalPlayTime: Long = 0, // in milliseconds
27 | val inLibrary: LocalDateTime? = null,
28 | ) {
29 | fun toggleLike() = copy(
30 | liked = !liked,
31 | inLibrary = if (!liked) inLibrary ?: LocalDateTime.now() else inLibrary
32 | )
33 |
34 | fun toggleLibrary() = copy(inLibrary = if (inLibrary == null) LocalDateTime.now() else null)
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/SortedSongAlbumMap.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.DatabaseView
5 |
6 | @DatabaseView(
7 | viewName = "sorted_song_album_map",
8 | value = "SELECT * FROM song_album_map ORDER BY `index`")
9 | data class SortedSongAlbumMap(
10 | @ColumnInfo(index = true) val songId: String,
11 | @ColumnInfo(index = true) val albumId: String,
12 | val index: Int,
13 | )
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/db/entities/SortedSongArtistMap.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.db.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.DatabaseView
5 |
6 | @DatabaseView(
7 | viewName = "sorted_song_artist_map",
8 | value = "SELECT * FROM song_artist_map ORDER BY position")
9 | data class SortedSongArtistMap(
10 | @ColumnInfo(index = true) val songId: String,
11 | @ColumnInfo(index = true) val artistId: String,
12 | val position: Int,
13 | )
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/extensions/CoroutineExt.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.extensions
2 |
3 | import kotlinx.coroutines.CoroutineExceptionHandler
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.flow.Flow
6 | import kotlinx.coroutines.flow.collectLatest
7 | import kotlinx.coroutines.launch
8 |
9 | fun Flow.collect(scope: CoroutineScope, action: suspend (value: T) -> Unit) {
10 | scope.launch {
11 | collect(action)
12 | }
13 | }
14 |
15 | fun Flow.collectLatest(scope: CoroutineScope, action: suspend (value: T) -> Unit) {
16 | scope.launch {
17 | collectLatest(action)
18 | }
19 | }
20 |
21 | val SilentHandler = CoroutineExceptionHandler { _, _ -> }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/extensions/FileExt.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.extensions
2 |
3 | import java.io.File
4 | import java.io.InputStream
5 | import java.io.OutputStream
6 | import java.util.zip.ZipInputStream
7 | import java.util.zip.ZipOutputStream
8 |
9 | operator fun File.div(child: String): File = File(this, child)
10 |
11 | fun InputStream.zipInputStream():ZipInputStream = ZipInputStream(this)
12 | fun OutputStream.zipOutputStream(): ZipOutputStream = ZipOutputStream(this)
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/extensions/ListExt.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.extensions
2 |
3 | fun List.reversed(reversed: Boolean) = if (reversed) asReversed() else this
4 |
5 | fun MutableList.move(fromIndex: Int, toIndex: Int): MutableList {
6 | add(toIndex, removeAt(fromIndex))
7 | return this
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/extensions/StringExt.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.extensions
2 |
3 | import androidx.sqlite.db.SimpleSQLiteQuery
4 | import java.net.InetSocketAddress
5 | import java.net.InetSocketAddress.createUnresolved
6 |
7 | inline fun > String?.toEnum(defaultValue: T): T =
8 | if (this == null) defaultValue
9 | else try {
10 | enumValueOf(this)
11 | } catch (e: IllegalArgumentException) {
12 | defaultValue
13 | }
14 |
15 | fun String.toSQLiteQuery(): SimpleSQLiteQuery = SimpleSQLiteQuery(this)
16 |
17 | fun String.toInetSocketAddress(): InetSocketAddress {
18 | val (host, port) = split(":")
19 | return createUnresolved(host, port.toInt())
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/extensions/UtilExt.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.extensions
2 |
3 | fun tryOrNull(block: () -> T): T? =
4 | try {
5 | block()
6 | } catch (e: Exception) {
7 | null
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/lyrics/KuGouLyricsProvider.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.lyrics
2 |
3 | import android.content.Context
4 | import com.zionhuang.kugou.KuGou
5 | import com.zionhuang.music.constants.EnableKugouKey
6 | import com.zionhuang.music.utils.dataStore
7 | import com.zionhuang.music.utils.get
8 |
9 | object KuGouLyricsProvider : LyricsProvider {
10 | override val name = "Kugou"
11 | override fun isEnabled(context: Context): Boolean =
12 | context.dataStore[EnableKugouKey] ?: true
13 |
14 | override suspend fun getLyrics(id: String, title: String, artist: String, duration: Int): Result =
15 | KuGou.getLyrics(title, artist, duration)
16 |
17 | override suspend fun getAllLyrics(id: String, title: String, artist: String, duration: Int, callback: (String) -> Unit) {
18 | KuGou.getAllLyrics(title, artist, duration, callback)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/lyrics/LyricsEntry.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.lyrics
2 |
3 | data class LyricsEntry(
4 | val time: Long,
5 | val text: String,
6 | ) : Comparable {
7 | override fun compareTo(other: LyricsEntry): Int = (time - other.time).toInt()
8 |
9 | companion object {
10 | val HEAD_LYRICS_ENTRY = LyricsEntry(0L, "")
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/lyrics/LyricsProvider.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.lyrics
2 |
3 | import android.content.Context
4 |
5 | interface LyricsProvider {
6 | val name: String
7 | fun isEnabled(context: Context): Boolean
8 | suspend fun getLyrics(id: String, title: String, artist: String, duration: Int): Result
9 | suspend fun getAllLyrics(id: String, title: String, artist: String, duration: Int, callback: (String) -> Unit) {
10 | getLyrics(id, title, artist, duration).onSuccess(callback)
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/lyrics/YouTubeLyricsProvider.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.lyrics
2 |
3 | import android.content.Context
4 | import com.zionhuang.innertube.YouTube
5 | import com.zionhuang.innertube.models.WatchEndpoint
6 |
7 | object YouTubeLyricsProvider : LyricsProvider {
8 | override val name = "YouMusic"
9 | override fun isEnabled(context: Context) = true
10 | override suspend fun getLyrics(id: String, title: String, artist: String, duration: Int): Result = runCatching {
11 | val nextResult = YouTube.next(WatchEndpoint(videoId = id)).getOrThrow()
12 | YouTube.lyrics(
13 | endpoint = nextResult.lyricsEndpoint ?: throw IllegalStateException("Lyrics endpoint not found")
14 | ).getOrThrow() ?: throw IllegalStateException("Lyrics unavailable")
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/lyrics/YouTubeSubtitleLyricsProvider.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.lyrics
2 |
3 | import android.content.Context
4 | import com.zionhuang.innertube.YouTube
5 |
6 | object YouTubeSubtitleLyricsProvider : LyricsProvider {
7 | override val name = "YouMusic"
8 | override fun isEnabled(context: Context) = true
9 | override suspend fun getLyrics(id: String, title: String, artist: String, duration: Int): Result =
10 | YouTube.transcript(id)
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/models/ItemsPage.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.models
2 |
3 | import com.zionhuang.innertube.models.YTItem
4 |
5 | data class ItemsPage(
6 | val items: List,
7 | val continuation: String?,
8 | )
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/models/PersistQueue.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.models
2 |
3 | import java.io.Serializable
4 |
5 | data class PersistQueue(
6 | val title: String?,
7 | val items: List,
8 | val mediaItemIndex: Int,
9 | val position: Long,
10 | ) : Serializable
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/playback/queues/EmptyQueue.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.playback.queues
2 |
3 | import androidx.media3.common.MediaItem
4 | import com.zionhuang.music.models.MediaMetadata
5 |
6 | object EmptyQueue : Queue {
7 | override val preloadItem: MediaMetadata? = null
8 | override suspend fun getInitialStatus() = Queue.Status(null, emptyList(), -1)
9 | override fun hasNextPage() = false
10 | override suspend fun nextPage() = emptyList()
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/playback/queues/ListQueue.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.playback.queues
2 |
3 | import androidx.media3.common.MediaItem
4 | import com.zionhuang.music.models.MediaMetadata
5 |
6 | class ListQueue(
7 | val title: String? = null,
8 | val items: List,
9 | val startIndex: Int = 0,
10 | val position: Long = 0L,
11 | ) : Queue {
12 | override val preloadItem: MediaMetadata? = null
13 | override suspend fun getInitialStatus() = Queue.Status(title, items, startIndex, position)
14 | override fun hasNextPage(): Boolean = false
15 | override suspend fun nextPage() = throw UnsupportedOperationException()
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/playback/queues/Queue.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.playback.queues
2 |
3 | import androidx.media3.common.MediaItem
4 | import com.zionhuang.music.models.MediaMetadata
5 |
6 | interface Queue {
7 | val preloadItem: MediaMetadata?
8 | suspend fun getInitialStatus(): Status
9 | fun hasNextPage(): Boolean
10 | suspend fun nextPage(): List
11 |
12 | data class Status(
13 | val title: String?,
14 | val items: List,
15 | val mediaItemIndex: Int,
16 | val position: Long = 0L,
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/playback/queues/YouTubeQueue.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.playback.queues
2 |
3 | import androidx.media3.common.MediaItem
4 | import com.zionhuang.innertube.YouTube
5 | import com.zionhuang.innertube.models.WatchEndpoint
6 | import com.zionhuang.music.extensions.toMediaItem
7 | import com.zionhuang.music.models.MediaMetadata
8 | import kotlinx.coroutines.Dispatchers.IO
9 | import kotlinx.coroutines.withContext
10 |
11 | class YouTubeQueue(
12 | private var endpoint: WatchEndpoint,
13 | override val preloadItem: MediaMetadata? = null,
14 | ) : Queue {
15 | private var continuation: String? = null
16 |
17 | override suspend fun getInitialStatus(): Queue.Status {
18 | val nextResult = withContext(IO) {
19 | YouTube.next(endpoint, continuation).getOrThrow()
20 | }
21 | endpoint = nextResult.endpoint
22 | continuation = nextResult.continuation
23 | return Queue.Status(
24 | title = nextResult.title,
25 | items = nextResult.items.map { it.toMediaItem() },
26 | mediaItemIndex = nextResult.currentIndex ?: 0
27 | )
28 | }
29 |
30 | override fun hasNextPage(): Boolean = continuation != null
31 |
32 | override suspend fun nextPage(): List {
33 | val nextResult = withContext(IO) {
34 | YouTube.next(endpoint, continuation).getOrThrow()
35 | }
36 | endpoint = nextResult.endpoint
37 | continuation = nextResult.continuation
38 | return nextResult.items.map { it.toMediaItem() }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/ui/component/EmptyPlaceholder.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.ui.component
2 |
3 | import androidx.annotation.DrawableRes
4 | import androidx.compose.foundation.Image
5 | import androidx.compose.foundation.layout.*
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Alignment
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.graphics.ColorFilter
12 | import androidx.compose.ui.res.painterResource
13 | import androidx.compose.ui.unit.dp
14 |
15 | @Composable
16 | fun EmptyPlaceholder(
17 | @DrawableRes icon: Int,
18 | text: String,
19 | modifier: Modifier = Modifier,
20 | ) {
21 | Column(
22 | horizontalAlignment = Alignment.CenterHorizontally,
23 | modifier = modifier
24 | .fillMaxSize()
25 | .padding(12.dp)
26 | ) {
27 | Image(
28 | painter = painterResource(icon),
29 | contentDescription = null,
30 | colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
31 | modifier = Modifier.size(64.dp)
32 | )
33 |
34 | Spacer(Modifier.height(12.dp))
35 |
36 | Text(
37 | text = text,
38 | style = MaterialTheme.typography.bodyLarge
39 | )
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/ui/component/shimmer/ButtonPlaceholder.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.ui.component.shimmer
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Spacer
5 | import androidx.compose.foundation.layout.height
6 | import androidx.compose.foundation.shape.RoundedCornerShape
7 | import androidx.compose.material3.ButtonDefaults
8 | import androidx.compose.material3.MaterialTheme
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.draw.clip
12 |
13 | @Composable
14 | fun ButtonPlaceholder(
15 | modifier: Modifier = Modifier,
16 | ) {
17 | Spacer(modifier
18 | .height(ButtonDefaults.MinHeight)
19 | .clip(RoundedCornerShape(50))
20 | .background(MaterialTheme.colorScheme.onSurface))
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/ui/component/shimmer/TextPlaceholder.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.ui.component.shimmer
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Spacer
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.height
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.material3.MaterialTheme
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.runtime.remember
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.unit.Dp
13 | import androidx.compose.ui.unit.dp
14 | import kotlin.random.Random
15 |
16 | @Composable
17 | fun TextPlaceholder(
18 | modifier: Modifier = Modifier,
19 | height: Dp = 16.dp,
20 | ) {
21 | Spacer(
22 | modifier = modifier
23 | .padding(vertical = 4.dp)
24 | .background(MaterialTheme.colorScheme.onSurface)
25 | .fillMaxWidth(remember { 0.25f + Random.nextFloat() * 0.5f })
26 | .height(height)
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/ui/screens/Screens.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.ui.screens
2 |
3 | import androidx.annotation.DrawableRes
4 | import androidx.annotation.StringRes
5 | import androidx.compose.runtime.Immutable
6 | import com.zionhuang.music.R
7 |
8 | @Immutable
9 | sealed class Screens(
10 | @StringRes val titleId: Int,
11 | @DrawableRes val iconId: Int,
12 | val route: String,
13 | ) {
14 | object Home : Screens(R.string.home, R.drawable.home, "home")
15 | object Songs : Screens(R.string.songs, R.drawable.music_note, "songs")
16 | object Artists : Screens(R.string.artists, R.drawable.artist, "artists")
17 | object Albums : Screens(R.string.albums, R.drawable.album, "albums")
18 | object Playlists : Screens(R.string.playlists, R.drawable.queue_music, "playlists")
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/ui/utils/NavControllerUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.ui.utils
2 |
3 | import androidx.navigation.NavController
4 | import androidx.navigation.NavGraph
5 |
6 | val NavController.canNavigateUp: Boolean
7 | get() = backQueue.count { entry -> entry.destination !is NavGraph } > 1
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/ui/utils/ShapeUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.ui.utils
2 |
3 | import androidx.compose.foundation.shape.CornerBasedShape
4 | import androidx.compose.foundation.shape.CornerSize
5 | import androidx.compose.ui.unit.dp
6 |
7 | fun CornerBasedShape.top(): CornerBasedShape =
8 | copy(bottomStart = CornerSize(0.dp), bottomEnd = CornerSize(0.dp))
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/ui/utils/StringUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.ui.utils
2 |
3 | import kotlin.math.absoluteValue
4 |
5 | fun formatFileSize(sizeBytes: Long): String {
6 | val prefix = if (sizeBytes < 0) "-" else ""
7 | var result: Long = sizeBytes.absoluteValue
8 | var suffix = "B"
9 | if (result > 900) {
10 | suffix = "KB"
11 | result /= 1024
12 | }
13 | if (result > 900) {
14 | suffix = "MB"
15 | result /= 1024
16 | }
17 | if (result > 900) {
18 | suffix = "GB"
19 | result /= 1024
20 | }
21 | if (result > 900) {
22 | suffix = "TB"
23 | result /= 1024
24 | }
25 | if (result > 900) {
26 | suffix = "PB"
27 | result /= 1024
28 | }
29 | return "$prefix$result $suffix"
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/ui/utils/YouTubeUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.ui.utils
2 |
3 | fun String.resize(
4 | width: Int? = null,
5 | height: Int? = null,
6 | ): String {
7 | if (width == null && height == null) return this
8 | "https://lh3\\.googleusercontent\\.com/.*=w(\\d+)-h(\\d+).*".toRegex().matchEntire(this)?.groupValues?.let { group ->
9 | val (W, H) = group.drop(1).map { it.toInt() }
10 | var w = width
11 | var h = height
12 | if (w != null && h == null) h = (w / W) * H
13 | if (w == null && h != null) w = (h / H) * W
14 | return "${split("=w")[0]}=w$w-h$h-p-l90-rj"
15 | }
16 | if (this matches "https://yt3\\.ggpht\\.com/.*=s(\\d+)".toRegex()) {
17 | return "$this-s${width ?: height}"
18 | }
19 | return this
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/utils/StringUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.utils
2 |
3 | import java.math.BigInteger
4 | import java.security.MessageDigest
5 |
6 | fun makeTimeString(duration: Long?): String {
7 | if (duration == null || duration < 0) return ""
8 | var sec = duration / 1000
9 | val day = sec / 86400
10 | sec %= 86400
11 | val hour = sec / 3600
12 | sec %= 3600
13 | val minute = sec / 60
14 | sec %= 60
15 | return when {
16 | day > 0 -> "%d:%02d:%02d:%02d".format(day, hour, minute, sec)
17 | hour > 0 -> "%d:%02d:%02d".format(hour, minute, sec)
18 | else -> "%d:%02d".format(minute, sec)
19 | }
20 | }
21 |
22 | fun md5(str: String): String {
23 | val md = MessageDigest.getInstance("MD5")
24 | return BigInteger(1, md.digest(str.toByteArray())).toString(16).padStart(32, '0')
25 | }
26 |
27 | fun joinByBullet(vararg str: String?) =
28 | str.filterNot {
29 | it.isNullOrEmpty()
30 | }.joinToString(separator = " • ")
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/viewmodels/AccountViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.viewmodels
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.zionhuang.innertube.YouTube
6 | import com.zionhuang.innertube.models.PlaylistItem
7 | import com.zionhuang.music.utils.reportException
8 | import dagger.hilt.android.lifecycle.HiltViewModel
9 | import kotlinx.coroutines.flow.MutableStateFlow
10 | import kotlinx.coroutines.launch
11 | import javax.inject.Inject
12 |
13 | @HiltViewModel
14 | class AccountViewModel @Inject constructor() : ViewModel() {
15 | val playlists = MutableStateFlow?>(null)
16 |
17 | init {
18 | viewModelScope.launch {
19 | YouTube.likedPlaylists().onSuccess {
20 | playlists.value = it
21 | }.onFailure {
22 | reportException(it)
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/viewmodels/ArtistViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.viewmodels
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import androidx.lifecycle.SavedStateHandle
7 | import androidx.lifecycle.ViewModel
8 | import androidx.lifecycle.viewModelScope
9 | import com.zionhuang.innertube.YouTube
10 | import com.zionhuang.innertube.pages.ArtistPage
11 | import com.zionhuang.music.db.MusicDatabase
12 | import com.zionhuang.music.utils.reportException
13 | import dagger.hilt.android.lifecycle.HiltViewModel
14 | import kotlinx.coroutines.flow.SharingStarted
15 | import kotlinx.coroutines.flow.stateIn
16 | import kotlinx.coroutines.launch
17 | import javax.inject.Inject
18 |
19 | @HiltViewModel
20 | class ArtistViewModel @Inject constructor(
21 | database: MusicDatabase,
22 | savedStateHandle: SavedStateHandle,
23 | ) : ViewModel() {
24 | val artistId = savedStateHandle.get("artistId")!!
25 | var artistPage by mutableStateOf(null)
26 | val libraryArtist = database.artist(artistId)
27 | .stateIn(viewModelScope, SharingStarted.Lazily, null)
28 | val librarySongs = database.artistSongsPreview(artistId)
29 | .stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
30 |
31 | init {
32 | viewModelScope.launch {
33 | YouTube.artist(artistId)
34 | .onSuccess {
35 | artistPage = it
36 | }.onFailure {
37 | reportException(it)
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/viewmodels/MoodAndGenresViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.viewmodels
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.zionhuang.innertube.YouTube
6 | import com.zionhuang.innertube.pages.MoodAndGenres
7 | import com.zionhuang.music.utils.reportException
8 | import dagger.hilt.android.lifecycle.HiltViewModel
9 | import kotlinx.coroutines.flow.MutableStateFlow
10 | import kotlinx.coroutines.launch
11 | import javax.inject.Inject
12 |
13 | @HiltViewModel
14 | class MoodAndGenresViewModel @Inject constructor() : ViewModel() {
15 | val moodAndGenres = MutableStateFlow?>(null)
16 |
17 | init {
18 | viewModelScope.launch {
19 | YouTube.moodAndGenres().onSuccess {
20 | moodAndGenres.value = it
21 | }.onFailure {
22 | reportException(it)
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/viewmodels/OnlinePlaylistViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.viewmodels
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 | import androidx.lifecycle.ViewModel
5 | import androidx.lifecycle.viewModelScope
6 | import com.zionhuang.innertube.YouTube
7 | import com.zionhuang.innertube.models.PlaylistItem
8 | import com.zionhuang.innertube.models.SongItem
9 | import com.zionhuang.innertube.utils.completed
10 | import com.zionhuang.music.utils.reportException
11 | import dagger.hilt.android.lifecycle.HiltViewModel
12 | import kotlinx.coroutines.Dispatchers
13 | import kotlinx.coroutines.flow.MutableStateFlow
14 | import kotlinx.coroutines.launch
15 | import javax.inject.Inject
16 |
17 | @HiltViewModel
18 | class OnlinePlaylistViewModel @Inject constructor(
19 | savedStateHandle: SavedStateHandle,
20 | ) : ViewModel() {
21 | private val playlistId = savedStateHandle.get("playlistId")!!
22 |
23 | val playlist = MutableStateFlow(null)
24 | val playlistSongs = MutableStateFlow>(emptyList())
25 |
26 | init {
27 | viewModelScope.launch(Dispatchers.IO) {
28 | YouTube.playlist(playlistId).completed()
29 | .onSuccess { playlistPage ->
30 | playlist.value = playlistPage.playlist
31 | playlistSongs.value = playlistPage.songs
32 | }.onFailure {
33 | reportException(it)
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zionhuang/music/viewmodels/YouTubeBrowseViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.music.viewmodels
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 | import androidx.lifecycle.ViewModel
5 | import androidx.lifecycle.viewModelScope
6 | import com.zionhuang.innertube.YouTube
7 | import com.zionhuang.innertube.pages.BrowseResult
8 | import com.zionhuang.music.utils.reportException
9 | import dagger.hilt.android.lifecycle.HiltViewModel
10 | import kotlinx.coroutines.flow.MutableStateFlow
11 | import kotlinx.coroutines.launch
12 | import javax.inject.Inject
13 |
14 | @HiltViewModel
15 | class YouTubeBrowseViewModel @Inject constructor(
16 | savedStateHandle: SavedStateHandle,
17 | ) : ViewModel() {
18 | private val browseId = savedStateHandle.get("browseId")!!
19 | private val params = savedStateHandle.get("params")
20 |
21 | val result = MutableStateFlow(null)
22 |
23 | init {
24 | viewModelScope.launch {
25 | YouTube.browse(browseId, params).onSuccess {
26 | result.value = it
27 | }.onFailure {
28 | reportException(it)
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_stat_onesignal_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable-hdpi/ic_stat_onesignal_default.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/splash_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable-hdpi/splash_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_stat_onesignal_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable-mdpi/ic_stat_onesignal_default.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/splash_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable-mdpi/splash_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_stat_onesignal_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable-xhdpi/ic_stat_onesignal_default.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/splash_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable-xhdpi/splash_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_stat_onesignal_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable-xxhdpi/ic_stat_onesignal_default.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/splash_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable-xxhdpi/splash_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_stat_onesignal_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable-xxxhdpi/ic_stat_onesignal_default.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/splash_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable-xxxhdpi/splash_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/add.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/album.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/app_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable/app_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/arrow_back.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/arrow_downward.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/arrow_forward.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/arrow_top_left.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/arrow_upward.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/artist.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/backup.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bedtime.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bookmark.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bookmark_filled.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cached.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/casino.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/clear_all.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/close.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/contrast.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/dark_mode.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/delete.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/discover_tune.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/download.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/drag_handle.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/edit.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/equalizer.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/error.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/expand_less.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/expand_more.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/explicit.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/favorite.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/favorite_border.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/github.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/graphic_eq.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/grid_view.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/history.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/home.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/info.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/input.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/language.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/library_add.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/library_add_check.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/library_music.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/list.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/location_on.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/lock.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/lock_open.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/lyrics.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/manage_search.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/mood.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/more_horiz.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/more_vert.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/music_note.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/navigate_next.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/offline.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/palette.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/pause.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/person.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/play.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/playlist_add.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/playlist_play.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/queue_music.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/radio.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/radio_button_checked.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/radio_button_unchecked.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/remove.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/repeat.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/repeat_on.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/repeat_one.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/repeat_one_on.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/replay.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/restore.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/search.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/security.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/share.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shortcut_albums.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shortcut_playlists.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shortcut_search.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shortcut_songs.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shuffle.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shuffle_on.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/skip_next.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/skip_previous.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/slow_motion_video.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/splash_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/drawable/splash_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/storage.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sync.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/tab.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/translate.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/trending_up.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/tune.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/update.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/volume_up.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/font/amsterdam.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/font/amsterdam.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/arvo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/font/arvo.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/bod.TTF:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/font/bod.TTF
--------------------------------------------------------------------------------
/app/src/main/res/font/edutas.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/font/edutas.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/kenia.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/font/kenia.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/oswald.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/font/oswald.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/raleway1.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/font/raleway1.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/raleway2.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/font/raleway2.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/roboto1.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/font/roboto1.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/roboto2.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/font/roboto2.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/teko.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/font/teko.ttf
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/app_name.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | YouMusic
4 | 107606197596
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/values.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @drawable/play
5 | @drawable/pause
6 | @drawable/skip_previous
7 | @drawable/skip_next
8 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/automotive_app_desc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
9 |
12 |
--------------------------------------------------------------------------------
/assets/buymeacoffee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/assets/buymeacoffee.png
--------------------------------------------------------------------------------
/assets/liberapay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/assets/liberapay.png
--------------------------------------------------------------------------------
/crowdin.yml:
--------------------------------------------------------------------------------
1 | files:
2 | - source: /app/src/main/res/values/strings.xml
3 | translation: /app/src/main/res/values-%android_code%/strings.xml
4 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/app_banner_youmusic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/app_banner_youmusic.jpg
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/10.txt:
--------------------------------------------------------------------------------
1 |
Improved
2 |
3 | * Support Android 13 themed icon (#157)
4 |
5 |
Fixed
6 |
7 | * Fix stream can't be played (#161)
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/11.txt:
--------------------------------------------------------------------------------
1 | In this version, we are using a new library: Innertube, which makes more powerful features possible!
2 | * Browse everything as in YouTube Music
3 | * New home page to browse suggestions and find new music releases
4 | * Search result summary tab
5 | * Loading stream is faster
6 |
7 | Credit: vfsfitvnm/ViMusic, tombulled/innertube, zerodytrash/YouTube-Internal-Clients
8 |
9 | ⚠️ Note: For users upgrading from <0.3.3, please select all songs in your library and do "Refetch" to get thumbnails shown.
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/12.txt:
--------------------------------------------------------------------------------
1 | * Start radio from library songs
2 | * Default open tab setting
3 | * Audio quality setting
4 | * Pause or clear search history
5 | * Backup and restore
6 | * Support SOCKS proxy
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/13.txt:
--------------------------------------------------------------------------------
1 | Music is now renamed to InnerTune!
2 |
3 | - Player redesign
4 | - Queue redesign
5 | - Cache songs
6 | - Stats for nerds
7 | - Persistent queue
8 | - Add built-in playlist (liked, downloaded)
9 | - Set cache limits
10 | - Sort songs by play time
11 | - Customize navigation tabs
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/14.txt:
--------------------------------------------------------------------------------
1 | - Lyrics
2 | - Android Auto support
3 | - Skip silence
4 | - Audio normalization
5 | - Export downloaded songs via SAF
6 | - Improve player view layout
7 | - Add wake lock for player
8 | - Option to hide buttons in player notification
9 | - Delete persistent queue when restoring database
10 | - Show itag in stats for nerds
11 | - Minor changes and fixes
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/15.txt:
--------------------------------------------------------------------------------
1 | Improvement
2 |
3 | - Add shuffle button in Android Auto
4 |
5 | Fixed
6 |
7 | - Fix apostrophe displayed as HTML entity in lyrics #401
8 | - Update YouTube search filter key #436
9 | - Fix #432
10 |
11 | Translation
12 |
13 | - Update Hungarian translation #407 #425
14 | - Add Indonesia translation #415
15 | - Update Simplified Chinese translation #416
16 | - Update Japanese translation #419
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/16.txt:
--------------------------------------------------------------------------------
1 | - New app icon
2 | - Rewrite UI using Jetpack Compose
3 | - Better UI/UX
4 | - Dynamic theme
5 | - Redesigned home screen
6 | - Improved download experience
7 | - New lyrics source: YouTube subtitle
8 | - Show replay button when playing ended
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/17.txt:
--------------------------------------------------------------------------------
1 | - Login support
2 | - Mood & Genres
3 | - Better stats screen
4 | - Bookmark artists
5 | - Minor enhancement and bug fixes
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/18.txt:
--------------------------------------------------------------------------------
1 | - Improve library design
2 | - Lyrics translator (full version only)
3 | - Minor enhancement and bug fixes
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/19.txt:
--------------------------------------------------------------------------------
1 | - Better UI
2 | - Grid layout for albums and playlists
3 | - Minor enhancement and bug fixes
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/5.txt:
--------------------------------------------------------------------------------
1 | * App updater (Preview)
2 | * Share button function in bottom control fragment
3 | * Open YouTube urls by this app #11
4 | * You can now search for YouTube Music albums
5 | * Remove download of a song
6 | * Show channel or playlist name in search fragment title
7 | * Scrollable search filter chip group
8 | * Minor fixes
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/6.txt:
--------------------------------------------------------------------------------
1 |
Fixed
2 |
3 | * Fix YouTube not playing any streams #27, TeamNewPipe/NewPipe#8202
4 | * Fix YouTube age restricted videos being throttled TeamNewPipe/NewPipeExtractor#832
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/7.txt:
--------------------------------------------------------------------------------
1 | * Material You #25
2 | * Dark mode settings
3 | * Show an icon indicating if a song is in your library in search fragment
4 | * Make search filter bar fixed
5 | * Move song downloaded icon to second line
6 | * Smoother transition animation
7 | * Merge artists with same name (#50)
8 | * Minor fixes
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/8.txt:
--------------------------------------------------------------------------------
1 |
Fixed
2 |
3 | * Fix main content resize animation
4 | * Fix dark theme malfunction #69
5 | * Fix renaming an artist can lead to lose songs #75
6 |
7 |
Translation updates
8 |
9 | * Finnish (by @teemue) #67
10 | * Italian (by @airon90) #71
11 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/9.txt:
--------------------------------------------------------------------------------
1 |
Fixed
2 |
3 | * Fix stream can't be played (#148)
4 | * Fix bottom navigation item isn't checked when clicked (#12)
5 |
6 |
Translation updates
7 |
8 | * Korean (by @dongsu8142) #107
9 | * Spanish (by @DD21S) #120
10 | * Japanese (by @HiSubway) #138
11 | * Swedish (by @Itroublve) #143
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/full_description.txt:
--------------------------------------------------------------------------------
1 | With this app, you're like getting a free music streaming service. You can listen to music from YouTube Music and build your own library. What's more, songs can be downloaded for offline playback. You can also create playlists to organize your songs. The aim of YouMusic is to enable everyone to listen to music at no cost by an easy-to-use, practical and ad-free application.
2 |
3 |
Note:
4 |
5 | The project is currently in an unstable stage. If you encounter bugs, please report by opening an issue on GitHub.
6 |
7 |
Features:
8 |
9 | - Play songs from YT/YT Music without ads
10 | - Background playback
11 | - Search songs, videos, albums, and playlists from YouTube Music
12 | - Library management
13 | - Cache and download songs for offline playback
14 | - Synchronized lyrics
15 | - Skip silence
16 | - Audio normalization
17 | - Dynamic theme
18 | - Localization
19 | - Android Auto support
20 | - Personalized quick picks
21 | - Material 3
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/en-US/images/icon.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/en1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/en-US/images/phoneScreenshots/en1.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/en2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/en-US/images/phoneScreenshots/en2.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/en3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/en-US/images/phoneScreenshots/en3.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/en4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/en-US/images/phoneScreenshots/en4.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/en5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/en-US/images/phoneScreenshots/en5.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/en6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/en-US/images/phoneScreenshots/en6.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/en7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/en-US/images/phoneScreenshots/en7.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/en8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/en-US/images/phoneScreenshots/en8.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | A Material 3 YouTube Music client for Android
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/title.txt:
--------------------------------------------------------------------------------
1 | InnerTune
--------------------------------------------------------------------------------
/fastlane/metadata/android/es/full_description.txt:
--------------------------------------------------------------------------------
1 | Un cliente de YouTube Music con Material 3 para Android
2 |
3 |
Características:
4 |
5 | - Reproduce canciones de YT/YT Music sin anuncios
6 | - Reproducción en segundo plano
7 | - Busca canciones, vídeos, álbumes y listas de reproducción de YouTube Music
8 | - Gestión de biblioteca
9 | - Guarda en caché y descarga canciones para reproducirlas sin conexión
10 | - Letras sincronizadas
11 | - Saltar silencio
12 | - Normalización de audio
13 | - Tema dinámico
14 | - Localización
15 | - Compatibilidad con Android Auto
16 | - Selecciones rápidas personalizadas
17 | - Material 3
--------------------------------------------------------------------------------
/fastlane/metadata/android/es/short_description.txt:
--------------------------------------------------------------------------------
1 | Un cliente de YouTube Music con Material 3 para Android
--------------------------------------------------------------------------------
/fastlane/metadata/android/it/full_description.txt:
--------------------------------------------------------------------------------
1 | Con quest'app, avrai un servizio di streaming musicale gratuito. Puoi ascoltare musica da YouTube Music e crearti la tua libreria musicale personale. Inoltre, i brani possono essere scaricati per un ascolto offline. Puoi anche creare delle playlist per organizzare la tua musica. L'obiettivo di YouMusic è quello di dare la possibilità a chiunque di ascoltare musica senza costi e facilmente, in maniera pratica e priva di pubblicità.
2 |
3 |
Nota:
4 |
5 | Il progetto è attualmente in una fase instabile. Se trovi degli errori, per favore segnalali facendolo su GitHub.
6 |
7 |
Caratteristiche:
8 |
9 | - Ascolto dei brani da YT/YT Music senza pubblicità
10 | - Riproduzione in background
11 | - Ricerca dei brani, video, album e playlist da YouTube Music
12 | - Gestione della libreria
13 | - Cache e download dei brani per la riproduzione offline
14 | - Testi sincronizzati
15 | - Salto del silenzio
16 | - Normalizzazione dell'audio
17 | - Tema dinamico
18 | - Varie lingue disponibili
19 | - Supporto per Android Auto
20 | - Scelte rapide personalizzate
21 | - Material 3
22 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/it/short_description.txt:
--------------------------------------------------------------------------------
1 | Un client di YouTube Music in Material 3 per Android
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/it/title.txt:
--------------------------------------------------------------------------------
1 | InnerTune
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/ja/full_description.txt:
--------------------------------------------------------------------------------
1 | このアプリを使ったら、無料の音楽ストリーミングサービスを手に入れたようなものです。YouTube Musicの音楽を聴いたり、自分だけのライブラリを作り上げたりできます。さらには、音楽をダウンロードし、オフラインで再生することもできます。また、プレイリストを作って、曲を整理することも可能です。簡単で、実用的で、広告のないアプリで、誰もが無料で音楽を聴くことができるようにすることが、YouMusicの目的です。
2 |
3 |
4 |
注意:
5 |
6 | 現在、プロジェクトはまだ安定しない段階です。もし不具合が発生したら、GitHubからissueを報告してください。
7 |
8 |
機能:
9 |
10 | * 広告なしで音楽を再生
11 | * YouTube Musicのほぼすべてのページを閲覧
12 | * YouTube Musicから、曲、アルバム、動画、プレイリストを検索
13 | * YouTube Musicのリンクを開く
14 | * 曲、アルバム、動画、プレイリストをローカルデータベースに保存
15 | * 曲をダウンロードしてオフラインで再生
16 | * 曲のタイトルを編集
17 | * YouTube Musicのお気に入りプレイリストへのリンクを追加
18 | * Material Designに準拠したプレイヤー
19 | * ロック画面での再生
20 | * 通知パネルで音楽をコントロール
21 | * 次の/前の曲にスキップ
22 | * リピート/シャッフルモード
23 | * 再生中のキューを編集
24 | * カスタムテーマ
25 | * ダークモード
26 | * 多言語対応
27 | * プロキシ
28 | * バックアップとリストア
--------------------------------------------------------------------------------
/fastlane/metadata/android/ja/short_description.txt:
--------------------------------------------------------------------------------
1 | YouTube Musicから音楽をストリーミング再生するMaterial Designの音楽プレイヤー
--------------------------------------------------------------------------------
/fastlane/metadata/android/ko/full_description.txt:
--------------------------------------------------------------------------------
1 | Music은 YouTube/YouTube Music에서 정보를 검색하고 데이터를 스트리밍하기 위해 NewPipe Extractor를 사용합니다. 또한 음악 플레이어이므로 재생 목록을 직접 만들고 만든 아티스트별로 노래를 구성할 수 있습니다. Music의 목적은 사용하기 쉽고 실용적이며 광고 없이 모든 사람이 무료로 음악을 들을 수 있도록 하는 것입니다.
2 |
3 |
메모:
4 |
5 | 그 프로젝트는 현재 불안정한 단계에 있습니다. 버그가 발생하면 GitHub에서 이슈를 열어 보고해주세요.
6 |
7 |
특징:
8 |
9 | * 광고 없음
10 | * YouTube/YouTube Music에서 노래, 비디오, 재생 목록 및 채널 검색
11 | * YouTube에서 대기열의 마지막 5곡을 재생할 때 더 많은 노래를 자동으로 로드
12 | * 머티리얼 디자인 플레이어
13 | * 잠금 화면에서 재생
14 | * 알림의 미디어 컨트롤
15 | * 다음/이전 노래로 건너뛰기
16 | * 반복/셔플 모드
17 | * 지금 재생 중인 대기열 편집
18 | * YouTube/YouTube Music에서 노래 재생 및 저장
19 | * 오프라인 재생을 위한 음악 다운로드
20 | * 로컬 데이터베이스에 재생 목록 만들기
21 | * 노래 이름 및 노래 아티스트 편집
22 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/ko/short_description.txt:
--------------------------------------------------------------------------------
1 | YouTube/YouTube Music의 음악이 포함된 머티리얼 디자인 뮤직 플레이어
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/pa/full_description.txt:
--------------------------------------------------------------------------------
1 | ਇਸ ਐਪ ਦੇ ਨਾਲ, ਤੁਸੀਂ ਇੱਕ ਮੁਫਤ ਸੰਗੀਤ ਸਟ੍ਰੀਮਿੰਗ ਸੇਵਾ ਪ੍ਰਾਪਤ ਕਰਨ ਜਾ ਰਹੇ ਹੋ। ਤੁਸੀਂ ਯੂਟਿਊਬ ਮਿਊਜ਼ਕ ਤੋਂ ਸੰਗੀਤ ਸੁਣ ਸਕਦੇ ਹੋ ਅਤੇ ਆਪਣੀ ਖੁਦ ਦੀ ਲਾਇਬ੍ਰੇਰੀ ਬਣਾ ਸਕਦੇ ਹੋ। ਇਸ ਤੋਂ ਇਲਾਵਾ, ਆਫਲਾਈਨ ਪਲੇਬੈਕ ਲਈ ਗਾਣੇ ਡਾਊਨਲੋਡ ਕੀਤੇ ਜਾ ਸਕਦੇ ਹਨ। ਤੁਸੀਂ ਆਪਣੇ ਗੀਤਾਂ ਨੂੰ ਸੰਗਠਿਤ ਕਰਨ ਲਈ ਪਲੇਲਿਸਟਾਂ ਵੀ ਬਣਾ ਸਕਦੇ ਹੋ। ਇਨਰਟਿਊਨ ਦਾ ਉਦੇਸ਼ ਹਰ ਕਿਸੇ ਨੂੰ ਬਿਨਾਂ ਕਿਸੇ ਕੀਮਤ 'ਤੇ ਵਰਤੋਂ-ਵਿੱਚ-ਆਸਾਨ, ਵਿਹਾਰਕ ਅਤੇ ਵਿਗਿਆਪਨ-ਮੁਕਤ ਐਪਲੀਕੇਸ਼ਨ ਦੁਆਰਾ ਸੰਗੀਤ ਸੁਣਨ ਦੇ ਯੋਗ ਬਣਾਉਣਾ ਹੈ।
2 |
3 |
ਨੋਟ:
4 |
5 | ਪ੍ਰੋਜੈਕਟ ਇਸ ਸਮੇਂ ਇੱਕ ਅਸਥਿਰ ਪੜਾਅ ਵਿੱਚ ਹੈ। ਜੇਕਰ ਤੁਹਾਨੂੰ ਬੱਗ ਨਜ਼ਰ ਆਉਂਦੇ ਹਨ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਗਿਟਹੱਬਉੱਪਰ ਕੋਈ ਸਮੱਸਿਆ ਖੋਲ੍ਹ ਕੇ ਰਿਪੋਰਟ ਕਰੋ।
6 |
7 |
ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ:
8 |
9 | * ਬਿਨਾਂ ਵਿਗਿਆਪਨਾਂ ਦੇ ਗੀਤ ਚਲਾਓ
10 | * ਔਫਲਾਈਨ ਪਲੇਬੈਕ ਲਈ ਸੰਗੀਤ ਡਾਊਨਲੋਡ ਕਰੋ
11 | * ਲੋਕਲ ਲਾਇਬ੍ਰੇਰੀ ਪ੍ਰਬੰਧ
12 | * ਗੀਤ ਕੈਸ਼ੇ ਕਰੋ
13 | * ਸਮਵਰਤੀ ਬੋਲ
14 | * ਆਡੀਓ ਨਾਰਮੇਲਾਈਜ਼ੇਸ਼ਨ
15 | * ਚੁੱਪ ਨੂੰ ਅੱਗੇ ਲੰਘਾਓ
16 | * ਬੈਕਅੱਪ ਅਤੇ ਰੀਸਟੋਰ
17 | * ਪ੍ਰੌਕਸੀ ਸਮਰਥਨ
18 | * ਐਂਡਰਾਇਡ ਆਟੋ ਦਾ ਸਮਰਥਨ
--------------------------------------------------------------------------------
/fastlane/metadata/android/pa/short_description.txt:
--------------------------------------------------------------------------------
1 | ਇੱਕ ਮਟੀਰੀਅਲ ਡਿਜ਼ਾਈਨ ਵਾਲਾ ਯੂਟਿਊਬ ਮਿਊਜ਼ਕ ਕਲਾਈਂਟ
--------------------------------------------------------------------------------
/fastlane/metadata/android/ru-RU/full_description.txt:
--------------------------------------------------------------------------------
1 | Клиент YouTube Music для Android в стиле Material 3
2 |
3 |
Особенности:
4 |
5 | - Воспроизведение песен с YT/YT Music без рекламы
6 | - Фоновое воспроизведение
7 | - Поиск песен, видео, альбомов и плейлистов в YouTube Music
8 | - Управление библиотекой
9 | - Кэширование и загрузка песен для офлайн-воспроизведения
10 | - Синхронизированный текст песен
11 | - Пропуск тишины
12 | - Нормализация аудио
13 | - Динамическая тема
14 | - Локализация
15 | - Поддержка Android Auto
16 | - Персонализированные быстрые выборки
17 | - Material 3
18 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/ru-RU/short_description.txt:
--------------------------------------------------------------------------------
1 | Клиент YouTube Music для Android в стиле Material 3
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/ru-RU/title.txt:
--------------------------------------------------------------------------------
1 | InnerTune
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/tr/full_description.txt:
--------------------------------------------------------------------------------
1 | Bu uygulamayla, ücretsiz bir müzik akışı hizmeti alıyor gibisiniz. YouTube Music'ten müzik dinleyebilir ve kendi kitaplığınızı oluşturabilirsiniz. Dahası, şarkılar çevrimdışı oynatmak için indirilebilir. Şarkılarınızı düzenlemek için çalma listeleri de oluşturabilirsiniz. YouMusic'in amacı, kullanımı kolay, pratik ve reklamsız bir uygulama ile herkesin ücretsiz müzik dinlemesini sağlamaktır.
2 |
3 |
Not:
4 |
5 | Proje şu anda istikrarsız bir aşamada. Hatalarla karşılaşırsanız, lütfen GitHub'da bir sorun açarak bildirin.
6 |
7 |
Özellikler:
8 |
9 | - YT/YT Müzik'teki şarkıları reklamsız dinleyin
10 | - Arka planda oynatma
11 | - YouTube Müzik'te şarkı, video, albüm ve çalma listesi arama
12 | - Kütüphane yönetimi
13 | - Çevrimdışı çalmak için şarkıları önbelleğe alma ve indirme
14 | - Senkronize şarkı sözleri
15 | - Sessizliği atlama
16 | - Ses normalleştirme
17 | - Dinamik tema
18 | - Yerelleştirme
19 | - Android Auto desteği
20 | - Kişiselleştirilmiş hızlı seçimler
21 | - Materyal 3
22 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/tr/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/tr/images/icon.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/tr/images/phoneScreenshots/tr1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/tr/images/phoneScreenshots/tr1.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/tr/images/phoneScreenshots/tr2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/tr/images/phoneScreenshots/tr2.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/tr/images/phoneScreenshots/tr3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/tr/images/phoneScreenshots/tr3.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/tr/images/phoneScreenshots/tr4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/tr/images/phoneScreenshots/tr4.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/tr/images/phoneScreenshots/tr5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/tr/images/phoneScreenshots/tr5.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/tr/images/phoneScreenshots/tr6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/tr/images/phoneScreenshots/tr6.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/tr/images/phoneScreenshots/tr7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/tr/images/phoneScreenshots/tr7.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/tr/images/phoneScreenshots/tr8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/fastlane/metadata/android/tr/images/phoneScreenshots/tr8.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/tr/short_description.txt:
--------------------------------------------------------------------------------
1 | Materyal tasarımlı bir YouTube Müzik istemcisi
--------------------------------------------------------------------------------
/fastlane/metadata/android/uk-UA/full_description.txt:
--------------------------------------------------------------------------------
1 | Клієнт YouTube Music для Android у стилі Material 3
2 |
3 | Особливості:
4 |
5 | - Відтворення пісень з YT/YT Music без реклами
6 | - Фонове відтворення
7 | - Пошук пісень, відео, альбомів та плейлистів в YouTube Music
8 | - Керування бібліотекою
9 | - Кешування та завантаження пісень для офлайн-відтворення
10 | - Синхронізований текст пісень
11 | - Пропуск тиші
12 | - Нормалізація аудіо
13 | - Динамічна тема
14 | - Локалізація
15 | - Підтримка Android Auto
16 | - Персоналізовані швидкі вибірки
17 | - Material 3
18 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/uk-UA/short_description.txt:
--------------------------------------------------------------------------------
1 | Клієнт YouTube Music для Android у стилі Material 3
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/uk-UA/title.txt:
--------------------------------------------------------------------------------
1 | InnerTune
2 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## For more details on how to configure your build environment visit
2 | # http://www.gradle.org/docs/current/userguide/build_environment.html
3 | #
4 | # Specifies the JVM arguments used for the daemon process.
5 | # The setting is particularly useful for tweaking memory settings.
6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
7 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
8 | #
9 | # When configured, Gradle will run in incubating parallel mode.
10 | # This option should only be used with decoupled projects. More details, visit
11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
12 | # org.gradle.parallel=true
13 | #Sat Nov 19 15:59:34 CST 2022
14 |
15 | org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
16 | android.useAndroidX=true
17 | android.enableJetifier=true
18 | org.gradle.unsafe.configuration-cache=true
19 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamYouDown/YouMusic/6cf58be04ea6d7f70085ff8569e3d59ca18d3356/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Oct 04 14:57:57 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/innertube/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/innertube/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("jvm")
3 | @Suppress("DSL_SCOPE_VIOLATION")
4 | alias(libs.plugins.kotlin.serialization)
5 | }
6 |
7 | kotlin {
8 | jvmToolchain(11)
9 | }
10 |
11 | dependencies {
12 | implementation(libs.ktor.client.core)
13 | implementation(libs.ktor.client.okhttp)
14 | implementation(libs.ktor.client.content.negotiation)
15 | implementation(libs.ktor.serialization.json)
16 | implementation(libs.ktor.client.encoding)
17 | implementation(libs.brotli)
18 | testImplementation(libs.junit)
19 | }
--------------------------------------------------------------------------------
/innertube/src/main/java/com/zionhuang/innertube/encoder/BrotliEncoder.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.innertube.encoder
2 |
3 | import io.ktor.client.plugins.compression.*
4 | import io.ktor.utils.io.*
5 | import io.ktor.utils.io.jvm.javaio.*
6 | import kotlinx.coroutines.CoroutineScope
7 | import org.brotli.dec.BrotliInputStream
8 |
9 | object BrotliEncoder : ContentEncoder {
10 | override val name: String = "br"
11 |
12 | override fun CoroutineScope.decode(source: ByteReadChannel): ByteReadChannel =
13 | BrotliInputStream(source.toInputStream()).toByteReadChannel()
14 |
15 | override fun CoroutineScope.encode(source: ByteReadChannel): ByteReadChannel =
16 | throw UnsupportedOperationException("Encode not implemented by the library yet.")
17 | }
18 |
19 | fun ContentEncoding.Config.brotli(quality: Float? = null) {
20 | customEncoder(BrotliEncoder, quality)
21 | }
--------------------------------------------------------------------------------
/innertube/src/main/java/com/zionhuang/innertube/models/AccountInfo.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.innertube.models
2 |
3 | data class AccountInfo(
4 | val name: String,
5 | val email: String?,
6 | val channelHandle: String?,
7 | )
8 |
--------------------------------------------------------------------------------
/innertube/src/main/java/com/zionhuang/innertube/models/AutomixPreviewVideoRenderer.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.innertube.models
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class AutomixPreviewVideoRenderer(
7 | val content: Content,
8 | ) {
9 | @Serializable
10 | data class Content(
11 | val automixPlaylistVideoRenderer: AutomixPlaylistVideoRenderer,
12 | ) {
13 | @Serializable
14 | data class AutomixPlaylistVideoRenderer(
15 | val navigationEndpoint: NavigationEndpoint,
16 | )
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/innertube/src/main/java/com/zionhuang/innertube/models/Badges.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.innertube.models
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Badges(
7 | val musicInlineBadgeRenderer: MusicInlineBadgeRenderer,
8 | ) {
9 | @Serializable
10 | data class MusicInlineBadgeRenderer(
11 | val icon: Icon,
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/innertube/src/main/java/com/zionhuang/innertube/models/Button.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.innertube.models
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Button(
7 | val buttonRenderer: ButtonRenderer,
8 | ) {
9 | @Serializable
10 | data class ButtonRenderer(
11 | val text: Runs,
12 | val navigationEndpoint: NavigationEndpoint?,
13 | val command: NavigationEndpoint?,
14 | val icon: Icon?,
15 | )
16 | }
--------------------------------------------------------------------------------
/innertube/src/main/java/com/zionhuang/innertube/models/Context.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.innertube.models
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Context(
7 | val client: Client,
8 | val thirdParty: ThirdParty? = null,
9 | ) {
10 | @Serializable
11 | data class Client(
12 | val clientName: String,
13 | val clientVersion: String,
14 | val gl: String,
15 | val hl: String,
16 | val visitorData: String?,
17 | )
18 |
19 | @Serializable
20 | data class ThirdParty(
21 | val embedUrl: String,
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/innertube/src/main/java/com/zionhuang/innertube/models/Continuation.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.innertube.models
2 |
3 | import kotlinx.serialization.ExperimentalSerializationApi
4 | import kotlinx.serialization.Serializable
5 | import kotlinx.serialization.json.JsonNames
6 |
7 | @OptIn(ExperimentalSerializationApi::class)
8 | @Serializable
9 | data class Continuation(
10 | @JsonNames("nextContinuationData", "nextRadioContinuationData")
11 | val nextContinuationData: NextContinuationData?,
12 | ) {
13 | @Serializable
14 | data class NextContinuationData(
15 | val continuation: String,
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/innertube/src/main/java/com/zionhuang/innertube/models/GridRenderer.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.innertube.models
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class GridRenderer(
7 | val header: Header?,
8 | val items: List- ,
9 | ) {
10 | @Serializable
11 | data class Header(
12 | val gridHeaderRenderer: GridHeaderRenderer,
13 | ) {
14 | @Serializable
15 | data class GridHeaderRenderer(
16 | val title: Runs,
17 | )
18 | }
19 |
20 | @Serializable
21 | data class Item(
22 | val musicNavigationButtonRenderer: MusicNavigationButtonRenderer?,
23 | val musicTwoRowItemRenderer: MusicTwoRowItemRenderer?,
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/innertube/src/main/java/com/zionhuang/innertube/models/Icon.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.innertube.models
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Icon(
7 | val iconType: String,
8 | )
--------------------------------------------------------------------------------
/innertube/src/main/java/com/zionhuang/innertube/models/Menu.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.innertube.models
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Menu(
7 | val menuRenderer: MenuRenderer,
8 | ) {
9 | @Serializable
10 | data class MenuRenderer(
11 | val items: List
- ,
12 | val topLevelButtons: List?,
13 | ) {
14 | @Serializable
15 | data class Item(
16 | val menuNavigationItemRenderer: MenuNavigationItemRenderer?,
17 | val menuServiceItemRenderer: MenuServiceItemRenderer?,
18 | ) {
19 | @Serializable
20 | data class MenuNavigationItemRenderer(
21 | val text: Runs,
22 | val icon: Icon,
23 | val navigationEndpoint: NavigationEndpoint,
24 | )
25 |
26 | @Serializable
27 | data class MenuServiceItemRenderer(
28 | val text: Runs,
29 | val icon: Icon,
30 | val serviceEndpoint: NavigationEndpoint,
31 | )
32 | }
33 |
34 | @Serializable
35 | data class TopLevelButton(
36 | val buttonRenderer: ButtonRenderer?,
37 | ) {
38 | @Serializable
39 | data class ButtonRenderer(
40 | val icon: Icon,
41 | val navigationEndpoint: NavigationEndpoint,
42 | )
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/innertube/src/main/java/com/zionhuang/innertube/models/MusicCardShelfRenderer.kt:
--------------------------------------------------------------------------------
1 | package com.zionhuang.innertube.models
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class MusicCardShelfRenderer(
7 | val title: Runs,
8 | val subtitle: Runs,
9 | val thumbnail: ThumbnailRenderer,
10 | val header: Header,
11 | val contents: List?,
12 | val buttons: List