muzak 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/muzak-cmd +2 -1
- data/bin/muzak-dmenu +5 -6
- data/bin/muzak-index +22 -23
- data/bin/muzak-setup +2 -1
- data/bin/muzakd +4 -4
- data/lib/muzak.rb +3 -1
- data/lib/muzak/album.rb +4 -2
- data/lib/muzak/cmd.rb +3 -1
- data/lib/muzak/cmd/config.rb +2 -0
- data/lib/muzak/cmd/index.rb +7 -5
- data/lib/muzak/cmd/meta.rb +6 -4
- data/lib/muzak/cmd/player.rb +9 -8
- data/lib/muzak/cmd/playlist.rb +4 -2
- data/lib/muzak/config.rb +31 -11
- data/lib/muzak/index.rb +22 -18
- data/lib/muzak/instance.rb +6 -7
- data/lib/muzak/player.rb +2 -0
- data/lib/muzak/player/mpd.rb +2 -0
- data/lib/muzak/player/mpv.rb +28 -20
- data/lib/muzak/player/multiplayer.rb +2 -0
- data/lib/muzak/player/stub_player.rb +5 -3
- data/lib/muzak/player/vlc.rb +2 -0
- data/lib/muzak/playlist.rb +3 -5
- data/lib/muzak/plugin.rb +3 -1
- data/lib/muzak/song.rb +10 -9
- data/lib/muzak/utils.rb +15 -12
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64f9b1939f3f50d2a64717f9becb02ed072f37e2
|
4
|
+
data.tar.gz: ff514441823414639bb1df7917517fae7e991eac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d49e644fb6ad60237635b412ec0c0595fcc51e60d3e2491deb9ad0aa4959afc074bd18fd0bf9d44e456baf60ed506fd5d1923756c39e2cc89f93388eef2cbbe0
|
7
|
+
data.tar.gz: be732cb96cf268795b9bf89f9b1eb2745c283d2fbbbd123af902bf56df13a45d97841ba3b784d56896a39d78b764cef8c80aa883d3816b0932bee18849e5e4cb
|
data/bin/muzak-cmd
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require "muzak"
|
4
5
|
require "socket"
|
@@ -8,7 +9,7 @@ require "shellwords"
|
|
8
9
|
begin
|
9
10
|
server_host = Muzak::Config.daemon_host
|
10
11
|
server_port = Muzak::Config.daemon_port
|
11
|
-
sock
|
12
|
+
sock = TCPSocket.new server_host, server_port
|
12
13
|
rescue
|
13
14
|
error = { response: { error: "muzak is not running" } }.to_json
|
14
15
|
puts error
|
data/bin/muzak-dmenu
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require "muzak"
|
4
5
|
require "socket"
|
5
6
|
require "json"
|
6
7
|
require "open3"
|
7
8
|
|
8
|
-
DMENU_EXEC
|
9
|
+
DMENU_EXEC = Muzak::Config.dmenu_exec || "dmenu"
|
9
10
|
DMENU_LINES_EXEC = Muzak::Config.dmenu_lines_exec || "dmenu -l 10"
|
10
11
|
|
11
12
|
def fatal(msg)
|
@@ -15,7 +16,8 @@ end
|
|
15
16
|
|
16
17
|
def dmenu(options, lines: false)
|
17
18
|
dmenu_cmd = lines ? DMENU_LINES_EXEC : DMENU_EXEC
|
18
|
-
|
19
|
+
opts = options.join("\n")
|
20
|
+
|
19
21
|
Open3.popen2(dmenu_cmd) do |stdin, stdout|
|
20
22
|
stdin.puts opts
|
21
23
|
stdin.close
|
@@ -25,9 +27,7 @@ end
|
|
25
27
|
|
26
28
|
def muzak_cmd(command)
|
27
29
|
begin
|
28
|
-
|
29
|
-
server_port = Muzak::Config.daemon_port
|
30
|
-
sock = TCPSocket.new server_host, server_port
|
30
|
+
sock = TCPSocket.new Muzak::Config.daemon_host, Muzak::Config.daemon_port
|
31
31
|
rescue
|
32
32
|
fatal "Is muzakd running?"
|
33
33
|
end
|
@@ -60,4 +60,3 @@ command = "#{command.chomp} #{arguments}" if arguments
|
|
60
60
|
puts command
|
61
61
|
|
62
62
|
muzak_cmd command
|
63
|
-
|
data/bin/muzak-index
CHANGED
@@ -1,37 +1,38 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require "muzak"
|
4
5
|
|
5
|
-
VERSION =
|
6
|
+
VERSION = 3
|
6
7
|
|
7
8
|
OPTS = {
|
8
9
|
deep: !!(ARGV.delete("--deep") || ARGV.delete("-d")),
|
9
10
|
verbose: !!(ARGV.delete("--verbose") || ARGV.delete("-V")),
|
10
11
|
help: !!(ARGV.delete("--help") || ARGV.delete("-h")),
|
11
|
-
version: !!(ARGV.delete("--version") || ARGV.delete("-v"))
|
12
|
-
}
|
12
|
+
version: !!(ARGV.delete("--version") || ARGV.delete("-v")),
|
13
|
+
}.freeze
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
Usage: #{$PROGRAM_NAME} [options] [tree] [index]
|
15
|
+
HELP = <<~EOS
|
16
|
+
Usage: #{$PROGRAM_NAME} [options] [tree] [index]
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
Options:
|
19
|
+
--deep, -d Build a "deep" index (contains metadata)
|
20
|
+
--verbose, -V Be verbose while indexing
|
21
|
+
--help, -h Print this help message
|
22
|
+
--version, -v Print version information
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
Arguments:
|
25
|
+
[tree] The filesystem tree to index (default: #{Muzak::Config.music})
|
26
|
+
[index] The saved index (default: #{Muzak::Config::INDEX_FILE})
|
27
|
+
EOS
|
28
28
|
|
29
|
+
def help
|
30
|
+
puts HELP
|
29
31
|
exit
|
30
32
|
end
|
31
33
|
|
32
34
|
def version
|
33
35
|
puts "muzak-index version #{VERSION}."
|
34
|
-
|
35
36
|
exit
|
36
37
|
end
|
37
38
|
|
@@ -55,7 +56,7 @@ index_file = ARGV.shift || Muzak::Config::INDEX_FILE
|
|
55
56
|
index_hash = {
|
56
57
|
"tree" => tree,
|
57
58
|
"timestamp" => Time.now.to_i,
|
58
|
-
"deep" => OPTS[:deep]
|
59
|
+
"deep" => OPTS[:deep],
|
59
60
|
}
|
60
61
|
|
61
62
|
artist_names = Dir.entries(tree).reject! { |ent| ent.start_with?(".") }
|
@@ -70,8 +71,8 @@ artist_names.each do |artist|
|
|
70
71
|
|
71
72
|
info "indexing '#{artist}' - '#{album}'..."
|
72
73
|
|
73
|
-
album_hash
|
74
|
-
album_hash["songs"]
|
74
|
+
album_hash = {}
|
75
|
+
album_hash["songs"] = []
|
75
76
|
album_hash["deep-songs"] = []
|
76
77
|
|
77
78
|
Dir.glob(File.join(tree, artist, album, "**")) do |file|
|
@@ -79,9 +80,7 @@ artist_names.each do |artist|
|
|
79
80
|
|
80
81
|
if Muzak::Utils.music?(file)
|
81
82
|
album_hash["songs"] << file
|
82
|
-
if OPTS[:deep]
|
83
|
-
album_hash["deep-songs"] << Muzak::Song.new(file)
|
84
|
-
end
|
83
|
+
album_hash["deep-songs"] << Muzak::Song.new(file) if OPTS[:deep]
|
85
84
|
end
|
86
85
|
end
|
87
86
|
|
@@ -90,7 +89,7 @@ artist_names.each do |artist|
|
|
90
89
|
# if any of the track numbers in the album are > 0 (the fallback),
|
91
90
|
# sort by ID3 track numbers. otherwise, hope that the song
|
92
91
|
# paths contain track numbers (e.g, "01 song.mp3").
|
93
|
-
if album_hash["deep-songs"].any? { |s| s.track
|
92
|
+
if album_hash["deep-songs"].any? { |s| s.track.positive? }
|
94
93
|
album_hash["deep-songs"].sort_by!(&:track)
|
95
94
|
else
|
96
95
|
album_hash["deep-songs"].sort_by!(&:path)
|
data/bin/muzak-setup
CHANGED
data/bin/muzakd
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require "muzak"
|
4
5
|
require "shellwords"
|
@@ -8,8 +9,7 @@ require "thread"
|
|
8
9
|
|
9
10
|
Process.daemon unless Muzak::Config.debug || Muzak::Config.verbose
|
10
11
|
|
11
|
-
muzak
|
12
|
-
|
12
|
+
muzak = Muzak::Instance.new
|
13
13
|
server = TCPServer.new Muzak::Config.daemon_port
|
14
14
|
|
15
15
|
loop do
|
@@ -18,10 +18,10 @@ loop do
|
|
18
18
|
cmd_argv = Shellwords.split(client.readline)
|
19
19
|
client.puts(muzak.command(*cmd_argv).to_json)
|
20
20
|
exit! 0 if cmd_argv.first == "quit"
|
21
|
-
rescue
|
21
|
+
rescue => e
|
22
22
|
client.puts({ response: { error: e.to_s } }.to_json)
|
23
23
|
ensure
|
24
|
-
client
|
24
|
+
client&.close
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
data/lib/muzak.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "muzak/config"
|
2
4
|
require_relative "muzak/utils"
|
3
5
|
require_relative "muzak/plugin"
|
@@ -12,5 +14,5 @@ require_relative "muzak/instance"
|
|
12
14
|
# The primary namespace for muzak.
|
13
15
|
module Muzak
|
14
16
|
# Muzak's current version
|
15
|
-
VERSION = "0.
|
17
|
+
VERSION = "0.5.0"
|
16
18
|
end
|
data/lib/muzak/album.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Muzak
|
2
4
|
# Represents a collection of songs for muzak.
|
3
5
|
class Album
|
@@ -14,8 +16,8 @@ module Muzak
|
|
14
16
|
# @param songs [Array<Song>] the album's songs
|
15
17
|
# @param cover_art [String] the album's cover art
|
16
18
|
def initialize(title, songs, cover_art = nil)
|
17
|
-
@title
|
18
|
-
@songs
|
19
|
+
@title = title
|
20
|
+
@songs = songs
|
19
21
|
@cover_art = cover_art
|
20
22
|
end
|
21
23
|
end
|
data/lib/muzak/cmd.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Dir.glob(File.join(__dir__, "cmd/*")) { |f| require_relative f }
|
2
4
|
|
3
5
|
module Muzak
|
@@ -5,7 +7,7 @@ module Muzak
|
|
5
7
|
# @see file:COMMANDS.md User Commands
|
6
8
|
module Cmd
|
7
9
|
# load commands included by the user
|
8
|
-
Dir
|
10
|
+
Dir[Config::USER_COMMAND_GLOB].each { |cmd| require cmd }
|
9
11
|
|
10
12
|
# @return [Array<String>] all valid muzak commands
|
11
13
|
def self.commands
|
data/lib/muzak/cmd/config.rb
CHANGED
data/lib/muzak/cmd/index.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Muzak
|
2
4
|
module Cmd
|
3
5
|
# Reload the active index from the index file.
|
@@ -19,7 +21,7 @@ module Muzak
|
|
19
21
|
# @cmdexample `muzak> dump-index`
|
20
22
|
def dump_index
|
21
23
|
build_response data: {
|
22
|
-
index: index.hash
|
24
|
+
index: index.hash,
|
23
25
|
}
|
24
26
|
end
|
25
27
|
|
@@ -28,7 +30,7 @@ module Muzak
|
|
28
30
|
# @cmdexample `muzak> list-artists`
|
29
31
|
def list_artists
|
30
32
|
build_response data: {
|
31
|
-
artists: index.artists
|
33
|
+
artists: index.artists,
|
32
34
|
}
|
33
35
|
end
|
34
36
|
|
@@ -37,7 +39,7 @@ module Muzak
|
|
37
39
|
# @cmdexample `muzak> list-albums`
|
38
40
|
def list_albums
|
39
41
|
build_response data: {
|
40
|
-
albums: index.album_names
|
42
|
+
albums: index.album_names,
|
41
43
|
}
|
42
44
|
end
|
43
45
|
|
@@ -50,7 +52,7 @@ module Muzak
|
|
50
52
|
albums = index.albums_by(artist).map(&:title)
|
51
53
|
|
52
54
|
build_response data: {
|
53
|
-
albums: albums
|
55
|
+
albums: albums,
|
54
56
|
}
|
55
57
|
end
|
56
58
|
|
@@ -63,7 +65,7 @@ module Muzak
|
|
63
65
|
songs = index.songs_by(artist).map(&:title)
|
64
66
|
|
65
67
|
build_response data: {
|
66
|
-
songs: songs
|
68
|
+
songs: songs,
|
67
69
|
}
|
68
70
|
end
|
69
71
|
end
|
data/lib/muzak/cmd/meta.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Muzak
|
2
4
|
module Cmd
|
3
5
|
# Return a simple heartbeat message.
|
@@ -8,16 +10,16 @@ module Muzak
|
|
8
10
|
debug "pong: #{timestamp}"
|
9
11
|
|
10
12
|
build_response data: {
|
11
|
-
pong: timestamp
|
13
|
+
pong: timestamp,
|
12
14
|
}
|
13
15
|
end
|
14
16
|
|
15
17
|
# Return a "helpful" listing of commands.
|
16
18
|
# @command `help`
|
17
19
|
# @cmdexample `muzak> help`
|
18
|
-
def help(*
|
20
|
+
def help(*_args)
|
19
21
|
build_response data: {
|
20
|
-
commands: Muzak::Cmd.commands
|
22
|
+
commands: Muzak::Cmd.commands,
|
21
23
|
}
|
22
24
|
end
|
23
25
|
|
@@ -28,7 +30,7 @@ module Muzak
|
|
28
30
|
# plugins are configured.
|
29
31
|
def list_plugins
|
30
32
|
build_response data: {
|
31
|
-
plugins: Plugin.plugin_names
|
33
|
+
plugins: Plugin.plugin_names,
|
32
34
|
}
|
33
35
|
end
|
34
36
|
|
data/lib/muzak/cmd/player.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Muzak
|
2
4
|
module Cmd
|
3
5
|
# Activate the configured player.
|
@@ -12,7 +14,7 @@ module Muzak
|
|
12
14
|
end
|
13
15
|
|
14
16
|
build_response data: {
|
15
|
-
player: player.class.name
|
17
|
+
player: player.class.name,
|
16
18
|
}
|
17
19
|
end
|
18
20
|
|
@@ -27,7 +29,7 @@ module Muzak
|
|
27
29
|
player.deactivate!
|
28
30
|
|
29
31
|
build_response data: {
|
30
|
-
player: player.class.name
|
32
|
+
player: player.class.name,
|
31
33
|
}
|
32
34
|
end
|
33
35
|
|
@@ -87,7 +89,7 @@ module Muzak
|
|
87
89
|
artist = args.join(" ")
|
88
90
|
albums = index.albums_by(artist)
|
89
91
|
|
90
|
-
|
92
|
+
if albums.any?
|
91
93
|
albums.each do |album|
|
92
94
|
player.enqueue_album album
|
93
95
|
end
|
@@ -102,8 +104,7 @@ module Muzak
|
|
102
104
|
# @cmdexample `muzak> enqueue-album Your Favorite Album`
|
103
105
|
def enqueue_album(*args)
|
104
106
|
album_name = args.join(" ")
|
105
|
-
|
106
|
-
album = index.albums[album_name]
|
107
|
+
album = index.albums[album_name]
|
107
108
|
|
108
109
|
if album
|
109
110
|
player.enqueue_album album
|
@@ -122,7 +123,7 @@ module Muzak
|
|
122
123
|
songs.each { |s| player.enqueue_song s }
|
123
124
|
|
124
125
|
build_response data: {
|
125
|
-
jukebox: songs.map(&:full_title)
|
126
|
+
jukebox: songs.map(&:full_title),
|
126
127
|
}
|
127
128
|
end
|
128
129
|
|
@@ -131,7 +132,7 @@ module Muzak
|
|
131
132
|
# @cmdexample `muzak> list-queue`
|
132
133
|
def list_queue
|
133
134
|
build_response data: {
|
134
|
-
queue: player.list_queue.map(&:title)
|
135
|
+
queue: player.list_queue.map(&:title),
|
135
136
|
}
|
136
137
|
end
|
137
138
|
|
@@ -160,7 +161,7 @@ module Muzak
|
|
160
161
|
def now_playing
|
161
162
|
if player.playing?
|
162
163
|
build_response data: {
|
163
|
-
playing: player.now_playing&.full_title
|
164
|
+
playing: player.now_playing&.full_title,
|
164
165
|
}
|
165
166
|
else
|
166
167
|
build_response error: "no currently playing song"
|
data/lib/muzak/cmd/playlist.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Muzak
|
2
4
|
module Cmd
|
3
5
|
# List all currently available playlists.
|
@@ -5,7 +7,7 @@ module Muzak
|
|
5
7
|
# @cmdexample `muzak> list-playlists`
|
6
8
|
def list_playlists
|
7
9
|
build_response data: {
|
8
|
-
playlists: Playlist.playlist_names
|
10
|
+
playlists: Playlist.playlist_names,
|
9
11
|
}
|
10
12
|
end
|
11
13
|
|
@@ -53,7 +55,7 @@ module Muzak
|
|
53
55
|
artist = args.join(" ")
|
54
56
|
songs = index.songs_by(artist)
|
55
57
|
|
56
|
-
|
58
|
+
if songs.any?
|
57
59
|
playlists[pname].add(songs)
|
58
60
|
build_response
|
59
61
|
else
|
data/lib/muzak/config.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "yaml"
|
2
4
|
require "fileutils"
|
3
5
|
|
@@ -23,12 +25,18 @@ module Muzak
|
|
23
25
|
# The directory for all user playlists
|
24
26
|
PLAYLIST_DIR = File.join(CONFIG_DIR, "playlists").freeze
|
25
27
|
|
28
|
+
# The glob pattern for all user playlists
|
29
|
+
PLAYLIST_GLOB = File.join(PLAYLIST_DIR, "*.yml").freeze
|
30
|
+
|
26
31
|
# The directory for all user plugins
|
27
32
|
USER_PLUGIN_DIR = File.join(CONFIG_DIR, "plugins").freeze
|
28
33
|
|
29
34
|
# The directory for all user commands
|
30
35
|
USER_COMMAND_DIR = File.join(CONFIG_DIR, "commands").freeze
|
31
36
|
|
37
|
+
# The glob pattern for all user commands
|
38
|
+
USER_COMMAND_GLOB = File.join(USER_COMMAND_DIR, "*.rb").freeze
|
39
|
+
|
32
40
|
# All filename suffixes that muzak recognizes as music.
|
33
41
|
MUSIC_SUFFIXES = [
|
34
42
|
".mp3",
|
@@ -41,17 +49,17 @@ module Muzak
|
|
41
49
|
].freeze
|
42
50
|
|
43
51
|
# The regular expression that muzak uses to find album art.
|
44
|
-
ALBUM_ART_REGEX = /(cover)|(folder).(jpg)|(png)/i
|
52
|
+
ALBUM_ART_REGEX = /(cover)|(folder).(jpg)|(png)/i
|
45
53
|
|
46
54
|
# All events currently propagated by {Muzak::Instance#event}
|
47
|
-
PLUGIN_EVENTS = [
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
+
PLUGIN_EVENTS = %i[
|
56
|
+
instance_started
|
57
|
+
instance_quitting
|
58
|
+
player_activated
|
59
|
+
player_deactivated
|
60
|
+
song_loaded
|
61
|
+
song_unloaded
|
62
|
+
playlist_enqueued
|
55
63
|
].freeze
|
56
64
|
|
57
65
|
# The default configuration keys and values.
|
@@ -91,7 +99,19 @@ module Muzak
|
|
91
99
|
# Catches all undefined configuration keys and defaults them to false.
|
92
100
|
# @return [false]
|
93
101
|
def self.method_missing(method, *args)
|
94
|
-
|
102
|
+
# this is basically useless, since respond_to_missing? will always be true,
|
103
|
+
# but it gets RuboCop to shut up.
|
104
|
+
if respond_to_missing? method, *args
|
105
|
+
false
|
106
|
+
else
|
107
|
+
super
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# We "respond" to all methods with a default of false, so this is always true.
|
112
|
+
# @return [true]
|
113
|
+
def self.respond_to_missing?(*_args)
|
114
|
+
true
|
95
115
|
end
|
96
116
|
|
97
117
|
# @return [Boolean] whether or not the given plugin is configured
|
@@ -102,7 +122,7 @@ module Muzak
|
|
102
122
|
end
|
103
123
|
|
104
124
|
if File.exist?(CONFIG_FILE)
|
105
|
-
user_config = YAML
|
125
|
+
user_config = YAML.load_file(CONFIG_FILE)
|
106
126
|
else
|
107
127
|
user_config = DEFAULT_CONFIG
|
108
128
|
|
data/lib/muzak/index.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Muzak
|
2
4
|
# Represents muzak's music index.
|
3
5
|
class Index
|
@@ -26,8 +28,7 @@ module Muzak
|
|
26
28
|
debug "loading index from '#{file}'..."
|
27
29
|
|
28
30
|
@file = file
|
29
|
-
|
30
|
-
@hash = Marshal.load(File.read file)
|
31
|
+
@hash = Marshal.load File.read(file) # rubocop:disable Security/MarshalLoad
|
31
32
|
|
32
33
|
memoize_collections!
|
33
34
|
end
|
@@ -37,7 +38,7 @@ module Muzak
|
|
37
38
|
# @return [void]
|
38
39
|
def reload!
|
39
40
|
debug "reloading index from '#{file}'..."
|
40
|
-
@hash = Marshal.load
|
41
|
+
@hash = Marshal.load File.read(file) # rubocop:disable Security/MarshalLoad
|
41
42
|
@albums_hash = nil
|
42
43
|
memoize_collections!
|
43
44
|
end
|
@@ -65,11 +66,7 @@ module Muzak
|
|
65
66
|
|
66
67
|
artists.each do |a|
|
67
68
|
@hash["artists"][a]["albums"].each do |title, album_hash|
|
68
|
-
|
69
|
-
songs = album_hash["deep-songs"]
|
70
|
-
else
|
71
|
-
songs = album_hash["songs"].map { |s| Song.new(s) }
|
72
|
-
end
|
69
|
+
songs = load_songs album_hash
|
73
70
|
albums_hash[title] = Album.new(title, songs, album_hash["cover"])
|
74
71
|
end
|
75
72
|
end
|
@@ -101,11 +98,7 @@ module Muzak
|
|
101
98
|
def albums_by(artist)
|
102
99
|
if artists.include?(artist)
|
103
100
|
@hash["artists"][artist]["albums"].map do |title, album_hash|
|
104
|
-
|
105
|
-
songs = album_hash["deep-songs"]
|
106
|
-
else
|
107
|
-
songs = album_hash["songs"].map { |s| Song.new(s) }
|
108
|
-
end
|
101
|
+
songs = load_songs album_hash
|
109
102
|
Album.new(title, songs, album_hash["cover"])
|
110
103
|
end
|
111
104
|
else
|
@@ -133,10 +126,8 @@ module Muzak
|
|
133
126
|
error "no such artist: '#{artist}'" unless @hash["artists"].key?(artist)
|
134
127
|
|
135
128
|
begin
|
136
|
-
albums_by(artist).map
|
137
|
-
|
138
|
-
end.flatten
|
139
|
-
rescue Exception => e
|
129
|
+
albums_by(artist).map(&:songs).flatten
|
130
|
+
rescue
|
140
131
|
[]
|
141
132
|
end
|
142
133
|
end
|
@@ -148,10 +139,23 @@ module Muzak
|
|
148
139
|
@all_albums = @hash["artists"].map { |_, a| a["albums"] }.flatten
|
149
140
|
|
150
141
|
if deep?
|
151
|
-
@all_deep_songs = @all_albums.map
|
142
|
+
@all_deep_songs = @all_albums.map do |aa|
|
143
|
+
aa.map { |_, a| a["deep-songs"] }
|
144
|
+
end.flatten
|
152
145
|
else
|
153
146
|
@all_songs = @all_albums.map { |aa| aa.map { |_, a| a["songs"] } }.flatten
|
154
147
|
end
|
155
148
|
end
|
149
|
+
|
150
|
+
# Load the songs from an album hash into {Song} instances.
|
151
|
+
# @param ah [Hash] the album hash
|
152
|
+
# @api private
|
153
|
+
def load_songs(ah)
|
154
|
+
if deep?
|
155
|
+
ah["deep-songs"]
|
156
|
+
else
|
157
|
+
ah["songs"].map { |s| Song.new s }
|
158
|
+
end
|
159
|
+
end
|
156
160
|
end
|
157
161
|
end
|
data/lib/muzak/instance.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Muzak
|
2
4
|
# Encapsulates the entirety of muzak's running state.
|
3
5
|
class Instance
|
@@ -15,7 +17,7 @@ module Muzak
|
|
15
17
|
if Cmd.commands.include?(cmd)
|
16
18
|
meth = method(Config.resolve_command(cmd))
|
17
19
|
if meth.arity == args.size || meth.arity <= -1
|
18
|
-
meth.call
|
20
|
+
meth.call(*args)
|
19
21
|
else
|
20
22
|
build_response error: "got #{args.size} args, needed #{meth.arity}"
|
21
23
|
end
|
@@ -42,12 +44,9 @@ module Muzak
|
|
42
44
|
|
43
45
|
error! "#{Config.music} doesn't exist" unless File.exist?(Config.music)
|
44
46
|
|
45
|
-
@index
|
46
|
-
|
47
|
-
@
|
48
|
-
|
49
|
-
@plugins = Plugin.load_plugins!
|
50
|
-
|
47
|
+
@index = Index.load_index!
|
48
|
+
@player = Player.load_player!(self)
|
49
|
+
@plugins = Plugin.load_plugins!
|
51
50
|
@playlists = Playlist.load_playlists!
|
52
51
|
|
53
52
|
enqueue_playlist Config.default_playlist if Config.default_playlist
|
data/lib/muzak/player.rb
CHANGED
data/lib/muzak/player/mpd.rb
CHANGED
data/lib/muzak/player/mpv.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "tempfile"
|
2
4
|
require "socket"
|
3
5
|
require "json"
|
@@ -8,6 +10,14 @@ module Muzak
|
|
8
10
|
module Player
|
9
11
|
# Exposes MPV's IPC to muzak for playback control.
|
10
12
|
class MPV < StubPlayer
|
13
|
+
DEFAULT_MPV_ARGS = [
|
14
|
+
"--no-osc",
|
15
|
+
"--no-osd-bar",
|
16
|
+
"--no-input-default-bindings",
|
17
|
+
"--no-input-cursor",
|
18
|
+
"--load-scripts=no", # autoload and other scripts clobber our mpv management
|
19
|
+
].freeze
|
20
|
+
|
11
21
|
# @return [Boolean] whether or not MPV is available for execution
|
12
22
|
def self.available?
|
13
23
|
::MPV::Server.available?
|
@@ -25,24 +35,7 @@ module Muzak
|
|
25
35
|
|
26
36
|
debug "activating #{self.class}"
|
27
37
|
|
28
|
-
args =
|
29
|
-
# there's also this, which (might) also work
|
30
|
-
# "--audio-display=no",
|
31
|
-
"--no-osc",
|
32
|
-
"--no-osd-bar",
|
33
|
-
"--no-input-default-bindings",
|
34
|
-
"--no-input-cursor",
|
35
|
-
"--load-scripts=no", # autoload and other scripts with clobber our mpv management
|
36
|
-
]
|
37
|
-
|
38
|
-
args.concat ["--no-force-window", "--no-video"] if Config.mpv_no_art
|
39
|
-
|
40
|
-
args << "--geometry=#{Config.art_geometry}" if Config.art_geometry
|
41
|
-
|
42
|
-
# this is an experimental flag, but it could improve
|
43
|
-
# muzak's load times substantially when used with a network
|
44
|
-
# mounted music library
|
45
|
-
args << "--prefetch-playlist" if ::MPV::Server.has_flag?("--prefetch-playlist")
|
38
|
+
args = DEFAULT_MPV_ARGS + configured_mpv_args
|
46
39
|
|
47
40
|
@mpv = ::MPV::Session.new(user_args: args)
|
48
41
|
@mpv.callbacks << ::MPV::Callback.new(self, :dispatch_event!)
|
@@ -172,7 +165,22 @@ module Muzak
|
|
172
165
|
# Get mpv's currently loaded song.
|
173
166
|
# @return [Song, nil] the currently loaded song
|
174
167
|
def now_playing
|
175
|
-
@_now_playing ||= Song.new
|
168
|
+
@_now_playing ||= Song.new @mpv.get_property("path")
|
169
|
+
end
|
170
|
+
|
171
|
+
def configured_mpv_args
|
172
|
+
args = []
|
173
|
+
|
174
|
+
args.concat ["--no-force-window", "--no-video"] if Config.mpv_no_art
|
175
|
+
|
176
|
+
args << "--geometry=#{Config.art_geometry}" if Config.art_geometry
|
177
|
+
|
178
|
+
# this is an experimental flag, but it could improve
|
179
|
+
# muzak's load times substantially when used with a network
|
180
|
+
# mounted music library
|
181
|
+
args << "--prefetch-playlist" if ::MPV::Server.has_flag?("--prefetch-playlist")
|
182
|
+
|
183
|
+
args
|
176
184
|
end
|
177
185
|
|
178
186
|
# Load a song and optional album art into mpv.
|
@@ -184,7 +192,7 @@ module Muzak
|
|
184
192
|
append_type = Config.autoplay ? "append-play" : "append"
|
185
193
|
cmds = ["loadfile", song.path, append_type]
|
186
194
|
cmds << "external-file=\"#{art}\"" if art
|
187
|
-
@mpv.command
|
195
|
+
@mpv.command(*cmds)
|
188
196
|
end
|
189
197
|
|
190
198
|
# Dispatch the given event to the active {Muzak::Instance}.
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Muzak
|
2
4
|
module Player
|
3
5
|
# A no-op player that all players inherit from.
|
@@ -85,7 +87,7 @@ module Muzak
|
|
85
87
|
# @param song [Song] the song to enqueue
|
86
88
|
# @return [void]
|
87
89
|
# @note NO-OP
|
88
|
-
def enqueue_song(
|
90
|
+
def enqueue_song(_song)
|
89
91
|
debug "#enqueue_song"
|
90
92
|
end
|
91
93
|
|
@@ -93,7 +95,7 @@ module Muzak
|
|
93
95
|
# @param album [Album] the album to enqueue
|
94
96
|
# @return [void]
|
95
97
|
# @note NO-OP
|
96
|
-
def enqueue_album(
|
98
|
+
def enqueue_album(_album)
|
97
99
|
debug "#enqueue_album"
|
98
100
|
end
|
99
101
|
|
@@ -101,7 +103,7 @@ module Muzak
|
|
101
103
|
# @param playlist [Playlist] the playlist to enqueue
|
102
104
|
# @return [void]
|
103
105
|
# @note NO-OP
|
104
|
-
def enqueue_playlist(
|
106
|
+
def enqueue_playlist(_playlist)
|
105
107
|
debug "#enqueue_playlist"
|
106
108
|
end
|
107
109
|
|
data/lib/muzak/player/vlc.rb
CHANGED
data/lib/muzak/playlist.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Muzak
|
2
4
|
# Represents a sequential list of songs for muzak.
|
3
5
|
class Playlist
|
@@ -30,11 +32,7 @@ module Muzak
|
|
30
32
|
|
31
33
|
# @return [Array<String>] the names of all currently available playlists
|
32
34
|
def self.playlist_names
|
33
|
-
Dir
|
34
|
-
ent.start_with?(".")
|
35
|
-
end.map do |ent|
|
36
|
-
File.basename(ent, File.extname(ent))
|
37
|
-
end
|
35
|
+
Dir[Config::PLAYLIST_GLOB].map { |p| File.basename(p, File.extname(p)) }
|
38
36
|
end
|
39
37
|
|
40
38
|
# Instantiates all playlists by loading them from disk.
|
data/lib/muzak/plugin.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# we have to require StubPlugin first because ruby's module resolution is bad
|
2
4
|
require_relative "plugin/stub_plugin"
|
3
5
|
|
@@ -30,7 +32,7 @@ module Muzak
|
|
30
32
|
Config.plugin?(pk.plugin_name) && pk.available?
|
31
33
|
end
|
32
34
|
|
33
|
-
pks.map
|
35
|
+
pks.map(&:new)
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
data/lib/muzak/song.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "taglib"
|
2
4
|
require "json"
|
3
5
|
|
@@ -39,15 +41,14 @@ module Muzak
|
|
39
41
|
@path = path
|
40
42
|
|
41
43
|
TagLib::FileRef.open(path) do |ref|
|
42
|
-
|
43
|
-
@
|
44
|
-
@
|
45
|
-
@
|
46
|
-
@
|
47
|
-
@
|
48
|
-
@
|
49
|
-
@
|
50
|
-
@length = ref.audio_properties.length
|
44
|
+
@title = ref&.tag&.title
|
45
|
+
@artist = ref&.tag&.artist
|
46
|
+
@album = ref&.tag&.album
|
47
|
+
@year = ref&.tag&.year
|
48
|
+
@track = ref&.tag&.track
|
49
|
+
@genre = ref&.tag&.genre
|
50
|
+
@comment = ref&.tag&.comment
|
51
|
+
@length = ref&.audio_properties&.length
|
51
52
|
end
|
52
53
|
|
53
54
|
# provide some sane fallbacks
|
data/lib/muzak/utils.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Muzak
|
2
4
|
# A collection of convenience utilities for use throughout muzak.
|
3
5
|
module Utils
|
@@ -38,13 +40,13 @@ module Muzak
|
|
38
40
|
# @param color [Symbol] the color to use on the string
|
39
41
|
# @param str [String] the string to format
|
40
42
|
# @return [String] the color-formatted string
|
41
|
-
def pretty(color = :none
|
43
|
+
def pretty(str, color = :none)
|
42
44
|
colors = {
|
43
45
|
none: 0,
|
44
46
|
red: 31,
|
45
47
|
green: 32,
|
46
48
|
yellow: 33,
|
47
|
-
blue: 34
|
49
|
+
blue: 34,
|
48
50
|
}
|
49
51
|
|
50
52
|
"\e[#{colors[color]}m#{str}\e[0m"
|
@@ -63,22 +65,22 @@ module Muzak
|
|
63
65
|
# @param args [Array<String>] the message(s)
|
64
66
|
# @return [void]
|
65
67
|
def danger(*args)
|
66
|
-
output pretty(
|
68
|
+
output pretty("warn", :yellow), args
|
67
69
|
end
|
68
70
|
|
69
71
|
# Outputs a boxed error message.
|
70
72
|
# @param args [Array<String>] the message(s)
|
71
73
|
# @return [void]
|
72
74
|
def error(*args)
|
73
|
-
context =
|
74
|
-
output pretty(
|
75
|
+
context = is_a?(Module) ? name : self.class.name
|
76
|
+
output pretty("error", :red), "[#{context}]", args
|
75
77
|
end
|
76
78
|
|
77
79
|
# Outputs a boxed error message and then exits.
|
78
80
|
# @param args [Array<String>] the message(s)
|
79
81
|
# @return [void]
|
80
82
|
def error!(*args)
|
81
|
-
error
|
83
|
+
error(*args)
|
82
84
|
exit 1
|
83
85
|
end
|
84
86
|
|
@@ -87,8 +89,8 @@ module Muzak
|
|
87
89
|
# @return [void]
|
88
90
|
def debug(*args)
|
89
91
|
return unless debug?
|
90
|
-
context =
|
91
|
-
output pretty(
|
92
|
+
context = is_a?(Module) ? name : self.class.name
|
93
|
+
output pretty("debug", :yellow), "[#{context}]", args
|
92
94
|
end
|
93
95
|
|
94
96
|
# Outputs a boxed verbose message.
|
@@ -97,18 +99,19 @@ module Muzak
|
|
97
99
|
def verbose(*args)
|
98
100
|
return unless verbose?
|
99
101
|
|
100
|
-
output pretty(
|
102
|
+
output pretty("verbose", :blue), args
|
101
103
|
end
|
102
104
|
|
103
105
|
# Returns a response hash containing the given data and error.
|
104
106
|
# @param error [String] the error string, if needed
|
105
107
|
# @param data [String, Hash] the data, if needed
|
106
108
|
def build_response(error: nil, data: nil)
|
107
|
-
{
|
109
|
+
{
|
110
|
+
response: {
|
108
111
|
error: error,
|
109
112
|
data: data,
|
110
|
-
method: caller_locations.first.label
|
111
|
-
}
|
113
|
+
method: caller_locations.first.label,
|
114
|
+
},
|
112
115
|
}
|
113
116
|
end
|
114
117
|
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.
|
4
|
+
version: 0.5.0
|
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-
|
11
|
+
date: 2017-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: taglib-ruby
|