obs = obslua -- Estado global local prefix_config = {} local available_source_types = {} -- Prefixos padrão local default_prefixes = { ffmpeg_source = "[VID]", image_source = "[IMG]", browser_source = "[WEB]", dshow_input = "[CAM]", wasapi_input_capture = "[AID]", wasapi_output_capture = "[AOD]", text_gdiplus = "[TXT]", text_gdiplus_v2 = "[TXT]", text_gdiplus_v3 = "[TXT]", text_ft2_source = "[TXT]", monitor_capture = "[DC]", window_capture = "[WC]", game_capture = "[GC]", color_source = "[COL]", color_source_v2 = "[COL]", color_source_v3 = "[COL]", slideshow = "[SLD]", vlc_source = "[VLC]", scene = "[SCN]", group = "[GRP]" } -- Categorias de fontes local source_categories = { common = { "ffmpeg_source", "image_source", "browser_source", "text_gdiplus", "text_gdiplus_v2", "text_gdiplus_v3", "text_ft2_source", "text_ft2_source_v2", "color_source", "color_source_v2", "color_source_v3", "slideshow", "vlc_source", "scene", "group" }, windows = { "dshow_input", "wasapi_input_capture", "wasapi_output_capture", "monitor_capture", "window_capture", "game_capture" }, macos = { "av_capture_input", "coreaudio_input_capture", "coreaudio_output_capture", "display_capture", "syphon-input" }, linux = { "v4l2_input", "pulse_input_capture", "pulse_output_capture", "alsa_input_capture", "jack_output_capture", "xcomposite_input", "xshm_input" } } -- Lista de tipos conhecidos local function get_default_source_types() return { {id = "ffmpeg_source", name = "Media Source"}, {id = "image_source", name = "Image"}, {id = "browser_source", name = "Browser"}, {id = "dshow_input", name = "Video Capture Device"}, {id = "wasapi_input_capture", name = "Audio Input Capture"}, {id = "wasapi_output_capture", name = "Audio Output Capture"}, {id = "text_gdiplus", name = "Text (GDI+)"}, {id = "text_gdiplus_v2", name = "Text (GDI+) v2"}, {id = "text_gdiplus_v3", name = "Text (GDI+) v3"}, {id = "text_ft2_source", name = "Text (FreeType 2)"}, {id = "text_ft2_source_v2", name = "Text (FreeType 2) v2"}, {id = "monitor_capture", name = "Display Capture"}, {id = "window_capture", name = "Window Capture"}, {id = "game_capture", name = "Game Capture"}, {id = "color_source", name = "Color Source"}, {id = "color_source_v2", name = "Color Source v2"}, {id = "color_source_v3", name = "Color Source v3"}, {id = "slideshow", name = "Image Slide Show"}, {id = "vlc_source", name = "VLC Video Source"}, {id = "scene", name = "Scene"}, {id = "group", name = "Group"}, {id = "spout_capture", name = "Spout2 Capture"}, {id = "ndi_source", name = "NDI Source"}, {id = "source-clone", name = "Source Clone"}, {id = "av_capture_input", name = "Video Capture Device (macOS)"}, {id = "coreaudio_input_capture", name = "Audio Input Capture (macOS)"}, {id = "coreaudio_output_capture", name = "Audio Output Capture (macOS)"}, {id = "display_capture", name = "Display Capture (macOS)"}, {id = "syphon-input", name = "Syphon Client (macOS)"}, {id = "v4l2_input", name = "Video Capture Device (V4L2)"}, {id = "pulse_input_capture", name = "Audio Input Capture (PulseAudio)"}, {id = "pulse_output_capture", name = "Audio Output Capture (PulseAudio)"}, {id = "alsa_input_capture", name = "Audio Input Capture (ALSA)"}, {id = "jack_output_capture", name = "Audio Output Capture (JACK)"}, {id = "xcomposite_input", name = "Window Capture (Xcomposite)"}, {id = "xshm_input", name = "Screen Capture (XSHM)"} } end -- Escaneia tipos de fonte do OBS local function scan_source_types() local type_map = {} local types = {} for _, source_type in ipairs(get_default_source_types()) do type_map[source_type.id] = true table.insert(types, source_type) end local sources = obs.obs_enum_sources() if sources then for _, source in ipairs(sources) do local source_id = obs.obs_source_get_id(source) local display_name = obs.obs_source_get_display_name(source_id) if not type_map[source_id] then type_map[source_id] = true table.insert(types, {id = source_id, name = display_name}) end end obs.source_list_release(sources) end local scenes = obs.obs_frontend_get_scenes() if scenes then for _, scene_source in ipairs(scenes) do local scene = obs.obs_scene_from_source(scene_source) local scene_items = obs.obs_scene_enum_items(scene) if scene_items then for _, scene_item in ipairs(scene_items) do local source = obs.obs_sceneitem_get_source(scene_item) local source_id = obs.obs_source_get_id(source) local display_name = obs.obs_source_get_display_name(source_id) if not type_map[source_id] then type_map[source_id] = true table.insert(types, {id = source_id, name = display_name}) end end obs.sceneitem_list_release(scene_items) end end obs.source_list_release(scenes) end table.sort(types, function(a, b) return a.name < b.name end) return types end -- Organiza tipos por categoria local function categorize_sources(source_types) local categories = { common = {}, windows = {}, macos = {}, linux = {}, plugins = {} } local function is_in_category(source_id, category_name) for _, id in ipairs(source_categories[category_name]) do if source_id == id then return true end end return false end for _, source_type in ipairs(source_types) do local categorized = false for _, category_name in ipairs({"common", "windows", "macos", "linux"}) do if is_in_category(source_type.id, category_name) then table.insert(categories[category_name], source_type) categorized = true break end end if not categorized then table.insert(categories.plugins, source_type) end end return categories end -- Verifica se tem prefixo local function has_prefix(name) return string.match(name, "^%[.-%]") ~= nil end -- Remove prefixo local function remove_prefix(name) return string.gsub(name, "^%[.-%]%s*", "") end -- Aplica prefixo local function apply_prefix(name, prefix) local clean_name = remove_prefix(name) return prefix .. " " .. clean_name end -- Pega todas as fontes local function get_all_sources() local processed = {} local sources_list = {} local all_sources = obs.obs_enum_sources() if all_sources then for _, source in ipairs(all_sources) do local name = obs.obs_source_get_name(source) if not processed[name] then table.insert(sources_list, source) processed[name] = true end end obs.source_list_release(all_sources) end local scenes = obs.obs_frontend_get_scenes() if scenes then for _, scene_source in ipairs(scenes) do local scene = obs.obs_scene_from_source(scene_source) local scene_items = obs.obs_scene_enum_items(scene) if scene_items then for _, scene_item in ipairs(scene_items) do local source = obs.obs_sceneitem_get_source(scene_item) local name = obs.obs_source_get_name(source) if not processed[name] then table.insert(sources_list, source) processed[name] = true end end obs.sceneitem_list_release(scene_items) end end obs.source_list_release(scenes) end return sources_list end -- Adiciona prefixos local function add_prefixes() local sources = get_all_sources() local renamed = 0 local skipped = 0 print("──────────────────────────────") print("Starting prefix operation...") for _, source in ipairs(sources) do local name = obs.obs_source_get_name(source) local source_id = obs.obs_source_get_id(source) local prefix = prefix_config[source_id] if prefix then local new_name = apply_prefix(name, prefix) obs.obs_source_set_name(source, new_name) renamed = renamed + 1 print(string.format("Renamed [%s]: %s -> %s", source_id, name, new_name)) else skipped = skipped + 1 print(string.format("Skipped [%s]: %s (no prefix configured)", source_id, name)) end end print("──────────────────────────────") print("Prefixing complete!") print("Sources renamed: " .. renamed) print("Sources skipped: " .. skipped) print("──────────────────────────────") return renamed, skipped end -- Remove prefixos local function remove_all_prefixes() local sources = get_all_sources() local removed = 0 print("──────────────────────────────") print("Starting prefix removal...") for _, source in ipairs(sources) do local name = obs.obs_source_get_name(source) if has_prefix(name) then local clean_name = remove_prefix(name) obs.obs_source_set_name(source, clean_name) removed = removed + 1 print(string.format("Removed prefix: %s -> %s", name, clean_name)) end end print("──────────────────────────────") print("Prefix removal complete!") print("Prefixes removed: " .. removed) print("──────────────────────────────") return removed end -- Adiciona seção de categoria na UI local function add_category_section(props, category_name, sources, header_text) if #sources == 0 then return end obs.obs_properties_add_text(props, "header_" .. category_name, header_text, obs.OBS_TEXT_INFO) for _, source_type in ipairs(sources) do local prop_name = "prefix_" .. source_type.id obs.obs_properties_add_text(props, prop_name, source_type.name .. ":", obs.OBS_TEXT_DEFAULT) end end -- Callbacks do script function script_description() return [[

Add Prefix to Sources by Type

This script dynamically detects all available source types and allows you to configure prefixes for each.

Leave prefix fields blank for source types you don't want to prefix.

Click "Add Prefixes to All Sources" to apply.

]] end function script_properties() local props = obs.obs_properties_create() available_source_types = scan_source_types() local categories = categorize_sources(available_source_types) obs.obs_properties_add_text(props, "info", "Configure Prefixes (leave blank to skip):", obs.OBS_TEXT_INFO) add_category_section(props, "common", categories.common, "── Common Sources ──") add_category_section(props, "windows", categories.windows, "── Windows Sources ──") add_category_section(props, "macos", categories.macos, "── macOS Sources ──") add_category_section(props, "linux", categories.linux, "── Linux Sources ──") add_category_section(props, "plugins", categories.plugins, "── Plugins & Other ──") obs.obs_properties_add_text(props, "separator", "────────────────────────────", obs.OBS_TEXT_INFO) obs.obs_properties_add_button(props, "add_prefixes", "Add Prefixes to All Sources", add_prefixes_clicked) obs.obs_properties_add_button(props, "remove_prefixes", "Remove Prefixes from All Sources", remove_prefixes_clicked) return props end function script_defaults(settings) for source_id, prefix in pairs(default_prefixes) do obs.obs_data_set_default_string(settings, "prefix_" .. source_id, prefix) end end function script_update(settings) prefix_config = {} for _, source_type in ipairs(available_source_types) do local prop_name = "prefix_" .. source_type.id local prefix = obs.obs_data_get_string(settings, prop_name) if prefix ~= "" then prefix_config[source_type.id] = prefix end end end function script_load(settings) available_source_types = scan_source_types() end function add_prefixes_clicked(props, p) add_prefixes() return true end function remove_prefixes_clicked(props, p) remove_all_prefixes() return true end