Compare commits

...

13 Commits

Author SHA1 Message Date
155cbe019a upd Gemfile.lock 2023-07-26 19:53:04 +01:00
6293ae7b8c patch bump 2023-07-26 18:55:57 +01:00
57fca646b5 remove the monkey patching 2023-07-26 18:55:35 +01:00
d12a1a5954 refactor Callbacks 2023-07-26 18:55:19 +01:00
438f3b1659 upd Gemfile.lock 2023-07-26 17:27:41 +01:00
d15418a660 mixin only methods for directing the driver
patch bump
2023-07-26 16:55:59 +01:00
2883fd42cc Socket class and driver methods
moved into Driver module

patch bump
2023-07-26 16:38:36 +01:00
88b2eabc0c typo fix 2023-07-26 16:15:43 +01:00
e15e17cc9f update readme title 2023-07-26 16:12:38 +01:00
72e09d5278 minor version bump 2023-07-26 14:38:12 +01:00
11d991b039 examples updated 2023-07-26 14:38:01 +01:00
3d3d8f3020 log level may now be set with environment variable 2023-07-26 14:37:49 +01:00
82c6ced760 logger module added 2023-07-26 14:37:35 +01:00
14 changed files with 111 additions and 93 deletions

View File

@ -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)

View File

@ -2,7 +2,7 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/onyx-and-iris/obsws-ruby/blob/dev/LICENSE) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/onyx-and-iris/obsws-ruby/blob/dev/LICENSE)
[![Ruby Code Style](https://img.shields.io/badge/code_style-standard-violet.svg)](https://github.com/standardrb/standard) [![Ruby Code Style](https://img.shields.io/badge/code_style-standard-violet.svg)](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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View 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

View File

@ -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
View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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