Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
e891cb8
Implemented month, day subsorting, and split comma-separated genres
FlorentLM Mar 19, 2025
77418aa
refactoring of the requests construction
FlorentLM Mar 20, 2025
24137ca
SQL-based albums list querying
FlorentLM Mar 20, 2025
da8167c
SQL-based albums list querying
FlorentLM Mar 20, 2025
46bc23c
Added support for native ffmpeg binary
FlorentLM Mar 20, 2025
f764f2e
Continuing implementing paged queries in SQL, and refactored request …
FlorentLM Mar 20, 2025
e4a0b70
Added getAlbumInfo 1 and 2
FlorentLM Mar 20, 2025
7f0a448
Formatting consistency
FlorentLM Mar 20, 2025
c633a02
Slightly better genre formatter
FlorentLM Mar 20, 2025
58ce768
using url-safe version of base64 encode/decode
FlorentLM Mar 20, 2025
e46aa48
Added logo images
FlorentLM Mar 20, 2025
bafca22
Lil speed boost for cover art fetching
FlorentLM Mar 21, 2025
c316054
renamed 'id' for consistency and clarity
FlorentLM Mar 21, 2025
789eb76
refactored playlists.py like the rest
FlorentLM Mar 21, 2025
8208bd5
Cleanup
FlorentLM Mar 21, 2025
d64f974
Minor changes
FlorentLM Mar 22, 2025
1e68c81
Preparing support for playlists and smartplaylist plugins
FlorentLM Mar 22, 2025
0fa9655
Rewrote playlist for native beets (smart)playlists plugin support
FlorentLM Mar 22, 2025
bc927d9
Fix for resized images not showing up
FlorentLM Mar 22, 2025
5b1caaf
Added support for returning failed requests status
FlorentLM Mar 22, 2025
94aa533
Added support for returning failed requests status
FlorentLM Mar 22, 2025
6ea96ff
Added fallback to coverarchive.org if art not found
FlorentLM Mar 22, 2025
193e1e2
Small safety checks for missing art data
FlorentLM Mar 22, 2025
b1eef86
Added support for ffmpeg-python in the coverart functions
FlorentLM Mar 22, 2025
12be69e
Removed obnoxious errors capture in covertart
FlorentLM Mar 22, 2025
1034c70
Forgot to uncomment a debug comment
FlorentLM Mar 22, 2025
9800e69
Full getAlbumInfo(2) response
FlorentLM Mar 22, 2025
a0a9026
Updated missing-endpoints.md
FlorentLM Mar 22, 2025
ac80819
Minor naming changes for consistency
FlorentLM Mar 22, 2025
768b05f
Finished porting search, users to the new response format
FlorentLM Mar 22, 2025
9bada76
Cleaned up functions that are not needed anymore
FlorentLM Mar 22, 2025
2b399b6
Formatting, cleaning, commenting
FlorentLM Mar 22, 2025
f6bb2b1
404 when stream item not found
FlorentLM Mar 22, 2025
7530674
Removed unnecessary for loops and sorting calls
FlorentLM Mar 23, 2025
623292a
Fixed small SQL syntax error
FlorentLM Mar 23, 2025
ec4b1dd
Added missing import
FlorentLM Mar 23, 2025
fc9f0c3
Adding missing fields to artist mapping
FlorentLM Mar 23, 2025
1c3d073
Added missing fields to artist mapping
FlorentLM Mar 23, 2025
856fc0a
Added missing fields to artist mapping
FlorentLM Mar 23, 2025
d6bcfbb
Renamed a utility function
FlorentLM Mar 23, 2025
702490a
Some more small performance improvements for search endpoint
FlorentLM Mar 23, 2025
79ab9f6
Forgot a debug print, oops
FlorentLM Mar 23, 2025
200c74d
smol fix for album type processing
FlorentLM Mar 23, 2025
7cce613
Refactor of the mapping functions and inclusion of more fields, for a…
FlorentLM Mar 23, 2025
84e755a
Various small modifications
FlorentLM Mar 24, 2025
87e9603
Proper XML formatting
FlorentLM Mar 25, 2025
44c4139
Testing artist images
FlorentLM Mar 25, 2025
2f45499
Musicbrainz query (maybe for 'version' album tag)
FlorentLM Mar 25, 2025
f6300be
Fixed botched XML
FlorentLM Mar 25, 2025
f860d6f
Fixed botched XML
FlorentLM Mar 25, 2025
cfbcfa5
Fixed broken XML response
FlorentLM Mar 25, 2025
16b51bf
oopsy
FlorentLM Mar 25, 2025
917412e
had wrong name for arist's album list attribute...
FlorentLM Mar 25, 2025
7d8b53d
clearer function names
FlorentLM Mar 25, 2025
7120ab1
Merge branch 'dev'
FlorentLM Mar 25, 2025
26ead8f
removed unused line
FlorentLM Mar 25, 2025
a9f054f
Added support for artist images via Deezer's API
FlorentLM Mar 25, 2025
43e2ec1
Added new config options to control artist photos fetching
FlorentLM Mar 26, 2025
49f74d8
Updated README.md
FlorentLM Mar 26, 2025
11bdfd4
Fixed an issue with playlists and XML format
FlorentLM Mar 26, 2025
8d0400a
Small things
FlorentLM Mar 26, 2025
cd38ce1
Added getArtistInfo2 endpoint
FlorentLM Mar 26, 2025
cda3dfa
unified the functions with versions 1, 2, 3
FlorentLM Mar 26, 2025
f852f12
Added getSimilarSongs 1 and 2
FlorentLM Mar 27, 2025
0c205e9
Added getSimilarSongs 1 and 2
FlorentLM Mar 27, 2025
52fc157
Removed endpoint_to_tag function
FlorentLM Mar 27, 2025
17b2b42
Removed endpoint_to_tag function
FlorentLM Mar 27, 2025
936c967
Removed endpoint_to_tag function
FlorentLM Mar 27, 2025
3d3e1b9
Added getOpenSubsonicExtensions endpoint (empty)
FlorentLM Mar 27, 2025
5e085b5
groundwork for supporting user authentication
FlorentLM Mar 27, 2025
f373268
Started adding the proper errors
FlorentLM Mar 28, 2025
10cf463
Serving Beetstream icon for root folder getCoverArt requests
FlorentLM Mar 28, 2025
06df827
Fixed quotes issue for Python <3.12
FlorentLM Mar 28, 2025
e79cca9
Cleaner playlists IDs in preparation for adding endpoints
FlorentLM Mar 29, 2025
1601cee
Added createPlaylist endpoint
FlorentLM Apr 6, 2025
63a6a5d
Added deletePlaylist endpoint
FlorentLM Apr 6, 2025
ae3739c
Added deletePlaylist endpoint
FlorentLM Apr 6, 2025
554122d
Added transcode offset extension
FlorentLM Apr 6, 2025
41979e8
oopsy
FlorentLM Apr 6, 2025
44c24b7
cleaner check for request boolean
FlorentLM Apr 6, 2025
829a50f
Preparing for SQLite storage of beetstream's own data (users, etc)
FlorentLM Apr 6, 2025
7426fa7
Added more stuff to prepare for SQLite storage of beetstream's data
FlorentLM Apr 7, 2025
d38b071
Renamed project
FlorentLM Aug 20, 2025
cb99833
Merge branch 'dev'
FlorentLM Aug 20, 2025
8c0e7e5
Updated install instructions to follow Beets.io's preferred method
FlorentLM Aug 20, 2025
a2a5751
Updated gitignore
FlorentLM Aug 20, 2025
ca036be
Removed .idea folder from github
FlorentLM Aug 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,6 @@ dmypy.json

# Pyre type checker
.pyre/

Jetbrains stuff
.idea/
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2020 Sacha Bron
Copyright (c) 2025 Florent Le Moël

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
29 changes: 29 additions & 0 deletions NOTICE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Dependency Licenses

This fork includes code from the parent project. The original license and copyright notices are preserved below.

---

## beetsream

MIT License

Copyright (c) 2020 Sacha Bron

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
243 changes: 191 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,226 @@
# Beetstream
<br />

Beetstream is a [Beets.io](https://beets.io) plugin that exposes [SubSonic API endpoints](http://www.subsonic.org/pages/api.jsp), allowing you to stream your music everywhere.
<div align="center">

## Motivation
<a href="https://github.com/FlorentLM/BeetstreamNext">
<img src="beetstreamnext.svg" alt="Logo" width="128" height="128">
</a>

I personally use Beets to manage my music library on a Raspberry Pi but when I was looking for a way to stream it to my phone I couldn't find any comfortable, suitable and free options.
I tried [AirSonic](https://airsonic.github.io) and [SubSonic](http://www.subsonic.org), [Plex](https://www.plex.tv) and some other tools but a lot of these solutions want to manage the library as they need (but I prefer Beets) and AirSonic/SubSonic were quite slow and CPU intensive and seemed to have a lot of overhead just to browse albums and send music files. Thus said, SubSonic APIs are good and implemented by a lot of different [clients](#supported-clients), so I decided to re-implement the server side but based on Beets database (and some piece of code).
<h3 align="center">BeetstreamNext</h3>
<p>
A modern, feature-rich OpenSubsonic API server for your Beets.io music library.
<br/>

## Install & Run
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Requires Python 3.8 or newer.
</p>
</div>

1) First of all, you need to [install Beets](https://beets.readthedocs.io/en/stable/guides/main.html):

2) Install the dependancies with:
BeetstreamNext is a fork of Beetstream, a [Beets.io](https://beets.io) plugin that exposes [OpenSubsonic API endpoints](https://opensubsonic.netlify.app/docs/opensubsonic-api/), allowing you to stream your Beets library.

```
$ pip install beetstream
```
I started implementing new features to Beetstream but ended up rewriting a significant part of it, so I figured it'd make more sense to keep it as a distinct project.
The goal is to cover all of the modern OpenSubsonic API, with some additions. I'm getting there :)

3) Enable the plugin for Beets in your config file `~/.config/beets/config.yaml`:
```yaml
plugins: beetstream
```
Personally, I use Beets to manage my music library but I don't like to write metadata to the files. So with this, I can have the best of both worlds.

## Key Features

- **Extensive API coverage**: Implements a wide range of OpenSubsonic endpoints, soon to cover everything.
- **On-the-fly transcoding**: As in the original project, this uses **FFmpeg** to transcode audio in real-time to your desired bitrate. Direct streaming is also supported.
- **Full cover art support**: Serves artwork from multiple sources:
- Local album art path from your Beets library
- Fallback to the [Cover Art Archive](https://coverartarchive.org/) using MusicBrainz IDs
- Fetches and caches artist images from the [Deezer API](https://developers.deezer.com/api)
- Extracts embedded artwork directly from media files
- **Dynamic Playlist management**:
- Reads `.m3u` playlists from specified directories
- Supports creating and deleting playlists directly through the API
- Integrates with Beets' native `playlist` and `smartplaylist` plugins
- **Rich metadata integration**: Fetches optional artist info like biographies, top tracks, and similar artists from the [Last.fm API](https://www.last.fm/api)

- **COMING SOON: Multi-user support**: Dedicated SQLite database to manage users, ratings, bookmarks, and starred content (full multi-user endpoints are in development)
- **COMING SOON: Actual authentication**: User credentials, encryption at rest

## Installation

Requires Python 3.9.1 or newer.

> [!NOTE]
> BeetstreamNext is not yet available on PyPI. Installation currently requires cloning the source code from GitHub.

[//]: # (### For Users)

1. **Install Beets**: If you haven't already, [install and configure Beets](https://beets.readthedocs.io/en/stable/guides/main.html). You will also need `git` installed on your system.

2. **Clone the BeetstreamNext Repository**:
```bash
git clone https://github.com/FlorentLM/BeetstreamNext.git
cd BeetstreamNext
```

3. **Install the Plugin**:
From inside the `BeetstreamNext` directory, run the installation command. You can use `pip` or any modern installer like `uv`.
```bash
pip install .
```
For transcoding, you will also need to have **FFmpeg** installed and available in your system's PATH.

4. **Enable the Plugin**: Add `beetstreamnext` to the `plugins` section of your Beets config file (`~/.config/beets/config.yaml`):
```yaml
plugins: beetstreamnext
```

5. **Run the Server**:
```bash
beet beetstreamnext
```

[//]: # (### For Developers)

[//]: # ()
[//]: # (If you want to contribute to BeetstreamNext, the setup process uses [Poetry]&#40;https://python-poetry.org/&#41; for dependency management.)

[//]: # ()
[//]: # (1. **Clone the repository**:)

[//]: # ( ```bash)

[//]: # ( git clone https://github.com/FlorentLM/BeetstreamNext.git)

[//]: # ( cd BeetstreamNext)

[//]: # ( ```)

[//]: # ()
[//]: # (2. **Install dependencies**:)

[//]: # ( This will create a virtual environment and install all required packages for development and testing.)

[//]: # ( ```bash)

[//]: # ( poetry install)

4) **Optional** You can change the host and port in your config file `~/.config/beets/config.yaml`.
You can also chose to never re-encode files even if the clients asks for it with the option `never_transcode: True`. This can be useful if you have a weak CPU or a lot of clients.
[//]: # ( ```)

[//]: # ()
[//]: # (3. **Activate the environment**:)

[//]: # ( ```bash)

[//]: # ( poetry shell)

[//]: # ( ```)

[//]: # ()
[//]: # (4. **Run the server**:)

[//]: # ( From inside the activated shell, you can run the plugin directly.)

[//]: # ( ```bash)

[//]: # ( beet beetstreamnext)

[//]: # ( ```)

[//]: # (See the [**CONTRIBUTING.md**]&#40;./CONTRIBUTING.md&#41; file for more details on running tests and submitting changes.)

## Configuration

You can configure BeetstreamNext in your Beets `config.yaml` file. Here are the available options with their default values:

Here are the default values:
```yaml
beetstream:
beetstreamnext:
host: 0.0.0.0
port: 8080
never_transcode: False
never_transcode: False # Never re-encode files, even if a client requests it.

# Artist Image Handling
fetch_artists_images: True # Fetch artist photos from Deezer when a client requests them.
save_artists_images: True # Save fetched artist photos to their respective folders.

# Playlist Configuration
playlist_dirs: # A list of directories to scan for .m3u playlists.
- '/path/to/my/playlists'
- '/another/path/for/playlists'
```

5) Run with:
```
$ beet beetstream
```
### Environment Variables

Some features require API keys or secrets, which should be configured as environment variables for security. You can place these in a `.env` file in the directory where you run the `beet` command.

[//]: # (- **`BEETSTREAMNEXT_KEY` &#40;Required for User features&#41;**: A unique encryption key for storing user data. You can generate one by running this Python command:)

[//]: # ( ```bash)

[//]: # ( python -c "from cryptography.fernet import Fernet; print&#40;Fernet.generate_key&#40;&#41;.decode&#40;&#41;&#41;")

[//]: # ( ```)

- **`LASTFM_API_KEY` (Optional)**: Your API key from Last.fm to enable fetching artist bios, top tracks, and similar songs.
```
LASTFM_API_KEY="your_lastfm_api_key_here"
```

## Clients Configuration
## Client Configuration

### Authentication

There is currently no security whatsoever. You can put whatever user and password you want in your favorite app.
The backend for a full, multi-user authentication system is under active development. For now, **you can use any username and password** in your client to connect to the server. The server will respond as a default "admin" user.

### Server and Port

Currently runs on port `8080`. i.e: `https://192.168.1.10:8080`. You can configure it in `~/.config/beets/config.yaml`. Defaults are:
```yaml
beetstream:
host: 0.0.0.0
port: 8080
```
Connect your client to the server's IP address and the configured port (default is `8080`). For example: `http://192.168.1.10:8080`.

## Supported Clients

All clients below are working with this server. By "working", it means one can use most of the features, browse library and most importantly play music!
BeetstreamNext aims for broad compatibility with any Subsonic-compliant player. It has been successfully tested with the following clients:

#### Android
- [Symfonium](https://symfonium.app/)
- [Tempo](https://github.com/CappielloAntonio/tempo)
- [SubTune](https://github.com/TaylorKunZhang/SubTune)
- [substreamer](https://substreamerapp.com/)
- [Ultrasonic](https://gitlab.com/ultrasonic/ultrasonic)

#### Desktop
- [Supersonic](https://github.com/dweymouth/supersonic)

### Android
## Roadmap

- [Subsonic](https://play.google.com/store/apps/details?id=net.sourceforge.subsonic.androidapp) (official app)
- [DSub](https://play.google.com/store/apps/details?id=github.daneren2005.dsub)
- [Audinaut](https://play.google.com/store/apps/details?id=net.nullsum.audinaut)
- [Ultrasonic](https://play.google.com/store/apps/details?id=org.moire.ultrasonic)
- [GoSONIC](https://play.google.com/store/apps/details?id=com.readysteadygosoftware.gosonic)
- [Subtracks](https://play.google.com/store/apps/details?id=com.subtracks)
- [Music Stash](https://play.google.com/store/apps/details?id=com.ghenry22.mymusicstash)
- [substreamer](https://play.google.com/store/apps/details?id=com.ghenry22.substream2)
This project is under active development. Here is a high-level overview of planned features and missing endpoints.

### Desktop
#### Core Features (In Progress)
- [ ] Finalize BeetstreamNext's database storage for multi-user data
- [ ] Implement a complete, secure authentication and user management system
- [ ] Create a Docker image

- [Clementine](https://www.clementine-player.org)
#### Missing API Endpoints
These endpoints require the database and user management systems to be fully operational:
- `getUsers`, `createUser`, `updateUser`, `deleteUser`
- `changePassword`
- `updatePlaylist`
- `getAvatar`
- `star`, `unstar`, `setRating`
- `getBookmarks`, `createBookmark`, `deleteBookmark`

### Web
#### Future API Endpoints
- `getLyrics`
- `getPlayQueue`, `savePlayQueue`
- `getScanStatus`, `startScan`

- [Jamstash](http://jamstash.com) ([Chrome App](https://chrome.google.com/webstore/detail/jamstash/jccdpflnecheidefpofmlblgebobbloc))
- [SubFire](http://subfireplayer.net)
#### Video, Radio & Podcast Endpoints
These are lower priority but may be considered in the future.
- `getVideos`, `getVideoInfo`, `hls`, `getCaptions`
- `getPodcasts`, `getNewestPodcasts`, etc.
- `getInternetRadioStations` and related endpoints.

_Currently supports a subset of API v1.16.1, avaiable as Json, Jsonp and XML._
#### Social Endpoints
- `getNowPlaying`
- `getShares`, `createShare`, `updateShare`, `deleteShare`
- `jukeboxControl`
- `getChatMessages`, `addChatMessage`

## Contributing
## License

There is still some [missing endpoints](missing-endpoints.md) and `TODO` in the code.
Feel free to create some PR!
This project is licensed under the MIT License. See the `LICENSE` file for details.
Loading