Advanced Scene Switcher

Advanced Scene Switcher 1.32.5

Warmuptill

Active Member
Hi @Warmuptill ,

Version 1.32.4 resolved the issue I described earlier. However, I have now detected that it is no longer possible to correctly save "scene item visibility" actions when the item is a vertical scene inserted as a source in another vertical scene.

I have also detected that when creating actions to enable/disable filters in vertical scenes, the Advanced Scene Switcher does not relate the filters to the respective vertical scene.

I performed the tests on Windows 11 Pro 25H2 and also on Linux Mint 22.2 XFCE, and the results were identical.
Thanks for letting me know!
I will look into it.

Is there any possibility to save the login with twitch permanently? in streamer.bot it works. but in adv scene switcher, I need every 1 or 2 month renew the login.
Hm .. interesting.
I wasn't aware of that.
I will investigate the problem and try to resolve it.

Thanks for letting me know about this!
 
Same thing: I installed it. When I opened it, everything froze in Obs. Nothing worked. I opened it again in safe mode, clicked a few things then reopened it in normal mode. It works fine! Thanks!
 

Warmuptill

Active Member
Hi @Warmuptill ,

Version 1.32.4 resolved the issue I described earlier. However, I have now detected that it is no longer possible to correctly save "scene item visibility" actions when the item is a vertical scene inserted as a source in another vertical scene.

I have also detected that when creating actions to enable/disable filters in vertical scenes, the Advanced Scene Switcher does not relate the filters to the respective vertical scene.

I performed the tests on Windows 11 Pro 25H2 and also on Linux Mint 22.2 XFCE, and the results were identical.
A build with a fix will be available here in a few minutes:
Note that you will have to be logged into GitHub to be able to download it at the bottom of the page.
If that should be a problem feel free to reach out and I can try to share the build some other way.

Let me know if you face any issues with this test build! :)
 

FabioElizeu

New Member
A build with a fix will be available here in a few minutes:
Note that you will have to be logged into GitHub to be able to download it at the bottom of the page.
If that should be a problem feel free to reach out and I can try to share the build some other way.

Let me know if you face any issues with this test build! :)
I tested the Linux and Windows versions, and both worked.

Thank you very much!!!
 

Akinraze

New Member
Is Advanced Scene Switcher capable of doing what this guy has (<--- Link) in the bottom left corner of his streams? I looked for a Push To Talk type plugin that can manage multiple button presses simultaneously and the only one I found (<--- Link) requires Python coding (which is way over my head).

I tried the built in hotkey feature and that only enabled the source (would required two hotkeys to then turn off). I tried a basic macro, but that was a Push to start only or a push toggle on then again to toggle off.

Here is an example what I am trying to achieve. The left controller I will have on screen and same for mouse. As I depress buttons, they will turn red.

example.gif


Like this
Untitled2.png


Then revert to unilluminated state once the button is released.
 

Attachments

  • example.jpg
    example.jpg
    239 KB · Views: 4

Akinraze

New Member
Grok is the best. I asked it to rewrite that Python script to accommodate 27 buttons and within 20 seconds I had what I was looking for. I just had to troubleshoot a few minutes to determine how to resolve some conflict issues, ask Grok to adjust the script and now I am set!

Here is the new Python script for anyone hung-up on one button. If you need more, ask Grok to rewrite it for you and mention how many you need it to be.

Python:
import obspython as obs
import ntpath

NUM_BUTTONS = 27

# Per-button configuration
button_sources = [None] * NUM_BUTTONS        # Source name for each button
button_inverts = [False] * NUM_BUTTONS       # Invert flag per button
hotkey_ids = [obs.OBS_INVALID_HOTKEY_ID] * NUM_BUTTONS
hotkey_names = [""] * NUM_BUTTONS

# Global state for reference counting
active_counts = {}       # source_name -> int: number of pressed hotkeys affecting it
target_visibility = {}   # source_name -> bool: desired visibility when count > 0
rest_visibility = {}     # source_name -> bool: desired visibility when count == 0


def script_description():
    return "<b>Push to Enable - 27 Buttons (with Reference Counting)</b>\n\n" \
           "Each hotkey enables (or disables if inverted) its assigned source while held.\n" \
           "Multiple hotkeys can control the same source — it stays active as long as " \
           "at least one assigned hotkey is pressed.\n\n" \
           "Settings can be changed without interrupting active hotkeys."


def script_load(settings):
    print("--- " + ntpath.basename(__file__) + " loaded ---")

    for i in range(NUM_BUTTONS):
        hk_name = f"push_to_enable_button_{i+1}"
        hk_desc = f"Push to Enable Button {i+1}"
        hotkey_names[i] = hk_name

        hotkey_ids[i] = obs.obs_hotkey_register_frontend(
            hk_name, hk_desc,
            lambda pressed, idx=i: hotkey_callback(pressed, idx)
        )

        saved_array = obs.obs_data_get_array(settings, hk_name)
        if saved_array is not None:
            obs.obs_hotkey_load(hotkey_ids[i], saved_array)
            obs.obs_data_array_release(saved_array)


def script_save(settings):
    for i in range(NUM_BUTTONS):
        save_array = obs.obs_hotkey_save(hotkey_ids[i])
        obs.obs_data_set_array(settings, hotkey_names[i], save_array)
        obs.obs_data_array_release(save_array)


def script_properties():
    props = obs.obs_properties_create()

    for i in range(NUM_BUTTONS):
        # Source dropdown
        list_prop = obs.obs_properties_add_list(
            props, f"source_{i}", f"Button {i+1} Source",
            obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING
        )
        obs.obs_property_list_add_string(list_prop, "(none)", "")

        sources_list = obs.obs_enum_sources()
        if sources_list:
            for src in sources_list:
                name = obs.obs_source_get_name(src)
                obs.obs_property_list_add_string(list_prop, name, name)
            obs.source_list_release(sources_list)

        # Invert checkbox
        obs.obs_properties_add_bool(props, f"invert_{i}", f"Invert Button {i+1} (hide while pressed)")

    return props


def script_update(settings):
    global button_sources, button_inverts
    global active_counts, target_visibility, rest_visibility

    # Read new configuration
    new_sources = []
    new_inverts = []
    for i in range(NUM_BUTTONS):
        src_name = obs.obs_data_get_string(settings, f"source_{i}")
        src_name = None if src_name == "" or src_name == "(none)" else src_name
        new_sources.append(src_name)
        new_inverts.append(obs.obs_data_get_bool(settings, f"invert_{i}"))

    # Track old sources for cleanup
    old_sources = set(active_counts.keys())

    # Update button config
    button_sources = new_sources
    button_inverts = new_inverts

    # Rebuild visibility mappings
    target_visibility.clear()
    rest_visibility.clear()

    for i in range(NUM_BUTTONS):
        src = button_sources[i]
        if src is None:
            continue
        invert = button_inverts[i]
        target_visibility[src] = not invert   # Visible when active (normal), hidden if inverted
        rest_visibility[src] = invert         # Hidden at rest (normal), visible if inverted

    # Update active_counts for currently assigned sources
    current_sources = set(target_visibility.keys())
    for src in current_sources:
        if src not in active_counts:
            active_counts[src] = 0  # New source, start at 0

    # Clean up sources no longer assigned to any button
    removed_sources = old_sources - current_sources
    for src in removed_sources:
        # If count is already 0, force rest state (safe to hide)
        if active_counts[src] == 0:
            set_source_visibility(src, False)
        # If count > 0, leave it alone — some hotkey is still conceptually held
        del active_counts[src]

    # IMPORTANT: Do NOT force visibility changes on currently assigned sources
    # This preserves visibility when holding hotkeys while changing settings


def hotkey_callback(pressed, idx):
    src_name = button_sources[idx]
    if src_name is None:
        return

    # Ensure source is in our tracking dicts (in case config changed mid-use)
    if src_name not in active_counts:
        active_counts[src_name] = 0
        # Use current button's invert setting as fallback
        target_visibility[src_name] = not button_inverts[idx]
        rest_visibility[src_name] = button_inverts[idx]

    if pressed:
        prev_count = active_counts[src_name]
        active_counts[src_name] += 1
        # Only change visibility if transitioning from 0 to 1
        if prev_count == 0:
            set_source_visibility(src_name, target_visibility.get(src_name, False))
    else:
        if active_counts[src_name] > 0:
            active_counts[src_name] -= 1
        # Only change visibility if dropping to 0
        if active_counts[src_name] == 0:
            set_source_visibility(src_name, rest_visibility.get(src_name, False))


def set_source_visibility(source_name, visible):
    if source_name is None:
        return

    scene_sources = obs.obs_frontend_get_scenes()
    if scene_sources is None:
        return

    for scn_src in scene_sources:
        scene = obs.obs_scene_from_source(scn_src)
        items = obs.obs_scene_enum_items(scene)
        if items:
            for item in items:
                item_src = obs.obs_sceneitem_get_source(item)
                if obs.obs_source_get_name(item_src) == source_name:
                    if obs.obs_sceneitem_visible(item) != visible:
                        obs.obs_sceneitem_set_visible(item, visible)
            obs.sceneitem_list_release(items)
    obs.source_list_release(scene_sources)
 
Top