Initial commit of the source code in the repository.
This commit is contained in:
763
CSubtitle.wdc
Normal file
763
CSubtitle.wdc
Normal file
@@ -0,0 +1,763 @@
|
||||
#To edit and compare internal_properties, use WINDEV integrated tools.
|
||||
#Internal properties refer to the properties of controls in windows, reports, etc.
|
||||
info :
|
||||
name : CSubtitle
|
||||
major_version : 30
|
||||
minor_version : 0
|
||||
type : 4
|
||||
description : ""
|
||||
subtype : 0
|
||||
options : 256
|
||||
class :
|
||||
identifier : 0x181e7a1f02f43ce8
|
||||
internal_properties : HwAAAB4AAAD+1kTo6KJy2WQU0Y4fbFD6aQ7NWfD4KKkN7Ml67nIqLXIRxJ1vjfJ1
|
||||
code_elements :
|
||||
type_code : 10
|
||||
p_codes :
|
||||
-
|
||||
code : |1+
|
||||
/* Copyright 2025 Alexandre Leclerc. MPL 2.0. See https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Allow to read and write SRT files...
|
||||
|
||||
CSubtitle est une Classe
|
||||
public
|
||||
m_sFilename est une chaine
|
||||
m_tabContent est un tableau local de CSubtitleEntry // CAUTION: "local" and not "dynamic": When the class is copied, each record must be copied and unique (due to some destructive processes).
|
||||
m_nMaxCharPerLine est un entier = 60 // When splitting sentences, use this a max number of characters there should be per line
|
||||
fin
|
||||
|
||||
TextFormat est une énumération
|
||||
tfContinuousText = 1
|
||||
tfParagraphOnTimestampBreak = 2
|
||||
tfParagraphOnSentence = 3
|
||||
fin
|
||||
|
||||
ETimecodeRangeAction est une énumération
|
||||
traKeepEntry // Will keep the whole entry
|
||||
traRemoveEntry // Will remove the whole entry
|
||||
traTrimEntry // Will trim the entry, if it is at least 1000ms long in duration
|
||||
fin
|
||||
type : 131072
|
||||
procedures :
|
||||
-
|
||||
name : Constructeur
|
||||
procedure_id : 1737960779823791336
|
||||
type_code : 27
|
||||
code : |1+
|
||||
procédure Constructeur()
|
||||
|
||||
type : 589824
|
||||
-
|
||||
name : Destructeur
|
||||
procedure_id : 1737960779823856872
|
||||
type_code : 28
|
||||
code : |1+
|
||||
procédure Destructeur()
|
||||
|
||||
type : 655360
|
||||
-
|
||||
name : LoadFromFile
|
||||
procedure_id : 1737960779823922408
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : <indiquez ici ce que fait la procédure>
|
||||
// Paramètres :
|
||||
// sFilename (chaîne ANSI) : <indiquez ici le rôle de sFilename>
|
||||
// Valeur de retour :
|
||||
// booléen : <indiquez ici le rôle de la valeur de retour>
|
||||
//
|
||||
procédure LoadFromFile(sFilename est chaine)
|
||||
|
||||
si pas fFichierExiste(sFilename) ALORS
|
||||
ErreurDéclenche(101,"File does not exist.")
|
||||
renvoyer faux
|
||||
FIN
|
||||
|
||||
// Validate file format
|
||||
sFormat est une chaine = minuscule(fExtraitChemin(sFilename,fExtension))
|
||||
si pas sFormat dans (".srt", ".sbv") alors
|
||||
ErreurDéclenche(102,"This is not an supported format (.SRT or .SBV files).")
|
||||
RENVOYER Faux
|
||||
FIN
|
||||
|
||||
// Open file content
|
||||
sContent est une chaine = UTF8VersChaîne(fChargeTexte(sFilename))
|
||||
si sContent = "" _ET_ ErreurDétectée ALORS
|
||||
erreur(erreurinfo(errComplet))
|
||||
renvoyer faux
|
||||
FIN
|
||||
|
||||
m_sFilename = sFilename
|
||||
tableausupprimetout(m_tabContent)
|
||||
|
||||
// Create entries
|
||||
selon sFormat
|
||||
CAS ".srt" : _LoadDecodeSRT(sContent)
|
||||
cas ".sbv" : _LoadDecodeSBV(sContent)
|
||||
FIN
|
||||
|
||||
renvoyer Vrai
|
||||
type : 458752
|
||||
-
|
||||
name : ExportToSRT
|
||||
procedure_id : 1737960779823987944
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : <indiquez ici ce que fait la procédure>
|
||||
// Paramètres :
|
||||
// sFilename (chaîne ANSI) : <indiquez ici le rôle de sFilename>
|
||||
// Valeur de retour :
|
||||
// booléen : <indiquez ici le rôle de la valeur de retour>
|
||||
//
|
||||
procédure ExportToSRT(sFilename est chaîne)
|
||||
|
||||
si m_tabContent..Occurrence = 0 alors
|
||||
ErreurDéclenche(103,"There is no content to save.")
|
||||
renvoyer faux
|
||||
FIN
|
||||
|
||||
// Build content to save
|
||||
sContent est une chaine
|
||||
pour tout e de m_tabContent
|
||||
sContent += [rc+rc] + e.m_nID + rc + e.TimecodeToString(CSubtitleEntry.tfSRT) + rc + e.m_sText
|
||||
FIN
|
||||
|
||||
renvoyer fSauveTexte(sFilename,ChaîneVersUTF8(sContent))
|
||||
type : 458752
|
||||
-
|
||||
name : ContentToNumberedText
|
||||
procedure_id : 1737960779824053480
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Will return all the content in the form of a text with numbers matching the IDs of the timestamps.
|
||||
// Paramètres :
|
||||
// eParagraphFormat (CSRTFile.TextFormat) : Optional paragraph formatting.
|
||||
// Valeur de retour :
|
||||
// chaîne ANSI : <indiquez ici le rôle de la valeur de retour>
|
||||
//
|
||||
procédure ContentToNumberedText(eParagraphFormat est un TextFormat = tfContinuousText) : chaine
|
||||
|
||||
sContent est une chaîne
|
||||
|
||||
SI eParagraphFormat = tfParagraphOnTimestampBreak ALORS
|
||||
POUR TOUT e,i DE m_tabContent
|
||||
// Is this clip consecutive with the next one? If no, we insert a paragraph break
|
||||
sContent += [" "] + "{" + e.m_nID + "}" + [" "] + e.TextAsSingleLine()
|
||||
SI i+1 <= m_tabContent..Occurrence ALORS
|
||||
SI PAS m_tabContent[i].m_duTimecodeEnd = m_tabContent[i+1].m_duTimecodeStart ALORS
|
||||
sContent += RC
|
||||
FIN
|
||||
FIN
|
||||
FIN
|
||||
|
||||
sContent = remplace(sContent,rc+" ",rc)
|
||||
SINON
|
||||
POUR TOUT e DE m_tabContent
|
||||
sContent += [" "] + "{" + e.m_nID + "}" + [" "] + e.TextAsSingleLine()
|
||||
FIN
|
||||
|
||||
SI eParagraphFormat = tfParagraphOnSentence ALORS
|
||||
sContent = Remplace(sContent, ". ", "."+CRLF)
|
||||
sContent = Remplace(sContent, "! ", "!"+CRLF)
|
||||
sContent = Remplace(sContent, "? ", "?"+CRLF)
|
||||
FIN
|
||||
FIN
|
||||
|
||||
// Delete all unbreakable spaces that might be in the file. This messes things up for nothing
|
||||
// We also cleanup all double spaces
|
||||
sContent = Remplace(sContent," "," ") // Delete unbreakable spaces
|
||||
sContent = Remplace(sContent," "," ") // Delete all double spaces
|
||||
sContent = Remplace(sContent,RC+" ",RC) // Delete all cases where a new line starts with a space
|
||||
|
||||
renvoyer sContent
|
||||
type : 458752
|
||||
-
|
||||
name : ContentFromNumberedText
|
||||
procedure_id : 1737960779824119016
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Will take a numbered text with nested IDs and put back the text as the next SRT content.
|
||||
// Paramètres :
|
||||
// sTextBlock (chaîne ANSI) : Text block to use
|
||||
// bDeleteEntriesNotFound (booléen - valeur par défaut=0) : If an entry is not found in the text block, it will be deleted from the SRT file. Otherwise it will stay there.
|
||||
// bRemoveCommentsAndCRLF (booléen - valeur par défaut=1) : Considers a ligne begining with a "#" as a comment. Removes all CRLF in the text to have it as a valid TextBlock. This feature is useful if the textblock has been reviewed and formated during revision.
|
||||
// Valeur de retour :
|
||||
// Aucune
|
||||
//
|
||||
procédure ContentFromNumberedText(local sTextBlock est une chaine, bDeleteEntriesNotFound est un booléen = Faux, bRemoveCommentsAndCRLF est un booléen = Vrai)
|
||||
|
||||
si bRemoveCommentsAndCRLF ALORS
|
||||
sNewText est une chaine
|
||||
s est une chaine
|
||||
|
||||
// All # are considered as comment, even if they are MD titles. (This is on purpose).
|
||||
// An easy way to go around this is to simply add one space before #, and it will be treated as text part of the subtitles.
|
||||
POUR TOUTE CHAÎNE s DE sTextBlock SÉPARÉE PAR rc
|
||||
si s [= "#" alors
|
||||
continue
|
||||
FIN
|
||||
sNewText += s + rc
|
||||
FIN
|
||||
|
||||
// Remove all markdown from the text
|
||||
sNewText = MarkdownVersTexte(sNewText)
|
||||
|
||||
// We could also remove all HTML code from the text... but have to test that later.
|
||||
sNewText = HTMLVersTexte(sNewText)
|
||||
|
||||
// Remove double spaces that might have been created...
|
||||
sNewText = Remplace(sNewText, " ", " ")
|
||||
|
||||
// We are done with removing special formating
|
||||
sTextBlock = sNewText
|
||||
FIN
|
||||
|
||||
// Find each entry in the text block to get it's updated value
|
||||
pour tout e de m_tabContent
|
||||
sID est une chaine = "{"+e.m_nID+"}"
|
||||
|
||||
// Find the entry point
|
||||
i est un entier = position(sTextBlock, sID)
|
||||
si i = 0 ALORS
|
||||
si bDeleteEntriesNotFound ALORS
|
||||
TableauSupprime(m_tabContent, ElémentCourant)
|
||||
FIN
|
||||
continue
|
||||
FIN
|
||||
|
||||
// Find the end of the entry point
|
||||
o est un entier = position(sTextBlock, "{", i+1)
|
||||
si o = 0 alors
|
||||
o = taille(sTextBlock)+1
|
||||
FIN
|
||||
|
||||
sSubtitle est une chaine = sansespace(sTextBlock[[i+Taille(sID) À o-1]])
|
||||
|
||||
// Update the content
|
||||
e.p_sText = sSubtitle
|
||||
FIN
|
||||
type : 458752
|
||||
-
|
||||
name : ClipMergeOptimization
|
||||
procedure_id : 1737960779824250088
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Will try to merge clips in an optimized way, respecting a maximum length duration.
|
||||
// Paramètres :
|
||||
// nMaxClipDuration (entier) : Maximum clip duration (in sec) that cannot be exceded.
|
||||
// TranslationSRT (CSRTFile dynamique - valeur par défaut=0) : <indiquez ici le rôle de TranslationSRT>
|
||||
// bIgnoreSentenceOptimization (booléen - valeur par défaut=0) : <indiquez ici le rôle de bIgnoreSentenceOptimization>
|
||||
// Valeur de retour :
|
||||
// Aucune
|
||||
//
|
||||
procédure ClipMergeOptimization(nMaxClipDuration est une entier, TranslationSRT est un CSubtitle dynamique = Null, bIgnoreSentenceOptimization est un booléen = Faux)
|
||||
|
||||
// - We can try to optimize clip length for Dubbing in ElevenLabs. This will merge clips together to have a better length and a better rendering.
|
||||
// - Logic we will try to accomplish:
|
||||
// - Make a clip "stop" if there is a "end" punctuation to the clip (.!?) [option to disable if desired?].
|
||||
// - Make a clip stop before the maximum clip duration is reached (important).
|
||||
// - Do not merge clips that are not immediately consecutive.
|
||||
|
||||
// This is not our responsability to check if both SRT files are compatible, but we want to avoid a crash with different index count.
|
||||
si TranslationSRT <> Null _ET_ TranslationSRT.m_tabContent..Occurrence <> m_tabContent..Occurrence ALORS
|
||||
retour
|
||||
FIN
|
||||
|
||||
e est un CSubtitleEntry dynamique
|
||||
bMerged est un booléen
|
||||
|
||||
BOUCLE
|
||||
SI m_tabContent..Occurrence <= 1 ALORS
|
||||
SORTIR
|
||||
FIN
|
||||
i est un entier = 1
|
||||
bMerged = Faux
|
||||
|
||||
BOUCLE
|
||||
// Have we reached the end?
|
||||
si i+1 > m_tabContent..Occurrence ALORS
|
||||
sortir
|
||||
FIN
|
||||
|
||||
// Is this clip consecutive with the next one? If no, we cannot merge.
|
||||
si pas m_tabContent[i].m_duTimecodeEnd = m_tabContent[i+1].m_duTimecodeStart alors
|
||||
i++
|
||||
continuer
|
||||
fin
|
||||
|
||||
// Can both clip be merge length-wise? If it is too long, we cannot merge.
|
||||
d est une durée = m_tabContent[i].TimecodeDuration() + m_tabContent[i+1].TimecodeDuration()
|
||||
si d..EnSecondes > nMaxClipDuration alors
|
||||
i++
|
||||
CONTINUER
|
||||
FIN
|
||||
|
||||
// Avoid a merge? — Is clip punctuation too good for a merge? (Check the next one if it is also good.)
|
||||
si bIgnoreSentenceOptimization = Faux alors
|
||||
// Optimize strings for verification. We eliminate quotations are they are considered “transparent” punctuation. (And non-breaking, narrow non-breaking and thin space.)
|
||||
s1 est une chaine = sansespace( remplace(m_tabContent[i].m_sText, ["""", "«", "»", "“", "”", CaractUnicode(0x00A0), CaractUnicode(0x202F), CaractUnicode(0x2009)],"") )
|
||||
s2 est une chaîne = SansEspace( Remplace(m_tabContent[i+1].m_sText, ["""", "«", "»", "“", "”", CaractUnicode(0x00A0), CaractUnicode(0x202F), CaractUnicode(0x2009)],"") )
|
||||
si droite(s1,1) dans (".","!","?") ALORS
|
||||
// Unless the other sentence ends very well, we do not merge.
|
||||
SI pas droite(s2,1) DANS (".","!","?") ALORS
|
||||
i++
|
||||
CONTINUER
|
||||
FIN
|
||||
sinon
|
||||
// If this is nevertheless a not too bad spot to end, and there is nothing better after, we stop here too
|
||||
SI droite(s1,1) DANS (";",",",":","—","–") ALORS
|
||||
// So if we have a punctuation here, but nothing in the next one, we do not merge. We keep this ending
|
||||
SI PAS droite(s2,1) DANS (".","!","?",";",",",":","—","–") ALORS
|
||||
i++
|
||||
CONTINUER
|
||||
FIN
|
||||
FIN
|
||||
FIN
|
||||
FIN
|
||||
|
||||
// Merge
|
||||
// At this point me made all the tests we desired and we are good to merge
|
||||
m_tabContent[i].MergeWith(m_tabContent[i+1])
|
||||
tableausupprime(m_tabContent,i+1)
|
||||
|
||||
// If we have a translation SRT, we have to sync all the merges there too. We hope it works for it too.
|
||||
si TranslationSRT <> Null alors
|
||||
TranslationSRT.m_tabContent[i].MergeWith(TranslationSRT.m_tabContent[i+1])
|
||||
TableauSupprime(TranslationSRT.m_tabContent,i+1)
|
||||
FIN
|
||||
|
||||
// We do not go to the next element as this element is still active for another potential merge
|
||||
bMerged = Vrai
|
||||
|
||||
FIN
|
||||
|
||||
À FAIRE TANTQUE bMerged
|
||||
|
||||
// We change the numbering of the IDs for the SRT file (in case the user wants to save this as an SRT file)
|
||||
|
||||
pour tout e,i de m_tabContent
|
||||
e.m_nID = i
|
||||
FIN
|
||||
type : 458752
|
||||
-
|
||||
name : ExportToSBV
|
||||
procedure_id : 1737960779824315624
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Export to SubViewer format
|
||||
// Paramètres :
|
||||
// sFilename (chaîne ANSI) : <indiquez ici le rôle de sFilename>
|
||||
// Valeur de retour :
|
||||
// booléen : <indiquez ici le rôle de la valeur de retour>
|
||||
//
|
||||
procédure ExportToSBV(sFilename est chaîne)
|
||||
|
||||
SI m_tabContent..Occurrence = 0 ALORS
|
||||
ErreurDéclenche(103,"There is no content to save.")
|
||||
RENVOYER Faux
|
||||
FIN
|
||||
|
||||
// Build content to save
|
||||
// https://wiki.videolan.org/SubViewer/
|
||||
//
|
||||
sContent est une chaîne
|
||||
POUR TOUT e DE m_tabContent
|
||||
sContent += [RC+RC] + e.TimecodeToString(CSubtitleEntry.tfSVB) + RC + e.m_sText
|
||||
FIN
|
||||
|
||||
// To write in version 2 of subviewer, replace CRLF with "[br]".
|
||||
|
||||
RENVOYER fSauveTexte(sFilename,ChaîneVersUTF8(sContent))
|
||||
type : 458752
|
||||
-
|
||||
name : _LoadDecodeSRT
|
||||
procedure_id : 1737960779824381160
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : <indiquez ici ce que fait la procédure>
|
||||
// Paramètres :
|
||||
// sContent (chaîne ANSI) : <indiquez ici le rôle de sContent>
|
||||
// Valeur de retour :
|
||||
// Aucune
|
||||
//
|
||||
procédure protégée _LoadDecodeSRT(sContent est une chaine)
|
||||
|
||||
e est un CSubtitleEntry dynamique
|
||||
|
||||
// Load each lines on the SRT file and fill the content.
|
||||
POUR TOUTE CHAÎNE sLine DE sContent SÉPARÉE PAR RC
|
||||
// New srt entry (his is a integer number)
|
||||
SI Val(sLine) > 0 _ET_ Val(sLine) = sLine ALORS
|
||||
// Add any previous entry
|
||||
SI e <> Null _ET_ e.m_nID > 0 ALORS
|
||||
TableauAjoute(m_tabContent,e)
|
||||
FIN
|
||||
|
||||
// Create new entry
|
||||
e = allouer un CSubtitleEntry
|
||||
e.m_nID = sLine
|
||||
CONTINUE
|
||||
FIN
|
||||
|
||||
// Is this the timecode line?
|
||||
SI sLine [=] " --> " ALORS
|
||||
e.TimecodeFromString(CSubtitleEntry.tfSRT,sLine)
|
||||
CONTINUE
|
||||
FIN
|
||||
|
||||
// This is obviously text line
|
||||
SI sLine <> "" ALORS
|
||||
e.m_sText += [RC] + sLine
|
||||
FIN
|
||||
FIN
|
||||
|
||||
// Just in case the last SRT entry was not written (because the last line has no empty line after)
|
||||
SI e <> Null _ET_ e.m_nID > 0 ALORS
|
||||
TableauAjoute(m_tabContent,e)
|
||||
e = Null
|
||||
FIN
|
||||
|
||||
type : 458752
|
||||
-
|
||||
name : _LoadDecodeSBV
|
||||
procedure_id : 1737960779824446696
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : <indiquez ici ce que fait la procédure>
|
||||
// Paramètres :
|
||||
// sContent (chaîne ANSI) : <indiquez ici le rôle de sContent>
|
||||
// Valeur de retour :
|
||||
// Aucune
|
||||
//
|
||||
procédure protégée _LoadDecodeSBV(sContent est une chaine)
|
||||
|
||||
e est un CSubtitleEntry dynamique = allouer un CSubtitleEntry
|
||||
|
||||
// SBV format is expected to have:
|
||||
// - first line as timecode
|
||||
// - other lines as text
|
||||
// - empty line as end of timecode indicator
|
||||
|
||||
// Load each lines on the SRT file and fill the content.
|
||||
bSkip est un booléen
|
||||
POUR TOUTE CHAÎNE sLine DE sContent SÉPARÉE PAR RC
|
||||
// Skipping potential "additionnal information" section and control codes
|
||||
si sLine = "[INFORMATION]" alors
|
||||
bSkip = Vrai
|
||||
continue
|
||||
FIN
|
||||
si bSkip alors
|
||||
si sLine = "[END INFORMATION]" alors
|
||||
bSkip = Faux
|
||||
FIN
|
||||
continue
|
||||
FIN
|
||||
si sLine [= "[" alors
|
||||
continue
|
||||
FIN
|
||||
|
||||
// Empty line (new entry?)
|
||||
SI sLine = "" ALORS
|
||||
si e <> Null _ET_ e.m_duTimecodeEnd..EnMillisecondes <> 0 alors
|
||||
// Add any previous entry
|
||||
e.m_nID = TableauOccurrence(m_tabContent) + 1 // SBV files do not have an ID. We create one because we need it internally for different processses.
|
||||
TableauAjoute(m_tabContent,e)
|
||||
e = Null
|
||||
fin
|
||||
|
||||
// Create new entry
|
||||
si e = Null alors
|
||||
e = allouer un CSubtitleEntry
|
||||
fin
|
||||
|
||||
CONTINUE
|
||||
FIN
|
||||
|
||||
// The first non-empty line is always the timecode
|
||||
si e.m_duTimecodeEnd..EnMillisecondes = 0 alors
|
||||
// Transform the sbv timecode to SRT timecode
|
||||
e.TimecodeFromString(CSubtitleEntry.tfSVB,sLine)
|
||||
continue
|
||||
FIN
|
||||
|
||||
// This is a text line
|
||||
e.m_sText += [RC] + replace(sLine,"[br]",crlf) // SBV version 2 allows CRLF to be replaced with [br] placeholder. We want the text as SRT.
|
||||
FIN
|
||||
|
||||
// Just in case the last SRT entry was not written (because the last line has no empty line after)
|
||||
SI e <> Null _ET_ e.m_duTimecodeEnd..EnMillisecondes <> 0 ALORS
|
||||
e.m_nID = TableauOccurrence(m_tabContent) + 1 // SBV files do not have an ID. We create one because we need it internally for different processses.
|
||||
TableauAjoute(m_tabContent,e)
|
||||
e = Null
|
||||
FIN
|
||||
type : 458752
|
||||
-
|
||||
name : CalculateTimecodeContinuity
|
||||
procedure_id : 1737960779824512232
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Calculate timecode continuity.
|
||||
// Paramètres :
|
||||
// Aucun
|
||||
// Valeur de retour :
|
||||
// Aucune
|
||||
//
|
||||
procédure CalculateTimecodeContinuity()
|
||||
|
||||
pour i = 2 _à_ m_tabContent..Occurrence
|
||||
m_tabContent[i].m__bTimecodeNotContinuous = m_tabContent[i-1].m_duTimecodeEnd <> m_tabContent[i].m_duTimecodeStart
|
||||
FIN
|
||||
type : 458752
|
||||
-
|
||||
name : GetLastTimecodeEnd
|
||||
procedure_id : 1737960779824577768
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Get the last timecode ending time.
|
||||
// Paramètres :
|
||||
// Aucun
|
||||
// Valeur de retour :
|
||||
// durée : <indiquez ici le rôle de la valeur de retour>
|
||||
//
|
||||
procédure GetLastTimecodeEnd() : durée
|
||||
|
||||
si m_tabContent..Occurrence > 0 alors
|
||||
renvoyer m_tabContent[m_tabContent..Occurrence].m_duTimecodeEnd
|
||||
FIN
|
||||
|
||||
renvoyer ""
|
||||
type : 458752
|
||||
-
|
||||
name : TimecodesIdenticalWith
|
||||
procedure_id : 1737960779824643304
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Check if the timecodes are identical with another file.
|
||||
// Paramètres :
|
||||
// c (CSRTFile dynamique) : Other file to check against.
|
||||
// Valeur de retour :
|
||||
// booléen : True if both files have identical timecodes.
|
||||
//
|
||||
procédure TimecodesIdenticalWith(c est un CSubtitle dynamique) : booléen
|
||||
|
||||
si c = Null alors
|
||||
renvoyer vrai
|
||||
FIN
|
||||
|
||||
SI m_tabContent..Occurrence <> c.m_tabContent..Occurrence ALORS
|
||||
renvoyer Faux
|
||||
FIN
|
||||
|
||||
e est un CSubtitleEntry dynamique
|
||||
POUR TOUT e,i DANS m_tabContent
|
||||
SI e.m_duTimecodeStart <> c.m_tabContent[i].m_duTimecodeStart _OU_ e.m_duTimecodeEnd <> c.m_tabContent[i].m_duTimecodeEnd ALORS
|
||||
renvoyer Faux
|
||||
FIN
|
||||
FIN
|
||||
|
||||
renvoyer vrai
|
||||
type : 458752
|
||||
-
|
||||
name : ExportToCSV
|
||||
procedure_id : 1737960779824708840
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Will export the file to CSV compatible with ElevenLabs Dubbing project.
|
||||
// Paramètres :
|
||||
// sFilename (chaîne ANSI) : Filename to write to.
|
||||
// nOptimizeClips (entier - valeur par défaut=-1) : -1 if clips should not be opimized, all other values will trigger ClipMergeOptimization.
|
||||
// cTranslation (CSRTFile dynamique - valeur par défaut=0) : Translation file to add to the export. Both files must have identical timecodes.
|
||||
// Valeur de retour :
|
||||
// booléen : <indiquez ici le rôle de la valeur de retour>
|
||||
//
|
||||
procédure ExportToCSV(sFilename est une chaîne, nOptimizeClips est un entier = -1, cTranslation est un CSubtitle dynamique = Null) : booléen
|
||||
|
||||
si pas TimecodesIdenticalWith(cTranslation) alors
|
||||
ErreurDéclenche(1,"Both subtitle files must have identical timecodes. The selected files do not match.")
|
||||
renvoyer faux
|
||||
FIN
|
||||
|
||||
si nOptimizeClips > -1 alors
|
||||
ClipMergeOptimization(nOptimizeClips, cTranslation)
|
||||
FIN
|
||||
|
||||
// Now we create the CSV file
|
||||
// speaker,start_time,end_time,transcription,translation
|
||||
sCSV est une chaîne = `speaker,start_time,end_time,transcription,translation`
|
||||
POUR TOUT e,i DANS m_tabContent
|
||||
s est une chaine = e.m__sSpeaker = "" ? "Speaker 1" sinon e.m__sSpeaker
|
||||
sCSV += [RC] + ChaîneConstruit(`"%1","%2","%3","%4","%5"`, ...
|
||||
s, ...
|
||||
e.TimecodeToString(CSubtitleEntry.tfSRT,CSubtitleEntry.tpStart), ...
|
||||
e.TimecodeToString(CSubtitleEntry.tfSRT,CSubtitleEntry.tpEnd), ...
|
||||
Remplace(e.TextAsSingleLine(),`"`,`""`), ...
|
||||
cTranslation <> Null ? Remplace(cTranslation.m_tabContent[i].TextAsSingleLine(),`"`,`""`) sinon "" )
|
||||
FIN
|
||||
|
||||
// Save the CSV file
|
||||
renvoyer fSauveTexte(sFilename,ChaîneVersUTF8(sCSV))
|
||||
type : 458752
|
||||
-
|
||||
name : RemoveTimecodesOutOfRange
|
||||
procedure_id : 1737960779824774376
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Will delete all timecode entries that are not withing the desired range. This is a destructive process, so work on a copy.
|
||||
// Paramètres :
|
||||
// duStart (durée) : Start point (inclusive). Before this point, everything will be deleted.
|
||||
// duEnd (durée) : End point (inclusive). After this point, everything will be deleted.
|
||||
// duTimecodeOffest (durée - valeur par défaut=0) : If timecodes should be corrected, specify the offset. It will be deducted from the timecodes.
|
||||
// eActionOnStraddlingTimecodes (CSRTFile.ETimecodeRangeAction) : Action to be taken if a timecode is stranddling on the range. Default action is to trim (adjust) the timecode so to fit.
|
||||
// Valeur de retour :
|
||||
// Aucune
|
||||
//
|
||||
procédure RemoveTimecodesOutOfRange(duStart est une durée, duEnd est une durée, duTimecodeOffest est une durée = 0, eActionOnStraddlingTimecodes est une ETimecodeRangeAction = traTrimEntry)
|
||||
|
||||
// Prune out timecodes
|
||||
pour i = m_tabContent..Occurrence _A_ 1 pas -1
|
||||
e est un CSubtitleEntry dynamique <- m_tabContent[i]
|
||||
|
||||
// Entry is completely outside of the range
|
||||
si e.m_duTimecodeEnd < duStart _OU_ e.m_duTimecodeStart > duEnd ALORS
|
||||
// Remove
|
||||
TableauSupprime(m_tabContent,i)
|
||||
continue
|
||||
fin
|
||||
|
||||
// Starting point is outside of the starting range
|
||||
si e.m_duTimecodeStart < duStart alors
|
||||
selon eActionOnStraddlingTimecodes
|
||||
CAS traRemoveEntry:
|
||||
TableauSupprime(m_tabContent,i)
|
||||
|
||||
CAS traTrimEntry:
|
||||
e.m_duTimecodeStart = duStart
|
||||
si (e.m_duTimecodeEnd - e.m_duTimecodeStart) < 1000 alors
|
||||
TableauSupprime(m_tabContent,i)
|
||||
FIN
|
||||
FIN
|
||||
continue
|
||||
fin
|
||||
|
||||
// Ending point is outside of the ending range
|
||||
SI e.m_duTimecodeEnd > duEnd ALORS
|
||||
SELON eActionOnStraddlingTimecodes
|
||||
CAS traRemoveEntry:
|
||||
TableauSupprime(m_tabContent,i)
|
||||
|
||||
CAS traTrimEntry:
|
||||
e.m_duTimecodeEnd = duEnd
|
||||
SI e.m_duTimecodeEnd - e.m_duTimecodeStart < 1000 ALORS
|
||||
TableauSupprime(m_tabContent,i)
|
||||
FIN
|
||||
FIN
|
||||
continue
|
||||
FIN
|
||||
|
||||
FIN
|
||||
|
||||
// Correct timecode
|
||||
si duTimecodeOffest..EnMillisecondes > 0 alors
|
||||
|
||||
pour tout e de m_tabContent
|
||||
e.m_duTimecodeStart = e.m_duTimecodeStart - duTimecodeOffest
|
||||
e.m_duTimecodeEnd = e.m_duTimecodeEnd - duTimecodeOffest
|
||||
FIN
|
||||
|
||||
FIN
|
||||
type : 458752
|
||||
-
|
||||
name : CalculateSpeakerChange
|
||||
procedure_id : 1738311005448731556
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Calculate speaker switching.
|
||||
// Paramètres :
|
||||
// sSpeaker1 (chaîne ANSI - valeur par défaut="Speaker 1") : Name of Speaker 1
|
||||
// sSpeaker2 (chaîne ANSI - valeur par défaut="Speaker 2") : Name of Speaker 2
|
||||
// Valeur de retour :
|
||||
// Aucune
|
||||
//
|
||||
procédure CalculateSpeakerChange(sSpeaker1 est une chaine = "Speaker 1", sSpeaker2 est une chaine = "Speaker 2")
|
||||
|
||||
bSwitch est un booléen
|
||||
n est un entier = m_tabContent..Occurrence
|
||||
|
||||
POUR i = 1 _À_ n
|
||||
m_tabContent[i].m__sSpeaker = bSwitch = Faux ? sSpeaker1 SINON sSpeaker2
|
||||
si i+1 <= n _ET_ m_tabContent[i].m_duTimecodeEnd <> m_tabContent[i+1].m_duTimecodeStart ALORS
|
||||
bSwitch = pas bSwitch
|
||||
FIN
|
||||
FIN
|
||||
type : 458752
|
||||
-
|
||||
name : TimecodesToYAMLSequenceOfCompactMappings
|
||||
procedure_id : 1740092690090315318
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Exports the timecode information to YAML document
|
||||
// Paramètres :
|
||||
// Aucun
|
||||
// Valeur de retour :
|
||||
// chaîne ANSI : <indiquez ici le rôle de la valeur de retour>
|
||||
//
|
||||
// y (YAML) : Yaml variable to fill with the timecodes (direct filling, considered as an array)
|
||||
procédure TimecodesToYAMLSequenceOfCompactMappings() : chaine
|
||||
|
||||
LF est un chaine = Caract(10)
|
||||
s est une chaine
|
||||
|
||||
pour tout e,i de m_tabContent
|
||||
s += [LF] + "- " + "{" + " id: " + e.m_nID + ", i: " + DuréeVersChaîne(e.m_duTimecodeStart,"HH:MM:SS.CCC") + ", o: " + DuréeVersChaîne(e.m_duTimecodeEnd,"HH:MM:SS.CCC") + " }"
|
||||
FIN
|
||||
|
||||
renvoyer s
|
||||
type : 458752
|
||||
-
|
||||
name : TimecodesFromYAML
|
||||
procedure_id : 1740112968090671115
|
||||
type_code : 12
|
||||
code : |1+
|
||||
// Résumé : Import timecodes from YAML document.
|
||||
// Paramètres :
|
||||
// y (YAML) : YAML node containing a sequence of mappings (id, i, o)
|
||||
// Valeur de retour :
|
||||
// booléen : <indiquez ici le rôle de la valeur de retour>
|
||||
//
|
||||
procédure TimecodesFromYAML(y est un yaml)
|
||||
|
||||
// We allow overriding existing timecodes, only if the count matches
|
||||
bNewEntries est un booléen = Vrai
|
||||
|
||||
si m_tabContent..Occurrence > 0 alors
|
||||
si m_tabContent..Occurrence <> y..Occurrence alors
|
||||
ErreurDéclenche(100, "The number of timecodes does not match. Unable to import the new timecodes.")
|
||||
renvoyer faux
|
||||
FIN
|
||||
bNewEntries = Faux
|
||||
FIN
|
||||
|
||||
pour i = 1 _a_ y..Occurrence
|
||||
si bNewEntries alors
|
||||
ajoute(m_tabContent)
|
||||
FIN
|
||||
|
||||
m_tabContent[i].m_nid = y[i].id
|
||||
m_tabContent[i].m_duTimecodeStart = ChaîneVersDurée(y[i].i..Valeur, y[i].i..Type = 25 ? "HHMMSSLLL" SINON "HH:MM:SS.LLL")
|
||||
m_tabContent[i].m_duTimecodeEnd = ChaîneVersDurée(y[i].o..Valeur, y[i].i..Type = 25 ? "HHMMSSLLL" SINON "HH:MM:SS.LLL")
|
||||
FIN
|
||||
|
||||
renvoyer vrai
|
||||
type : 458752
|
||||
procedure_templates : []
|
||||
property_templates : []
|
||||
code_parameters :
|
||||
internal_properties : HwAAAB4AAAB7MB8NZB5rGUbyk77+IjQnJ74vm430Ar3yq0zmP05sGBBw0ur17uG6ZWry
|
||||
original_name : Classe1
|
||||
resources :
|
||||
string_res :
|
||||
identifier : 0x17e5b16407d0f1fb
|
||||
internal_properties : HwAAAB4AAAA809Qj/IAi+r8QXyrnW7sarQeYORCUjKBkmMeTFexSj5AuvTfTUpN0Eg==
|
||||
custom_note :
|
||||
internal_properties : HwAAAB4AAADnl3uxgA6ylw4vtqUKEOJQD3VAAOKeNUmhPNojcRFoDpHEcUyYAw==
|
||||
Reference in New Issue
Block a user