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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 917b86d06294801e33561fe181e64fd755f09828
4
- data.tar.gz: 3aebb78550918a276f0cefc7fc11e57d277759a3
3
+ metadata.gz: a2c396ccd25b5ca8dad7e33aad87e5e6f277f82f
4
+ data.tar.gz: 28f0518d153ca7dffa8713884a481b583ccdbdbb
5
5
  SHA512:
6
- metadata.gz: 2f9bcb70833ce12b36e14e2f56e6042cdbfef39dc88362b8e9908a255f1eaad19d185956ebde3dbf8608f562e41a3e52151390a9c49b6afb2b38435646c53e77
7
- data.tar.gz: 817a120b22297d8f3ba64afa8fe11a42452914c628de0f4b1a5f64d4d701b44c371024b2cdba0e9191cb9555ed4a0bd7d84fd8cadc2f8921c2e0e3e7f231e1fd
6
+ metadata.gz: 7619e9fec7c0413fadbd240c3abc828cef121aa8e9f34ba0a1c504750c31171a29aa0c43f18fdbac8d99de2ed68815db88266852ddaf0ed353dd525ee53f598a
7
+ data.tar.gz: a3b2b9fa7d6606a4d1f081878034890358fe97b763bcc8816365bf7cd24690905414631754c39450b1aca3d01aabe45792daa40eb0ef2a7978022effe5a045e3
@@ -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 short name of the user's music player.
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
- `autoplay: <playlist>` should be set to a playlist that the user wishes to
149
- automatically start when muzak starts. If not set, no playlist is automatically
150
- played.
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
- * replace MPV IPC implementation with [ruby-mpv](https://github.com/woodruffw/ruby-mpv)
39
+ * figure out how to do events/album art in VLC
@@ -12,5 +12,5 @@ require_relative "muzak/instance"
12
12
  # The primary namespace for muzak.
13
13
  module Muzak
14
14
  # Muzak's current version
15
- VERSION = "0.3.0".freeze
15
+ VERSION = "0.3.1".freeze
16
16
  end
@@ -39,6 +39,7 @@ module Muzak
39
39
  verbose "muzak is quitting..."
40
40
  player.deactivate!
41
41
 
42
+ event :instance_quitting
42
43
  build_response data: "quitting"
43
44
  end
44
45
  end
@@ -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
- warn "player is already running"
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
- warn "player is not running" unless player.running?
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.full_title
163
+ playing: player.now_playing&.full_title
164
164
  }
165
165
  else
166
166
  build_response error: "no currently playing song"
@@ -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,
@@ -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
- warn "unknown command: '#{cmd}'"
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(opts = {})
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.autoplay if Config.autoplay
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)
@@ -8,11 +8,18 @@ module Muzak
8
8
  module Player
9
9
  extend Utils
10
10
 
11
- # An association of shorthand player "names" to Class objects.
12
- PLAYER_MAP = {
13
- "stub" => Player::StubPlayer,
14
- "mpv" => Player::MPV
15
- }.freeze
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
@@ -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] Whether or not MPV is available for execution
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] Whether or not the current instance is running.
16
+ # @return [Boolean] whether or not the current instance is running.
17
17
  def running?
18
- @mpv&.running?
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
- cmds = ["loadfile", song.path, "append-play"]
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
@@ -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
- # @return [Array<Class>] all plugin classes visible under Plugin
14
- def self.plugin_classes
15
- constants.map(&Plugin.method(:const_get)).grep(Class)
16
- end
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
- # @return [Array<String>] the names of all plugin classes under Plugin
19
- # @see StubPlugin.plugin_name
20
- def self.plugin_names
21
- plugin_classes.map do |pk|
22
- pk.plugin_name
23
- end
24
- end
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 = Plugin.plugin_classes.select { |pk| Config.plugin? pk.plugin_name }
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
@@ -1,6 +1,8 @@
1
1
  module Muzak
2
2
  module Plugin
3
3
  # A no-op plugin that all real plugins inherit from.
4
+ # @abstract Subclass and define any of {Config::PLUGIN_EVENTS}
5
+ # as methods to implement plugins.
4
6
  class StubPlugin
5
7
  include Utils
6
8
 
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.0
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-22 00:00:00.000000000 Z
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