the_little_streamer 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
-