Config
This commit is contained in:
@@ -57,10 +57,16 @@ Process RunEx(String args) {
|
|||||||
DWORD dw = GetLastError();
|
DWORD dw = GetLastError();
|
||||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
|
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
|
||||||
LocalFree(lpMsgBuf);
|
|
||||||
|
char *buff = (char *)lpMsgBuf;
|
||||||
|
size_t buffLen = strlen((const char *)buff);
|
||||||
|
if (buffLen > 0 && buff[buffLen - 1] == '\n') {
|
||||||
|
buff[buffLen - 1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// @warning: leak! but we don't care
|
// @warning: leak! but we don't care
|
||||||
result.error_message = Format(GetSystemAllocator(), "Failed to create process | message: %s | cmd: %.*s", (char *)lpMsgBuf, FmtString(args));
|
result.error_message = Format(GetSystemAllocator(), "Failed to create process | message: %s | cmd: %.*s", (char *)lpMsgBuf, FmtString(args));
|
||||||
|
LocalFree(lpMsgBuf);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
153
src/transcript_browser/config.cpp
Normal file
153
src/transcript_browser/config.cpp
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
struct Var {
|
||||||
|
String key;
|
||||||
|
String value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConfigParseResult {
|
||||||
|
Array<Var> vars;
|
||||||
|
Array<String> errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
char SRTCommand[512] = "\"C:\\Program Files\\VideoLAN\\VLC\\vlc.exe\" --start-time {time_in_seconds} {video}";
|
||||||
|
char PDFCommand[512] = "C:/Users/Karol/AppData/Local/SumatraPDF/SumatraPDF.exe -page {page} {file}";
|
||||||
|
char TXTCommand[512] = "notepad.exe {file}";
|
||||||
|
|
||||||
|
void SkipWhitespace(String *s) {
|
||||||
|
while (s->len && IsWhitespace(s->data[0])) {
|
||||||
|
*s = s->skip(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String ParseWord(String *s) {
|
||||||
|
String result = {};
|
||||||
|
SkipWhitespace(s);
|
||||||
|
if (s->len && IsAlphabetic(s->data[0])) {
|
||||||
|
result = {s->data, 0};
|
||||||
|
while (s->len && IsAlphanumeric(s->data[0])) {
|
||||||
|
result.len += 1;
|
||||||
|
*s = s->skip(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpectString(String *s, String expect) {
|
||||||
|
SkipWhitespace(s);
|
||||||
|
if (StartsWith(*s, expect)) {
|
||||||
|
*s = s->skip(expect.len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigParseResult ParseConfig(Allocator allocator, String string) {
|
||||||
|
Scratch scratch;
|
||||||
|
Array<String> lines = Split(scratch, string, "\n");
|
||||||
|
|
||||||
|
ConfigParseResult result = {};
|
||||||
|
result.errors = {allocator};
|
||||||
|
result.vars = {allocator};
|
||||||
|
|
||||||
|
For(lines) {
|
||||||
|
String s = Trim(it);
|
||||||
|
if (s.len == 0) continue;
|
||||||
|
|
||||||
|
String key = ParseWord(&s);
|
||||||
|
if (key.len == 0) {
|
||||||
|
result.errors.add(Format(allocator, "failed to parse config at line: %d, the key is invalid", (int)lines.get_index(it)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool e = ExpectString(&s, "=");
|
||||||
|
if (!e) {
|
||||||
|
result.errors.add(Format(allocator, "failed to parse config at line: %d, expected '=' assignment sign", (int)lines.get_index(it)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String value = Trim(s);
|
||||||
|
if (value.len == 0) {
|
||||||
|
result.errors.add(Format(allocator, "failed to parse config at line: %d, the value is invalid", (int)lines.get_index(it)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.vars.add({key, value});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
String GetValue(Array<Var> vars, String key) {
|
||||||
|
For(vars) {
|
||||||
|
if (it.key == key) return it.value;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
String SerializeConfig(Allocator allocator) {
|
||||||
|
String content = Format(allocator, "SRTCommand = %s\nPDFCommand = %s\nTXTCommand = %s\n", SRTCommand, PDFCommand, TXTCommand);
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
String CreateDefaultConfig(String path) {
|
||||||
|
String content = SerializeConfig(Perm);
|
||||||
|
WriteFile(path, content);
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadConfig() {
|
||||||
|
Scratch scratch;
|
||||||
|
String exe_dir = GetExeDir(scratch);
|
||||||
|
String config_path = Format(scratch, "%.*s/transcript_browser.config", FmtString(exe_dir));
|
||||||
|
String config = ReadFile(Perm, config_path);
|
||||||
|
if (config.len == 0) {
|
||||||
|
config = CreateDefaultConfig(config_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigParseResult result = ParseConfig(Perm, config);
|
||||||
|
For(result.errors) {
|
||||||
|
XFileLoadResults.add({"", it});
|
||||||
|
}
|
||||||
|
|
||||||
|
String pdf_command = GetValue(result.vars, "PDFCommand");
|
||||||
|
if (pdf_command.len) {
|
||||||
|
int len = ClampTop((int)pdf_command.len, (int)sizeof(PDFCommand) - 1);
|
||||||
|
memcpy(PDFCommand, pdf_command.data, len);
|
||||||
|
PDFCommand[len] = 0;
|
||||||
|
}
|
||||||
|
String txt_command = GetValue(result.vars, "TXTCommand");
|
||||||
|
if (txt_command.len) {
|
||||||
|
int len = ClampTop((int)txt_command.len, (int)sizeof(TXTCommand) - 1);
|
||||||
|
memcpy(TXTCommand, txt_command.data, len);
|
||||||
|
TXTCommand[len] = 0;
|
||||||
|
}
|
||||||
|
String srt_command = GetValue(result.vars, "SRTCommand");
|
||||||
|
if (srt_command.len) {
|
||||||
|
int len = ClampTop((int)srt_command.len, (int)sizeof(SRTCommand) - 1);
|
||||||
|
memcpy(SRTCommand, srt_command.data, len);
|
||||||
|
SRTCommand[len] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveConfig() {
|
||||||
|
Scratch scratch;
|
||||||
|
String exe_dir = GetExeDir(scratch);
|
||||||
|
String config_path = Format(scratch, "%.*s/transcript_browser.config", FmtString(exe_dir));
|
||||||
|
String content = SerializeConfig(scratch);
|
||||||
|
WriteFile(config_path, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestConfig() {
|
||||||
|
Scratch scratch;
|
||||||
|
{
|
||||||
|
ConfigParseResult result = ParseConfig(scratch, "\nPDFCommand = CoolCommand\nTXTCommand = ABC\nSRTCommand = DAS\n");
|
||||||
|
Assert(result.errors.len == 0);
|
||||||
|
Assert(result.vars.len == 3);
|
||||||
|
Assert(result.vars[0].key == "PDFCommand");
|
||||||
|
Assert(result.vars[1].value == "ABC");
|
||||||
|
Assert(result.vars[2].value == "DAS");
|
||||||
|
Assert(result.vars[2].key == "SRTCommand");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ConfigParseResult result = ParseConfig(scratch, "\n= CoolCommand\nTXTCommand = ABC\nSRTCommand =\n");
|
||||||
|
Assert(result.errors.len == 2);
|
||||||
|
Assert(result.vars.len == 1);
|
||||||
|
}
|
||||||
|
LoadConfig();
|
||||||
|
}
|
||||||
@@ -28,16 +28,10 @@ struct XToSource {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileLoadResult {
|
Arena XArena;
|
||||||
String filename;
|
std::mutex XArenaAddMutex;
|
||||||
String error;
|
Array<XToSource> XToSourceArray;
|
||||||
};
|
int64_t XLoadThreadComplete;
|
||||||
|
|
||||||
Arena XArena;
|
|
||||||
std::mutex XArenaAddMutex;
|
|
||||||
Array<XToSource> XToSourceArray;
|
|
||||||
Array<FileLoadResult> XFileLoadResults;
|
|
||||||
int64_t XLoadThreadComplete;
|
|
||||||
|
|
||||||
void XInitLoading() {
|
void XInitLoading() {
|
||||||
InitArena(&XArena);
|
InitArena(&XArena);
|
||||||
@@ -68,8 +62,8 @@ WORK_FUNCTION(ParseFilesWork) {
|
|||||||
} else if (EndsWith(it_time_file, ".txt", true) || EndsWith(it_time_file, ".html", true)) {
|
} else if (EndsWith(it_time_file, ".txt", true) || EndsWith(it_time_file, ".html", true)) {
|
||||||
String string = ReadFile(scratch, it_time_file);
|
String string = ReadFile(scratch, it_time_file);
|
||||||
if (string.data) {
|
if (string.data) {
|
||||||
XArenaAddMutex.lock();
|
|
||||||
Array<String> lines = Split(scratch, string, "\n");
|
Array<String> lines = Split(scratch, string, "\n");
|
||||||
|
XArenaAddMutex.lock();
|
||||||
For(lines) {
|
For(lines) {
|
||||||
String s = Copy(XArena, it);
|
String s = Copy(XArena, it);
|
||||||
s.data[s.len] = ' ';
|
s.data[s.len] = ' ';
|
||||||
@@ -83,6 +77,19 @@ WORK_FUNCTION(ParseFilesWork) {
|
|||||||
} else {
|
} else {
|
||||||
XFileLoadResults.bounded_add({it_time_file, "failed to read the file"});
|
XFileLoadResults.bounded_add({it_time_file, "failed to read the file"});
|
||||||
}
|
}
|
||||||
|
} else if (EndsWith(it_time_file, ".pdf", true)) {
|
||||||
|
PDF pdf = pdfioReadPDF(scratch, it_time_file);
|
||||||
|
|
||||||
|
XArenaAddMutex.lock();
|
||||||
|
For(pdf.pages) {
|
||||||
|
String s = Copy(XArena, it.string);
|
||||||
|
s.data[s.len] = ' ';
|
||||||
|
|
||||||
|
XToSource t = {SourceKind_PDF, s, it_time_file};
|
||||||
|
t.pdf = {it.number};
|
||||||
|
XToSourceArray.add(t);
|
||||||
|
}
|
||||||
|
XArenaAddMutex.unlock();
|
||||||
} else {
|
} else {
|
||||||
XFileLoadResults.bounded_add({it_time_file, "internal error: extension is not supported but got propagated to parse stage"});
|
XFileLoadResults.bounded_add({it_time_file, "internal error: extension is not supported but got propagated to parse stage"});
|
||||||
}
|
}
|
||||||
@@ -114,6 +121,8 @@ void XAddFolder(String folder, Array<String> *filenames) {
|
|||||||
files_to_parse.add(file);
|
files_to_parse.add(file);
|
||||||
} else if (EndsWith(iter.filename, ".txt", true) || EndsWith(iter.filename, ".html", true)) {
|
} else if (EndsWith(iter.filename, ".txt", true) || EndsWith(iter.filename, ".html", true)) {
|
||||||
files_to_parse.add(file);
|
files_to_parse.add(file);
|
||||||
|
} else if (EndsWith(iter.filename, ".pdf", true)) {
|
||||||
|
files_to_parse.add(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
@todo: txt files
|
@todo: popup when error
|
||||||
@todo: read recursive
|
@todo: read recursive
|
||||||
@todo: add option for user to control what files are read
|
@todo: add option for user to control what files are read
|
||||||
@todo: pdf files
|
@todo: pdf files
|
||||||
@@ -25,20 +25,24 @@
|
|||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include "glad.h"
|
#include "glad.h"
|
||||||
|
|
||||||
|
struct FileLoadResult {
|
||||||
|
String filename;
|
||||||
|
String error;
|
||||||
|
};
|
||||||
|
|
||||||
|
Arena Perm;
|
||||||
|
Array<FileLoadResult> XFileLoadResults;
|
||||||
|
|
||||||
|
#include "config.cpp"
|
||||||
#include <pdfio.h>
|
#include <pdfio.h>
|
||||||
#include "read_pdf.cpp"
|
#include "read_pdf.cpp"
|
||||||
#include "read_srt.cpp"
|
#include "read_srt.cpp"
|
||||||
|
|
||||||
Arena Perm;
|
|
||||||
WorkQueue MainWorkQueue;
|
WorkQueue MainWorkQueue;
|
||||||
|
|
||||||
#include "loading_thread.cpp"
|
#include "loading_thread.cpp"
|
||||||
#include "searching_thread.cpp"
|
#include "searching_thread.cpp"
|
||||||
|
|
||||||
char SRTCommand[512] = "\"C:\\Program Files\\VideoLAN\\VLC\\vlc.exe\" --start-time {time_in_seconds} {video}";
|
|
||||||
char PDFCommand[512];
|
|
||||||
char TXTCommand[512] = "subl.exe {file}";
|
|
||||||
|
|
||||||
struct ReplaceVar {
|
struct ReplaceVar {
|
||||||
String replace;
|
String replace;
|
||||||
String with;
|
String with;
|
||||||
@@ -160,6 +164,14 @@ void UISearchResults(Array<String> filenames) {
|
|||||||
String args = ReplaceVars(scratch, replace_vars, TXTCommand);
|
String args = ReplaceVars(scratch, replace_vars, TXTCommand);
|
||||||
Process process = RunEx(args);
|
Process process = RunEx(args);
|
||||||
if (!process.is_valid) XFileLoadResults.bounded_add({"", process.error_message});
|
if (!process.is_valid) XFileLoadResults.bounded_add({"", process.error_message});
|
||||||
|
} else if (item->kind == SourceKind_PDF) {
|
||||||
|
replace_vars.add({"file", Format(scratch, "\"%.*s\"", FmtString(item->filepath))});
|
||||||
|
replace_vars.add({"page", Format(scratch, "%d", item->pdf.page)});
|
||||||
|
String args = ReplaceVars(scratch, replace_vars, PDFCommand);
|
||||||
|
Process process = RunEx(args);
|
||||||
|
if (!process.is_valid) XFileLoadResults.bounded_add({"", process.error_message});
|
||||||
|
} else {
|
||||||
|
Assert(!"Invalid codepath");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -395,6 +407,9 @@ int main(int, char **) {
|
|||||||
ImGui::InputText(".srt", SRTCommand, sizeof(SRTCommand));
|
ImGui::InputText(".srt", SRTCommand, sizeof(SRTCommand));
|
||||||
ImGui::InputText(".pdf", PDFCommand, sizeof(PDFCommand));
|
ImGui::InputText(".pdf", PDFCommand, sizeof(PDFCommand));
|
||||||
ImGui::InputText(".txt", TXTCommand, sizeof(TXTCommand));
|
ImGui::InputText(".txt", TXTCommand, sizeof(TXTCommand));
|
||||||
|
if (ImGui::Button("Save config")) {
|
||||||
|
SaveConfig();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (show_loaded_files) {
|
if (show_loaded_files) {
|
||||||
UILoadedFiles();
|
UILoadedFiles();
|
||||||
|
|||||||
4
src/transcript_browser/user_help.txt
Normal file
4
src/transcript_browser/user_help.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
When you are refering to a particular program through an absolute path, for example:
|
||||||
|
> C:/Program files/SumatraPDF/SumatraPDF.exe
|
||||||
|
If the path has spaces, make sure to sandvich the path with quotes '"', like this:
|
||||||
|
> "C:/Program files/SumatraPDF/SumatraPDF.exe"
|
||||||
Reference in New Issue
Block a user