Giter Club home page Giter Club logo

spmp's Introduction

日本語のREADMEへ


SpMp

A YouTube Music client with a focus on customisation of colours and song metadata, built using Compose Multiplatform for Android and desktop



Features

  • Edit song, artist, and playlist titles
  • Select multiple songs for batch actions on any screen
  • Customisable colour theming throughout the app
    • Use a colour from the current song's thumbnail
    • Or use a custom colour set per-song or globally
  • Display app UI and metadata like song titles in separate languages
  • In-app YouTube Music login
  • Display time-synchronised lyrics from KuGou and PetitLyrics
    • Timed lyrics are displayed in a toggleable bar above every app page
    • Furigana (readings) display above Japanese kanji within lyrics
  • Pin any song, playlist, album, or artist to the top of the main page
  • Customisable Discord rich presence
  • Easily insert songs at any position in the queue
And more (see the wiki for a full more complete list of features)

Screenshots

More Screenshots


Installation

Downloads for all platforms can be found on the repository's releases page.

Additional requirements on desktop

The Android version is also available on F-Droid. There is no difference between APKs available on GitHub and those on F-Droid.

Documentation

Documentation on advanced usage of SpMp including compilation instructions can be found at https://spmp.toastbits.dev/docs/

About the project

I started this project after I got tired of dealing with YouTube's official music app's lack of language features and customisation. I tried several alternatives, but all had at least a few issues that bugged me.

So instead of spending a few weeks learning the codebase of an existing project and contributing to it, I decided to spend almost a year (as of writing) creating my own solution.

Contributing

Pull requests, bug reports, and feature suggestions are welcome! This is my first Compose project (and also the largest project I've ever done in general) so there's probably plenty of room for improvement.

The app is currently being developed for both English and Japanese. Contributions for other languages would be greatly appreciated! Information about app localisation can be found here.

Thanks to

  • smlqrs: For designing the project's icon graphic
  • ytmusicapi: Used as a reference for the YouTube Music API
  • ExoVisualizer: Music visualiser implementation
  • ViMusic: A major inspiration for this project

Libraries (see all in shared/build.gradle.kts)

Disclaimer

This project and its contents are not affiliated with, funded, authorized, endorsed by, or in any way associated with YouTube, Google LLC or any of its affiliates and subsidiaries.

Any trademark, service mark, trade name, or other intellectual property rights used in this project are owned by the respective owners.

spmp's People

Contributors

jeelpatel231 avatar jellybrick avatar mikropsoft avatar mrgaboz avatar nichind avatar opusforlife2 avatar romarickc1 avatar ron159 avatar toasterofbread avatar waldist avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spmp's Issues

Show top bar lyrics on all pages

  • Home page
  • Now playing
  • Song queue
  • Library (non-inline)
  • Radio builder
  • Settings
  • YTM/Discord login
  • Playlist
  • Artist
  • View more
  • Search

Misc

  • Add proper back navigation to PlayerView (for artist page, playlist page, etc.)
  • Add option to treat singles (albums) as their song
  • Handle OOM (clear thumbnails, then mediaitems)
  • Add song like button to LongPressMenu
  • Add artist subscribe button to LongPressMenu

Playlists

  • Local playlists
  • YouTube account playlists
  • Add / remove / move songs
  • Set title and description
  • Set thumbnail (probably local-only?)
  • Set visibility (account only)
  • Multiselect support
  • Add / remove songs to a playlist from any long press menu context
  • Show playlists in a library page
  • Create from song long press menu
  • Create from library page button

Font style

Most of the things are working fine so far, thank you for that! If you can please add an option to change the application font, that would be great!

Radio builder

  • Edit radio after creation
  • Remember recent radios
  • 'Set as default' button for modifiers

Option to treat singles (albums) with a single item as that item

We don't know if a single actually contains just one item (or what the ID of that item is) until loaded.

  • Add 'treat playlist as this song' a long press action to the playlist view
  • Add an option to automatically apply 'treat playlist as this song' for albums with a single song upon opening them

Persistent queue

Save current queue songs, playing index, and playing time to disk, and load on startup.
Interval: every X seconds during playback and when queue changes.

Centralised thumbnail loading

Create a centralised system for loading MediaItem thumbnails using a thread pool, rather than individual items handling the loading
Should minimise UI stutter when loading many thumbnails at once

Bugfixes

  • Now playing queue reordering broken (possibly in release build only?)
  • Playback continues when app closed, but notification interaction breaks and app hangs when reopened
  • Colouring for the current song in the NowPlayingQueue is sometimes incorrect
  • Adding radio to queue causes network error. Sometimes it still works (but doesn't add the first song), and sometimes it doesn't work.
  • Lyrics with romaji do not display correctly
  • NowPlayingQueue current element sometimes doesn't use different colour until updated
  • NowPlaying overscroll produces transparent gap
  • NP thumbnail can desync (load 1, load 2, finish 2, finish1)
  • LongPressIconMenu divider default state is incorrect
  • Songs don't start playing automatically

Search page

Should be easier to rewrite from scratch than salvage the code I wrote 4-5 months ago

  • UI bar at bottom by default (config option for top?)
  • Recent searches (incl. search type w/ icon)?
  • Close keyboard on confirm

Server rework

The client-server model no longer seems necessary

Pros:

  • Code is easier to maintain/upgrade using Python
  • Server is portable and can be used in future projects (ex. desktop player)
  • Allows large requests to be performed ahead of client request (mainly the home feed)

Cons:

  • Ngrok adds latency to server requests and is a potential bottleneck / failure point
  • Integrated server may be slow (needs testing)

Options:

  • Continue using client-server model, use pre-emptive retrieval to increase speed
  • Keep server code, but use integrated server by default and improve integrated server speed
  • Abandon current server and convert to Kotlin. This could still be used in future projects fairly easily.

Multi-select MediaItems

  • Add a button to the song longpressmenu which begins multi-select mode
  • Toggle song selection on click
  • Bar with selected amount, cancel button, and interaction button(s)
  • Implement separately for NP queue and home page
  • Setting to determine whether multiselect state is ended after performing an action

Actions

  • Add / remove from queue
  • Pin
  • #51
  • Download

Implement for all pages with MediaItems

  • Main page
  • Now playing queue
  • Artist
  • Playlist
  • Search
  • Library
  • Radio builder preview

Library page

  • Playlists
  • Most listened songs of week, month, year, or all time (changeable)
  • Most listened artists

Stats page

Display statistics about:

  • Listen time (global and per-song)
  • Song ranking
  • Artist ranking

To be collected:

  • Song listen count
  • Song listen time

Settings:

  • Toggle stat collection
  • Song listen count criterial (ex. 20 seconds)

Settings

  • Audio quality
  • Theming mode
  • Download settings
  • Clear settings
  • App language
  • Song metadata language
  • Accessibility service
  • Invalidate cached song data on language change

Wait for lyrics to load before starting playback
Disable long press menu icon animation
Show MediaItem preview titles on multiple lines

  • Enable / disable furigana over top bar lyrics

  • Option to open lyrics sync menu on top bar click
  • Option for amount of text lines in square MediaItem preview

DataApi

- [x] Lyrics cache
[ ] API docs on index page
- [x] Batch song/artist info request support
- [ ] Prefs option to always use integrated server
- [ ] Prefs option to specify a custom server address

Queue and radio controls

Song long press menu

  • Long press action hints are incorrect for non-queue songs
    'Play after' long press action should only skip adding the first (selected) song if it was selected from the queue

APK at tag v0.1.1 identifies as 0.1.0

Just wanting to check if there's the correct APK attached, as it identifies as

package: name='com.toasterofbread.spmp' versionCode='2' versionName='0.1.0'

while the tag name suggests a versionName of 0.1.1, and the number of releases a versionCode of > 3 (it's the 4th release). Could you please replace it by the correct one? Thanks in advance!

BUG: App throws the following error on load

kotlin.NotImplementedError: Podcasts
at com.toasterofbread.spmp.api.YoutubeiBrowseResponse.getHeaderChips(Unknown Source:163)
at com.toasterofbread.spmp.api.HomeFeedKt$getHomeFeed$2.invokeSuspend(Unknown Source:637)
at com.toasterofbread.spmp.api.HomeFeedKt$getHomeFeed$2.invoke(Unknown Source:8)
at com.toasterofbread.spmp.api.HomeFeedKt$getHomeFeed$2.invoke(Unknown Source:4)
at androidx.appcompat.R$id.startUndispatchedOrReturn(Unknown Source:4)
at okio.Utf8.withContext(Unknown Source:71)
at com.toasterofbread.spmp.api.HomeFeedKt.getHomeFeed(Unknown Source:65)
at com.toasterofbread.spmp.ui.layout.mainpage.PlayerStateImplKt.loadFeedLayouts(Unknown Source:56)
at com.toasterofbread.spmp.ui.layout.mainpage.PlayerStateImplKt.access$loadFeedLayouts(Unknown Source:0)
at com.toasterofbread.spmp.ui.layout.mainpage.PlayerStateImpl$loadFeed$2.invokeSuspend(Unknown Source:173)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(Unknown Source:8)
at kotlinx.coroutines.DispatchedTask.run(Unknown Source:100)
at kotlinx.coroutines.internal.LimitedDispatcher.run(Unknown Source:12)
at kotlinx.coroutines.scheduling.TaskImpl.run(Unknown Source:2)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(Unknown Source:79)
Suppressed: coil.network.HttpException: [androidx.compose.ui.platform.MotionDurationScaleImpl@25b3fbd, androidx.compose.runtime.BroadcastFrameClock@773e9b2, StandaloneCoroutine{Cancelling}@f3aed03, AndroidUiDispatcher@3a00480]

Home recommendation feed

ytmusicapi home page request doesn't seem to work correctly

Show recently liked songs, subscribed artist new releases, frequently listened to songs, etc. The radio API can be used to build onto this. Ensure minimal repeats of songs/ artists.

Music download

  • Manual download
  • Automatically download frequently played songs (w/ config)
    - [ ] Switch to playing downloaded file on completion (instead of streaming)
    - [ ] Access to downloaded library when offline

MediaItem data editing

Interfaces for editing MediaItem details (title and description):

  • Song (NowPlaying)
  • Artist (ArtistPage)
  • Playlist (PlaylistPage)
  • All (LongPressMenu)
  • Add UI hint to artist/playlist page for title editing

Integrated server

App should request service to host server only if external server cannot be reached

Fine volume control

  • Toggleable indicator popup for volume adjustment
  • Active when screen off using root
  • Detect and/or intercept volume adjustment via Bluetooth
  • Option to increase system volume instead if app volume is at 100%


Modes:

  • Off
  • On when app open
  • On when mediasession focused

Memory usage

  • Unload MediaItems when no longer in use
  • Try alternative JSON libraries
  • Request protobuf or otherwise from YouTube instead of JSON
  • Handle low system memory notifications

java.lang.SecurityException: Session rejected the connection request

When creating a MediaController for the second time (as in when the service is already running) such as when opening the app after closing it, buildAsync() results in the above error.

I have no idea why this occurs. The Media3 demo works fine but I can't tell what the key difference in implementation is. I can work around this by using the first connection forever, not disconnecting it even when the app is closed, but this causes its own issues.

UI for podcasts

  • Alternate player menu for podcast playback (with speed control, forward and back buttons, etc.)
  • Alternate playlist page layout for podcasts

[SUGGESTION] add/use kugou.com as alternative lyric source

kugou.com has similar(i think) features as PetitLyrics as imo it has a wider source of lyrics than Petit and also has support for timed/synchronized lyrics.

Also, i think it's worth noting that this website/source is what ViMusic uses as well.

[Bug] NullPointerException on radio load while signed in

hi me again i just wanted to know if this error code is saying somthing importend i get it when i start playing a song but the app is still working normal

java.lang.NullPointerException
at com.toasterofbread.spmp.api.radio.GetSongRadioKt$getSongRadio$2.invokeSuspend(Unknown Source:515)
at com.toasterofbread.spmp.api.radio.GetSongRadioKt$getSongRadio$2.invoke(Unknown Source:8)
at com.toasterofbread.spmp.api.radio.GetSongRadioKt$getSongRadio$2.invoke(Unknown Source:4)
at androidx.core.R$dimen.startUndispatchedOrReturn(Unknown Source:4)
at coil.util.-Contexts.withContext(Unknown Source:71)
at com.toasterofbread.spmp.api.radio.GetSongRadioKt.getSongRadio(Unknown Source:60)
at com.toasterofbread.spmp.api.radio.RadioInstance.getInitialSongs-IoAF18A(Unknown Source:144)
at com.toasterofbread.spmp.api.radio.RadioInstance.access$getInitialSongs-IoAF18A(Unknown Source:0)
at com.toasterofbread.spmp.api.radio.RadioInstance$loadContinuation$1$1.invokeSuspend(Unknown Source:120)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(Unknown Source:8)
at kotlinx.coroutines.DispatchedTask.run(Unknown Source:100)
at kotlinx.coroutines.internal.LimitedDispatcher.run(Unknown Source:12)
at kotlinx.coroutines.scheduling.TaskImpl.run(Unknown Source:2)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(Unknown Source:79)
Suppressed: coil.network.HttpException: [StandaloneCoroutine{Cancelling}@fd6c69b, Dispatchers.IO]

Lyrics

  • Fix random(?) lyrics fetch fail
  • Tweak loading bar size
  • Re-implement timed lyrics highlighting for performance
  • Font size adjustment
  • Latency adjustment
    Add long-press function to overlay menu button
    Show top bar lyrics on all/most pages #56

Song related content

Implement YouTube Music related content menu as a standard MediaItemLayout list

  • Access through LongPressMenu
  • Access with NP OverlayMenu button?

Now Playing

Overlay menu:

  • Increase button touch area
  • Reduce animation duration

Queue

  • Easy swipe out of queue
  • Move shuffle and repeat to queue menu
  • Add padding to the bottom of the queue
  • Show playing radio and available options (filters)
    - [ ] Add radio undo #55
  • Radio loading indicator
  • Use accent for playing queue item instead of queue_colour.getContrasted()
  • Add reorder undo (how am I just realising this?)
  • Show time until/since song plays in long press menu

Other

  • Fine volume adjustment
  • Fix laggy open/close animation
    Animate song transition (thumbnail slide?)
  • Prefs option to open now playing when a song is played
  • Hide minimised NP until session starts
  • Disable artist ripple and add long press menu
  • Subtle loading indicator for thumbnails (pulsing dot?)
    [ ] Clear thumbnail on song change (until new one can load)

Reduce amount of overlay menu buttons

  • Lyrics: Keep
  • Download: Keep
  • Info: Move content in info menu to the main overlay menu
  • Colour: Keep
    Edit: Move to lyrics display

SpMp crashed at the first open, maybe from the system language

App Version: 0.1.2
Android Version: 13 QPR2 (SDK 33) - LineageOS 20
Device: Oneplus 9
System Language: Turkish (Turkey)

Logs are here:

kotlin.NotImplementedError: tr
	at com.toasterofbread.spmp.resources.uilocalisation.AmountStringsKt.parseYoutubeSubscribersString(Unknown Source:95)
	at com.toasterofbread.spmp.api.LoadMediaitemKt$processDefaultResponse$2.invokeSuspend(Unknown Source:929)
	at com.toasterofbread.spmp.api.LoadMediaitemKt$processDefaultResponse$2.invoke(Unknown Source:8)
	at com.toasterofbread.spmp.api.LoadMediaitemKt$processDefaultResponse$2.invoke(Unknown Source:4)
	at androidx.core.R$dimen.startUndispatchedOrReturn(Unknown Source:4)
	at coil.util.-Contexts.withContext(Unknown Source:71)
	at com.toasterofbread.spmp.api.LoadMediaitemKt.processDefaultResponse(Unknown Source:13)
	at com.toasterofbread.spmp.api.LoadMediaitemKt$loadMediaItemData$2.invokeSuspend(Unknown Source:241)
	at com.toasterofbread.spmp.api.LoadMediaitemKt$loadMediaItemData$2.invoke(Unknown Source:8)
	at com.toasterofbread.spmp.api.LoadMediaitemKt$loadMediaItemData$2.invoke(Unknown Source:4)
	at androidx.core.R$dimen.startUndispatchedOrReturn(Unknown Source:4)
	at coil.util.-Contexts.withContext(Unknown Source:71)
	at com.toasterofbread.spmp.api.LoadMediaitemKt.loadMediaItemData(Unknown Source:76)
	at com.toasterofbread.spmp.model.mediaitem.MediaItem$loadGeneralData$2.invokeSuspend(Unknown Source:121)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(Unknown Source:8)
	at kotlinx.coroutines.DispatchedTask.run(Unknown Source:100)
	at kotlinx.coroutines.internal.LimitedDispatcher.run(Unknown Source:12)
	at kotlinx.coroutines.scheduling.TaskImpl.run(Unknown Source:2)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(Unknown Source:79)
	Suppressed: coil.network.HttpException: [androidx.compose.ui.platform.MotionDurationScaleImpl@afa453a, androidx.compose.runtime.BroadcastFrameClock@28355eb, StandaloneCoroutine{Cancelling}@4db5f48, AndroidUiDispatcher@21fb1e1]

error Code after logging in and let feed load

kotlin.NotImplementedError: de
at com.toasterofbread.spmp.resources.uilocalisation.AmountStringsKt.parseYoutubeSubscribersString(Unknown Source:95)
at com.toasterofbread.spmp.api.LoadMediaitemKt$processDefaultResponse$2.invokeSuspend(Unknown Source:929)
at com.toasterofbread.spmp.api.LoadMediaitemKt$processDefaultResponse$2.invoke(Unknown Source:8)
at com.toasterofbread.spmp.api.LoadMediaitemKt$processDefaultResponse$2.invoke(Unknown Source:4)
at androidx.core.R$dimen.startUndispatchedOrReturn(Unknown Source:4)
at coil.util.-Contexts.withContext(Unknown Source:71)
at com.toasterofbread.spmp.api.LoadMediaitemKt.processDefaultResponse(Unknown Source:13)
at com.toasterofbread.spmp.api.LoadMediaitemKt$loadMediaItemData$2.invokeSuspend(Unknown Source:241)
at com.toasterofbread.spmp.api.LoadMediaitemKt$loadMediaItemData$2.invoke(Unknown Source:8)
at com.toasterofbread.spmp.api.LoadMediaitemKt$loadMediaItemData$2.invoke(Unknown Source:4)
at androidx.core.R$dimen.startUndispatchedOrReturn(Unknown Source:4)
at coil.util.-Contexts.withContext(Unknown Source:71)
at com.toasterofbread.spmp.api.LoadMediaitemKt.loadMediaItemData(Unknown Source:76)
at com.toasterofbread.spmp.model.mediaitem.MediaItem$loadGeneralData$2.invokeSuspend(Unknown Source:121)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(Unknown Source:8)
at kotlinx.coroutines.DispatchedTask.run(Unknown Source:100)
at kotlinx.coroutines.internal.LimitedDispatcher.run(Unknown Source:12)
at kotlinx.coroutines.scheduling.TaskImpl.run(Unknown Source:2)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(Unknown Source:79)
Suppressed: coil.network.HttpException: [androidx.compose.ui.platform.MotionDurationScaleImpl@5847aec, androidx.compose.runtime.BroadcastFrameClock@5646eb5, StandaloneCoroutine{Cancelling}@a7c794a, AndroidUiDispatcher@d3060bb]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.