muzak 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -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