[LIBRARY] srt.lua A simple srt caption parser
-
- Citizen
- Posts: 67
- Joined: Sat May 08, 2021 9:45 pm
[LIBRARY] srt.lua A simple srt caption parser
This is my first contribution to the community and really there's not much to talk about this is a simple library for getting the current line of an .srt of a song or video in a given time.
Get a copy here:
Github: https://github.com/alejandro-alzate/srt-lua
or here:
Luarocks: https://luarocks.org/modules/alejandro-alzate/srt
Overview
There are three simple highlights to the library simple quick and dirty as well of three api methods to keep in mind:
The function srt.new(string) returns an table containing the given string, a clock and the text on the given time plus some other goodies.
A real world example would look like this:
local captions = srt.new(love.filesystem.read("captions.en.srt"))
The best way to interact with this library is with the method :setTime(time) that takes an integer time that is the time elapsed in seconds, this method also automatically calls internally :update(delta) that is only needed if you want to offset the playback of the captions. you can use the Source:tell to get the elapsed time and pass it to srt, a real world example would look like this:
captions:setTime(yourAudio:tell())
Last but not least important there's the :getText() method that just returns the text stored within the captions object this can seem useless since you can access directly to the text through captions.outputText but calling the method is recommended because it could get updates to add other functionality also i'm not gonna lie but it just looks good in my humble opinion.
Once again here could be a real world example of the library on use.
local text = captions:getText()
local x = (love.graphics.getWidth() / 2) - (love.graphics.getFont():getWidth(text) / 2 )
local y = love.graphics.getHeight() - love.graphics.getFont():getHeight() * 2
love.graphics.print(captions:getText(), x, y)
A complete example would look like this:
Well with nothing to be said anymore i log off, have a great day/night and enjoy coding with my library!
Get a copy here:
Github: https://github.com/alejandro-alzate/srt-lua
or here:
Luarocks: https://luarocks.org/modules/alejandro-alzate/srt
Overview
There are three simple highlights to the library simple quick and dirty as well of three api methods to keep in mind:
The function srt.new(string) returns an table containing the given string, a clock and the text on the given time plus some other goodies.
A real world example would look like this:
local captions = srt.new(love.filesystem.read("captions.en.srt"))
The best way to interact with this library is with the method :setTime(time) that takes an integer time that is the time elapsed in seconds, this method also automatically calls internally :update(delta) that is only needed if you want to offset the playback of the captions. you can use the Source:tell to get the elapsed time and pass it to srt, a real world example would look like this:
captions:setTime(yourAudio:tell())
Last but not least important there's the :getText() method that just returns the text stored within the captions object this can seem useless since you can access directly to the text through captions.outputText but calling the method is recommended because it could get updates to add other functionality also i'm not gonna lie but it just looks good in my humble opinion.
Once again here could be a real world example of the library on use.
local text = captions:getText()
local x = (love.graphics.getWidth() / 2) - (love.graphics.getFont():getWidth(text) / 2 )
local y = love.graphics.getHeight() - love.graphics.getFont():getHeight() * 2
love.graphics.print(captions:getText(), x, y)
A complete example would look like this:
Well with nothing to be said anymore i log off, have a great day/night and enjoy coding with my library!
Code: Select all
target = boardIndex.getWhosPeekingThisLine()
target:setObey(true)
Re: [LIBRARY] srt.lua A simple srt caption parser
Nice. Are you developing a video player?
My boat driving game demo: https://dusoft.itch.io/captain-bradley- ... itius-demo
Re: [LIBRARY] srt.lua A simple srt caption parser
Hi Alejandro, congratulations on getting it done. I always learn a lot when I finish a project, I'm sure it must've been the same for you.
With that said, I don't think it's quite production-quality yet. There's room for a lot of optimizations.
For example, reading your code, the update(delta) method parses the entire SRT file on each call. When you call it from love.update() like in your usage notes, that means it's being run per frame on 60FPS or at least 30FPS. That's a lot of redundant work.
You need to cache the structures you're creating when parsing so you can reuse them later.
Another thing is, making it faster to sample what's the current subtitle to display given the current seek time.
Seeking will always involve some sort of looping (like a binary search etc) since there's no easy way to hash the start time and end time of a subtitle in a way that lets you just specify the time and have it spit out the appropriate subtitle. This is the same challenge that keyframed animations have, with animation code having to locate what's the current keyframe to use based on the animation seek time.
However, in both cases for keyframes and subtitles, you can optimize things based on the fact that, for most of the time, you're usually advancing forward in time, so you only need to keep track of the current subtitle and when the time advances past its end time, you can iterate from that subtitle onward until you find the next one whose end time is ahead of the current seek time. This makes it cheap to query the current subtitle text to display.
Seeking backwards is very rare, like only when the user rewinds to some previous point or something like that, so it's okay if it's done with a brute-force loop.
I took a bit of time to rewrite it with those optimizations in mind.Good luck with your future projects.
Edit 4: Fixed some cases when the index counter could turn nil and stay there.
With that said, I don't think it's quite production-quality yet. There's room for a lot of optimizations.
For example, reading your code, the update(delta) method parses the entire SRT file on each call. When you call it from love.update() like in your usage notes, that means it's being run per frame on 60FPS or at least 30FPS. That's a lot of redundant work.
You need to cache the structures you're creating when parsing so you can reuse them later.
Another thing is, making it faster to sample what's the current subtitle to display given the current seek time.
Seeking will always involve some sort of looping (like a binary search etc) since there's no easy way to hash the start time and end time of a subtitle in a way that lets you just specify the time and have it spit out the appropriate subtitle. This is the same challenge that keyframed animations have, with animation code having to locate what's the current keyframe to use based on the animation seek time.
However, in both cases for keyframes and subtitles, you can optimize things based on the fact that, for most of the time, you're usually advancing forward in time, so you only need to keep track of the current subtitle and when the time advances past its end time, you can iterate from that subtitle onward until you find the next one whose end time is ahead of the current seek time. This makes it cheap to query the current subtitle text to display.
Seeking backwards is very rare, like only when the user rewinds to some previous point or something like that, so it's okay if it's done with a brute-force loop.
I took a bit of time to rewrite it with those optimizations in mind.
Code: Select all
-- Code by Rafael Navega (2024)
-- License: Public Domain
-- Allow 'srt' to be used as a metatable for instances of subtitles objects.
local srt = {}
srt.__index = srt
-- The 'msDT' time offset should be in MILLISECONDS.
--
-- To be called from love.update() like so:
-- mySubtitle:advance(dt * 1000)
--
-- Returns true if the index of the node being sampled has changed, or false if it hasn't.
function srt:advance(msDT)
local newTime = self.time + msDT
self.time = newTime
-- Check if we need to advance to a future node whose *end time* is ahead of
-- the current seek time.
local newIndex = self.currentIndex or 1
local node = self.parsedNodes[newIndex]
while node and newTime > node.endTime do
newIndex = newIndex + 1
node = self.parsedNodes[newIndex]
end
if self.currentIndex ~= newIndex then
if newIndex > self.lastIndex then
self.currentIndex = self.lastIndex
else
self.currentIndex = newIndex
end
return true
end
return false
end
-- The 'msDT' time offset should be in MILLISECONDS.
function srt:rewind(msDT)
local newTime = self.time - msDT
self.time = newTime
-- Check if we need to rewind to a previous node whose *start time* is behind
-- the current seek time.
local newIndex = self.currentIndex or self.lastIndex
local node = self.parsedNodes[newIndex]
while node and newTime < node.startTime do
newIndex = newIndex - 1
node = self.parsedNodes[newIndex]
end
if self.currentIndex ~= newIndex then
if newIndex < 1 then
self.currentIndex = 1
else
self.currentIndex = newIndex
end
return true
end
return false
end
function srt:getText()
local node = self:getNode()
return (node and node.text or nil)
end
function srt:getNode()
local node = self.parsedNodes[self.currentIndex]
if node and self.time >= node.startTime and self.time <= node.endTime then
return node
else
return nil
end
end
function srt:getTime()
return self.time
end
-- The absolute 'msTime' should be in MILLISECONDS.
function srt:setTime(msTime)
self.time = msTime
-- Seek from the beginning until we find some near node, like the next
-- node whose end time is bigger than 'msTime'.
for index = 1, self.lastIndex do
if self.parsedNodes[index].endTime > msTime then
self.currentIndex = index
return
end
end
end
function srt:clear()
-- Note: when on LuaJIT, there's a special method for the fast clearing of tables.
-- require('table.clear')
-- (...)
-- table.clear(self.parsedNodes)
-- Read more in here: https://luajit.org/extensions.html
for index = 1, self.lastIndex do
self.parsedNodes[index] = nil
end
self.lastIndex = 0
end
function srt:parse(srtString)
self:clear()
local nodeCount = 0
-- Make sure that the input text finishes with the SRT blank line separator ('\n\n'), so
-- that the last subtitle piece is definitely read. If the file already ends on that separator
-- then this won't affect anything.
srtString = srtString .. '\n\n'
-- SRT format from: https://en.wikipedia.org/wiki/SubRip#Format
-- Note: added a safeguard in case the SRT file is "extended" with coordinates
-- after the timestamps, in the same line.
for srtIndex, hours1, minutes1, seconds1, millis1,
hours2, minutes2, seconds2, millis2, text in srtString:gmatch(
'(%d+)\n'..
'(%d%d):(%d%d):(%d%d),(%d%d%d) %-%-> (%d%d):(%d%d):(%d%d),(%d%d%d)[^\n]*\n'..
'(.-)\n'..
'\n') do
-- Create a new node.
nodeCount = nodeCount + 1
local newNode = {
-- Transform the timecodes into milliseconds integers, easier to compare.
-- Note: strings are being implicitly coerced into numbers.
startTime = hours1 * 3600000 + minutes1 * 60000 + seconds1 * 1000 + millis1,
endTime = hours2 * 3600000 + minutes2 * 60000 + seconds2 * 1000 + millis2,
index = nodeCount,
text = text
}
table.insert(self.parsedNodes, newNode)
end
-- Reset the subtitle scanning to read from the first node.
self.time = 0.0
self.currentIndex = (nodeCount > 0 and 1 or nil)
self.lastIndex = nodeCount
end
-- Used to create new instances of a subtitle object that parsed a piece of text.
-- To be used like so:
-- local mySubtitle = srt.new(myString)
function srt.new(srtString)
-- Apply 'srt' as the metatable to a new blank table, so all shared functions and
-- shared variables are available.
local subs = setmetatable({time=0.0, parsedNodes={}, lastIndex=0}, srt)
if srtString and type(srtString) == 'string' then
subs:parse(tostring(srtString))
end
return subs
end
function tests()
local testString=[[1
00:02:16,612 --> 00:02:19,376
Senator, we're making
our final approach into Coruscant.
2
00:02:19,482 --> 00:02:21,609
Very good, Lieutenant.
3
00:03:13,336 --> 00:03:15,167
We made it.
4
00:03:18,608 --> 00:03:20,371
I guess I was wrong.
5
00:03:20,476 --> 00:03:22,671
There was no danger at all.]]
local mySubtitle = srt.new(testString)
for index = 1, mySubtitle.lastIndex do
local node = mySubtitle.parsedNodes[index]
print(node.index)
print(node.startTime, node.endTime)
print(node.text)
print()
end
print('-----------')
-- Should be nil (time hasn't yet reached the first node, at 02:16.612)
print('Current time:', mySubtitle.time, 'Text:', mySubtitle:getText())
-- Test advancing 2 minutes and twenty seconds, in milliseconds.
-- This should sample subtitle node #2, that ranges 02:19.482 ~ 02:21.609.
print('Advancing...')
mySubtitle:advance((2*60 + 20) * 1000)
print('Current time:', mySubtitle.time, 'Text:', mySubtitle:getText())
-- Test seeking and rewinding.
-- This should sample subtitle node #4, that ranges 03:18.608 ~ 03:20.371.
print('Seeking & rewinding...')
mySubtitle:setTime((3 * 60 + 22) * 1000)
mySubtitle:rewind(3000)
print('Current time:', mySubtitle.time, 'Text:', mySubtitle:getText())
end
-- Optional, run the tests function and print to console.
--tests()
return srt
Edit 4: Fixed some cases when the index counter could turn nil and stay there.
Last edited by RNavega on Tue Feb 06, 2024 4:32 am, edited 2 times in total.
-
- Citizen
- Posts: 67
- Joined: Sat May 08, 2021 9:45 pm
Re: [LIBRARY] srt.lua A simple srt caption parser
Thanks for the contribution i might take a deeper look and test it on my free time, my compromises were that no one had made a library for this purpose earlier and while i know a fair bit of lua code i don't consider myself a master of coding. that "a crappy implementation" written on my description i wasn't joking, this library was a "throw things on the wall to see what sticks" because i wanted a quick & dirty implementation of it up and running.RNavega wrote: ↑Thu Jan 11, 2024 1:57 am Hi Alejandro, congratulations on getting it done. I always learn a lot when I finish a project, I'm sure it must've been the same for you.
With that said, I don't think it's quite production-quality yet. There's room for a lot of optimizations.
For example, reading your code, the update(delta) method parses the entire SRT file on each call. When you call it from love.update() like in your usage notes, that means it's being run per frame on 60FPS or at least 30FPS. That's a lot of redundant work.
You need to cache the structures you're creating when parsing so you can reuse them later.
Another thing is, making it faster to sample what's the current subtitle to display given the current seek time.
Seeking will always involve some sort of looping (like a binary search etc) since there's no easy way to hash the start time and end time of a subtitle in a way that lets you just specify the time and have it spit out the appropriate subtitle. This is the same challenge that keyframed animations have, with animation code having to locate what's the current keyframe to use based on the animation seek time.
However, in both cases for keyframes and subtitles, you can optimize things based on the fact that, for most of the time, you're usually advancing forward in time, so you only need to keep track of the current subtitle and when the time advances past its end time, you can iterate from that subtitle onward until you find the next one whose end time is ahead of the current seek time. This makes it cheap to query the current subtitle text to display.
Seeking backwards is very rare, like only when the user rewinds to some previous point or something like that, so it's okay if it's done with a brute-force loop.
I took a bit of time to rewrite it with those optimizations in mind.Good luck with your future projects.Code: Select all
-- Allow 'srt' to be used as a metatable for instances of subtitles objects. local srt = {} srt.__index = srt -- The 'msDT' time offset should be in MILLISECONDS. -- -- To be called from love.update() like so: -- mySubtitle:advance(dt * 1000) function srt:advance(msDT) self.time = self.time + msDT -- Check if we need to advance to a future node whose end time is ahead of -- the current seek time. if self.currentNode and self.time > self.currentNode.endTime then local currentIndex = self.currentNode.index local node = self.currentNode while node and self.time > node.endTime do currentIndex = currentIndex + 1 node = self.parsedNodes[currentIndex] end if self.currentNode ~= node then self.currentNode = node return true end end return false end function srt:getText() if self.currentNode and self.time >= self.currentNode.startTime then return self.currentNode.text else return nil end end function srt:getTime() return self.time end -- The absolute 'msTime' should be in MILLISECONDS. function srt:setTime(msTime) self.time = msTime -- Seek until we find the next subtitle node whose end time is bigger than 'time'. for index = 1, #self.parsedNodes do local node = self.parsedNodes[index] if node.endTime > msTime then self.currentNode = node return end end end function srt:clear() -- Note: when on LuaJIT, there's a special method for the fast clearing of tables. -- require('table.clear') -- (...) -- table.clear(self.parsedNodes) -- Read more here: https://luajit.org/extensions.html for index = 1, #self.parsedNodes do self.parsedNodes[index] = nil end self.currentNode = nil end function srt:parse(srtString) self:clear() local nodeCount = 0 -- Make sure that the input text finishes with the SRT blank line separator ('\n\n'), so -- that the last subtitle piece is definitely read. If the file already ends on that separator -- then this won't affect anything. srtString = srtString .. '\n\n' -- SRT format from: https://en.wikipedia.org/wiki/SubRip#Format for srtIndex, hours1, minutes1, seconds1, millis1, hours2, minutes2, seconds2, millis2, text in srtString:gmatch( '(%d+)\n'.. '(%d%d):(%d%d):(%d%d),(%d%d%d) %-%-> (%d%d):(%d%d):(%d%d),(%d%d%d)\n'.. '(.-)\n'.. '\n') do -- Create a new node. nodeCount = nodeCount + 1 local newNode = { -- Transform the timecodes into milliseconds integers, easier to compare. -- Note: strings are being implicitly coerced into numbers. startTime = hours1 * 3600000 + minutes1 * 60000 + seconds1 * 1000 + millis1, endTime = hours2 * 3600000 + minutes2 * 60000 + seconds2 * 1000 + millis2, index = nodeCount, text = text } table.insert(self.parsedNodes, newNode) end -- Reset the subtitle scanning to read from the first node. self.time = 0.0 self.currentNode = self.parsedNodes[1] end function srt.new(srtString) -- Apply 'srt' as the metatable to a new blank table, so all shared functions and -- shared variables are available. local subs = setmetatable({time=0.0, parsedNodes={}, currentNode=nil}, srt) if srtString and type(srtString) == 'string' then subs:parse(tostring(srtString)) end return subs end return srt
Edit: updated the above with a better pattern that reads whole blocks at once, instead of using a state machine parser as it was before.
Edit 2: fixed a bug in srt:advance().
about the "Rewind is rare" in my case that is a pickle since this library was born in a game where will be dialogue playing backwards so i didn't wanted to risk it an so i just made the crappy way brute forcing the file each frame. maybe if I iterate your take of my code (once i understand it) so is resilient to go backwards and such things. i will update the code with it.
I know my O() function is garbage, but since generally even a 3hr file could get parsed in so little i took the performance hit.
Once i test it for my use cases i'll integrate it, so do you want to get credited as a contributor? if so what display name and such
I'm glad people took a peek at my lib though.
Edit: i took a look at the code and i couldn't make it work my guess is the parse method is not making parsedNodes successfully, not even converting the units (since it shall be retro compatible and most of the units passed are floats in seconds) also my brain hurts with lua's oop way of doing things, i mean i do understand it but man that mental parsing hurts so bad
Last edited by alejandroalzate on Sun Feb 04, 2024 10:54 pm, edited 4 times in total.
Code: Select all
target = boardIndex.getWhosPeekingThisLine()
target:setObey(true)
-
- Citizen
- Posts: 67
- Joined: Sat May 08, 2021 9:45 pm
Re: [LIBRARY] srt.lua A simple srt caption parser
A game, it contains dialogs, voiced music and such
Code: Select all
target = boardIndex.getWhosPeekingThisLine()
target:setObey(true)
Re: [LIBRARY] srt.lua A simple srt caption parser
Since your code works, I don't consider it garbage. But it would be much faster using caching -- not in a noticeable way though, probably microseconds faster.alejandroalzate wrote: ↑Sun Feb 04, 2024 7:13 pm I know my O() function is garbage, but since generally even a 3hr file could get parsed in so little i took the performance hit.
Okay, I added my name and the Public Domain license, but you don't have to use it at all.alejandroalzate wrote: ↑Sun Feb 04, 2024 7:13 pm Once i test it for my use cases i'll integrate it, so do you want to get credited as a contributor? if so what display name and such
I'm glad people took a peek at my lib though.
The example is functional and is how I'd do it, with those optimizations and the relative seeking (seeking from the current node onwards or backwards, instead of brute-forcing from the start or the end of the file).
I added a tests() function that comes with the sample SRT from the Wikipedia article, please check it out.alejandroalzate wrote: ↑Sun Feb 04, 2024 7:13 pm Edit: i took a look at the code and i couldn't make it work my guess is the parse method is not making parsedNodes successfully, not even converting the units (since it shall be retro compatible and most of the units passed are floats in seconds) also my brain hurts with lua's oop way of doing things, i mean i do understand it but man that mental parsing hurts so bad
Also note the comments, you need to pass the time as milliseconds (which is timeInSeconds * 1000).
But anyway, if you'd rather use seconds as units, you can change the code in :parse() to use seconds to be like this:
Code: Select all
local newNode = {
-- Transform the timestamps into seconds.
-- Note: strings are being implicitly coerced into numbers.
startTime = hours1 * 3600 + minutes1 * 60 + seconds1 + millis1 / 1000.0,
endTime = hours2 * 3600 + minutes2 * 60 + seconds2 + millis2 / 1000.0,
index = nodeCount,
text = text
}
Code: Select all
print('Advancing...')
mySubtitle:advance((2*60 + 20))
print('Current time:', mySubtitle.time, 'Text:', mySubtitle:getText())
-- Test seeking and rewinding.
-- This should sample subtitle node #4, that ranges 03:18.608 ~ 03:20.371.
print('Seeking & rewinding...')
mySubtitle:setTime((3 * 60 + 22))
mySubtitle:rewind(3)
-
- Citizen
- Posts: 67
- Joined: Sat May 08, 2021 9:45 pm
Re: [LIBRARY] srt.lua A simple srt caption parser
I swear it doesn't make parsedNodes now i'm sure using zbstudio are you doing any luaJIT shenanigans or obscure tricks as you noted on srt:clear that i'm not aware of?
I swear i followed your steps the thing is broken for me:
As you can see the console is past the srt.new()
Edit omg i'm cringing so hard rn of my blindness you version of the code cares if you call srt:new() instead of srt.new() (although i gonna take a look if i did used colon on other places)
nope the same deal:
Now parsed nodes work but the output is still nil, also i did a conscious decision of when there's no subtitle present at the time return an empty string witch ensures the same result as nil but avoiding to deal with nil on love.graphics.print() since for what i remember it doesn't like it
I swear i followed your steps the thing is broken for me:
As you can see the console is past the srt.new()
Edit omg i'm cringing so hard rn of my blindness you version of the code cares if you call srt:new() instead of srt.new() (although i gonna take a look if i did used colon on other places)
nope the same deal:
Code: Select all
local function loadCaptions()
--Load captions
local srtLUT = { english = "en", spanish = "es" }
local captionsText = ""
local info = love.filesystem.getInfo
local correctCaptions = info("res/sfx/songs/monster/monster." .. srtLUT[getNestedValue("config.system.language")] .. ".srt")
local englishCaptions = info("res/sfx/songs/monster/monster.en.srt")
local genericCaptions = info("res/sfx/songs/monster/monster.srt")
if genericCaptions then captionsText = love.filesystem.read("res/sfx/songs/monster/monster.srt") end
if englishCaptions then captionsText = love.filesystem.read("res/sfx/songs/monster/monster.en.srt") end
if genericCaptions then captionsText = love.filesystem.read("res/sfx/songs/monster/monster." .. srtLUT[getNestedValue("config.system.language")] .. ".srt") end
globalResources.obj.srtobj = modules.srt.new(captionsText)
end
- Attachments
Code: Select all
target = boardIndex.getWhosPeekingThisLine()
target:setObey(true)
Re: [LIBRARY] srt.lua A simple srt caption parser
That's a cool IDE, it shows a stack / debug info, very useful. I code in Notepad++ like a peasant, it's just the text and that's it.
There are good reasons for either case: return an empty string so you can plug :getText() directly into love.graphics.print() and not have to worry about it returning nil:
Or, return nil in case you want to use it in boolean expressions, as only false and nil are falsy values in Lua. Everything else, including zero and empty strings, are truthy.
PS there was a bug with nil indexes, I updated that code with it.
That's a good debate, should it return nil or an empty string when the current time isn't sampling any subtitle nodes (i.e. the blank time between nodes) ?alejandroalzate wrote: ↑Mon Feb 05, 2024 2:56 pm Now parsed nodes work but the output is still nil, also i did a conscious decision of when there's no subtitle present at the time return an empty string witch ensures the same result as nil but avoiding to deal with nil on love.graphics.print() since for what i remember it doesn't like it
There are good reasons for either case: return an empty string so you can plug :getText() directly into love.graphics.print() and not have to worry about it returning nil:
Code: Select all
-- If returning an empty string.
love.graphics.print(mySubtitle:getText(), ...)
Code: Select all
-- If returning nil.
local text = mySubtitle:getText()
if text then
love.graphics.print(text, ...)
end
-
- Citizen
- Posts: 67
- Joined: Sat May 08, 2021 9:45 pm
Re: [LIBRARY] srt.lua A simple srt caption parser
In the IDE: that ide is called ZeroBraneStudio (For me is 0 brain studio lol) my choice is sublime but when a bug escapes my comprehension i go to zbstudioRNavega wrote: ↑Tue Feb 06, 2024 4:28 am That's a cool IDE, it shows a stack / debug info, very useful. I code in Notepad++ like a peasant, it's just the text and that's it.
That's a good debate, should it return nil or an empty string when the current time isn't sampling any subtitle nodes (i.e. the blank time between nodes) ?alejandroalzate wrote: ↑Mon Feb 05, 2024 2:56 pm Now parsed nodes work but the output is still nil, also i did a conscious decision of when there's no subtitle present at the time return an empty string witch ensures the same result as nil but avoiding to deal with nil on love.graphics.print() since for what i remember it doesn't like it
There are good reasons for either case: return an empty string so you can plug :getText() directly into love.graphics.print() and not have to worry about it returning nil:Or, return nil in case you want to use it in boolean expressions, as only false and nil are falsy values in Lua. Everything else, including zero and empty strings, are truthy.Code: Select all
-- If returning an empty string. love.graphics.print(mySubtitle:getText(), ...)
PS there was a bug with nil indexes, I updated that code with it.Code: Select all
-- If returning nil. local text = mySubtitle:getText() if text then love.graphics.print(text, ...) end
In the debate: my library spits an empty string when there's no sub available in the time this ensures that the font methods getWidth and getHeight doesn't brick itself, and "as only false and nil are falsy values in Lua. Everything else, including zero and empty strings, are truthy." for what i know 0 is treated as false as well, i might check but that is what i remember
I checked I've lived in a lie :
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
> if 0 then
>> print("true")
>> else
>> print("false")
>> end
true
I know the clear oversight of an actual caption with and empty line but then that caption line is usseless anyway, so why bother for a real empty string.
my take when i do a check is
Code: Select all
local isTextPresent = mySub:getText() ~= ""
Code: Select all
target = boardIndex.getWhosPeekingThisLine()
target:setObey(true)
Who is online
Users browsing this forum: Ahrefs [Bot], Google [Bot] and 7 guests