mirror of
https://github.com/danbee/mpd-client
synced 2025-03-04 08:39:09 +00:00
Merge pull request #1 from leemachin/gem-extract
WIP: Refactor and Gemify (for convenience)
This commit is contained in:
commit
f8f8b9d194
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
*.gem
|
||||
*.rbc
|
||||
.bundle
|
||||
.config
|
||||
.yardoc
|
||||
Gemfile.lock
|
||||
InstalledFiles
|
||||
_yardoc
|
||||
coverage
|
||||
doc/
|
||||
lib/bundler/man
|
||||
pkg
|
||||
rdoc
|
||||
spec/reports
|
||||
test/tmp
|
||||
test/version_tmp
|
||||
tmp
|
||||
.sass-cache
|
||||
5
Gemfile
5
Gemfile
@ -2,12 +2,15 @@ source 'https://rubygems.org'
|
||||
|
||||
ruby '2.0.0'
|
||||
|
||||
# todo: get everything working before going whole hog with the gem
|
||||
# gemspec
|
||||
|
||||
gem 'sinatra'
|
||||
gem 'sinatra-contrib'
|
||||
gem 'sinatra-asset-pipeline'
|
||||
gem 'foreman'
|
||||
|
||||
gem 'ruby-mpd', git: 'git@github.com:archSeer/ruby-mpd.git'
|
||||
gem 'active_support', require: false
|
||||
|
||||
group :development, :test do
|
||||
gem 'pry'
|
||||
|
||||
14
Gemfile.lock
14
Gemfile.lock
@ -7,6 +7,9 @@ GIT
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
active_support (3.0.0)
|
||||
activesupport (= 3.0.0)
|
||||
activesupport (3.0.0)
|
||||
backports (3.3.5)
|
||||
coderay (1.0.9)
|
||||
coffee-script (2.2.0)
|
||||
@ -29,7 +32,7 @@ GEM
|
||||
method_source (~> 0.8)
|
||||
slop (~> 3.4)
|
||||
rack (1.5.2)
|
||||
rack-protection (1.5.0)
|
||||
rack-protection (1.5.1)
|
||||
rack
|
||||
rack-test (0.6.2)
|
||||
rack (>= 1.0)
|
||||
@ -45,11 +48,11 @@ GEM
|
||||
sass (3.2.12)
|
||||
shotgun (0.9)
|
||||
rack (>= 1.0)
|
||||
sinatra (1.4.3)
|
||||
sinatra (1.4.4)
|
||||
rack (~> 1.4)
|
||||
rack-protection (~> 1.4)
|
||||
tilt (~> 1.3, >= 1.3.4)
|
||||
sinatra-asset-pipeline (0.2.1)
|
||||
sinatra-asset-pipeline (0.3.3)
|
||||
coffee-script
|
||||
rake
|
||||
sass
|
||||
@ -65,12 +68,12 @@ GEM
|
||||
sinatra (~> 1.4.0)
|
||||
tilt (~> 1.3)
|
||||
slop (3.4.6)
|
||||
sprockets (2.10.0)
|
||||
sprockets (2.10.1)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
sprockets-helpers (1.0.1)
|
||||
sprockets-helpers (1.1.0)
|
||||
sprockets (~> 2.0)
|
||||
sprockets-sass (1.0.2)
|
||||
sprockets (~> 2.0)
|
||||
@ -86,6 +89,7 @@ PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
active_support
|
||||
foreman
|
||||
pry
|
||||
rspec
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
ENV['RACK_ENV'] ||= 'development'
|
||||
|
||||
require 'bundler'
|
||||
|
||||
Bundler.setup
|
||||
require './mpd_client'
|
||||
Bundler.require(:default, ENV['RACK_ENV'])
|
||||
|
||||
run MPDClient
|
||||
require File.expand_path('lib/mpd_client', __dir__)
|
||||
|
||||
run MPDClient::Webserver
|
||||
|
||||
49
lib/mpd_client.rb
Normal file
49
lib/mpd_client.rb
Normal file
@ -0,0 +1,49 @@
|
||||
require 'forwardable'
|
||||
require 'ruby-mpd'
|
||||
require 'set'
|
||||
require 'json'
|
||||
|
||||
module MPDClient
|
||||
|
||||
autoload :Jsonable, File.expand_path('mpd_client/jsonable.rb', __dir__)
|
||||
autoload :ClassToProc, File.expand_path('mpd_client/class_to_proc.rb', __dir__)
|
||||
|
||||
autoload :Connection, File.expand_path('mpd_client/connection.rb', __dir__)
|
||||
autoload :Song, File.expand_path('mpd_client/song.rb', __dir__)
|
||||
autoload :Album, File.expand_path('mpd_client/album.rb', __dir__)
|
||||
autoload :Artist, File.expand_path('mpd_client/artist.rb', __dir__)
|
||||
autoload :Control, File.expand_path('mpd_client/control.rb', __dir__)
|
||||
autoload :Queue, File.expand_path('mpd_client/queue.rb', __dir__)
|
||||
|
||||
# Don't want to automatically require this thing
|
||||
autoload :Webserver, File.expand_path('mpd_client/webserver.rb', __dir__)
|
||||
|
||||
MPD_HOST = ENV.fetch('MPD_HOST', 'localhost')
|
||||
MPD_PORT = ENV.fetch('MPD_PORT', 6600)
|
||||
|
||||
def self.connect!
|
||||
@conn ||= Connection.new(MPD_HOST, MPD_PORT)
|
||||
@conn.connect unless @conn.connected?
|
||||
@conn
|
||||
end
|
||||
|
||||
def self.conn
|
||||
@conn
|
||||
end
|
||||
|
||||
def self.status
|
||||
self.conn.status
|
||||
end
|
||||
|
||||
def self.listen(&block)
|
||||
self.conn.instance_eval(&block)
|
||||
end
|
||||
|
||||
def self.connect_user(conn)
|
||||
self.conn.connected_users << conn
|
||||
end
|
||||
|
||||
def self.disconnect_user(conn)
|
||||
self.conn.connected_users.delete(conn)
|
||||
end
|
||||
end
|
||||
47
lib/mpd_client/album.rb
Normal file
47
lib/mpd_client/album.rb
Normal file
@ -0,0 +1,47 @@
|
||||
module MPDClient
|
||||
class Album
|
||||
|
||||
include ClassToProc
|
||||
include Jsonable
|
||||
|
||||
extend Forwardable
|
||||
|
||||
delegate %i(artist genre) => :@first_song
|
||||
|
||||
def initialize(album)
|
||||
@first_song = MPDClient::Song.by(album: album).first
|
||||
end
|
||||
|
||||
def title
|
||||
@first_song.album
|
||||
end
|
||||
|
||||
def year
|
||||
@first_song.date
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
year <=> other.year
|
||||
end
|
||||
|
||||
def to_h
|
||||
{
|
||||
title: title,
|
||||
artist: artist,
|
||||
genre: genre,
|
||||
year: year
|
||||
}
|
||||
end
|
||||
|
||||
class << self
|
||||
def all
|
||||
MPDClient.conn.albums.sort.map(&self)
|
||||
end
|
||||
|
||||
def by_artist(artist)
|
||||
MPDClient.conn.albums(artist).map(&self)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
25
lib/mpd_client/artist.rb
Normal file
25
lib/mpd_client/artist.rb
Normal file
@ -0,0 +1,25 @@
|
||||
module MPDClient
|
||||
class Artist
|
||||
|
||||
include ClassToProc
|
||||
include Jsonable
|
||||
|
||||
attr :name
|
||||
|
||||
def initialize(name)
|
||||
@name = name
|
||||
end
|
||||
|
||||
def to_h
|
||||
{
|
||||
name: name
|
||||
}
|
||||
end
|
||||
|
||||
class << self
|
||||
def all
|
||||
MPDClient.conn.artists.sort.map(&self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
11
lib/mpd_client/class_to_proc.rb
Normal file
11
lib/mpd_client/class_to_proc.rb
Normal file
@ -0,0 +1,11 @@
|
||||
module MPDClient
|
||||
module ClassToProc
|
||||
|
||||
def self.included(receiver)
|
||||
receiver.define_singleton_method :to_proc do
|
||||
proc { |*args| new(*args) }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
16
lib/mpd_client/connection.rb
Normal file
16
lib/mpd_client/connection.rb
Normal file
@ -0,0 +1,16 @@
|
||||
module MPDClient
|
||||
class Connection < MPD
|
||||
|
||||
attr_accessor :connected_users
|
||||
|
||||
def initialize(host, port, opts = {})
|
||||
@connected_users = Set.new
|
||||
super host, port, opts.merge(callbacks: true)
|
||||
end
|
||||
|
||||
def each_conn(&block)
|
||||
connected_users.each(&block)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
35
lib/mpd_client/control.rb
Normal file
35
lib/mpd_client/control.rb
Normal file
@ -0,0 +1,35 @@
|
||||
module MPDClient
|
||||
class Control
|
||||
|
||||
class << self
|
||||
def controls
|
||||
%i(play stop next previous pause)
|
||||
end
|
||||
|
||||
def play(pos)
|
||||
MPDClient.conn.play(pos)
|
||||
end
|
||||
|
||||
def stop
|
||||
MPDClient.conn.stop
|
||||
end
|
||||
|
||||
def next
|
||||
MPDClient.conn.next
|
||||
end
|
||||
|
||||
def previous
|
||||
MPDClient.conn.previous
|
||||
end
|
||||
|
||||
def pause
|
||||
MPDClient.conn.pause = !MPDClient.conn.paused?
|
||||
end
|
||||
|
||||
def volume(value)
|
||||
MPDClient.conn.volume = value
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
15
lib/mpd_client/jsonable.rb
Normal file
15
lib/mpd_client/jsonable.rb
Normal file
@ -0,0 +1,15 @@
|
||||
module MPDClient
|
||||
module Jsonable
|
||||
|
||||
def self.included(receiver)
|
||||
receiver.send(:include, InstanceMethods)
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def to_json(*args)
|
||||
to_h.to_json(*args)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
28
lib/mpd_client/queue.rb
Normal file
28
lib/mpd_client/queue.rb
Normal file
@ -0,0 +1,28 @@
|
||||
module MPDClient
|
||||
class Queue
|
||||
|
||||
include Enumerable
|
||||
include Jsonable
|
||||
|
||||
attr :songs
|
||||
|
||||
def initialize
|
||||
@songs = fetch_songs
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
songs.each(&block)
|
||||
end
|
||||
|
||||
def to_h
|
||||
map(&:to_h)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_songs
|
||||
MPDClient.conn.queue.map(&Song)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
0
lib/mpd_client/server.rb
Normal file
0
lib/mpd_client/server.rb
Normal file
61
lib/mpd_client/song.rb
Normal file
61
lib/mpd_client/song.rb
Normal file
@ -0,0 +1,61 @@
|
||||
module MPDClient
|
||||
class Song
|
||||
|
||||
include ClassToProc
|
||||
include Comparable
|
||||
include Jsonable
|
||||
|
||||
extend Forwardable
|
||||
|
||||
delegate %i(id track artist album title genre date time pos) => :@song
|
||||
|
||||
def initialize(song, pos: nil)
|
||||
@song = song
|
||||
end
|
||||
|
||||
def playing?
|
||||
if current = self.class.current_song
|
||||
[artist, album, title] == [current.artist, current.album, current.title]
|
||||
end
|
||||
end
|
||||
|
||||
def length
|
||||
time
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
[artist, album, title] <=> [other.artist, other.album, other.title]
|
||||
end
|
||||
|
||||
def to_h
|
||||
{
|
||||
id: id,
|
||||
track: track,
|
||||
artist: artist,
|
||||
album: album,
|
||||
title: title,
|
||||
length: length,
|
||||
pos: pos,
|
||||
playing: playing?
|
||||
}
|
||||
end
|
||||
|
||||
class << self
|
||||
def by(**params)
|
||||
params.delete_if {|_, v| v.nil? }
|
||||
MPDClient.conn.where(params).map(&self)
|
||||
end
|
||||
|
||||
def all
|
||||
MPDClient.conn.songs.map(&self)
|
||||
end
|
||||
|
||||
def current_song
|
||||
if song = MPDClient.conn.current_song
|
||||
new(song)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
3
lib/mpd_client/version.rb
Normal file
3
lib/mpd_client/version.rb
Normal file
@ -0,0 +1,3 @@
|
||||
module MPDClient
|
||||
VERSION = "0.0.1"
|
||||
end
|
||||
119
lib/mpd_client/webserver.rb
Executable file
119
lib/mpd_client/webserver.rb
Executable file
@ -0,0 +1,119 @@
|
||||
require 'sinatra'
|
||||
require 'sinatra/asset_pipeline'
|
||||
require 'sass'
|
||||
require 'cgi'
|
||||
require 'active_support/core_ext/hash/slice'
|
||||
|
||||
module MPDClient
|
||||
class Webserver < Sinatra::Base
|
||||
|
||||
set server: 'thin'
|
||||
|
||||
set :root, File.expand_path('../../', __dir__)
|
||||
|
||||
set :assets_precompile, %w(app.js app.css *.png *.jpg *.svg *.eot *.ttf *.woff)
|
||||
set :assets_prefix, ['assets']
|
||||
|
||||
register Sinatra::AssetPipeline
|
||||
register Sinatra::Namespace
|
||||
|
||||
MPDClient.connect!
|
||||
|
||||
MPDClient.listen do
|
||||
on :song do
|
||||
each_conn do |conn|
|
||||
json = { type: 'status', data: status }.to_json
|
||||
conn << "data: #{json}\n\n"
|
||||
end
|
||||
end
|
||||
|
||||
on :state do
|
||||
each_conn do |conn|
|
||||
json = { type: 'status', data: status }.to_json
|
||||
conn << "data: #{json}\n\n"
|
||||
end
|
||||
end
|
||||
|
||||
on :playlist do
|
||||
each_conn do |conn|
|
||||
json = { type: 'queue', data: Queue.new }.to_json
|
||||
conn << "data: #{json}\n\n"
|
||||
end
|
||||
end
|
||||
|
||||
on :time do |elapsed, total|
|
||||
each_conn do |conn|
|
||||
json = { type: 'time', data: [elapsed, total] }.to_json
|
||||
conn << "data: #{json}\n\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get '/' do
|
||||
erb :index
|
||||
end
|
||||
|
||||
namespace '/api' do
|
||||
|
||||
get '/status' do
|
||||
MPDClient.status.to_json
|
||||
end
|
||||
|
||||
get '/stream', provides: 'text/event-stream' do
|
||||
stream :keep_open do |conn|
|
||||
MPDClient.connect_user(conn)
|
||||
conn.callback { MPDClient.disconnect_user(conn) }
|
||||
end
|
||||
end
|
||||
|
||||
get '/albums' do
|
||||
content_type 'application/json'
|
||||
if params[:artist]
|
||||
Album.by_artist(params[:artist]).sort.to_json
|
||||
else
|
||||
Album.all.to_json
|
||||
end
|
||||
end
|
||||
|
||||
get '/artists' do
|
||||
content_type 'application/json'
|
||||
Artist.all.to_json
|
||||
end
|
||||
|
||||
get '/songs' do
|
||||
content_type 'application/json'
|
||||
if query = params.slice(:artist, :album) and !query.empty?
|
||||
Song.by(**query).to_json
|
||||
else
|
||||
Song.all.sort.to_json
|
||||
end
|
||||
end
|
||||
|
||||
get '/queue' do
|
||||
content_type 'application/json'
|
||||
{ data: Queue.new }.to_json
|
||||
end
|
||||
|
||||
put '/control/play' do
|
||||
Control.play(params[:pos])
|
||||
end
|
||||
|
||||
put '/control/:action' do
|
||||
if Control.controls.include?(params[:action].to_sym)
|
||||
Control.send(params[:action])
|
||||
else
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
||||
put '/control/volume/:value' do
|
||||
content_type 'application/json'
|
||||
unless Control.volume(params[:value].to_i)
|
||||
status 422
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@ -1,24 +0,0 @@
|
||||
require './models/mpd_connection'
|
||||
|
||||
class Album < Struct.new(:title, :artist, :genre, :year)
|
||||
|
||||
def initialize(album)
|
||||
first_song = MPDConnection.mpd.where(album: album).first
|
||||
self.title = first_song.album
|
||||
self.artist = first_song.artist
|
||||
self.genre = first_song.genre
|
||||
self.year = first_song.date
|
||||
end
|
||||
|
||||
def <=>(album)
|
||||
year <=> album.year
|
||||
end
|
||||
|
||||
def self.all
|
||||
MPDConnection.mpd.albums.sort.map { |album| self.new(album) }
|
||||
end
|
||||
|
||||
def self.by_artist(artist)
|
||||
MPDConnection.mpd.albums(artist).map { |album| self.new(album) }
|
||||
end
|
||||
end
|
||||
@ -1,7 +0,0 @@
|
||||
require './models/mpd_connection'
|
||||
|
||||
class Artist < Struct.new(:name)
|
||||
def self.all
|
||||
MPDConnection.mpd.artists.sort.map { |artist| self.new(artist) }
|
||||
end
|
||||
end
|
||||
@ -1,33 +0,0 @@
|
||||
require './models/mpd_connection'
|
||||
|
||||
class Control
|
||||
class << self
|
||||
def controls
|
||||
[:play, :stop, :next, :previous, :pause]
|
||||
end
|
||||
|
||||
def play(pos = nil)
|
||||
MPDConnection.mpd.play(pos)
|
||||
end
|
||||
|
||||
def stop
|
||||
MPDConnection.mpd.stop
|
||||
end
|
||||
|
||||
def next
|
||||
MPDConnection.mpd.next
|
||||
end
|
||||
|
||||
def previous
|
||||
MPDConnection.mpd.previous
|
||||
end
|
||||
|
||||
def pause
|
||||
MPDConnection.mpd.pause = !MPDConnection.mpd.paused?
|
||||
end
|
||||
|
||||
def volume(value)
|
||||
MPDConnection.mpd.volume = value
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,11 +0,0 @@
|
||||
class MPDConnection
|
||||
def self.mpd
|
||||
@mpd ||= MPD.new('localhost', 6600, { callbacks: true })
|
||||
@mpd.connect unless @mpd.connected?
|
||||
@mpd
|
||||
end
|
||||
|
||||
def self.status
|
||||
self.mpd.status
|
||||
end
|
||||
end
|
||||
@ -1,36 +0,0 @@
|
||||
require './models/mpd_connection'
|
||||
|
||||
class Song < Struct.new(:id, :track, :artist, :album, :title, :length, :pos, :playing)
|
||||
|
||||
def initialize(song, pos: nil, playing: false)
|
||||
self.id = song.id
|
||||
self.track = song.track
|
||||
self.artist = song.artist
|
||||
self.album = song.album
|
||||
self.title = song.title
|
||||
self.length = song.time
|
||||
self.pos = pos
|
||||
self.playing = playing
|
||||
end
|
||||
|
||||
def <=>(song)
|
||||
title <=> song.title
|
||||
end
|
||||
|
||||
def self.queue
|
||||
current_song = MPDConnection.mpd.status[:songid]
|
||||
MPDConnection.mpd.queue.map { |song| self.new(song, playing: (song.id == current_song), pos: song.pos) }
|
||||
end
|
||||
|
||||
def self.all
|
||||
MPDConnection.mpd.songs.map { |album| self.new(album) }
|
||||
end
|
||||
|
||||
def self.by_artist(artist)
|
||||
MPDConnection.mpd.where(artist: artist).map { |song| self.new(song) }
|
||||
end
|
||||
|
||||
def self.by_album(artist, album)
|
||||
MPDConnection.mpd.where(artist: artist, album: album).map { |song| self.new(song) }
|
||||
end
|
||||
end
|
||||
113
mpd_client.rb
113
mpd_client.rb
@ -1,113 +0,0 @@
|
||||
require 'bundler'
|
||||
ENV['RACK_ENV'] ||= 'development'
|
||||
Bundler.require(:default, ENV['RACK_ENV'])
|
||||
|
||||
require 'sinatra/asset_pipeline'
|
||||
|
||||
require 'sass'
|
||||
require 'json'
|
||||
require 'cgi'
|
||||
|
||||
require './models/mpd_connection'
|
||||
require './models/control'
|
||||
require './models/album'
|
||||
require './models/artist'
|
||||
require './models/song'
|
||||
|
||||
class MPDClient < Sinatra::Base
|
||||
|
||||
set server: 'thin', connections: []
|
||||
|
||||
set :assets_precompile, %w(app.js app.css *.png *.jpg *.svg *.eot *.ttf *.woff)
|
||||
set :assets_prefix, 'assets'
|
||||
register Sinatra::AssetPipeline
|
||||
|
||||
register Sinatra::Namespace
|
||||
|
||||
get '/' do
|
||||
erb :index
|
||||
end
|
||||
|
||||
def self.send_status
|
||||
response = JSON({ type: 'status', data: MPDConnection.status })
|
||||
settings.connections.each { |out| out << "data: #{response}\n\n" }
|
||||
end
|
||||
|
||||
def self.send_queue
|
||||
response = JSON({ type: 'queue', data: Song.queue.map(&:to_h) })
|
||||
settings.connections.each { |out| out << "data: #{response}\n\n" }
|
||||
end
|
||||
|
||||
def self.send_time(elapsed, total)
|
||||
response = JSON({ type: 'time', data: [elapsed, total] })
|
||||
settings.connections.each { |out| out << "data: #{response}\n\n" }
|
||||
end
|
||||
|
||||
MPDConnection.mpd.on(:song) { |song| send_status }
|
||||
MPDConnection.mpd.on(:state) { |state| send_status }
|
||||
MPDConnection.mpd.on(:playlist) { |playlist| send_queue }
|
||||
MPDConnection.mpd.on(:time) { |elapsed, total| send_time(elapsed, total) }
|
||||
|
||||
namespace '/api' do
|
||||
|
||||
get '/status' do
|
||||
JSON MPDConnection.status
|
||||
end
|
||||
|
||||
get '/stream', provides: 'text/event-stream' do
|
||||
stream :keep_open do |out|
|
||||
settings.connections << out
|
||||
out.callback { settings.connections.delete(out) }
|
||||
end
|
||||
end
|
||||
|
||||
get '/albums' do
|
||||
content_type 'application/json'
|
||||
if params[:artist]
|
||||
JSON Album.by_artist(CGI.unescape(params[:artist])).sort.map(&:to_h)
|
||||
else
|
||||
JSON Album.all.map(&:to_h)
|
||||
end
|
||||
end
|
||||
|
||||
get '/artists' do
|
||||
content_type 'application/json'
|
||||
JSON Artist.all.map(&:to_h)
|
||||
end
|
||||
|
||||
get '/songs' do
|
||||
content_type 'application/json'
|
||||
if params[:artist] && params[:album]
|
||||
JSON Song.by_album(CGI.unescape(params[:artist]), CGI.unescape(params[:album])).map(&:to_h)
|
||||
elsif params[:artist]
|
||||
JSON Song.by_artist(CGI.unescape(params[:artist])).map(&:to_h)
|
||||
else
|
||||
JSON Song.all.sort.map(&:to_h)
|
||||
end
|
||||
end
|
||||
|
||||
get '/queue' do
|
||||
content_type 'application/json'
|
||||
JSON({ data: Song.queue.map(&:to_h) })
|
||||
end
|
||||
|
||||
put '/control/play' do
|
||||
Control.play(params[:pos])
|
||||
end
|
||||
|
||||
put '/control/:action' do
|
||||
if Control.controls.include?(params[:action].to_sym)
|
||||
Control.send(params[:action])
|
||||
else
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
||||
put '/control/volume/:value' do
|
||||
content_type 'application/json'
|
||||
Control.volume(params[:value])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@ -1,30 +1,34 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Album do
|
||||
describe MPDClient::Album do
|
||||
|
||||
subject { MPDClient::Album }
|
||||
|
||||
let(:song1) { MPD::Song.new({ album: 'Back in Black', genre: 'Rock', date: '1980' }) }
|
||||
let(:song2) { MPD::Song.new({ album: 'Highway to Hell', genre: 'Rock', date: '1979' }) }
|
||||
|
||||
before do
|
||||
MPDConnection.mpd.stub(:albums).and_return([song1.album, song2.album])
|
||||
MPDConnection.mpd.stub(:search).and_return([song1, song2])
|
||||
MPDConnection.mpd.stub(:search).with(:album, song1.album).and_return([song1])
|
||||
MPDConnection.mpd.stub(:search).with(:album, song2.album).and_return([song2])
|
||||
MPDClient.conn.tap do |mpd|
|
||||
mpd.stub(:albums).and_return([song1.album, song2.album])
|
||||
mpd.stub(:where).and_return([song1, song2])
|
||||
mpd.stub(:where).with(album: song1.album).and_return([song1])
|
||||
mpd.stub(:where).with(album: song2.album).and_return([song2])
|
||||
end
|
||||
end
|
||||
|
||||
it 'has attributes based on first song' do
|
||||
album = Album.new(song1.album)
|
||||
album = subject.new(song1.album)
|
||||
expect(album.title).to eq(song1.album)
|
||||
expect(album.genre).to eq(song1.genre)
|
||||
expect(album.year).to eq(song1.date)
|
||||
end
|
||||
|
||||
it 'should return a list of albums' do
|
||||
expect(Album.by_artist('AC/DC')).to have(2).items
|
||||
expect(subject.by_artist('AC/DC')).to have(2).items
|
||||
end
|
||||
|
||||
it 'should sort the albums by year' do
|
||||
albums = Album.by_artist('AC/DC')
|
||||
albums = subject.by_artist('AC/DC')
|
||||
expect(albums.sort.map(&:year)).to eq(['1979', '1980'])
|
||||
end
|
||||
|
||||
23
spec/lib/mpd_client/artist_spec.rb
Normal file
23
spec/lib/mpd_client/artist_spec.rb
Normal file
@ -0,0 +1,23 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe MPDClient::Artist do
|
||||
|
||||
subject { MPDClient::Artist }
|
||||
|
||||
let(:artist) { subject.new('Alice Cooper') }
|
||||
let(:artists) { ['Alice Cooper', 'Jimmy Eat World', 'Dream Theater'] }
|
||||
|
||||
before do
|
||||
MPDClient.conn.stub(:artists).and_return(artists)
|
||||
end
|
||||
|
||||
it 'has attributes' do
|
||||
expect(artist.name).to eq('Alice Cooper')
|
||||
end
|
||||
|
||||
it 'returns all artists' do
|
||||
expect(subject.all).to have(3).items
|
||||
expect(subject.all.map(&:name)).to eq(artists.sort)
|
||||
end
|
||||
|
||||
end
|
||||
23
spec/lib/mpd_client/connection_spec.rb
Normal file
23
spec/lib/mpd_client/connection_spec.rb
Normal file
@ -0,0 +1,23 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe MPDClient::Connection do
|
||||
|
||||
subject { MPDClient::Connection }
|
||||
|
||||
let(:conn) { subject.new('localhost', 6600) }
|
||||
|
||||
describe "#each_conn" do
|
||||
let(:fake_users) { [1, 2, 3] }
|
||||
it "enumerates the connected users" do
|
||||
fake_users.each {|n| conn.connected_users << n }
|
||||
conn.each_conn.to_a.should eq(fake_users)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#connected_users" do
|
||||
it "doesn't allow duplicate connections" do
|
||||
3.times { conn.connected_users << "user" }
|
||||
conn.connected_users.should have(1).item
|
||||
end
|
||||
end
|
||||
end
|
||||
4
spec/lib/mpd_client/control_spec.rb
Normal file
4
spec/lib/mpd_client/control_spec.rb
Normal file
@ -0,0 +1,4 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe MPDClient::Control do
|
||||
end
|
||||
@ -1,16 +1,17 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Song do
|
||||
describe MPDClient::Queue do
|
||||
|
||||
subject { MPDClient::Queue.new }
|
||||
|
||||
let(:song1) { MPD::Song.new({ title: 'Back in Black', album: 'Back in Black', genre: 'Rock', date: '1980' }) }
|
||||
|
||||
let(:song2) { MPD::Song.new({ title: 'Highway to Hell', album: 'Highway to Hell', genre: 'Rock', date: '1979' }) }
|
||||
|
||||
before do
|
||||
MPDConnection.mpd.stub(:queue).and_return([song1, song2])
|
||||
MPDClient.conn.stub(:queue).and_return([song1, song2])
|
||||
end
|
||||
|
||||
it 'returns the queue of songs' do
|
||||
queue = Song.queue
|
||||
expect(queue).to have(2).items
|
||||
end
|
||||
its(:songs) { should have(2).items }
|
||||
|
||||
end
|
||||
30
spec/lib/mpd_client/song_spec.rb
Normal file
30
spec/lib/mpd_client/song_spec.rb
Normal file
@ -0,0 +1,30 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe MPDClient::Song do
|
||||
|
||||
subject { MPDClient::Song }
|
||||
|
||||
let(:song1) { MPD::Song.new({ title: 'Back in Black', album: 'Back in Black', genre: 'Rock', date: '1980' }) }
|
||||
let(:song2) { MPD::Song.new({ title: 'Highway to Hell', album: 'Highway to Hell', genre: 'Rock', date: '1979' }) }
|
||||
|
||||
before do
|
||||
MPDClient.conn.stub(:queue).and_return([song1, song2])
|
||||
end
|
||||
|
||||
describe "#playing?" do
|
||||
let(:playing_song) { subject.new(song1) }
|
||||
let(:not_playing_song) { subject.new(song2) }
|
||||
|
||||
before do
|
||||
MPDClient.conn.stub(:current_song).and_return(song1)
|
||||
end
|
||||
|
||||
it "should be true when the song is playing" do
|
||||
playing_song.playing?.should eq(true)
|
||||
end
|
||||
|
||||
it "should be false when the song is not playing" do
|
||||
not_playing_song.playing?.should eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
23
spec/lib/mpd_client_spec.rb
Normal file
23
spec/lib/mpd_client_spec.rb
Normal file
@ -0,0 +1,23 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe MPDClient do
|
||||
|
||||
subject { MPDClient }
|
||||
|
||||
before do
|
||||
subject.connect!
|
||||
end
|
||||
|
||||
describe "#listen" do
|
||||
it "exposes `Connection#on` for easy event listening" do
|
||||
subject.conn.should_receive(:on).exactly(3).times
|
||||
|
||||
subject.listen do
|
||||
on(:first) { "something" }
|
||||
on(:second) { "something" }
|
||||
on(:third) { "something" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@ -1,18 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Artist do
|
||||
|
||||
let(:artist) { Artist.new('Alice Cooper') }
|
||||
let(:artists) { ['Alice Cooper', 'Jimmy Eat World', 'Dream Theater'] }
|
||||
|
||||
it 'has attributes' do
|
||||
expect(artist.name).to eq('Alice Cooper')
|
||||
end
|
||||
|
||||
it 'returns all artists' do
|
||||
MPDConnection.mpd.stub(:artists).and_return(artists)
|
||||
expect(Artist.all).to have(3).items
|
||||
expect(Artist.all.map(&:name)).to eq(artists.sort)
|
||||
end
|
||||
|
||||
end
|
||||
@ -1,4 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Control do
|
||||
end
|
||||
@ -1,8 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe MPDClient do
|
||||
it 'should respond to GET' do
|
||||
get '/'
|
||||
expect(last_response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
@ -1,20 +1,19 @@
|
||||
ENV['RACK_ENV'] = 'test'
|
||||
|
||||
require File.join(File.dirname(__FILE__), '..', 'mpd_client.rb')
|
||||
require 'bundler'
|
||||
Bundler.setup
|
||||
Bundler.require(:default, ENV['RACK_ENV'])
|
||||
|
||||
require 'sinatra'
|
||||
require File.expand_path('../lib/mpd_client', __dir__)
|
||||
require 'rspec'
|
||||
require 'rspec/mocks'
|
||||
require 'rack/test'
|
||||
|
||||
# setup test environment
|
||||
set :environment, :test
|
||||
set :run, false
|
||||
set :raise_errors, true
|
||||
set :logging, false
|
||||
|
||||
def app
|
||||
MPDClient
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include Rack::Test::Methods
|
||||
|
||||
config.before(:each) do
|
||||
allow_message_expectations_on_nil
|
||||
MPDClient::Connection.any_instance.stub(:connected?).and_return(true)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Loading…
Reference in New Issue
Block a user