Skip to content

Commit

Permalink
🎵 Beets Store.
Browse files Browse the repository at this point in the history
A plugin for the music geek's media organizer.
  • Loading branch information
tschaefer committed Dec 20, 2023
0 parents commit 0f06675
Show file tree
Hide file tree
Showing 48 changed files with 4,282 additions and 0 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/docker-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: docker-release
on:
push:
branches:
- 'main'

jobs:
build:
name: Buid and push Docker image to GitHub Container registry
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Checkout the repository
uses: actions/checkout@v3

- name: Docker Setup Buildx
uses: docker/setup-buildx-action@v2.2.1

- name: Docker Login
uses: docker/login-action@v2.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push Docker images
uses: docker/build-push-action@v3.2.0
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
REFERENCE: ${{ github.ref == 'refs/heads/main' && 'latest' || github.ref_name }}
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.REFERENCE }}
59 changes: 59 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#### joe made this: https://goel.io/joe

#####=== Python ===#####

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Sphinx documentation
docs/_build/

# PyBuilder
target/

18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM docker.io/bitnami/minideb:latest
LABEL org.opencontainers.image.source="https://github.com/tschaefer/beets-store"

RUN install_packages pipx curl
COPY rootfs /

RUN bash -c ' \
PIPX_HOME=/opt/beets/.local/pipx \
PIPX_BIN_DIR=$PIPX_HOME/bin \
pipx install --include-deps https://github.com/tschaefer/beets-store/archive/refs/heads/develop.zip \
'
VOLUME ["/opt/beets/media/music"]
EXPOSE 3000

USER root
WORKDIR /opt/beets

ENTRYPOINT ["/opt/beets/entrypoint"]
27 changes: 27 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Copyright (c) 2015, Tobias Schäfer
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name of [project] nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
recursive-include beetsplug/store/templates *
recursive-include beetsplug/store/static *
90 changes: 90 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Beets Store

A plugin for the music geek's media organizer.

## Introduction

*Beets Store* is a web frontend for your music library organized by
[beets](http://beets.radbox.org/).

* Play the music in your browser.
* Optional scrobble the played music info to [LastFM](https://www.last.fm)
* Download the music files and entire albums (A zipped directory with the music
files and the album cover image.)

## Installation

Install required services.

$ apt install redis

Install package and scripts.

$ git clone https://github.com/tschaefer/beets-store
$ cd beets-store
$ pipx install --include-deps .

## Usage

Add plugin settings to beets configuration file.

```
store:
host: "::1"
port: 8080
zipdir: /tmp/beets/store/zip
lastfm:
api_key: API_KEY
secret_key: SECRET_KEY
```
The `lastfm` settings are optional. If you don't want to scrobble leave the
settings out.

Example beets [configuration file](https://gist.github.com/tschaefer/daa09959eb7272715800#gistcomment-1684418)

Import audio files.

$ beet import /music

Fetch cover art.

The album art image must be stored as `cover.jpg` alongside the music files
for an album. For optimal display all the images should have an equal width and
height of at least 300x300 px.

$ beet fetchart

Start job queue worker.

The job queue is used to create album zip files for the download.

$ rq worker

Start the web service.

$ beet store

### Docker

Configure environment file.

Set `BEETS_MUSIC_VOLUME` in the environment file `docker-compose.env`.

For overriding the configuration file and persist the database enable and set
the proper settings in the enviroment and compose files.

Start the service.

$ docker compose --env-file docker-compose.env up

## License

[BSD 3-Clause “New” or “Revised” License](https://choosealicense.com/licenses/bsd-3-clause/)

### Further thirdparty license

* [beetsplug/store/static/img/404.jpg](https://pngimg.com/image/101094)

## Is it any good?

[Yes](https://news.ycombinator.com/item?id=3067434)
7 changes: 7 additions & 0 deletions beetsplug/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-

"""A namespace package for beets plugins."""

# Make this a namespace package.
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
56 changes: 56 additions & 0 deletions beetsplug/store/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-

"""Main module to control the beets store."""

import beets

from beets.plugins import BeetsPlugin
from beets.ui import Subcommand

from .app import App

import waitress


class Store(BeetsPlugin):
def __init__(self):
super(Store, self).__init__()
self.config.add(
{
"host": u"",
"port": 8080,
}
)

def parse(self, args):
args = beets.ui.decargs(args)
if args:
self.config["host"] = args.pop(0)
if args:
self.config["port"] = int(args.pop(0))

def func(self, lib, opts, args):
self.parse(args)
app = App(self.config, lib, beets.config["directory"].get())

if opts.wsgi:
listen = "{}:{}".format(
self.config["host"].get(), self.config["port"].get(int)
)
return waitress.serve(app.app, listen=listen)

app.run()

def commands(self):
cmd = Subcommand("store", help="start the Store web interface")
cmd.parser.add_option(
"-w",
"--wsgi",
dest="wsgi",
action="store_true",
default=False,
help="start the store as a WSGI app",
)

cmd.func = self.func
return [cmd]
Loading

0 comments on commit 0f06675

Please sign in to comment.