muzak 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONFIGURATION.md +19 -4
- data/README.md +1 -2
- data/lib/muzak.rb +1 -1
- data/lib/muzak/cmd/meta.rb +1 -0
- data/lib/muzak/cmd/player.rb +3 -3
- data/lib/muzak/config.rb +2 -0
- data/lib/muzak/instance.rb +5 -3
- data/lib/muzak/player.rb +12 -5
- data/lib/muzak/player/mpv.rb +5 -4
- data/lib/muzak/player/stub_player.rb +8 -0
- data/lib/muzak/player/vlc.rb +173 -0
- data/lib/muzak/plugin.rb +12 -15
- data/lib/muzak/plugin/stub_plugin.rb +2 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2c396ccd25b5ca8dad7e33aad87e5e6f277f82f
|
4
|
+
data.tar.gz: 28f0518d153ca7dffa8713884a481b583ccdbdbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7619e9fec7c0413fadbd240c3abc828cef121aa8e9f34ba0a1c504750c31171a29aa0c43f18fdbac8d99de2ed68815db88266852ddaf0ed353dd525ee53f598a
|
7
|
+
data.tar.gz: a3b2b9fa7d6606a4d1f081878034890358fe97b763bcc8816365bf7cd24690905414631754c39450b1aca3d01aabe45792daa40eb0ef2a7978022effe5a045e3
|
data/CONFIGURATION.md
CHANGED
@@ -111,7 +111,9 @@ Muzak uses this key to point the indexer at the correct directory.
|
|
111
111
|
|
112
112
|
*Default:* `mpv`
|
113
113
|
|
114
|
-
`player: <player>` should be set to the
|
114
|
+
`player: <player>` should be set to the human-friendly name of the user's music
|
115
|
+
player. This human-friendly name is generated from the player's ruby class name
|
116
|
+
just like a plugin (e.g., `Muzak::Player::MPV` becomes `mpv`).
|
115
117
|
Muzak uses this key to find the correct {Muzak::Player} underclass to
|
116
118
|
initialize and control.
|
117
119
|
|
@@ -143,11 +145,24 @@ It's entirely up to the user's player to obey this value.
|
|
143
145
|
|
144
146
|
*Optional.*
|
145
147
|
|
148
|
+
*Default:* `false`
|
149
|
+
|
150
|
+
If `autoplay: true` is set in the configuration file, then muzak will begin
|
151
|
+
tell the player to begin playing as soon as media is loaded.
|
152
|
+
|
153
|
+
### `default_playlist`
|
154
|
+
|
155
|
+
*Optional.*
|
156
|
+
|
146
157
|
*No default.*
|
147
158
|
|
148
|
-
`
|
149
|
-
automatically
|
150
|
-
|
159
|
+
`default_playlist: <playlist>` should be set to a playlist that the user wishes
|
160
|
+
to automatically load when muzak starts. If not set, no playlist is
|
161
|
+
automatically loaded.
|
162
|
+
|
163
|
+
*Note:* `default_playlist` does not automatically play the specified playlist
|
164
|
+
(it only loads it). `autoplay: true` must be set to automatically play the
|
165
|
+
default playlist.
|
151
166
|
|
152
167
|
## Daemon/client configuration
|
153
168
|
|
data/README.md
CHANGED
@@ -35,6 +35,5 @@ as well as on RubyDoc.
|
|
35
35
|
### TODO
|
36
36
|
|
37
37
|
* GUI "frontend"?
|
38
|
-
* isolation of art and music output (`Muzak::ArtProvider`?)
|
39
38
|
* current indexing/sorting logic is terrible
|
40
|
-
*
|
39
|
+
* figure out how to do events/album art in VLC
|
data/lib/muzak.rb
CHANGED
data/lib/muzak/cmd/meta.rb
CHANGED
data/lib/muzak/cmd/player.rb
CHANGED
@@ -6,7 +6,7 @@ module Muzak
|
|
6
6
|
# @note Many playback commands will automatically activate the player.
|
7
7
|
def player_activate
|
8
8
|
if player.running?
|
9
|
-
|
9
|
+
danger "player is already running"
|
10
10
|
else
|
11
11
|
player.activate!
|
12
12
|
end
|
@@ -21,7 +21,7 @@ module Muzak
|
|
21
21
|
# @cmdexample `muzak> player-deactivate`
|
22
22
|
# @note Deactivating the player (usually) ends playback immediately.
|
23
23
|
def player_deactivate
|
24
|
-
|
24
|
+
danger "player is not running" unless player.running?
|
25
25
|
|
26
26
|
# do cleanup even if the player isn't running, just in case
|
27
27
|
player.deactivate!
|
@@ -160,7 +160,7 @@ module Muzak
|
|
160
160
|
def now_playing
|
161
161
|
if player.playing?
|
162
162
|
build_response data: {
|
163
|
-
playing: player.now_playing
|
163
|
+
playing: player.now_playing&.full_title
|
164
164
|
}
|
165
165
|
else
|
166
166
|
build_response error: "no currently playing song"
|
data/lib/muzak/config.rb
CHANGED
@@ -46,6 +46,7 @@ module Muzak
|
|
46
46
|
# All events currently propagated by {Muzak::Instance#event}
|
47
47
|
PLUGIN_EVENTS = [
|
48
48
|
:instance_started,
|
49
|
+
:instance_quitting,
|
49
50
|
:player_activated,
|
50
51
|
:player_deactivated,
|
51
52
|
:song_loaded,
|
@@ -61,6 +62,7 @@ module Muzak
|
|
61
62
|
"music" => File.expand_path("~/music"),
|
62
63
|
"player" => "mpv",
|
63
64
|
"jukebox-size" => 100,
|
65
|
+
"autoplay" => false,
|
64
66
|
|
65
67
|
# client/daemon defaults
|
66
68
|
"daemon-port" => 2669,
|
data/lib/muzak/instance.rb
CHANGED
@@ -7,6 +7,7 @@ module Muzak
|
|
7
7
|
# Sends a command to the instance.
|
8
8
|
# @param cmd [String] the name of the command
|
9
9
|
# @param args [Array<String>] the command's arguments
|
10
|
+
# @return [Hash] the command's response hash
|
10
11
|
# @example
|
11
12
|
# instance.command "enqueue-playlist", "favorites"
|
12
13
|
# instance.command "pause"
|
@@ -19,7 +20,7 @@ module Muzak
|
|
19
20
|
build_response error: "got #{args.size} args, needed #{meth.arity}"
|
20
21
|
end
|
21
22
|
else
|
22
|
-
|
23
|
+
danger "unknown command: '#{cmd}'"
|
23
24
|
build_response error: "unknown command '#{cmd}'"
|
24
25
|
end
|
25
26
|
end
|
@@ -36,7 +37,7 @@ module Muzak
|
|
36
37
|
# @return [Hash{String => Playlist}] the instance's playlists
|
37
38
|
attr_reader :playlists
|
38
39
|
|
39
|
-
def initialize
|
40
|
+
def initialize
|
40
41
|
verbose "muzak is starting..."
|
41
42
|
|
42
43
|
error! "#{Config.music} doesn't exist" unless File.exist?(Config.music)
|
@@ -49,7 +50,7 @@ module Muzak
|
|
49
50
|
|
50
51
|
@playlists = Playlist.load_playlists!
|
51
52
|
|
52
|
-
enqueue_playlist Config.
|
53
|
+
enqueue_playlist Config.default_playlist if Config.default_playlist
|
53
54
|
|
54
55
|
event :instance_started, self
|
55
56
|
end
|
@@ -57,6 +58,7 @@ module Muzak
|
|
57
58
|
# Dispatch an event to all plugins.
|
58
59
|
# @param type [Symbol] the type of event to dispatch
|
59
60
|
# @param args [Array] the event's arguments
|
61
|
+
# @return [void]
|
60
62
|
# @note {Config::PLUGIN_EVENTS} contains all valid events.
|
61
63
|
def event(type, *args)
|
62
64
|
return unless Config::PLUGIN_EVENTS.include?(type)
|
data/lib/muzak/player.rb
CHANGED
@@ -8,11 +8,18 @@ module Muzak
|
|
8
8
|
module Player
|
9
9
|
extend Utils
|
10
10
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
# All classes (player implementations) under the {Player} namespace.
|
12
|
+
# @api private
|
13
|
+
PLAYER_CLASSES = constants.map(&Player.method(:const_get)).grep(Class).freeze
|
14
|
+
|
15
|
+
# All human-friendly player names.
|
16
|
+
# @see Player::StubPlayer.player_name
|
17
|
+
# @api private
|
18
|
+
PLAYER_NAMES = PLAYER_CLASSES.map(&:player_name).freeze
|
19
|
+
|
20
|
+
# An association of human-friendly player names to implementation classes.
|
21
|
+
# @api private
|
22
|
+
PLAYER_MAP = PLAYER_NAMES.zip(PLAYER_CLASSES).to_h.freeze
|
16
23
|
|
17
24
|
# Returns an instantiated player as specified in `Config.player`.
|
18
25
|
# @return [StubPlayer] the player instance
|
data/lib/muzak/player/mpv.rb
CHANGED
@@ -8,14 +8,14 @@ module Muzak
|
|
8
8
|
module Player
|
9
9
|
# Exposes MPV's IPC to muzak for playback control.
|
10
10
|
class MPV < StubPlayer
|
11
|
-
# @return [Boolean]
|
11
|
+
# @return [Boolean] whether or not MPV is available for execution
|
12
12
|
def self.available?
|
13
13
|
Utils.which?("mpv")
|
14
14
|
end
|
15
15
|
|
16
|
-
# @return [Boolean]
|
16
|
+
# @return [Boolean] whether or not the current instance is running.
|
17
17
|
def running?
|
18
|
-
|
18
|
+
!!@mpv&.running?
|
19
19
|
end
|
20
20
|
|
21
21
|
# Activate mpv by executing it and preparing for event processing.
|
@@ -179,7 +179,8 @@ module Muzak
|
|
179
179
|
# @return [void]
|
180
180
|
# @api private
|
181
181
|
def load_song(song, art)
|
182
|
-
|
182
|
+
append_type = Config.autoplay ? "append-play" : "append"
|
183
|
+
cmds = ["loadfile", song.path, append_type]
|
183
184
|
cmds << "external-file=\"#{art}\"" if art
|
184
185
|
@mpv.command *cmds
|
185
186
|
end
|
@@ -1,12 +1,20 @@
|
|
1
1
|
module Muzak
|
2
2
|
module Player
|
3
3
|
# A no-op player that all players inherit from.
|
4
|
+
# @abstract Subclass and implement all public methods to implement
|
5
|
+
# a player.
|
4
6
|
class StubPlayer
|
5
7
|
include Utils
|
6
8
|
|
7
9
|
# @return [Instance] the instance associated with this player
|
8
10
|
attr_reader :instance
|
9
11
|
|
12
|
+
# The player's human friendly name.
|
13
|
+
# @return [String] the name
|
14
|
+
def self.player_name
|
15
|
+
name.split("::").last.downcase
|
16
|
+
end
|
17
|
+
|
10
18
|
# @return [true] whether or not this type of player is available
|
11
19
|
def self.available?
|
12
20
|
true
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require "vlc-client"
|
2
|
+
|
3
|
+
module Muzak
|
4
|
+
module Player
|
5
|
+
# Exposes a VLC process to muzak for playback control.
|
6
|
+
class VLC < StubPlayer
|
7
|
+
# The expression used to extract the filename out of {#status}
|
8
|
+
# @api private
|
9
|
+
INPUT_REGEX = /\( new input: file:\/\/(.*) \)/.freeze
|
10
|
+
|
11
|
+
# The expression used to extract the volume out of {#status}
|
12
|
+
# @api private
|
13
|
+
VOLUME_REGEX = /\( audio volume: (\d+) \)/.freeze
|
14
|
+
|
15
|
+
# The expression used to extract the playing status out of {#status}
|
16
|
+
# @api private
|
17
|
+
PLAYING_REGEX = /\( state (.*) \)/.freeze
|
18
|
+
|
19
|
+
# @return [Boolean] whether or not VLC is available
|
20
|
+
def self.available?
|
21
|
+
Utils.which?("vlc") && Utils.which?("cvlc")
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Boolean] whether or not the current instance is running.
|
25
|
+
def running?
|
26
|
+
!!@vlc&.connected?
|
27
|
+
end
|
28
|
+
|
29
|
+
# Activates a VLC process.
|
30
|
+
# @return [void]
|
31
|
+
def activate!
|
32
|
+
return if running?
|
33
|
+
|
34
|
+
debug "activating #{self.class}"
|
35
|
+
|
36
|
+
@vlc = ::VLC::System.new
|
37
|
+
|
38
|
+
instance.event :player_activated
|
39
|
+
end
|
40
|
+
|
41
|
+
# Deactivates the VLC process, if one is running.
|
42
|
+
# @return [void]
|
43
|
+
def deactivate!
|
44
|
+
return unless running?
|
45
|
+
|
46
|
+
debug "deactivating #{self.class}"
|
47
|
+
|
48
|
+
@vlc.client.disconnect
|
49
|
+
@vlc.server.stop
|
50
|
+
|
51
|
+
instance.event :player_deactivated
|
52
|
+
end
|
53
|
+
|
54
|
+
# Tell VLC to begin playback.
|
55
|
+
# @return [void]
|
56
|
+
# @note Does nothing is playback is already in progress.
|
57
|
+
def play
|
58
|
+
@vlc.play
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# Tell VLC to pause playback.
|
63
|
+
# @return [void]
|
64
|
+
# @note Does nothing is playback is already paused.
|
65
|
+
def pause
|
66
|
+
@vlc.pause
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [Boolean] whether or not VLC is currently playing.
|
70
|
+
def playing?
|
71
|
+
@vlc.playing?
|
72
|
+
end
|
73
|
+
|
74
|
+
# Tell VLC to play the next song in its queue.
|
75
|
+
# @return [void]
|
76
|
+
# @note Does nothing if the current song is the last.
|
77
|
+
def next_song
|
78
|
+
@vlc.next
|
79
|
+
end
|
80
|
+
|
81
|
+
# Tell VLC to play the previous song in its queue.
|
82
|
+
# @return [void]
|
83
|
+
# @note Restarts the song if the current song is the first.
|
84
|
+
def previous_song
|
85
|
+
@vlc.previous
|
86
|
+
end
|
87
|
+
|
88
|
+
# Tell VLC to add the given song to its queue.
|
89
|
+
# @param song [Song] the song to add
|
90
|
+
# @return [void]
|
91
|
+
# @note Activates VLC if not already activated.
|
92
|
+
def enqueue_song(song)
|
93
|
+
activate! unless running?
|
94
|
+
|
95
|
+
load_song song
|
96
|
+
end
|
97
|
+
|
98
|
+
# Tell VLC to add the given album to its queue.
|
99
|
+
# @param album [Album] the album to add
|
100
|
+
# @return [void]
|
101
|
+
# @note Activates VLC if not already activated.
|
102
|
+
def enqueue_album(album)
|
103
|
+
activate! unless running?
|
104
|
+
|
105
|
+
album.songs.each do |song|
|
106
|
+
load_song song
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Tell VLC to add the given playlist to its queue.
|
111
|
+
# @param playlist [Playlist] the playlist to add
|
112
|
+
# @return [void]
|
113
|
+
# @note Activates VLC if not already activated.
|
114
|
+
def enqueue_playlist(playlist)
|
115
|
+
activate! unless running?
|
116
|
+
|
117
|
+
playlist.songs.each do |song|
|
118
|
+
load_song song
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Get VLC's internal queue.
|
123
|
+
# @return [Array<Song>] all songs in VLC's queue
|
124
|
+
# @note This includes songs already played.
|
125
|
+
# @todo Implement this.
|
126
|
+
def list_queue
|
127
|
+
debug @vlc.playlist.to_s
|
128
|
+
danger "this player doesn't support list_queue"
|
129
|
+
# TODO: figure out how to get VLC::Client#playlist to return filenames
|
130
|
+
[]
|
131
|
+
end
|
132
|
+
|
133
|
+
# Shuffle VLC's internal queue.
|
134
|
+
# @return [void]
|
135
|
+
# @todo Implement this.
|
136
|
+
def shuffle
|
137
|
+
danger "this player doesn't support shuffling (?)"
|
138
|
+
end
|
139
|
+
|
140
|
+
# Clear VLC's internal queue.
|
141
|
+
# @return [void]
|
142
|
+
def clear_queue
|
143
|
+
@vlc.clear
|
144
|
+
end
|
145
|
+
|
146
|
+
# Get VLC's currently loaded song.
|
147
|
+
# @return [Song, nil] the currently loaded song
|
148
|
+
def now_playing
|
149
|
+
filename = INPUT_REGEX.match(status.first)[1]
|
150
|
+
Song.new(filename)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Load a song into VLC.
|
154
|
+
# @param song [Song] the song to load
|
155
|
+
# @return [void]
|
156
|
+
# @api private
|
157
|
+
def load_song(song)
|
158
|
+
@vlc.add_to_playlist song.path
|
159
|
+
@vlc.play if Config.autoplay
|
160
|
+
end
|
161
|
+
|
162
|
+
# Get VLC's status.
|
163
|
+
# @return [Array<String>] the loaded file, volume, and playing statuses
|
164
|
+
# @api private
|
165
|
+
# @note The strings returned by this method can be handled via
|
166
|
+
# {INPUT_REGEX}, {VOLUME_REGEX}, and {PLAYING_REGEX}.
|
167
|
+
def status
|
168
|
+
@vlc.connection.write "status"
|
169
|
+
3.times.collect { @vlc.connection.read }
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
data/lib/muzak/plugin.rb
CHANGED
@@ -10,27 +10,24 @@ module Muzak
|
|
10
10
|
# load plugins included by the user
|
11
11
|
Dir.glob(File.join(Config::USER_PLUGIN_DIR, "*")) { |file| require file }
|
12
12
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
# All classes (plugins) under the {Player} namespace.
|
14
|
+
# @see Plugin::StubPlugin.plugin_name
|
15
|
+
# @api private
|
16
|
+
PLUGIN_CLASSES = constants.map(&Plugin.method(:const_get)).grep(Class).freeze
|
17
17
|
|
18
|
-
#
|
19
|
-
# @
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
# All human-friendly player plugin names.
|
19
|
+
# @api private
|
20
|
+
PLUGIN_NAMES = PLUGIN_CLASSES.map(&:plugin_name).freeze
|
21
|
+
|
22
|
+
# An association of human-friendly plugin names to plugin classes.
|
23
|
+
# @api private
|
24
|
+
PLUGIN_MAP = PLUGIN_NAMES.zip(PLUGIN_CLASSES).to_h.freeze
|
25
25
|
|
26
26
|
# Instantiates all configured plugins and returns them.
|
27
27
|
# @return [Array<StubPlugin>] the instantiated plugins
|
28
28
|
def self.load_plugins!
|
29
|
-
pks =
|
29
|
+
pks = PLUGIN_CLASSES.select { |pk| Config.plugin? pk.plugin_name }
|
30
30
|
pks.map { |pk| pk.new }
|
31
31
|
end
|
32
|
-
|
33
|
-
# An association of plugin names to their Class objects.
|
34
|
-
PLUGIN_MAP = plugin_names.zip(plugin_classes).to_h.freeze
|
35
32
|
end
|
36
33
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: muzak
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William Woodruff
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: taglib-ruby
|
@@ -44,6 +44,20 @@ dependencies:
|
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: 1.1.0
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: vlc-client
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.0.6
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.0.6
|
47
61
|
description: A library for controlling playlists and media players.
|
48
62
|
email: william@tuffbizz.com
|
49
63
|
executables:
|
@@ -77,6 +91,7 @@ files:
|
|
77
91
|
- lib/muzak/player.rb
|
78
92
|
- lib/muzak/player/mpv.rb
|
79
93
|
- lib/muzak/player/stub_player.rb
|
94
|
+
- lib/muzak/player/vlc.rb
|
80
95
|
- lib/muzak/playlist.rb
|
81
96
|
- lib/muzak/plugin.rb
|
82
97
|
- lib/muzak/plugin/stub_plugin.rb
|