the_little_streamer 0.0.1
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.
- data/README.md +25 -0
- data/bin/the_little_streamer +218 -0
- metadata +97 -0
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# The Little Streamer
|
2
|
+
|
3
|
+
Extremely simple music streamer using Sinatra.
|
4
|
+
|
5
|
+
## Install
|
6
|
+
|
7
|
+
gem install the_little_streamer
|
8
|
+
|
9
|
+
## Use
|
10
|
+
|
11
|
+
the_little_streamer /path/to/music
|
12
|
+
|
13
|
+
Browse to http://localhost:4567
|
14
|
+
|
15
|
+
## Dependencies
|
16
|
+
|
17
|
+
* [Sinatra](http://www.sinatrarb.com/)
|
18
|
+
* [ruby-taglib2](https://github.com/rumblehq/ruby-taglib2)
|
19
|
+
* A browser that supports HTML5, like [Chrome](http://www.google.com/chrome/)
|
20
|
+
|
21
|
+
You may also want to use [Mongrel](https://github.com/fauna/mongrel) instead of WEBrick, as it will be way faster. Just install it and Sinatra will prefer it over WEBrick.
|
22
|
+
|
23
|
+
## License
|
24
|
+
|
25
|
+
[MIT](http://www.opensource.org/licenses/mit-license.php)
|
@@ -0,0 +1,218 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#This is a simple Sinatra application to play mp3/ogg files through a browser
|
3
|
+
#using the HTML5 audio tag
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'taglib2'
|
7
|
+
require 'sinatra'
|
8
|
+
require 'cgi'
|
9
|
+
require 'pathname'
|
10
|
+
|
11
|
+
#Supply a directory with all the music in it
|
12
|
+
abort "Point me to the music!" unless ARGV[0]
|
13
|
+
|
14
|
+
#Wrap up in HTML
|
15
|
+
def html body
|
16
|
+
<<-HTML
|
17
|
+
<html>
|
18
|
+
<body>
|
19
|
+
#{body}
|
20
|
+
</body>
|
21
|
+
</html>
|
22
|
+
HTML
|
23
|
+
end
|
24
|
+
|
25
|
+
#Create a link
|
26
|
+
def link root, path, text = path
|
27
|
+
"<a href='#{root}#{CGI.escape path}'>#{text}</a>"
|
28
|
+
end
|
29
|
+
|
30
|
+
#Audio tag
|
31
|
+
def audio path
|
32
|
+
<<-HTML
|
33
|
+
<audio id='player' onEnded='javascript:play_next()' src=#{("/" << CGI.escapeHTML(path)).inspect} autobuffer controls autoplay >
|
34
|
+
You need the power of HTML5!
|
35
|
+
</audio>
|
36
|
+
<script type="text/javascript">
|
37
|
+
document.getElementById('player').volume = 0.3;
|
38
|
+
</script>
|
39
|
+
HTML
|
40
|
+
end
|
41
|
+
|
42
|
+
#Javascript for playlist
|
43
|
+
def playlist songs
|
44
|
+
<<-JAVASCRIPT
|
45
|
+
<script type="text/javascript">
|
46
|
+
var player = document.getElementById('player');
|
47
|
+
var artist = document.getElementById('artist');
|
48
|
+
var album = document.getElementById('album');
|
49
|
+
var title = document.getElementById('song_title');
|
50
|
+
var playlist = [#{songs.map { |s| song_to_js s }.join "," }]
|
51
|
+
var current_song = 0;
|
52
|
+
|
53
|
+
play_next = function(reverse) {
|
54
|
+
if(reverse && current_song > 0)
|
55
|
+
current_song--;
|
56
|
+
else if(!reverse && current_song < playlist.length)
|
57
|
+
current_song++;
|
58
|
+
else
|
59
|
+
return;
|
60
|
+
|
61
|
+
var song = playlist[current_song];
|
62
|
+
player.src = song.path;
|
63
|
+
artist.innerHTML = song.artist
|
64
|
+
album.innerHTML = song.album
|
65
|
+
title.innerHTML = song.title
|
66
|
+
player.play();
|
67
|
+
}
|
68
|
+
</script>
|
69
|
+
JAVASCRIPT
|
70
|
+
end
|
71
|
+
|
72
|
+
#Javascript for a song
|
73
|
+
def song_to_js song
|
74
|
+
<<-JAVASCRIPT
|
75
|
+
{ artist: #{song.artist.inspect},
|
76
|
+
album: #{song.album.inspect},
|
77
|
+
title: #{song.title.inspect},
|
78
|
+
path: #{(CGI.escapeHTML "/" << song.path).inspect} }
|
79
|
+
JAVASCRIPT
|
80
|
+
end
|
81
|
+
|
82
|
+
#Back/forward links
|
83
|
+
def prev_and_next
|
84
|
+
"<a href='javascript:play_next(true)'>Prev</a> <a href='javascript:play_next()'>Next</a>"
|
85
|
+
end
|
86
|
+
|
87
|
+
#HTML for playing all songs by artist
|
88
|
+
def play_artist artist
|
89
|
+
songs = []
|
90
|
+
|
91
|
+
Music[artist].each_value do |album|
|
92
|
+
album.values.sort_by { |s| s.track }.each do |song|
|
93
|
+
songs << song
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
html <<-HTML
|
98
|
+
#{song_info songs.first}<br/>
|
99
|
+
#{audio songs.first.path}<br/>
|
100
|
+
#{prev_and_next}
|
101
|
+
#{playlist songs}
|
102
|
+
HTML
|
103
|
+
end
|
104
|
+
|
105
|
+
#HTML for playing all songs on an album
|
106
|
+
def play_album artist, album
|
107
|
+
songs = Music[artist][album].values.sort_by { |s| s.track }
|
108
|
+
|
109
|
+
html <<-HTML
|
110
|
+
#{song_info songs.first}<br/>
|
111
|
+
#{audio songs.first.path}<br/>
|
112
|
+
#{prev_and_next}
|
113
|
+
#{playlist songs}
|
114
|
+
HTML
|
115
|
+
end
|
116
|
+
|
117
|
+
#HTML for song information header
|
118
|
+
def song_info song
|
119
|
+
"\"<span id='song_title'>#{song.title}</span>\" by <span id='artist'>#{song.artist}</span> (<span id='album'>#{song.album}</span>)"
|
120
|
+
end
|
121
|
+
|
122
|
+
#Storing all music information in here
|
123
|
+
Music = Hash.new do |h,k|
|
124
|
+
h[k] = Hash.new do |hash, key|
|
125
|
+
hash[key] = Hash.new
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#Song information
|
130
|
+
Song = Struct.new :artist, :album, :title, :track, :path
|
131
|
+
|
132
|
+
#Used to figure out path to music files
|
133
|
+
root = Pathname.new ARGV[0]
|
134
|
+
|
135
|
+
#Make song files public
|
136
|
+
set :public, ARGV[0]
|
137
|
+
|
138
|
+
#Make sure Sinatra sings
|
139
|
+
set :run, true
|
140
|
+
|
141
|
+
#Grab information from all the song files
|
142
|
+
Dir.glob "#{ARGV[0]}/**/*.{mp3,ogg}" do |file|
|
143
|
+
begin
|
144
|
+
info = TagLib2::File.new(file)
|
145
|
+
|
146
|
+
artist, album, title, track = info.artist, info.album, info.title, info.track
|
147
|
+
artist ||= "Unknown"
|
148
|
+
album ||= "Unknown"
|
149
|
+
info = nil
|
150
|
+
|
151
|
+
if title
|
152
|
+
[artist, album, title].each { |i| i.tr!('/', '-') }
|
153
|
+
Music[artist][album][title] = Song.new(artist, album, title, track, Pathname.new(file).relative_path_from(root).to_s)
|
154
|
+
end
|
155
|
+
rescue Exception => e
|
156
|
+
$stderr.puts e if $DEBUG
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
GC.start #Clean up after TagLib2
|
161
|
+
|
162
|
+
#List artists
|
163
|
+
get '/' do
|
164
|
+
path = '/artist/'
|
165
|
+
html Music.keys.sort.map { |a| link path, a }.join "<br/>"
|
166
|
+
end
|
167
|
+
|
168
|
+
#List albums by given artist
|
169
|
+
get '/artist/:artist/?' do |artist|
|
170
|
+
unless Music[artist].empty?
|
171
|
+
path = "/artist/#{artist}/album/"
|
172
|
+
html Music[artist].keys.sort.map { |a| link path, a }.join("<br/>") << "<br/><br/>" <<
|
173
|
+
link("/artist/#{artist}/", "play", "Play All")
|
174
|
+
else
|
175
|
+
html "Could not find <b>#{artist}</b>."
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
#Play songs by given artist
|
180
|
+
get '/artist/:artist/play/?' do |artist|
|
181
|
+
unless Music[artist].empty?
|
182
|
+
play_artist artist
|
183
|
+
else
|
184
|
+
html "Could not find <b>#{artist}</b>."
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
#List songs on given album
|
189
|
+
get '/artist/:artist/album/:album/?' do |artist, album|
|
190
|
+
unless Music[artist][album].empty?
|
191
|
+
path = "/artist/#{artist}/album/#{album}/song/"
|
192
|
+
html Music[artist][album].values.sort_by { |s| s.track || 0}.map { |s| link path, s.title }.join("<br/>") << "<br/><br/>" <<
|
193
|
+
link("/artist/#{artist}/album/#{album}/", "play", "Play All")
|
194
|
+
else
|
195
|
+
html "Could not find <b>#{album}</b> for <b>#{artist}</b>."
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
#Play all songs from a given album
|
200
|
+
get '/artist/:artist/album/:album/play/?' do |artist, album|
|
201
|
+
if Music[artist][album]
|
202
|
+
play_album artist, album
|
203
|
+
else
|
204
|
+
html "Could not find <b>#{album}</b> by <b>#{artist}</b>."
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
#Play song!
|
209
|
+
get '/artist/:artist/album/:album/song/:song/?' do |artist, album, title|
|
210
|
+
if song = Music[artist][album][title]
|
211
|
+
html <<-HTML
|
212
|
+
#{song_info song}<br/>
|
213
|
+
#{audio song.path}
|
214
|
+
HTML
|
215
|
+
else
|
216
|
+
html "Could not find <b>#{title}</b> on <b>#{album}</b> by <b>#{artist}</b>."
|
217
|
+
end
|
218
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: the_little_streamer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Justin Collins
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-14 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: sinatra
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 15
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
version: "1.0"
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: ruby-taglib2
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 11
|
45
|
+
segments:
|
46
|
+
- 1
|
47
|
+
- 2
|
48
|
+
version: "1.02"
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
description: Point the Little Streamer to your music directory and it will serve up the tunes using the HTML5 audio tag.
|
52
|
+
email:
|
53
|
+
executables:
|
54
|
+
- the_little_streamer
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
extra_rdoc_files: []
|
58
|
+
|
59
|
+
files:
|
60
|
+
- bin/the_little_streamer
|
61
|
+
- README.md
|
62
|
+
has_rdoc: true
|
63
|
+
homepage: http://github.com/presidentbeef/the_little_streamer
|
64
|
+
licenses: []
|
65
|
+
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
requirements: []
|
90
|
+
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.4.1
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: Sinatra application for streaming music.
|
96
|
+
test_files: []
|
97
|
+
|