I made a script to upgrade subtitles to titles

hello everyone!

I have been using Kdenlive for sometime now and i absolutely love it! especially the auto subtitle feature. unfortunately the subtitles themselves lack a bit of customization, unlike title clips. so i needed a way to turn subtitles into titles, like “the upgrade captions to graphics” feature in premiere.

I know upgrades to the subtitle editor are in the works but in the meantime i hope someone finds this helpful.

It’s a simple bash script to convert SRT files into kdenlive Title files, you just need a title clip template (.kdenlivetitle) and the SRT file itself in the same directory as the script.

the title clip template must meet 2 criteria in order to work:

  • duration is 30 frames
  • text is %s

the output is a folder called “Kden_Titles” :

  • drag and drop it to kdenlive bin.
  • drag and drop the clips to an empty video track.

make sure the bin is sorted by name so that the clips are in the correct order.
some clip files will have “_” in the name, those are blanks used for padding so that the real clips are correctly positioned in the timeline. if you wish to remove them you can sort by date and all blanks will be at the bottom.

#!/bin/bash

read -p "frame rate:"$'\n' frate
[ "$frate" = "" ] && frate=60
echo "..."

[ -d ./Kden_Titles/ ] && rm -r ./Kden_Titles
mkdir -p Kden_Titles

readarray -t frm < <( (sed -n '2~4p' ./*.srt) )
readarray -t sub < <( (sed -n '3~4p' ./*.srt) )

n=1
w=$(bc<<<"length(${#sub[@]}*2)")

for i in "${!frm[@]}"; do
	
	b=$(date -d "${frm[i]:0:12}" "+%S.%3N")
	e=$(date -d "${frm[i]:17:12}" "+%S.%3N")
	ee=$(date -d "${frm[i-1]:17:12}" "+%S.%3N")

	if [ "$i" -eq 0 ]; then ee=0; fi
	if [ "$(bc<<<"$b<$ee && $i!=0")" -eq 1 ]; then b="$(bc<<<"$b+60")"; fi
	if [ "$(bc<<<"$e<$b")" -eq 1 ]; then e="$(bc<<<"$e+60")"; fi

	blank="$(bc <<< "($b*$frate+0.5)/1-($ee*$frate+0.5)/1")"
	duration="$(bc <<< "($e*$frate+0.5)/1-($b*$frate+0.5)/1")"

	if [ "$blank" -gt 0 ]; then	
	sed -e "s/30/$blank/" -e "s/%s//" ./*.kdenlivetitle* > ./Kden_Titles/"$(printf "%0*d" "$w" "$n")"_.kdenlivetitle
	((n++))
	fi

	sed -e "s/30/$duration/" -e "s/%s/${sub[i]}/" ./*.kdenlivetitle* > ./Kden_Titles/"$(printf "%0*d" "$w" "$n")".kdenlivetitle
	((n++))

done

sleep 1
echo "Titles in $PWD/Kden_Titles"$'\n'
touch ./Kden_Titles/*_*

$SHELL
2 Likes

Hi Grog
Thank you for your contribtion! I tested your script on Windows and the converter doesn’t work. I get following error:

$ sh Subtitle-converter.sh
frame rate:
30
...
Subtitle-converter.sh: line 14: bc: command not found
Subtitle-converter.sh: line 23: bc: command not found
Subtitle-converter.sh: line 23: [: : integer expression expected
Subtitle-converter.sh: line 24: bc: command not found
Subtitle-converter.sh: line 24: [: : integer expression expected
Subtitle-converter.sh: line 26: bc: command not found
Subtitle-converter.sh: line 27: bc: command not found
Subtitle-converter.sh: line 29: [: : integer expression expected
Subtitle-converter.sh: line 23: bc: command not found
...
Subtitle-converter.sh: line 29: [: : integer expression expected
Titles in /c/users/customer/videos/titles/Kden_Titles

Here my srt file:

1
00:00:00,000 --> 00:00:02,200
Eine 29-jährige Frau war auf dem Weg zur

2
00:00:02,200 --> 00:00:04,100
Arbeit, als ihr SUV heute Morgen in

3
00:00:04,100 --> 00:00:06,333
Heightsville von einem CSX-Zug angefahren

4
00:00:06,333 --> 00:00:06,833
wurde.

5
00:00:07,466 --> 00:00:09,200
Erstaunlicherweise überlebte sie diesen

6
00:00:09,200 --> 00:00:10,433
Unfall und diesen Absturz.

What could be the issue?

Your sh on windows does not like having a string used in a numeric comparison test (eg. [ “$blank” -gt 0 ]), and you do not have the ‘bc’ utility installed, which is an arbitrary precision calculator that’s a standard *nix tool.

If you have a linux environment installed on your windows machine it should be able to run there, but there may also be native bash and bc binaries available somewhere.

Thank you for the hint. I installed bc via pacman and now the script runs without error. And I guess it could check -gt as well (greater then).

But the subtitle text is not in the title clip. Mmh.

hey @Eugen_Mohr , I’m glad you got the script to run !
as Ron said i didn’t have windows in mind when i wrote this tbh.

if subtitle text is not in the title clip then make sure the template text is: “placeholder” case sensitive and without the quotes.
let me know how it goes.

Since the Template Title function in Kdenlive already uses %s as the placeholder, could your script be changed to use that? It may be confusing atm due to the ambiguity …

that’s a great idea !
all you’d have to do is change the word placeholder with %s in lines 30 and 34

should i modify the post to include this change ?

Template text is: “placeholder” (BTW: The script runs with %s instead of placeholder)

And it creates 7 titles in folder Kden_Titles. Here the content of 01.kdenlivetitle:

01.kdenlivetitle
<kdenlivetitle duration="125" LC_NUMERIC="C" width="1920" height="1080" out="125">
 <item type="QGraphicsTextItem" z-index="0">
  <position x="607" y="335">
   <transform>1,0,0,0,1,0,0,0,1</transform>
  </position>
  <content line-spacing="0" shadow="1;#64000000;3;5;5" font-underline="0" box-height="90" font-outline-color="0,0,0,255" font="Arial Black" letter-spacing="0" font-pixel-size="64" font-italic="0" typewriter="0;2;1;0;0" alignment="2" font-weight="50" font-outline="2" box-width="284" font-color="255,255,255,255">Shadow</content>
 </item>
 <startviewport rect="0,0,1920,1080"/>
 <endviewport rect="0,0,1920,1080"/>
 <background color="0,0,0,0"/>
</kdenlivetitle>
<kdenlivetitle LC_NUMERIC="C" duration="66" height="480" out="30" width="640">
 <startviewport rect="0,0,640,480"/>
 <endviewport rect="0,0,640,480"/>
 <background color="0,0,0,0"/>
</kdenlivetitle>
<kdenlivetitle duration="250" LC_NUMERIC="C" width="1920" height="1080" out="250">
 <item type="QGraphicsRectItem" z-index="2">
  <position x="1179" y="235">
   <transform zoom="100">1,0,0,0,1,0,0,0,1</transform>
  </position>
  <content brushcolor="170,85,0,255" pencolor="0,0,0,255" penwidth="0" rect="0,0,467.588,466.706"/>
 </item>
 <item type="QGraphicsTextItem" z-index="0">
  <position x="697" y="349">
   <transform>1,0,0,0,1,0,0,0,1</transform>
  </position>
   <content shadow="0;#64000000;3;3;3" font-underline="0" box-height="65" font-outline-color="0,0,0,255" font="MS Shell Dlg 2" letter-spacing="0" font-pixel-size="54" font-italic="0" typewriter="0;2;1;0;0" alignment="2" font-weight="50" font-outline="0" box-width="105" font-color="255,255,255,255">Text</content>
  </item>
  <item type="QGraphicsRectItem" z-index="-1">
   <position x="412" y="168">
    <transform zoom="100">1,0,0,0,1,0,0,0,1</transform>
   </position>
   <content brushcolor="0,0,0,255" pencolor="0,0,0,255" penwidth="0" rect="0,0,529.235,287.353"/>
  </item>
  <startviewport rect="0,0,1920,1080"/>
  <endviewport rect="0,0,1920,1080"/>
  <background color="0,0,0,0"/>
 </kdenlivetitle>

Up to you but probably a good idea. I’ll do it in the manual …

1 Like

j7 files is a good start, your srt has 6 lines so one clip is for padding

that’s weird, do all clips just say “text”?
does it work with %s instead ?

Yes, all 7 clips/titles say just ,255>text</content>

Yes, the script works with %s instead of placeholder

Could it be the typical Linux/Windows slash/backslash issue (/ vs // or \ in Windows)?

I’m not sure tbh i’m baffled by this …
could you tell me how you are running bash scripts in windows ? i’ll give it a try

i rewrote the script for powershell, this should work on windows no problem.
(save as .ps1)

# Prompt for frame rate
$frate = Read-Host "frame rate"
if (-not $frate) { $frate = 60 }
Write-Host "..."

# Remove existing Kden_Titles directory if it exists
if (Test-Path -Path "./Kden_Titles/") { Remove-Item -Path "./Kden_Titles/" -Recurse -Force }
New-Item -ItemType Directory -Path "Kden_Titles"

# Read frames and subtitles from SRT files
$frm = Get-Content -Path '*.srt' | Select-Object -Skip 1 | ForEach-Object -Begin {$i=0} -Process {if ($i++ % 4 -eq 0) {$_}}
$sub = Get-Content -Path '*.srt' | Select-Object -Skip 2 | ForEach-Object -Begin {$i=0} -Process {if ($i++ % 4 -eq 0) {$_}}
# output File name width / counter
$w = [math]::Ceiling($sub.Count * 2)
$w = "$w".length
$n = 1
# Template file placeholders
[regex] $p_1='30'
[regex] $p_2='%s'

for ($i = 0; $i -lt $frm.Count; $i++) {
    # Timing
    $b = [datetime]::ParseExact($frm[$i].Substring(0, 12), "hh:mm:ss,fff", $null).ToString("ss.fff")
    $e = [datetime]::ParseExact($frm[$i].Substring(17, 12), "hh:mm:ss,fff", $null).ToString("ss.fff")
    $ee = if ($i -gt 0) { [datetime]::ParseExact($frm[$i - 1].Substring(17, 12), "hh:mm:ss,fff", $null).ToString("ss.fff") } else { 0 }
    # Add 60s if necessary
    if ($i -eq 0) { $ee = 0 }
    if ($b -lt $ee -and $i -ne 0) { $b = [math]::Round([double]$b + 60, 3) }
    if ($e -lt $b) { $e = [math]::Round([double]$e + 60, 3) }
    # Clip length / padding
    $blank = [math]::Round([decimal]$b * $frate + 0.1) - [math]::Round([decimal]$ee * $frate + 0.1)
    $duration = [math]::Round([decimal]$e * $frate + 0.1) - [math]::Round([decimal]$b * $frate + 0.1)
    # Replace placeholders
    if ($blank -gt 0) {
        Get-Content -Path ./*.kdenlivetitle* |
	    ForEach-Object { $p_1.replace("$_", "$blank", 1) } | 
	    ForEach-Object { $p_2.replace("$_", '',1) } |
        Set-Content -Path "./Kden_Titles/$($n.ToString("D$w"))_.kdenlivetitle"
        $n++
    }
    Get-Content -Path ./*.kdenlivetitle* |
    ForEach-Object { $p_1.replace("$_", "$duration", 1) } |
    ForEach-Object { $p_2.replace("$_", $sub[$i],1) } | 
    Set-Content -Path "./Kden_Titles/$($n.ToString("D$w")).kdenlivetitle"
    $n++
}

Start-Sleep -Seconds 1
Write-Host "`nTitles in $PWD\Kden_Titles`n"
# Set date modified for blank clips
(Get-ChildItem -Path ./Kden_Titles/*_*) | % {$_.LastWriteTime = (Get-Date)}

( Btw is there a limit on post edits ? can’t seem to edit my original post )

Only because you’re ‘new’ here. You’ll automatically get more privileges if you stick around and play nice.

1 Like

Thank you for the PowerShell script! It runs on Windows.
Still, it doesn’t read/generate the subtitle text. The output looks the same as with the bash script.
Here my:

placeholder.kdenlivetitle
<kdenlivetitle LC_NUMERIC="C" duration="30" height="480" out="30" width="640">
 <startviewport rect="0,0,640,480"/>
 <endviewport rect="0,0,640,480"/>
 <background color="0,0,0,0"/>
</kdenlivetitle>

I think the script reads the wrong srt line?

is this your template or output ?

if possible upload your srt, template and output files somewhere and link them to me

This is my title-template. I’ll uplaod the files tomorrow.

@Grog here the link to the files: KDE Collaborative Storage

@Eugen_Mohr
i think i see the problem, your template has no text inside! also the script is picking up other templates in it’s directory.

  • make sure the template actually has %s as text
  • make sure the template is the only “.kdenlivetitle” file in that directory

if for some reason you did put %s as text and kdenlive is removing it then just input any other text and replace %s in the script (line 19) with that text.

Embarrassing. Proper reading would help. It works!

The trick is that the title template should be proper formatted with the correct font size and place of the text you want. Once the folder Kden_Titles is dropped to the bin and sorted by name you can select all title clips and drop it to the timeline and the order of the titles are kept.

At the moment the script adds the srt text at the template text position to the right.

One suggestion. Would it be possible that the script asks if the title text should be centered? Meaning the %s is center position of the text?

1 Like