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,16 +1,16 @@
1
1
  module Muzak
2
2
  module Plugin
3
+ # A no-op plugin that all real plugins inherit from.
3
4
  class StubPlugin
4
5
  include Utils
5
6
 
7
+ # The plugin's human friendly name.
8
+ # @return [String] the name
6
9
  def self.plugin_name
7
10
  name.split("::").last.downcase
8
11
  end
9
12
 
10
- attr_reader :instance
11
-
12
- def initialize(instance)
13
- @instance = instance
13
+ def initialize
14
14
  debug "loading #{self.class}"
15
15
  end
16
16
 
@@ -1,11 +1,39 @@
1
1
  require "taglib"
2
2
 
3
3
  module Muzak
4
+ # Represents a single song for muzak.
4
5
  class Song
5
6
  include Utils
6
7
 
7
- attr_reader :path, :title, :artist, :album, :year, :track, :genre, :comment, :length
8
+ # @return [String] the fully-qualified path to the song
9
+ attr_reader :path
8
10
 
11
+ # @return [String] the title of the song, identified from metadata
12
+ # @note if metadata is missing, the basename of the path is used instead
13
+ attr_reader :title
14
+
15
+ # @return [String, nil] the artist of the song, identified from metadata
16
+ attr_reader :artist
17
+
18
+ # @return [String, nil] the album of the song, identified from metadata
19
+ attr_reader :album
20
+
21
+ # @return [Integer, 0] the year of the song, identified from metadata
22
+ attr_reader :year
23
+
24
+ # @return [Integer, 0] the track number of the song, identified from metadata
25
+ attr_reader :track
26
+
27
+ # @return [String, nil] the genre of the song, identified from metadata
28
+ attr_reader :genre
29
+
30
+ # @return [String, nil] any comments in the song's metadata
31
+ attr_reader :comment
32
+
33
+ # @return [Integer] the length of the song, in seconds
34
+ attr_reader :length
35
+
36
+ # @param path [String] the path of the song to load
9
37
  def initialize(path)
10
38
  @path = path
11
39
 
@@ -26,6 +54,9 @@ module Muzak
26
54
  @track ||= 0 # we'll need to sort by track number
27
55
  end
28
56
 
57
+ # @return [String] A best guess path for the song's cover art
58
+ # @example
59
+ # song.best_guess_album_art # => "/path/to/song/directory/cover.jpg"
29
60
  def best_guess_album_art
30
61
  album_dir = File.dirname(path)
31
62
 
@@ -33,6 +64,10 @@ module Muzak
33
64
  File.join(album_dir, art) unless art.nil?
34
65
  end
35
66
 
67
+ # @return [String] the "full" title of the song, including artist and album
68
+ # if available.
69
+ # @example
70
+ # song.full_title # => "Song by Artist on Album"
36
71
  def full_title
37
72
  full = title.dup
38
73
  full << " by #{artist}" if artist
@@ -1,29 +1,52 @@
1
1
  module Muzak
2
+ # A collection of convenience utilities for use throughout muzak.
2
3
  module Utils
4
+ # Convert the given command into a method (kebab to camel case).
5
+ # @param cmd [String] the command to convert
6
+ # @return [String] the method corresponding to the command
7
+ # @example
8
+ # resolve_command "do-something" # => "do_something"
3
9
  def self.resolve_command(cmd)
4
10
  cmd.tr "-", "_"
5
11
  end
6
12
 
13
+ # Convert the given method into a command (camel to kebab case).
14
+ # @param meth [String, Symbol] the method to convert
15
+ # @return [String] the command corresponding to the method
16
+ # @example
17
+ # resolve_method "do_something" # => "do-something"
7
18
  def self.resolve_method(meth)
8
19
  meth.to_s.tr "_", "-"
9
20
  end
10
21
 
22
+ # Tests whether the given filename is likely to be music.
23
+ # @param filename [String] the filename to test
24
+ # @return [Boolean] whether or not the file is a music file
11
25
  def music?(filename)
12
26
  [".mp3", ".flac", ".m4a", ".wav", ".ogg", ".oga", ".opus"].include?(File.extname(filename.downcase))
13
27
  end
14
28
 
29
+ # Tests whether the given filename is likely to be album art.
30
+ # @param filename [String] the filename to test
31
+ # @return [Boolean] whether or not the file is an art file
15
32
  def album_art?(filename)
16
33
  File.basename(filename) =~ /(cover)|(folder).(jpg)|(png)/i
17
34
  end
18
35
 
36
+ # @return [Boolean] whether or not muzak is running in debug mode
19
37
  def debug?
20
38
  Config.debug
21
39
  end
22
40
 
41
+ # @return [Boolean] whether or not muzak is running in verbose mode
23
42
  def verbose?
24
43
  Config.verbose
25
44
  end
26
45
 
46
+ # Formats a string with ANSI colors.
47
+ # @param color [Symbol] the color to use on the string
48
+ # @param str [String] the string to format
49
+ # @return [String] the color-formatted string
27
50
  def pretty(color = :none, str)
28
51
  colors = {
29
52
  none: 0,
@@ -36,39 +59,68 @@ module Muzak
36
59
  "\e[#{colors[color]}m#{str}\e[0m"
37
60
  end
38
61
 
62
+ # Outputs a boxed message and arguments.
63
+ # @param box [String] the string to box
64
+ # @param args [Array<String>] the trailing strings to print
65
+ # @return [void]
39
66
  def output(box, *args)
40
67
  msg = args.join(" ")
41
68
  puts "[#{box}] #{msg}"
42
69
  end
43
70
 
71
+ # Outputs a boxed informational message.
72
+ # @param args [Array<String>] the message(s)
73
+ # @return [void]
44
74
  def info(*args)
45
75
  output pretty(:green, "info"), args
46
76
  end
47
77
 
78
+ # Outputs a boxed warning message.
79
+ # @param args [Array<String>] the message(s)
80
+ # @return [void]
48
81
  def warn(*args)
49
82
  output pretty(:yellow, "warn"), args
50
83
  end
51
84
 
85
+ # Outputs a boxed error message.
86
+ # @param args [Array<String>] the message(s)
87
+ # @return [void]
52
88
  def error(*args)
53
89
  output pretty(:red, "error"), "[#{self.class.name}]", args
54
90
  end
55
91
 
92
+ # Outputs a boxed debugging message.
93
+ # @param args [Array<String>] the message(s)
94
+ # @return [void]
56
95
  def debug(*args)
57
96
  return unless debug?
58
97
 
59
98
  output pretty(:yellow, "debug"), "[#{self.class.name}]", args
60
99
  end
61
100
 
101
+ # Outputs a boxed verbose message.
102
+ # @param args [Array<String>] the message(s)
103
+ # @return [void]
62
104
  def verbose(*args)
63
105
  return unless verbose?
64
106
 
65
107
  output pretty(:blue, "verbose"), args
66
108
  end
67
109
 
110
+ # Outputs a boxed warning message unless the arity of the given arguments
111
+ # equals the expected arity.
112
+ # @param args [Array<String>] the arguments
113
+ # @param arity [Integer] the expected arity
114
+ # @return [void]
68
115
  def warn_arity(args, arity)
69
116
  warn "expected #{arity} arguments, got #{args.length}" unless args.length == arity
70
117
  end
71
118
 
119
+ # Outputs a boxed failure message unless the arity of the given arguments
120
+ # equals the expected arity.
121
+ # @param args [Array<String>] the arguments
122
+ # @param arity [Integer] the expected arity
123
+ # @return [void]
72
124
  def fail_arity(args, arity)
73
125
  error "needed #{arity} arguments, got #{args.length}" unless args.length == arity
74
126
  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.0.11
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Woodruff
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-20 00:00:00.000000000 Z
11
+ date: 2016-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: taglib-ruby
@@ -34,6 +34,7 @@ executables:
34
34
  extensions: []
35
35
  extra_rdoc_files: []
36
36
  files:
37
+ - ".yardopts"
37
38
  - LICENSE
38
39
  - README.md
39
40
  - bin/muzak
@@ -57,13 +58,10 @@ files:
57
58
  - lib/muzak/player/stub_player.rb
58
59
  - lib/muzak/playlist.rb
59
60
  - lib/muzak/plugin.rb
60
- - lib/muzak/plugin/cava.rb
61
- - lib/muzak/plugin/notify.rb
62
- - lib/muzak/plugin/scrobble.rb
63
61
  - lib/muzak/plugin/stub_plugin.rb
64
62
  - lib/muzak/song.rb
65
63
  - lib/muzak/utils.rb
66
- homepage: https://github.com/woodruffw/muzak
64
+ homepage: https://github.com/muzak-project/muzak
67
65
  licenses:
68
66
  - MIT
69
67
  metadata: {}
@@ -1,44 +0,0 @@
1
- require "shellwords"
2
-
3
- module Muzak
4
- module Plugin
5
- class Cava < StubPlugin
6
- include Utils
7
-
8
- def initialize(instance)
9
- super
10
- @term_args = Shellwords.split Config.plugin_cava
11
- @pid = nil
12
- end
13
-
14
- def player_activated
15
- start_cava! unless cava_running?
16
- end
17
-
18
- def player_deactivated
19
- stop_cava! if cava_running?
20
- end
21
-
22
- private
23
-
24
- def cava_running?
25
- begin
26
- !!@pid && Process.waitpid(@pid, Process::WNOHANG).nil?
27
- rescue Errno::ECHILD
28
- false
29
- end
30
- end
31
-
32
- def start_cava!
33
- args = [*@term_args, "-e", "cava"]
34
- @pid = Process.spawn(*args)
35
- end
36
-
37
- def stop_cava!
38
- Process.kill :TERM, @pid
39
- Process.wait @pid
40
- @pid = nil
41
- end
42
- end
43
- end
44
- end
@@ -1,16 +0,0 @@
1
- module Muzak
2
- module Plugin
3
- class Notify < StubPlugin
4
- def song_loaded(song)
5
- notify song.full_title
6
- end
7
-
8
- private
9
-
10
- def notify(msg)
11
- pid = Process.spawn("notify-send", "muzak", msg)
12
- Process.detach(pid)
13
- end
14
- end
15
- end
16
- end
@@ -1,81 +0,0 @@
1
- require "net/http"
2
- require "digest"
3
-
4
- module Muzak
5
- module Plugin
6
- class Scrobble < StubPlugin
7
- include Utils
8
-
9
- def initialize(instance)
10
- super
11
- @username, @password_hash = Config.plugin_scrobble.split(":")
12
- end
13
-
14
- def song_loaded(song)
15
- if song.title.nil? || song.artist.nil?
16
- debug "cowardly refusing to scrobble a song ('#{song.path}') with missing metadata"
17
- return
18
- end
19
-
20
- scrobble song
21
- end
22
-
23
- private
24
-
25
- def scrobble(song)
26
- if @username.nil? || @password_hash.nil?
27
- error "missing username or password"
28
- return
29
- end
30
-
31
- handshake_endpoint = "http://post.audioscrobbler.com/"
32
- handshake_params = {
33
- "hs" => true,
34
- "p" => 1.1,
35
- "c" => "lsd",
36
- "v" => "1.0.4",
37
- "u" => @username
38
- }
39
-
40
- uri = URI(handshake_endpoint)
41
- uri.query = URI.encode_www_form(handshake_params)
42
-
43
- resp = Net::HTTP.get_response(uri)
44
-
45
- status, token, post_url, int = resp.body.split("\n")
46
-
47
- unless status =~ /UP(TO)?DATE/
48
- error "bad handshake, got '#{status}'"
49
- return
50
- end
51
-
52
- session_token = Digest::MD5.hexdigest(@password_hash + token)
53
-
54
- request_params = {
55
- "u" => @username,
56
- "s" => session_token,
57
- "a[0]" => song.artist,
58
- "t[0]" => song.title,
59
- "b[0]" => song.album,
60
- "m[0]" => "", # we don't know the MBID, so send an empty one
61
- "l[0]" => song.length,
62
- "i[0]" => Time.now.gmtime.strftime("%Y-%m-%d %H:%M:%S")
63
- }
64
-
65
- uri = URI(URI.encode(post_url))
66
- # uri.query = URI.encode_www_form(request_params)
67
-
68
- resp = Net::HTTP.post_form(uri, request_params)
69
-
70
- status, int = resp.body.split("\n")
71
-
72
- case status
73
- when "OK"
74
- debug "scrobble of '#{song.title}' successful"
75
- else
76
- debug "scrobble of '#{song.title}' failed, got '#{status}'"
77
- end
78
- end
79
- end
80
- end
81
- end