Spaces:
Sleeping
Sleeping
| --[[ | |
| TranscriptExporter.lua - Transcript export UI | |
| ]]-- | |
| TranscriptExporter = Polo { | |
| TITLE = 'Export', | |
| WIDTH = 650, | |
| HEIGHT = 200, | |
| BUTTON_WIDTH = 120, | |
| INPUT_WIDTH = 120, | |
| FILE_WIDTH = 500, | |
| } | |
| function TranscriptExporter:init() | |
| assert(self.transcript, 'missing transcript') | |
| self.is_open = false | |
| self.export_formats = TranscriptExporterFormats.new { | |
| TranscriptExportFormat.exporter_json(), | |
| TranscriptExportFormat.exporter_srt(), | |
| TranscriptExportFormat.exporter_csv(), | |
| } | |
| self.export_options = {} | |
| self.file = '' | |
| self.success = AlertPopup.new { title = 'Export Successful' } | |
| self.failure = AlertPopup.new { title = 'Export Failed' } | |
| end | |
| function TranscriptExporter:show_success() | |
| self.success.onclose = function () | |
| self.success.onclose = nil | |
| self:close() | |
| end | |
| self.success:show('Exported ' .. self.export_formats:selected_key() .. ' to: ' .. self.file) | |
| end | |
| function TranscriptExporter:show_error(msg) | |
| self.failure:show(msg) | |
| end | |
| function TranscriptExporter:render() | |
| if not self.is_open then | |
| return | |
| end | |
| local center = {ImGui.Viewport_GetCenter(ImGui.GetWindowViewport(ctx))} | |
| ImGui.SetNextWindowPos(ctx, center[1], center[2], ImGui.Cond_Appearing(), 0.5, 0.5) | |
| ImGui.SetNextWindowSize(ctx, self.WIDTH, self.HEIGHT, ImGui.Cond_FirstUseEver()) | |
| local flags = ( | |
| 0 | |
| | ImGui.WindowFlags_AlwaysAutoResize() | |
| | ImGui.WindowFlags_NoCollapse() | |
| | ImGui.WindowFlags_NoDocking() | |
| ) | |
| local visible, open = ImGui.Begin(ctx, self.TITLE, true, flags) | |
| if visible then | |
| app:trap(function () | |
| self:render_content() | |
| self.success:render() | |
| self.failure:render() | |
| end) | |
| ImGui.End(ctx) | |
| end | |
| if not open then | |
| self:close() | |
| end | |
| end | |
| function TranscriptExporter:render_content() | |
| self.export_formats:render_combo(self.INPUT_WIDTH) | |
| ImGui.Spacing(ctx) | |
| self.export_formats:render_format_options(self.export_options) | |
| ImGui.Spacing(ctx) | |
| self:render_file_selector() | |
| self:render_separator() | |
| self:render_buttons() | |
| end | |
| -- Display a text input for the output filename, with a Browse button if | |
| -- the js_ReaScriptAPI extension is available. | |
| function TranscriptExporter:render_file_selector() | |
| ImGui.Text(ctx, 'File') | |
| if app:has_js_ReaScriptAPI() then | |
| if ImGui.Button(ctx, 'Choose File', self.BUTTON_WIDTH, 0) then | |
| local rv, file = app:show_file_dialog { | |
| title = 'Save transcript', | |
| file = self.file, | |
| save = true, | |
| ext = self.export_formats:file_selector_spec(), | |
| } | |
| if rv == 1 then | |
| self.file = file | |
| end | |
| end | |
| ImGui.SameLine(ctx) | |
| end | |
| ImGui.SetNextItemWidth(ctx, self.FILE_WIDTH) | |
| local file_changed, file = ImGui.InputText(ctx, '##file', self.file, 256) | |
| if file_changed then | |
| self.file = file | |
| end | |
| if not app:has_js_ReaScriptAPI() then | |
| ImGui.Text(ctx, "For a better experience, install js_ReaScriptAPI") | |
| ImGui.Spacing(ctx) | |
| end | |
| end | |
| function TranscriptExporter:render_buttons() | |
| ReaUtil.disabler(ctx)(self.file == '', function() | |
| if ImGui.Button(ctx, 'Export', self.BUTTON_WIDTH, 0) then | |
| if self:handle_export() then | |
| self:show_success() | |
| end | |
| end | |
| end) | |
| ImGui.SameLine(ctx) | |
| if ImGui.Button(ctx, 'Cancel', self.BUTTON_WIDTH, 0) then | |
| self:close() | |
| end | |
| end | |
| function TranscriptExporter:render_separator() | |
| ImGui.Dummy(ctx, 0, 0) | |
| ImGui.Separator(ctx) | |
| ImGui.Dummy(ctx, 0, 0) | |
| end | |
| function TranscriptExporter:handle_export() | |
| if self.file == '' then | |
| self:show_error('Please specify a file name.') | |
| return false | |
| end | |
| local file = io.open(self.file, 'w') | |
| if not file then | |
| self:show_error('Could not open file: ' .. self.file) | |
| return false | |
| end | |
| self.export_formats:write(self.transcript, file, self.export_options) | |
| file:close() | |
| return true | |
| end | |
| function TranscriptExporter:open() | |
| self.is_open = true | |
| end | |
| function TranscriptExporter:close() | |
| self.is_open = false | |
| end | |
| TranscriptExporterFormats = Polo { | |
| new = function(formatters) | |
| local format_map = {} | |
| for i, formatter in ipairs(formatters) do | |
| format_map[formatter.key] = i | |
| end | |
| return { | |
| formatters = formatters, | |
| format_map = format_map, | |
| } | |
| end, | |
| } | |
| function TranscriptExporterFormats:render_combo(width) | |
| ImGui.Text(ctx, 'Format') | |
| ImGui.SetNextItemWidth(ctx, width) | |
| if ImGui.BeginCombo(ctx, "##format", self.selected_format_key) then | |
| app:trap(function() | |
| for _, format in pairs(self.formatters) do | |
| local is_selected = self.selected_format_key == format.key | |
| if ImGui.Selectable(ctx, format.key, is_selected) then | |
| self.selected_format_key = format.key | |
| end | |
| if is_selected then | |
| ImGui.SetItemDefaultFocus(ctx) | |
| end | |
| end | |
| end) | |
| ImGui.EndCombo(ctx) | |
| end | |
| end | |
| function TranscriptExporterFormats:selected_key() | |
| return self:selected_format().key | |
| end | |
| function TranscriptExporterFormats:file_selector_spec() | |
| return self:selected_format():file_selector_spec() | |
| end | |
| function TranscriptExporterFormats:write(transcript, output_file, options) | |
| return self:selected_format().writer(transcript, output_file, options) | |
| end | |
| function TranscriptExporterFormats:selected_format() | |
| if not self.selected_format_key then | |
| if not self.formatters or #self.formatters < 1 then | |
| app:debug('no formats to set for default') | |
| return | |
| end | |
| self.selected_format_key = self.formatters[1].key | |
| end | |
| local index = self.format_map[self.selected_format_key] | |
| return self.formatters[index] | |
| end | |
| function TranscriptExporterFormats:render_format_options(options) | |
| app:trap(function() | |
| local format = self:selected_format() | |
| if format then | |
| format.option_renderer(options) | |
| end | |
| end) | |
| end | |
| TranscriptExportFormat = Polo { | |
| OPTIONS_NOOP = function(_options) end, | |
| new = function (key, extension, option_renderer, writer_f) | |
| return { | |
| key = key, | |
| extension = extension, | |
| option_renderer = option_renderer, | |
| writer = writer_f, | |
| } | |
| end, | |
| } | |
| function TranscriptExportFormat:file_selector_spec() | |
| local selector_spec = '%s files (*.%s)\0*.%s\0All files (*.*)\0*.*\0\0' | |
| return selector_spec:format(self.key, self.extension, self.extension) | |
| end | |
| function TranscriptExportFormat.exporter_json() | |
| return TranscriptExportFormat.new( | |
| 'JSON', 'json', | |
| TranscriptExportFormat.options_json, | |
| TranscriptExportFormat.writer_json | |
| ) | |
| end | |
| function TranscriptExportFormat.options_json(options) | |
| local rv, value = ImGui.Checkbox(ctx, 'One Object per Transcript Segment', options.object_per_segment) | |
| if rv then | |
| options.object_per_segment = value | |
| end | |
| end | |
| function TranscriptExportFormat.writer_json(transcript, output_file, options) | |
| if options.object_per_segment then | |
| for _, segment in pairs(transcript:get_segments()) do | |
| output_file:write(segment:to_json()) | |
| output_file:write('\n') | |
| end | |
| else | |
| output_file:write(transcript:to_json()) | |
| end | |
| end | |
| function TranscriptExportFormat.exporter_srt() | |
| return TranscriptExportFormat.new( | |
| 'SRT', 'srt', | |
| TranscriptExportFormat.options_srt, | |
| TranscriptExportFormat.writer_srt | |
| ) | |
| end | |
| function TranscriptExportFormat.strip_non_numeric(value) | |
| return value:gsub("[^0-9]", ""):gsub("^0+", "") | |
| end | |
| function TranscriptExportFormat.options_srt(options) | |
| local rv, value | |
| rv, value = ImGui.InputText(ctx, 'X1', options.coords_x1, ImGui.InputTextFlags_CharsDecimal()) | |
| if rv then | |
| options.coords_x1 = TranscriptExportFormat.strip_non_numeric(value) | |
| end | |
| ImGui.SameLine(ctx) | |
| rv, value = ImGui.InputText(ctx, 'Y1', options.coords_y1, ImGui.InputTextFlags_CharsDecimal()) | |
| if rv then | |
| options.coords_y1 = TranscriptExportFormat.strip_non_numeric(value) | |
| end | |
| rv, value = ImGui.InputText(ctx, 'X2', options.coords_x2, ImGui.InputTextFlags_CharsDecimal()) | |
| if rv then | |
| options.coords_x2 = TranscriptExportFormat.strip_non_numeric(value) | |
| end | |
| ImGui.SameLine(ctx) | |
| rv, value = ImGui.InputText(ctx, 'Y2', options.coords_y2, ImGui.InputTextFlags_CharsDecimal()) | |
| if rv then | |
| options.coords_y2 = TranscriptExportFormat.strip_non_numeric(value) | |
| end | |
| end | |
| function TranscriptExportFormat.writer_srt(transcript, output_file, options) | |
| local writer = SRTWriter.new { file = output_file, options = options } | |
| writer:write(transcript) | |
| end | |
| function TranscriptExportFormat.exporter_csv() | |
| return TranscriptExportFormat.new( | |
| 'CSV', 'csv', | |
| TranscriptExportFormat.options_csv, | |
| TranscriptExportFormat.writer_csv | |
| ) | |
| end | |
| function TranscriptExportFormat.options_csv(options) | |
| local delimiters = CSVWriter.DELIMITERS | |
| local selected_delimiter = delimiters[1] | |
| for _, delimiter in ipairs(delimiters) do | |
| if delimiter.char == options.delimiter then | |
| selected_delimiter = delimiter | |
| break | |
| end | |
| end | |
| if ImGui.BeginCombo(ctx, 'Delimiter', selected_delimiter.name) then | |
| app:trap(function() | |
| for _, delimiter in ipairs(delimiters) do | |
| local is_selected = options.delimiter == delimiter.char | |
| if ImGui.Selectable(ctx, delimiter.name, is_selected) then | |
| options.delimiter = delimiter.char | |
| end | |
| if is_selected then | |
| ImGui.SetItemDefaultFocus(ctx) | |
| end | |
| end | |
| end) | |
| ImGui.EndCombo(ctx) | |
| end | |
| ImGui.Spacing(ctx) | |
| local rv, value = ImGui.Checkbox(ctx, 'Include Header Row', options.include_header_row) | |
| if rv then | |
| options.include_header_row = value | |
| end | |
| end | |
| function TranscriptExportFormat.writer_csv(transcript, output_file, options) | |
| local writer = CSVWriter.new { | |
| file = output_file, | |
| delimiter = options.delimiter, | |
| include_header_row = options.include_header_row | |
| } | |
| writer:write(transcript) | |
| end | |