muzak 0.3.0 → 0.3.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.
- 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
|