muzak 0.0.11 → 0.0.12
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/.yardopts +1 -0
- data/lib/muzak/album.rb +11 -4
- data/lib/muzak/cmd.rb +1 -0
- data/lib/muzak/cmd/config.rb +3 -0
- data/lib/muzak/cmd/index.rb +15 -0
- data/lib/muzak/cmd/meta.rb +11 -0
- data/lib/muzak/cmd/player.rb +45 -0
- data/lib/muzak/cmd/playlist.rb +29 -13
- data/lib/muzak/config.rb +11 -1
- data/lib/muzak/const.rb +16 -1
- data/lib/muzak/index.rb +37 -1
- data/lib/muzak/instance.rb +23 -2
- data/lib/muzak/player.rb +9 -1
- data/lib/muzak/player/mpv.rb +42 -0
- data/lib/muzak/player/stub_player.rb +51 -0
- data/lib/muzak/playlist.rb +34 -1
- data/lib/muzak/plugin.rb +8 -1
- data/lib/muzak/plugin/stub_plugin.rb +4 -4
- data/lib/muzak/song.rb +36 -1
- data/lib/muzak/utils.rb +52 -0
- metadata +4 -6
- data/lib/muzak/plugin/cava.rb +0 -44
- data/lib/muzak/plugin/notify.rb +0 -16
- data/lib/muzak/plugin/scrobble.rb +0 -81
data/lib/muzak/instance.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
module Muzak
|
2
|
+
# Encapsulates the entirety of muzak's running state.
|
2
3
|
class Instance
|
3
4
|
include Cmd
|
4
5
|
include Utils
|
5
6
|
|
7
|
+
# Sends a command to the instance.
|
8
|
+
# @param cmd [String] the name of the command
|
9
|
+
# @param args [Array<String>] the command's arguments
|
10
|
+
# @example
|
11
|
+
# instance.command "enqueue-playlist", "favorites"
|
12
|
+
# instance.command "pause"
|
6
13
|
def command(cmd, *args)
|
7
14
|
send Utils.resolve_command(cmd), *args
|
8
15
|
end
|
@@ -12,14 +19,24 @@ module Muzak
|
|
12
19
|
help
|
13
20
|
end
|
14
21
|
|
15
|
-
|
22
|
+
# @return [Index] the instance's music index
|
23
|
+
attr_reader :index
|
24
|
+
|
25
|
+
# @return [StubPlayer] the instance's player
|
26
|
+
attr_reader :player
|
27
|
+
|
28
|
+
# @return [Array<StubPlugin>] the instance's plugins
|
29
|
+
attr_reader :plugins
|
30
|
+
|
31
|
+
# @return [Hash{String => Playlist}] the instance's playlists
|
32
|
+
attr_reader :playlists
|
16
33
|
|
17
34
|
def initialize(opts = {})
|
18
35
|
verbose "muzak is starting..."
|
19
36
|
|
20
37
|
@index = Index.new(Config.music, deep: Config.deep_index)
|
21
38
|
|
22
|
-
@player = Player
|
39
|
+
@player = Player.load_player!(self)
|
23
40
|
|
24
41
|
@plugins = Plugin.load_plugins!
|
25
42
|
|
@@ -28,6 +45,10 @@ module Muzak
|
|
28
45
|
enqueue_playlist Config.autoplay if Config.autoplay
|
29
46
|
end
|
30
47
|
|
48
|
+
# Dispatch an event to all plugins.
|
49
|
+
# @param type [Symbol] the type of event to dispatch
|
50
|
+
# @param args [Array] the event's arguments
|
51
|
+
# @note {Muzak::PLUGIN_EVENTS} contains all valid events.
|
31
52
|
def event(type, *args)
|
32
53
|
return unless PLUGIN_EVENTS.include?(type)
|
33
54
|
|
data/lib/muzak/player.rb
CHANGED
@@ -4,10 +4,18 @@ require_relative "player/stub_player"
|
|
4
4
|
Dir.glob(File.join(__dir__, "player/*")) { |file| require_relative file }
|
5
5
|
|
6
6
|
module Muzak
|
7
|
+
# The namespace for muzak players.
|
7
8
|
module Player
|
9
|
+
# An association of shorthand player "names" to Class objects.
|
8
10
|
PLAYER_MAP = {
|
9
11
|
"stub" => Player::StubPlayer,
|
10
12
|
"mpv" => Player::MPV
|
11
|
-
}
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
# Returns an instantiated player as specified in `Config.player`.
|
16
|
+
# @return [StubPlayer] the player instance
|
17
|
+
def self.load_player!(instance)
|
18
|
+
PLAYER_MAP[Config.player].new(instance)
|
19
|
+
end
|
12
20
|
end
|
13
21
|
end
|
data/lib/muzak/player/mpv.rb
CHANGED
@@ -5,7 +5,9 @@ require "thread"
|
|
5
5
|
|
6
6
|
module Muzak
|
7
7
|
module Player
|
8
|
+
# Exposes MPV's IPC to muzak for playback control.
|
8
9
|
class MPV < StubPlayer
|
10
|
+
# @return [Boolean] Whether or not the current instance is running.
|
9
11
|
def running?
|
10
12
|
begin
|
11
13
|
!!@pid && Process.waitpid(@pid, Process::WNOHANG).nil?
|
@@ -14,6 +16,8 @@ module Muzak
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
19
|
+
# Activate mpv by executing it and preparing for event processing.
|
20
|
+
# @return [void]
|
17
21
|
def activate!
|
18
22
|
return if running?
|
19
23
|
|
@@ -56,6 +60,8 @@ module Muzak
|
|
56
60
|
instance.event :player_activated
|
57
61
|
end
|
58
62
|
|
63
|
+
# Deactivate mpv by killing it and cleaning up.
|
64
|
+
# @return [void]
|
59
65
|
def deactivate!
|
60
66
|
return unless running?
|
61
67
|
|
@@ -73,38 +79,59 @@ module Muzak
|
|
73
79
|
File.delete(@sock_path) if @sock_path && File.exists?(@sock_path)
|
74
80
|
end
|
75
81
|
|
82
|
+
# Tell mpv to begin playback.
|
83
|
+
# @return [void]
|
84
|
+
# @note Does nothing is playback is already in progress.
|
76
85
|
def play
|
77
86
|
return unless running?
|
78
87
|
|
79
88
|
set_property "pause", false
|
80
89
|
end
|
81
90
|
|
91
|
+
# Tell mpv to pause playback.
|
92
|
+
# @return [void]
|
93
|
+
# @note Does nothing is playback is already paused.
|
82
94
|
def pause
|
83
95
|
return unless running?
|
84
96
|
|
85
97
|
set_property "pause", true
|
86
98
|
end
|
87
99
|
|
100
|
+
# @return [Boolean] Whether or not mpv is currently playing.
|
88
101
|
def playing?
|
89
102
|
return false unless running?
|
90
103
|
|
91
104
|
!get_property "pause"
|
92
105
|
end
|
93
106
|
|
107
|
+
# Tell mpv to play the next song in its queue.
|
108
|
+
# @return [void]
|
109
|
+
# @note Does nothing if the current song is the last.
|
94
110
|
def next_song
|
95
111
|
command "playlist-next"
|
96
112
|
end
|
97
113
|
|
114
|
+
# Tell mpv to play the previous song in its queue.
|
115
|
+
# @return [void]
|
116
|
+
# @note Does nothing if the current song is the first.
|
98
117
|
def previous_song
|
99
118
|
command "playlist-prev"
|
100
119
|
end
|
101
120
|
|
121
|
+
# Tell mpv to add the given song to its queue.
|
122
|
+
# @param song [Song] the song to add
|
123
|
+
# @return [void]
|
124
|
+
# @note Activates mpv if not already activated.
|
102
125
|
def enqueue_song(song)
|
103
126
|
activate! unless running?
|
104
127
|
|
105
128
|
load_song song, song.best_guess_album_art
|
106
129
|
end
|
107
130
|
|
131
|
+
# Tell mpv to add the given album to its queue.
|
132
|
+
# @param album [Album] the album to add
|
133
|
+
# @return [void]
|
134
|
+
# @note Activates mpv if not already activated.
|
108
135
|
def enqueue_album(album)
|
109
136
|
activate! unless running?
|
110
137
|
|
@@ -113,6 +140,10 @@ module Muzak
|
|
113
140
|
end
|
114
141
|
end
|
115
142
|
|
143
|
+
# Tell mpv to add the given playlist to its queue.
|
144
|
+
# @param playlist [Playlist] the playlist to add
|
145
|
+
# @return [void]
|
146
|
+
# @note Activates mpv if not already activated.
|
116
147
|
def enqueue_playlist(playlist)
|
117
148
|
activate! unless running?
|
118
149
|
|
@@ -121,6 +152,9 @@ module Muzak
|
|
121
152
|
end
|
122
153
|
end
|
123
154
|
|
155
|
+
# Get mpv's internal queue.
|
156
|
+
# @return [Array<Song>] all songs in mpv's queue
|
157
|
+
# @note This includes songs already played.
|
124
158
|
def list_queue
|
125
159
|
entries = get_property "playlist/count"
|
126
160
|
|
@@ -133,18 +167,24 @@ module Muzak
|
|
133
167
|
playlist
|
134
168
|
end
|
135
169
|
|
170
|
+
# Shuffle mpv's internal queue.
|
171
|
+
# @return [void]
|
136
172
|
def shuffle_queue
|
137
173
|
return unless running?
|
138
174
|
|
139
175
|
command "playlist-shuffle"
|
140
176
|
end
|
141
177
|
|
178
|
+
# Clears mpv's internal queue.
|
179
|
+
# @return [void]
|
142
180
|
def clear_queue
|
143
181
|
return unless running?
|
144
182
|
|
145
183
|
command "playlist-clear"
|
146
184
|
end
|
147
185
|
|
186
|
+
# Get mpv's currently playing song.
|
187
|
+
# @return [Song] the currently playing song
|
148
188
|
def now_playing
|
149
189
|
return unless running? && playing?
|
150
190
|
|
@@ -201,6 +241,8 @@ module Muzak
|
|
201
241
|
# somehow.
|
202
242
|
song = Song.new(get_property "path")
|
203
243
|
instance.event :song_loaded, song
|
244
|
+
when "end-file"
|
245
|
+
instance.event :song_unloaded
|
204
246
|
end
|
205
247
|
end
|
206
248
|
end
|
@@ -1,70 +1,121 @@
|
|
1
1
|
module Muzak
|
2
2
|
module Player
|
3
|
+
# A no-op player that all players inherit from.
|
3
4
|
class StubPlayer
|
4
5
|
include Utils
|
5
6
|
|
7
|
+
# @return [Instance] the instance associated with this player
|
6
8
|
attr_reader :instance
|
7
9
|
|
10
|
+
# @param instance [Instance] the instance associated with the player
|
8
11
|
def initialize(instance)
|
9
12
|
@instance = instance
|
10
13
|
end
|
11
14
|
|
15
|
+
# @return [false] whether or not the player is running
|
16
|
+
# @note NO-OP
|
12
17
|
def running?
|
13
18
|
debug "#running?"
|
19
|
+
false
|
14
20
|
end
|
15
21
|
|
22
|
+
# Activates the player.
|
23
|
+
# @return [void]
|
24
|
+
# @note NO-OP
|
16
25
|
def activate!
|
17
26
|
debug "#activate!"
|
18
27
|
end
|
19
28
|
|
29
|
+
# Deactivates the player.
|
30
|
+
# @return [void]
|
31
|
+
# @note NO-OP
|
20
32
|
def deactivate!
|
21
33
|
debug "#deactivate!"
|
22
34
|
end
|
23
35
|
|
36
|
+
# Starts playback.
|
37
|
+
# @return [void]
|
38
|
+
# @note NO-OP
|
24
39
|
def play
|
25
40
|
debug "#play"
|
26
41
|
end
|
27
42
|
|
43
|
+
# Ends playback.
|
44
|
+
# @return [void]
|
45
|
+
# @note NO-OP
|
28
46
|
def pause
|
29
47
|
debug "#pause"
|
30
48
|
end
|
31
49
|
|
50
|
+
# @return [false] whether or not the player is currently playing
|
51
|
+
# @note NO-OP
|
32
52
|
def playing?
|
33
53
|
debug "#playing?"
|
54
|
+
false
|
34
55
|
end
|
35
56
|
|
57
|
+
# Moves to the next song.
|
58
|
+
# @return [void]
|
59
|
+
# @note NO-OP
|
36
60
|
def next_song
|
37
61
|
debug "#next_song"
|
38
62
|
end
|
39
63
|
|
64
|
+
# Moves to the previous song.
|
65
|
+
# @return [void]
|
66
|
+
# @note NO-OP
|
40
67
|
def previous_song
|
41
68
|
debug "#previous_song"
|
42
69
|
end
|
43
70
|
|
71
|
+
# Enqueues the given song.
|
72
|
+
# @param song [Song] the song to enqueue
|
73
|
+
# @return [void]
|
74
|
+
# @note NO-OP
|
44
75
|
def enqueue_song(song)
|
45
76
|
debug "#enqueue_song"
|
46
77
|
end
|
47
78
|
|
79
|
+
# Enqueues the given album.
|
80
|
+
# @param album [Album] the album to enqueue
|
81
|
+
# @return [void]
|
82
|
+
# @note NO-OP
|
48
83
|
def enqueue_album(album)
|
49
84
|
debug "#enqueue_album"
|
50
85
|
end
|
51
86
|
|
87
|
+
# Enqueues the given playlist.
|
88
|
+
# @param playlist [Playlist] the playlist to enqueue
|
89
|
+
# @return [void]
|
90
|
+
# @note NO-OP
|
52
91
|
def enqueue_playlist(playlist)
|
53
92
|
debug "#enqueue_playlist"
|
54
93
|
end
|
55
94
|
|
95
|
+
# List the player's queue.
|
96
|
+
# @return [void]
|
97
|
+
# @note NO-OP
|
56
98
|
def list_queue
|
57
99
|
debug "#list_queue"
|
58
100
|
end
|
59
101
|
|
102
|
+
# Shuffle the player's queue.
|
103
|
+
# @return [void]
|
104
|
+
# @note NO-OP
|
60
105
|
def shuffle_queue
|
61
106
|
debug "#shuffle_queue"
|
62
107
|
end
|
63
108
|
|
109
|
+
# Clear the player's queue.
|
110
|
+
# @return [void]
|
111
|
+
# @note NO-OP
|
64
112
|
def clear_queue
|
65
113
|
debug "#clear_queue"
|
66
114
|
end
|
67
115
|
|
116
|
+
# Get the currently playing song.
|
117
|
+
# @return [void]
|
118
|
+
# @note NO-OP
|
68
119
|
def now_playing
|
69
120
|
debug "#now_playing"
|
70
121
|
end
|
data/lib/muzak/playlist.rb
CHANGED
@@ -1,19 +1,33 @@
|
|
1
1
|
module Muzak
|
2
2
|
class Playlist
|
3
|
-
|
3
|
+
# @return [String] the absolute path to the playlist on disk
|
4
|
+
attr_accessor :filename
|
4
5
|
|
6
|
+
# @return [Array<Song>] the playlist's songs
|
7
|
+
attr_accessor :songs
|
8
|
+
|
9
|
+
# @param pname [String] the playlist's name
|
10
|
+
# @return [String] the absolute path to the given playlist name
|
5
11
|
def self.path_for(pname)
|
6
12
|
File.join(PLAYLIST_DIR, pname) + ".yml"
|
7
13
|
end
|
8
14
|
|
15
|
+
# @param pname [String] the playlist's name
|
16
|
+
# @return [Boolean] whether or not the given playlist name already exists
|
9
17
|
def self.exist?(pname)
|
10
18
|
File.exist?(path_for(pname))
|
11
19
|
end
|
12
20
|
|
21
|
+
# Deletes the given playlist from disk.
|
22
|
+
# @param pname [String] the playlist's name
|
23
|
+
# @return [void]
|
24
|
+
# @note If already instantiated, the playlist may still be present in
|
25
|
+
# memory (and may reappear on disk if modified in memory)
|
13
26
|
def self.delete!(pname)
|
14
27
|
File.delete(path_for(pname)) if exist? pname
|
15
28
|
end
|
16
29
|
|
30
|
+
# @return [Array<String>] the names of all currently available playlists
|
17
31
|
def self.playlist_names
|
18
32
|
Dir.entries(PLAYLIST_DIR).reject do |ent|
|
19
33
|
ent.start_with?(".")
|
@@ -22,6 +36,9 @@ module Muzak
|
|
22
36
|
end
|
23
37
|
end
|
24
38
|
|
39
|
+
# Instantiates all playlists by loading them from disk.
|
40
|
+
# @return [Hash{String => Playlist}] an association of playlist names to
|
41
|
+
# {Playlist} instances
|
25
42
|
def self.load_playlists!
|
26
43
|
playlists = {}
|
27
44
|
playlists.default_proc = proc { |h, k| h[k] = Playlist.new(k) }
|
@@ -33,6 +50,9 @@ module Muzak
|
|
33
50
|
playlists
|
34
51
|
end
|
35
52
|
|
53
|
+
# Create a new {Playlist} with the given name, or load one by that
|
54
|
+
# name if it already exists.
|
55
|
+
# @param pname [String] the playlist's name
|
36
56
|
def initialize(pname)
|
37
57
|
@filename = self.class.path_for pname
|
38
58
|
|
@@ -46,10 +66,13 @@ module Muzak
|
|
46
66
|
sync!
|
47
67
|
end
|
48
68
|
|
69
|
+
# @return [String] the playlist's name
|
49
70
|
def name
|
50
71
|
File.basename(@filename, File.extname(@filename))
|
51
72
|
end
|
52
73
|
|
74
|
+
# @param songs [Song, Array<Song>] one or more songs to add to the playlist
|
75
|
+
# @return [void]
|
53
76
|
def add(songs)
|
54
77
|
# coerce a single song into an array
|
55
78
|
[*songs].each do |song|
|
@@ -60,20 +83,30 @@ module Muzak
|
|
60
83
|
sync!
|
61
84
|
end
|
62
85
|
|
86
|
+
# @param songs [Song, Array<Song>] one or more songs to delete from the
|
87
|
+
# playlist
|
88
|
+
# @return [void]
|
63
89
|
def delete(songs)
|
64
90
|
[*songs].each { |song| @songs.delete(song) }
|
65
91
|
|
66
92
|
sync!
|
67
93
|
end
|
68
94
|
|
95
|
+
# Shuffles the internal order of the playlist's songs.
|
96
|
+
# @return [void]
|
69
97
|
def shuffle!
|
70
98
|
@songs.shuffle!
|
71
99
|
end
|
72
100
|
|
101
|
+
# Synchronizes the current instance with its disk representation.
|
102
|
+
# @return [void]
|
103
|
+
# @note You shouldn't need to call this.
|
73
104
|
def sync!
|
74
105
|
File.open(@filename, "w") { |io| io.write to_hash.to_yaml }
|
75
106
|
end
|
76
107
|
|
108
|
+
# Provides a hash representation of the current instance.
|
109
|
+
# @return [Hash{String => Array<Song>}] the instance's state
|
77
110
|
def to_hash
|
78
111
|
{ "songs" => @songs }
|
79
112
|
end
|
data/lib/muzak/plugin.rb
CHANGED
@@ -5,25 +5,32 @@ require_relative "plugin/stub_plugin"
|
|
5
5
|
Dir.glob(File.join(__dir__, "plugin/*")) { |file| require_relative file }
|
6
6
|
|
7
7
|
module Muzak
|
8
|
+
# The namespace for muzak plugins.
|
8
9
|
module Plugin
|
9
10
|
# load plugins included by the user
|
10
11
|
Dir.glob(File.join(USER_PLUGIN_DIR, "*")) { |file| require file }
|
11
12
|
|
13
|
+
# @return [Array<Class>] all plugin classes visible under Plugin
|
12
14
|
def self.plugin_classes
|
13
15
|
constants.map(&Plugin.method(:const_get)).grep(Class)
|
14
16
|
end
|
15
17
|
|
18
|
+
# @return [Array<String>] the names of all plugin classes under Plugin
|
19
|
+
# @see StubPlugin.plugin_name
|
16
20
|
def self.plugin_names
|
17
21
|
plugin_classes.map do |pk|
|
18
22
|
pk.plugin_name
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
26
|
+
# Instantiates all configured plugins and returns them.
|
27
|
+
# @return [Array<StubPlugin>] the instantiated plugins
|
22
28
|
def self.load_plugins!
|
23
29
|
pks = Plugin.plugin_classes.select { |pk| Config.plugin? pk.plugin_name }
|
24
|
-
pks.map { |pk| pk.new
|
30
|
+
pks.map { |pk| pk.new }
|
25
31
|
end
|
26
32
|
|
33
|
+
# An association of plugin names to their Class objects.
|
27
34
|
PLUGIN_MAP = plugin_names.zip(plugin_classes).to_h.freeze
|
28
35
|
end
|
29
36
|
end
|