radiodan 0.0.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile.lock +28 -3
- data/TODO +7 -4
- data/lib/radiodan/adapter/mpd/ack.rb +3 -3
- data/lib/radiodan/adapter/mpd/connection.rb +6 -17
- data/lib/radiodan/adapter/mpd/playlist_parser.rb +15 -8
- data/lib/radiodan/adapter/mpd/response.rb +25 -21
- data/lib/radiodan/adapter/mpd.rb +96 -11
- data/lib/radiodan/builder.rb +11 -10
- data/lib/radiodan/em_additions.rb +7 -5
- data/lib/radiodan/event_binding.rb +4 -1
- data/lib/radiodan/logging.rb +18 -7
- data/lib/radiodan/middleware/panic.rb +6 -12
- data/lib/radiodan/middleware/playlist_to_start.rb +11 -0
- data/lib/radiodan/middleware/toggle_playlist.rb +19 -0
- data/lib/radiodan/middleware/touch_file.rb +1 -1
- data/lib/radiodan/middleware/web_server.rb +17 -0
- data/lib/radiodan/player.rb +52 -25
- data/lib/radiodan/playlist.rb +25 -4
- data/lib/radiodan/playlist_sync.rb +39 -15
- data/lib/radiodan/sinatra.rb +13 -0
- data/lib/radiodan/track.rb +6 -3
- data/lib/radiodan/version.rb +1 -1
- data/lib/radiodan.rb +12 -12
- data/radiodan.gemspec +6 -3
- data/spec/lib/builder_spec.rb +24 -3
- data/spec/lib/logging_spec.rb +7 -2
- data/spec/lib/player_spec.rb +10 -6
- data/spec/lib/playlist_spec.rb +47 -5
- data/spec/lib/playlist_sync_spec.rb +49 -19
- data/spec/spec_helper.rb +1 -1
- metadata +53 -35
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 98308159d0c67a5989de555035862e73754604f0
|
4
|
+
data.tar.gz: 7d5e7f602adc3f0223524e97debd9f1a38280b7f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9fbdab2af2852649db310b85489318dc0e9c3ac88826223fa938369699463379e9a88a39310bdc2a81d8392126d5e42eb5cb782ece3a23808892daf1b1f3ce98
|
7
|
+
data.tar.gz: 55076df49a6d487c6b4f3dda9922e941e9f91c840375cc4d52c2a460aaf220f7395994b8d89b5c4c6a51bab2964ece92c40bb86c052fe2112a33052d460bc9b4
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
radiodan (0.0
|
4
|
+
radiodan (1.0.0)
|
5
5
|
active_support (~> 3.0.0)
|
6
6
|
em-http-request (~> 1.0.3)
|
7
7
|
em-simple_telnet (~> 0.0.6)
|
8
8
|
em-synchrony (~> 1.0.3)
|
9
9
|
eventmachine (~> 1.0.3)
|
10
10
|
i18n (~> 0.6.4)
|
11
|
+
sinatra (~> 1.4.2)
|
12
|
+
sinatra-synchrony (~> 0.4.1)
|
13
|
+
thin (~> 1.5.1)
|
11
14
|
|
12
15
|
GEM
|
13
16
|
remote: https://rubygems.org/
|
@@ -18,6 +21,7 @@ GEM
|
|
18
21
|
addressable (2.3.5)
|
19
22
|
coderay (1.0.9)
|
20
23
|
cookiejar (0.3.0)
|
24
|
+
daemons (1.1.9)
|
21
25
|
diff-lcs (1.2.4)
|
22
26
|
em-http-request (1.0.3)
|
23
27
|
addressable (>= 2.2.3)
|
@@ -25,7 +29,8 @@ GEM
|
|
25
29
|
em-socksify
|
26
30
|
eventmachine (>= 1.0.0.beta.4)
|
27
31
|
http_parser.rb (>= 0.5.3)
|
28
|
-
em-
|
32
|
+
em-resolv-replace (1.1.3)
|
33
|
+
em-simple_telnet (0.0.14)
|
29
34
|
eventmachine (>= 1.0.0)
|
30
35
|
em-socksify (0.3.0)
|
31
36
|
eventmachine (>= 1.0.0.beta.4)
|
@@ -44,7 +49,7 @@ GEM
|
|
44
49
|
guard (>= 1.8)
|
45
50
|
rspec (~> 2.13)
|
46
51
|
http_parser.rb (0.5.3)
|
47
|
-
i18n (0.6.
|
52
|
+
i18n (0.6.5)
|
48
53
|
listen (1.0.3)
|
49
54
|
rb-fsevent (>= 0.9.3)
|
50
55
|
rb-inotify (>= 0.9)
|
@@ -55,6 +60,10 @@ GEM
|
|
55
60
|
coderay (~> 1.0.5)
|
56
61
|
method_source (~> 0.8)
|
57
62
|
slop (~> 3.4)
|
63
|
+
rack (1.5.2)
|
64
|
+
rack-fiber_pool (0.9.3)
|
65
|
+
rack-protection (1.5.1)
|
66
|
+
rack
|
58
67
|
rake (10.1.0)
|
59
68
|
rb-fsevent (0.9.3)
|
60
69
|
rb-inotify (0.9.0)
|
@@ -69,9 +78,25 @@ GEM
|
|
69
78
|
rspec-expectations (2.13.0)
|
70
79
|
diff-lcs (>= 1.1.3, < 2.0)
|
71
80
|
rspec-mocks (2.13.1)
|
81
|
+
sinatra (1.4.4)
|
82
|
+
rack (~> 1.4)
|
83
|
+
rack-protection (~> 1.4)
|
84
|
+
tilt (~> 1.3, >= 1.3.4)
|
85
|
+
sinatra-synchrony (0.4.1)
|
86
|
+
em-http-request (~> 1.0)
|
87
|
+
em-resolv-replace (~> 1.1)
|
88
|
+
em-synchrony (~> 1.0.1)
|
89
|
+
eventmachine (~> 1.0.0)
|
90
|
+
rack-fiber_pool (~> 0.9)
|
91
|
+
sinatra (~> 1.0)
|
72
92
|
slop (3.4.4)
|
73
93
|
terminal-notifier-guard (1.5.3)
|
94
|
+
thin (1.5.1)
|
95
|
+
daemons (>= 1.0.9)
|
96
|
+
eventmachine (>= 0.12.6)
|
97
|
+
rack (>= 1.0.0)
|
74
98
|
thor (0.18.1)
|
99
|
+
tilt (1.4.1)
|
75
100
|
|
76
101
|
PLATFORMS
|
77
102
|
ruby
|
data/TODO
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
* Player
|
2
2
|
- #sync determines which events to trigger under which circumstances
|
3
|
-
|
4
|
-
- Resume
|
5
|
-
* Search for music in library, return playlist / array of tracks
|
3
|
+
* Playlist
|
4
|
+
- Resume mode updates seek mode on syc
|
6
5
|
* MPD Adapter
|
7
6
|
- Figure out a way of testing interface with playlist object
|
7
|
+
* MPD::Response
|
8
|
+
- Add tests
|
8
9
|
* Panic Mode
|
9
10
|
- Add tests
|
10
|
-
*
|
11
|
+
* Event binding
|
12
|
+
- Replace event_binding with EM::Channel?
|
13
|
+
- Add a :all event, call block every time any event is triggered.
|
@@ -1,11 +1,11 @@
|
|
1
1
|
class Radiodan::MPD
|
2
2
|
class Ack
|
3
3
|
FORMAT = /ACK \[(\d)+@(\d)+\] \{(.*)\} (.*)/
|
4
|
-
|
4
|
+
attr_reader :error_id, :position, :command, :description
|
5
5
|
|
6
|
-
def
|
6
|
+
def initialize(ack)
|
7
7
|
matches = FORMAT.match(ack)
|
8
|
-
error_id, position, command, description = *matches[1..-1]
|
8
|
+
@error_id, @position, @command, @description = *matches[1..-1]
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -15,27 +15,16 @@ class Connection
|
|
15
15
|
|
16
16
|
def cmd(command, options={})
|
17
17
|
options = {match: /^(OK|ACK)/}.merge(options)
|
18
|
-
response =
|
18
|
+
response = nil
|
19
19
|
|
20
|
-
connect do |c|
|
21
|
-
begin
|
22
|
-
logger.debug command
|
23
|
-
response = c.cmd(command, options).strip
|
24
|
-
rescue Exception => e
|
25
|
-
logger.error "#{command}, #{options} - #{e.to_s}"
|
26
|
-
raise
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
Response.new(response, command)
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
def connect(&blk)
|
35
20
|
EM::P::SimpleTelnet.new(host: @host, port: @port, prompt: /^(OK|ACK)(.*)$/) do |host|
|
36
21
|
host.waitfor(/^OK MPD \d{1,2}\.\d{1,2}\.\d{1,2}$/)
|
37
|
-
|
22
|
+
logger.debug command
|
23
|
+
result = host.cmd(command, options).strip
|
24
|
+
response = Response.new(result, command)
|
38
25
|
end
|
26
|
+
|
27
|
+
response
|
39
28
|
end
|
40
29
|
end
|
41
30
|
end
|
@@ -13,18 +13,25 @@ module PlaylistParser
|
|
13
13
|
private
|
14
14
|
def self.parse_attributes(attributes)
|
15
15
|
options = {}
|
16
|
-
options[:state] = attributes['state'].to_sym
|
17
|
-
options[:mode] = parse_mode(attributes)
|
18
|
-
options[:repeat] = attributes['repeat'] == '1'
|
19
|
-
options[:position] = attributes['song'].to_i
|
20
|
-
options[:seek] = attributes['elapsed'].to_f
|
21
|
-
options[:volume] = attributes['volume'].to_i
|
22
16
|
|
23
|
-
|
17
|
+
begin
|
18
|
+
options[:state] = attributes['state'].to_sym
|
19
|
+
options[:mode] = parse_mode(attributes)
|
20
|
+
options[:repeat] = attributes['repeat'] == '1'
|
21
|
+
options[:position] = attributes['song'].to_i
|
22
|
+
options[:seek] = attributes['elapsed'].to_f
|
23
|
+
options[:volume] = attributes['volume'].to_i
|
24
|
+
ensure
|
25
|
+
return options
|
26
|
+
end
|
24
27
|
end
|
25
28
|
|
26
29
|
def self.parse_tracks(tracks)
|
27
|
-
tracks.collect
|
30
|
+
if tracks.respond_to?(:collect)
|
31
|
+
tracks.collect{ |t| Track.new(t) }
|
32
|
+
else
|
33
|
+
[]
|
34
|
+
end
|
28
35
|
end
|
29
36
|
|
30
37
|
def self.parse_mode(attributes)
|
@@ -1,23 +1,32 @@
|
|
1
|
+
require 'logging'
|
1
2
|
require_relative 'ack'
|
2
3
|
|
3
|
-
class Radiodan
|
4
|
+
class Radiodan
|
5
|
+
class MPD
|
4
6
|
class Response
|
7
|
+
include Logging
|
5
8
|
attr_accessor :value, :string
|
6
9
|
alias_method :to_s, :string
|
7
10
|
|
8
|
-
MULTILINE_COMMANDS = %w{playlistinfo}
|
11
|
+
MULTILINE_COMMANDS = %w{playlistinfo search find}
|
9
12
|
|
10
13
|
def initialize(response_string, command=nil)
|
11
|
-
@string
|
14
|
+
@string = response_string
|
12
15
|
@command = command
|
16
|
+
@value = parse(@string, @command)
|
17
|
+
|
18
|
+
if ack?
|
19
|
+
logger.error "ACK #{@command}, #{@value.inspect}"
|
20
|
+
raise AckError, @value.description
|
21
|
+
end
|
13
22
|
end
|
14
23
|
|
15
|
-
def
|
16
|
-
|
24
|
+
def ack?
|
25
|
+
value.is_a?(Ack)
|
17
26
|
end
|
18
27
|
|
19
|
-
def
|
20
|
-
value
|
28
|
+
def ==(other)
|
29
|
+
self.value == other
|
21
30
|
end
|
22
31
|
|
23
32
|
def method_missing(method, *args, &block)
|
@@ -44,11 +53,11 @@ class Radiodan::MPD
|
|
44
53
|
when response == 'OK'
|
45
54
|
true
|
46
55
|
when response =~ /^ACK/
|
47
|
-
|
56
|
+
Ack.new(response)
|
48
57
|
when response.split.size == 1
|
49
58
|
# set value -> value
|
50
59
|
Hash[*(response.split.*2)]
|
51
|
-
when MULTILINE_COMMANDS.include?(command)
|
60
|
+
when MULTILINE_COMMANDS.include?(command.split.first)
|
52
61
|
# create array of hash values
|
53
62
|
parse_multiline(response)
|
54
63
|
else
|
@@ -57,23 +66,16 @@ class Radiodan::MPD
|
|
57
66
|
end
|
58
67
|
end
|
59
68
|
|
60
|
-
def parse_ack(response)
|
61
|
-
ack = Ack.new(response)
|
62
|
-
logger.warn ack
|
63
|
-
|
64
|
-
ack
|
65
|
-
end
|
66
|
-
|
67
69
|
def parse_multiline(response)
|
68
70
|
multiline = []
|
69
71
|
values = {}
|
70
72
|
|
71
73
|
split_response(response) do |key, value|
|
72
|
-
if values.
|
74
|
+
if key == 'file' && values.has_key?('file')
|
73
75
|
multiline << values
|
74
76
|
values = {}
|
75
77
|
end
|
76
|
-
|
78
|
+
|
77
79
|
values[key] = value
|
78
80
|
end
|
79
81
|
|
@@ -82,10 +84,11 @@ class Radiodan::MPD
|
|
82
84
|
|
83
85
|
def split_response(response)
|
84
86
|
response = response.split("\n")
|
85
|
-
# remove first response: "OK"
|
86
|
-
response.pop
|
87
87
|
|
88
88
|
response.collect do |r|
|
89
|
+
# remove "OK" responses
|
90
|
+
next if r == 'OK'
|
91
|
+
|
89
92
|
split = r.split(':')
|
90
93
|
key = split.shift.strip
|
91
94
|
value = split.join(':').strip
|
@@ -93,7 +96,8 @@ class Radiodan::MPD
|
|
93
96
|
yield(key, value) if block_given?
|
94
97
|
|
95
98
|
[key, value]
|
96
|
-
end
|
99
|
+
end.compact
|
97
100
|
end
|
98
101
|
end
|
99
102
|
end
|
103
|
+
end
|
data/lib/radiodan/adapter/mpd.rb
CHANGED
@@ -5,22 +5,26 @@ require_relative './mpd/playlist_parser'
|
|
5
5
|
|
6
6
|
class Radiodan
|
7
7
|
class MPD
|
8
|
+
class AckError < Exception; end
|
8
9
|
include Logging
|
9
10
|
extend Forwardable
|
10
11
|
|
11
12
|
def_delegators :@connection, :cmd
|
12
13
|
|
13
|
-
COMMANDS = %w{stop pause clear play next previous}
|
14
|
+
COMMANDS = %w{stop pause clear play next previous enqueue search update}
|
15
|
+
SEARCH_SCOPE = %w{artist album title track name genre date composer performer comment disc filename any}
|
14
16
|
attr_reader :player
|
15
17
|
|
16
18
|
def initialize(options={})
|
17
19
|
@connection = Connection.new(options)
|
20
|
+
@connection.cmd('clear')
|
21
|
+
@connection.cmd('update')
|
18
22
|
end
|
19
|
-
|
23
|
+
|
20
24
|
def player=(player)
|
21
25
|
@player = player
|
22
26
|
|
23
|
-
# register
|
27
|
+
# register available player commands
|
24
28
|
COMMANDS.each do |command|
|
25
29
|
@player.register_event command do |data|
|
26
30
|
if data
|
@@ -35,21 +39,47 @@ class MPD
|
|
35
39
|
@player.register_event :playlist do |playlist|
|
36
40
|
self.playlist = playlist
|
37
41
|
end
|
42
|
+
|
43
|
+
# register volume changes
|
44
|
+
@player.register_event :volume do |volume|
|
45
|
+
self.volume = volume
|
46
|
+
end
|
38
47
|
end
|
39
48
|
|
40
49
|
def playlist=(playlist)
|
41
50
|
# get rid of current playlist, stop playback
|
42
51
|
clear
|
52
|
+
|
53
|
+
# set random & repeat
|
54
|
+
cmd(%Q{random #{boolean_to_s(playlist.random?)}})
|
55
|
+
cmd(%Q{repeat #{boolean_to_s(playlist.repeat?)}})
|
56
|
+
|
57
|
+
# set volume
|
58
|
+
begin
|
59
|
+
volume = playlist.volume
|
60
|
+
rescue AckError => e
|
61
|
+
logger.error e.msg
|
62
|
+
end
|
63
|
+
|
64
|
+
if playlist.empty?
|
65
|
+
logger.error 'Playlist empty, nothing to do'
|
66
|
+
return false
|
67
|
+
end
|
43
68
|
|
44
|
-
if enqueue playlist
|
45
|
-
play
|
69
|
+
if enqueue playlist.tracks
|
70
|
+
# set for seek position (will play from seek point)
|
71
|
+
cmd(%Q{seek #{playlist.position} #{Integer(playlist.seek)}})
|
46
72
|
else
|
47
73
|
raise "Cannot load playlist #{playlist}"
|
48
74
|
end
|
49
75
|
end
|
76
|
+
|
77
|
+
def volume=(new_volume)
|
78
|
+
cmd(%Q{setvol #{Integer(new_volume)}})
|
79
|
+
end
|
50
80
|
|
51
|
-
def enqueue(
|
52
|
-
|
81
|
+
def enqueue(tracks)
|
82
|
+
tracks.each do |track|
|
53
83
|
cmd(%Q{add "#{track[:file]}"})
|
54
84
|
end
|
55
85
|
end
|
@@ -59,10 +89,61 @@ class MPD
|
|
59
89
|
end
|
60
90
|
|
61
91
|
def playlist
|
62
|
-
|
63
|
-
|
92
|
+
begin
|
93
|
+
status = cmd('status')
|
94
|
+
tracks = cmd('playlistinfo')
|
64
95
|
|
65
|
-
|
96
|
+
playlist = PlaylistParser.parse(status, tracks)
|
97
|
+
playlist
|
98
|
+
rescue Playlist::StateError,
|
99
|
+
Playlist::ModeError,
|
100
|
+
Playlist::PositionError,
|
101
|
+
Playlist::SeekError,
|
102
|
+
Playlist::VolumeError => e
|
103
|
+
logger.warn("Playlist parsing raised error: #{e}")
|
104
|
+
retry
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# search :artist => "Bob Marley", :exact => true
|
109
|
+
# search :filename => './bob.mp3'
|
110
|
+
# search "Bob Marley"
|
111
|
+
def search(args)
|
112
|
+
if args.nil?
|
113
|
+
logger.error 'no query found'
|
114
|
+
return []
|
115
|
+
end
|
116
|
+
|
117
|
+
if args.to_s == args
|
118
|
+
args = {'any' => args}
|
119
|
+
end
|
120
|
+
|
121
|
+
if args.delete(:exact)
|
122
|
+
command = 'find'
|
123
|
+
else
|
124
|
+
command = 'search'
|
125
|
+
end
|
126
|
+
|
127
|
+
if args.keys.size > 1
|
128
|
+
raise 'Too many arguments for search'
|
129
|
+
end
|
130
|
+
|
131
|
+
scope = args.keys.first.to_s
|
132
|
+
term = args.values.first
|
133
|
+
|
134
|
+
unless SEARCH_SCOPE.include?(scope)
|
135
|
+
raise "Unknown search scope #{scope}"
|
136
|
+
end
|
137
|
+
|
138
|
+
cmd_string = %Q{#{command} #{scope} "#{term}"}
|
139
|
+
|
140
|
+
tracks = cmd(cmd_string)
|
141
|
+
|
142
|
+
if tracks.respond_to?(:collect)
|
143
|
+
tracks.collect { |t| Track.new(t) }
|
144
|
+
else
|
145
|
+
[]
|
146
|
+
end
|
66
147
|
end
|
67
148
|
|
68
149
|
def respond_to?(method)
|
@@ -73,7 +154,6 @@ class MPD
|
|
73
154
|
end
|
74
155
|
end
|
75
156
|
|
76
|
-
private
|
77
157
|
def method_missing(method, *args, &block)
|
78
158
|
if COMMANDS.include?(method.to_s)
|
79
159
|
cmd(method.to_s, *args, &block)
|
@@ -81,5 +161,10 @@ class MPD
|
|
81
161
|
super
|
82
162
|
end
|
83
163
|
end
|
164
|
+
|
165
|
+
private
|
166
|
+
def boolean_to_s(bool)
|
167
|
+
bool == true ? '1' : '0'
|
168
|
+
end
|
84
169
|
end
|
85
170
|
end
|
data/lib/radiodan/builder.rb
CHANGED
@@ -8,30 +8,31 @@ require 'player'
|
|
8
8
|
class Radiodan
|
9
9
|
class Builder
|
10
10
|
attr_reader :middleware, :player
|
11
|
-
|
11
|
+
|
12
12
|
def initialize(&blk)
|
13
13
|
@middleware = []
|
14
14
|
@player = Player.new
|
15
|
-
|
15
|
+
|
16
16
|
yield(self) if block_given?
|
17
17
|
end
|
18
18
|
|
19
19
|
def use(klass, *config)
|
20
20
|
@middleware << register(klass, 'middleware', *config)
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def adapter(klass, *config)
|
24
24
|
player.adapter = register(klass, 'adapter', *config)
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def playlist(new_playlist)
|
28
|
-
|
28
|
+
use :playlist_to_start, new_playlist
|
29
29
|
end
|
30
|
-
|
31
|
-
def log(log)
|
30
|
+
|
31
|
+
def log(log, level=nil)
|
32
32
|
Logging.output = log
|
33
|
+
Logging.level = level unless level.nil?
|
33
34
|
end
|
34
|
-
|
35
|
+
|
35
36
|
def call_middleware!
|
36
37
|
middleware.each{ |m| m.call(@player) }
|
37
38
|
end
|
@@ -45,14 +46,14 @@ class Builder
|
|
45
46
|
rescue NameError => e
|
46
47
|
klass_path ||= false
|
47
48
|
raise if klass_path
|
48
|
-
|
49
|
+
|
49
50
|
# attempt to require from given path
|
50
51
|
klass_path = Pathname.new(File.join(File.dirname(__FILE__), klass_type, "#{klass.underscore}.rb"))
|
51
52
|
require klass_path if klass_path.exist?
|
52
53
|
|
53
54
|
retry
|
54
55
|
end
|
55
|
-
|
56
|
+
|
56
57
|
if config.empty?
|
57
58
|
radio_klass.new
|
58
59
|
else
|
@@ -7,24 +7,26 @@
|
|
7
7
|
seconds.
|
8
8
|
=end
|
9
9
|
|
10
|
-
module EventMachine
|
10
|
+
module EventMachine::Synchrony
|
11
11
|
def self.now_and_every(period, &blk)
|
12
12
|
seconds = case
|
13
13
|
when period.respond_to?(:to_f)
|
14
|
-
period
|
14
|
+
period
|
15
15
|
when period.include?(:hours)
|
16
16
|
period[:hours]*60*60
|
17
17
|
when period.include?(:minutes)
|
18
18
|
period[:minutes]*60
|
19
19
|
else
|
20
20
|
period[:seconds]
|
21
|
-
end
|
21
|
+
end.to_f
|
22
|
+
|
23
|
+
raise "Period must be higher than 0" if period == 0.0
|
22
24
|
|
23
|
-
|
25
|
+
next_tick do
|
24
26
|
yield
|
25
27
|
end
|
26
28
|
|
27
|
-
|
29
|
+
add_periodic_timer(seconds) do
|
28
30
|
yield
|
29
31
|
end
|
30
32
|
end
|
@@ -16,8 +16,11 @@ module EventBinding
|
|
16
16
|
logger.error "Event #{event} triggered but not found"
|
17
17
|
end
|
18
18
|
|
19
|
+
# also, run the events bound to :all, no matter the event
|
20
|
+
bindings += event_bindings[:all]
|
21
|
+
|
19
22
|
bindings.each do |blk|
|
20
|
-
EM.next_tick { blk.call(data) }
|
23
|
+
EM::Synchrony.next_tick { blk.call(data) }
|
21
24
|
end
|
22
25
|
end
|
23
26
|
|
data/lib/radiodan/logging.rb
CHANGED
@@ -3,15 +3,17 @@ require 'logger'
|
|
3
3
|
class Radiodan
|
4
4
|
module Logging
|
5
5
|
@@output = '/dev/null'
|
6
|
-
|
6
|
+
@@level = :DEBUG
|
7
|
+
|
7
8
|
def self.included(klass)
|
8
9
|
klass.extend ClassMethods
|
9
10
|
end
|
10
|
-
|
11
|
+
|
11
12
|
def self.output=(output)
|
12
13
|
@@output = output
|
14
|
+
STDOUT.sync = true if @@output == STDOUT
|
13
15
|
end
|
14
|
-
|
16
|
+
|
15
17
|
def self.output
|
16
18
|
@@output
|
17
19
|
end
|
@@ -20,17 +22,26 @@ module Logging
|
|
20
22
|
self.class.logger
|
21
23
|
end
|
22
24
|
|
23
|
-
|
25
|
+
def self.level=(level)
|
26
|
+
@@level = Logger.const_get(level.to_sym.upcase)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.level
|
30
|
+
@@level
|
31
|
+
end
|
32
|
+
|
33
|
+
module ClassMethods
|
24
34
|
@@logs = {}
|
25
|
-
|
35
|
+
|
26
36
|
def logger
|
27
37
|
unless @@logs.include? self.name
|
28
38
|
new_log = Logger.new(Logging.output)
|
29
39
|
new_log.progname = self.name
|
30
|
-
|
40
|
+
new_log.level = Logging.level
|
41
|
+
|
31
42
|
@@logs[self.name] = new_log
|
32
43
|
end
|
33
|
-
|
44
|
+
|
34
45
|
@@logs[self.name]
|
35
46
|
end
|
36
47
|
end
|
@@ -26,18 +26,12 @@ class Panic
|
|
26
26
|
@panic = true
|
27
27
|
|
28
28
|
original_state = @player.playlist
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
},
|
36
|
-
proc {
|
37
|
-
return_to_state original_state
|
38
|
-
}
|
39
|
-
|
40
|
-
@panic
|
29
|
+
logger.debug "panic for #{@timeout} seconds"
|
30
|
+
@player.playlist = @playlist
|
31
|
+
|
32
|
+
EM::Synchrony.add_timer(@timeout) do
|
33
|
+
return_to_state original_state
|
34
|
+
end
|
41
35
|
end
|
42
36
|
|
43
37
|
def return_to_state(playlist)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Radiodan
|
2
|
+
class TogglePlaylist
|
3
|
+
include Logging
|
4
|
+
|
5
|
+
def initialize(main_playlist, toggle_playlist)
|
6
|
+
@playlists = [main_playlist, toggle_playlist]
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(player)
|
10
|
+
@player = player
|
11
|
+
@player.playlist = @playlists.shift
|
12
|
+
|
13
|
+
@player.register_event :toggle do
|
14
|
+
logger.info "Toggling playlist"
|
15
|
+
@player.playlist, @playlists = @playlists.shift, [@player.playlist]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|