Skip to content

Commit

Permalink
Merge pull request blackcandy-org#228 from blackcandy-org/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
aidewoode authored Nov 30, 2022
2 parents 85d128a + 10fb16d commit 4ab5e08
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 149 deletions.
224 changes: 185 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,186 @@
<p align='center'>
<img alt='Black candy logo' width='200' src='https://raw.githubusercontent.com/aidewoode/black_candy/master/app/assets/images/logo.svg'>
<img alt='Black Candy logo' width='200' src='https://raw.githubusercontent.com/blackcandy-org/black_candy/master/app/assets/images/logo.svg'>
</p>

# Black Candy
[![CI](https://github.com/aidewoode/black_candy/actions/workflows/ci.yml/badge.svg)](https://github.com/aidewoode/black_candy/actions/workflows/ci.yml)
[![CI](https://github.com/blackcandy-org/black_candy/actions/workflows/ci.yml/badge.svg)](https://github.com/blackcandy-org/black_candy/actions/workflows/ci.yml)
[![Coverage Status](https://coveralls.io/repos/github/blackcandy-org/black_candy/badge.svg?branch=master)](https://coveralls.io/github/blackcandy-org/black_candy?branch=master)
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
![Docker Pulls](https://img.shields.io/docker/pulls/blackcandy/blackcandy)

Black candy is a self hosted music streaming server built with [Rails](https://rubyonrails.org) and [Hotwire](https://hotwired.dev). The goal of the project is to create a real personal music center.
Black Candy is a self hosted music streaming server built with [Rails](https://rubyonrails.org) and [Hotwire](https://hotwired.dev). The goal of the project is to create a real personal music center.

## Screenshot
![screenshot theme dark](https://raw.githubusercontent.com/aidewoode/black_candy/master/screenshots/screenshot_theme_dark.png)
![screenshot theme dark](https://raw.githubusercontent.com/blackcandy-org/black_candy/master/screenshots/screenshot_theme_dark.png)

![screenshot theme light](https://raw.githubusercontent.com/aidewoode/black_candy/master/screenshots/screenshot_theme_light.png)
![screenshot theme light](https://raw.githubusercontent.com/blackcandy-org/black_candy/master/screenshots/screenshot_theme_light.png)

## Installation
> ⚠️ **Note:** This installation instruction is for edge version, which means the docker image is build base on master branch. Because upcoming major version of Black Candy is going to have a lot of infrastructure changes. So the installation process will have a lot of difference.
> If you are looking for installation instruction for latest stable version, please visit [here](https://github.com/blackcandy-org/black_candy/blob/7f9202bd8a9777d439e95eabd0654e9b4a336be9/README.md).
Black Candy use docker image to install easily. You can simply run Black Candy like this.

## Getting started
```shell
docker run blackcandy/blackcandy:edge
```

Black candy use docker for simplify deployment, development and test process. So you should install docker and docker-compose first.
That's all. Now, you can use initial admin user to login (email: [email protected], password: foobar).

Black candy support mp3, m4a, ogg, oga, opus, flac, wma and wav formats now.

## Installation
## Mobile App
Black Candy now has an iOS app in beta. You can visit [here](https://github.com/blackcandy-org/black_candy_ios) and join TestFlight to give it a try. Because this iOS app still in beta, you need use the edge version of Black Candy.

Black candy has built [docker images](https://hub.docker.com/r/blackcandy/blackcandy). You can use docker compose to run all services.
## Configuration

First, you should ensure your music files stored under "/media_data"
### Port Mapping

Then run:
Black Candy exports the 3000 port. If you want to be able to access it from the host. You can add `-p 3000:3000` to the arguments of docker run command and then access either http://localhost:3000 or http://host-ip:3000 in a browser.

```shell
$ curl https://raw.githubusercontent.com/blackcandy-org/black_candy/v2.1.1/docker-compose.yml > docker-compose.yml
$ docker-compose up -d
docker run -p 3000:3000 blackcandy/blackcandy:edge
```

### Media Files Mounts

You can mount media files from host to container and use `MEDIA_PATH` environment variable to set the media path for black candy.

```shell
docker run -v /media_data:/media_data -e MEDIA_PATH=/media_data blackcandy/blackcandy:edge
```

### Use PostgreSQL As Database

Black Candy use SQLite as database by default. Because SQLite can simplify the process of installation, and it's an ideal choice for self hosted small server. If you think SQLite is not enough or you are using some cloud service like heroku to host Black Candy, you can also use PostgreSQL as database.

```shell
docker run -e DATABASE_ADAPTER=postgresql -e DATABASE_URL=postgresql://yourdatabaseurl blackcandy/blackcandy:edge
```

### How to Persist Data

There are two parts of data need to persist in Black Candy. First it's the data from database which store in `/app/db/production.sqlite3`, second it's the data from the asset of media files which store in `/app/public/uploads`.

```shell
touch production.sqlite3

docker run -v ./production.sqlite3:/app/db/production.sqlite3 -v ./uploads_data:/app/public/uploads blackcandy/blackcandy:edge
```

### Enhance With Redis

By default, Black Candy use async adapter for background job and WebSockets, and use file storage for cache. It maybe not have problem when your music library isn't large or doesn't have many users to use it. But you can use Redis to enhance the experience for Black Candy.

When you have set the `REDIS_URL` environment variable, black candy will use Sidekiq for background job, Redis adapter for WebSockets and use Redis to store cache. In another way, you can also use `REDIS_SIDEKIQ_URL`, `REDIS_CABLE_URL`, and `REDIS_CACHE_URL` to set those service separately.

```shell
docker run -e REDIS_URL=redis://yourredisurl blackcandy/blackcandy:edge
```
That's all. Now, you can use initial admin user to login (email: [email protected], password: foobar).

You can also change the `docker-compose.yml` for your own needs.
### Nginx To Send File

Black Candy supports use Nginx to delivery audio file to client. It's a more effective way than handle by Black Candy backend. And Black Candy docker image are also ready for [nginx-proxy](https://github.com/nginx-proxy/nginx-proxy), which means you can setup a Nginx proxy for Black Candy easily. I recommend you use [nginx-proxy](https://github.com/nginx-proxy/nginx-proxy) with Black Candy.

You can use docker-compose to setup those services. The docker-compose.yml file looks like this:

```yaml
version: '3'

services:
nginx-proxy:
image: nginxproxy/nginx-proxy
ports:
- "80:80"
volumes:
- ./blackcandy.local:/etc/nginx/vhost.d/blackcandy.local:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
- /media_data:/media_data # Keep the path of media files in container same as blackcandy container.

app:
image: blackcandy/blackcandy:edge
volumes:
- ./log:/app/log
- ./production.sqlite3:/app/db/production.sqlite3
- ./uploads_data:/app/public/uploads
- /media_data:/media_data
environment:
VIRTUAL_HOST: blackcandy.local
MEDIA_PATH: /media_data
NGINX_SENDFILE: "true" # Don't foreget to set `NGINX_SENDFILE` environment variable to true to enable nginx sendfile.
```
```shell
# Get the default sendfile config for blackcandy. This file need to mount to nginx proxy container to add custom configuration for nginx.
curl https://raw.githubusercontent.com/blackcandy-org/black_candy/master/config/nginx/sendfile.conf > blackcandy.local

docker-compose up
```

### Embedded Sidekiq

By default, you need another process to run Sidekiq for background job. Like this:

```yaml
version: '3'
services:
app: &app_base
image: blackcandy/blackcandy:edge
volumes:
- ./log:/app/log
- ./production.sqlite3:/app/db/production.sqlite3
- ./uploads_data:/app/public/uploads
- /media_data:/media_data
sidekiq:
<<: *app_base
command: bundle exec sidekiq
```
But you can also use embedded mode of Sidekiq if you don't want another separate Sidekiq process. This can help your deployment become easier.
All you need to do is to set `EMBEDDED_SIDEKIQ` environment variable to true.

### Listener For Media Library

Listener for media library can automatically sync for media library changes. You need another process to run the listener.

```yaml
version: '3'
services:
app: &app_base
image: blackcandy/blackcandy:edge
volumes:
- ./log:/app/log
- ./production.sqlite3:/app/db/production.sqlite3
- ./uploads_data:/app/public/uploads
- /media_data:/media_data
listener:
<<: *app_base
command: bundle exec rails listen_media_changes
```

## Environment Variables

| Name | Default | Description |
| --- | --- | --- |
| REDIS_URL | | The URL of Redis, when this environment variable has been set Black Candy will use Sidekiq for background job, Redis adapter for WebSockets and use Redis to store cache|
| REDIS_CACHE_URL | REDIS_URL | This environment variable can override the REDIS_URL, if you want to set different Redis URL for cache.|
| REDIS_SIDEKIQ_URL | REDIS_URL | This environment variable can override the REDIS_URL, if you want to set different Redis URL for Sidekiq. |
| REDIS_CABLE_URL | REDIS_URL | This environment variable can override the REDIS_URL, if you want to set different Redis URL for WebSockets. |
| DATABASE_URL | | The URL of PostgreSQL database. You must set this environment variable if you use PostgreSQL as database. |
| MEDIA_PATH | | You can use this environment variable to set media path for Black Candy, otherwise you can set media path in settings page. |
| DATABASE_ADAPTER | "sqlite" | There are two adapters are supported, "sqlite" and "postgresql".|
| NGINX_SENDFILE | false | Whether enable Nginx sendfile. |
| EMBEDDED_SIDEKIQ | false | Whether enable embedded mode of Sidekiq. |
| EMBEDDED_SIDEKIQ_CONCURRENCY | 2 | The concurrency number of embedded Sidekiq. This value should not greater than 2. Because we should keep embedded Sidekiq concurrency very low. For more details, see this [document](https://github.com/mperham/sidekiq/wiki/Embedding) about embedded Sidekiq. |
| SECRET_KEY_BASE | | When the SECRET_KEY_BASE environment variable is not set, Black candy will generate SECRET_KEY_BASE environment variable every time when service start up. This will cause old sessions invalid, You can set your own SECRET_KEY_BASE environment variable on docker service to avoid it. |

> **Note:** When the SECRET_KEY_BASE environment variable is not set, Black candy will generate SECRET_KEY_BASE environment variable every time when service start up.
> This will cause old sessions invalid, You can set your own SECRET_KEY_BASE environment variable on docker service to avoid it.

## Try in PWD

[![Try in PWD](https://raw.githubusercontent.com/play-with-docker/stacks/master/assets/images/button.png)](http://play-with-docker.com/?stack=https://raw.githubusercontent.com/aidewoode/black_candy/master/docker-compose.pwd.yml)
[![Try in PWD](https://raw.githubusercontent.com/play-with-docker/stacks/master/assets/images/button.png)](http://play-with-docker.com/?stack=https://raw.githubusercontent.com/blackcandy-org/black_candy/master/docker-compose.pwd.yml)

Click the button above, then you can try black candy on [Play with Docker](https://labs.play-with-docker.com).
Click the button above, then you can try Black Candy on [Play with Docker](https://labs.play-with-docker.com).

When the service is ready, access black candy from port 80. Then use initial admin user to login (email: [email protected], password: foobar). This demo already contains some sample music file. You can go to the setting page and click the sync button of the media path to import the sample music into the database.
When the service is ready, access Black Candy from port 3000. Then use initial admin user to login (email: [email protected], password: foobar). This demo already contains some sample music file. You can go to the setting page and click the sync button of the media path to import the sample music into the database.

And feel free to try it.

Expand All @@ -69,43 +202,56 @@ If like their music, you can buy their albums to support them.
Pull new image from remote

```shell
$ docker pull blackcandy/blackcandy
```

Restart services:

```shell
$ docker-compose restart
$ docker pull blackcandy/blackcandy:edge
```

## Development

### Requirements

- Ruby 3.1
- Postgres 11
- Redis 6.0
- Nodejs 12
- Node.js 14
- Yarn
- Imagemagick
- ffmpeg
- ImageMagick
- FFmpeg

You can use VS Code Remote Containers or GitHub Codespaces to setup dev environment easily.
For more infomations about dev container, please visit this link <https://code.visualstudio.com/docs/remote/create-dev-container>.
Make sure you have installed all those dependencies.

After the dev container has been built. You can run `./bin/dev` in terminal to start all services.
### Install gem dependencies

```shell
bundle install
```

### Install JavaScript dependencies

```shell
yarn install
```

### Database Configuration

```shell
rails db:prepare
rails db:seed
```

### Start all services

After you’ve set up everything, now you can running `./bin/dev` to start all service you need to develop.
Then visit <http://localhost:3000> use initial admin user to login (email: [email protected], password: foobar).


## Test

```shell
# Runing test
$ rails test RAILS_ENV=test
# Runing all test
$ rails test:all
# Runing lint
$ rails lint:all
```

## Integrations

Black candy support get artist and album image from Discogs API. You can create a API token from Discogs and set Discogs token on Setting page to enable it.
Black Candy support get artist and album image from Discogs API. You can create a API token from Discogs and set Discogs token on Setting page to enable it.
12 changes: 0 additions & 12 deletions app/jobs/media_sync_job.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,7 @@
class MediaSyncJob < ApplicationJob
queue_as :default
before_enqueue :start_syncing
after_perform :stop_syncing

def perform(type = :all, file_paths = [])
Media.sync(type, file_paths)
end

private

def start_syncing
Media.syncing = true
end

def stop_syncing
Media.syncing = false
end
end
4 changes: 4 additions & 0 deletions app/models/media.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ class Media

class << self
def sync(type = :all, file_paths = [])
self.syncing = true
file_paths = MediaFile.file_paths if type == :all

return if file_paths.blank?

case type
Expand All @@ -25,6 +27,8 @@ def sync(type = :all, file_paths = [])
remove_files(file_paths)
add_files(file_paths)
end
ensure
self.syncing = false
end

def syncing?
Expand Down
2 changes: 2 additions & 0 deletions app/models/song.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class Song < ApplicationRecord

search_by :name, associations: {artist: :name, album: :name}

attribute :is_favorited, :boolean

def format
MediaFile.format(file_path)
end
Expand Down
2 changes: 1 addition & 1 deletion app/views/api/v1/songs/_song.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ json.call(song, :id, :name, :duration)
json.url need_transcode?(song.format) ? new_api_v1_transcoded_stream_url(song_id: song.id) : new_api_v1_stream_url(song_id: song.id)
json.album_name song.album.title
json.artist_name song.artist.title
json.is_favorited song.respond_to?(:is_favorited) ? song.is_favorited : Current.user.favorited?(song)
json.is_favorited song.is_favorited || Current.user.favorited?(song)
json.format need_transcode?(song.format) ? Stream::TRANSCODE_FORMAT : song.format
json.album_image_url do
json.small URI.join(root_url, image_url_for(song.album, size: "small"))
Expand Down
41 changes: 7 additions & 34 deletions docker-compose.pwd.yml
Original file line number Diff line number Diff line change
@@ -1,45 +1,18 @@
version: '3.4'
version: '3'

services:
app: &app_base
image: blackcandy/blackcandy
app:
image: blackcandy/blackcandy:edge
volumes:
- production_music_data:/media_data
- production_uploads_data:/app/public/uploads
ports:
- 3000:3000
environment:
DB_HOST: postgres
DB_USER: postgres
REDIS_CACHE_URL: redis://redis:6379/1
REDIS_SIDEKIQ_URL: redis://redis:6379/2
REDIS_CABLE_URL: redis://redis:6379/3
MEDIA_PATH: /media_data
depends_on:
- postgres
- redis
postgres:
image: postgres:11.1-alpine
volumes:
- production_db_data:/var/lib/postgresql/data
redis:
image: redis:6.2-alpine
volumes:
- production_redis_data:/data
worker:
<<: *app_base
command: bundle exec sidekiq
web:
<<: *app_base
depends_on:
- app
- worker
ports:
- 80:80
command: nginx -g 'pid /tmp/nginx.pid; daemon off;'

music:
image: blackcandy/sample-music
volumes:
- production_music_data:/music
volumes:
production_db_data:
production_redis_data:
production_uploads_data:
production_music_data:
Loading

0 comments on commit 4ab5e08

Please sign in to comment.