mpv-ipc 5.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2e7d0c0d23fc7f57e54fa0c0425686e20c6fd08c0002ca1c3569ab9d7aabab68
4
+ data.tar.gz: d63982804801f0831ab131ce860e601c2ae7908f30d79c2f3781d256da50c9b9
5
+ SHA512:
6
+ metadata.gz: c00ca5d89ffc2343b378ccee7a3013847d931b17b8cbbf0743c517b3825b20c2c4df13660d6eb4db99d9e63ef0005287f963df62d85eb577e9241d51c341359f
7
+ data.tar.gz: 28388a0ae21f8d550d1d42d5974db247bce0f28ce6efdeed94f09cd9f8e1813ac175adf0b8d438aef8af0ee8ddb8cfb3425886cdd72165fb586d795640c16f03
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 William Woodruff <william @ tuffbizz.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,140 @@
1
+ mpv-ipc
2
+ =======
3
+
4
+ Start or connect to the mpv media player and control it via IPC.
5
+
6
+ A Ruby library that provides a simple interface for interacting with
7
+ [mpv](https://mpv.io) via its JSON IPC protocol.
8
+
9
+ This gem supports multiple usage patterns:
10
+ - starting and managing an mpv instance (IPC server + connected client),
11
+ - connecting to an already running mpv process as a plain IPC client,
12
+ - writing Ruby-based mpv scripts that are automatically launched at startup.
13
+
14
+ ### Installation
15
+
16
+ ```bash
17
+ $ gem install mpv-ipc
18
+ ```
19
+
20
+ This gem requires [mpv](https://mpv.io) to be installed separately.
21
+
22
+ ### Usage
23
+
24
+ For full documentation, please see the [RubyDocs](https://www.rubydoc.info/gems/mpv-ipc).
25
+
26
+ Check out mpv's property [documentation](https://mpv.io/manual/stable/#properties)
27
+ for more details on supported properties and their usage.
28
+
29
+ #### Standalone session
30
+
31
+ This gem can be used to create a standalone mpv session, which starts
32
+ and manages its own mpv instance and communicates with it via JSON IPC.
33
+
34
+ ```ruby
35
+ require "mpv_ipc"
36
+
37
+ # Initialize a new mpv session with both a Server and a Client
38
+ mpv = MPV::Session.new
39
+
40
+ # Observe changes in properties
41
+ mpv.observe_property("volume") do |prop_event|
42
+ puts "Volume changed to: #{prop_event.data}"
43
+ end
44
+
45
+ # Interact with mpv
46
+ mpv.get_property "pause"
47
+ mpv.set_property "volume", 50.0
48
+ mpv.command "loadfile", "video.mkv"
49
+
50
+ sleep 10
51
+
52
+ # Exit mpv gracefully
53
+ mpv.quit!
54
+ ```
55
+
56
+ Internally, `MPV::Session` instantiates and wires together an `MPV::Server`
57
+ and an `MPV::Client`.
58
+
59
+ #### IPC client
60
+
61
+ Alternatively, the library can be used as a plain JSON IPC client to connect
62
+ to an already running mpv process. This makes it suitable for both:
63
+ - external control tools that attach to an existing mpv instance, and
64
+ - scripts started and managed by mpv, communicating with it over IPC.
65
+
66
+ To use the gem as an mpv script, place the Ruby file in mpv's script configuration
67
+ directory (e.g. `$HOME/.config/mpv/scripts`) with a `.run` suffix.
68
+ It will automatically start the script at launch and provide the script
69
+ with an IPC connection via `--mpv-ipc-fd`.
70
+
71
+ ```ruby
72
+ #!/usr/bin/ruby
73
+
74
+ require "mpv_ipc/client"
75
+
76
+ # Connect to an existing mpv process via its IPC socket
77
+ mpv = MPV::Client.new("/var/run/mpv.socket")
78
+
79
+ # Or initialize a Client using the IPC connection provided by mpv
80
+ # when running as an mpv-managed script (via --mpv-ipc-fd)
81
+ # mpv = MPV::Client.script
82
+
83
+ # Add a listener for the "seek" event
84
+ mpv.register_event_listener("seek") do |event|
85
+ pos = mpv.get_property!("percent-pos")
86
+ mpv.command("show-text", "Pos: %.2f%%" % pos)
87
+ end
88
+
89
+ # Register keybindings for zooming
90
+ mpv.register_keybindings("+", "-") do |key_event|
91
+ if key_event.hold?
92
+ zoom = mpv.get_property!("video-zoom")
93
+ mpv.set_property!("video-zoom", zoom.send(key_event.key, 0.1).clamp(-4, 4))
94
+ end
95
+ end
96
+
97
+ # Observe changes in the "video-zoom" property
98
+ mpv.observe_property("video-zoom") do |prop_event|
99
+ mpv.command("show-text", "Zoom: %.0f%%" % (2 ** prop_event.data * 100))
100
+ end
101
+
102
+ # Wait until the connection closes
103
+ mpv.wait
104
+ ```
105
+
106
+ All event-related callbacks are executed sequentially on the same dedicated
107
+ internal thread to preserve event order. Handlers that may take longer
108
+ can delegate their work to another thread or a new one.
109
+
110
+ #### OSD messages
111
+
112
+ For more advanced overlays, you can also create and edit styled OSD messages
113
+ using the bundled Ass text helpers, with custom fonts, colors, text styles,
114
+ alignment, and other formatting.
115
+
116
+ ```ruby
117
+ title = Ass::Span.new("Now playing")
118
+ title.bold.size(28).color(:gold).outline(2, :black)
119
+
120
+ body = Ass::Span.new("Interstellar")
121
+ body.italic.size(24).color(:white).outline(2, :black)
122
+
123
+ text = Ass::Text.new(title, "\n", body).align(:top_right)
124
+ id = mpv.create_osd_message(text)
125
+
126
+ body.content = "Inception"
127
+ mpv.edit_osd_message(id, text)
128
+ ```
129
+
130
+ ### Compatibility
131
+
132
+ This library currently targets Unix-like systems and has been tested on Linux.
133
+ Some features depend on Unix-specific IPC and process behavior, so Windows
134
+ compatibility has not been implemented or tested yet.
135
+
136
+ This gem is based on the original [ruby-mpv](https://github.com/woodruffw/ruby-mpv)
137
+ project and the extended [fork](https://github.com/pigoz/ruby-mpv).
138
+
139
+ Although this implementation differs from the original,
140
+ it remains partially compatible with its API.
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ass
4
+ # Represents an ASS color.
5
+ class Color
6
+ PRESETS = {
7
+ # grayscale
8
+ black: [ 0, 0, 0],
9
+ carbon: [ 64, 64, 64],
10
+ gray: [128, 128, 128],
11
+ silver: [192, 192, 192],
12
+ white: [255, 255, 255],
13
+
14
+ # primary
15
+ red: [255, 0, 0],
16
+ green: [ 0, 255, 0],
17
+ blue: [ 0, 0, 255],
18
+
19
+ # secondary
20
+ yellow: [255, 255, 0],
21
+ magenta: [255, 0, 255],
22
+ cyan: [ 0, 255, 255],
23
+
24
+ # vivid
25
+ orange: [255, 128, 0],
26
+ gold: [255, 192, 0],
27
+ purple: [128, 0, 255],
28
+ turquoise: [ 0, 255, 128],
29
+
30
+ # pastel
31
+ pink: [255, 128, 255],
32
+ sky: [128, 255, 255],
33
+ mint: [128, 255, 128],
34
+ vanilla: [255, 255, 128],
35
+ coral: [255, 128, 128],
36
+
37
+ # shade
38
+ maroon: [128, 0, 0],
39
+ forest: [ 0, 128, 0],
40
+ navy: [ 0, 0, 128],
41
+ teal: [ 0, 128, 128],
42
+ plum: [128, 0, 128],
43
+ olive: [128, 128, 0],
44
+ brown: [128, 64, 0],
45
+ }.freeze
46
+
47
+ # Gets the RGBA component values.
48
+ # @return [Array<Integer>] red, green, blue and alpha codes
49
+ attr_reader :codes
50
+ alias_method :to_a, :codes
51
+
52
+ # Creates an ASS::Color.
53
+ # @param color [Array<Integer>, Symbol] preset symbol or custom color codes
54
+ # Color codes are given as [red, green, blue, alpha] in the range 0..255.
55
+ # Alpha defaults to 0, meaning fully opaque, while 255 means fully transparent.
56
+ def initialize(color)
57
+ set(color)
58
+ end
59
+
60
+ # Changes the color value.
61
+ # @param color [Array<Integer>, Symbol] preset symbol or custom color codes
62
+ # Color codes are given as [red, green, blue, alpha] in the range 0..255.
63
+ # Alpha defaults to 0, meaning fully opaque, while 255 means fully transparent.
64
+ # @return [self]
65
+ def set(color)
66
+ color = PRESETS.fetch(color) if color.is_a?(Symbol)
67
+ @codes = color.to_a.values_at(0..3).map{ |code| code.to_i.clamp(0, 255) }
68
+ self
69
+ end
70
+
71
+ # Changes the red value.
72
+ # @param code [Integer] red channel value in the range 0..255
73
+ # @return [self]
74
+ def red(code)
75
+ @codes[0] = code.to_i.clamp(0, 255)
76
+ self
77
+ end
78
+
79
+ # Changes the green value.
80
+ # @param code [Integer] green channel value in the range 0..255
81
+ # @return [self]
82
+ def green(code)
83
+ @codes[1] = code.to_i.clamp(0, 255)
84
+ self
85
+ end
86
+
87
+ # Changes the blue value.
88
+ # @param code [Integer] blue channel value in the range 0..255
89
+ # @return [self]
90
+ def blue(code)
91
+ @codes[2] = code.to_i.clamp(0, 255)
92
+ self
93
+ end
94
+
95
+ # Changes the alpha value.
96
+ # @param code [Integer] alpha channel value in the range 0..255
97
+ # 0 means fully opaque, while 255 means fully transparent
98
+ # @return [self]
99
+ def alpha(code)
100
+ @codes[3] = code.to_i.clamp(0, 255)
101
+ self
102
+ end
103
+
104
+ # Converts the color to ASS syntax.
105
+ # @return [String] ASS color representation
106
+ def to_script
107
+ codes = @codes.last.zero? ? @codes[0..-2] : @codes
108
+ "&H#{codes.reverse.map{ |code| "%02X" % code }.join}&"
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "color"
4
+
5
+ module Ass
6
+ # Represents a styled ASS text fragment.
7
+ class Span
8
+ # Gets or changes the plain text fragment content.
9
+ # @return [String] plain text content
10
+ attr_accessor :content
11
+ alias_method :to_s, :content
12
+
13
+ # Creates an ASS::Span text fragment.
14
+ # @param content [String] plain text content
15
+ def initialize(content=nil)
16
+ @content = content
17
+ @tags = {}
18
+ end
19
+
20
+ # Sets the font name.
21
+ # @param name [String] font family name
22
+ # @return [self]
23
+ def font(name)
24
+ @tags["fn"] = name.to_s
25
+ self
26
+ end
27
+
28
+ # Sets bold text rendering.
29
+ # @param value [Boolean] whether bold styling is enabled
30
+ # @return [self]
31
+ def bold(value=true)
32
+ @tags["b"] = value ? 1 : 0
33
+ self
34
+ end
35
+
36
+ # Sets italic text rendering.
37
+ # @param value [Boolean] whether italic styling is enabled
38
+ # @return [self]
39
+ def italic(value=true)
40
+ @tags["i"] = value ? 1 : 0
41
+ self
42
+ end
43
+
44
+ # Sets underline text rendering.
45
+ # @param value [Boolean] whether underline styling is enabled
46
+ # @return [self]
47
+ def underline(value=true)
48
+ @tags["u"] = value ? 1 : 0
49
+ self
50
+ end
51
+
52
+ # Sets strikethrough text rendering.
53
+ # @param value [Boolean] whether strikethrough styling is enabled
54
+ # @return [self]
55
+ def strike(value=true)
56
+ @tags["s"] = value ? 1 : 0
57
+ self
58
+ end
59
+
60
+ # Sets the font size.
61
+ # @param value [Integer] font size
62
+ # @return [self]
63
+ def size(value)
64
+ @tags["fs"] = value.to_i
65
+ self
66
+ end
67
+
68
+ # Sets the font color.
69
+ # @param value [Ass::Color, Array<Integer>, Symbol] font color
70
+ # @return [self]
71
+ def color(value)
72
+ @tags["1c"] = value.is_a?(Color) ? value : Color.new(value)
73
+ self
74
+ end
75
+
76
+ # Sets the outline thickness and color.
77
+ # @param size [Integer] outline thickness value
78
+ # @param color [Ass::Color, Array<Integer>, Symbol] outline color value
79
+ # @return [self]
80
+ def outline(size, color)
81
+ @tags["3c"] = color.is_a?(Color) ? color : Color.new(color)
82
+ @tags["bord"] = size.to_i
83
+ self
84
+ end
85
+
86
+ # Sets the shadow size.
87
+ # @param size [Integer] shadow size value
88
+ # @return [self]
89
+ def shadow(size)
90
+ @tags["shad"] = size.to_i
91
+ self
92
+ end
93
+
94
+ # Sets the blur strength.
95
+ # @param strength [Numeric] blur strength value
96
+ # @return [self]
97
+ def blur(strength)
98
+ @tags["blur"] = strength.to_f
99
+ self
100
+ end
101
+
102
+ # Removes all style tags.
103
+ # @return [self]
104
+ def bare
105
+ @tags.clear
106
+ self
107
+ end
108
+
109
+ # Converts the text fragment to ASS syntax.
110
+ # @return [String] ASS text fragment representation
111
+ def to_script
112
+ tags = @tags.map do |(key, value)|
113
+ value = value.to_script if value.is_a?(Color)
114
+ "\\#{key}#{value}"
115
+ end
116
+ head, tail = "{#{tags.join}}", "{\\r}" unless tags.empty?
117
+ content = @content.to_s.gsub("\\", "\\\\").gsub("{", "\\{").gsub("}", "\\}").gsub("\n", "\\N")
118
+ "#{head}#{content}#{tail}"
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "span"
4
+
5
+ module Ass
6
+ # Represents an ASS text containing styled fragments with layout tags.
7
+ class Text
8
+ ALIGN = {
9
+ top_left: 7, top: 8, top_right: 9,
10
+ left: 4, center: 5, right: 6,
11
+ bottom_left: 1, bottom: 2, bottom_right: 3,
12
+ }.freeze
13
+
14
+ # Creates an ASS::Text.
15
+ # @param spans [Array<Ass::Span, String>] initial text fragments
16
+ def initialize(*spans)
17
+ @tags = {}
18
+ @spans = []
19
+ add(*spans)
20
+ end
21
+
22
+ # Adds new fragments to the current text.
23
+ # @param spans [Array<Ass::Span, String>] text fragments to append
24
+ # @return [self]
25
+ def add(*spans)
26
+ spans.each{ |span| @spans << (span.is_a?(Span) ? span : Span.new(span)) }
27
+ self
28
+ end
29
+
30
+ # Replaces the current text fragments.
31
+ # @param spans [Array<Ass::Span, String>] new text fragments
32
+ # @return [self]
33
+ def set(*spans)
34
+ @spans.clear
35
+ add(*spans)
36
+ end
37
+
38
+ # Gets the current text fragments.
39
+ # @return [Array<Ass::Span>] text fragments
40
+ def get()
41
+ @spans.dup.freeze
42
+ end
43
+
44
+ # Sets the absolute position of the text.
45
+ # @param x [Numeric] horizontal coordinate
46
+ # @param y [Numeric] vertical coordinate
47
+ # @return [self]
48
+ def position(x, y)
49
+ @tags["pos"] = x.round(2), y.round(2)
50
+ self
51
+ end
52
+
53
+ # Sets the alignment anchor of the text.
54
+ # @param value [Integer, Symbol] 1..9 number or symbolic alignment name
55
+ # @return [self]
56
+ def align(value)
57
+ @tags["an"] = value.is_a?(Symbol) ? ALIGN.fetch(value) : value.to_i
58
+ self
59
+ end
60
+
61
+ # Removes all layout tags.
62
+ # @return [self]
63
+ def bare
64
+ @tags.clear
65
+ self
66
+ end
67
+
68
+ # Converts the text to ASS syntax.
69
+ # @return [String] ASS text representation
70
+ def to_script
71
+ tags = @tags.map do |(key, value)|
72
+ value = "(#{value.join(",")})" if value.is_a?(Array)
73
+ "\\#{key}#{value}"
74
+ end
75
+ head = "{#{tags.join}}" unless tags.empty?
76
+ "#{head}#{@spans.map(&:to_script).join}"
77
+ end
78
+
79
+ # Returns the plain text content of each fragment.
80
+ # @return [Array<String>] plain text fragments
81
+ def to_a
82
+ @spans.map(&:to_s)
83
+ end
84
+
85
+ # Returns the full plain text content.
86
+ # @return [String] concatenated plain text
87
+ def to_s
88
+ to_a.join
89
+ end
90
+ end
91
+ end
data/lib/mpv_ass.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "mpv_ass/text"