This repository has been archived by the owner on Mar 25, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathpizza.rb
199 lines (191 loc) · 5.85 KB
/
pizza.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#require 'mpd_driver.rb'
require './mpd_driver.rb'
$mpd=MPD.new("chicken.local")
$remote_hosts={}
$playlist_hosts={}
def track_sort_by(filters)
#sensible defaults for now
tag_order=(filters||{}).keys+["Track","Title","file"]
lambda do |track|
#future feature: more straightforward and flexible way of transforming all the tags into a sortable form, accounting of course for nil
vals=tag_order.map {|tag| (track[tag]||"ZZZZZZZZZZ").downcase.gsub(/^(the|a\s+)/,'')}
vals[tag_order.index "Track"]=track["Track"].to_i if tag_order.include? "Track" #in cases like track = '1/12', '2/12', etc., to_i gives us the first number. convenient.
vals
end
end
#SECTION: other functions
get '/' do
redirect '/index.html'
end
get '/status' do
$mpd.status.to_json
end
put '/reload_db' do
$mpd.send_command("update")
$cached_tracks=nil
'["ok"]'
end
#meant to be used with the file_uploader javascript thing
require 'tempfile'
require 'fileutils'
class String
def sanitize_path
gsub(/(\.\.)|\//,'')#HOPEFULLY safe for unix systems
end
def sanitize_path!
gsub!(/(\.\.)|\//,'')
end
end
post '/upload_file' do
fname=params[:qqfile].sanitize_path
t=Tempfile.open(fname) do |f|
f << request.env["rack.input"].read
artist,album = case fname.match(/\.[^.]*$/)[0].downcase #for the extension
when ".mp3"
Mp3Info.open(f.path) {|m| [m.tag["artist"],m.tag["album"]]}
when ".m4a",".mp4","m4v"
m=MP4Info.open(f.path)
[m.ART,m.ALB]
when ".wav"
[nil,nil] #NO METADATA ON WAV, I THINK
when ".wma"
m=WmaInfo.new(f.path)
[m.tags["artist"],m.tags["album"]] #UNTESTED, need wma with acceptable tags
when ".aif"
[nil,nil] #NO IDEA HERE, I don't think I have tagged aif files either
when ".ogg"
OggInfo.open(f.path) {|m| [m.tag["artist"],m.tag["album"]]}
else
[nil,nil] #UNKNOWN EXTENSION, should talk to someone ;)
end
path="/home/moto/Music"+if (artist.nil?)
"/untagged_uploads"
else
artist.sanitize_path!
if album
"/#{artist}/#{album.sanitize_path}"
else
"/#{artist}"
end
end+"/"
FileUtils.mkpath(path)
#TODO: check for filename collision
FileUtils.cp f.path,path+fname
end
{:success=> true }.to_json
end
#SECTION: search functions
def all_tracks
$cached_tracks ||= $mpd.all_tracks
end
get '/all_tracks' do
all_tracks.to_json
end
get '/untagged/:tag' do
all_tracks.select {|x| x[params[:tag]].nil?}.to_json
end
get '/search/:query' do
all_q=params[:query].split(/\s/).map {|q| Regexp.new(q,true)} #it'll be converted to a regex anyway if we use match, and this is easier than downcasing everything for case insensitivity
tracks=all_tracks.select do |entry|
all_q.all? do |query|
entry.any? do |key,value|
query === value
end
end
end
res={"Tracks"=>tracks.sort_by(&track_sort_by(params[:filters]))}
params[:tags].each {|tag| res[tag] = tracks.map{|t| t[tag]}.uniq.sort_by {|x| (x||"").downcase}} if params[:tags]
res.to_json
end
get '/filter' do
available={}
tracks=all_tracks.clone
#WARNING: this function only works *correctly* if the hash is in the order it was in when presented to the application.
#I don't know if this is standard behavior of get params, and if it is, how to guarantee it.
#it does, however, seem to work for the moment
params[:filters].each do |(tag,selected_choices)|
available[tag]=tracks.map {|entry| entry[tag]}.uniq.sort_by {|x| (x||"").downcase}
if (selected_choices) # if they gave us no choices, just return everything.
if i=selected_choices.index("")
selected_choices[i]=nil #for unknown albums
end
tracks.select! {|entry| selected_choices.include? entry[tag]}
end
end
available["Tracks"]=tracks.sort_by(&track_sort_by(params[:filters]))
available.to_json
end
get '/search_and_filter' do
#mostly copy-pasted from the above functions
all_q=params[:query].split(/\s/).map {|q| Regexp.new(q,true)}
tracks=all_tracks.select {|entry| all_q.all? {|query| entry.any? {|key,value| query === value}}}
available={}
params[:filters].each do |(tag,selected_choices)|
available[tag]=tracks.map {|entry| entry[tag]}.uniq.sort_by {|x| (x||"").downcase}
if (selected_choices) # if they gave us no choices, just return everything.
if i=selected_choices.index("") #for unknown albums - it'll be passed as a "" from the client, we convert it to nil for comparison with hash
selected_choices[i]=nil
end
tracks.select! {|entry| selected_choices.include? entry[tag]}
end
end
available["Tracks"]=tracks.sort_by(&track_sort_by(params[:filters]))
available.to_json
end
#SECTION: 'queue' functions. MPD calls these 'playlist' commands, but I'm differentiating here the live queue from saved playlists.
#all commands will return the current queue in JSON
#NOTE: all these will have to add a bit of thread/version safety
post '/queue/add' do
#add params[:filename] to the queue
#TODO: safety here
id=$mpd.add(params[:filename])
if id
$playlist_hosts[id]=request.env["REMOTE_HOST"]
'["ok"]'
else
[404,'no such filename']
end
#TODO: return
end
put '/queue/clear' do
$mpd.send_command("clear")
#TODO: return
'["ok"]'
end
delete '/queue/delete/:pos' do
#TODO: safety here
$mpd.send_command("delete "+params[:pos])
#TODO: return
'["ok"]'
end
put '/queue/move/:song_id' do
$playlist_hosts[params[:song_id]]=request.env["REMOTE_HOST"]
$mpd.send_command("moveid #{params[:song_id]} #{params[:pos]}")
'["ok"]'
end
get '/queue/list' do
$mpd.queue.map {|x| x.merge({"Requester"=>$playlist_hosts[x["Id"]]})}.to_json
end
#SECTION: 'playlist' functions
#SECTION: playback functions
post '/playback/next' do
$mpd.send_command("next")
'["ok"]'
end
post '/playback/previous' do
$mpd.send_command("previous")
'["ok"]'
end
put '/playback/pause' do
#TODO: safety here
$mpd.send_command("pause #{params[:state]}")
'["ok"]'
end
put '/playback/play' do
$mpd.send_command("play #{params[:pos]}")
'["ok"]'
end
put '/playback/setvol' do
$mpd.send_command("setvol #{params[:vol]}")
'["ok"]'
end