Skip to content
Permalink
main
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
- Fixed an issue when user checked the checkbox for timer in Stopwatch mode setting reset on "source visible" or "scene active" .
1 contributor

Users who have contributed to this file

--[[
----------------------------------------------------------------------------------------------------------------------------------------
Open Broadcaster Software®️
OBS > Tools > Scripts
@midnight-studios
Stopwatch
***************************************************************************************************************************************
Version 4.10
Published / Released: 2023-02-09 10:00
NEW FEATURES
-
OPTIMIZATION
-
USER EXPERIENCE & FEATURE ENHANCEMENTS
-
BUGS
- Fixed an issue 'Reset Timer on Source Visible'.
- Fixed an issue 'Reset Timer on Scene Active'.
***************************************************************************************************************************************
Version 4.9
Published / Released: 2023-02-09 10:00
NEW FEATURES
-
OPTIMIZATION
-
USER EXPERIENCE & FEATURE ENHANCEMENTS
-
BUGS
- Fixed an issue that caused the time marker function to break.
***************************************************************************************************************************************
Version 4.8
Published / Released: 2023-02-02 10:45
NEW FEATURES
- Custom Minute Format that supports minute formats of any length. To use open Scipt settings go to 'Time Format' and set to 'Custom Time Format' and define the time stamp you require. To adjust the minute format you need to add the following syntax: '{M90}' Add 'M' and number enclosed in '{}' to adjust minute format: {M90} will display 90 minutes units. The number value following the 'M' will be assigned to the Minute Format
OPTIMIZATION
-
USER EXPERIENCE & FEATURE ENHANCEMENTS
-
BUGS
-
***************************************************************************************************************************************
***************************************************************************************************************************************
Version 4.7
Published / Released: 2022-12.29 18:56
NEW FEATURES
- Expanded Add / Subtract seconds to time.
- Alow this feature to be hidden or disabled
- This feature now allows a limit for updating the timer (Zero = infinite)
- A source text note can be defined to notify the user when the limit was reached
- User can define how long the note is displayed (Zero = disables the hide feature)
OPTIMIZATION
- some back end improvements or changes
USER EXPERIENCE & FEATURE ENHANCEMENTS
- Some improvements with conflict prevention for text source selection
- Renamed 'Autoload last time stamp on OBS start'
BUGS
- Fixed some bugs introduced during the previous release
- Fixed an issue that caused timer to crash when OBS shuts down if the script was duplicated (used multiple times) [@Xagika]
- Fixed a bug that would reset the timer if user enables 'auto load end timsestamp'
***************************************************************************************************************************************
Version 4.6
Published / Released: 2022-12.20 12:48
NEW FEATURES
- Allow Timer reset if scene becomes activated
OPTIMIZATION
-
USER EXPERIENCE & FEATURE ENHANCEMENTS
- Start on Scene active improvements
BUGS
- Fixed an issue that caused the stinger transition to break when the timer is in countdown mode.
***************************************************************************************************************************************
Version 4.5
Published / Released: 2022-12.03 01:56
NEW FEATURES
- Add / Subtract seconds to time (3 sets available, configurable up to 72 hours or 259200 seconds)
OPTIMIZATION
-
USER EXPERIENCE & FEATURE ENHANCEMENTS
-
BUGS
-
***************************************************************************************************************************************
Version 4.4
Published / Released: 2022-11.04 19:25
NEW FEATURES
- Change counter direction
- Prevent Negative Time Value (Opt-In)
- Enable / Disable new feature: Change counter direction (Opt-In)
- Hotkey and Button to change counter direction (Supported in Stopwatch and Countdown mode)
OPTIMIZATION
-
USER EXPERIENCE & FEATURE ENHANCEMENTS
-
BUGS
-
***************************************************************************************************************************************
Version 4.3
Published / Released: 2022-10.20 11:51
NEW FEATURES
-
OPTIMIZATION
-
USER EXPERIENCE & FEATURE ENHANCEMENTS
-
BUGS
- Fixed the expired timestamp for 'custom time format'
***************************************************************************************************************************************
Version 4.2
Published / Released: 2022-10.20 21:23
NEW FEATURES
- Add Media Playback for Timer end
OPTIMIZATION
-
USER EXPERIENCE & FEATURE ENHANCEMENTS
- Changed Property Setting label 'Trigger Text' to 'Marker Notes'
BUGS
- Fixed an infinate timer callback loop
- Reverted and applied new Fix for timer Reset issue
- Fixed an issue for recording call when recording is already active
- Fixed switching "split type" does not reset the timer display
- Fixed changing "Mark Time" does not reset the timer display
- Fixed timer display color reset after Mark A & B conditions were met
- Fixed stopping Media playback if media is set to loop when the timer expires
- Fixed a routine issue on sources loaded
- Fixed a timer reset issue (for activate timer on visible)
- Fixed a load 'last saved time' issue (for activate timer on visible)
- Fixed a 'set stopwatch' issue (for activate timer on visible)
***************************************************************************************************************************************
Version 4.1
Published / Released: 2022-10.06 15:23
NEW FEATURES
-
OPTIMIZATION
-
USER EXPERIENCE & FEATURE ENHANCEMENTS
-
BUGS
- Fixed timer Reset issue
***************************************************************************************************************************************
Version 4.0
Published / Released: 2022-10.06 12:26
NEW FEATURES
-
OPTIMIZATION
- Backend updates
USER EXPERIENCE & FEATURE ENHANCEMENTS
-
BUGS
- Fixed a signal handler crash due to an unloaded source
- Fixed a bug that triggered the Media when the timer was not active
***************************************************************************************************************************************
Version 3.9
Published / Released: 2022-10.10 15:15
NEW FEATURES
- Added button Show / Hide Milliseconds
- Added Time Stamp trigger for Show / Hide Milliseconds
- Added Timer text output options (Still not convinvinced this makes sense)
- Added Hotkey for Show / Hide Milliseconds
- Pause Media if timer is paused
- Added option to force visibility of the "Active Source"
OPTIMIZATION
- OBS 28 Supported
- Rebuild timer text options
- Prevented unnecessary Callback Events
- Localized functions to reduce Globals clutter
- Renamed some functions to something more sensible
- Added and updated developer code comments
- Realigned function stacking order
- Renamed Hotkey references
USER EXPERIENCE & FEATURE ENHANCEMENTS
- Added tooltip for Next Scene list
- Added tooltip for Recording list
- Items listed in Cycle Sources list will ignore incorrectly referenced sources.
- User can now define the direction of Cycle Sources
- Cycle Sources (and toggle source visibility) with timer is now fully automated without third party plugins/scripts
- Cycle Sources now support Nested Scenes
- Added new Toggles for settings properties to hide or show certain settings
BUGS
- Fixed a Stack Overflow Crash produced by the Next Scene process.
- Fixed an issue that caused a timer feature to break.
- Fixed a critical memory leak for media sources that caused OBS to crash
- Fixed Media Playback time limit
- Fixed Split time outputs for Stopwatch
----------------------------------------------------------------------------------------------------------------------------------------
]]
--Globals
obs = obslua;
gversion = "4.10";
luafile = "StopWatch.lua";
obsurl = "comprehensive-stopwatch-countdown-timer.1364/";
patch_notes = "Patch Notes";
icon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAAENElEQVQ4jY1UTUgjZxh+ksl/JuMkMYb4F40bNZqK0KJFqBZqS9ddyl76dyhdKPRQShH2sNDSnnopCz11D10KS/dSKNiDoD2I7KXFQ0XSSGpM1llFMYn5mZiMY2IymfIOhgazXfaDj5n53u975vme531fnaqqeMHxJYCvAOgAlABcAyA1jxLO1tYW1tbWoL+Kd3x8jGg0imw2C0VRWkMEYgNgBeAFYKTFRqOh7aVnE9xwFTSZTGJ7exszMzPQ6XSQZRk8z9P7YrVa/Y5hmKLBYHCpqirW63Wcn5/j7OwMHo9HA6bvNqY2mw1Op1N70qaTkxPkcjmbLMsDZrN5hOO4NxuNhlMUxTFiSCA0FEW5GQ6H/wmHwzfamDavUKlUYDKZAoFA4Gue52/r9f/9v6OjQ5uKojwpFAr3RFF8UCwWjW63OzQ/P/9yGyiBnZ6eEtN3eZ7/9XJZrlQqP2cymcf5fL4QDAbHdTrd2yzLXvd4PD9yHHdLEISFXC7nsdvtuTb3c7kcEokEJiYmhliWtaiqWs5ms4f1el0lE2lOTU0hn8/DYrF09vb23jebze9JkvRXNBqdMpvNaIJaLh1tHScAzpvsSd+joyOkUimEQiFNa4vFAlEU4Xa7HwYCgduFQuHRxsbGx5p+qqq+o/7/SF7uQSaTwcHBgZYdgiBMqKqa2dnZ8S8tLaFcLicIIR6PjzU13Qew+gzPKNEj9JJOp5tag+O41/v7+x/v7u7+sLOzc8BxHN1icXR0dMXlcn3xQhW1v7+PSCSC6enptxwOx3WWZRcbjcbTjY2NAJ1nWRYGgwHj4+OqoigFYnr/UlPlClYFwJ1arVYjU8bGxhZ8Pt9KMxiLxd5gGEbTlTSv1WqQJOmJw+G4RqCfPYfkN4qiFDs7O9HT0/Nqa4BhmKd2u10DrFaruLi4oJmncibQSUCrLHJabDlHzItGo1E7FIvFvg+FQjMmkykkCMK9eDwOivl8PvqhBspxXJAOEujfz2HazzBMdXh4OJNMJoupVGre7/cbBEGor6+vY2RkROsLlwY6jUajS5KkSGvtf0oVemUeAPiDgsFgUHMeQJ3MmZycxNzcnMZWkiT4/f67FJRl+UFrmcYB/N7y3UyLSHOBzNjb20MgEMDg4CC6urqwublJZo12d3ffVRRFEQTh4TNTqlQqaawoTShOVdOsqMPDQ8zOzmqFQK3PZrO91NPTs2U0GkmWG4lEYrWt9cViMSwvL1Ntvw9gRafT/aTX6z8AwFKcuhU5zjDMkNfr/XZgYCBKgMfHx3eSyeSqw+Fob9LEipxMp9MRp9P5uclkWuB5/hOKWa3Wvb6+vjLP8wNer5fXUkRRLkql0ofZbPY3ug019TZQ6jKU0AzD7Iqi+Josy6+4XK6P7Hb7LbvdPkS5SXpXKpU/ZVn+5ezs7FG9Xi9brVZNLr1ej38BVDs6EbSfFQsAAAAASUVORK5CYII=";
desc =
[[
<hr/><center><h2>Advanced Stopwatch</h2>( Version: %s )</center>
<br><center><img width=38 height=42 src="]] .. icon .. [["/></center>
<br><center><a href="https://github.com/midnight-studios/obs-lua/blob/main/]] .. luafile ..[[">Find it on GitHub</a></center>
<center><a href="https://obsproject.com/forum/resources/]] .. obsurl ..[[updates">]] .. patch_notes ..[[</a></center>
<br><p>The Properties for this script will adjust visibility as needed. Some advanced properties will only be visible if the Configuration is set to "Advanced". If the Configuration is set to "Basic" the defined values will still be used, so ensure you define those correctly.</p><p>Find help on the <a href="https://obsproject.com/forum/resources/]] .. obsurl ..[[">OBS Forum Thread</a>.</p><hr/>
]];
text_prefix = "";
text_suffix = "";
last_text = "";
custom_time_format = "";
timer_source = "";
countdown_type = "";
backup_folder = "";
import_list = "";
longtimetext_s = "";
longtimetext_p = "";
last_split_data = "";
split_source = "";
active_source = "";
next_scene = "";
stop_text = "";
toggle_mili_trigger = "";
sec_add_1 = "";
sec_add_2 = "";
sec_add_3 = "";
sec_sub_1 = "";
sec_sub_2 = "";
sec_sub_3 = "";
output_file_name = "-backup($date_stamp).json";
font_normal = "#ffffff";
font_dimmed = "#bfbbbf";
font_highlight = "#fffdcf";
add_limit_note_source = "";
sub_limit_note_source = "";
note_source_marker_a = "";
note_source_marker_b = "";
add_limit_note_source_visible = 0;
sub_limit_note_source_visible = 0;
sources_loaded = 0;
timer_manipulation = 1;
sec_add_limit = 0;
sec_add_limit_used = 0;
sec_sub_limit_used = 0;
sec_sub_limit = 0;
total_sources = 0;
sw_hours_saved = 0;
sw_minutes_saved = 0;
sw_seconds_saved = 0;
sw_milliseconds_saved = 0;
split_type = 2;
current_seconds = 0;
cycle_direction = 1;
default_seconds = 0;
split_count = 0;
timer_year = 0;
timer_month = 0;
timer_day = 0;
timer_hours = 0;
timer_minutes = 0;
timer_seconds = 0;
timer_mode = 0;
last_timer_mode = 0;
timer_format = 1;
timer_display = 1;
start_recording = 0;
media_playback_limit = 0;
recording_type = 0;
enable_marker_notes = 1;
orig_time = 0;
time_frequency = 0;
completed_cycles = 0;
ns_last = 0;
cycle_index = 1;
current_count_direction = 1;
timer_cycle = 10; --milliseconds
split_itm = {};
split_data = nil;
minute_format = nil;
local ctx = {
propsDef = nil, -- property definition
propsDefSrc = nil, -- property definition (source scene)
propsSet = nil, -- property settings (model)
propsVal = {}, -- property values
propsValSrc = nil, -- property values (first source scene)
};
props = nil;
timer_mode_changed = false;
set_timer_activated = false;
color_normal_updated = false;
activated = false;
prevent_callback = false;
timer_active = false;
reset_activated = false;
start_on_visible = false;
force_reset_on_visible = false;
force_reset_on_scene_active = false;
active_source_force_visible = false;
start_on_scene_active = false;
disable_script = false;
enable_direction_toggle = false;
show_mili = true;
timer_expired = true;
mili_toggle_triggered = false;
direction_changed = false;
prevent_negative_time = false;
record_timer_set = false;
media = { -- table start
text_marker_b = "",
text_marker_a = "",
source_name_audio_marker_b = "",
source_name_audio_marker_a = "",
source_name_audio_marker_end = "",
note_source_marker_a = "",
note_source_marker_b = "",
note_marker_a = "",
note_marker_b = "",
activated_marker_b = false,
activated_marker_a = false,
current_seconds_marker_a = 0,
current_seconds_marker_b = 0,
duration_marker_a = 0,
duration_marker_b = 0,
duration_marker_end = 0,
media_ended_marker_a = false,
media_ended_marker_b = false,
color_normal = 4294967295, -- 4294967295 0xFFFFFFFF
color_marker_a = 4256749, -- 4256749 0x40f3ed
color_marker_b = 329050, -- 329050 0x05055a
last_state_marker_a = obs.OBS_MEDIA_STATE_NONE,
last_state_marker_b = obs.OBS_MEDIA_STATE_NONE
}; -- table end
selected_source_list = {};
hotkey_id_reset = obs.OBS_INVALID_HOTKEY_ID;
hotkey_id_pause = obs.OBS_INVALID_HOTKEY_ID;
hotkey_id_split = obs.OBS_INVALID_HOTKEY_ID;
hotkey_id_mili = obs.OBS_INVALID_HOTKEY_ID;
hotkey_id_direction = obs.OBS_INVALID_HOTKEY_ID;
hotkey_id_sec_add_1 = obs.OBS_INVALID_HOTKEY_ID;
hotkey_id_sec_add_2 = obs.OBS_INVALID_HOTKEY_ID;
hotkey_id_sec_add_3 = obs.OBS_INVALID_HOTKEY_ID;
hotkey_id_sec_sub_1 = obs.OBS_INVALID_HOTKEY_ID;
hotkey_id_sec_sub_2 = obs.OBS_INVALID_HOTKEY_ID;
hotkey_id_sec_sub_3 = obs.OBS_INVALID_HOTKEY_ID;
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: A function named script_description returns the description shown to
the user
Credit: OBS
Modified: User dependent
function: Script Description
type: OBS Core
input type: data
returns: string
----------------------------------------------------------------------------------------------------------------------------------------
]]
function script_description()
return string.format( desc, tostring( gversion ) );
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Dumps input to string, if input is a table it returns the expanded table
Credit: et al
Modified: yes
function:
type: Support (debug tool)
input type: variable
returns: string
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function pre_dump( input )
if type( input ) == "table" then
local str = "{ ";
for key, value in pairs( input ) do
if type( key ) ~= "number" then key = "'" .. key .. "'" end;
str = str .. "[" .. key .. "] = " .. pre_dump( value ) .. ",";
end;
return str .. "} ";
else
return tostring( input );
end;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Use this to create a Script Log Output used in testing
Credit: et al
Modified: No
function:
type: Support (debug tool)
input type: string
returns: print(string)
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function log( name, msg )
if msg ~= nil then
msg = " > " .. tostring( msg );
else
msg = "";
end;
obs.script_log( obs.LOG_DEBUG, tostring( name ) .. msg );
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Builds a table by splitting a string by defined character or sequence of characters marking
the beginning or end of a unit of data. That which delimits, that separates.
Credit: midnight-studios, et al
Modified:
function: breaks string into sections by a reference that is returned in a table
type:
input type: string, delimiter
returns: table
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function explode( str, delim )
local tbl, index;
tbl = {};
index = 0;
if( #str == 1 ) then return {str} end; -- returns a table with the input string as the only value
while true do
local trace_index = string.find( str, delim, index, true ); -- find the next d in the string
if trace_index ~= nil then -- if "not not" found then..
table.insert( tbl, string.sub( str, index, trace_index - 1 ) ); -- Save it in our array.
index = trace_index + 1; -- save just after where we found it for searching next time.
else
table.insert( tbl, string.sub( str, index ) ); -- Save what's left in our array.
break; -- Break at end, as it should be, according to the lua manual.
end;
end;
return tbl;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Gives you an iterator that moves through an
ordinary table (eg. string keys) but sorted
into key sequence.
It does that by copying the table keys into
a temporary table and sorting that.
Possibly being string referenced the list
will be compiled chronologically, thus the
list names (values) may appear unordered and
random. To reorganise and arrange the list
alphabetically we will use pairsByKeys().
This will make it easier for the user to review
and select the desired item from the list.
Credit: https://github.com/nickgammon/mushclient/blob/master/lua/pairsbykeys.lua
https://github.com/nickgammon/mushclient/tree/master/lua
If you need to sort keys other than strings, see:
See: http://lua-users.org/wiki/SortedIteration
Modified: Yes, minor changes
function: support: This prints the math functions in key order
type: sort table
input type: table, function (optional)
returns: table
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function pairsByKeys( tbl, input_function )
if type( tbl ) ~= "table" then return tbl end; -- if the input table is not of type table return input
local temp_tbl = {}; -- build temporary table of the keys
for items in pairs( tbl ) do table.insert( temp_tbl, items ) end;
table.sort( temp_tbl, input_function ); -- sort using supplied function, if any
local i = 0 -- iterator variable
local iter = function () -- iterator function
i = i + 1;
if temp_tbl[i] == nil then return nil;
else return temp_tbl[i], tbl[temp_tbl[i]];
end;
end;
return iter;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Provides the length of a table
(how many items the table contains)
Credit: midnight-studios, et al
Modified: Author
function: Create a table with unique items
type: Support
input type: table
returns: integer
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function tablelength( tbl )
local count = 0;
if type( tbl ) == "table" then -- if the input table is not of type table return 0
for _ in pairs( tbl ) do count = count + 1 end;
end;
return count;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Remove duplicated values from table
Credit: midnight-studios, et al
Modified: Author
function: Create a table with unique items
type: Support
input type: table, string
returns: bool
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function tableHasKey( tbl, key )
if type( tbl ) ~= "table" then return false end; -- if the input table is not of type table return bool(false)
return tbl[key] ~= nil;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Remove duplicated values from table
Credit: midnight-studios, et al
Modified: Author
function: Create a table with unique items
type: Support
input type: table, string
returns: bool
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function in_table( tbl, input_value )
if type( tbl ) ~= "table" then return false end; -- if the input table is not of type table return bool(false)
local found = false; -- set result default bool (not found)
for key, value in pairs( tbl ) do
if value == input_value then -- compare search value against table value
found = true; -- found, update result bool
break; -- found, end and exit here
end;
end;
return found; -- return bool
end
--[[
----------------------------------------------------------
Description:
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------
]]
function refresh_properties()
return true;
end
--[[
----------------------------------------------------------
Description: Remove duplicated values from table
Credit: midnight-studios, et al
Modified: Author
function: Create a table with unique items
type: Support
input type: table
returns: table
----------------------------------------------------------
]]
local function remove_duplicates( tbl )
if type( tbl ) ~= "table" then return table end; -- if the input table is not of type table return input
local hash = {};
local clean_tbl = {};
for _, value in pairsByKeys( tbl ) do
if ( not hash[value] ) then
clean_tbl[#clean_tbl+1] = value; -- you could print here instead of saving to result table if you wanted
hash[value] = true;
end;
end;
return clean_tbl; -- return final result
end
--[[
----------------------------------------------------------
Description: This is basically obs.obs_enum_sources()
but "Nested Scenes" are not listed in "obs.obs_enum_sources()"
Credit: midnight-studios, et al
Modified: Author
function: Used to build a list from OBS source names into a table
type: Support
input type: "id", "unversioned_id", "display_name", "source_name"
returns: table default with "source_name" or define return_ref: "id" or "unversioned_id" or "display_name" or "source_name"
----------------------------------------------------------
]]
function get_source_list( return_ref )
local scenes = obs.obs_frontend_get_scenes();
local source_list = {};
local list = {};
local sub = {};
--[[
]]
if scenes ~= nil then
--[[
]]
for key_scenesource, value_scenesource in pairs( scenes ) do
local scenename = obs.obs_source_get_name( value_scenesource );
local scene = obs.obs_scene_from_source( value_scenesource );
local sceneitems = obs.obs_scene_enum_items( scene );
--[[
]]
local index = 0;
for key_sceneitem, value_sceneitem in pairs( sceneitems ) do
index = index + 1;
sub = {};
local source = obs.obs_sceneitem_get_source( value_sceneitem );
local source_name_parent = obs.obs_source_get_name( source );
local group = obs.obs_group_from_source( source );
local id_parent = obs.obs_source_get_id( source );
local unversioned_id_parent = obs.obs_source_get_unversioned_id( source );
local display_name_parent = obs.obs_source_get_display_name( id_parent );
sub["id"] = id_parent;
sub["unversioned_id"] = unversioned_id_parent;
sub["display_name"] = display_name_parent;
sub["source_name"] = source_name_parent;
list[index] = sub;
source_list[source_name_parent] = source_name_parent; -- will return this by default if return_ref not defined as the name is a unique id
if group ~= nil then
local groupitems = obs.obs_scene_enum_items( group );
if groupitems ~= nil then
for key_groupitem, value_groupitem in pairs( groupitems ) do
index = index + 1;
sub = {};
local groupitemsource = obs.obs_sceneitem_get_source( value_groupitem );
local source_name_group = obs.obs_source_get_name( groupitemsource );
local id_group = obs.obs_source_get_id( groupitemsource );
local unversioned_id_group = obs.obs_source_get_unversioned_id( groupitemsource );
local display_name_group = obs.obs_source_get_display_name( id_group );
sub["id"] = id_group;
sub["unversioned_id"] = unversioned_id_group;
sub["display_name"] = display_name_group;
sub["source_name"] = source_name_group;
list[index] = sub;
source_list[source_name_group] = source_name_group; -- will return this by default if return_ref not defined as the name is a unique id
end -- end for
obs.sceneitem_list_release( groupitems );
end
end
end -- end for in pairs( sceneitems )
obs.sceneitem_list_release( sceneitems );
end -- end for in pairs( scenes )
--[[
]]
obs.source_list_release( scenes );
end; -- scenes ~= nil
--[[
is "return_ref" defined and a valid (existing) reference?
]]
local tmp_list = {};
local found = false;
if return_ref ~= nil then
for key, value in pairs( list ) do
if type( list[key] ) == "table" then
if tableHasKey( list[key], return_ref ) then
found = true;
tmp_list[list[key]["source_name"]] = list[key][return_ref];
end;
end;
end;
end;
if found then source_list = tmp_list end;
return source_list;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Function to convert OBS data array to table
obs_data_array_to_table( settings, "reference" )
Description: Grab OBS data array and return in a table
Credit: midnight-studios
Modified:
function: data array to table
type: Support
input type: Settings, property reference
returns: table
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function obs_data_array_to_table( set, item )
local array = obs.obs_data_get_array( set, item );
local count = obs.obs_data_array_count( array );
local list = {};
for i = 0, count do
local array_item = obs.obs_data_array_item( array, i );
local value = obs.obs_data_get_string( array_item, "value" );
list[i] = value;
end;
obs.obs_data_array_release( array );
return list;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Get the name of this script
Credit: midnight-studios, et al
Modified:
function: regular expression
type: Support
input type: string
returns: string
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function filename()
local str = debug.getinfo(2).source:sub(2);
return str:match("^.*/(.*).lua$") or str;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: List files
Credit: midnight-studios
Modified: midnight-studios, et al
function: Used to list files with target extension
type: directory path, file extension
input type:
returns: table
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function get_filenames( path, file_extension )
local filenames = {};
local dir = obs.os_opendir( path );
local entry;
repeat
entry = obs.os_readdir( dir );
if entry then
local ext = obs.os_get_path_extension( entry.d_name );
if ext == file_extension then
local filename = string.gsub( entry.d_name, ext, "" );
table.insert( filenames, filename );
end
end
until not entry;
obs.os_closedir( dir );
return filenames;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Convert data to json
Credit: midnight-studios, et al
Modified: Yes, custom params to suit targeted need
function:
type: Support
input type: OBS data (Settings)
returns: json file
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function write_to_json( data )
output_folder = backup_folder;
-- convert Windows path to UNIX path
local file_name = filename() .. output_file_name:gsub("$date_stamp", os.date("%Y-%m-%d-%H%M"));
-- set output path as the script path by default
local script_path = script_path();
local output_path = script_path .. file_name;
-- if specified output path exists, then set this as the new output path
if (output_folder ~= "") then
output_path = output_folder .. "/" .. file_name;
else
output_path = script_path .. file_name;
end
output_path = output_path:gsub([[\]], "/");
obs.obs_data_erase( data, "backup_folder" );
obs.obs_data_erase( data, "backup_mode" );
return obs.obs_data_save_json( data, output_path );
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Assign a default Frequency based on the Frame Rate
video_info.base_width
video_info.base_height
video_info.fps_den
video_info.output_width
video_info.output_height
video_info.range
video_info.colorspace
Credit: midnight-studios
Modified:
function: Get obs user defined video frame rate
type: Support
input type: none
returns: double
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function assign_default_frequency()
local fps = 60; -- 60 is the maximum supported frame rate
local video_info = obs.obs_video_info();
if obs.obs_get_video_info(video_info) then
fps = video_info.fps_num;
end;
time_frequency = ( 1/fps );
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description:
Local variables format_hour, format_minutes, format_seconds, format_mili are initialized to the strings that define the format of hour, minutes, seconds and mili based on whether they are present or not.
"time" variable is initialized with the formatted string using string.format and the values of format_hour, format_minutes, format_seconds, format_mili and the input arguments hour, minutes, seconds, and mili.
If show_mili is false, then time is re-initialized with the formatted string using string.format and values of format_hour, format_minutes, format_seconds and input arguments hour, minutes, and seconds.
Return time.
Take the time segments:
Hours, Minutes, Seconds, Millisieconds
Configure to standard format:
HH:MM:SS:FF
$function status: in service
Credit: midnight-studios
Modified:
function: Dependency / Support
type:
input type: 4 variables - "HH" "MM" "SS" "FF"
returns: formatted time string: time stamp 00:00:00,00 (HH:MM:SS,FF)
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function config_time( hour, minutes, seconds, mili )
local format_hour,
format_minutes,
format_seconds,
format_mili =
( hour and "%02d" or "" ),
( minutes and ":%02d" or "" ),
( seconds and ":%02d" or "" ),
( mili and ",%02d" or "" );
local time = string.format( format_hour..format_minutes..format_seconds..format_mili, hour, minutes, seconds, mili );
--[[
configure for SHOW or Hide millisonds
]]
if not show_mili then
time = string.format( format_hour..format_minutes..format_seconds, hour, minutes, seconds );
end;
return time;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Convert Seconds to hours:minutes:seconds:miliseconds
$function status: in service
Local variables hour, minutes, seconds, and mili are initialized to 0.
If time is greater than 86399 (23:59:59), c_time is calculated as the nearest multiple of 86400 that is less than time and time is updated by subtracting c_time from it.
Hour is calculated as the floor division of time by 3600.
If hour is less than 10 and trim is true, hour is updated with a leading zero.
Minutes are calculated based on whether the custom_time_format has a value of 90.
If minutes are greater than or equal to 60, minutes are updated as the remainder after dividing by 90.
If minutes are less than 10 and trim is true, minutes are updated with a leading zero.
Seconds are calculated as the floor value of time minus the product of hour and 3600 and the product of minutes and 60.
If seconds are less than 10 and trim is true, seconds are updated with a leading zero.
Miliseconds are calculated as the floor value of time minus the product of hour and 3600 and the product of minutes and 60 and the value of seconds.
If miliseconds are less than 10 and trim is true, miliseconds are updated with a leading zero.
Local variable "output" is initialized to an empty string.
If simplify is true, "output" is updated with the result of calling config_time with hour, minutes, seconds and nil.
"output" is updated with the result of calling config_time with hour, minutes, seconds, and miliseconds.
Return "output".
Credit:
Modified:
function:
type:
input type: Double (Seconds / Split Seconds)
returns: time stamp 00:00:00,00 (HH:MM:SS,FF)
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function raw_time( time, simplify )
local hour, minutes, seconds, mili = 0, 0, 0, 0;
--[[
If there is more than 24 hours in the time value
we need to remove the additional time value to leave only a 23:59:59
value. We will do this by calculating days
]]
-- If there is more than 24 hours, remove 23:59:59 as it will be in the clock
if time > 86399 then -- 23:59:59
local c_time = ( math.floor( ( time ) / 86400 ) * 86400 );
time = time - c_time;
end;
--[[
]]
hour = math.floor( time/3600 );
if hour < 10 and trim then
hour = "0"..hour;
end;
--[[
check flag: custom_time_format
if flag assign a 90 minute unit then apply it, else use default 60 minute unit
If there is a use case, this could potentially be expanded here but we will have to make sure the code checks out.
]]
if minute_format ~= nil then
minutes = math.floor( ( ( time/3600 ) * 3600 ) / 60 );
minutes = minutes % minute_format;
else
minutes = math.floor( ( time - math.floor( time/3600 )*3600 )/60 );
end
if minutes < 10 and trim then
minutes = "0"..minutes;
end;
--[[
]]
seconds = math.floor( time - math.floor( time/3600 )*3600 - math.floor( ( time - math.floor( time/3600 )*3600 )/60 )*60 );
if seconds < 10 and trim then
seconds = "0"..seconds;
end;
--[[
]]
mili = math.floor( ( time - math.floor( time/3600 )*3600 - math.floor( ( time - math.floor( time/3600 )*3600 )/60 )*60 - math.floor( time - math.floor( time/3600 )*3600 - math.floor( ( time - math.floor( time/3600 )*3600 )/60 )*60 ) )*100 );
if mili < 10 and trim then
mili = "0"..mili;
end
--[[
Use this to see if the time stamp matches certain criteria
This looks at HH:MM:SS only and is used to match the
timer's current time stamp against a user defined time mark that
will for example activate Mark A or Mark B
]]
local output = "";
if simplify then
output = config_time( hour, minutes, seconds, nil );
else
output = config_time( hour, minutes, seconds, mili );
end;
return output;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Used this in testing to measure accuracy
The Text Source and the Log should produce the same value
The Text source is updated by the time function while the debug
uses start and end time stamps to get a value
Credit: midnight-studios
Modified:
function: calculate time difference between two points in time
type: Support
input type: none
returns: double
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function get_time_lapsed()
local ns = obs.os_gettime_ns();
local delta = ( ns/1000000000.0 ) - ( orig_time/1000000000.0 );
return raw_time( delta );
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: The true frequency between cycles varies due to script
and system task processing, therefore a static frequency
will produce inaccuarte results over time.
Start with a default frequency of 1 second devided by
the assigned active fps and then update the frequency
calculated from the difference between cycles for the
previous and current cycle using high-precision system
time, in nanoseconds.
It should be noted, the frequency is based on the
script defined cycle time, which in this case is
10 miliseconds. Based on testing 10 Miliseconds is the
fastest cycle supported in OBS lua.
Credit: midnight-studios
Modified:
function: determine the correct fraction of the split second based on frame rate
type: Support
input type: double
returns: double
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function get_frequency( previous )
local ns = obs.os_gettime_ns();
ns_last = ns;
local f = ( ns/1000000000.0 ) - ( previous/1000000000.0 );
if f > 1 then f = time_frequency end;
return f;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: This was developed because some tasks were not completing
Credit: midnight-studios
Modified:
function: delayed recording task to allow other tasks to complete
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function frontend_recording_start_callback( )
if not record_timer_set then return end;
if not obs.obs_frontend_recording_active() then
obs.obs_frontend_recording_start();
end;
obs.timer_remove( frontend_recording_start_callback );
record_timer_set = false;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: "Timer Expires" = 1
"Marker A Time" = 2
"Marker B Time" = 3
"Timer Visible" = 4
"Timer Start" = 5
Credit: midnight-studios
Modified:
function: Start obs call obs_frontend_recording_start()
type:
input type: reference, milliseconds
returns: none
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function record( mark, ms )
if obs.obs_frontend_recording_active() then -- if already recording, remove and reset timer
frontend_recording_start_callback( );
return;
end;
if timer_mode ~= 2 or obs.obs_frontend_recording_active() then return end; -- if not countdown or timer active, then exit
if start_recording == 1 and mark == recording_type then
if not record_timer_set then
obs.timer_add( frontend_recording_start_callback, ms ); --< milliseconds
record_timer_set = true;
end;
end;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Convert hours:minutes:seconds to Seconds
When the user defines the Hours, Minutes & Seconds
we need to convert it to seconds as the timer works
on the value "seconds"
$function status: in service
Credit: midnight-studios
Modified:
function: convert date, hour, minutes and secods to seconds
type:
input type: interger for date, time
returns: interger (seconds)
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function delta_time( year, month, day, hour, minute, second )
local now = os.time();
if ( year == -1 ) then
year = os.date( "%Y", now );
end;
if ( month == -1 ) then
month = os.date( "%m", now );
end;
if ( day == -1 ) then
day = os.date( "%d", now );
end;
local future = os.time{year=year, month=month, day=day, hour=hour, min=minute, sec=second};
local seconds = os.difftime( future, now );
if ( seconds < 0 ) then
seconds = 0;
end;
return seconds;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Take the raw time format "HH:MM:SS:FF" and allow the user to
define a custom format.
$function status: in service
Credit:
Modified: midnight-studios
function: The timestamp is what we put in, the format is what we want this little princess to be transformed into
type:
input type: 00:00:00,00
returns: Whatever the format incleded: $T $D $H $M $S $F and anything inbetween
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function format_time( timestamp, format )
--[[
table 1, break time stamp in pieces by character reference ":"
input: DD:HH:MM:SS,FF
result: [DD], [HH], [MM], [SS,FF]
]]
local table1 = explode( timestamp, ":" ); -- reference ":" 4 parts
if table1 == nil then return timestamp end; -- have result or return input
local c = tablelength( table1 ); -- measure table parts (there should be 4: [DD], [HH], [MM], [SS,FF])
--[[
This does something fancy that is needed
]]
local _, d = timestamp:gsub(":","");
local _, t = format:gsub("$T","");
local day, hour, minute, seconds, mili = 0, 0, 0, 0, 0; -- start some blank variables that we will need
if d == 3 then -- it should be 3 parts by default if the user uses a standard timestamp
if tableHasKey( table1, 1 ) then -- day
day = table1[1];
end;
if tableHasKey( table1, 2 ) then -- hour
hour = table1[2];
end
if tableHasKey( table1, 3 ) then -- minute
minute = table1[3];
end;
if tableHasKey( table1, 4 ) then -- seconds
seconds = table1[4];
local table2 = explode( table1[4], "," );
if tableHasKey( table2, 1 ) and tableHasKey( table2, 2 ) then -- milliseconds
seconds = table2[1];
mili = table2[2];
end;
end;
end;
if d == 2 then -- okay, the user is doing something fancy now and requested a non-standard timestamp
if tableHasKey( table1, 1 ) then -- hour
hour = table1[1];
end;
if tableHasKey( table1, 2 ) then -- minute
minute = table1[2];
end;
if tableHasKey( table1, 3 ) then -- seconds
seconds = table1[3];
local table2 = explode( table1[3], "," );
if tableHasKey( table2, 1 ) and tableHasKey( table2, 2 ) then -- milliseconds
seconds = table2[1];
mili = table2[2];
end;
end;
end;
if d == 1 then -- okay, the user is doing something fancy now and requested a non-standard timestamp
if tableHasKey( table1, 1 ) then -- minute
minute = table1[1];
end;
if tableHasKey( table1, 2 ) then -- seconds
seconds = table1[2];
local table2 = explode( table1[2], "," );
if tableHasKey( table2, 1 ) and tableHasKey( table2, 2 ) then -- milliseconds
seconds = table2[1];
mili = table2[2];
end ;
end;
end;
if tonumber(day) < 10 then
day = "0"..day;
end;
--[[
Athis stage we have some groups to work with:
TRIM: identified by $T, if this is found it will remove, trim or otherwise zap all zeros
DD: identified by (will replace) $D
HH: identified by (will replace) $H
MM: identified by (will replace) $M
SS: identified by (will replace) $S
FF: identified by (will replace) $F
]]
timestamp = format:gsub("$T", ""):gsub("$D", day):gsub( "$H", hour):gsub("$M", minute):gsub("$S", seconds):gsub("$F", mili);
if not show_mili then
format = format:gsub(",$F", ""):gsub("$F", ""); -- remove these if by default
timestamp = format:gsub("$T", ""):gsub("$D", day):gsub( "$H", hour):gsub("$M", minute):gsub("$S", seconds);
end
--[[
If the user wants leading zeros trimmed
]]
if t ~= 0 and current_seconds > 0.01 then
--local reg = "^[0]+[:]?[0]+[:]?[0]+[:]?[0]?"
local reg = "^[0:,]*" -- close, but misses 1 instance
timestamp = timestamp:gsub(reg, "");
end
--[[
If the user wants end time stamp displayed
]]
if current_seconds < 0.01 and ( timer_display == 1 and timer_mode ~= 1 ) then
if not in_table({1,5}, timer_format ) then timestamp = "0" end; -- the user wants the timer to end with a reminder that it is Game Over
end;
if current_seconds < 0.01 and timer_display == 2 then -- else it will show 00:00:00
timestamp = ""; -- the user wants the timer to disapear
end ;
return timestamp;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description:
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function long_time( time )
local c_time = time;
-- If there is more than 24 hours, remove 23:59:59 as it will be in the clock
if time > 86399 then -- 23:59:59
c_time = math.floor( ( time ) / 86400 );
end;
if time < 86400 then
c_time = 0;
end;
return c_time;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: This function checks if a string contains the characters "{" and "}" and the character "M" followed
by a numeric value
If the string meets these conditions, the function returns the numeric value
Check to see if a user defined a custom time format and if the format defined a minute allocation.
If the user need the minute clock to be for example 90 minutes instead of 60 then the user could add the expression as followes:
{M90} will assign a 90 minute value
Note, the M90 must be inside brackets to be considered.
Credit:
Modified: Asking if miliseconds property must be shown or hidden and this is for back end UI
function: yer, no
type: Support, UI
input type: properties, settings
returns: bool
----------------------------------------------------------------------------------------------------------------------------------------
]]
function get_minutes_allocation( str )
-- Find the first occurrence of a balanced pair of braces in the string
local start_index, end_index = string.find(str, "%b{}")
-- If no balanced pair of braces is found, return nil
if start_index == nil then
return nil
end
-- Extract the substring between the braces
local inside = string.sub(str, start_index + 1, end_index - 1)
-- Find the first occurrence of the letter "M" followed by one or more digits in the substring
local m_index, _ = string.find(inside, "M%d+")
-- If the letter "M" and numeric value is not found, return nil
if m_index == nil then
return nil
end
-- Convert the numeric value following the letter "M" to a number and return the result
return tonumber(string.sub(inside, m_index + 1))
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: This function uses the string.find function to find the first occurrence of a balanced pair of braces (%b{}) in the input string str. If no such pair is found, the function returns the input string as is.
If a pair of braces is found, the function uses string.sub to extract the substrings of str before and after the pair of braces, concatenates them using the .. operator, and returns the result.
Credit:
Modified: Asking if miliseconds property must be shown or hidden and this is for back end UI
function: yer, no
type: Support, UI
input type: properties, settings
returns: bool
----------------------------------------------------------------------------------------------------------------------------------------
]]
function removeBrackets(str)
local start_index, end_index = string.find(str, "%b{}")
if start_index == nil then
return str
end
return string.sub(str, 1, start_index - 1) .. string.sub(str, end_index + 1)
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Callback on properties modification
Show/Hide a field in the properties based on a
some criteria. In this case, show or hide the field
"Toggle Milliseconds" only when required.
Credit:
Modified: Asking if miliseconds property must be shown or hidden and this is for back end UI
function: yer, no
type: Support, UI
input type: properties, settings
returns: bool
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function show_split( props, settings )
local config_value = obs.obs_data_get_int( settings, "config" );
local mode = obs.obs_data_get_int( settings, "timer_mode" );
local shw = false;
shw = ( config_value == 2 and mode == 2 and in_table( {1, 2}, timer_format ) );
if ( timer_format == 5 and config_value == 2 and mode == 2 ) then
if ( string.find( custom_time_format, "$F" ) ~= nil ) then
shw = true;
else
shw = false;
end;
end;
return shw;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Function to set the source text
Credit: et al
Modified:
function: Update Text Source
type: Support, Render
input type: target source by name, contents to be added to text contents
returns: nothing
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function set_text( source_name, text )
if source_name == "Select" or source_name == "select" then
return;
end;
--[[
Increments the source reference counter,
use obs_source_release() to release it when complete.
]]
local source = obs.obs_get_source_by_name( source_name );
if source ~= nil then
local settings = obs.obs_source_get_settings( source );
obs.obs_data_set_string( settings, "text", text );
end;
obs.obs_source_update( source, settings );
obs.obs_data_release( settings );
obs.obs_source_release( source );
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: set source visibility
Credit: midnight-studios, et al
Modified:
function: Update Text Source
type: Support, Render
input type:
returns: bool
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function set_visible( source_name, visible )
if visible == nil then visible = true end;
local action_completed = false;
if in_table( {"","None", "Select","none", "select"}, source_name ) then return action_completed; end;
local scenes = obs.obs_frontend_get_scenes();
if scenes ~= nil then
for i, scn in ipairs( scenes ) do
local scene = obs.obs_scene_from_source( scn );
local sceneitem = obs.obs_scene_find_source_recursive( scene, source_name );
if sceneitem ~= nil then
if visible and not obs.obs_sceneitem_visible( sceneitem ) then -- only set visible if not visible
obs.obs_sceneitem_set_visible( sceneitem, visible );
end
if not visible and obs.obs_sceneitem_visible( sceneitem ) then -- only hide if visible
obs.obs_sceneitem_set_visible( sceneitem, visible );
end;
action_completed = true;
break;
end;
end; --end for
obs.bfree( scn );
obs.source_list_release( scenes );
end;
return action_completed;
end
--[[
----------------------------------------------------------
Description: check source visibility
Credit: midnight-studios, et al
Modified:
function: Check source visibility state by name
type:
input type: source name (string)
returns: boolean
----------------------------------------------------------
]]
local function is_visible( source_name )
local isvisible = false;
local scenes = obs.obs_frontend_get_scenes();
if scenes ~= nil then
for i, scn in ipairs( scenes ) do
local scene = obs.obs_scene_from_source( scn );
local sceneitem = obs.obs_scene_find_source_recursive( scene, source_name );
if sceneitem ~= nil then
isvisible = obs.obs_sceneitem_visible( sceneitem );
break;
end;
end; --end for
obs.bfree( scn );
obs.source_list_release( scenes );
end; --end scenes ~= nil
return isvisible;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Set source visibility to hidden
Credit:
Modified:
function: a callback for a timer used to set a source visibility to hidden
type:
input type: none
returns: none
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function marker_a_media_end_callback( )
set_visible( media["source_name_audio_marker_a"], false );
obs.remove_current_callback();
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Set source visibility to hidden
Credit:
Modified:
function: a callback for a timer used to set a source visibility to hidden
type:
input type: none
returns: none
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function marker_b_media_end_callback( )
set_visible( media["source_name_audio_marker_b"], false );
obs.remove_current_callback();
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description:
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function signal_media_ended( cd )
--[[
Get source from CallData
]]
local source = obs.calldata_source( cd, "source" );
--[[
Found Source?
]]
if source ~= nil then
local name = obs.obs_source_get_name( source );
--[[
Set Source Visibility to Hidden
]]
set_visible( name, false );
obs.remove_current_callback();
end
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description:
Credit: OBS, Source Signals, https://obsproject.com/docs/reference-sources.html?highlight=media_ended
Modified:
function:
type: Support
input type: ref
returns: signal_media_ended
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function disconnect_after_media_end( ref )
local source_name = media["source_name_audio_".. ref];
local source = obs.obs_get_source_by_name( source_name ); -- Increments the source reference counter, use obs_source_release() to release it when complete. --[[
--[[
Found Source?
]]
if source ~= nil then
local source_id = obs.obs_source_get_unversioned_id( source ); -- get source id
if source_id == "ffmpeg_source" then -- check if source id match that of type we need to focus on
--[[
Create a signal handler for the source
]]
local sh = obs.obs_source_get_signal_handler( source );
--[[
https://obsproject.com/docs/reference-sources.html?highlight=media_started
attach event listener callback [source_signal]: Called when media has ended.
]]
obs.signal_handler_connect( sh, "media_ended", signal_media_ended );
end;
end;
obs.obs_source_release( source );
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description:
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function start_media_action( source_name, ref )
if in_table( {"","None", "Select","none", "select"}, source_name ) then return end;
if not media["activated_".. ref] then
media["current_seconds_".. ref] = math.ceil( current_seconds );
set_visible( source_name, true );
--[[
connect signal handler to ensure we reset the source if the media ended.
]]
disconnect_after_media_end( ref );
media["activated_".. ref] = true;
end
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description:
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function start_media( source_name, ref )
start_media_action( source_name, ref );
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Change color of font for text source
Credit: et al
Modified:
function: Update Text Source (timer text source)
type: Support, Render
input type: Integer
returns: none
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function set_text_timer_color( int )
if in_table( {"Select", "select"}, timer_source ) then return end; -- if timer_source not defined, then return
local source = obs.obs_get_source_by_name( timer_source ); -- get source by name
if source ~= nil then -- continue if we have a source
local settings = obs.obs_source_get_settings( source ); -- get source settings
obs.obs_data_set_int( settings, "color", int ); -- update source settings
end
obs.obs_source_update( source, settings ); -- save source new settings
obs.obs_data_release( settings ); -- release settings
obs.obs_source_release( source ); -- release source
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Change color of font for text source
Credit: et al
Modified:
function: Update Text Source
type: Support, Render
input type:
returns: none
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function set_text_note_color( ref )
if media["note_source_" .. ref] == "Select" then return end; -- if source not defined, then return
local source = obs.obs_get_source_by_name( media["note_source_" .. ref] ); -- get source by name
if source ~= nil then -- continue if we have a source
local settings = obs.obs_source_get_settings( source ) -- get source settings
obs.obs_data_set_string( settings, "text", media["note_".. ref] ); -- update source settings
obs.obs_data_set_int( settings, "color", media["color_".. ref] ); -- update source settings
end;
obs.obs_source_update( source, settings ); -- save source new settings
obs.obs_data_release( settings ); -- release settings
obs.obs_source_release( source ); -- release source
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Comapre current time with a time mark reference.
If the marker match, then complete required
tasks.
The tasks include changing the timer text source
font colour as defined, setting linked text sources
visible or/and hidden and changing linked text
sources font colour.
enable_marker_notes must be equal to 2 to be used.
enable_marker_notes equal to 1 is disabled
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function time_mark_check( ref )
if not timer_active then return end; -- only allow mark checks if the timer is active
--[[
Make sure the trigger is as accurate as possible depending
if the timer is counting up or down.
]]
local round_seconds = math.ceil( current_seconds ); -- round to nearset upper value
--[[
if not Countdown so target Stopwatch, or
if the count direction changed and the count direction is positive
]]
if timer_mode ~= 2 and not direction_changed or ( direction_changed and current_count_direction == 2 ) then
round_seconds = math.floor( current_seconds ); -- round to nearset lower value
end
if raw_time( round_seconds, true ) == media["text_".. ref] then -- compare current time with marker
--[[
If Marker notes is enabled and the reference provided
match to Marker A, complete some tasks
]]
if enable_marker_notes ~= 1 and ref == "marker_a" then -- marker notes is enabled and the input reference matches
set_visible( media["note_source_" .. ref], true ); -- Set visble the source for the note for marker a
set_visible( media["note_source_marker_b"], false ); -- Set hiden the source for the note for marker b (only show one note at a time)
set_text_note_color( ref ); -- Update the note text font to match the font colour defined for marker a
end;
--[[
If Marker notes is enabled and the reference provided
match to Marker B, complete some tasks
]]
if enable_marker_notes ~= 1 and ref == "marker_b" then -- marker notes is enabled and the input reference matches
set_visible( media["note_source_" .. ref], true ); -- Set visble the source for the note for marker b
set_visible( media["note_source_marker_a"], false ); -- Set hiden the source for the note for marker a (only show one note at a time)
set_text_note_color( ref ); -- Update the note text font to match the font colour defined for marker b
end;
--[[
Update the timer text source font colour to match the defined font colour for the referenced marker
This will ensure that the timer text font matches the font colour of the currently displayed note.
]]
set_text_timer_color( media["color_".. ref] );
--[[
]]
start_media( media["source_name_audio_".. ref], ref );
--[[
if the user wants OBS to start recording when the marker activates
check the reference and activate the timer to initiate recording
recording_type:
1 = "Timer Expires",
2 = "Marker A Time",
3 = "Marker B Time",
4 = "Timer Visible",
5 = "Timer Start"
]]
if ref == "marker_a" then record( 2, 100 ) end; -- an integer reference used to compare with recording_type
if ref == "marker_b" then record( 3, 100 ) end; -- an integer reference used to compare with recording_type
end;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Check source type of media if the media is set to loop
The source is referenced by name.
Credit:
Modified:
function:
type:
input type: reference (string)
returns: bool
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function get_source_looping( source_name )
local property = "looping"; -- we want to check this property setting
--[[
Increments the source reference counter,
use obs_source_release() to release it when complete.
we got a source name, let's see if it exist...
]]
local source = obs.obs_get_source_by_name( source_name ); -- get source by name
local enabled = false;
if source ~= nil then -- continue if we have a source
local source_id = obs.obs_source_get_unversioned_id( source ); -- get source id
if source_id == "ffmpeg_source" then -- check if source id match that of type we need to focus on
local settings = obs.obs_source_get_settings( source ); -- get source settings
enabled = obs.obs_data_get_bool( settings, property ); -- check if media source has playback looping enabled
end;
end;
obs.obs_data_release( settings ); -- release settings
obs.obs_source_release( source ); -- release source
return enabled; -- bool
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Check if the source state changed,
if so, set source visble = false
Credit:
Modified:
function:
type: tasks
input type: ref (string)
returns: none
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function stop_media_action( ref )
local source_name = media["source_name_audio_".. ref]; -- assign local variable
if in_table( {nil, "","None", "Select","none", "select"}, source_name ) and not media["media_ended_".. ref] then return end; -- if source not defined, then return
--[[
ref is either Mark A or B
Check if ref has duration set and check if media_playback_limit is enabled
]]
if media["duration_".. ref] ~= 0 and media_playback_limit ~= 1 then
--[[
Increments the source reference counter,
use obs_source_release() to release it when complete.
we got a source name, let's see if it exist...
]]
local source = obs.obs_get_source_by_name( source_name );
if source ~= nil then -- source is valid
local state = obs.obs_source_media_get_state( source ); -- get the current state for the source
if media["last_state_".. ref] ~= state then -- The state has changed
if state == obs.OBS_MEDIA_STATE_PLAYING then
--[[
time remaining is calculated differently depending on the timer_mode (count is up or down)
]]
local media_time_started = math.ceil( media["current_seconds_".. ref] ); -- round to nearset upper value
local media_time_limit = math.floor( media["duration_".. ref] ); -- round to nearset lower value
local time_currently = math.ceil( current_seconds ); -- round to nearset upper value
local media_time_remaining = 0; -- set integer variable with default value
local time_end = false; -- set bool variable with default value
if timer_mode == 1 then -- count direction is positive
media_time_remaining = media_time_started + media_time_limit; -- time media became active and add playback time limit
time_end = ( time_currently >= media_time_remaining ); -- time is equal to or greater than the limit
end
if timer_mode == 2 then -- count direction is negative
media_time_remaining = media_time_started - media_time_limit; -- time media became active and subtract playback time limit
time_end = ( time_currently <= media_time_remaining ); -- time is equal to or less than the limit
end
if time_end then -- bool, has the time limit been reached?
media["last_state_".. ref] = state; -- update the ref state global variable because we need to track it
media["media_ended_".. ref] = true; -- update the ref media end global variable because we need to track it
set_visible( source_name, false ); -- The source visibility must now be changed to hidden
end
end ;
else -- The state has not changed
media["last_state_".. ref] = state; -- update the ref state global variable because we need to track it
-- the state is currently either stopped or completed, then reset source visibility to hidden
if state == obs.OBS_MEDIA_STATE_STOPPED or state == obs.OBS_MEDIA_STATE_ENDED then -- state match?
set_visible( source_name, false ); -- The source visibility must now be changed to hidden
end;
end; -- source state check end
end; -- source ~= nil
end;
obs.obs_source_release( source ); -- release source from the reference counter
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Stop Media Playback
Credit: OBS, midnight-studios
Modified:
function:
type:
input type:
returns: none
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function stop_media_playback( source_name )
if in_table( {"","None", "Select","none", "select"}, source_name ) or not is_visible( source_name ) then return end;
local source = obs.obs_get_source_by_name( source_name );
if source ~= nil then
local source_id = obs.obs_source_get_unversioned_id( source ); -- unversioned_id will not be affected by language settings
if source_id ~= "ffmpeg_source" then return end; -- apply this to media type sources only
local state = obs.obs_source_media_get_state( source ) -- get the current state for the source
if state == obs.OBS_MEDIA_STATE_PLAYING or obs.OBS_MEDIA_STATE_PAUSED then
obs.obs_source_media_stop( source );
end;
--obs.obs_source_media_get_duration( source )
--obs.obs_source_media_get_time( source )
--obs.obs_source_media_restart( source )
end;
obs.obs_source_release( source );
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Play / Pause Media
Credit: OBS, midnight-studios
Modified:
function:
type:
input type:
returns: none, play /pause media source
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function pause_play_media( source_name, play )
if in_table( {"","None", "Select","none", "select"}, source_name ) or not is_visible( source_name ) then return end;
local source = obs.obs_get_source_by_name( source_name );
if source ~= nil then
local source_id = obs.obs_source_get_unversioned_id( source ); -- unversioned_id will not be affected by language settings
if source_id ~= "ffmpeg_source" then return end; -- apply this to media type sources only
obs.obs_source_media_play_pause( source, play );
--obs.obs_source_media_get_duration( source )
--obs.obs_source_media_get_time( source )
--obs.obs_source_media_restart( source )
--local state = obs.obs_source_media_get_state( source ) -- get the current state for the source
--if state == obs.OBS_MEDIA_STATE_PLAYING then
--end
--if state == obs.OBS_MEDIA_STATE_PAUSED then
--end
end
obs.obs_source_release( source );
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Stop Media is designed to reset the source to its
starting state. In other words, make the source
invisible again. This sould only happen if the media
ended, or if it is looped, then end the media after a
defined time.
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function stop_looping_media( ref )
local source_name = media["source_name_audio_".. ref];
if get_source_looping( source_name ) then
stop_media_playback( source_name );
--[[
We don't need this because we have already
attached a source signal hanlder for 'media_ended'
that will hide the source if it is ended
]]
--set_visible( source_name, false ) -- Set the media source state to hidden.
end
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Stop Media is designed to reset the source to its
starting state. In other words, make the source
invisible again. This sould only happen if the media
ended, or if it is looped, then end the media after a
defined time.
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function stop_media( ref, bypass )
if bypass == nil then bypass = false end;
if bypass then -- No checks, just stop it
set_visible( media["source_name_audio_".. ref], false ); -- Set the media source state to hidden
else -- do some checks
stop_media_action( ref ); -- handle this request elsewhere
end
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Function to cycle through a list for sources or scenes
Credit:
Modified:
function:
type:
input type: string
returns: nothing
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function cycle_source_list_by_source_type( source_type )
--[[
Create a table for a list
]]
local i = 0; -- create interger variable
local list = {}; -- create temporary table variable
local item_list = {}; -- create temporary table variable
local data_list = obs_data_array_to_table( ctx.propsSet, "cycle_list" ); -- fetch obs userdata from property settings and return in table
local direction = 1; -- create interger variable
--[[
handle scenes
]]
if source_type ~= "source" then -- string is not equal to "source" as it should be "scene"
direction = 1; -- Descend Ascend change direction to 1 or 2
local scenes = obs.obs_frontend_get_scene_names(); -- List Scenes names
if scenes ~= nil then
for _, scene in ipairs( scenes ) do -- cycle through list items one at a time
item_list[scene] = scene; -- add scene name (string) to the table
end
obs.bfree( scene ); -- free memory, release source as it is no longer needed
end
else -- List Source names
direction = 1;-- Descend Ascend change direction to 1 or 2
local sources = get_source_list(); -- "id" or "unversioned_id" or "display_name" or "source_name"
--sources = remove_duplicates( sources )
for key, value in pairsByKeys( sources ) do
item_list[value] = value;
end
end
--[[
Build a cycle list
]]
for key, value in pairs( data_list ) do
if in_table( item_list, value ) then
i = i + 1;
list[i] = value;
end
end
local count = tablelength( list );
if cycle_index > count then
cycle_index = 1;
end
local index = 0;
if cycle_direction ~= direction then
index = 1;
else
index = count;
end
for i = 1, count do
local index_match = ( index == cycle_index );
--[[
The list contains all available sources.
The value targets the sources requested.
Check if the requested source is available
]]
if list[i] ~= nil then
--[[
Type is "Scene"
]]
if source_type ~= "source" then --
if index_match then
local scene_source = obs.obs_frontend_get_current_scene();
local name = obs.obs_source_get_name( scene_source );
obs.obs_source_release( scene_source );
local source = obs.obs_get_source_by_name( list[i] );
if source ~= nil then
obs.obs_frontend_set_current_scene( source );
end
obs.obs_source_release( source );
end
--[[
Type is "Source"
]]
else
set_visible( list[i], index_match );
end
if index_match then
--[[
force the visibility
state of this source.
]]
if active_source_force_visible then set_visible( active_source, true ) end;
set_text( active_source, list[i] );
end;
end;
if cycle_direction ~= direction then
index = index + 1;
else
index = index - 1;
end;
end;
cycle_index = cycle_index + 1;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Check if a scene with a specific name has a source with a specific name
Credit: midnight-studios, et al
Modified:
function: check true or false
type: Dependency / Support
input type: string, string
returns: bool
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function scene_name_has_source_name( scene_name, source_name )
scene_source = obs.obs_get_source_by_name( scene_name );
local scenename = obs.obs_source_get_name( scene_source );
local scene = obs.obs_scene_from_source( scene_source );
local sceneitems = obs.obs_scene_enum_items( scene );
local result = false;
for key_sceneitem, value_sceneitem in pairs( sceneitems ) do
local source = obs.obs_sceneitem_get_source( value_sceneitem );
local source_name_parent = obs.obs_source_get_name( source );
local group = obs.obs_group_from_source( source );
if source_name_parent == source_name then
result = true;
break;
end
if group ~= nil then
local groupitems = obs.obs_scene_enum_items( group );
if groupitems ~= nil then
for key_groupitem, value_groupitem in pairs( groupitems ) do
local groupitemsource = obs.obs_sceneitem_get_source( value_groupitem );
local source_name_group = obs.obs_source_get_name( groupitemsource );
if source_name_group == source_name then
result = true;
break;
end;
end; -- end for
obs.sceneitem_list_release( groupitems );
end;
end;
end; -- end for in pairs( sceneitems )
obs.sceneitem_list_release( sceneitems );
obs.obs_source_release( scene_source );
return result;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Called when a scene is activated/deactivated
Credit: midnight-studios, et al
Modified:
function: make a source visible
type: Dependency / Support
input type: source, bool, start_on_scene_active (global), scene_name_has_source_name()
returns: none
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function activate_timer_on_scene( source, activating )
--[[
Reset to starting point
if, start_on_scene_active then set to visible
]]
if start_on_scene_active and activating then
local source_id = obs.obs_source_get_id( source );
local current_scene_source = obs.obs_frontend_get_current_scene();
local current_scene_name = obs.obs_source_get_name( current_scene_source );
obs.obs_source_release( current_scene_source );
if source_id == "scene" then
if scene_name_has_source_name( current_scene_name, timer_source ) then
if not is_visible( timer_source ) then
set_visible( timer_source, true );
end;
end;
end;
end;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Update Properties
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function update_properties_setting_int( reference, value )
--[[
When this is updated it will trigger a
callback "property_onchange", let's
disable that for a moment.
]]
--prevent_callback = true;
obs.obs_data_set_int( ctx.propsSet, reference, value );
obs.obs_properties_apply_settings( ctx.propsDef, ctx.propsSet );
--[[
When this is updated it will trigger a
callback "property_onchange", let's
enable it again
]]
--prevent_callback = false;
return true
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Update Properties
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function update_prop_settings_current_seconds( value )
--[[
When this is updated it will trigger a
callback "property_onchange", let's
disable that for a moment.
]]
prevent_callback = true;
obs.obs_data_set_double( ctx.propsSet, "sw_current_seconds", value );
sw_current_seconds = value;
obs.obs_properties_apply_settings( ctx.propsDef, ctx.propsSet );
--[[
When this is updated it will trigger a
callback "property_onchange", let's
enable it again
]]
prevent_callback = false;
return true;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Everytime the timer value is updated,
it will happen here
Credit:
Modified:
function: update the timer value
type: Dependency / Support
input type: double
returns: current_seconds
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function timer_value( value )
current_seconds = value;
return current_seconds;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Assign the correct frequency value to the timer incriment
depending on if timer is counting up or down
Credit:
Modified:
function: update the timer value
type: Dependency / Support
input type:
returns: calls timer_value()
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function set_time_direction( )
--[[
]]
local t = 0;
--[[
]]
if direction_changed then -- normal function suspended
if current_count_direction == 1 then
t = ( current_seconds - time_frequency ); -- value
else
t = ( current_seconds + time_frequency ); -- value
end
else -- normal function active
--[[
]]
if timer_mode ~= 2 then
t = ( current_seconds + time_frequency ); -- value
else
t = ( current_seconds - time_frequency ); -- value
end
end
--[[
update the timer value
]]
timer_value( t ); -- value
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Function to set the defined time text source value
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
function update_timer_display( source_name, text )
--[[
Increments the source reference counter,
use obs_source_release() to release it when complete.
]]
local source = obs.obs_get_source_by_name( source_name );
if source ~= nil then
local settings = obs.obs_source_get_settings( source );
if not media["activated_marker_a"] and not media["activated_marker_b"] and not color_normal_updated then
obs.obs_data_set_int( settings, "color", media["color_normal"] );
color_normal_updated = true;
end
time_mark_check( "marker_a" );
time_mark_check( "marker_b" );
obs.obs_data_set_string( settings, "text", text );
end
obs.obs_source_update( source, settings );
obs.obs_data_release( settings );
obs.obs_source_release( source );
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Function to toggle milliseconds
Credit:
Modified:
function: mili_toggle
type:
input type: globals: toggle_mili_trigger, timer_mode, mili_toggle_triggered, raw_time()
returns: none
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function toggle_mili()
--[[
This feature will only activate if "Trigger Value" is defined
and if "Trigger Value" matches "Current Time" and if
"Timer Type" is "Countdown"
]]
if toggle_mili_trigger ~= "" and timer_mode == 2 and not mili_toggle_triggered then
local time_offset = 1; -- offset by 1 second to allow user to achieve accurate setting
if raw_time( ( current_seconds + time_offset ), true ) == toggle_mili_trigger then
--[[
The action trigger a toggle, so if the
active state at the time of the trigger
is "Show" the toggle will "Hide" and
Vicas Versa.
Should we force a state?
To force define: show_mili = false
]]
mili( true );
mili_toggle_triggered = true;
end;
end;
--return true
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Function to set the defined time text source value
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function set_time_text( source_name )
--[[
First Check we have a source reference
]]
if source_name == nil then return end;
--[[
Force absolute zero at this point
]]
if current_seconds <= 0.01 and ( timer_mode ~= 1 or ( direction_changed and current_count_direction == 1 and prevent_negative_time ) ) then
timer_value( 0 ); -- value, update_settings
end;
toggle_mili();
local l_time = long_time( current_seconds );
local t_time = raw_time( current_seconds );
--[[
Timer Format Type: Full Format
]]
local text = tostring( raw_time( current_seconds ) );
--[[
Timer Format Type: Remove Leading Zeros
]]
if timer_format == 2 then
local system_time_format = "$T$H:$M:$S,$F";
text = format_time( ( l_time ~= 0 ) and string.format( "%s:%s", l_time, t_time ) or string.format( "%s", t_time ), system_time_format );
end
--[[
Timer Format Type: No Leading Zeros, No Split Seconds
]]
if timer_format == 3 then
local system_time_format = "$T$H:$M:$S";
text = format_time( ( l_time ~= 0 ) and string.format( "%s:%s", l_time, t_time ) or string.format( "%s", t_time ), system_time_format );
end
--[[
Timer Format Type: No Leading Zeros, No Split Seconds
]]
if timer_format == 4 then
local system_time_format = "$H:$M:$S";
text = format_time( ( l_time ~= 0 ) and string.format( "%s:%s", l_time, t_time ) or string.format( "%s", t_time ), system_time_format );
end
if timer_format ~= 5 then
--[[
Format the Text "Day/Days"
]]
if timer_mode == 2 and countdown_type == 1 and current_seconds ~= 0 then
local longtimetext = longtimetext_p;
if math.floor( current_seconds / 86400 ) <= 1 then
longtimetext = longtimetext_s;
end
if math.floor( current_seconds / 86400 ) <= 0 then
longtimetext = longtimetext_p;
end
text = string.gsub(longtimetext .. text, "[#]", long_time( current_seconds ));
end
end
--[[
Timer Format Type: Custom Time Format
]]
if timer_format == 5 then
text = format_time( ( l_time ~= 0 ) and string.format( "%s:%s", l_time, t_time ) or string.format( "%s", t_time ), removeBrackets(custom_time_format) );
end
if timer_mode ~= 2 then
--text_prefix = ""
-- text_suffix = ""
end
text = text_prefix .. text .. text_suffix;
if text ~= last_text then
update_timer_display( timer_source, text );
end
--[[
Check is media is playing and stop if required
]]
stop_media( "marker_a" );
stop_media( "marker_b" );
last_text = text;
--[[
Timer Ended
]]
if current_seconds <= 0.01 and ( timer_mode ~= 1 or ( direction_changed and current_count_direction == 1 and prevent_negative_time ) ) then
--[[
Timer is shutting down, this would be a
good time to reset some items.
]]--
if enable_marker_notes ~= 1 then
set_visible( media["note_source_marker_a"], false );
set_visible( media["note_source_marker_b"], false );
end
--[[
Timer was started and now has EXPIRED
execute any final instructions that
the user require on TIMER END
]]--
if timer_active then timer_ended( source_name ) end;
--[[
Now remove the timer callback
"timer_ended()" offer options to
restart the timer and may define
a value to "current_seconds".
Final check, if current_seconds == 0 then
deactivate (end/remove) the timer callback
This is a fallback but should not be needed
as the timer callback may be removed by
timer_ended() if needed
]]--
if current_seconds == 0 then timer_expired = true end;
end
--return true
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: This captures the split times and unpack it in the
correct format.
The text source only permits linebreaks ( "\n" ) this
limitation affects how the data can be formated ):
Credit:
Modified:
function: split time
type: Dependency / Support
input type: globals
returns: updates global split_data
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function split_unpack()
local data = nil;
local c = table.getn( split_itm );
local text = "";
local title = "";
local subtitle = "";
local line = "______________________________";
for i = 1, c do
local mark = split_itm[i];
local interval = mark;
if i > 1 then
local j = i - 1;
interval = split_itm[i] - split_itm[j];
end
--[[
"Interval" = 1
"Mark" = 2
"Mark Interval" = 3
"Interval Mark" = 4
]]
if split_type == 1 then
title = "Interval";
--subtitle = ""
text = tostring( raw_time( interval ) );
elseif split_type == 2 then
title = "Mark";
--subtitle = ""
text = tostring( raw_time( mark ) );
elseif split_type == 3 then
title = "Mark ";
subtitle = "Interval";
text = tostring( raw_time( mark ).." "..raw_time( interval ) );
else -- "Interval Mark"
title = "Interval ";
subtitle = "Mark";
text = tostring( raw_time( interval ).." "..raw_time( mark ) );
end;
-- data collection here
local n = i --formatting the index number
if i < 10 then n = "0"..tostring( i ) end;
if data ~= nil then
data = data .. "\n" .. n.." ) "..text;
else
data = "# "..title..subtitle.."\n"..line.."\n\n"..n.." ) "..text;
end
end -- end for
split_data = data;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Decide if current_seconds needs to reset to default_seconds
Credit:
Modified:
function: Check if current_seconds needs to reset to default_seconds
type: check
input type: next_scene
returns: bool
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function update_default_time()
if next_scene == "Source List" or next_scene == "Scene List" then
return true;
end
return false;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Function to set the split time text
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function set_split_text( source_name )
if in_table( {"","None", "Select","none", "select"}, source_name ) then return end;
local text = split_data;
if text ~= last_split_data then
set_text( source_name, text );
end
last_split_data = text;
end
--[[
----------------------------------------------------------------------------------------------------------------------------------------
Description: Used when we need to set some gloabsl for the timer to default state
Credit:
Modified:
function:
type:
input type:
returns:
----------------------------------------------------------------------------------------------------------------------------------------
]]
local function default_timer_globals( set_to_default )
--[[
if set_to_default == true
and timer_mode == 2 (Countdown)
]]