Video Source File Path from .txt file

onairmitflo

Member
Hello dear friends,
I have been working on Visual Radio for a while now. Since I use the automation mAirList I could output the path of a played mp4 file via logging in a text file. This would also update itself again and again.

What I am looking for now in OBS is a way to read and play video files from a TXT file. Also a variable video source. This must grab, if the file is also overwritten by mAirList, the new video path.

Do any of you smart people have an idea or have experimented with this?

Best regards
Florian
 

khaver

Member
A Lua script will probably work. I made a Lua script that reads an ini file and updates two text sources from the data in the file. I might be able to adapt it to your needs. Does the text file contain the path to a single video file or is it a list of multiple files? If you have one of these files, could you post an example here? I'll be waiting for your reply.
 

onairmitflo

Member
Okay? It is a path to a video file that is written into a text file by mAirList. This TXT file is always overwritten by mAirList when a new track starts. Of course with the "Now Playing Path". I would like to have a look at your example. :)
 

khaver

Member
Florian, here's the lua script code. Copy and paste it to a file called "MediaFromTXT.lua". Save it somewhere on your OBS computer. Open OBS and go to Tools->Scripts. At the lower left, click the "+" symbol and select the MediaFromTXT.lua file. In the MediaFromTXT.lua settings, first select the text file you want it to check, then the Media Source from the drop-down, and check the "Enable Script". It should start working. The default text file reading interval is 1000ms (1 second). If you want to change it, select the MediaFromTXT.lua in the list and click the Edit Script button. At the top of the file you'll see the interval variable. Change it to the milliseconds you want. Save the file then click the refresh button in the OBS scripts list. Let me know if you have problems or need changes.

Lua:
-- Version 1.0
o = obslua

--Set interval in ms to how often the text file is checked for changes. Default = 1000 ms (1 second).
interval = 1000

TEXTFILE= nil
VIDFILE= nil
PVIDFILE= nil
source_name = nil

enabled = true

function read_txt_file(filename)
  filename = filename or ''
  local line
  PVIDFILE = VIDFILE
  --print(filename)
  if not file_exists(filename) then return end
  for line in io.lines(filename) do
  --print(line)
  if line ~= "" then
    VIDFILE = line
    if VIDFILE == PVIDFILE then return PVIDFILE end
    --print(VIDFILE)
    refresh_media()
  end
  end
end

function file_exists(file)
    local f = io.open(file, "rb")
    if f then f:close() end
    return f ~= nil
end

function script_description()
    return [[Reads the path of a video file from a selected text file and updates a selected media source using the new video file path. It reads the text file for changes at preditermined intervals.]]
end

function script_load()

end

function script_unload()

end

function refresh_media()
local source = o.obs_get_source_by_name(source_name)
    if source ~= nil then
        local settings = o.obs_data_create()
        o.obs_data_set_string(settings, "local_file", VIDFILE)
        o.obs_source_update(source, settings)
        o.obs_data_release(settings)
    end
o.obs_source_release(source)
end

function timer_callback()
    read_txt_file(TEXTFILE)
end

function script_properties()
    local p = o.obs_properties_create()

    o.obs_properties_add_bool(p, "enable", "Enable script")
    o.obs_properties_add_path(p, "theTextFile", "mAirList txt File",
        o.OBS_PATH_FILE,
        "Video File List (*.txt)",
        nil
    )
    local t = o.obs_properties_add_list(p, "tsource", "Media Source", o.OBS_COMBO_TYPE_EDITABLE, o.OBS_COMBO_FORMAT_STRING)
    local sources = o.obs_enum_sources()
    if sources ~= nil then
        for _, source in ipairs(sources) do
            source_id = o.obs_source_get_id(source)
            if source_id == "ffmpeg_source" then
                local name = o.obs_source_get_name(source)
                o.obs_property_list_add_string(t, name, name)
            end
        end
    end
    o.source_list_release(sources)
    return p
end

function script_defaults(s)
    o.obs_data_set_default_bool(s, "enable", false)
    --o.obs_data_set_default_string(s, "theTextFile", DEFAULTBASE)
end

function script_update(s)
    TEXTFILE = o.obs_data_get_string(s, "theTextFile")
    source_name = o.obs_data_get_string(s, "tsource")
    enabled = o.obs_data_get_bool(s, "enable")
    if enabled then
        print("Enabled")
        read_txt_file(TEXTFILE)
        o.timer_remove(timer_callback)
        o.timer_add(timer_callback, interval)
    else
        print("Disabled")
        o.timer_remove(timer_callback)
    end
end
 

onairmitflo

Member
Florian, here's the lua script code. Copy and paste it to a file called "MediaFromTXT.lua". Save it somewhere on your OBS computer. Open OBS and go to Tools->Scripts. At the lower left, click the "+" symbol and select the MediaFromTXT.lua file. In the MediaFromTXT.lua settings, first select the text file you want it to check, then the Media Source from the drop-down, and check the "Enable Script". It should start working. The default text file reading interval is 1000ms (1 second). If you want to change it, select the MediaFromTXT.lua in the list and click the Edit Script button. At the top of the file you'll see the interval variable. Change it to the milliseconds you want. Save the file then click the refresh button in the OBS scripts list. Let me know if you have problems or need changes.

Lua:
-- Version 1.0
o = obslua

--Set interval in ms to how often the text file is checked for changes. Default = 1000 ms (1 second).
interval = 1000

TEXTFILE= nil
VIDFILE= nil
PVIDFILE= nil
source_name = nil

enabled = true

function read_txt_file(filename)
  filename = filename or ''
  local line
  PVIDFILE = VIDFILE
  --print(filename)
  if not file_exists(filename) then return end
  for line in io.lines(filename) do
  --print(line)
  if line ~= "" then
    VIDFILE = line
    if VIDFILE == PVIDFILE then return PVIDFILE end
    --print(VIDFILE)
    refresh_media()
  end
  end
end

function file_exists(file)
    local f = io.open(file, "rb")
    if f then f:close() end
    return f ~= nil
end

function script_description()
    return [[Reads the path of a video file from a selected text file and updates a selected media source using the new video file path. It reads the text file for changes at preditermined intervals.]]
end

function script_load()

end

function script_unload()

end

function refresh_media()
local source = o.obs_get_source_by_name(source_name)
    if source ~= nil then
        local settings = o.obs_data_create()
        o.obs_data_set_string(settings, "local_file", VIDFILE)
        o.obs_source_update(source, settings)
        o.obs_data_release(settings)
    end
o.obs_source_release(source)
end

function timer_callback()
    read_txt_file(TEXTFILE)
end

function script_properties()
    local p = o.obs_properties_create()

    o.obs_properties_add_bool(p, "enable", "Enable script")
    o.obs_properties_add_path(p, "theTextFile", "mAirList txt File",
        o.OBS_PATH_FILE,
        "Video File List (*.txt)",
        nil
    )
    local t = o.obs_properties_add_list(p, "tsource", "Media Source", o.OBS_COMBO_TYPE_EDITABLE, o.OBS_COMBO_FORMAT_STRING)
    local sources = o.obs_enum_sources()
    if sources ~= nil then
        for _, source in ipairs(sources) do
            source_id = o.obs_source_get_id(source)
            if source_id == "ffmpeg_source" then
                local name = o.obs_source_get_name(source)
                o.obs_property_list_add_string(t, name, name)
            end
        end
    end
    o.source_list_release(sources)
    return p
end

function script_defaults(s)
    o.obs_data_set_default_bool(s, "enable", false)
    --o.obs_data_set_default_string(s, "theTextFile", DEFAULTBASE)
end

function script_update(s)
    TEXTFILE = o.obs_data_get_string(s, "theTextFile")
    source_name = o.obs_data_get_string(s, "tsource")
    enabled = o.obs_data_get_bool(s, "enable")
    if enabled then
        print("Enabled")
        read_txt_file(TEXTFILE)
        o.timer_remove(timer_callback)
        o.timer_add(timer_callback, interval)
    else
        print("Disabled")
        o.timer_remove(timer_callback)
    end
end
WOW. THIS WORKS!!!! I have just created VisualRadio. Awesome. Now I have 2 questions. How do I get rid of the BrowserPlayer icons? And would it be possible to pass the start time of the video/track to OBS, and thus apply the start time to the music video?

Surprising greetings!
 

khaver

Member
First, I don't know what the BrowserPlayer icons are.

Second, do you mean instead of the script checking the text file and changing the Media Source to the path in the text file as soon as a new path is detected, the text file would now contain both the path and the required start time and the script would load the new video file at the designated time? If this is the case, yes I should be able to modify the script to do this. I would need to know how the path and time would look in the text file.
 

onairmitflo

Member
So the first question has to do with this:
CSS:
video::-webkit-media-controls {
  display:none !important;
}
clarified.
 

onairmitflo

Member
First, I don't know what the BrowserPlayer icons are.

Second, do you mean instead of the script checking the text file and changing the Media Source to the path in the text file as soon as a new path is detected, the text file would now contain both the path and the required start time and the script would load the new video file at the designated time? If this is the case, yes I should be able to modify the script to do this. I would need to know how the path and time would look in the text file.

This is versatile with mAirList. According to mAirList Wiki I can output the following time options:

-> HH:MM:SS format
-> everything from this website in its own order here
-> in seconds with fractions

I can add the time in the same document with spaces or other separator to the same TXT document. On the other hand I can add the start time in another text file. All possible as soon as a script would allow such a thing. Would be mega if OBS would grab this time and start the video at this time.

Nice regards!
Florian
 

khaver

Member
For the start time, what is it that you're trying to accomplish? Play the video in the text file at a certain start time? Add a "start time" text overlay over the video showing the time of day the video started? I'm not sure what exactly you want.
 

onairmitflo

Member
Play the video in the text file at a certain start time?


Yes Exactly! Some videos have intros that we truncate in mAirList (Radio Playout) with a markup. This mark, I can write as time into this text file, as described above - so that the video in OBS will START at THIS TIME.

That would be my goal... As I know OBS, this is certainly possible.

Do you have ideas?
 

khaver

Member
What will the contents of the text file look like? The path to the video file on one line and the start time on the next? Or the start time and path on the same line separated by a coma, semi-colon, or...? I need to know exactly so the script can parse the file correctly. Also, how long before the start time of the video will the text file be written?
 

onairmitflo

Member
It's up to you. I can implement anything in mAirList, depending on how it would be easier for you to implement.

How about example 1:
Code:
C:\RADIO\#DOWNLOAD\Jason Derulo x Nuka - Love Not War [Official Music Video].mp4;00:00:12

-> HH:MM:SS

(would be easiest for me now)
 

khaver

Member
Okay, I think I understand the problem. When the video file gets loaded, you want it to skip the intro and begin playing a certain time after the intro? Is this correct? If so, I'll have to do some research into the media player in OBS.
 

onairmitflo

Member
Okay, I think I understand the problem. When the video file gets loaded, you want it to skip the intro and begin playing a certain time after the intro? Is this correct? If so, I'll have to do some research into the media player in OBS.
That's right. Based on the time that mAirList writes to the file, OBS should start the video at that point.
 

onairmitflo

Member
And another problem gets in the way. Is it possible to add the appropriate scene to the source? Currently the source is under SOURCES\MUSICVIDEO.

If now another scene is selected, the music video should change anyway :=)
 

khaver

Member
And another problem gets in the way. Is it possible to add the appropriate scene to the source? Currently the source is under SOURCES\MUSICVIDEO.

If now another scene is selected, the music video should change anyway :=)
Make sure in the MUSICVIDEO properties you have "Loop", "Restart playback when source becomes active", and "Close file when inactive" unchecked.

Here's new code that starts playing the file after the specified time in the text file. The format for the text file needs to be what you showed above: Path to video file;00:00:00

It's a little clunky. The new file loads and starts playback from the start for a fraction of a second, then advances to the place indicted in the text file. When I have time, I'll continue working on it to see if it can be improved.

Make a backup of the previous file and copy and paste this over the code in the MediaFromTXT.lua file. In OBS refresh the script.

Lua:
-- Version 1.1
o = obslua

--Set interval in ms to how often the text file is checked for changes. Default = 1000 ms (1 second).
interval = 1000

TEXTFILE= nil
VIDFILE= nil
PVIDFILE= nil
VIDPATH= nil
SKIP = nil
source_name = nil
updated = false

enabled = true

function read_txt_file(filename)
  filename = filename or ''
  local line
  PVIDFILE = VIDFILE
  if not file_exists(filename) then return end
  for line in io.lines(filename) do
  if line ~= "" then
    VIDFILE = line
    local p, t = line:match("^([^=]+)%;(.+)$")
    if p then
        VIDPATH = p
        if not t then t = "00:00:00" end
        local shour, sminute, ssecond = line:match("(%d+):(%d+):(%d+)")   --("^([^=]+)%:(.+)$")
        local hour = tonumber(shour)
        local minute = tonumber(sminute)
        local second = tonumber(ssecond)
        SKIP = (hour * 3600000) + (minute * 60000) + (second * 1000)
    end
  end
  end
end

function file_exists(file)
    local f = io.open(file, "rb")
    if f then f:close() end
    return f ~= nil
end

function script_description()
    return [[Reads the path of a video file from a selected text file and updates a selected media source using the new video file path. It reads the text file for changes at preditermined intervals.]]
end

function script_load()

end

function script_unload()

end

function refresh_media()
    local source = o.obs_get_source_by_name(source_name)
    if source == nil then return end
    local settings = o.obs_data_create()
    o.obs_data_set_string(settings, "local_file", VIDPATH)
    o.obs_source_update(source, settings)
    o.obs_data_release(settings)
    o.obs_source_release(source)
    updated = true

end

function cue_media()
    local source = o.obs_get_source_by_name(source_name)
    if source == nil then return end
    o.obs_source_media_set_time(source, SKIP)
    o.obs_source_release(source)
    updated = false
end

function timer_callback()
    if updated then
        cue_media()
    else
        read_txt_file(TEXTFILE)
        if VIDFILE ~= PVIDFILE then
            refresh_media()
        --    --cue_media()
        end
    end
end

function script_properties()
    local p = o.obs_properties_create()

    o.obs_properties_add_bool(p, "enable", "Enable script")
    o.obs_properties_add_path(p, "theTextFile", "mAirList txt File",
        o.OBS_PATH_FILE,
        "Video File List (*.txt)",
        nil
    )
    local t = o.obs_properties_add_list(p, "tsource", "Media Source", o.OBS_COMBO_TYPE_EDITABLE, o.OBS_COMBO_FORMAT_STRING)
    local sources = o.obs_enum_sources()
    if sources ~= nil then
        for _, source in ipairs(sources) do
            source_id = o.obs_source_get_id(source)
            if source_id == "ffmpeg_source" then
                local name = o.obs_source_get_name(source)
                o.obs_property_list_add_string(t, name, name)
            end
        end
    end
    o.source_list_release(sources)
    return p
end

function script_defaults(s)
    o.obs_data_set_default_bool(s, "enable", false)
end

function script_update(s)
    TEXTFILE = o.obs_data_get_string(s, "theTextFile")
    source_name = o.obs_data_get_string(s, "tsource")
    enabled = o.obs_data_get_bool(s, "enable")
    if enabled then
        print("Enabled")
        read_txt_file(TEXTFILE)
        if VIDFILE ~= PVIDFILE then
            refresh_media()
        end
        o.timer_remove(timer_callback)
        o.timer_add(timer_callback, interval)
    else
        print("Disabled")
        o.timer_remove(timer_callback)
    end
end
 

onairmitflo

Member
Make sure in the MUSICVIDEO properties you have "Loop", "Restart playback when source becomes active", and "Close file when inactive" unchecked.

Here's new code that starts playing the file after the specified time in the text file. The format for the text file needs to be what you showed above: Path to video file;00:00:00

It's a little clunky. The new file loads and starts playback from the start for a fraction of a second, then advances to the place indicted in the text file. When I have time, I'll continue working on it to see if it can be improved.

Make a backup of the previous file and copy and paste this over the code in the MediaFromTXT.lua file. In OBS refresh the script.

Lua:
-- Version 1.1
o = obslua

--Set interval in ms to how often the text file is checked for changes. Default = 1000 ms (1 second).
interval = 1000

TEXTFILE= nil
VIDFILE= nil
PVIDFILE= nil
VIDPATH= nil
SKIP = nil
source_name = nil
updated = false

enabled = true

function read_txt_file(filename)
  filename = filename or ''
  local line
  PVIDFILE = VIDFILE
  if not file_exists(filename) then return end
  for line in io.lines(filename) do
  if line ~= "" then
    VIDFILE = line
    local p, t = line:match("^([^=]+)%;(.+)$")
    if p then
        VIDPATH = p
        if not t then t = "00:00:00" end
        local shour, sminute, ssecond = line:match("(%d+):(%d+):(%d+)")   --("^([^=]+)%:(.+)$")
        local hour = tonumber(shour)
        local minute = tonumber(sminute)
        local second = tonumber(ssecond)
        SKIP = (hour * 3600000) + (minute * 60000) + (second * 1000)
    end
  end
  end
end

function file_exists(file)
    local f = io.open(file, "rb")
    if f then f:close() end
    return f ~= nil
end

function script_description()
    return [[Reads the path of a video file from a selected text file and updates a selected media source using the new video file path. It reads the text file for changes at preditermined intervals.]]
end

function script_load()

end

function script_unload()

end

function refresh_media()
    local source = o.obs_get_source_by_name(source_name)
    if source == nil then return end
    local settings = o.obs_data_create()
    o.obs_data_set_string(settings, "local_file", VIDPATH)
    o.obs_source_update(source, settings)
    o.obs_data_release(settings)
    o.obs_source_release(source)
    updated = true

end

function cue_media()
    local source = o.obs_get_source_by_name(source_name)
    if source == nil then return end
    o.obs_source_media_set_time(source, SKIP)
    o.obs_source_release(source)
    updated = false
end

function timer_callback()
    if updated then
        cue_media()
    else
        read_txt_file(TEXTFILE)
        if VIDFILE ~= PVIDFILE then
            refresh_media()
        --    --cue_media()
        end
    end
end

function script_properties()
    local p = o.obs_properties_create()

    o.obs_properties_add_bool(p, "enable", "Enable script")
    o.obs_properties_add_path(p, "theTextFile", "mAirList txt File",
        o.OBS_PATH_FILE,
        "Video File List (*.txt)",
        nil
    )
    local t = o.obs_properties_add_list(p, "tsource", "Media Source", o.OBS_COMBO_TYPE_EDITABLE, o.OBS_COMBO_FORMAT_STRING)
    local sources = o.obs_enum_sources()
    if sources ~= nil then
        for _, source in ipairs(sources) do
            source_id = o.obs_source_get_id(source)
            if source_id == "ffmpeg_source" then
                local name = o.obs_source_get_name(source)
                o.obs_property_list_add_string(t, name, name)
            end
        end
    end
    o.source_list_release(sources)
    return p
end

function script_defaults(s)
    o.obs_data_set_default_bool(s, "enable", false)
end

function script_update(s)
    TEXTFILE = o.obs_data_get_string(s, "theTextFile")
    source_name = o.obs_data_get_string(s, "tsource")
    enabled = o.obs_data_get_bool(s, "enable")
    if enabled then
        print("Enabled")
        read_txt_file(TEXTFILE)
        if VIDFILE ~= PVIDFILE then
            refresh_media()
        end
        o.timer_remove(timer_callback)
        o.timer_add(timer_callback, interval)
    else
        print("Disabled")
        o.timer_remove(timer_callback)
    end
end
Hey, I'm getting back to you with current events. I wasn't 100% sure which time format I'm getting right now from mAirList. So I tried it today and the only result I got was "19.972". Not HH:MM:SS mAirList :/

That's what it says in the TXT file:
C:\RADIO\#DOWNLOAD\Tiësto - The Business.mp4;19,972

The CueIN is 19 seconds and 972 milliseconds. This would have to be adjusted in the script if necessary. Otherwise the script already works in OBS, at least I didn't get any error messages. Maybe you have also already progress concerning this:

It's a little clunky. The new file loads and starts playback from the start for a fraction of a second, then advances to the place indicted in the text file. When I have time, I'll continue working on it to see if it can be improved.

Best regards
Florian
 

khaver

Member
I've changed the text reading function so it uses your seconds and milliseconds format.

Copy and paste this new "read_txt_file" function over the one in the MediaFromTXT.lua file, and refresh the script in OBS.
Lua:
function read_txt_file(filename)
  filename = filename or ''
  local line
  PVIDFILE = VIDFILE
  if not file_exists(filename) then return end
  for line in io.lines(filename) do
  if line ~= "" then
    VIDFILE = line
    local p, t = line:match("^([^=]+)%;(.+)$")
    if p then
        VIDPATH = p
        if not t then t = "0" end
        local tsecond = t:gsub('%.', '')
        local msecond = tsecond:gsub(',', '')
        SKIP = tonumber(msecond)
    end
  end
  end
end

I haven't yet found a way to make the skip forward in the video file any better. I'll keep trying, but referring to your other post about making a transition between the video file, if we can get that work with a fade-out/ fade-in or a dissolve between them, you wouldn't see the beginning of the video file anyway.

As I mentioned in that other post of yours, can mAirList write the path to the upcoming video file before the current one ends? If not I think the only transition that would work is the fade-out/fade-in.
 
Top