mirror of
https://github.com/danbee/mpd-client
synced 2025-03-04 08:39:09 +00:00
WIP: Refactor and Gemify
This commit is contained in:
parent
d4a8cfc4a3
commit
5e45aad95a
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'
|
ruby '2.0.0'
|
||||||
|
|
||||||
|
# todo: get everything working before going whole hog with the gem
|
||||||
|
# gemspec
|
||||||
|
|
||||||
gem 'sinatra'
|
gem 'sinatra'
|
||||||
gem 'sinatra-contrib'
|
gem 'sinatra-contrib'
|
||||||
gem 'sinatra-asset-pipeline'
|
gem 'sinatra-asset-pipeline'
|
||||||
gem 'foreman'
|
gem 'foreman'
|
||||||
|
|
||||||
gem 'ruby-mpd', git: 'git@github.com:archSeer/ruby-mpd.git'
|
gem 'ruby-mpd', git: 'git@github.com:archSeer/ruby-mpd.git'
|
||||||
|
gem 'active_support', require: false
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'pry'
|
gem 'pry'
|
||||||
|
|||||||
14
Gemfile.lock
14
Gemfile.lock
@ -7,6 +7,9 @@ GIT
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
|
active_support (3.0.0)
|
||||||
|
activesupport (= 3.0.0)
|
||||||
|
activesupport (3.0.0)
|
||||||
backports (3.3.5)
|
backports (3.3.5)
|
||||||
coderay (1.0.9)
|
coderay (1.0.9)
|
||||||
coffee-script (2.2.0)
|
coffee-script (2.2.0)
|
||||||
@ -29,7 +32,7 @@ GEM
|
|||||||
method_source (~> 0.8)
|
method_source (~> 0.8)
|
||||||
slop (~> 3.4)
|
slop (~> 3.4)
|
||||||
rack (1.5.2)
|
rack (1.5.2)
|
||||||
rack-protection (1.5.0)
|
rack-protection (1.5.1)
|
||||||
rack
|
rack
|
||||||
rack-test (0.6.2)
|
rack-test (0.6.2)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
@ -45,11 +48,11 @@ GEM
|
|||||||
sass (3.2.12)
|
sass (3.2.12)
|
||||||
shotgun (0.9)
|
shotgun (0.9)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
sinatra (1.4.3)
|
sinatra (1.4.4)
|
||||||
rack (~> 1.4)
|
rack (~> 1.4)
|
||||||
rack-protection (~> 1.4)
|
rack-protection (~> 1.4)
|
||||||
tilt (~> 1.3, >= 1.3.4)
|
tilt (~> 1.3, >= 1.3.4)
|
||||||
sinatra-asset-pipeline (0.2.1)
|
sinatra-asset-pipeline (0.3.3)
|
||||||
coffee-script
|
coffee-script
|
||||||
rake
|
rake
|
||||||
sass
|
sass
|
||||||
@ -65,12 +68,12 @@ GEM
|
|||||||
sinatra (~> 1.4.0)
|
sinatra (~> 1.4.0)
|
||||||
tilt (~> 1.3)
|
tilt (~> 1.3)
|
||||||
slop (3.4.6)
|
slop (3.4.6)
|
||||||
sprockets (2.10.0)
|
sprockets (2.10.1)
|
||||||
hike (~> 1.2)
|
hike (~> 1.2)
|
||||||
multi_json (~> 1.0)
|
multi_json (~> 1.0)
|
||||||
rack (~> 1.0)
|
rack (~> 1.0)
|
||||||
tilt (~> 1.1, != 1.3.0)
|
tilt (~> 1.1, != 1.3.0)
|
||||||
sprockets-helpers (1.0.1)
|
sprockets-helpers (1.1.0)
|
||||||
sprockets (~> 2.0)
|
sprockets (~> 2.0)
|
||||||
sprockets-sass (1.0.2)
|
sprockets-sass (1.0.2)
|
||||||
sprockets (~> 2.0)
|
sprockets (~> 2.0)
|
||||||
@ -86,6 +89,7 @@ PLATFORMS
|
|||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
active_support
|
||||||
foreman
|
foreman
|
||||||
pry
|
pry
|
||||||
rspec
|
rspec
|
||||||
|
|||||||
134
bin/mpd_client
Executable file
134
bin/mpd_client
Executable file
@ -0,0 +1,134 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
ENV['RACK_ENV'] ||= 'development'
|
||||||
|
|
||||||
|
require 'bundler'
|
||||||
|
|
||||||
|
Bundler.setup
|
||||||
|
Bundler.require(:default, ENV['RACK_ENV'])
|
||||||
|
|
||||||
|
require 'sinatra'
|
||||||
|
require 'sinatra/asset_pipeline'
|
||||||
|
|
||||||
|
require 'sass'
|
||||||
|
require 'json'
|
||||||
|
require 'cgi'
|
||||||
|
|
||||||
|
require 'active_support/core_ext/hash/slice'
|
||||||
|
|
||||||
|
require File.expand_path('../lib/mpd_client', __dir__)
|
||||||
|
|
||||||
|
module MPDClient
|
||||||
|
class Application < Sinatra::Base
|
||||||
|
|
||||||
|
set server: 'thin'
|
||||||
|
|
||||||
|
set :root, File.expand_path('../', __dir__).to_s
|
||||||
|
|
||||||
|
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!
|
||||||
|
|
||||||
|
# TODO: Figure out why failing to supplying args breaks stuff
|
||||||
|
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: Song.queue.map(&:to_h) }.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.map(&:to_h).to_json
|
||||||
|
else
|
||||||
|
Album.all.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/artists' do
|
||||||
|
content_type 'application/json'
|
||||||
|
Artist.all.map(&:to_h).to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/songs' do
|
||||||
|
content_type 'application/json'
|
||||||
|
if query = params.slice(:artist, :album) and !query.empty?
|
||||||
|
Song.by(**query).map(&:to_h).to_json
|
||||||
|
else
|
||||||
|
Song.all.sort.map(&:to_h).to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/queue' do
|
||||||
|
content_type 'application/json'
|
||||||
|
{ data: Song.queue.map(&:to_h) }.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'
|
||||||
|
Control.volume(params[:value])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
MPDClient::Application.run!
|
||||||
@ -1,6 +0,0 @@
|
|||||||
require 'bundler'
|
|
||||||
|
|
||||||
Bundler.setup
|
|
||||||
require './mpd_client'
|
|
||||||
|
|
||||||
run MPDClient
|
|
||||||
43
lib/mpd_client.rb
Normal file
43
lib/mpd_client.rb
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
require 'forwardable'
|
||||||
|
require 'ruby-mpd'
|
||||||
|
require 'set'
|
||||||
|
|
||||||
|
require File.expand_path('mpd_client/class_to_proc', __dir__)
|
||||||
|
|
||||||
|
module MPDClient
|
||||||
|
|
||||||
|
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__)
|
||||||
|
|
||||||
|
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
|
||||||
48
lib/mpd_client/album.rb
Normal file
48
lib/mpd_client/album.rb
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
module MPDClient
|
||||||
|
class Album
|
||||||
|
|
||||||
|
include ClassToProc
|
||||||
|
include Enumerable
|
||||||
|
extend Forwardable
|
||||||
|
|
||||||
|
delegate %i(artist genre) => :@first_song
|
||||||
|
|
||||||
|
def initialize(album)
|
||||||
|
@songs = MPDClient::Song.by(album: album)
|
||||||
|
@first_song = @songs.first
|
||||||
|
end
|
||||||
|
|
||||||
|
def each(&block)
|
||||||
|
@songs.each(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def title
|
||||||
|
@first_song.album
|
||||||
|
end
|
||||||
|
|
||||||
|
def year
|
||||||
|
@first_song.year
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_h
|
||||||
|
{
|
||||||
|
title: title,
|
||||||
|
artist: artist,
|
||||||
|
genre: genre,
|
||||||
|
year: year,
|
||||||
|
songs: self.map(&:to_h)
|
||||||
|
}
|
||||||
|
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
|
||||||
12
lib/mpd_client/artist.rb
Normal file
12
lib/mpd_client/artist.rb
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
module MPDClient
|
||||||
|
class Artist
|
||||||
|
|
||||||
|
include ClassToProc
|
||||||
|
|
||||||
|
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
|
||||||
0
lib/mpd_client/server.rb
Normal file
0
lib/mpd_client/server.rb
Normal file
59
lib/mpd_client/song.rb
Normal file
59
lib/mpd_client/song.rb
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
module MPDClient
|
||||||
|
class Song
|
||||||
|
|
||||||
|
include ClassToProc
|
||||||
|
include Comparable
|
||||||
|
extend Forwardable
|
||||||
|
|
||||||
|
delegate %i(id track artist album title time pos) => :@song
|
||||||
|
|
||||||
|
def initialize(song, pos: nil)
|
||||||
|
@song = song
|
||||||
|
end
|
||||||
|
|
||||||
|
def playing?
|
||||||
|
self == self.class.current_song
|
||||||
|
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 queue
|
||||||
|
MPDClient.conn.queue.map(&self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_song
|
||||||
|
new(MPDClient.conn.current_song)
|
||||||
|
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
|
||||||
@ -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
|
|
||||||
32
mpd_client.gemspec
Normal file
32
mpd_client.gemspec
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
lib = File.expand_path('../lib', __FILE__)
|
||||||
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||||
|
require 'mpd_client/version'
|
||||||
|
|
||||||
|
Gem::Specification.new do |spec|
|
||||||
|
spec.name = "mpd_client"
|
||||||
|
spec.version = MPDClient::VERSION
|
||||||
|
spec.authors = ["Dan Barber", "Lee Machin"]
|
||||||
|
spec.email = ["dan@new-bamboo.co.uk", "lee@new-bamboo.co.uk"]
|
||||||
|
spec.description = %q{Tasty web interface for MPD}
|
||||||
|
spec.summary = %q{Tasty web interface for MPD}
|
||||||
|
spec.homepage = ""
|
||||||
|
|
||||||
|
spec.files = `git ls-files`.split($/)
|
||||||
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
||||||
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
||||||
|
spec.require_paths = ["lib"]
|
||||||
|
|
||||||
|
spec.add_development_dependency "bundler", "~> 1.3"
|
||||||
|
spec.add_development_dependency "rake"
|
||||||
|
spec.add_development_dependency "rspec"
|
||||||
|
spec.add_development_dependency "rspec-mocks"
|
||||||
|
spec.add_development_dependency "pry"
|
||||||
|
|
||||||
|
spec.add_dependency "thin"
|
||||||
|
spec.add_dependency "sinatra"
|
||||||
|
spec.add_dependency "sinatra-contrib"
|
||||||
|
spec.add_dependency "sinatra-asset-pipeline"
|
||||||
|
spec.add_dependency "ruby-mpd"
|
||||||
|
spec.add_dependency "active_support"
|
||||||
|
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
|
|
||||||
Loading…
Reference in New Issue
Block a user