8 $
: << MYDIR
= File
.dirname(Pathname(__FILE__
).realpath
)
15 o
= OptionParser
.new
do |opts
|
17 opts
.banner
= "Usage: mpdhero [options]"
18 opts
.on("--version", "display version") do |v
| puts
version; exit
end
19 opts
.on("-v", "--[no-]verbose", "print information about what is happening") do |v
|
22 opts
.on("-q", "--[no-]quiet", "supress warnings") do |v
|
25 opts
.on("-R", "--[no-]more-randomness", "add more randomness in some operations") do |v
|
26 conf
.more_randomness
= v
28 opts
.on("-a", "--shuffle-albums", "shuffle the playlist but keep albums intact") do |v
|
29 conf
.mode
= :shuffle_albums
31 opts
.on("-k", "--shuffle-chunks", "shuffle 20-minute chunks of albums") do |v
|
32 conf
.mode
= :shuffle_chunks
34 opts
.on("-A", "--shuffle-artists", "shuffle the playlist but keep artists intact") do |v
|
35 conf
.mode
= :shuffle_artists
37 opts
.on("-b", "--add-random-album [PATH]", String
, "adds a random album") do |v
|
38 conf
.mode
= :add_random_album
39 conf
.random_album_path
= v
41 opts
.on("-d", "--add-random-song [PATH]", String
, "adds a random song") do |v
|
42 conf
.mode
= :add_random_song
43 conf
.random_song_path
= v
45 opts
.on("-n", "--number n", Numeric
, "specifies a number") do |v
|
48 opts
.on("-l", "--log [n]", Numeric
, "delay the addition of songs by log n (x) minutes") do |v
|
51 opts
.on("-P", "--play", "start playing") do |v
|
54 opts
.on("-C", "--clear", "clear playlist before doing anything") do |v
|
57 opts
.on("-c", "--[no-]current-first", "put current playling song/album first, when shuffling") do |v
|
58 conf
.current_first
= v
60 opts
.on("-h", "--host IP", String
, "the mpd server, defaults to MPD_HOST || localhost") do |v
|
63 opts
.on("-p", "--port PORT", Integer
, "the mpd server port, defaults to MPD_PORT || 6600") do |v
|
66 opts
.on("-r", "--remove-current", "removes the current entry/entries if something is added") do |v
|
67 conf
.remove_current
= v
71 o
.parse
! rescue abort $
!.name
, $
!.msg
74 puts oparser
.help
or exit
if !conf
.mode
75 $stderr.reopen('/dev/null', 'w') if conf
.quiet
76 $stdout.reopen('/dev/null', 'w') if !conf
.verbose
77 Mpd
= MPD
.new(conf
.host
, conf
.port
)
78 Mpd
.connect
rescue abort
"failed to connect"
80 def sleep_log(base
, x
)
82 n
= Math
.log(x
+1) / Math
.log(base
) * 60.0
90 def splitter(list
, average
) #{{{
92 average
= average
.to_f
94 for name
, album
in list
95 total_time
= album
.time
96 parts
= [[ (total_time
/ average
).round
, album
.length
].min
, 1].max
97 avg
= total_time
/ parts
100 stack
= Array
.new(album
)
101 while item
= stack
.shift
104 elsif part
.time
< avg
105 if part
.time
+ item
.time
.to_i
< avg
* 1.5
119 # puts "##{album.length} #{total_time} (#{total_time/60}m) / #{parts}"
120 # for chunk in chunks do print "##{chunk.length} #{chunk.time/60}m -- " end
126 Mpd
.clear
if conf
.clear
132 ## albums will be {"album1" => [track3, track1, track2], "album2" => [...], ...}
134 for track
in Mpd
.playlistinfo
do
135 (albums
[track
.album
] ||= []) << track
138 ## make sure tracks within albums are sorted
139 for key
, album
in albums
do
140 album
.sort
! {|a
, b
| a
.track
<=> b
.track
}
143 list
= albums
.keys
.shuffle
!
145 if conf
.current_first
146 list
.swap
!(0, list
.index(Mpd
.currentsong
.album
))
151 for album_key
in list
do
152 puts
"%-30s - %s" % [albums
[album_key
].first
.artist
, album_key
] if conf
.verbose
153 for track
in albums
[album_key
] do
154 arr
<< [track
.dbid
, i
+=1]
155 # Mpd.moveid(track.dbid, i) if track.dbid != i
159 Mpd
.play
if conf
.play
163 ## albums will be {"album1" => [track3, track1, track2], "album2" => [...], ...}
165 for track
in Mpd
.playlistinfo
do
166 (albums
[File
.dirname(track
.file
)] ||= []) << track
173 if conf
.more_randomness
174 ## shuffle album tracks
175 for key
, album
in albums
do
179 ## make sure tracks within albums are sorted
180 for key
, album
in albums
do
181 album
.sort
! {|a
, b
| (a
.track
|| '') <=> (b
.track
|| '')}
185 list
= splitter(albums
, 1200)
188 if conf
.current_first
189 list
.each_with_index
do |chunk
, i
|
190 if chunk
.include? Mpd
.currentsong
200 puts
"%-30s - %s" % [chunk
.first
.artist
, chunk
.first
.album
] if conf
.verbose
201 for track
in chunk
do
202 arr
<< [track
.dbid
, i
+=1]
203 # Mpd.moveid(track.dbid, i) if track.dbid != i
207 Mpd
.play
if conf
.play
209 when :shuffle_artists
211 ## artists will be {"album1" => [track3, track1, track2], "album2" => [...], ...}
213 for track
in Mpd
.playlistinfo
do
214 (artists
[track
.artist
] ||= []) << track
217 ## make sure tracks within artists are sorted
218 # for key, album in artists do
219 # album.sort! {|a, b| a.track <=> b.track}
222 list
= artists
.keys
.shuffle
!
224 if conf
.current_first
225 list
.swap
!(0, list
.index(Mpd
.currentsong
.artist
))
230 for artist_key
in list
do
231 puts
"%-30s - %s" % [artists
[artist_key
].first
.artist
, artist_key
] if conf
.verbose
232 for track
in artists
[artist_key
] do
233 arr
<< [track
.dbid
, i
+=1]
234 # Mpd.moveid(track.dbid, i) if track.dbid != i
238 Mpd
.play
if conf
.play
240 when :add_random_album
242 # get a list of all files in the given path
244 all
= Mpd
.listall(conf
.random_album_path
|| '/')
247 # get a random song and find all in the same album
248 one
= Mpd
.find("filename", all
.random
).first
249 found
= Mpd
.find('album', one
.album
)
251 # remove unwanted entries (duplicates, other artist with same album)
253 found
= found
.select
do |this
|
254 if this
.artist
== one
.artist
and !memory
.include?(this
.title
)
267 Mpd
.play
if conf
.play
&& x
== 0
268 sleep_log(conf
.log
, x
)
271 when :add_random_song
273 all
= Mpd
.listall(conf
.random_song_path
|| '/')
274 if (conf
.remove_current
)
275 Mpd
.delete(Mpd
.currentsong
.pos
)
277 xstart
= Mpd
.playlistlength
279 Mpd
.add(Mpd
.find("filename", all
.random
).first
.file
)
280 Mpd
.play
if conf
.play
&& x
== 0
281 sleep_log(conf
.log
, x
+ xstart
)
285 puts
"no mode specified"