mirror of
https://github.com/onyx-and-iris/obsws-ruby.git
synced 2025-04-04 12:23:45 +01:00
Compare commits
13 Commits
72ee539b96
...
155cbe019a
Author | SHA1 | Date | |
---|---|---|---|
155cbe019a | |||
6293ae7b8c | |||
57fca646b5 | |||
d12a1a5954 | |||
438f3b1659 | |||
d15418a660 | |||
2883fd42cc | |||
88b2eabc0c | |||
e15e17cc9f | |||
72e09d5278 | |||
11d991b039 | |||
3d3d8f3020 | |||
82c6ced760 |
@ -1,7 +1,7 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
obsws (0.2.1)
|
obsws (0.3.3)
|
||||||
waitutil (~> 0.2.1)
|
waitutil (~> 0.2.1)
|
||||||
websocket-driver (~> 0.7.5)
|
websocket-driver (~> 0.7.5)
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ GEM
|
|||||||
rubocop-performance (~> 1.18.0)
|
rubocop-performance (~> 1.18.0)
|
||||||
unicode-display_width (2.4.2)
|
unicode-display_width (2.4.2)
|
||||||
waitutil (0.2.1)
|
waitutil (0.2.1)
|
||||||
websocket-driver (0.7.5)
|
websocket-driver (0.7.6)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.5)
|
websocket-extensions (0.1.5)
|
||||||
|
|
||||||
|
13
README.md
13
README.md
@ -2,7 +2,7 @@
|
|||||||
[](https://github.com/onyx-and-iris/obsws-ruby/blob/dev/LICENSE)
|
[](https://github.com/onyx-and-iris/obsws-ruby/blob/dev/LICENSE)
|
||||||
[](https://github.com/standardrb/standard)
|
[](https://github.com/standardrb/standard)
|
||||||
|
|
||||||
# A Ruby wrapper around OBS Studio WebSocket v5.0
|
# Ruby Clients for OBS Studio WebSocket v5.0
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@ -114,15 +114,12 @@ For a full list of status codes refer to [Codes](https://github.com/obsproject/o
|
|||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
|
|
||||||
To see the raw messages set log level to debug
|
To enable logs set an environmental variable `OBSWS_LOG_LEVEL` to the appropriate level.
|
||||||
|
|
||||||
example:
|
example in powershell:
|
||||||
|
|
||||||
```ruby
|
```powershell
|
||||||
require "obsws"
|
$env:OBSWS_LOG_LEVEL="DEBUG"
|
||||||
|
|
||||||
OBSWS::LOGGER.debug!
|
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tests
|
### Tests
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
require_relative "../../lib/obsws"
|
require_relative "../../lib/obsws"
|
||||||
require "yaml"
|
require "yaml"
|
||||||
|
|
||||||
OBSWS::LOGGER.info!
|
|
||||||
|
|
||||||
class Main
|
class Main
|
||||||
attr_reader :running
|
attr_reader :running
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
require_relative "../../lib/obsws"
|
require_relative "../../lib/obsws"
|
||||||
require "yaml"
|
require "yaml"
|
||||||
|
|
||||||
OBSWS::LOGGER.info!
|
|
||||||
|
|
||||||
module LevelTypes
|
module LevelTypes
|
||||||
VU = 0
|
VU = 0
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
require_relative "../../lib/obsws"
|
require_relative "../../lib/obsws"
|
||||||
require "yaml"
|
require "yaml"
|
||||||
|
|
||||||
OBSWS::LOGGER.info!
|
|
||||||
|
|
||||||
class Main
|
class Main
|
||||||
def conn_from_yaml
|
def conn_from_yaml
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
require "logger"
|
|
||||||
|
|
||||||
require_relative "obsws/req"
|
require_relative "obsws/req"
|
||||||
require_relative "obsws/event"
|
require_relative "obsws/event"
|
||||||
|
|
||||||
module OBSWS
|
module OBSWS
|
||||||
include Logger::Severity
|
|
||||||
|
|
||||||
LOGGER = Logger.new(STDOUT)
|
|
||||||
LOGGER.level = WARN
|
|
||||||
end
|
end
|
||||||
|
@ -1,27 +1,16 @@
|
|||||||
require "socket"
|
|
||||||
require "websocket/driver"
|
|
||||||
require "digest/sha2"
|
require "digest/sha2"
|
||||||
require "json"
|
require "json"
|
||||||
require "waitutil"
|
require "waitutil"
|
||||||
|
|
||||||
require_relative "mixin"
|
require_relative "driver"
|
||||||
require_relative "error"
|
require_relative "error"
|
||||||
|
require_relative "logger"
|
||||||
|
require_relative "mixin"
|
||||||
|
|
||||||
module OBSWS
|
module OBSWS
|
||||||
class Socket
|
|
||||||
attr_reader :url
|
|
||||||
|
|
||||||
def initialize(url, socket)
|
|
||||||
@url = url
|
|
||||||
@socket = socket
|
|
||||||
end
|
|
||||||
|
|
||||||
def write(s)
|
|
||||||
@socket.write(s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Base
|
class Base
|
||||||
|
include Logging
|
||||||
|
include Driver::Director
|
||||||
include Mixin::OPCodes
|
include Mixin::OPCodes
|
||||||
|
|
||||||
attr_reader :closed
|
attr_reader :closed
|
||||||
@ -32,21 +21,7 @@ module OBSWS
|
|||||||
port = kwargs[:port] || 4455
|
port = kwargs[:port] || 4455
|
||||||
@password = kwargs[:password] || ""
|
@password = kwargs[:password] || ""
|
||||||
@subs = kwargs[:subs] || 0
|
@subs = kwargs[:subs] || 0
|
||||||
|
setup_driver(host, port)
|
||||||
@socket = TCPSocket.new(host, port)
|
|
||||||
@driver =
|
|
||||||
WebSocket::Driver.client(Socket.new("ws://#{host}:#{port}", @socket))
|
|
||||||
@driver.on :open do |msg|
|
|
||||||
LOGGER.debug("driver socket open")
|
|
||||||
end
|
|
||||||
@driver.on :close do |msg|
|
|
||||||
LOGGER.debug("driver socket closed")
|
|
||||||
@closed = true
|
|
||||||
end
|
|
||||||
@driver.on :message do |msg|
|
|
||||||
LOGGER.debug("received: #{msg.data}")
|
|
||||||
msg_handler(JSON.parse(msg.data, symbolize_names: true))
|
|
||||||
end
|
|
||||||
start_driver
|
start_driver
|
||||||
WaitUtil.wait_for_condition(
|
WaitUtil.wait_for_condition(
|
||||||
"successful identification",
|
"successful identification",
|
||||||
@ -55,22 +30,6 @@ module OBSWS
|
|||||||
) { @identified }
|
) { @identified }
|
||||||
end
|
end
|
||||||
|
|
||||||
private def start_driver
|
|
||||||
Thread.new do
|
|
||||||
@driver.start
|
|
||||||
|
|
||||||
loop do
|
|
||||||
@driver.parse(@socket.readpartial(4096))
|
|
||||||
rescue EOFError
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
public def stop_driver
|
|
||||||
@driver.close
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def auth_token(salt:, challenge:)
|
def auth_token(salt:, challenge:)
|
||||||
@ -91,7 +50,7 @@ module OBSWS
|
|||||||
if @password.empty?
|
if @password.empty?
|
||||||
raise OBSWSError("auth enabled but no password provided")
|
raise OBSWSError("auth enabled but no password provided")
|
||||||
end
|
end
|
||||||
LOGGER.info("initiating authentication")
|
logger.info("initiating authentication")
|
||||||
payload[:d][:authentication] = auth_token(**auth)
|
payload[:d][:authentication] = auth_token(**auth)
|
||||||
end
|
end
|
||||||
@driver.text(JSON.generate(payload))
|
@driver.text(JSON.generate(payload))
|
||||||
@ -117,7 +76,7 @@ module OBSWS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
payload[:d][:requestData] = data if data
|
payload[:d][:requestData] = data if data
|
||||||
LOGGER.debug("sending request: #{payload}")
|
logger.debug("sending request: #{payload}")
|
||||||
@driver.text(JSON.generate(payload))
|
@driver.text(JSON.generate(payload))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
53
lib/obsws/driver.rb
Normal file
53
lib/obsws/driver.rb
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
require "socket"
|
||||||
|
require "websocket/driver"
|
||||||
|
|
||||||
|
module OBSWS
|
||||||
|
module Driver
|
||||||
|
class Socket
|
||||||
|
attr_reader :url
|
||||||
|
|
||||||
|
def initialize(url, socket)
|
||||||
|
@url = url
|
||||||
|
@socket = socket
|
||||||
|
end
|
||||||
|
|
||||||
|
def write(s)
|
||||||
|
@socket.write(s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Director
|
||||||
|
def setup_driver(host, port)
|
||||||
|
@socket = TCPSocket.new(host, port)
|
||||||
|
@driver =
|
||||||
|
WebSocket::Driver.client(Socket.new("ws://#{host}:#{port}", @socket))
|
||||||
|
@driver.on :open do |msg|
|
||||||
|
logger.debug("driver socket open")
|
||||||
|
end
|
||||||
|
@driver.on :close do |msg|
|
||||||
|
logger.debug("driver socket closed")
|
||||||
|
@closed = true
|
||||||
|
end
|
||||||
|
@driver.on :message do |msg|
|
||||||
|
msg_handler(JSON.parse(msg.data, symbolize_names: true))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def start_driver
|
||||||
|
Thread.new do
|
||||||
|
@driver.start
|
||||||
|
|
||||||
|
loop do
|
||||||
|
@driver.parse(@socket.readpartial(4096))
|
||||||
|
rescue EOFError
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
public def stop_driver
|
||||||
|
@driver.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -2,6 +2,7 @@ require "json"
|
|||||||
|
|
||||||
require_relative "util"
|
require_relative "util"
|
||||||
require_relative "mixin"
|
require_relative "mixin"
|
||||||
|
require_relative "logger"
|
||||||
|
|
||||||
module OBSWS
|
module OBSWS
|
||||||
module Events
|
module Events
|
||||||
@ -34,27 +35,28 @@ module OBSWS
|
|||||||
end
|
end
|
||||||
|
|
||||||
module Callbacks
|
module Callbacks
|
||||||
include Util
|
include Util::String
|
||||||
|
|
||||||
|
def observers
|
||||||
|
@observers ||= []
|
||||||
|
end
|
||||||
|
|
||||||
def add_observer(observer)
|
def add_observer(observer)
|
||||||
@observers = [] unless defined?(@observers)
|
|
||||||
observer = [observer] if !observer.respond_to? :each
|
observer = [observer] if !observer.respond_to? :each
|
||||||
observer.each { |o| @observers.append(o) }
|
observer.each { |o| observers << o }
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_observer(observer)
|
def remove_observer(observer)
|
||||||
@observers.delete(observer)
|
observers.delete(observer)
|
||||||
end
|
end
|
||||||
|
|
||||||
def notify_observers(event, data)
|
def notify_observers(event, data)
|
||||||
if defined?(@observers)
|
observers.each do |o|
|
||||||
@observers.each do |o|
|
if o.respond_to? "on_#{snakecase(event)}"
|
||||||
if o.respond_to? "on_#{event.to_snake}"
|
if data.empty?
|
||||||
if data.empty?
|
o.send("on_#{snakecase(event)}")
|
||||||
o.send("on_#{event.to_snake}")
|
else
|
||||||
else
|
o.send("on_#{snakecase(event)}", data)
|
||||||
o.send("on_#{event.to_snake}", data)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -62,6 +64,7 @@ module OBSWS
|
|||||||
end
|
end
|
||||||
|
|
||||||
class Client
|
class Client
|
||||||
|
include Logging
|
||||||
include Callbacks
|
include Callbacks
|
||||||
include Mixin::TearDown
|
include Mixin::TearDown
|
||||||
include Mixin::OPCodes
|
include Mixin::OPCodes
|
||||||
@ -69,9 +72,10 @@ module OBSWS
|
|||||||
def initialize(**kwargs)
|
def initialize(**kwargs)
|
||||||
kwargs[:subs] ||= SUBS::LOW_VOLUME
|
kwargs[:subs] ||= SUBS::LOW_VOLUME
|
||||||
@base_client = Base.new(**kwargs)
|
@base_client = Base.new(**kwargs)
|
||||||
LOGGER.info("#{self} succesfully identified with server")
|
logger.info("#{self} successfully identified with server")
|
||||||
@base_client.updater = ->(op_code, data) {
|
@base_client.updater = ->(op_code, data) {
|
||||||
if op_code == Mixin::OPCodes::EVENT
|
if op_code == Mixin::OPCodes::EVENT
|
||||||
|
logger.debug("received: #{data}")
|
||||||
event = data[:eventType]
|
event = data[:eventType]
|
||||||
data = data.fetch(:eventData, {})
|
data = data.fetch(:eventData, {})
|
||||||
notify_observers(event, Mixin::Data.new(data, data.keys))
|
notify_observers(event, Mixin::Data.new(data, data.keys))
|
||||||
|
11
lib/obsws/logger.rb
Normal file
11
lib/obsws/logger.rb
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
require "logger"
|
||||||
|
|
||||||
|
module OBSWS
|
||||||
|
module Logging
|
||||||
|
def logger
|
||||||
|
@logger = Logger.new($stdout, level: ENV.fetch("OBSWS_LOG_LEVEL", "WARN"))
|
||||||
|
@logger.progname = instance_of?(::Module) ? name : self.class.name
|
||||||
|
@logger
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -3,11 +3,11 @@ require_relative "util"
|
|||||||
module OBSWS
|
module OBSWS
|
||||||
module Mixin
|
module Mixin
|
||||||
module Meta
|
module Meta
|
||||||
include Util
|
include Util::String
|
||||||
|
|
||||||
def make_field_methods(*params)
|
def make_field_methods(*params)
|
||||||
params.each do |param|
|
params.each do |param|
|
||||||
define_singleton_method(param.to_s.to_snake) { @resp[param] }
|
define_singleton_method(snakecase(param.to_s)) { @resp[param] }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -23,7 +23,7 @@ module OBSWS
|
|||||||
|
|
||||||
def empty? = @fields.empty?
|
def empty? = @fields.empty?
|
||||||
|
|
||||||
def attrs = @fields.map { |f| f.to_s.to_snake }
|
def attrs = @fields.map { |f| snakecase(f.to_s) }
|
||||||
end
|
end
|
||||||
|
|
||||||
class Response < MetaObject
|
class Response < MetaObject
|
||||||
|
@ -4,18 +4,21 @@ require_relative "base"
|
|||||||
require_relative "error"
|
require_relative "error"
|
||||||
require_relative "util"
|
require_relative "util"
|
||||||
require_relative "mixin"
|
require_relative "mixin"
|
||||||
|
require_relative "logger"
|
||||||
|
|
||||||
module OBSWS
|
module OBSWS
|
||||||
module Requests
|
module Requests
|
||||||
class Client
|
class Client
|
||||||
|
include Logging
|
||||||
include Error
|
include Error
|
||||||
include Mixin::TearDown
|
include Mixin::TearDown
|
||||||
include Mixin::OPCodes
|
include Mixin::OPCodes
|
||||||
|
|
||||||
def initialize(**kwargs)
|
def initialize(**kwargs)
|
||||||
@base_client = Base.new(**kwargs)
|
@base_client = Base.new(**kwargs)
|
||||||
LOGGER.info("#{self} succesfully identified with server")
|
logger.info("#{self} successfully identified with server")
|
||||||
@base_client.updater = ->(op_code, data) {
|
@base_client.updater = ->(op_code, data) {
|
||||||
|
logger.debug("response received: #{data}")
|
||||||
@response = data if op_code == Mixin::OPCodes::REQUESTRESPONSE
|
@response = data if op_code == Mixin::OPCodes::REQUESTRESPONSE
|
||||||
}
|
}
|
||||||
@response = {requestId: 0}
|
@response = {requestId: 0}
|
||||||
@ -56,7 +59,7 @@ module OBSWS
|
|||||||
@response[:responseData]
|
@response[:responseData]
|
||||||
rescue WaitUtil::TimeoutError
|
rescue WaitUtil::TimeoutError
|
||||||
msg = "no response with matching id received"
|
msg = "no response with matching id received"
|
||||||
LOGGER.error(msg)
|
logger.error(msg)
|
||||||
raise OBSWSError.new(msg)
|
raise OBSWSError.new(msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
module OBSWS
|
module OBSWS
|
||||||
module Util
|
module Util
|
||||||
class ::String
|
module String
|
||||||
def to_camel
|
def camelcase(s)
|
||||||
self.split(/_/).map(&:capitalize).join
|
s.split("_").map(&:capitalize).join
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_snake
|
def snakecase(s)
|
||||||
self
|
s
|
||||||
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
||||||
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
||||||
.downcase
|
.downcase
|
||||||
|
@ -7,11 +7,11 @@ module OBSWS
|
|||||||
end
|
end
|
||||||
|
|
||||||
def minor
|
def minor
|
||||||
2
|
3
|
||||||
end
|
end
|
||||||
|
|
||||||
def patch
|
def patch
|
||||||
1
|
3
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_a
|
def to_a
|
||||||
|
Loading…
x
Reference in New Issue
Block a user