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.
@@ -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
- attr_reader :index, :player, :plugins, :playlists
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::PLAYER_MAP[Config.player].new(self)
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
 
@@ -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
@@ -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
@@ -1,19 +1,33 @@
1
1
  module Muzak
2
2
  class Playlist
3
- attr_accessor :filename, :songs
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
@@ -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(self) }
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