{"id":16417561,"url":"https://github.com/upgradeQ/Streaming-Software-Scripting-Reference","last_synced_at":"2025-10-26T20:31:13.950Z","repository":{"id":40529321,"uuid":"270560350","full_name":"upgradeQ/Streaming-Software-Scripting-Reference","owner":"upgradeQ","description":"Reference for OBS Studio Python API","archived":false,"fork":false,"pushed_at":"2024-07-27T13:46:32.000Z","size":520,"stargazers_count":341,"open_issues_count":0,"forks_count":35,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-12T03:27:36.124Z","etag":null,"topics":["api","callbacks","example","filters","hotkey","obs","obs-api","obs-get-source-by-name","obs-script","obs-scripts","obs-studio","obs-studio-api","obspython","python","timer"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/upgradeQ.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-06-08T06:45:58.000Z","updated_at":"2024-10-09T03:05:47.000Z","dependencies_parsed_at":"2024-07-27T14:50:01.140Z","dependency_job_id":"a476ef84-8f1d-4246-90d5-b1f5524c68d0","html_url":"https://github.com/upgradeQ/Streaming-Software-Scripting-Reference","commit_stats":null,"previous_names":["upgradeq/streaming-software-scripting-reference","upgradeq/obs-studio-python-scripting-cheatsheet-obspython-examples-of-api"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upgradeQ%2FStreaming-Software-Scripting-Reference","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upgradeQ%2FStreaming-Software-Scripting-Reference/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upgradeQ%2FStreaming-Software-Scripting-Reference/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upgradeQ%2FStreaming-Software-Scripting-Reference/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/upgradeQ","download_url":"https://codeload.github.com/upgradeQ/Streaming-Software-Scripting-Reference/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238397042,"owners_count":19465106,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","callbacks","example","filters","hotkey","obs","obs-api","obs-get-source-by-name","obs-script","obs-scripts","obs-studio","obs-studio-api","obspython","python","timer"],"created_at":"2024-10-11T07:11:35.028Z","updated_at":"2025-10-26T20:31:08.559Z","avatar_url":"https://github.com/upgradeQ.png","language":"Python","readme":"Getting started:\r\n- [Python](https://www.python.org/about/gettingstarted/)\r\n- [OBS Studio Scripting](https://github.com/obsproject/obs-studio/wiki/Getting-Started-With-OBS-Scripting)\r\n\r\nConsider cloning this repo and running examples, they are self contained.\r\nTip: you can create a copy of script, rename it, and add to OBS. So two of identical scripts will be run in parallel with separate namespaces. \r\nAlso check out [issues](https://github.com/upgradeQ/OBS-Studio-Python-Scripting-Cheatsheet-obspython-Examples-of-API/issues)  to report error or have a suggestion and [discussions](https://github.com/upgradeQ/OBS-Studio-Python-Scripting-Cheatsheet-obspython-Examples-of-API/discussions) \r\n# Table of content \r\n- [UI](#ui)\r\n- [Property modification](#property-modification)\r\n- [Property additional input](#property-additional-input)\r\n- [obs_data](#obs_data)\r\n- [Print all source settings and filter names](#print-all-source-settings-and-filter-names)\r\n- [Save settings as json](#save-settings-as-json)\r\n- [Source's and filters with identifier string](#sources-and-filters-with-identifier-string)\r\n- [Add source](#add-source)\r\n- [Move source](#move-source)\r\n- [Duplicate source](#duplicate-source)\r\n- [Add filter to source](#add-filter-to-source)\r\n- [Toggle sceneitem visibility](#toggle-sceneitem-visibility)\r\n- [Set current scene](#set-current-scene)\r\n- [Get set order in scene](#get-set-order-in-scene)\r\n- [Add scene with sources to current scene](#add-scene-with-sources-to-current-scene)\r\n- [Events](#events)\r\n- [Program state](#program-state)\r\n- [Signals](#signals)\r\n- [Timers and threads](#timers-and-threads)\r\n- [Hotkeys](#hotkeys)\r\n- [Play sound](#play-sound)\r\n- [Read and write private data from scripts or plugins](#read-and-write-private-data-from-scripts-or-plugins)\r\n- [Browser source interaction](#browser-source-interaction)\r\n- [Browser source receive JSON data](#browser-source-receive-json-data)\r\n- [Access source dB volume level](#access-source-db-volume-level)\r\n- [Get current profile settings via ffi](#get-current-profile-settings-via-ffi)\r\n- [Convert from SWIG type to ctype](#convert-from-swig-type-to-ctype)\r\n- [Set current stream key](#set-current-stream-key)\r\n- [Raw frames](#raw-frames)\r\n- [Debug](#debug)\r\n- [Security](#security)\r\n- [Docs and code examples](#docs-and-code-examples)\r\n- [Changes between versions](#changes-between-versions)\r\n- [Links](#links)\r\n- [Contribute](#contribute)\r\n\r\n## UI\r\n|Preview| \r\n| --- | \r\n| `S.obs_properties_add_button(props, \"button1\", \"Refresh1:\",callback)` ![img](src/assets/button.png) |\r\n|`S.obs_properties_add_bool(props,\"_bool\",\"_bool:\")` ![img](src/assets/bool.png) |\r\n|`S.obs_properties_add_int(props,\"_int\",\"_int:\",1,100,1)` ![img](src/assets/int.png) |\r\n|`S.obs_properties_add_int_slider(props,\"_slider\",\"_slider:\",1,100,1) ` ![img](src/assets/slider.png) |\r\n|`S.obs_properties_add_text(props, \"_text\", \"_text:\", S.OBS_TEXT_DEFAULT) ` ![img](src/assets/text.png) |\r\n|`S.obs_properties_add_color(props,\"_color\",\"_color:\") ` ![img](src/assets/color.png) |\r\n|`S.obs_properties_add_font(props,\"_font\",\"_font:\")  ` ![img](src/assets/font.png) |\r\n|`S.obs_properties_add_font(props,\"_font\",\"_font:\")  ` ![img](src/assets/font.png) |\r\n|`bool_p = S.obs_properties_add_bool(props, \"_obs_bool\", \"Yes/No\"); S.obs_property_set_long_description(bool_p, \"Check if yes,else uncheck\")` ![img](src/assets/description.gif) |\r\n\r\n\r\nSee also :   \r\nhttps://obsproject.com/docs/reference-properties.html#property-object-functions\r\n\r\n## Property modification\r\n```python\r\ndef callback(props, prop, *args, **kwargs):  # pass settings implicitly\r\n    p = S.obs_properties_get(props, \"button\")\r\n    n = next(counter)\r\n    S.obs_property_set_description(p, f\"refresh pressed {n} times\")\r\n    return True\r\n...\r\ndef script_properties():\r\n    props = S.obs_properties_create()\r\n    b = S.obs_properties_add_button(\r\n        props, \"button\", \"refresh pressed 0 times\", refresh_pressed\r\n    )\r\n    S.obs_property_set_modified_callback(b, callback)\r\n    return props\r\n```\r\n[Full source](src/property_modification.py)  \r\nSee also :  \r\nhttps://obsproject.com/docs/reference-properties.html#property-modification-functions\r\n\r\n## Property additional input \r\n```python\r\ndef callback(props, prop, settings):\r\n    _number = S.obs_data_get_int(settings, \"_int\")\r\n    _text_value = S.obs_data_get_string(settings, \"_text\")\r\n    text_property = S.obs_properties_get(props, \"_text\")\r\n    if _number \u003e 50:\r\n        eg.data = _text_value + str(_number)\r\n        S.obs_property_set_visible(text_property, True)\r\n        return True\r\n    else:\r\n        eg.data = \"\"\r\n        S.obs_property_set_visible(text_property, False)\r\n        return True\r\n...\r\n\r\ndef script_properties():  # ui\r\n\r\n    ...\r\n    number = S.obs_properties_add_int(props, \"_int\", \"Number\", 1, 100, 1)\r\n    text_value = S.obs_properties_add_text(\r\n        props, \"_text\", \"Additional input:\", S.OBS_TEXT_DEFAULT\r\n    )\r\n    S.obs_property_set_visible(text_value, False)\r\n    S.obs_property_set_modified_callback(number, callback)\r\n    ...\r\n```\r\n[Full source](src/modification_prop.py)  \r\n\r\nNote: properties share similar structure , in Python, Lua, C.\r\n[Example C](https://github.com/obsproject/obs-studio/blob/05c9ddd2293a17717a1bb4189406dfdad79a93e1/plugins/oss-audio/oss-input.c#L626)\r\nSee also :  \r\nhttps://obsproject.com/docs/reference-properties.html#property-modification-functions\r\n\r\n## obs_data\r\n\r\n- `obs_data_get_string`\r\n- `obs_data_get_int`\r\n- `obs_data_get_double`\r\n- `obs_data_get_bool`\r\n- `obs_data_get_obj`\r\n- `obs_data_get_array`\r\n\r\n## Print all source settings and filter names\r\n\r\n```python\r\nsource = S.obs_get_source_by_name(self.source_name)\r\nsettings = S.obs_source_get_settings(source)\r\npsettings = S.obs_source_get_private_settings(source)\r\ndsettings = S.obs_data_get_defaults(settings)\r\npdsettings = S.obs_data_get_defaults(psettings)\r\nprint(\"[---------- settings ----------\")\r\nprint(S.obs_data_get_json(settings))\r\nprint(\"---------- private_settings ----------\")\r\nprint(S.obs_data_get_json(psettings))\r\nprint(\"---------- default settings for this source type ----------\")\r\nprint(S.obs_data_get_json(dsettings))\r\nprint(\"---------- default private settings for this source type ----------\")\r\nprint(S.obs_data_get_json(pdsettings))\r\n... \r\nprint(\"[--------- filter names --------\")\r\nfor i in range(filter_count):\r\n    settings = S.obs_data_array_item(filters, i)\r\n    filter_name = S.obs_data_get_string(settings, \"name\")\r\n    S.obs_data_release(settings)\r\n    print(filter_name)\r\nprint(\" filter names of %s --------\" % self.source_name)\r\n\r\n```\r\n\r\n[Full source](src/print_all_source_settings_and_filter_names.py)  \r\n\r\n## Save settings as json\r\n\r\n```python\r\np = Path(__file__).absolute()  # current script path\r\nfile = p.parent / \"saved_settings.json\"\r\ntry:\r\n    content = S.obs_data_get_json(Data._settings_)\r\n    with open(file, \"w\") as f:\r\n        f.write(content)\r\nexcept Exception as e:\r\n    print(e, \"cannot write to file\")\r\n```\r\n[Full source](src/save_json_data.py)  \r\nSee also :  \r\nhttps://obsproject.com/docs/reference-settings.html  \r\nhttps://obsproject.com/docs/scripting.html#getting-the-current-script-s-path  \r\n\r\n# Source's and filters with identifier string\r\nTo identify with `obs_source_get_unversioned_id` , or creating source/filter.\r\n## Source's\r\n| Name | Source type identifier string |\r\n| --- | --- | \r\n| Application Audio Capture (BETA) | wasapi_process_output_capture |\r\n| Browser | browser_source | \r\n| Color Source | color_source |\r\n| Display Capture | monitor_capture |\r\n| Game Capture | game_capture |\r\n| Image Slide Show | slideshow |\r\n| Image | image_source | \r\n| Media Source | ffmpeg_source |\r\n| Text (GDI+) | text_gdiplus |\r\n| Window Capture | window_capture |\r\n## Filters\r\n| Name | Source type identifier string |\r\n| --- | --- | \r\n| 3-Band Equalizer | basic_eq_filter |\r\n| Async Delay | async_delay_filter |\r\n| Chroma Key V2 | chroma_key_filter_v2 |\r\n| Chroma Key | chroma_key_filter |\r\n| Color Correction V2 | color_filter_v2 |\r\n| Color Correction | color_filter |\r\n| Color Grade | color_grade_filter |\r\n| Color Key V2 | color_key_filter_v2 |\r\n| Color Key | color_key_filter |\r\n| Compressor | compressor_filter | \r\n| Crop/Pad | crop_filter | \r\n| Expander | expander_filter | \r\n| GPU Delay | gpu_delay_filter | \r\n| Gain | gain_filter | \r\n| HDR Tone Mapping (Override) | hdr_tonemap_filter |\r\n| Image Mask/Blend | mask_filter |\r\n| Invert Polarity | invert_polarity_filter |\r\n| Limiter | limiter_filter | \r\n| Luma Key V2 | luma_key_filter_v2 |\r\n| Luma Key | luma_key_filter |\r\n| Mask V2 | mask_filter_v2 |\r\n| Mask | mask_filter |\r\n| Noise Gate | noise_gate_filter |\r\n| Noise Suppression V2 | noise_suppress_filter_v2 |\r\n| Noise Suppression | noise_suppress_filter |\r\n| Render Delay | gpu_delay |\r\n| Scaling/Aspect Ratio | scale_filter |\r\n| Scroll | scroll_filter | \r\n| Sharpen V2 | sharpness_filter_v2 | \r\n| Sharpen | sharpness_filter | \r\n| Upward Compressor | upward_compressor_filter |\r\n| VST 2.x Plug-in | vst_filter |\r\n| Video Delay (Async) | async_delay_filter |\r\n\r\n# Add source\r\nCreate source and add it to current scene \r\n```python\r\nS.obs_data_set_string(settings, \"text\", \"The quick brown fox jumps over the lazy dog\")\r\nsource = S.obs_source_create_private(\"text_gdiplus\", \"test_py\", settings)\r\nS.obs_scene_add(scene, source)\r\n```\r\n[Full source](src/source_add.py)  \r\nSee also :  \r\nhttps://obsproject.com/docs/reference-scenes.html\r\n\r\n# Move source\r\nGet current scene , get source name, move source to location \r\n```python\r\ndef __init__(self):\r\n    pos = S.vec2()\r\n    self.location = pos\r\n...\r\ndef move_text_source(self):\r\n    current_scene = S.obs_frontend_get_current_scene()\r\n    source = S.obs_get_source_by_name(\"test_py\")\r\n    scene = S.obs_scene_from_source(current_scene)\r\n    scene_item = S.obs_scene_find_source(scene, \"test_py\")\r\n    if scene_item:\r\n        dx, dy = 10, 10\r\n        print(\"old values\", self.location.x)\r\n        S.obs_sceneitem_get_pos(\r\n            scene_item, self.location\r\n        )  # update to last position if its changed from OBS\r\n        self.location.x += dx\r\n        self.location.y += dy\r\n        print(\"new values\", self.location.x)\r\n        S.obs_sceneitem_set_pos(scene_item, self.location)\r\n```\r\n[Full source](src/move_source_.py)\r\n\r\n# Duplicate source\r\n\r\n```python\r\n def dup(self):\r\n     current_scene = S.obs_scene_from_source(S.obs_frontend_get_current_scene())\r\n     scene_item = S.obs_scene_find_source(current_scene, self.source_name)\r\n     info = S.obs_transform_info()\r\n     crop = S.obs_sceneitem_crop()\r\n     S.obs_sceneitem_get_info(scene_item,info)\r\n     S.obs_sceneitem_get_crop(scene_item,crop)\r\n     duplicate = S.obs_sceneitem_get_source(scene_item)\r\n     duplicated = S.obs_source_duplicate(\r\n         duplicate, \"duplicate\" + self.source_name, False\r\n     )\r\n\r\n     scenes = S.obs_frontend_get_scenes()\r\n     for scene in scenes:\r\n         name = S.obs_source_get_name(scene)\r\n         if name == self.scene_name:\r\n             scene = S.obs_scene_from_source(scene)\r\n             scene_item2 = S.obs_scene_add(scene, duplicated)\r\n             S.obs_sceneitem_set_info(scene_item2,info)\r\n             S.obs_sceneitem_set_crop(scene_item2,crop)\r\n```\r\n[Full source](src/duplicate_source.py)  \r\n\r\n# Add filter to source\r\nFilters are sources,they are not listed in obspython module, you need to know its id from `obs_source_info`\r\n```python\r\nS.obs_data_set_int(settings, \"opacity\", 50)\r\nsource_color = S.obs_source_create_private(\r\n    \"color_filter\", \"opacity to 50\", settings\r\n)\r\nS.obs_source_filter_add(source, source_color)\r\n```\r\n[Full source](src/source_filter.py)  \r\nSee also :  \r\n[Color correction source](https://github.com/obsproject/obs-studio/blob/c938ea712bce0e9d8e0cf348fd8f77725122b9a5/plugins/obs-filters/color-correction-filter.c#L408)  \r\nhttps://obsproject.com/docs/reference-sources.html\r\n# Toggle sceneitem visibility\r\n```python\r\ndef toggle(self):\r\n    current_scene = S.obs_scene_from_source(S.obs_frontend_get_current_scene())\r\n    scene_item = S.obs_scene_find_source(current_scene, self.source_name)\r\n    boolean = not S.obs_sceneitem_visible(scene_item)\r\n    S.obs_sceneitem_set_visible(scene_item, boolean)\r\n```\r\n[Full source](src/toggle_sceneitem_vis.py)\r\n\r\n# Set current scene\r\n```python\r\ndef set_current_scene(self):\r\n    scenes = S.obs_frontend_get_scenes()\r\n    for scene in scenes:\r\n        name = S.obs_source_get_name(scene)\r\n        if name == self.scene_name:\r\n            S.obs_frontend_set_current_scene(scene)\r\n...\r\nscenes = S.obs_frontend_get_scenes() # Dropdown menu UI\r\nfor scene in scenes:\r\n    name = S.obs_source_get_name(scene)\r\n    S.obs_property_list_add_string(p, name, name) \r\n```\r\n[Full source](src/get_scene_by_name.py)\r\n# Get set order in scene\r\n```python\r\ndef get_order(scene_items=None):\r\n    order = list()\r\n    for i, s in enumerate(scene_items):\r\n        source = S.obs_sceneitem_get_source(s)\r\n        name = S.obs_source_get_name(source)\r\n        order.append({\"index\": i, \"name\": name, \"scene_item\": s})\r\n    return order\r\n\r\n\r\ndef reorder():\r\n    current_scene = S.obs_frontend_get_current_scene()\r\n    with scene_ar(current_scene) as scene:\r\n        with scene_enum(scene) as scene_items:\r\n            order = get_order(scene_items)\r\n            # change second index with pre last\r\n            order[1][\"index\"], order[-2][\"index\"] = (\r\n                order[-2][\"index\"],\r\n                order[1][\"index\"],\r\n            )\r\n            for s in sorted(order, key=lambda i: i[\"index\"]):\r\n                S.obs_sceneitem_set_order_position(s[\"scene_item\"], s[\"index\"])\r\n\r\n```\r\n[Full source](src/change_order.py)\r\n\r\n# Add scene with sources to current scene\r\n```python\r\ndef add_random_text_source(scene):\r\n    r = \" random text # \" + str(randint(0, 10))\r\n    with data_ar() as settings:\r\n        S.obs_data_set_string(settings, \"text\", f\"random text value {r}\")\r\n        with source_create_ar(\"text_ft2_source\", f\"random text{r}\", settings) as source:\r\n            pos = S.vec2()\r\n            pos.x = randint(0, 1920)\r\n            pos.y = randint(0, 1080)\r\n            scene_item = S.obs_scene_add(scene, source)\r\n            S.obs_sceneitem_set_pos(scene_item, pos)\r\n\r\ndef add_scene_with_sources():\r\n    current_scene_source = S.obs_frontend_get_current_scene()\r\n    with scene_from_source_ar(current_scene_source) as scene_source:\r\n        with scene_create_ar(\"_nested_scene\") as _scene:\r\n            py_scene_source = S.obs_scene_get_source(_scene)\r\n\r\n            with scene_from_source_ar(py_scene_source) as scene:\r\n                add_random_text_source(scene)\r\n                add_random_text_source(scene)\r\n                add_random_text_source(scene)\r\n\r\n            # add created scene to current scene ( nested scene)\r\n            _scene_source = S.obs_scene_get_source(scene)\r\n            S.obs_scene_add(scene_source, _scene_source)\r\n```\r\nNote: sometimes OBS crashes if one of such scenes has been deleted.  \r\n- [Full source](src/add_nested.py)\r\n\r\n# Events\r\n```python\r\ndef on_event(event):\r\n    if event == S.OBS_FRONTEND_EVENT_SCENE_CHANGED:\r\n        raise Exception(\"Triggered when the current scene has changed.\")\r\n\r\n\r\ndef script_load(settings):\r\n    S.obs_frontend_add_event_callback(on_event)\r\n```\r\n[Full source](src/obs_event_exmpl.py)  \r\nSee also:  \r\nhttps://obsproject.com/docs/reference-frontend-api.html#structures-enumerations  \r\n# Program state\r\nThose functions return true or false :\r\n- `S.obs_frontend_preview_program_mode_active()`\r\n- `S.obs_frontend_replay_buffer_active()`\r\n- `S.obs_frontend_recording_active()`\r\n- `S.obs_frontend_recording_paused()`\r\n- `S.obs_frontend_streaming_active()`\r\n# Signals\r\n[Signals](https://obsproject.com/docs/frontends.html#signals) , [callbacks](https://obsproject.com/docs/reference-libobs-callback.html) , [differences from C](https://obsproject.com/docs/scripting.html#other-differences-from-the-c-api)\r\n## Core signals\r\n```python\r\nsh = S.obs_get_signal_handler()\r\nS.signal_handler_connect(sh,\"source_create\",callback)\r\ndef callback(calldata):\r\n    source = S.calldata_source(cd,\"source\")\r\n    print(S.obs_source_get_name(source))\r\n```\r\n**source_create**, **source_destroy**, **source_remove**, **source_save**, **source_load**, **source_activate**, **source_deactivate**, **source_show**, **source_hide**, **source_rename**, **source_volume**, **source_transition_start**, **source_transition_video_stop**, **source_transition_stop**, **channel_change**, **master_volume**, **hotkey_layout_change**, **hotkey_register**, **hotkey_unregister**, **hotkey_bindings_changed** \r\n\r\nhttps://obsproject.com/docs/reference-core.html#core-obs-signals\r\n\r\n## Scene signals\r\n```python\r\ndef connect_cur_scene():\r\n    source = S.obs_frontend_get_current_scene()\r\n    sh = S.obs_source_get_signal_handler(source)\r\n    S.signal_handler_connect(sh, \"item_add\", callback)\r\n    S.obs_source_release(source)\r\n\r\n\r\ndef callback(calldata):\r\n    scene_item = S.calldata_sceneitem(calldata, \"item\")\r\n    #scene = S.calldata_source(cd,\"scene\") # bad utf symbols \r\n    scene =  S.obs_sceneitem_get_scene(scene_item)\r\n    name = S.obs_source_get_name\r\n    source = S.obs_sceneitem_get_source\r\n    scene_source = S.obs_scene_get_source\r\n    scene_name = name(scene_source(scene))\r\n    scene_item_name = name(source(scene_item))\r\n    print(f\"item {scene_item_name} has been added to scene {scene_name}\")\r\n```\r\n- [Full source](src/scene_sig_con.py)\r\n\r\n**item_add**, **item_remove**, **reorder**, **refresh**, **item_visible**, **item_locked**, **item_select**, **item_deselect**, **item_transform**\r\n\r\nhttps://obsproject.com/docs/reference-scenes.html#scene-signals\r\n## Source signals \r\n\r\n```python\r\nsh = S.obs_source_get_signal_handler(some_source)\r\nS.signal_handler_connect(sh,\"show\",callback)\r\ndef callback(calldata):\r\n    source = S.calldata_source(cd,\"source\")\r\n    print(\"on source show\",S.obs_source_get_name(source))\r\n```\r\n\r\n**destroy**, **remove**, **save**, **load**, **activate**, **deactivate**, **show**, **hide**, **mute**, **push_to_mute_changed**, **push_to_mute_delay**, **push_to_talk_changed**, **push_to_talk_delay**, **enable**, **rename**, **volume**, **update_properties**, **update_flags**, **audio_sync**, **audio_mixers**, **filter_add**, **filter_remove**, **reorder_filters**, **transition_start**, **transition_video_stop**, **transition_stop**, **media_started**, **media_ended**, **media_pause**, **media_play**, **media_restart**, **media_stopped**, **media_next**, **media_previous**, **update**, **hooked**, **unhooked**\r\n\r\nhttps://obsproject.com/docs/reference-sources.html#source-signals\r\n\r\n## Output signals \r\n```python\r\ndef connect_to_rec():\r\n    sh = S.obs_output_get_signal_handler(S.obs_frontend_get_recording_output())\r\n    S.signal_handler_connect(sh, \"pause\", callback)\r\n\r\ndef callback(calldata):\r\n    #out = S.calldata_ptr(calldata, \"output\") # bad type\r\n    print('output paused')\r\n```\r\n\r\n**start**, **stop**, **pause**, **unpause**, **starting**, **stopping**, **activate**, **deactivate**, **reconnect**, **reconnect_success**\r\n\r\nhttps://obsproject.com/docs/reference-outputs.html#output-signals\r\n\r\n# Timers and threads\r\n\r\n```python\r\ndef script_update(settings):\r\n    eg.source_name = S.obs_data_get_string(settings, \"source\")\r\n    S.timer_remove(eg.update_text)\r\n    if eg.source_name != \"\":\r\n        S.timer_add(eg.update_text, 1 * 1000)\r\n```\r\n[Full source](src/example_class.py)  \r\nNote: each time script updated it's removed first  \r\nSee also :   \r\n[Version](src/start_stop_timer.py) with globals and only one timer allowed.  \r\nhttps://obsproject.com/docs/scripting.html#script-timers  \r\n## Thread\r\n```python\r\ndef callback(pressed):\r\n    if pressed:\r\n        toggle_thread()\r\n\r\n\r\ndef busy_thread():\r\n    while True:\r\n        if not data.thread_paused:\r\n            sleep(0.02)\r\n            data.status = \"active\"\r\n            # print to stdoud crashes OBS on exit\r\n        else:\r\n            sleep(0.5)\r\n            data.status = \"inactive\"\r\n\r\n\r\nprint('Press the \"~\" to toggle on/off')\r\nhook(\"OBS_KEY_ASCIITILDE\", \"id_\", callback)\r\nS.timer_add(lambda: print(data.status), 500)\r\nt = threading.Thread(target=busy_thread)\r\nt.start()\r\n```\r\n- [Full source](src/busy_thread.py)\r\n\r\n# Hotkeys\r\nThis hotkey example will create hotkeys in settings , but you need to bind it manually.\r\n```python\r\nclass Hotkey:\r\n    def __init__(self, callback, obs_settings, _id):\r\n        self.obs_data = obs_settings\r\n        self.hotkey_id = S.OBS_INVALID_HOTKEY_ID\r\n        self.hotkey_saved_key = None\r\n        self.callback = callback\r\n        self._id = _id\r\n\r\n        self.load_hotkey()\r\n        self.register_hotkey()\r\n        self.save_hotkey()\r\n\r\n...\r\n\r\nclass h:\r\n    htk_copy = None  # this attribute will hold instance of Hotkey\r\n\r\n...\r\nh1 = h()\r\nh2 = h()\r\n...\r\ndef script_load(settings):\r\n    h1.htk_copy = Hotkey(cb1, settings, \"h1_id\")\r\n    h2.htk_copy = Hotkey(cb2, settings, \"h2_id\")\r\n\r\n\r\ndef script_save(settings):\r\n    h1.htk_copy.save_hotkey()\r\n    h2.htk_copy.save_hotkey()\r\n```\r\nThis hotkey example will create hotkeys on fly from json settings , but you need to know internal id.\r\n```python\r\nID = \"htk_id\"\r\nJSON_DATA = '{\"%s\":[{\"key\":\"OBS_KEY_1\"}]}' % ID\r\n\r\ndef on_obs_key_1(pressed):\r\n    if pressed:\r\n        raise Exception(\"hotkey 1 pressed\")\r\n\r\ndef script_load(settings):\r\n    s = S.obs_data_create_from_json(JSON_DATA)\r\n    a = S.obs_data_get_array(s, ID)\r\n    h = S.obs_hotkey_register_frontend(ID, ID, on_obs_key_1)\r\n    S.obs_hotkey_load(h, a)\r\n```\r\nHere is how send hotkey to OBS\r\n\r\n```python\r\ndef send_hotkey(obs_htk_id, key_modifiers=None):\r\n    if key_modifiers:\r\n        shift = key_modifiers.get(\"shift\")\r\n        control = key_modifiers.get(\"control\")\r\n        alt = key_modifiers.get(\"alt\")\r\n        command = key_modifiers.get(\"command\")\r\n    ...\r\n    combo = S.obs_key_combination()\r\n    combo.modifiers = modifiers\r\n    combo.key = S.obs_key_from_name(obs_htk_id)\r\n    ...\r\n    S.obs_hotkey_inject_event(combo, False)\r\n    S.obs_hotkey_inject_event(combo, True)\r\n    S.obs_hotkey_inject_event(combo, False)\r\n```\r\n\r\n- [Full source](src/obs_httkeys.py) \r\n- [Example with json](src/hotkey_json.py)\r\n- [Example with send hotkey](src/send_hotkey.py)\r\n- [Example with global ](src/hotkey_exmpl.py) - The use of `global` is not recommended as it reduces readability.\r\n- [Example with many hotkeys](src/hotkey_many.py) - Implementation of keyboard, easily add many hotkeys to the program.\r\n\r\nSee also:\r\nhttps://github.com/obsproject/obs-studio/blob/master/libobs/obs-hotkeys.h\r\nhttps://github.com/Palakis/obs-websocket/pull/595\r\n\r\n# Play sound\r\n```python\r\ndef play_sound():\r\n    ...\r\n    mediaSource = S.obs_source_create_private(\r\n        \"ffmpeg_source\", \"Global Media Source\", None\r\n    )\r\n    s = S.obs_data_create()\r\n    S.obs_data_set_string(s, \"local_file\", script_path() + \"alert.mp3\")\r\n    S.obs_source_update(mediaSource, s)\r\n    S.obs_source_set_monitoring_type(\r\n        mediaSource, S.OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT\r\n    )\r\n    ...\r\n```\r\n- [Full source](src/play_sound_globally.py)\r\n\r\n# Read and write private data from scripts or plugins\r\nWrite in one script\r\n```python \r\ndef send_to_private_data(data_type, field, result):\r\n    settings = S.obs_data_create()\r\n    set = getattr(obs, f\"obs_data_set_{data_type}\")\r\n    set(settings, field, result)\r\n    S.obs_apply_private_data(settings)\r\n    S.obs_data_release(settings)\r\n\r\ndef write_private_data():\r\n    result = \"private value from \" + str(__file__) + \" \" + str(randint(1, 10))\r\n    send_to_private_data(\"string\", \"__private__\", result)\r\n```\r\nRead from another \r\n```python\r\n@contextmanager\r\ndef p_data_ar(data_type, field):\r\n    settings = S.obs_get_private_data()\r\n    get = getattr(obs, f\"obs_data_get_{data_type}\")\r\n    try:\r\n        yield get(settings, field)\r\n    finally:\r\n        S.obs_data_release(settings)\r\n\r\ndef print_private_data():\r\n    with p_data_ar(\"string\", \"__private__\") as value:\r\n        print(value)\r\n```\r\nLua is also supported\r\n```lua\r\nlocal obs = obslua\r\nlocal settings = S.obs_data_create()\r\nS.obs_data_set_int(settings,\"__private__\", 7)\r\nS.obs_apply_private_data(settings)\r\nS.obs_data_release(settings)\r\n```\r\n\r\n- [Full example read](src/read_private_data.py)\r\n- [Full example write](src/write_private_data.py) \r\n\r\n# Browser source interaction\r\n```python\r\ndef send_hotkey_to_browser(source, obs_htk_id, key_modifiers=None, key_up=False):\r\n\r\n    key = S.obs_key_from_name(obs_htk_id)\r\n    vk = S.obs_key_to_virtual_key(key)\r\n    event = S.obs_key_event()\r\n    event.native_vkey = vk\r\n    event.modifiers = get_modifiers(key_modifiers)\r\n    event.native_modifiers = event.modifiers  # https://doc.qt.io/qt-5/qkeyevent.html\r\n    event.native_scancode = vk\r\n    event.text = \"\"\r\n    S.obs_source_send_key_click(source, event, key_up)\r\n\r\n\r\ndef press_tab(*p):\r\n    with source_auto_release(G.source_name) as source:\r\n        send_hotkey_to_browser(source, \"OBS_KEY_TAB\")\r\n        send_hotkey_to_browser(source, \"OBS_KEY_TAB\", key_up=True)\r\n\r\n\r\ndef press_shift_tab(*p):\r\n    with source_auto_release(G.source_name) as source:\r\n        send_hotkey_to_browser(source, \"OBS_KEY_TAB\", {\"shift\": True})\r\n        send_hotkey_to_browser(source, \"OBS_KEY_TAB\", {\"shift\": True}, key_up=True)\r\n\r\n```\r\n- [Full source](src/browser_source_interaction.py)\r\n\r\n# Browser source receive JSON data\r\n```python\r\ncd = S.calldata_create()\r\nph = S.obs_source_get_proc_handler(source)\r\nS.calldata_set_string(cd, \"eventName\", \"my-test-event\")\r\nS.calldata_set_string(cd, \"jsonString\", '{\"key123\": \"\\\\nvalue123\"}')\r\nS.proc_handler_call(ph, \"javascript_event\", cd)\r\nS.calldata_destroy(cd)\r\n```\r\n\r\nPage source code, currently has no permission requirements. \r\n\r\n```javascript\r\n\r\n\u003c!DOCTYPE html\u003e\r\n\r\n\u003cbody style=\"background-color:aquamarine;\"\u003e\r\n   \u003ch1\u003eHTML h1 in body element\u003c/h1\u003e\r\n\u003c/body\u003e\r\n\r\n\u003cscript type=\"text/javascript\"\u003e\r\n  window.addEventListener('my-test-event', function(event) {\r\n    document.body.innerHTML +=(event.detail['key123']);\r\n  })\r\n\u003c/script\u003e\r\n```\r\n\r\n\r\n- [Full source](src/browser_source_json.py)\r\n\r\n# Access source dB volume level\r\nThere is FFI `ctypes` module in Python to wrap native `obs` lib.\r\nHowever,to run it on GNU/Linux you must start obs with `LD_PRELOAD`.\r\n```bash\r\nubsdrive3@usbdrive3:~$ LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libpython3.9.so obs\r\n```\r\nIt might be in different directory, use `find /usr -name 'libpython*' ` to\r\nfind exact location.\r\n\r\n```python\r\nvolmeter_callback_t = CFUNCTYPE(\r\n    None, c_void_p, POINTER(c_float), POINTER(c_float), POINTER(c_float)\r\n)\r\n...\r\nwrap(\r\n    \"obs_volmeter_add_callback\",\r\n    None,\r\n    argtypes=[POINTER(Volmeter), volmeter_callback_t, c_void_p],\r\n)\r\n...\r\n@volmeter_callback_t\r\ndef volmeter_callback(data, mag, peak, input):\r\n    G.noise = float(peak[0])\r\n```\r\n\r\n- [Full source](src/volmeter_via_ffi.py)\r\n\r\n# Get current profile settings via ffi\r\n\r\n```python\r\nwrap(\"obs_frontend_get_profile_config\", POINTER(Config), use_lib=G.obsffi_front)\r\n# const char *config_get_string(config_t *config, const char *section,\r\n#                  const char *name)\r\nwrap(\"config_get_string\", c_char_p, argtypes=[POINTER(Config), c_char_p, c_char_p])\r\nwrap(\"config_num_sections\", c_size_t, argtypes=[POINTER(Config)])\r\nwrap(\"config_get_section\", c_char_p, argtypes=[POINTER(Config), c_size_t])\r\n\r\n\r\ndef output_to_stdout():\r\n    cfg = G.obs_frontend_get_profile_config()\r\n    e = lambda x: x.encode(\"utf-8\")\r\n    s = G.config_get_string(cfg, e(\"SimpleOutput\"), e(\"FilePath\"))\r\n    l = G.config_num_sections(cfg)\r\n    for i in range(l):\r\n        tag = G.config_get_section(cfg, c_size_t(i))\r\n    print(s, l)\r\n```\r\n- [Full source](src/get_config_via_ffi.py)\r\n\r\n# Convert from SWIG type to ctype\r\n```python\r\ncfg = cast(\r\n    c_void_p(int(S.obs_frontend_get_profile_config())), POINTER(Config)\r\n)\r\n```\r\n\r\nNote,that this uses `obspython.obs_frontend_get_profile_config` so there is no need to load additional libraries and write glue ctypes code.\r\n\r\n# Set current stream key\r\n```python \r\nservice = S.obs_frontend_get_streaming_service()\r\nsettings = S.obs_service_get_settings(service)\r\nS.obs_data_set_string(settings, \"key\", _G._my_key)\r\nS.obs_service_update(service, settings)\r\nS.obs_data_release(settings)\r\nS.obs_frontend_save_streaming_service()\r\n```\r\n- [Full source](src/stream_key.py)\r\n\r\n# Raw frames\r\nIt is possible to grab raw frame from source see: [Get video frames programmatically](src/get_source_frame_data_ffi.py) - this is a viable option if you want check frame data in memory, with no plugins or recompilation.\r\nOther methods:\r\n- [`obs-screenshot-plugin`](https://github.com/synap5e/obs-screenshot-plugin/issues/47) - Fork + Windows only, it has `Output to Named Shared Memory Output` among other things.\r\n- [`obs-python`](https://github.com/zooba/obs-python) - Cython obs plugin with raw frames support\r\n- [`obs-virtualcam`](https://obsproject.com/forum/resources/obs-virtualcam.1744/) - Plugin, read webcam frames, [related](https://github.com/obsproject/obs-studio/issues/3635)\r\n- [`obs-rs`](https://github.com/not-matthias/obs-rs) - Uses the decoupled hook implementation of OBS Studio, written in Rust, uses Windows API, possible to port to Python ctypes.\r\n- [`DXcam`](https://github.com/ra1nty/DXcam/issues/100) - Current maintained standalone DirectX high performance screen capture written in Python ctypes. \r\n\r\n# Debug\r\nThere is no stdin therefore you can't use pdb , options are:\r\n- :star: using `print`\r\n- using generated log file (contains information about memory leaks), also accessible via Help\u003eLog Files\u003eView Current Log\r\n- using pycharm remote debugging (localhost)\r\n- using [vscode](https://code.visualstudio.com/docs/python/debugging) attach to the process:\r\n    - Load python extension\r\n    - open script file , `pip install debugpy` , place  `debugpy.breakpoint()` somewhere\r\n        - Run (F5) select configuration ( Attach using Process ID)\r\n        - select obs (on windows `obs64.exe`)\r\n        - View  select Debug Console (ctrl+shift+y) \r\n- [Example debugpy obs ](src/debug_exmpl.py)\r\n\r\n![screenshot](src/assets/debug.png)  \r\n\r\n# Security\r\n- \"Calling home\" - see [`#4044`](https://github.com/obsproject/obs-studio/issues/4044)\r\n- On GNU/Linux there is handy program called `tcpdump`, to run a check against OBS Studio - use this command (it is active on 443 port on start and on end)\r\n```bash\r\n# tcpdump -i \u003cyour_interface_e_g_wifi_or_wire\u003e 'port 443'\r\n```\r\n- Avoid using `sudo` or admin, check hashsums, do backups, do updates, setup a firewall, do hardening, etc...\r\n- There is no confirmation for loading Lua or Python scripts - they can be added/overwritten via .json key\r\n- Also solutions for Python source code obfuscation \u0026 loading from shared (compiled) library do exist. Applying that will make it a bit harder to reverse-engineer your private code.\r\n- Legal info (trademark) [link](https://www.trademarkelite.com/trademark/trademark-owner/Wizards%20of%20OBS%20LLC)\r\n- Privacy policy - https://obsproject.com/privacy-policy  - keyword *downlodable software*\r\n\r\n# Docs and code examples\r\n\r\n- [`Generated export.md`](src/export.md)\r\n\r\nContains all variables and functions available in `obspython` formatted with markdown. Table consist of links to appropriate search terms in OBS Studio repository, links to scripts in `obspython` and `obslua` with each script within GitHub code search.`gs_*` and `matrix_*` functions exluded from that table.\r\n[Export names](src/export_md.py)  \r\n\r\n- [`Generated export including graphics index.csv`](src/export_all.csv) - All variable and function exports.\r\n- [`Generated export libobs for ctypes index.csv`](src/dumps_libobs.csv) - Created using `dumpbin` tool with this command `.\\dumpbin /exports \"C:\\Program Files\\obs-studio\\bin\\64bit\\obs.dll\"` \r\n\r\n# Changes between versions\r\n* 28.0.0 version - Most Python 3 versions will work now, so there is no restriction to 3.6.8\r\n* 28.0.0 version - NO support for Windows 7 \u0026 8, macOS 10.13 \u0026 10.14, Ubuntu 18.04 and all 32-bit operating systems\r\n* 28.0.0 version - Added native support for websocket \r\n* 30.0.0 version - Python 3.11 support for Windows and mac OS\r\n* 30.0.0 version - Lua binary libraries loading fix\r\n* 30.1.0 version - Premultiplied Alpha option for game capture on Windows\r\n\r\n# Links\r\n- [Scripts forum](https://obsproject.com/forum/resources/categories/scripts.5/) , [Github topic `obs-scripts`](https://github.com/topics/obs-scripts) , [Github topic `obs-script`](https://github.com/topics/obs-script) , [Github login `obslua`](https://github.com/search?o=desc\u0026q=obslua+path%3A*.lua\u0026s=indexed\u0026type=code) , [Github login `obspython`](https://github.com/search?o=desc\u0026q=obspython+path%3A*.py\u0026s=indexed\u0026type=code) , [Github keyword `obs` 2024](https://github.com/search?q=%2F%5Bobs%5D%2F+created%3A%3E2024-01-01\u0026type=repositories\u0026s=updated\u0026o=desc)\r\n- [Awesome OBS Studio collection](https://github.com/rse/obs-setup)\r\n- [OBS Studio Repo](https://github.com/obsproject/obs-studio) , [obs-scripting-python.c](https://github.com/obsproject/obs-studio/blob/master/deps/obs-scripting/obs-scripting-python.c)\r\n- [Docs](https://obsproject.com/docs/) , [Docs/scripting](https://obsproject.com/docs/scripting.html) , [Docs/plugins](https://obsproject.com/docs/plugins.html) , [Docs index](https://obsproject.com/docs/genindex.html)\r\n- obspython [Gist](https://gist.github.com/search?l=Python\u0026q=obspython\u0026s=updated) , [Github](https://github.com/search?l=Python\u0026o=desc\u0026q=obspython\u0026s=indexed\u0026type=Code) , [grep.app](https://grep.app/search?q=obspython\u0026filter[lang][0]=Python)\r\n- obslua [Gist](https://gist.github.com/search?l=Lua\u0026o=desc\u0026q=obslua\u0026s=updated) , [Github](https://github.com/search?l=Lua\u0026o=desc\u0026q=obslua\u0026s=indexed\u0026type=Code) , [grep.app](https://grep.app/search?q=obslua\u0026filter[lang][0]=Lua)\r\n- [Lua tips and tricks](https://obsproject.com/forum/threads/tips-and-tricks-for-lua-scripts.132256/)\r\n- [Python 3.11.9, 64-bit installer, for Microsoft Windows](https://www.python.org/downloads/release/python-3119/)\r\n# Contribute\r\nSomething missing? Write a PR!\r\n","funding_links":[],"categories":["Python"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FupgradeQ%2FStreaming-Software-Scripting-Reference","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FupgradeQ%2FStreaming-Software-Scripting-Reference","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FupgradeQ%2FStreaming-Software-Scripting-Reference/lists"}