Skip to content

ubuntu-flutter-community/musicpod

Repository files navigation

Musicpod

Music, Radio, Television and Podcast player for Linux Desktop, MacOS, Windows and Android made with Flutter.

Install for Linux Desktop:

Get it from the Snap Store

Install For MacOS & Windows:

Release Page

Android release is WIP!

Dark Light

Credits

Thanks to all the MPV contributors!

Thank you @amugofjava for creating the very easy to use and reliable podcast_search!

Thanks @alexmercerind for the super performant Mediakit library and mpris_service dart implementation!

Thank you @KRTirtho for the very easy to use smtc_windows package!

Thank you @tomassasovsky for the dart implementation of radiobrowser-api!

Thank you @ClementBeal for the super fast, pure dart Audio Metadata Reader!

Thank you @escamoteur for creating get_it and watch_it, which made my application faster and the source code cleaner!

MusicPod Level 1

  • play local audio files
  • filter local files
  • set root directory
  • create and manage playlists
  • play internet radio streams
  • browse for radio stations
  • play podcasts
  • search for podcasts
  • load podcast charts
  • filter podcasts by country
  • filter podcasts by genre
  • save playlists
  • save liked songs
  • save settings on disk
  • notify when a new episode of your subscribed podcasts is available

MusicPod Level 2

  • Video Podcasts (#71)
  • Play TV Stations found on radiobrowser
  • Chromecast Support (#91)
  • streaming provider agnostic sharing links
  • option to download podcasts (#240)
  • reduced memory allocation
  • WebDav support (#248)
  • upnp/dlna support (#248)

Supported operating systems and package formats

  • Ubuntu Desktop
  • Windows Support
    • Windows Store
    • Exe
  • Android Support (Media Controls are WIP)
    • PlayStore
  • MacOs Support
    • Apple?Store?
    • DMG
  • iOS Support
    • AppStore

Contributing

Contributions are highly welcome. Especially translations. Please fork MusicPod to your GitHub namespace, clone it to your computer, create a branch named by yourself, commit your changes to your local branch, push them to your fork and then make a pull request from your fork to this repository. I recommend the vscode extension GitHub Pull Requests especially for people new to Git and GitHub.

Translations

For translations into your language change the corresponding app_xx.arb file where xx is the language code of your language in lower case. If the file does not exist yet please create it and copy the whole content of app_en.arb into it and change only the values to your translation but leave the keys untouched. The vscode extension arb editor by Google is highly recommended to avoid arb syntax errors. Also recommended is the Google Translate Extension.

Code contributions

If you find any error please feel free to report it as an issue and describe it as good as you can. If you want to contribute code, please create an issue first.

Testing

Test mocks are generated with Mockito. You need to run the build_runner command in order to re-generate mocks, in case you changed the signatures of service methods.

dart run build_runner build

Boring developer things

Under the flutter hood

MusicPod is basically a fancy front-end for MPV! Without it it would still look nice, but it wouldn't play any media :D!

The app, the player and each page have their own set of widgets, 1 view model and 1 service. There are additional view models for downloads or a service for all external path things but that's it.

Since all views need access to each other all the time, disposing the view models all the time makes no sense and is CPU intensive for no need so all services and view models are registered as singletons before the flutter tree is created.

Important services are also initialized once before the Flutter runApp call, the view models are initialized when the view is accessed but most of the internal calls are skipped when the views are accessed again after that.

flowchart LR

classDef view fill:#0e84207d
classDef viewmodel fill:#e9542080
classDef model fill:#77216f80

View["`
  **View**
  (Widgets)
`"]:::view--watch-->ViewModel["`
  **ViewModel**
  (ChangeNotifier)
`"]:::viewmodel--listen-->Model["`
  **(Domain) Model**
  (Service)
`"]:::model

ViewModel--notify-->View
Model--stream.add-->ViewModel

Dependency choices, dependency injection and state management

Regarding the packages to implement this architecture I've had quite a journey from provider to riverpod with get_it.

I found my personal favorite solution with get_it plus its watch_it extension because this fits the need of this application the most without being too invasive into the API of the flutter widget tree.

This way all layers are clearly separated and easy to follow, even if this brings a little bit of boilerplate code.

I am a big fan of the KISS principle (keep it simple, stupid), so when it comes to organizing software source code and choosing architectural patterns simplicity is a big goal for me.

Though performance is the biggest goal, especially for flutter apps on the desktop that compete against toolkits that are so slim and performant they could run on a toaster (exaggeration), so if simple things perform badly, I am willing to switch to more complicated approaches ;)

Persistence

For persisting both setting and application state I've chosen a home cooked solution inside the LibraryService and SettingsService that writes json files to disk. It is simple and fast and sufficient for the needs of this application. Eventually at some point it might make sense to switch to a real database though :).