summaryrefslogtreecommitdiff
path: root/crystal
diff options
context:
space:
mode:
authorJoe Rayhawk <jrayhawk+git@omgwallhack.org>2024-12-05 00:56:44 -0800
committerJoe Rayhawk <jrayhawk+git@omgwallhack.org>2024-12-05 00:56:44 -0800
commit6301a55b7ec77d70febc2ed76ad176efc6355878 (patch)
treed2bac8c1f2058bda823ef339e41e1e471ad1816f /crystal
parent33e8ef50262d0f17d709e381c511d9f9ffb3b210 (diff)
downloadtwitchtools-6301a55b7ec77d70febc2ed76ad176efc6355878.tar.gz
twitchtools-6301a55b7ec77d70febc2ed76ad176efc6355878.zip
crystal/bullshit: chat-driven dynamic media source download and displayHEADmaster
Diffstat (limited to 'crystal')
-rwxr-xr-xcrystal/bullshit.cr143
1 files changed, 143 insertions, 0 deletions
diff --git a/crystal/bullshit.cr b/crystal/bullshit.cr
new file mode 100755
index 0000000..9feb24d
--- /dev/null
+++ b/crystal/bullshit.cr
@@ -0,0 +1,143 @@
+require "obswebsocket"
+require "pretty_print"
+require "math"
+
+STDOUT.sync = true
+STDOUT.flush_on_newline = true
+
+struct Nil
+ def as_s?
+ self
+ end
+end
+
+settings = Hash(String, String).new
+
+settings["configdir"] = Path.home./("/.config/bungmobott/").to_s
+settings["statedir"] = Path.home./("/.local/state/bungmobott/").to_s
+
+if ENV["LOCALAPPDATA"]?
+ settings["configdir"] = Path.windows( ENV["LOCALAPPDATA"] + "\\bungmobott\\" ).to_s
+ settings["statedir"] = Path.windows( ENV["LOCALAPPDATA"] + "\\bungmobott\\state\\" ).to_s
+end
+
+settings["tempdir"] = "/tmp/bungmobott/"
+ENV["TEMP"]? && ( settings["tempdir"] = "#{ENV["TEMP"]}\\bungmobott\\" )
+
+ENV["XDG_CONFIG_HOME"]? && ( settings["configdir"] = ENV["XDG_CONFIG_HOME"] + "/bungmobott/" )
+#ENV["XDG_DATA_HOME"]? && ( settings["datadir"] = ENV["XDG_DATA_HOME"] ) # unused?
+ENV["XDG_STATE_HOME"]? && ( settings["statedir"] = ENV["XDG_STATE_HOME"] + "/bungmobott/" )
+
+Dir.mkdir_p( settings["configdir"] )
+Dir.mkdir_p( settings["statedir"] )
+Dir.mkdir_p( settings["tempdir"] )
+
+settings["home"] = Path.home.to_s
+regextwitchuser = /^[0-9a-zA-Z_]+$/
+
+obs = OBS::WebSocket.new( "ws://127.0.0.1:4455/" )
+directions = Hash( String, Float64 ).new
+
+# OBS event thread
+spawn do
+ evchan = Channel( JSON::Any ).new
+ obs.eventsub_add( evchan )
+ while json = evchan.receive
+ # A Fiber.yield occurs after this to make sure "json" doesn't get overwritten before we can use it.
+ spawn do
+ d = json # Copy *immediately*
+ case d["eventType"].as_s
+ when "SceneItemTransformChanged"
+ edata = d["eventData"]
+ puts "test 1"
+ scenelist = obs.scenes[edata["sceneName"].as_s]
+ puts "test 2"
+ sceneitem = scenelist[edata["sceneItemId"].as_i64]
+ puts "test 3"
+ t = edata["sceneItemTransform"]
+ if ( direction = directions[sceneitem.name]? )
+ spx = t["positionX" ].as_f.to_i64
+ spy = t["positionY" ].as_f.to_i64
+ sdx = t["sourceHeight"].as_f.to_i64
+ sdy = t["sourceWidth" ].as_f.to_i64
+ bx = obs.video.to_h["baseWidth" ].as(Int64 | Float64).to_i64
+ by = obs.video.to_h["baseHeight"].as(Int64 | Float64).to_i64
+ abx = bx - sdx
+ aby = by - sdy
+ ratio = Math.tan(direction)
+ spxr = (aby/2 * ratio).abs
+ if spxr > abx/2
+ spxr = abx/2
+ spyr = abx/2 * ratio
+ elsif ratio > 0
+ spyr = abx/2
+ else # ratio < 0
+ spyr = abx/2 * -1
+ end
+ if ( direction >= 0 ) && ( direction <= Math::PI )
+ nspx = spxr
+ else # ( direction >= Math::PI ) && ( direction <= 2*Math::PI )
+ nspx = ( spxr * -1 )
+ end
+ nspx = nspx - sdx
+ nspy = spyr - sdy
+ unless ( spx == nspx && spy == nspy )
+ sceneitem.transform( { "positionX" => nspx, "positionY" => nspy } )
+ end
+ end
+ end
+ end
+ Fiber.yield
+ end
+end
+
+def obsrandommediaenable( obs : OBS::WebSocket, siname : String )
+ if ( Random.rand(3) < 2 )
+ obs.scenes.current.metascene[siname][0].enable!
+ else
+ randsiname = obs.scenes.current.metascene.keys.select( /^#{siname}/ ).sample( 1 )[0]
+ obs.scenes.current.metascene[randsiname][0].enable!
+ end
+end
+
+name=ARGV[0].sub( /.+\//, "" ).sub( /\..+/, "" )
+
+def obsmediacreate( obs : OBS::WebSocket, sname : String, iname, path : String )
+ iname = "medialoop-bullshit-#{iname}"
+ isettings = Hash( String, String | Bool | Int64 | Float64 ){
+ "advanced" => true,
+ "clear_on_media_end" => true,
+ "color_range" => 0.to_i64,
+ "is_local_file" => true,
+ "looping" => true,
+ "restart_on_activate" => true,
+ "local_file" => path,
+ }
+ response = obs.scenes[sname].createinput( iname, "ffmpeg_source", isettings )
+ # Skip object model stuff and configure the SceneItem as fast as we possibly can
+ if ( rdata = response["responseData"]? ) && ( goodtransform = obs.sources.last_known_real_transform?( iname ) )
+ siid = rdata["sceneItemId"].as_i64
+ ENV["DISTANCE"] # percent
+ ENV["DIRECTION"] # degrees
+ ENV["SIZE"] # width\nheight
+ ENV["COLORMASK"] # color/RGB(A) html color code
+# obs.send_sync( OBS.req( "SetSceneItemTransform", JSON.parse( { "sceneName" => sname, "sceneItemId" => siid, "sceneItemTransform" => { "positionX" => goodtransform.to_h["positionX"], "positionY" => goodtransform.to_h["positionY"] } }.to_json ) ) )
+ end
+ if ( colormask = ENV["COLORMASK"].to_u32 )
+ obs.send_sync( OBS.req( "CreateSourceFilter", JSON.parse( { "sourceName" => iname, "filterName" => "chromakey", "filterKind" => "chroma_key_filter_v2", "filterSettings" => { "key_color_type" => "custom", "key_color" => colormask, "smoothness" => 10, "similarity" => "1" } }.to_json ) ) )
+ #obs.send_sync( OBS.req( "CreateSourceFilter", JSON.parse( { "sourceName" => iname, "filterName" => "colorkey", "filterKind" => "color_key_filter_v2", "filterSettings" => { "key_color_type" => "custom", "key_color" => colormask, "smoothness" => 1000 } }.to_json ) ) )
+ end
+end
+
+# white: 4294967296
+
+if ( direction = ENV["DIRECTION"].to_u16 )
+ directions[name] = Math::PI/180*direction
+else
+ directions[name] = Math::PI/180*rand(360)
+end
+
+obsmediacreate( obs, "meta-bullshit", name, ARGV[0] )
+
+sleep 2
+Fiber.yield