-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathCaption.cs
256 lines (233 loc) · 9.99 KB
/
Caption.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
namespace ClassTranscribeDatabase.Models
{
public enum CaptionType
{
// Since these are persisted in the database these integer values are immutable once assigned (hence explicit)
TextCaption = 0,
AudioDescription = 1
}
public static class CaptionConstants {
public const string PlaceHolderText = "...Processing...";
}
/// <summary>
/// Each line of caption is stored as a row in the database.
/// </summary>
public class Caption : Entity
{
public bool HasPlaceHolderText() { return this.Text == CaptionConstants.PlaceHolderText; }
public int Index { get; set; }
public TimeSpan Begin { get; set; }
public TimeSpan End { get; set; }
public string Text { get; set; }
public string TranscriptionId { get; set; }
public int UpVote { get; set; }
public int DownVote { get; set; }
[SwaggerIgnore]
[IgnoreDataMember]
public virtual Transcription Transcription { get; set; }
public CaptionType CaptionType { get; set; }
private string GetVTTEscapedText()
{
// <>& must be escaped < > & The replacement order is important
String escape = Text.Replace("&", "&").Replace("<", "<").Replace(">", ">");
// The VTT spec says the text may not contain '-->'
while (escape.Contains("-->"))
{
escape = escape.Replace("-->", "=>");
}
// The string may not contain an empty line
while (escape.Contains("\n\n"))
{
escape = escape.Replace("\n\n", "\n");
}
return escape;
}
private string GetXMLEscapedText()
{
StringBuilder result = new StringBuilder(Text, Text.Length + 32);
return result.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("'", "'").Replace("\"", """).ToString();
}
/// <summary>
/// Convert a line of caption to an srt subtitle format.
/// See https://en.wikipedia.org/wiki/SubRip
/// </summary>
public string SrtSubtitle(int reindex)
{
string time = string.Format("{0:hh\\:mm\\:ss\\,fff} --> {1:hh\\:mm\\:ss\\,fff}", Begin, End);
return $"{reindex}\n{time}\n{GetVTTEscapedText()}\n\n";
}
public string DXFPSubtitle()
{// see https://www.w3.org/TR/ttml1/#timing
//<span begin = "00:00:03.400" end = "00:00:06.177" >Hello xml </span>
string time = string.Format("begin=\"{0:hh\\:mm\\:ss\\.fff}\" end=\"{1:hh\\:mm\\:ss\\.fff}\">", Begin, End);
string t = GetXMLEscapedText().Replace("\n", "<br/>");
return $"<span {time}>{t}</span>\n";
}
/// <summary>
/// Convert a line of caption to an webVTT subtitle format.
/// See https://developer.mozilla.org/en-US/docs/Web/API/WebVTT_API
/// </summary>
public string WebVTTSubtitle()
{
string time = string.Format("{0:hh\\:mm\\:ss\\.fff} --> {1:hh\\:mm\\:ss\\.fff}", Begin, End);
return $"{time}\n{GetVTTEscapedText()}\n\n";
}
/// <summary>
/// Converts a long line of recognizedSpeech into smaller chunks of Globals.CAPTION_LENGTH characters,
/// and appends to a list of captions.
/// </summary>
/// <param name="captions">A pre-existing list of captions.</param>
/// <param name="Begin">The beginning time stamp of the recognizedSpeech</param>
/// <param name="End">The end time stamp of the recognizedSpeech</param>
/// <param name="recognizedSpeech">Recognized Speech received from the Speech Services API.</param>
public static List<Caption> ToCaptionEntitiesInterpolate(int captionsCount, TimeSpan Begin, TimeSpan End, string recognizedSpeech)
{
List<Caption> captions = new List<Caption>();
int captionLength = Globals.CAPTION_LENGTH;
int currCounter = captionsCount + 1;
string tempCaption = recognizedSpeech;
string caption;
int newDuration;
TimeSpan curBegin = Begin;
TimeSpan curDuration = End.Subtract(Begin);
TimeSpan curEnd;
while (tempCaption.Length > captionLength)
{
newDuration = Convert.ToInt32(captionLength * curDuration.TotalMilliseconds / tempCaption.Length);
int index = tempCaption.IndexOf(' ', captionLength);
if (index == -1)
{
caption = tempCaption;
tempCaption = "";
}
else
{
caption = tempCaption.Substring(0, index);
tempCaption = tempCaption[index..];
tempCaption = tempCaption.Trim();
}
curEnd = curBegin.Add(new TimeSpan(0, 0, 0, 0, newDuration));
captions.Add(new Caption
{
Index = currCounter++,
Begin = curBegin,
End = curEnd,
Text = caption
});
curBegin = curEnd;
curDuration = End.Subtract(curBegin);
}
if (tempCaption.Length > 0)
{
newDuration = Convert.ToInt32(captionLength * curDuration.TotalMilliseconds / tempCaption.Length);
curEnd = curBegin.Add(new TimeSpan(0, 0, 0, 0, newDuration));
captions.Add(new Caption
{
Index = currCounter++,
Begin = curBegin,
End = curEnd,
Text = tempCaption
});
// curBegin = curEnd;
// curDuration = End.Subtract(curBegin);
}
return captions;
}
/// <summary>
/// Generate an srt file from a list of captions.
/// </summary>
/// <returns>The path of the generated srt file</returns>
///
public static string GenerateSrtFile(List<Caption> captions)
{
string content = GenerateSrtString(captions);
string srtFile = CommonUtils.GetTmpFile();
WriteTextToUTF8File(content, srtFile);
return srtFile;
}
public static string GenerateSrtString(List<Caption> captions)
{
string header = "";
StringBuilder content = new StringBuilder(header, 100 * captions.Count);
int captionCounter = 1;
foreach (Caption caption in captions)
{
content.Append(caption.SrtSubtitle(captionCounter));
captionCounter++;
}
return content.ToString();
}
/// <summary>
/// Generate a webVTT file from a list of captions.
/// </summary>
/// <returns>The path of the generated vtt file</returns>
///
public static string GenerateWebVTTFile(List<Caption> captions, string language)
{
String contents = GenerateWebVTTString(captions, language);
string vttFile = CommonUtils.GetTmpFile();
WriteTextToUTF8File(contents, vttFile);
return vttFile;
}
public static string GenerateWebVTTString(List<Caption> captions, string language)
{
string now = DateTime.UtcNow.ToString("o", System.Globalization.CultureInfo.InvariantCulture);
string header1 = $"WEBVTT Kind: captions; Language: {language}\n\n";
string header2 = $"NOTE\nCreated on {now} by ClassTranscribe\n\n";
StringBuilder content = new StringBuilder(header1, 100 * captions.Count);
content.Append(header2);
foreach (Caption caption in captions)
{
content.Append(caption.WebVTTSubtitle());
}
return content.ToString();
}
//Sketch of New dxfp format - not yet tested nor integrated into rest of the code
// we don't need or use this format so it would be better to generate it dynamically i.e. as part of a web controller request as needed
//
public static string GenerateDXFPString(List<Caption> captions, string language)
{
// string now = DateTime.UtcNow.ToString("o", System.Globalization.CultureInfo.InvariantCulture);
string header = @"
<?xml version=""1.0"" encoding=""utf-8""?>
<tt xml:lang=""en"" xmlns=""http://www.w3.org/ns/ttml""
xmlns:tts=""http://www.w3.org/ns/ttml#styling""
xmlns:ttm=""http://www.w3.org/ns/ttml#metadata"">
<head>
<styling>
<style xml:id=""defaultCaption"" tts:fontSize=""10"" tts:fontFamily=""SansSerif""
tts:fontWeight=""normal"" tts:fontStyle=""normal""
tts:textDecoration=""none"" tts:color=""white""
tts:backgroundColor=""black"" />
</styling>
</head> <body>";
StringBuilder content = new StringBuilder(header, 100 * captions.Count);
content.Append($" < div style=\"defaultCaption\" xml:lang=\"{language}\">");
foreach (Caption caption in captions)
{
content.Append(caption.DXFPSubtitle());
}
content.Append(" </div>");
content.Append("</body></tt>");
return content.ToString();
}
/// <summary>
/// Write text to a file.
/// </summary>
public static void WriteTextToUTF8File(string text, string file)
{
// Using UTF8Encoding(false) ensures no Byte Order Mark is written.
// (no BOM is actually preferred for utf-8; it's also unnecessary for utf-8)
StreamWriter sw = new StreamWriter(file, false, new UTF8Encoding(false));
//Write a line of text
sw.WriteLine(text);
//Close the file
sw.Close();
}
}
}