the_little_streamer 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ba04105936f88f2cea71bf565934e892590cbf6f
4
+ data.tar.gz: 7da1b197bfc2e8ddac3bf5f3cee5faad14a09b8a
5
+ SHA512:
6
+ metadata.gz: b8ba19974db05fc9e901b76a06945e6f01eac9954bff693baf50060a80593375b8456084967e585cee38c21e8defb4a93d522702f9bced89847ba7f9a87f359f
7
+ data.tar.gz: e249d40b2a7ab750448890d33c3226e5c16d038c574a9abb59f9aa8d973f6552e4b5d780212d3e1c4546a3af47123b54d3be0d20dc9d02219c6f83a28b629e64
data/README.md CHANGED
@@ -15,7 +15,7 @@ Browse to [http://localhost:4567](http://localhost:4567)
15
15
  ## Dependencies
16
16
 
17
17
  * [Sinatra](http://www.sinatrarb.com/)
18
- * [ruby-taglib2](https://github.com/rumblehq/ruby-taglib2)
18
+ * [taglib-ruby](http://robinst.github.io/taglib-ruby/)
19
19
  * A browser that supports HTML5, like [Chrome](http://www.google.com/chrome/)
20
20
 
21
21
  You are strongly encouraged to use [Mongrel](https://github.com/fauna/mongrel) or [Thin](http://code.macournoyer.com/thin/) (installable as gems) instead of WEBrick, as they will be way faster. Just install one and Sinatra will prefer it over WEBrick.
@@ -1,13 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
  #This is a simple Sinatra application to play mp3/ogg files through a browser
3
3
  #using the HTML5 audio tag
4
+ $LOAD_PATH.unshift "./lib"
4
5
 
5
6
  require 'rubygems'
6
- require 'taglib2'
7
7
  require 'sinatra'
8
- require 'cgi'
9
- require 'uri'
10
8
  require 'pathname'
9
+ require 'the_little_streamer'
11
10
 
12
11
  #Supply a directory with all the music in it
13
12
  abort "Point me to the music!" unless ARGV[0]
@@ -17,280 +16,6 @@ trap "INT" do
17
16
  exit!
18
17
  end
19
18
 
20
- #Wrap up in HTML
21
- def html body
22
- <<-HTML
23
- <html>
24
- <head>
25
- <style type="text/css">
26
- #{css}
27
- </style>
28
- <title>The Little Streamer</title>
29
- </head>
30
- <body>
31
- #{body}
32
- </body>
33
- <script>
34
- if(document.getElementById('artist')) {
35
- window.onload = function() {
36
- var artist = document.getElementById('artist').firstChild.innerText;
37
- var title = document.getElementById('title').innerHTML;
38
- document.title = title + " by " + artist;
39
- }
40
- }
41
- </script>
42
- </html>
43
- HTML
44
- end
45
-
46
- def css
47
- <<-CSS
48
- body {
49
- font-family: Helvetica,Verdana,Arial,sans-serif;
50
- font-size: 13pt;
51
- }
52
-
53
- span#title {
54
- font-size: 14pt;
55
- font-weight: bold;
56
- }
57
-
58
- span#artist {
59
- font-size: 14pt;
60
- font-weight: bold;
61
- }
62
-
63
- span#artist a {
64
- text-decoration: none;
65
- color: black;
66
- }
67
-
68
- span#artist a:hover {
69
- text-decoration: underline;
70
- }
71
-
72
- span#album {
73
- font-style: italic;
74
- }
75
-
76
- span#album a {
77
- text-decoration: none;
78
- color: black;
79
- }
80
-
81
- span#album a:hover {
82
- text-decoration: underline;
83
- }
84
-
85
- div#playlist {
86
- float: left;
87
- margin-top: 15px;
88
- height: 80%;
89
- overflow: auto;
90
- text-align: left;
91
- }
92
-
93
- div#playerbox {
94
- float: left;
95
- text-align: center;
96
- }
97
- CSS
98
- end
99
-
100
- #Create a link
101
- def link root, path, text = path, params = {}
102
- full_path = root.split('/').map! {|r| URI.escape r }.join('/') << "/" << URI.escape(path)
103
- params = params.map { |k, v| "#{k}=#{v}" }.join "&"
104
- "<a href=\"#{full_path}?#{params}\">#{text}</a>"
105
- end
106
-
107
- #Some navigation
108
- def title artist, album = nil
109
- if album
110
- "<h3>#{link "/", "", "Music"} : #{link "/artist/", artist, artist} : #{album}</h3>"
111
- else
112
- "<h3>#{link "/", "", "Music"} : #{artist}</h3>"
113
- end
114
- end
115
-
116
- #Audio tag
117
- def audio path
118
- <<-HTML
119
- <audio id='player' onEnded='javascript:play_next()' src=#{("/" << URI.escape(path)).inspect} autobuffer controls autoplay >
120
- You need the power of HTML5!
121
- </audio>
122
- <script type="text/javascript">
123
- document.getElementById('player').volume = 0.3;
124
- </script>
125
- HTML
126
- end
127
-
128
- #HTML for displaying playlist
129
- def playlist_html songs
130
- list = []
131
-
132
- songs.each_with_index do |song, i|
133
- list << "<li><a href=\"javascript:play_index(#{i})\">#{song.artist} - #{song.title}</a></li>"
134
- end
135
-
136
- <<-HTML
137
- <div style="clear:both">&nbsp;</div>
138
- <div id="playlist">
139
- <ol>
140
- #{list.join}
141
- </ol>
142
- </div>
143
- HTML
144
- end
145
-
146
- #Javascript for playlist
147
- def playlist_js songs
148
- <<-JAVASCRIPT
149
- <script type="text/javascript">
150
- var player = document.getElementById('player');
151
- var artist = document.getElementById('artist');
152
- var album = document.getElementById('album');
153
- var title = document.getElementById('title');
154
- var playlist = [#{songs.map { |s| song_to_js s }.join "," }]
155
- var current_song = 0;
156
-
157
- play_next = function(reverse) {
158
- if(reverse && current_song > 0)
159
- current_song--;
160
- else if(!reverse && current_song < playlist.length)
161
- current_song++;
162
- else
163
- return;
164
-
165
- play_current();
166
- }
167
-
168
- play_current = function() {
169
- var song = playlist[current_song];
170
- player.src = song.path;
171
- artist.innerHTML = song.artist
172
- album.innerHTML = song.album
173
- title.innerHTML = song.title
174
- document.title = song.title + " by " + artist.firstChild.innerText;
175
- player.play();
176
- }
177
-
178
- play_index = function(index) {
179
- current_song = index;
180
- play_current();
181
- }
182
- </script>
183
- JAVASCRIPT
184
- end
185
-
186
- #Javascript for a song
187
- def song_to_js song
188
- <<-JAVASCRIPT
189
- { artist: #{link("/artist", song.artist, song.artist).inspect},
190
- album: #{link("/artist/#{song.artist}/album/", song.album, song.album).inspect},
191
- title: #{song.title.inspect},
192
- path: #{(CGI.escapeHTML "/" << CGI.escape(song.path)).inspect} }
193
- JAVASCRIPT
194
- end
195
-
196
- #Back/forward links
197
- def prev_and_next
198
- "<a href='javascript:play_next(true)'>Prev</a>&nbsp;&nbsp;&nbsp;<a href='javascript:play_next()'>Next</a>"
199
- end
200
-
201
- #Output HTML for player
202
- def player_html songs
203
- html <<-HTML
204
- <div id="playerbox">
205
- #{song_info songs.first}<br/>
206
- #{audio songs.first.path}<br/>
207
- #{prev_and_next if songs.length > 1}
208
- #{playlist_js songs}
209
- #{playlist_html songs if songs.length > 1}
210
- <div style="clear:both">#{link "/", "", "Back to All Music"}</div>
211
- </div>
212
- HTML
213
- end
214
-
215
- def play_all limit = 100, order = "normal"
216
- songs = []
217
-
218
- case order
219
- when "random"
220
- Music.values.each do |artist|
221
- artist.values.each do |album|
222
- album.values.each do |song|
223
- songs << song
224
- end
225
- end
226
- end
227
-
228
- songs.shuffle!
229
- else
230
- Music.values.each do |artist|
231
- artist.values.each do |album|
232
- album.values.sort_by { |s| s.track }.each do |song|
233
- songs << song
234
- end
235
- end
236
- end
237
- end
238
-
239
- player_html songs[0..limit.to_i]
240
- end
241
-
242
- #HTML for playing all songs by artist
243
- def play_artist artist, order = "normal"
244
- songs = []
245
-
246
- case order
247
- when "random"
248
- Music[artist].each_value do |album|
249
- album.each_value do |song|
250
- songs << song
251
- end
252
- end
253
-
254
- songs.shuffle!
255
- else
256
- Music[artist].each_value do |album|
257
- album.values.sort_by { |s| s.track }.each do |song|
258
- songs << song
259
- end
260
- end
261
- end
262
-
263
- player_html songs
264
- end
265
-
266
- #HTML for playing all songs on an album
267
- def play_album artist, album, order = "normal"
268
-
269
- case order
270
- when "random"
271
- songs = Music[artist][album].values.shuffle!
272
- else
273
- songs = Music[artist][album].values.sort_by { |s| s.track }
274
- end
275
-
276
- player_html songs
277
- end
278
-
279
- #HTML for song information header
280
- def song_info song
281
- "<span id='title'>#{song.title}</span> by <span id='artist'>#{link "/artist/", song.artist}</span><br/><span id='album'>#{link "/artist/#{song.artist}/album", song.album}</span>"
282
- end
283
-
284
- #Storing all music information in here
285
- Music = Hash.new do |h,k|
286
- h[k] = Hash.new do |hash, key|
287
- hash[key] = Hash.new
288
- end
289
- end
290
-
291
- #Song information
292
- Song = Struct.new :artist, :album, :title, :track, :path
293
-
294
19
  #Used to figure out path to music files
295
20
  root = Pathname.new ARGV[0]
296
21
 
@@ -302,101 +27,42 @@ set :run, true
302
27
 
303
28
  $stderr.puts "Scanning music..."
304
29
 
305
- old_stderr = $stderr.dup
30
+ music = TLS::Music.new(TLS::Finder.new(root, ARGV[0]))
306
31
 
307
- $stderr.reopen("/dev/null", "w")
308
-
309
- #Grab information from all the song files
310
- Dir.glob "#{ARGV[0]}/**/*.{mp3,ogg}", File::FNM_CASEFOLD do |file|
311
- begin
312
- info = TagLib2::File.new(file)
313
-
314
- artist, album, title, track = info.artist, info.album, info.title, info.track
315
- artist = "Unknown" if artist.nil? or artist.empty?
316
- album = "Unknown" if album.nil? or album.empty?
317
-
318
- info = nil
319
-
320
- if title
321
- [artist, album, title].each { |i| i.tr!('/', '-') }
322
- Music[artist][album][title] = Song.new(artist, album, title, track, Pathname.new(file).relative_path_from(root).to_s)
323
- end
324
- rescue Exception => e
325
- $stderr.puts e if $DEBUG
326
- end
32
+ before do
33
+ @player = TLS::Player.new(music)
327
34
  end
328
35
 
329
- $stderr = old_stderr
330
-
331
- GC.start #Clean up after TagLib2
332
-
333
36
  #List artists
334
37
  get '/' do
335
- path = '/artist/'
336
- html <<-HTML
337
- (Random
338
- #{link("/", "all", "10", :limit => 10, :order => :random)}
339
- #{link("/", "all", "50", :limit => 50, :order => :random) }
340
- #{link("/", "all", "100", :limit => 100, :order => :random) })<br/><hr/>
341
-
342
- #{Music.keys.sort.map { |a| link path, a }.join "<br/>"}<br/><br/>
343
- HTML
38
+ @player.list_artists
344
39
  end
345
40
 
346
41
  get '/all' do
347
- play_all params["limit"], params["order"]
42
+ @player.play_all params["limit"], params["order"]
348
43
  end
349
44
 
350
45
  #List albums by given artist
351
46
  get '/artist/:artist/?' do |artist|
352
- unless Music[artist].empty?
353
- path = "/artist/#{artist}/album/"
354
- html title(artist) <<
355
- Music[artist].keys.sort.map { |a| link path, a }.join("<br/>") << "<br/><br/>" <<
356
- link("/artist/#{artist}/", "play", "Play All") << " - " <<
357
- link("/artist/#{artist}/", "play", "Randomly", :order => :random)
358
-
359
- else
360
- html "Could not find <b>#{artist}</b>."
361
- end
47
+ @player.list_albums_by artist
362
48
  end
363
49
 
364
50
  #Play songs by given artist
365
51
  get '/artist/:artist/play/?' do |artist|
366
- unless Music[artist].empty?
367
- play_artist artist, params["order"]
368
- else
369
- html "Could not find <b>#{artist}</b>."
370
- end
52
+ @player.play_artist artist, params["order"]
371
53
  end
372
54
 
373
55
  #List songs on given album
374
56
  get '/artist/:artist/album/:album/?' do |artist, album|
375
- unless Music[artist][album].empty?
376
- path = "/artist/#{artist}/album/#{album}/song/"
377
- html title(artist, album) <<
378
- Music[artist][album].values.sort_by { |s| s.track || 0}.map { |s| link path, s.title }.join("<br/>") << "<br/><br/>" <<
379
- link("/artist/#{artist}/album/#{album}/", "play", "Play All") << " - " <<
380
- link("/artist/#{artist}/album/#{album}/", "play", "Randomly", :order => :random)
381
- else
382
- html "Could not find <b>#{album}</b> for <b>#{artist}</b>."
383
- end
57
+ @player.list_songs_on artist, album
384
58
  end
385
59
 
386
60
  #Play all songs from a given album
387
61
  get '/artist/:artist/album/:album/play/?' do |artist, album|
388
- if Music[artist][album]
389
- play_album artist, album, params["order"]
390
- else
391
- html "Could not find <b>#{album}</b> by <b>#{artist}</b>."
392
- end
62
+ @player.play_album artist, album, params["order"]
393
63
  end
394
64
 
395
65
  #Play song!
396
66
  get '/artist/:artist/album/:album/song/:song/?' do |artist, album, title|
397
- if song = Music[artist][album][title]
398
- player_html [song]
399
- else
400
- html "Could not find <b>#{title}</b> on <b>#{album}</b> by <b>#{artist}</b>."
401
- end
67
+ @player.play_song artist, album, title
402
68
  end
@@ -0,0 +1,72 @@
1
+ require 'taglib'
2
+ require 'the_little_streamer/song'
3
+
4
+ module TLS
5
+ class Finder
6
+ def initialize root, path
7
+ @root = root
8
+ @paths = [path] #maybe support multiple paths in future
9
+ @songs = nil
10
+ end
11
+
12
+ def each_song &block
13
+ if @songs
14
+ @songs.each(&block)
15
+ else
16
+ get_songs(&block)
17
+ end
18
+ end
19
+
20
+ def each_file
21
+ @paths.each do |path|
22
+ Dir.glob "#{path}/**/*.{mp3,mP3,Mp3,MP3,ogg,Ogg,OGg,OGG,oGg,oGG,ogG,OgG}" do |file|
23
+ yield file
24
+ end
25
+ end
26
+ end
27
+
28
+ def get_songs
29
+ puts "Getting songs..."
30
+ @songs = []
31
+
32
+ each_file do |file|
33
+ song = get_song file
34
+
35
+ if song
36
+ @songs << song
37
+ yield song
38
+ end
39
+ end
40
+
41
+ GC.start #Clean up after TagLib2
42
+ @songs
43
+ end
44
+
45
+ def get_song file
46
+ song = nil
47
+
48
+ begin
49
+ TagLib::FileRef.open(file) do |fileref|
50
+ info = fileref.tag
51
+
52
+ artist, album, title, track = info.artist, info.album, info.title, info.track
53
+
54
+ artist = "Unknown" if artist.nil? or artist.empty?
55
+ album = "Unknown" if album.nil? or album.empty?
56
+ title = File.basename(file).split('.').first if title.nil? or title.empty?
57
+
58
+ if title
59
+ [artist, album, title].each { |i| i.tr!('/', '-') }
60
+
61
+ song = Song.new(artist, album, title, track, Pathname.new(file).relative_path_from(@root).to_s)
62
+ end
63
+ end
64
+ rescue Exception => e
65
+ p e
66
+ #Should probably do something here?
67
+ end
68
+
69
+ song
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,219 @@
1
+ module TLS::HTML
2
+ #Wrap up in HTML
3
+ def page body = nil
4
+ if block_given?
5
+ body = yield
6
+ end
7
+
8
+ <<-HTML
9
+ <html>
10
+ <head>
11
+ <style type="text/css">
12
+ #{css}
13
+ </style>
14
+ <title>The Little Streamer</title>
15
+ </head>
16
+ <body>
17
+ #{body}
18
+ </body>
19
+ <script>
20
+ if(document.getElementById('artist')) {
21
+ window.onload = function() {
22
+ var artist = document.getElementById('artist').firstChild.innerText;
23
+ var title = document.getElementById('title').innerHTML;
24
+ document.title = title + " by " + artist;
25
+ }
26
+ }
27
+ </script>
28
+ </html>
29
+ HTML
30
+ end
31
+
32
+ def css
33
+ @css ||= <<-CSS
34
+ body {
35
+ font-family: Helvetica,Verdana,Arial,sans-serif;
36
+ font-size: 13pt;
37
+ }
38
+
39
+ span#title {
40
+ font-size: 14pt;
41
+ font-weight: bold;
42
+ }
43
+
44
+ span#artist {
45
+ font-size: 14pt;
46
+ font-weight: bold;
47
+ }
48
+
49
+ span#artist a {
50
+ text-decoration: none;
51
+ color: black;
52
+ }
53
+
54
+ span#artist a:hover {
55
+ text-decoration: underline;
56
+ }
57
+
58
+ span#album {
59
+ font-style: italic;
60
+ }
61
+
62
+ span#album a {
63
+ text-decoration: none;
64
+ color: black;
65
+ }
66
+
67
+ span#album a:hover {
68
+ text-decoration: underline;
69
+ }
70
+
71
+ div#playlist {
72
+ float: left;
73
+ margin-top: 15px;
74
+ height: 80%;
75
+ overflow: auto;
76
+ text-align: left;
77
+ }
78
+
79
+ div#playerbox {
80
+ float: left;
81
+ text-align: center;
82
+ }
83
+ CSS
84
+
85
+ @css
86
+ end
87
+
88
+ #Create a link
89
+ def link root, path, text = path, params = {}
90
+ full_path = root.split('/').map! {|r| URI.escape r }.join('/') << "/" << URI.escape(path)
91
+ params = params.map { |k, v| "#{k}=#{v}" }.join "&"
92
+ "<a href=\"#{full_path}?#{params}\">#{text}</a>"
93
+ end
94
+
95
+ #Some navigation
96
+ def title artist, album = nil
97
+ if album
98
+ "<h3>#{link "/", "", "Music"} : #{link "/artist/", artist, artist} : #{album}</h3>"
99
+ else
100
+ "<h3>#{link "/", "", "Music"} : #{artist}</h3>"
101
+ end
102
+ end
103
+
104
+ #Audio tag
105
+ def audio path
106
+ <<-HTML
107
+ <audio id='player' onEnded='javascript:play_next()' src=#{("/" << URI.escape(path)).inspect} autobuffer controls autoplay >
108
+ You need the power of HTML5!
109
+ </audio>
110
+ <script type="text/javascript">
111
+ document.getElementById('player').volume = 0.3;
112
+ </script>
113
+ HTML
114
+ end
115
+
116
+ #HTML for displaying playlist
117
+ def playlist_html songs
118
+ list = []
119
+
120
+ songs.each_with_index do |song, i|
121
+ list << "<li><a href=\"javascript:play_index(#{i})\">#{song.artist} - #{song.title}</a></li>"
122
+ end
123
+
124
+ <<-HTML
125
+ <div style="clear:both">&nbsp;</div>
126
+ <div id="playlist">
127
+ <ol>
128
+ #{list.join}
129
+ </ol>
130
+ </div>
131
+ HTML
132
+ end
133
+
134
+ #Javascript for playlist
135
+ def playlist_js songs
136
+ <<-JAVASCRIPT
137
+ <script type="text/javascript">
138
+ var player = document.getElementById('player');
139
+ var artist = document.getElementById('artist');
140
+ var album = document.getElementById('album');
141
+ var title = document.getElementById('title');
142
+ var playlist = [#{songs.map { |s| song_to_js s }.join "," }]
143
+ var current_song = 0;
144
+
145
+ play_next = function(reverse) {
146
+ if(reverse && current_song > 0)
147
+ current_song--;
148
+ else if(!reverse && current_song < playlist.length)
149
+ current_song++;
150
+ else
151
+ return;
152
+
153
+ play_current();
154
+ }
155
+
156
+ play_current = function() {
157
+ var song = playlist[current_song];
158
+ player.src = song.path;
159
+ artist.innerHTML = song.artist
160
+ album.innerHTML = song.album
161
+ title.innerHTML = song.title
162
+ document.title = song.title + " by " + artist.firstChild.innerText;
163
+ player.play();
164
+ }
165
+
166
+ play_index = function(index) {
167
+ current_song = index;
168
+ play_current();
169
+ }
170
+ </script>
171
+ JAVASCRIPT
172
+ end
173
+
174
+ #Javascript for a song
175
+ def song_to_js song
176
+ <<-JAVASCRIPT
177
+ { artist: #{link("/artist", song.artist, song.artist).inspect},
178
+ album: #{link("/artist/#{song.artist}/album/", song.album, song.album).inspect},
179
+ title: #{song.title.inspect},
180
+ path: #{(CGI.escapeHTML "/" << CGI.escape(song.path)).inspect} }
181
+ JAVASCRIPT
182
+ end
183
+
184
+ #HTML for song information header
185
+ def song_info song
186
+ "<span id='title'>#{song.title}</span> by <span id='artist'>#{link "/artist/", song.artist}</span><br/><span id='album'>#{link "/artist/#{song.artist}/album", song.album}</span>"
187
+ end
188
+
189
+ #Back/forward links
190
+ def prev_and_next
191
+ @prev_and_next ||= "<a href='javascript:play_next(true)'>Prev</a>&nbsp;&nbsp;&nbsp;<a href='javascript:play_next()'>Next</a>"
192
+ @prev_and_next
193
+ end
194
+
195
+ #Output HTML for player
196
+ def player_html songs
197
+ page <<-HTML
198
+ <div id="playerbox">
199
+ #{song_info songs.first}<br/>
200
+ #{audio songs.first.path}<br/>
201
+ #{prev_and_next if songs.length > 1}
202
+ #{playlist_js songs}
203
+ #{playlist_html songs if songs.length > 1}
204
+ <div style="clear:both">#{link "/", "", "Back to All Music"}</div>
205
+ </div>
206
+ HTML
207
+ end
208
+
209
+ def search_box
210
+ <<-HTML
211
+ <div id="searchbox">
212
+ <form action="/search">
213
+ <label for="search">Search:</label>
214
+ <input type="text" />
215
+ </form>
216
+ </div>
217
+ HTML
218
+ end
219
+ end
@@ -0,0 +1,83 @@
1
+ class TLS::Music
2
+ def initialize finder
3
+ #Hash three levels deep for artist, album, and song
4
+ @music = Hash.new do |h,k|
5
+ h[k] = Hash.new do |hash, key|
6
+ hash[key] = Hash.new
7
+ end
8
+ end
9
+
10
+ @songs = []
11
+
12
+ finder.each_song do |song|
13
+ add_song song
14
+ end
15
+ end
16
+
17
+ def add_song song
18
+ @music[song.artist][song.album][song.title] = song
19
+ @songs << song
20
+ end
21
+
22
+ def [] artist, album = nil, title = nil
23
+ if title
24
+ get_song artist, album, title
25
+ elsif album
26
+ get_album artist, album
27
+ elsif artist
28
+ get_artist artist
29
+ end
30
+ end
31
+
32
+ def all_songs
33
+ @songs
34
+ end
35
+
36
+ def has? artist, album = nil, title = nil
37
+ not self[artist, album, title].empty?
38
+ end
39
+
40
+ def get_artist artist
41
+ @music[artist]
42
+ end
43
+
44
+ def get_album artist, album
45
+ @music[artist][album]
46
+ end
47
+
48
+ def get_song artist, album, title
49
+ @music[artist][album][title]
50
+ end
51
+
52
+ def artist_names
53
+ @music.keys.sort!
54
+ end
55
+
56
+ def album_names_by artist
57
+ get_artist(artist).keys.sort!
58
+ end
59
+
60
+ def song_names_on artist, album
61
+ get_album(artist, album).keys.sort!
62
+ end
63
+
64
+ def songs_on artist, album
65
+ get_album(artist, album).values
66
+ end
67
+
68
+ def songs_by artist
69
+ songs = []
70
+
71
+ get_artist(artist).each_value do |album|
72
+ album.each_value do |song|
73
+ songs << song
74
+ end
75
+ end
76
+
77
+ songs
78
+ end
79
+
80
+ def albums_by artist
81
+ get_artist(artist).values
82
+ end
83
+ end
@@ -0,0 +1,127 @@
1
+ class TLS::Player
2
+ include TLS::HTML
3
+
4
+ def initialize music
5
+ @music = music
6
+ end
7
+
8
+ def play_all limit = 100, order = "normal"
9
+ case order
10
+ when "random"
11
+ songs = @music.all_songs.shuffle
12
+ else
13
+ songs = @music.all_songs
14
+ end
15
+
16
+ player_html songs[0..limit.to_i]
17
+ end
18
+
19
+ #HTML for playing all songs by artist
20
+ def play_artist artist, order = "normal"
21
+ if @music.has? artist
22
+ songs = []
23
+
24
+ case order
25
+ when "random"
26
+ songs = @music.songs_by(artist).shuffle!
27
+ else
28
+ @music.album_names_by(artist).each do |album|
29
+ @music.get_album(artist, album).values.sort_by { |s| s.track }.each do |song|
30
+ songs << song
31
+ end
32
+ end
33
+ end
34
+
35
+ player_html songs
36
+ else
37
+ artist_not_found artist
38
+ end
39
+ end
40
+
41
+ #HTML for playing all songs on an album
42
+ def play_album artist, album, order = "normal"
43
+ if @music.has? artist, album
44
+ if order == "random"
45
+ songs = @music.songs_on(artist, album).shuffle!
46
+ else
47
+ songs = @music.songs_on(artist, album).sort_by { |s| s.track }
48
+ end
49
+
50
+ player_html songs
51
+ else
52
+ album_not_found artist, album
53
+ end
54
+ end
55
+
56
+ def play_song artist, album, title
57
+ song = @music.get_song(artist, album, title)
58
+
59
+ if song
60
+ player_html [song]
61
+ else
62
+ song_not_found
63
+ end
64
+ end
65
+
66
+ def artist_not_found artist
67
+ page "Could not find <b>#{artist}</b>."
68
+ end
69
+
70
+ def song_not_found artist, album, title
71
+ page "Could not find <b>#{title}</b> on <b>#{album}</b> by <b>#{artist}</b>."
72
+ end
73
+
74
+ def album_not_found artist, album
75
+ page "Could not find <b>#{album}</b> by <b>#{artist}</b>."
76
+ end
77
+
78
+ def list_artists
79
+ path = '/artist/'
80
+ page <<-HTML
81
+ (Random
82
+ #{link("/", "all", "10", :limit => 10, :order => :random)}
83
+ #{link("/", "all", "50", :limit => 50, :order => :random) }
84
+ #{link("/", "all", "100", :limit => 100, :order => :random) })
85
+ #{search_box}
86
+ <br/><hr/>
87
+ #{@music.artist_names.map { |a| link path, a }.join "<br/>"}
88
+ <br/><br/>
89
+ HTML
90
+ end
91
+
92
+ def list_albums_by artist
93
+ if @music.has? artist
94
+ path = "/artist/#{artist}/album/"
95
+
96
+ page <<-HTML
97
+ #{title(artist)}
98
+ #{@music.album_names_by(artist).map { |a| link path, a }.join("<br/>")}
99
+ <br/><br/>
100
+ #{link("/artist/#{artist}/", "play", "Play All")} -
101
+ #{link("/artist/#{artist}/", "play", "Randomly", :order => :random)}
102
+ HTML
103
+ else
104
+ artist_not_found artist
105
+ end
106
+ end
107
+
108
+ def list_songs_on artist, album
109
+ if @music.has? artist, album
110
+ path = "/artist/#{artist}/album/#{album}/song/"
111
+
112
+ page <<-HTML
113
+ #{title(artist, album)}
114
+ #{@music.songs_on(artist, album).sort_by { |s| s.track || 0}.map { |s| link path, s.title }.join("<br/>")}
115
+ <br/><br/>
116
+ #{link "/artist/#{artist}/album/#{album}/", "play", "Play All" } -
117
+ #{link "/artist/#{artist}/album/#{album}/", "play", "Randomly", :order => :random }
118
+ HTML
119
+ else
120
+ album_not_found artist, album
121
+ end
122
+ end
123
+
124
+ def list_search search
125
+ songs = @music.find search
126
+ end
127
+ end
@@ -0,0 +1,21 @@
1
+ module TLS
2
+ class Searcher
3
+ def initialize music
4
+ @music = music
5
+ end
6
+
7
+ def find_all search
8
+ search = normalize search
9
+
10
+ @music.all_songs.select do |song|
11
+ song.title.downcase.include? search or
12
+ song.album.downcase.include? search or
13
+ song.artist.downcase.include? search
14
+ end
15
+ end
16
+
17
+ def normalize search
18
+ search.strip.gsub(/\s{2,}/, ' ').downcase
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,4 @@
1
+ module TLS
2
+ #Song information
3
+ Song = Struct.new :artist, :album, :title, :track, :path
4
+ end
@@ -0,0 +1,7 @@
1
+ require 'cgi'
2
+ require 'uri'
3
+ require 'the_little_streamer/song'
4
+ require 'the_little_streamer/finder'
5
+ require 'the_little_streamer/music'
6
+ require 'the_little_streamer/html'
7
+ require 'the_little_streamer/player'
metadata CHANGED
@@ -1,95 +1,81 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: the_little_streamer
3
- version: !ruby/object:Gem::Version
4
- hash: 11
5
- prerelease:
6
- segments:
7
- - 0
8
- - 4
9
- - 2
10
- version: 0.4.2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.3
11
5
  platform: ruby
12
- authors:
6
+ authors:
13
7
  - Justin Collins
14
8
  autorequire:
15
9
  bindir: bin
16
10
  cert_chain: []
17
-
18
- date: 2012-02-26 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
11
+ date: 2013-08-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
21
14
  name: sinatra
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- none: false
25
- requirements:
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
26
17
  - - ~>
27
- - !ruby/object:Gem::Version
28
- hash: 9
29
- segments:
30
- - 1
31
- - 3
32
- version: "1.3"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
33
20
  type: :runtime
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: ruby-taglib2
37
21
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
39
- none: false
40
- requirements:
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: taglib-ruby
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
41
31
  - - ~>
42
- - !ruby/object:Gem::Version
43
- hash: 11
44
- segments:
45
- - 1
46
- - 2
47
- version: "1.02"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.6'
48
34
  type: :runtime
49
- version_requirements: *id002
50
- description: Point the Little Streamer to your music directory and it will serve up the tunes using the HTML5 audio tag.
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '0.6'
41
+ description: Point the Little Streamer to your music directory and it will serve up
42
+ the tunes using the HTML5 audio tag.
51
43
  email:
52
- executables:
44
+ executables:
53
45
  - the_little_streamer
54
46
  extensions: []
55
-
56
47
  extra_rdoc_files: []
57
-
58
- files:
48
+ files:
59
49
  - bin/the_little_streamer
60
50
  - README.md
51
+ - lib/the_little_streamer.rb
52
+ - lib/the_little_streamer/finder.rb
53
+ - lib/the_little_streamer/html.rb
54
+ - lib/the_little_streamer/searcher.rb
55
+ - lib/the_little_streamer/player.rb
56
+ - lib/the_little_streamer/music.rb
57
+ - lib/the_little_streamer/song.rb
61
58
  homepage: http://github.com/presidentbeef/the_little_streamer
62
59
  licenses: []
63
-
60
+ metadata: {}
64
61
  post_install_message:
65
62
  rdoc_options: []
66
-
67
- require_paths:
63
+ require_paths:
68
64
  - lib
69
- required_ruby_version: !ruby/object:Gem::Requirement
70
- none: false
71
- requirements:
72
- - - ">="
73
- - !ruby/object:Gem::Version
74
- hash: 3
75
- segments:
76
- - 0
77
- version: "0"
78
- required_rubygems_version: !ruby/object:Gem::Requirement
79
- none: false
80
- requirements:
81
- - - ">="
82
- - !ruby/object:Gem::Version
83
- hash: 3
84
- segments:
85
- - 0
86
- version: "0"
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
87
75
  requirements: []
88
-
89
76
  rubyforge_project:
90
- rubygems_version: 1.8.15
77
+ rubygems_version: 2.0.7
91
78
  signing_key:
92
- specification_version: 3
79
+ specification_version: 4
93
80
  summary: Simple Sinatra application for streaming music.
94
81
  test_files: []
95
-