def dup(self): current_scene = S.obs_scene_from_source( S.obs_frontend_get_current_scene()) scene_item = S.obs_scene_find_source(current_scene, self.source_name) info = S.obs_transform_info() crop = S.obs_sceneitem_crop() S.obs_sceneitem_get_info(scene_item, info) S.obs_sceneitem_get_crop(scene_item, crop) duplicate = S.obs_sceneitem_get_source(scene_item) duplicated = S.obs_source_duplicate(duplicate, "duplicate" + self.source_name, False) scenes = S.obs_frontend_get_scenes() for scene in scenes: name = S.obs_source_get_name(scene) if name == self.scene_name: scene = S.obs_scene_from_source(scene) scene_item2 = S.obs_scene_add(scene, duplicated) S.obs_sceneitem_set_info(scene_item2, info) S.obs_sceneitem_set_crop(scene_item2, crop) S.obs_scene_release(scene) S.obs_source_release(duplicated) S.source_list_release(scenes) S.obs_scene_release(current_scene)
def getTransformationList(sceneitems): transformations = [] for sceneitem in sceneitems: pos = obs.vec2() obs.obs_sceneitem_get_pos(sceneitem, pos) rot = obs.obs_sceneitem_get_rot(sceneitem) scale = obs.vec2() obs.obs_sceneitem_get_scale(sceneitem, scale) alignment = obs.obs_sceneitem_get_alignment(sceneitem) bounds = obs.vec2() obs.obs_sceneitem_get_bounds(sceneitem, bounds) boundsType = obs.obs_sceneitem_get_bounds_type(sceneitem) boundsAlignment = obs.obs_sceneitem_get_bounds_alignment(sceneitem) crop = obs.obs_sceneitem_crop() obs.obs_sceneitem_get_crop(sceneitem, crop) transformations.append({ "pos": [pos.x, pos.y], "rot": rot, "scale": [scale.x, scale.y], "alignment": alignment, "bounds": [bounds.x, bounds.y], "boundsType": boundsType, "boundsAlignment": boundsAlignment, "crop": [crop.left, crop.right, crop.top, crop.bottom] }) obs.obs_sceneitem_release(sceneitem) return transformations
def dumpSceneData(): scene = obs.obs_frontend_get_current_scene() sceneObject = obs.obs_scene_from_source(scene) items = obs.obs_scene_enum_items(sceneObject) itemArray = [] for item in items: sceneItem = obs.obs_sceneitem_get_source(item) name = obs.obs_source_get_name(sceneItem) if "tweentool:" not in name: pos = obs.vec2() obs.obs_sceneitem_get_pos(item, pos) rot = obs.obs_sceneitem_get_rot(item) scale = obs.vec2() obs.obs_sceneitem_get_scale(item, scale) alignment = obs.obs_sceneitem_get_alignment(item) bounds = obs.vec2() obs.obs_sceneitem_get_bounds(item, bounds) boundsType = obs.obs_sceneitem_get_bounds_type(item) boundsAlignment = obs.obs_sceneitem_get_bounds_alignment(item) crop = obs.obs_sceneitem_crop() obs.obs_sceneitem_get_crop(item, crop) itemArray.append({ "name": name, "pos": [pos.x, pos.y], "rot": rot, "scale": [scale.x, scale.y], "alignment": alignment, "bounds": [bounds.x, bounds.y], "boundsType": boundsType, "boundsAlignment": boundsAlignment, "crop": [crop.left, crop.right, crop.top, crop.bottom] }) return itemArray
def _obs_sceneitem_set_crop(self, scene_name, source_name, crop_sizes): with self._sceneitem_by_name(scene_name, source_name) as si: crop = _obs.obs_sceneitem_crop() crop.left, crop.right, crop.top, crop.bottom = crop_sizes _obs.obs_sceneitem_set_crop(si, crop)
def _obs_sceneitem_get_crop(self, scene_name, source_name): with self._sceneitem_by_name(scene_name, source_name) as si: crop = _obs.obs_sceneitem_crop() _obs.obs_sceneitem_get_crop(si, crop) return crop.left, crop.right, crop.top, crop.bottom
def sad_threaded(props, prop): # 1. Make it grayscale cam_source = obs.obs_get_source_by_name("Shitty Webcam") grayscale_filter = obs.obs_source_get_filter_by_name(cam_source, "Gray") data = obs.obs_data_create() obs.obs_data_set_double(data, "clut_amount", 1.0) obs.obs_source_update(grayscale_filter, data) # 2. Save initial props current_scene_source = obs.obs_frontend_get_current_scene() obs.obs_frontend_set_current_preview_scene(current_scene_source) current_scene = obs.obs_scene_from_source(current_scene_source) cam_sceneitem = obs.obs_scene_find_source(current_scene, "Shitty Webcam") initial_scale = obs.vec2() obs.obs_sceneitem_get_scale(cam_sceneitem, initial_scale) initial_pos = obs.vec2() obs.obs_sceneitem_get_pos(cam_sceneitem, initial_pos) initial_box_transform = obs.matrix4() obs.obs_sceneitem_get_box_transform(cam_sceneitem, initial_box_transform) initial_crop = obs.obs_sceneitem_crop() obs.obs_sceneitem_get_crop(cam_sceneitem, initial_crop) # matrix4.x.x -> x size # matrix4.y.y -> y size # matrix4.t.x -> x position # matrix4.t.y -> y position # for prop in ["x", "y", "z", "t"]: # print(f"Matrix property {prop}") # vec = getattr(initial_box_transform, prop) # print(dir(vec)) # for vec_prop in ["m", "w", "x", "y", "z"]: # print(f"Vec Property {vec_prop}: {getattr(vec, vec_prop)}") size_x = initial_box_transform.x.x size_y = initial_box_transform.y.y initial_draw_transform = obs.matrix4() obs.obs_sceneitem_get_draw_transform(cam_sceneitem, initial_draw_transform) print(dir(initial_draw_transform)) # 3. Mute desktop audio, play sound of silence desktop_audio = obs.obs_get_output_source(4) obs.obs_source_set_muted(desktop_audio, True) song = obs.obs_source_create("ffmpeg_source", "Sound of Silence", None, None) song_settings = obs.obs_data_create() obs.obs_data_set_string( song_settings, "local_file", "C:\\Users\\avikn\\Downloads\\sound_of_silence.mp3") obs.obs_source_update(song, song_settings) obs.obs_data_release(song_settings) song_item = obs.obs_scene_add(current_scene, song) # 4. Scale up / reposition the camera. # It's gross but it works don't touch it. dynamic_scale = obs.vec2() obs.vec2_copy(dynamic_scale, initial_scale) increment = obs.vec2() increment.x = 0.0015 increment.y = 0.0015 crop = (initial_crop.top, initial_crop.right, initial_crop.bottom, initial_crop.left) size_x, size_y = get_size(cam_sceneitem) dynamic_crop = obs.obs_sceneitem_crop() bounds = obs.vec2() bounds.x = size_x bounds.y = size_y obs.obs_sceneitem_set_bounds(cam_sceneitem, bounds) for _ in range(240): obs.obs_sceneitem_set_bounds_type(cam_sceneitem, 0) obs.vec2_add(dynamic_scale, dynamic_scale, increment) obs.obs_sceneitem_set_scale(cam_sceneitem, dynamic_scale) nsize_x, nsize_y = get_size(cam_sceneitem) x_delta = nsize_x - size_x + (dynamic_crop.right * 2) y_delta = nsize_y - size_y + (dynamic_crop.top * 2) dynamic_crop.top = int(y_delta / 2) dynamic_crop.right = int(x_delta / 2) dynamic_crop.bottom = int(y_delta / 2) dynamic_crop.left = int(x_delta / 2) obs.obs_sceneitem_set_crop(cam_sceneitem, dynamic_crop) obs.obs_sceneitem_set_bounds_type(cam_sceneitem, 1) obs.obs_frontend_preview_program_trigger_transition() time.sleep(0.1) # 5. Cleanup obs.obs_source_set_muted(desktop_audio, False) obs.obs_sceneitem_set_scale(cam_sceneitem, initial_scale) obs.obs_sceneitem_set_crop(cam_sceneitem, initial_crop) obs.obs_data_set_double(data, "clut_amount", 0.0) obs.obs_source_update(grayscale_filter, data) obs.obs_sceneitem_remove(song_item) obs.obs_source_release(song) time.sleep(0.1) obs.obs_frontend_preview_program_trigger_transition()
def _crop(self): crop = obs.obs_sceneitem_crop() obs.obs_sceneitem_get_crop(self.sceneitem, crop) return crop
def changeSourceToMousePosition(): #Get current scene currentScene = currentSceneName() if currentScene is None: return #Get scene item src = obs.obs_get_source_by_name(currentScene) if src is None: return scene = obs.obs_scene_from_source(src) if scene is None: return obs.obs_source_release(src) for sourceName in [sourceName1, sourceName2]: sceneItem = obs.obs_scene_find_source(scene, sourceName) if sceneItem is None: break #Variables global currentLeft global currentTop global currentRight global currentBottom global wantedLeft global wantedTop global wantedRight global wantedBottom #Following mouse if followingMouse: global currentCamera mousePos = queryMousePosition() wantedCamera = mousePos xSizeNow = xMaxSize - currentRight - currentLeft ySizeNow = yMaxSize - currentBottom - currentTop scaleRateFixer = ((xMaxSize + yMaxSize) / (xSizeNow + ySizeNow)) xDiff = (wantedCamera.x - currentCamera[0]) * followRate / 100 * scaleRateFixer yDiff = (wantedCamera.y - currentCamera[1]) * followRate / 100 * scaleRateFixer #Check if already reached wanted camera if abs(currentCamera[0] + xDiff - wantedCamera.x) > abs(currentCamera[0] - wantedCamera.x): xDiff = wantedCamera.x - currentCamera[0] if abs(currentCamera[1] + yDiff - wantedCamera.y) > abs(currentCamera[1] - wantedCamera.y): yDiff = wantedCamera.y - currentCamera[1] #Move camera currentCamera[0] += xDiff currentCamera[1] += yDiff #Move crop wantedLeft += xDiff wantedRight -= xDiff wantedTop += yDiff wantedBottom -= yDiff #When rectangle is outside fixedWantedLeft = wantedLeft fixedWantedTop = wantedTop fixedWantedRight = wantedRight fixedWantedBottom = wantedBottom if fixedWantedLeft < 0: fixedWantedRight += fixedWantedLeft fixedWantedLeft = 0 elif fixedWantedRight < 0: fixedWantedLeft += fixedWantedRight fixedWantedRight = 0 if fixedWantedTop < 0: fixedWantedBottom += fixedWantedTop fixedWantedTop = 0 elif fixedWantedBottom < 0: fixedWantedTop += fixedWantedBottom fixedWantedBottom = 0 #If zoom is off if not wantsZoom: fixedWantedLeft = 0 fixedWantedTop = 0 fixedWantedRight = 0 fixedWantedBottom = 0 #Move crop currentLeft += (fixedWantedLeft - currentLeft) * zoomRate / 100 currentTop += (fixedWantedTop - currentTop) * zoomRate / 100 currentRight += (fixedWantedRight - currentRight) * zoomRate / 100 currentBottom += (fixedWantedBottom - currentBottom) * zoomRate / 100 #Set to OBS cropZone = obs.obs_sceneitem_crop() cropZone.left = int(round(currentLeft)) cropZone.top = int(round(currentTop)) cropZone.right = int(round(currentRight)) cropZone.bottom = int(round(currentBottom)) obs.obs_sceneitem_set_crop(sceneItem, cropZone)
def script_tick(seconds): # OBS script interface. global discord_source source_name = obs.obs_data_get_string(settings, 'discord_source') if source_name != obs.obs_source_get_name(discord_source): obs.obs_source_release( discord_source) # Doesn’t error even if discord_source == None. discord_source = obs.obs_get_source_by_name(source_name) if not client: return # NOTE: These are 0 when the source isn’t visible at all in the current scene. Not that it matters, but I was just weirded out by it until I got it. source_width = obs.obs_source_get_width(discord_source) source_height = obs.obs_source_get_height(discord_source) margin_top = MARGIN_TOP if not obs.obs_data_get_bool(settings, 'full_screen'): margin_top = margin_top + TITLE_BAR # Get Discord call layout distribution and caller size. people = [x for x in client.video] # Mutability and shiz. nonvideo = obs.obs_data_get_bool(settings, 'show_nonvideo_participants') if nonvideo: people += client.audio count = len(people) if count == 1 and (not client.audio or not client.video and nonvideo): count = 2 # Discord adds a call to action that occupies the same space as a second caller. rows = None cols = None width = 0 height = None offsetx = 0 offsety = 0 offset_last = None if source_width and source_height: totalw = source_width - MARGIN_SIDES * 2 totalh = source_height - margin_top - MARGIN_BTM if totalw > 0 and totalh > 0: wide = None # Discord packs the callers in as many columns as possible, unless their videos appear bigger with fewer columns. for c in reversed(range(1, count + 1)): r = math.ceil(count / c) w = (totalw - CALLER_SPACING * (c - 1)) / c h = (totalh - CALLER_SPACING * (r - 1)) / r wi = w / h > CALLER_ASPECT if wi: w = h * CALLER_ASPECT if w > width: rows = r cols = c width = w height = h wide = wi if rows: # If the window is wider or taller than the callers fit in, Discord will center them as a whole. inner_width = (width * cols + CALLER_SPACING * (cols - 1)) if wide: # Wider than needed, therefore center horizontally. offsetx = (totalw - inner_width) / 2 else: # Taller than needed, therefore center vertically. height = width / CALLER_ASPECT # We compared using widths only before, so height needs to be adjusted. offsety = (totalh - (height * rows + CALLER_SPACING * (rows - 1))) / 2 # If last row contains fewer callers than columns, Discord will center it. offset_last = count % cols if offset_last > 0: offset_last = (inner_width - (width * offset_last + CALLER_SPACING * (offset_last - 1))) / 2 # Apply necessary changes to relevant scene items. scene_sources = obs.obs_frontend_get_scenes() for scene_src in scene_sources: scene = obs.obs_scene_from_source(scene_src) # Shouldn’t be released. items = obs.obs_scene_enum_items(scene) i = 0 next_vis = None for item in reversed(items): _next_vis = None if obs.obs_sceneitem_get_source( item) == discord_source: # Shouldn’t be released. uid = int( obs.obs_data_get_string(settings, f'participant{i}') or -1) visible = True try: index = people.index(uid) except (IndexError, ValueError): visible = False i += 1 obs.obs_sceneitem_set_visible(item, visible) if visible and rows: crop = obs.obs_sceneitem_crop() obs.obs_sceneitem_get_crop(item, crop) scale = obs.vec2() obs.obs_sceneitem_get_scale(item, scale) bounds = obs.vec2() obs.obs_sceneitem_get_bounds(item, bounds) # If item was set to not use a bounding box policy, calculate it from its other transform properties. if obs.obs_sceneitem_get_bounds_type( item) == obs.OBS_BOUNDS_NONE: obs.vec2_set( bounds, scale.x * (source_width - crop.right - crop.left), scale.y * (source_height - crop.bottom - crop.top)) obs.obs_sceneitem_set_bounds(item, bounds) obs.obs_sceneitem_set_bounds_type( item, obs.OBS_BOUNDS_SCALE_OUTER) obs.obs_sceneitem_set_bounds_alignment( item, 0 ) # obs.OBS_ALIGN_CENTER doesn’t seem to be implemented. # Get top left corner of this caller. r = math.ceil((index + 1) / cols) c = index % cols + 1 x = MARGIN_SIDES + offsetx + (width + CALLER_SPACING) * (c - 1) if r == rows: x = x + offset_last y = margin_top + offsety + (height + CALLER_SPACING) * (r - 1) # Make sure the crop doesn’t overflow the item bounds. aspect = bounds.x / bounds.y clipx = 0 clipy = 0 if aspect > CALLER_ASPECT: clipy = (height - width / aspect) / 2 else: clipx = (width - height * aspect) / 2 crop.left = math.ceil(x + CALLER_BORDER + clipx) crop.top = math.ceil(y + CALLER_BORDER + clipy) crop.right = source_width - int(x + width - CALLER_BORDER - clipx) crop.bottom = source_height - int(y + height - CALLER_BORDER - clipy) obs.obs_sceneitem_set_crop(item, crop) sx = abs(scale.x) if uid == int( obs.obs_data_get_string(settings, 'myself') or -1) and uid in client.video: sx = -sx sy = scale.y obs.vec2_set(scale, sx, sy) obs.obs_sceneitem_set_scale(item, scale) if not nonvideo and obs.obs_data_get_bool( settings, 'item_right_below'): _next_vis = uid in client.audio elif next_vis is not None: obs.obs_sceneitem_set_visible(item, next_vis) next_vis = _next_vis obs.sceneitem_list_release(items) obs.source_list_release(scene_sources)
def script_tick(tick): global globSettings global animationInfo global animationRunning global app global currentScene if animationRunning: animationInfo["animTime"] += tick animationInfo["animTime"] = min(animationInfo["animTime"], animationInfo["stopTime"]) scaleFactor = easeInOutQuad( animationInfo["animTime"] / animationInfo["stopTime"], 0, 1, 1) animScene = animationInfo["animScene"] initial = animationInfo["initial"] destination = animationInfo["destination"] result = [] sceneObject = obs.obs_scene_from_source(animScene) if obs.obs_source_get_name( animScene) in obs.obs_frontend_get_scene_names(): items = obs.obs_scene_enum_items(sceneObject) for i in range(len(initial)): pos = obs.vec2() pos.x = scaleFactor * (destination[i]["pos"][0] - initial[i] ["pos"][0]) + initial[i]["pos"][0] pos.y = scaleFactor * (destination[i]["pos"][1] - initial[i] ["pos"][1]) + initial[i]["pos"][1] rot = scaleFactor * (destination[i]["rot"] - initial[i]["rot"]) + initial[i]["rot"] scale = obs.vec2() scale.x = scaleFactor * ( destination[i]["scale"][0] - initial[i]["scale"][0]) + initial[i]["scale"][0] scale.y = scaleFactor * ( destination[i]["scale"][1] - initial[i]["scale"][1]) + initial[i]["scale"][1] alignment = destination[i]["alignment"] bounds = obs.vec2() bounds.x = scaleFactor * ( destination[i]["bounds"][0] - initial[i]["bounds"][0]) + initial[i]["bounds"][0] bounds.y = scaleFactor * ( destination[i]["bounds"][1] - initial[i]["bounds"][1]) + initial[i]["bounds"][1] boundsType = destination[i]["boundsType"] boundsAlignment = destination[i]["boundsAlignment"] crop = obs.obs_sceneitem_crop() crop.left = math.floor( scaleFactor * (destination[i]["crop"][0] - initial[i]["crop"][0]) + initial[i]["crop"][0]) crop.right = math.floor( scaleFactor * (destination[i]["crop"][1] - initial[i]["crop"][1]) + initial[i]["crop"][1]) crop.top = math.floor( scaleFactor * (destination[i]["crop"][2] - initial[i]["crop"][2]) + initial[i]["crop"][2]) crop.bottom = math.floor( scaleFactor * (destination[i]["crop"][3] - initial[i]["crop"][3]) + initial[i]["crop"][3]) obs.obs_sceneitem_set_pos(items[i], pos) obs.obs_sceneitem_set_rot(items[i], rot) obs.obs_sceneitem_set_scale(items[i], scale) obs.obs_sceneitem_set_alignment(items[i], alignment) obs.obs_sceneitem_set_bounds(items[i], bounds) obs.obs_sceneitem_set_bounds_type(items[i], boundsType) obs.obs_sceneitem_set_bounds_alignment(items[i], boundsAlignment) obs.obs_sceneitem_set_crop(items[i], crop) obs.sceneitem_list_release(items) #obs.obs_scene_release(sceneObject) if animationInfo["animTime"] == animationInfo["stopTime"]: obs.obs_source_release(animScene) animationInfo["animScene"] = None animationRunning = False
def script_tick(tick): if scriptSettings['anim']['animating']: scriptSettings['anim']['time'] += tick scriptSettings['anim']['time'] = min(scriptSettings['anim']['time'], scriptSettings['anim']['length']) initial = scriptSettings['anim']['src'] destination = scriptSettings['anim']['dest'] tScale = easeInOutQuad( scriptSettings['anim']['time'] / scriptSettings['anim']['length'], 0, 1, 1) scene = obs.obs_frontend_get_current_scene() sceneObject = obs.obs_scene_from_source(scene) tweenItems = scriptSettings['anim']['tweener']['tweenItems'] sceneItems = obs.obs_scene_enum_items(sceneObject) for sItem in sceneItems: sceneItem = obs.obs_sceneitem_get_source(sItem) sName = obs.obs_source_get_name(sceneItem) for tItem in tweenItems: if sName == tItem['name']: pos = obs.vec2() pos.x = tScale * ( destination[sName]["pos"][0] - initial[sName]["pos"][0]) + initial[sName]["pos"][0] pos.y = tScale * ( destination[sName]["pos"][1] - initial[sName]["pos"][1]) + initial[sName]["pos"][1] rot = tScale * (destination[sName]["rot"] - initial[sName] ["rot"]) + initial[sName]["rot"] scale = obs.vec2() scale.x = tScale * (destination[sName]["scale"][0] - initial[sName]["scale"][0] ) + initial[sName]["scale"][0] scale.y = tScale * (destination[sName]["scale"][1] - initial[sName]["scale"][1] ) + initial[sName]["scale"][1] alignment = destination[sName]["alignment"] bounds = obs.vec2() bounds.x = tScale * (destination[sName]["bounds"][0] - initial[sName]["bounds"][0] ) + initial[sName]["bounds"][0] bounds.y = tScale * (destination[sName]["bounds"][1] - initial[sName]["bounds"][1] ) + initial[sName]["bounds"][1] boundsType = destination[sName]["boundsType"] boundsAlignment = destination[sName]["boundsAlignment"] crop = obs.obs_sceneitem_crop() crop.left = math.floor(tScale * (destination[sName]["crop"][0] - initial[sName]["crop"][0]) + initial[sName]["crop"][0]) crop.right = math.floor(tScale * (destination[sName]["crop"][1] - initial[sName]["crop"][1]) + initial[sName]["crop"][1]) crop.top = math.floor(tScale * (destination[sName]["crop"][2] - initial[sName]["crop"][2]) + initial[sName]["crop"][2]) crop.bottom = math.floor(tScale * (destination[sName]["crop"][3] - initial[sName]["crop"][3]) + initial[sName]["crop"][3]) obs.obs_sceneitem_set_pos(sItem, pos) obs.obs_sceneitem_set_rot(sItem, rot) obs.obs_sceneitem_set_scale(sItem, scale) obs.obs_sceneitem_set_alignment(sItem, alignment) obs.obs_sceneitem_set_bounds(sItem, bounds) obs.obs_sceneitem_set_bounds_type(sItem, boundsType) obs.obs_sceneitem_set_bounds_alignment( sItem, boundsAlignment) obs.obs_sceneitem_set_crop(sItem, crop) if scriptSettings['anim']['time'] >= scriptSettings['anim']['length']: scriptSettings['anim']['src'] = None scriptSettings['anim']['dest'] = None scriptSettings['anim']['time'] = math.inf scriptSettings['anim']['length'] = 10000 scriptSettings['anim']['tweener'] = None scriptSettings['anim']['animating'] = False