musica 0.1.2 → 0.3.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +37 -22
- data/README.md +40 -7
- data/exe/musica +5 -1
- data/lib/musica/cli.rb +44 -23
- data/lib/musica/config.rb +29 -0
- data/lib/musica/intent.json +47 -0
- data/lib/musica/interpreter.rb +126 -0
- data/lib/musica/player.rb +8 -3
- data/lib/musica/version.rb +1 -1
- data/lib/musica.rb +2 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 68997c53cdcb8c578599e7ede93568d15db73adf953c7c6d674e382aed778ee5
|
|
4
|
+
data.tar.gz: f5591c92f6bae98c446a2a8bce0be35f6c18749475fe9f553a8fdd79d36c779a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7945760f5d031faf50a9dd0b0161a2b26e5c127214a78c0b15cefeb58d165980640bd7c52d91a02f98d7f71906748d65143e39fb094a2678d4c5c582bbd0b736
|
|
7
|
+
data.tar.gz: 4ba20cab7bb5ac547b172936eabf69b6d154b6a450faaa3da4f0f57fe7f6eed95f9e11d9bf849dea6eb95b8bd62913dd5b25e8533f0bb44355b0d7360b6c149c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## [0.3.0] - 2026-10-19
|
|
2
|
+
|
|
3
|
+
- Turn plain requests into commands by default, without `-n`
|
|
4
|
+
- Send AppleScript commands as is with `-s`/`--script`, which was the default before
|
|
5
|
+
- Add `-h`/`--help` and `-v`/`--version`
|
|
6
|
+
|
|
7
|
+
## [0.2.0] - 2026-10-19
|
|
8
|
+
|
|
9
|
+
- Add natural language commands with `-n`, using Apple's on-device model through `fm`
|
|
10
|
+
- Play tracks and albums from the Music library by name, artist or album
|
|
11
|
+
|
|
1
12
|
## [0.1.2] - 2025-09-01
|
|
2
13
|
|
|
3
14
|
- Add YAML configuration support for player selection
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
musica (0.
|
|
4
|
+
musica (0.3.0)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
@@ -14,17 +14,20 @@ GEM
|
|
|
14
14
|
io-console (0.6.0)
|
|
15
15
|
irb (1.6.3)
|
|
16
16
|
reline (>= 0.3.0)
|
|
17
|
-
json (
|
|
17
|
+
json (3.1.0)
|
|
18
18
|
language_server-protocol (3.17.0.3)
|
|
19
|
+
lint_roller (1.1.0)
|
|
19
20
|
parallel (1.22.1)
|
|
20
|
-
parser (3.
|
|
21
|
+
parser (3.3.12.0)
|
|
21
22
|
ast (~> 2.4.1)
|
|
23
|
+
racc
|
|
24
|
+
prism (1.9.0)
|
|
25
|
+
racc (1.8.1)
|
|
22
26
|
rainbow (3.1.1)
|
|
23
|
-
rake (13.
|
|
24
|
-
regexp_parser (2.
|
|
27
|
+
rake (13.4.2)
|
|
28
|
+
regexp_parser (2.13.1)
|
|
25
29
|
reline (0.3.3)
|
|
26
30
|
io-console (~> 0.5)
|
|
27
|
-
rexml (3.2.5)
|
|
28
31
|
rspec (3.12.0)
|
|
29
32
|
rspec-core (~> 3.12.0)
|
|
30
33
|
rspec-expectations (~> 3.12.0)
|
|
@@ -38,31 +41,43 @@ GEM
|
|
|
38
41
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
39
42
|
rspec-support (~> 3.12.0)
|
|
40
43
|
rspec-support (3.12.0)
|
|
41
|
-
rubocop (1.
|
|
42
|
-
json (
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
rubocop (1.91.0)
|
|
45
|
+
json (>= 2.3)
|
|
46
|
+
language_server-protocol (~> 3.17.0.2)
|
|
47
|
+
lint_roller (~> 1.1.0)
|
|
48
|
+
parallel (>= 1.10)
|
|
49
|
+
parser (>= 3.3.0.2)
|
|
45
50
|
rainbow (>= 2.2.2, < 4.0)
|
|
46
|
-
regexp_parser (>=
|
|
47
|
-
|
|
48
|
-
rubocop-ast (>= 1.26.0, < 2.0)
|
|
51
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
52
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
49
53
|
ruby-progressbar (~> 1.7)
|
|
50
|
-
unicode-display_width (>= 2.4.0, <
|
|
51
|
-
rubocop-ast (1.
|
|
52
|
-
parser (>= 3.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
55
|
+
rubocop-ast (1.50.0)
|
|
56
|
+
parser (>= 3.3.7.2)
|
|
57
|
+
prism (~> 1.7)
|
|
58
|
+
rubocop-performance (1.26.1)
|
|
59
|
+
lint_roller (~> 1.1)
|
|
60
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
61
|
+
rubocop-ast (>= 1.47.1, < 2.0)
|
|
56
62
|
ruby-progressbar (1.13.0)
|
|
57
|
-
standard (1.
|
|
63
|
+
standard (1.57.0)
|
|
58
64
|
language_server-protocol (~> 3.17.0.2)
|
|
59
|
-
|
|
60
|
-
rubocop
|
|
65
|
+
lint_roller (~> 1.0)
|
|
66
|
+
rubocop (~> 1.91.0)
|
|
67
|
+
standard-custom (~> 1.0.0)
|
|
68
|
+
standard-performance (~> 1.8)
|
|
69
|
+
standard-custom (1.0.2)
|
|
70
|
+
lint_roller (~> 1.0)
|
|
71
|
+
rubocop (~> 1.50)
|
|
72
|
+
standard-performance (1.9.0)
|
|
73
|
+
lint_roller (~> 1.1)
|
|
74
|
+
rubocop-performance (~> 1.26.0)
|
|
61
75
|
unicode-display_width (2.4.2)
|
|
62
76
|
|
|
63
77
|
PLATFORMS
|
|
64
78
|
arm64-darwin-22
|
|
65
79
|
arm64-darwin-25
|
|
80
|
+
arm64-darwin-27
|
|
66
81
|
x86_64-linux
|
|
67
82
|
|
|
68
83
|
DEPENDENCIES
|
data/README.md
CHANGED
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
This is a Ruby command-line tool that interacts with music players on macOS
|
|
4
4
|
using AppleScript.
|
|
5
5
|
|
|
6
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be
|
|
7
|
-
able to package up your Ruby library into a gem. Put your Ruby code in the file
|
|
8
|
-
`lib/musica`. To experiment with that code, run `bin/console` for an interactive
|
|
9
|
-
prompt.
|
|
10
|
-
|
|
11
6
|
## Installation
|
|
12
7
|
|
|
13
8
|
Install the gem and add to the application's Gemfile by executing:
|
|
@@ -20,11 +15,49 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
|
20
15
|
|
|
21
16
|
## Usage
|
|
22
17
|
|
|
23
|
-
|
|
18
|
+
Tell Musica what you want in plain words:
|
|
19
|
+
|
|
20
|
+
$ musica skip this song
|
|
21
|
+
$ musica turn it down a bit
|
|
22
|
+
$ musica play Digital Love by Daft Punk
|
|
23
|
+
$ musica play something by Queen
|
|
24
|
+
$ musica play the album Adore
|
|
25
|
+
|
|
26
|
+
Musica uses Apple's on-device model, through the `fm` command, to turn the
|
|
27
|
+
request into a command for your music player. This needs macOS 26 with Apple
|
|
28
|
+
Intelligence.
|
|
29
|
+
|
|
30
|
+
It understands play, pause, next and previous track, volume changes, and
|
|
31
|
+
playing a track or album from your library by name, artist or album. To play
|
|
32
|
+
an album in order, Musica fills a playlist named "Musica" with its tracks and
|
|
33
|
+
plays that playlist, replacing what was in it before. Playing from your library
|
|
34
|
+
only works with Music.
|
|
35
|
+
|
|
36
|
+
After each request, Musica prints the current status: the artist and track
|
|
37
|
+
while playing, otherwise the player state:
|
|
24
38
|
|
|
25
39
|
$ musica play
|
|
40
|
+
🎧 Daft Punk - Digital Love
|
|
26
41
|
$ musica pause
|
|
27
|
-
|
|
42
|
+
🎧 paused
|
|
43
|
+
|
|
44
|
+
Run `musica` without arguments to print the status alone, `musica --help` for
|
|
45
|
+
a summary of the options, and `musica --version` for the version.
|
|
46
|
+
|
|
47
|
+
### AppleScript
|
|
48
|
+
|
|
49
|
+
With `-s` (or `--script`), Musica sends its arguments to the player as an
|
|
50
|
+
AppleScript command, without `fm`. Any command in the player's AppleScript
|
|
51
|
+
dictionary works (open it in Script Editor with File > Open Dictionary):
|
|
52
|
+
|
|
53
|
+
$ musica -s playpause
|
|
54
|
+
$ musica -s set sound volume to 50
|
|
55
|
+
|
|
56
|
+
Commands can differ between players. For example, Music uses
|
|
57
|
+
`set shuffle enabled to true` and Spotify uses `set shuffling to true`. Wrap
|
|
58
|
+
commands that contain quotes in single quotes so the shell keeps them:
|
|
59
|
+
|
|
60
|
+
$ musica -s 'play playlist "Favorite Songs"'
|
|
28
61
|
|
|
29
62
|
## Configuration
|
|
30
63
|
|
data/exe/musica
CHANGED
data/lib/musica/cli.rb
CHANGED
|
@@ -1,41 +1,62 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "yaml"
|
|
4
|
-
require "fileutils"
|
|
5
|
-
|
|
6
3
|
module Musica
|
|
7
4
|
class Cli
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
HELP = <<~TEXT
|
|
6
|
+
Usage: musica [request]
|
|
7
|
+
musica -s <applescript>
|
|
8
|
+
|
|
9
|
+
Without arguments, print what's playing. With a request, turn it into a
|
|
10
|
+
command using fm, send it to the music player, then print what's playing.
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
-s, --script Send an AppleScript command to the player as is
|
|
14
|
+
-h, --help Show this help
|
|
15
|
+
-v, --version Show the version
|
|
16
|
+
|
|
17
|
+
Examples:
|
|
18
|
+
musica skip this song
|
|
19
|
+
musica play the album Adore
|
|
20
|
+
musica -s set sound volume to 50
|
|
21
|
+
|
|
22
|
+
The player is set in musica/config.yml, under $XDG_CONFIG_HOME or ~/.config.
|
|
23
|
+
TEXT
|
|
24
|
+
|
|
25
|
+
def initialize(argv, player: nil, interpreter: nil, out: $stdout)
|
|
26
|
+
@argv = argv
|
|
27
|
+
@player = player
|
|
28
|
+
@interpreter = interpreter
|
|
29
|
+
@out = out
|
|
11
30
|
end
|
|
12
31
|
|
|
13
32
|
def run
|
|
14
|
-
|
|
15
|
-
|
|
33
|
+
case argv.first
|
|
34
|
+
when "-h", "--help"
|
|
35
|
+
out << HELP
|
|
36
|
+
when "-v", "--version"
|
|
37
|
+
out << "musica #{VERSION}\n"
|
|
38
|
+
when "-s", "--script"
|
|
39
|
+
play argv.drop(1).join(" ")
|
|
40
|
+
else
|
|
41
|
+
play(argv.empty? ? "" : interpreter.call(argv.join(" ")))
|
|
42
|
+
end
|
|
16
43
|
end
|
|
17
44
|
|
|
18
45
|
private
|
|
19
46
|
|
|
20
|
-
attr_reader :argv, :
|
|
47
|
+
attr_reader :argv, :out
|
|
21
48
|
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
unless File.exist?(config_file)
|
|
27
|
-
create_config(config_dir, config_file)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
YAML.load_file(config_file).dig("player")
|
|
49
|
+
def play(command)
|
|
50
|
+
player.tell command unless command.empty?
|
|
51
|
+
out << "\u{1F3A7} #{player.status}\n"
|
|
31
52
|
end
|
|
32
53
|
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
default_config = { "player" => "Music" }
|
|
54
|
+
def player
|
|
55
|
+
@player ||= Player.new(application: Config.player)
|
|
56
|
+
end
|
|
37
57
|
|
|
38
|
-
|
|
58
|
+
def interpreter
|
|
59
|
+
@interpreter ||= Interpreter.new
|
|
39
60
|
end
|
|
40
61
|
end
|
|
41
62
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module Musica
|
|
7
|
+
module Config
|
|
8
|
+
DEFAULTS = {"player" => "Music"}.freeze
|
|
9
|
+
|
|
10
|
+
def self.player(env: ENV)
|
|
11
|
+
file = path(env)
|
|
12
|
+
|
|
13
|
+
unless File.exist?(file)
|
|
14
|
+
FileUtils.mkdir_p(File.dirname(file))
|
|
15
|
+
File.write(file, YAML.dump(DEFAULTS))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
(YAML.load_file(file) || {}).fetch("player", DEFAULTS["player"])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.path(env)
|
|
22
|
+
base = env["XDG_CONFIG_HOME"]
|
|
23
|
+
base = File.join(env.fetch("HOME"), ".config") if base.nil? || base.empty?
|
|
24
|
+
|
|
25
|
+
File.join(base, "musica", "config.yml")
|
|
26
|
+
end
|
|
27
|
+
private_class_method :path
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Intent",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"required": [
|
|
6
|
+
"action"
|
|
7
|
+
],
|
|
8
|
+
"properties": {
|
|
9
|
+
"action": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"enum": [
|
|
12
|
+
"play",
|
|
13
|
+
"pause",
|
|
14
|
+
"next_track",
|
|
15
|
+
"previous_track",
|
|
16
|
+
"change_volume",
|
|
17
|
+
"play_track",
|
|
18
|
+
"play_album",
|
|
19
|
+
"unknown"
|
|
20
|
+
],
|
|
21
|
+
"description": "What to do"
|
|
22
|
+
},
|
|
23
|
+
"name": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "Track name, for play_track"
|
|
26
|
+
},
|
|
27
|
+
"artist": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Artist name, for play_track or play_album"
|
|
30
|
+
},
|
|
31
|
+
"album": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "Album name, for play_album"
|
|
34
|
+
},
|
|
35
|
+
"delta": {
|
|
36
|
+
"type": "integer",
|
|
37
|
+
"description": "Volume change from -100 to 100, for change_volume"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"x-order": [
|
|
41
|
+
"action",
|
|
42
|
+
"name",
|
|
43
|
+
"artist",
|
|
44
|
+
"album",
|
|
45
|
+
"delta"
|
|
46
|
+
]
|
|
47
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "open3"
|
|
5
|
+
|
|
6
|
+
module Musica
|
|
7
|
+
class Interpreter
|
|
8
|
+
SCHEMA = File.expand_path("intent.json", __dir__)
|
|
9
|
+
|
|
10
|
+
INSTRUCTIONS = <<~TEXT
|
|
11
|
+
You turn a request to a music player into an action.
|
|
12
|
+
- play: resume playback
|
|
13
|
+
- pause: pause or stop playback
|
|
14
|
+
- next_track: skip to the next song ("next", "skip")
|
|
15
|
+
- previous_track: go back to the previous song
|
|
16
|
+
- change_volume: louder or quieter; delta is the change, 10 for a bit, 30 for a lot, negative for quieter
|
|
17
|
+
- play_track: play a specific song; name is the song title, artist only if the request names one
|
|
18
|
+
- play_album: play an album; album is the album title, artist only if the request names one
|
|
19
|
+
- unknown: anything else
|
|
20
|
+
TEXT
|
|
21
|
+
|
|
22
|
+
FM = lambda do |request|
|
|
23
|
+
output, status = Open3.capture2(
|
|
24
|
+
"fm", "respond", "--no-stream", "--greedy",
|
|
25
|
+
"--schema", SCHEMA, "--instructions", INSTRUCTIONS, request
|
|
26
|
+
)
|
|
27
|
+
raise Error, "fm failed to interpret \"#{request}\"" unless status.success?
|
|
28
|
+
|
|
29
|
+
output
|
|
30
|
+
rescue Errno::ENOENT
|
|
31
|
+
raise Error, "Requests need fm (macOS 26 with Apple Intelligence); use -s to send AppleScript instead"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
COMMANDS = {
|
|
35
|
+
"play" => "play",
|
|
36
|
+
"pause" => "pause",
|
|
37
|
+
"next_track" => "next track",
|
|
38
|
+
"previous_track" => "previous track"
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
# Music can't play an album or queue tracks, so refill a playlist with the
|
|
42
|
+
# album in disc and track order and play that instead
|
|
43
|
+
ALBUM = <<~APPLESCRIPT
|
|
44
|
+
set found to every track of library playlist 1 whose %<conditions>s
|
|
45
|
+
if found is {} then error "No album " & %<album>s & " in your library"
|
|
46
|
+
set positions to {}
|
|
47
|
+
repeat with t in found
|
|
48
|
+
set end of positions to (disc number of t) * 1000 + (track number of t)
|
|
49
|
+
end repeat
|
|
50
|
+
if not (exists user playlist "Musica") then make new user playlist with properties {name:"Musica"}
|
|
51
|
+
set musica to user playlist "Musica"
|
|
52
|
+
delete every track of musica
|
|
53
|
+
repeat (count of found) times
|
|
54
|
+
set best to 0
|
|
55
|
+
repeat with i from 1 to count of found
|
|
56
|
+
if item i of positions is not missing value then
|
|
57
|
+
if best is 0 or item i of positions < item best of positions then set best to i
|
|
58
|
+
end if
|
|
59
|
+
end repeat
|
|
60
|
+
duplicate item best of found to musica
|
|
61
|
+
set item best of positions to missing value
|
|
62
|
+
end repeat
|
|
63
|
+
play musica
|
|
64
|
+
APPLESCRIPT
|
|
65
|
+
|
|
66
|
+
def initialize(runner: FM)
|
|
67
|
+
@runner = runner
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def call(request)
|
|
71
|
+
intent = JSON.parse(runner.call(request))
|
|
72
|
+
name = mentioned(intent["name"], request)
|
|
73
|
+
artist = mentioned(intent["artist"], request)
|
|
74
|
+
album = mentioned(intent["album"], request)
|
|
75
|
+
|
|
76
|
+
case intent["action"]
|
|
77
|
+
when "play", "play_track", "play_album"
|
|
78
|
+
if album && !name
|
|
79
|
+
play_album(album, artist)
|
|
80
|
+
elsif name || artist
|
|
81
|
+
play_track(name, artist, album)
|
|
82
|
+
else
|
|
83
|
+
"play"
|
|
84
|
+
end
|
|
85
|
+
when "change_volume"
|
|
86
|
+
volume(intent["delta"], request)
|
|
87
|
+
else
|
|
88
|
+
COMMANDS.fetch(intent["action"]) { not_understood(request) }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
attr_reader :runner
|
|
95
|
+
|
|
96
|
+
def mentioned(value, request)
|
|
97
|
+
value if value.is_a?(String) && !value.empty? && request.downcase.include?(value.downcase)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def play_track(name, artist, album)
|
|
101
|
+
"play (some track of library playlist 1 whose #{conditions("name" => name, "artist" => artist, "album" => album)})"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def play_album(album, artist)
|
|
105
|
+
format(ALBUM, conditions: conditions("album" => album, "artist" => artist), album: quote(album))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def conditions(filters)
|
|
109
|
+
filters.compact.map { |key, value| "#{key} is #{quote(value)}" }.join(" and ")
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def volume(delta, request)
|
|
113
|
+
not_understood(request) unless delta.is_a?(Integer)
|
|
114
|
+
|
|
115
|
+
"set sound volume to (sound volume + #{delta})"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def quote(value)
|
|
119
|
+
%("#{value.gsub(/[\\"]/) { |char| "\\#{char}" }}")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def not_understood(request)
|
|
123
|
+
raise Error, "Couldn't understand \"#{request}\""
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
data/lib/musica/player.rb
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "open3"
|
|
4
|
+
|
|
3
5
|
module Musica
|
|
4
6
|
class Player
|
|
5
|
-
|
|
7
|
+
OSASCRIPT = ->(script) { Open3.capture2("osascript", "-e", script).first.chomp }
|
|
8
|
+
|
|
9
|
+
def initialize(application:, runner: OSASCRIPT)
|
|
6
10
|
@application = application
|
|
11
|
+
@runner = runner
|
|
7
12
|
end
|
|
8
13
|
|
|
9
14
|
def status
|
|
@@ -11,12 +16,12 @@ module Musica
|
|
|
11
16
|
end
|
|
12
17
|
|
|
13
18
|
def tell(command)
|
|
14
|
-
|
|
19
|
+
runner.call(%(tell application "#{application}"\n#{command}\nend tell))
|
|
15
20
|
end
|
|
16
21
|
|
|
17
22
|
private
|
|
18
23
|
|
|
19
|
-
attr_reader :application
|
|
24
|
+
attr_reader :application, :runner
|
|
20
25
|
|
|
21
26
|
def artist
|
|
22
27
|
tell "return artist of the current track"
|
data/lib/musica/version.rb
CHANGED
data/lib/musica.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: musica
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ariel Rzezak
|
|
@@ -28,6 +28,9 @@ files:
|
|
|
28
28
|
- exe/musica
|
|
29
29
|
- lib/musica.rb
|
|
30
30
|
- lib/musica/cli.rb
|
|
31
|
+
- lib/musica/config.rb
|
|
32
|
+
- lib/musica/intent.json
|
|
33
|
+
- lib/musica/interpreter.rb
|
|
31
34
|
- lib/musica/player.rb
|
|
32
35
|
- lib/musica/version.rb
|
|
33
36
|
- sig/musica.rbs
|
|
@@ -52,7 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
52
55
|
- !ruby/object:Gem::Version
|
|
53
56
|
version: '0'
|
|
54
57
|
requirements: []
|
|
55
|
-
rubygems_version:
|
|
58
|
+
rubygems_version: 4.0.23
|
|
56
59
|
specification_version: 4
|
|
57
60
|
summary: Control macOS music players from the command-line
|
|
58
61
|
test_files: []
|