radiodan 0.0.4 → 1.0.0
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 +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
|