Skip to content

Commit

Permalink
Merge 0d40e33 into 910c5bd
Browse files Browse the repository at this point in the history
  • Loading branch information
aidewoode committed Jan 17, 2024
2 parents 910c5bd + 0d40e33 commit 199b68d
Show file tree
Hide file tree
Showing 51 changed files with 283 additions and 188 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Expand Up @@ -9,7 +9,6 @@
/node_modules
/public/packs/
/public/packs-test/
/public/uploads/
/test
/screenshots
/coverage
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Expand Up @@ -43,7 +43,7 @@ jobs:
- name: Test and Lint
run: |
sudo apt-get update
sudo apt-get -yqq install libpq-dev imagemagick ffmpeg
sudo apt-get -yqq install libpq-dev libvips ffmpeg
bundle exec rails db:setup
bundle exec rails lint:all
bundle exec rails test:all
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Expand Up @@ -33,7 +33,6 @@
/config/master.key
/public/packs
/public/packs-test
/public/uploads
/public/assets
/node_modules

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Expand Up @@ -43,7 +43,7 @@ ENV RAILS_ENV production
RUN apk add --no-cache \
tzdata \
libpq \
imagemagick \
vips \
ffmpeg \
curl

Expand Down
4 changes: 2 additions & 2 deletions Gemfile
Expand Up @@ -31,8 +31,8 @@ gem "wahwah", "~> 1.5.0"
# Pagination
gem "pagy", "~> 6.0.0"

# For image attachment
gem "carrierwave", "~> 3.0.0"
# For Active Storage variants
gem "image_processing", "~> 1.12"

# For API request
gem "httparty", "~> 0.21.0"
Expand Down
10 changes: 1 addition & 9 deletions Gemfile.lock
Expand Up @@ -107,13 +107,6 @@ GEM
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
carrierwave (3.0.4)
activemodel (>= 6.0.0)
activesupport (>= 6.0.0)
addressable (~> 2.6)
image_processing (~> 1.1)
marcel (~> 1.0.0)
ssrf_filter (~> 1.0)
concurrent-ruby (1.2.2)
connection_pool (2.4.1)
crack (0.4.5)
Expand Down Expand Up @@ -347,7 +340,6 @@ GEM
sshkit (1.21.5)
net-scp (>= 1.1.2)
net-ssh (>= 2.8.0)
ssrf_filter (1.1.2)
standard (1.25.5)
language_server-protocol (~> 3.17.0.2)
rubocop (~> 1.48.1)
Expand Down Expand Up @@ -399,13 +391,13 @@ DEPENDENCIES
browser (~> 5.3.1)
bullet (~> 7.1.0)
capybara (~> 3.39.0)
carrierwave (~> 3.0.0)
cssbundling-rails (~> 1.2.0)
cuprite (~> 0.14.3)
daemons (~> 1.4.0)
debug
erb_lint (~> 0.4.0)
httparty (~> 0.21.0)
image_processing (~> 1.12)
jbuilder (~> 2.11.5)
jsbundling-rails (~> 1.1.2)
kamal (~> 0.16.1)
Expand Down
11 changes: 4 additions & 7 deletions README.md
Expand Up @@ -65,13 +65,12 @@ docker run -e DB_ADAPTER=postgresql -e DB_URL=postgresql://yourdatabaseurl ghcr.

### How to Persist Data

There are two parts of data that need to persist in Black Candy. First it's the data from the database if you are using SQLite as database, which is stored in `/app/storage`, second it's the data from the asset of media files, which is stored in `/app/public/uploads`.
,s
All the data that need to persist in Black Candy are stored in `/app/storage`, So you can mount this directory to host to persist data.

```shell
mkdir storage_data
mkdir uploads_data

docker run -v ./storage_data:/app/storage -v ./uploads_data:/app/public/uploads ghcr.io/blackcandy-org/blackcandy:edge
docker run -v ./storage_data:/app/storage ghcr.io/blackcandy-org/blackcandy:edge
```

### Enhance With Redis
Expand Down Expand Up @@ -107,7 +106,6 @@ services:
image: ghcr.io/blackcandy-org/blackcandy:edge
volumes:
- ./storage_data:/app/storage
- ./uploads_data:/app/public/uploads
- /media_data:/media_data
environment:
VIRTUAL_HOST: blackcandy.local
Expand All @@ -133,7 +131,6 @@ services:
image: ghcr.io/blackcandy-org/blackcandy:edge
volumes:
- ./storage_data:/app/storage
- ./uploads_data:/app/public/uploads
- /media_data:/media_data
sidekiq:
<<: *app_base
Expand Down Expand Up @@ -180,7 +177,7 @@ $ docker pull ghcr.io/blackcandy-org/blackcandy:edge
- Ruby 3.2
- Node.js 18
- Yarn
- ImageMagick
- libvips
- FFmpeg

Make sure you have installed all those dependencies.
Expand Down
5 changes: 3 additions & 2 deletions app/controllers/albums_controller.rb
Expand Up @@ -7,6 +7,7 @@ class AlbumsController < ApplicationController

def index
records = Album.includes(:artist)
.with_attached_cover_image
.filter_records(filter_params)
.sort_records(*sort_params)

Expand All @@ -15,7 +16,7 @@ def index

def show
@groped_songs = @album.songs.includes(:artist).group_by(&:discnum)
@album.attach_image_from_discogs
@album.attach_cover_image_from_discogs
end

def update
Expand All @@ -31,7 +32,7 @@ def update
private

def album_params
params.require(:album).permit(:image)
params.require(:album).permit(:cover_image)
end

def find_album
Expand Down
10 changes: 5 additions & 5 deletions app/controllers/artists_controller.rb
Expand Up @@ -6,15 +6,15 @@ class ArtistsController < ApplicationController
before_action :get_sort_option, only: [:index]

def index
records = Artist.sort_records(*sort_params)
records = Artist.sort_records(*sort_params).with_attached_cover_image
@pagy, @artists = pagy(records)
end

def show
@albums = @artist.albums.load_async
@appears_on_albums = @artist.appears_on_albums.load_async
@albums = @artist.albums.with_attached_cover_image.load_async
@appears_on_albums = @artist.appears_on_albums.with_attached_cover_image.load_async

@artist.attach_image_from_discogs
@artist.attach_cover_image_from_discogs
end

def update
Expand All @@ -30,7 +30,7 @@ def update
private

def artist_params
params.require(:artist).permit(:image)
params.require(:artist).permit(:cover_image)
end

def find_artist
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/home_controller.rb
Expand Up @@ -2,7 +2,7 @@

class HomeController < ApplicationController
def index
@recently_added_albums = Album.includes(:artist).order(created_at: :desc).limit(10)
@recently_played_albums = Current.user.recently_played_albums
@recently_added_albums = Album.includes(:artist).with_attached_cover_image.order(created_at: :desc).limit(10)
@recently_played_albums = Current.user.recently_played_albums.with_attached_cover_image
end
end
2 changes: 1 addition & 1 deletion app/controllers/search/albums_controller.rb
Expand Up @@ -2,6 +2,6 @@

class Search::AlbumsController < ApplicationController
def index
@albums = Album.search(params[:query]).includes(:artist)
@albums = Album.search(params[:query]).includes(:artist).with_attached_cover_image
end
end
2 changes: 1 addition & 1 deletion app/controllers/search/artists_controller.rb
Expand Up @@ -2,6 +2,6 @@

class Search::ArtistsController < ApplicationController
def index
@artists = Artist.search(params[:query])
@artists = Artist.search(params[:query]).with_attached_cover_image
end
end
4 changes: 2 additions & 2 deletions app/controllers/search_controller.rb
Expand Up @@ -4,8 +4,8 @@ class SearchController < ApplicationController
SEARCH_RESULT_MAX_AMOUNT = 10

def index
searched_albums = Album.search(params[:query]).includes(:artist)
searched_artists = Artist.search(params[:query])
searched_albums = Album.search(params[:query]).includes(:artist).with_attached_cover_image
searched_artists = Artist.search(params[:query]).with_attached_cover_image
searched_playlists = Playlist.search(params[:query])
searched_songs = Song.search(params[:query]).includes(:artist, :album)

Expand Down
11 changes: 7 additions & 4 deletions app/helpers/application_helper.rb
Expand Up @@ -33,11 +33,14 @@ def icon_tag(name, options = {})
end
end

def image_url_for(object, size: "")
sizes_options = %w[small medium large]
size = size.in?(sizes_options) ? size : "medium"
def cover_image_url_for(object, size: :medium)
return unless object.respond_to?(:cover_image)

object.image.send(size).url
sizes_options = %i[small medium large]
size = size.in?(sizes_options) ? size : :medium
default_cover_url = "#{root_url}images/default_#{object.class.name.downcase}_#{size}.png"

object.has_cover_image? ? full_url_for(object.cover_image.variant(size)) : default_cover_url
end

def loader_tag(size: "")
Expand Down
6 changes: 3 additions & 3 deletions app/helpers/song_helper.rb
Expand Up @@ -13,9 +13,9 @@ def song_json_builder(song, for_api: false)
json.is_favorited song.is_favorited.nil? ? Current.user.favorited?(song) : song.is_favorited
json.format need_transcode?(song) ? Stream::TRANSCODE_FORMAT : song.format
json.album_image_url do
json.small URI.join(root_url, image_url_for(song.album, size: "small"))
json.medium URI.join(root_url, image_url_for(song.album, size: "medium"))
json.large URI.join(root_url, image_url_for(song.album, size: "large"))
json.small URI.join(root_url, cover_image_url_for(song.album, size: :small))
json.medium URI.join(root_url, cover_image_url_for(song.album, size: :medium))
json.large URI.join(root_url, cover_image_url_for(song.album, size: :large))
end
end
end
Expand Down
22 changes: 22 additions & 0 deletions app/jobs/attach_cover_image_from_discogs_job.rb
@@ -0,0 +1,22 @@
# frozen_string_literal: true

require "open-uri"
class AttachCoverImageFromDiscogsJob < ApplicationJob
queue_as :default

def perform(discogs_imageable)
image_url = DiscogsApi.image(discogs_imageable)
return unless image_url.present?

image_file = OpenURI.open_uri(image_url)
content_type = image_file.content_type
image_format = Mime::Type.lookup(content_type).symbol
return unless image_format.present?

discogs_imageable.cover_image.attach(
io: image_file,
filename: "cover.#{image_format}",
content_type: content_type
)
end
end
10 changes: 0 additions & 10 deletions app/jobs/attach_image_from_discogs_job.rb

This file was deleted.

30 changes: 23 additions & 7 deletions app/models/concerns/imageable_concern.rb
@@ -1,21 +1,37 @@
module ImageableConcern
extend ActiveSupport::Concern

ALLOWED_IMAGE_CONTENT_TYPES = %w[image/jpeg image/png].freeze

included do
mount_uploader :image, ImageUploader
has_one_attached :cover_image do |attachable|
attachable.variant :small, resize_to_fill: [200, 200]
attachable.variant :medium, resize_to_fill: [300, 300], preprocessed: true
attachable.variant :large, resize_to_fill: [400, 400]
end

validate :content_type_of_cover_image
end

def has_image?
image.file.present?
def has_cover_image?
cover_image.attached?
end

def attach_image_from_discogs
AttachImageFromDiscogsJob.perform_later(self) if needs_image_from_discogs?
def attach_cover_image_from_discogs
AttachCoverImageFromDiscogsJob.perform_later(self) if needs_cover_image_from_discogs?
end

private

def needs_image_from_discogs?
Setting.discogs_token.present? && !has_image? && !unknown?
def needs_cover_image_from_discogs?
Setting.discogs_token.present? && !has_cover_image? && !unknown?
end

def content_type_of_cover_image
return unless cover_image.attached?

unless cover_image.content_type.in?(ALLOWED_IMAGE_CONTENT_TYPES)
errors.add(:cover_image, :invalid_content_type)
end
end
end
2 changes: 1 addition & 1 deletion app/models/current_playlist.rb
Expand Up @@ -4,7 +4,7 @@ class CurrentPlaylist < Playlist
def songs_with_favorite
favorite_playlist = Current.user.favorite_playlist

songs.includes(:artist, :album)
songs.includes(:artist, album: {cover_image_attachment: :blob})
.joins("Left JOIN playlists_songs T1 ON songs.id = T1.song_id AND T1.playlist_id = #{favorite_playlist.id}")
.select("songs.*, T1.playlist_id IS NOT NULL as is_favorited")
end
Expand Down
5 changes: 4 additions & 1 deletion app/models/media.rb
Expand Up @@ -70,7 +70,10 @@ def attach(file_info)
)

album.update!(album_info(file_info))
album.update!(image: file_info[:image]) unless album.has_image?

unless album.has_cover_image?
album.cover_image.attach(file_info[:image]) if file_info[:image].present?
end

song = Song.find_or_initialize_by(md5_hash: file_info[:md5_hash])
song.update!(song_info(file_info).merge(album: album, artist: artist))
Expand Down
8 changes: 7 additions & 1 deletion app/models/media_file.rb
Expand Up @@ -44,7 +44,13 @@ def extract_image_from(tag)
return unless image.present?

image_format = Mime::Type.lookup(image[:mime_type]).symbol
CarrierWaveStringIO.new("cover.#{image_format}", image[:data]) if image_format.present?
return unless image_format.present?

{
io: StringIO.new(image[:data]),
filename: "cover.#{image_format}",
content_type: image[:mime_type]
}
end

def get_tag_info(file_path)
Expand Down

0 comments on commit 199b68d

Please sign in to comment.