Annotation-System/hmml_to_html/hmml_to_html.c

387 lines
13 KiB
C

#if 0
ctime -begin ${0%.*}.ctm
gcc -g -fsanitize=address $0 -o ${0%.*} hmml.a
ctime -end ${0%.*}.ctm
exit
#endif
#include <stdio.h>
#include <stdlib.h>
#include "hmmlib.h"
#ifndef bool
typedef bool unsigned int;
#endif
#define TRUE 1
#define FALSE 0
typedef struct
{
char *Location;
char *Ptr;
int Size;
} buffer;
void
ClaimBuffer(char *MemoryArena, int *ClaimedMemory, buffer *Buffer, int Size)
{
Buffer->Location = MemoryArena + *ClaimedMemory;
Buffer->Size = Size;
*ClaimedMemory += Buffer->Size;
Buffer->Ptr = Buffer->Location;
}
int
TimecodeToSeconds(char *Timecode)
{
int HMS[3] = { 0, 0, 0 }; // 0 == Seconds; 1 == Minutes; 2 == Hours
int Colons = 0;
while(*Timecode)
{
if((*Timecode < '0' || *Timecode > '9') && *Timecode != ':') { return FALSE; }
if(*Timecode == ':')
{
++Colons;
if(Colons > 2) { return FALSE; }
for(int i = 0; i < Colons; ++i)
{
HMS[Colons - i] = HMS[Colons - (i + 1)];
}
HMS[0] = 0;
}
else
{
HMS[0] = HMS[0] * 10 + *Timecode - '0';
}
++Timecode;
}
if(HMS[0] > 59 || HMS[1] > 59 || Timecode[-1] == ':') { return FALSE; }
return HMS[2] * 60 * 60 + HMS[1] * 60 + HMS[0];
}
void
CopyBuffer(buffer *Src, buffer *Dest)
{
Src->Ptr = Src->Location;
while(*Src->Ptr)
{
*Dest->Ptr++ = *Src->Ptr++;
}
}
void
CopyStringToBuffer(char *Src, buffer *Dest)
{
while(*Src)
{
*Dest->Ptr++ = *Src++;
}
}
int
main(int ArgC, char **Args)
{
if(ArgC < 2)
{
fprintf(stderr, "Usage: %s filename(s)\n", Args[0]);
return 1;
}
// NOTE(matt): Init MemoryArena
char *MemoryArena;
int ArenaSize = 1024 * 1024;
if(!(MemoryArena = calloc(ArenaSize, 1)))
{
perror(Args[0]);
return 1;
}
int ClaimedMemory = 0;
// NOTE(matt): Setup buffers and ptrs
char *InPtr;
buffer Working;
buffer Text;
buffer Out;
ClaimBuffer(MemoryArena, &ClaimedMemory, &Out, 1024 * 512);
for(int FileIndex = 1; FileIndex < ArgC; ++FileIndex)
{
FILE *InFile;
if(!(InFile = fopen(Args[FileIndex], "r")))
{
perror(Args[0]);
free(MemoryArena);
return 1;
}
HMML_Output HMML = hmml_parse_file(InFile);
fclose(InFile);
if(HMML.well_formed)
{
ClaimBuffer(MemoryArena, &ClaimedMemory, &Working, 1024);
sprintf(Working.Location,
"<html>\n"
" <head>\n"
" <meta charset=\"UTF-8\">\n"
"\n"
" <!-- Load the player -->\n"
" <script type=\"text/javascript\" src=\"player.js\"></script>\n"
" <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">\n"
" </head>\n"
" <body>\n"
" <div class=\"title\">\n"
" <span class=\"episode_name\">%s</span>\n", HMML.metadata.title);
Out.Ptr = Out.Location;
CopyBuffer(&Working, &Out);
int AnnotationIndex = 0;
int ReferenceIndex = 1;
while(AnnotationIndex < HMML.annotation_count)
{
if(HMML.annotations[AnnotationIndex].reference_count)
{
sprintf(Working.Location,
" <div class=\"refs_container\">\n"
" <span>References &#9660;</span>\n"
" <div class=\"mouse_catcher\"></div>\n"
" <div class=\"refs\">\n");
CopyBuffer(&Working, &Out);
while(AnnotationIndex < HMML.annotation_count)
{
for(int i = 0; i < HMML.annotations[AnnotationIndex].reference_count; ++i)
{
// NOTE(matt): Consider removing the ref_index class if it ain't needed
sprintf(Working.Location,
" <a data-id=\"%d\" href=\"%s\" target=\"_blank\" class=\"ref\">\n"
" <span class=\"ref_content\">\n"
" <div class=\"source\">%s</div>\n"
" <div class=\"ref_title\">%s</div>\n"
" </span>\n"
// NOTE(matt): Fill the div class="ref_indices" with <= 3 span
// class="ref_index" and ensure to put these <=3 spans on the same line without
// a space between them
" <div class=\"ref_indices\">\n"
" <span data-timestamp=\"%d\" class=\"timecode\"><span class=\"ref_index\">[%d]</span><span class=\"time\">%s</span></span>\n"
" </div>\n"
" </a>\n",
AnnotationIndex,
HMML.annotations[AnnotationIndex].references[i].url,
HMML.annotations[AnnotationIndex].references[i].site ? HMML.annotations[AnnotationIndex].references[i].site : HMML.annotations[AnnotationIndex].references[i].author,
HMML.annotations[AnnotationIndex].references[i].page ? HMML.annotations[AnnotationIndex].references[i].page : HMML.annotations[AnnotationIndex].references[i].title,
TimecodeToSeconds(HMML.annotations[AnnotationIndex].time),
ReferenceIndex,
HMML.annotations[AnnotationIndex].time);
CopyBuffer(&Working, &Out);
++ReferenceIndex;
}
++AnnotationIndex;
}
}
++AnnotationIndex;
}
sprintf(Working.Location,
" </div>\n"
" </div>\n"
" <span class=\"annotator_container\">Annotator: <span class=\"annotator\">%s</span></span>\n"
" </div>\n"
" <div class=\"player_container\">\n"
" <div class=\"video_container\" data-videoId=\"%s\"></div>\n"
" <div class=\"markers_container\">\n", HMML.metadata.annotator, HMML.metadata.id);
CopyBuffer(&Working, &Out);
int DataRef = 1;
for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex)
{
InPtr = HMML.annotations[AnnotationIndex].text;
ClaimBuffer(MemoryArena, &ClaimedMemory, &Text, 256);
if(HMML.annotations[AnnotationIndex].reference_count) // || HMML.annotations[AnnotationIndex].is_quote)
{
sprintf(Working.Location,
" <div class=\"marker\" data-timestamp=\"%d\" data-ref=\"%d\">\n", TimecodeToSeconds(HMML.annotations[AnnotationIndex].time), AnnotationIndex);
for(int RefLocalIndex = 0; RefLocalIndex < HMML.annotations[AnnotationIndex].reference_count; ++RefLocalIndex)
{
while(InPtr - HMML.annotations[AnnotationIndex].text < HMML.annotations[AnnotationIndex].references[RefLocalIndex].offset)
{
*Text.Ptr++ = *InPtr++;
}
int Inc = 0;
if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].offset <= 2)
{
if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].page)
{
Inc = sprintf(Text.Ptr, "%s",
HMML.annotations[AnnotationIndex].references[RefLocalIndex].page);
}
else if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].site)
{
Inc = sprintf(Text.Ptr, "%s",
HMML.annotations[AnnotationIndex].references[RefLocalIndex].site);
}
else if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].title)
{
Inc = sprintf(Text.Ptr, "%s",
HMML.annotations[AnnotationIndex].references[RefLocalIndex].title);
}
else
{
fprintf(stderr, "%s: Potentially incomplete reference at %s:%d\n",
Args[0],
Args[FileIndex],
HMML.annotations[AnnotationIndex].line);
}
Text.Ptr += Inc;
}
if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].offset <= 2 && Text.Ptr[-1] == ' ')
{
--Text.Ptr;
}
Inc = sprintf(Text.Ptr, "<sup>%d</sup>", DataRef);
Text.Ptr += Inc;
++DataRef;
}
CopyStringToBuffer(InPtr, &Text);
}
else
{
sprintf(Working.Location,
" <div class=\"marker\" data-timestamp=\"%d\">\n", TimecodeToSeconds(HMML.annotations[AnnotationIndex].time));
CopyStringToBuffer(InPtr, &Text);
}
*Text.Ptr = '\0';
CopyBuffer(&Working, &Out);
sprintf(Working.Location,
" <div class=\"content\"><span class=\"timecode\">%s</span>%s</div>\n"
" <div class=\"progress faded\">\n"
" <div class=\"content\"><span class=\"timecode\">%s</span>%s</div>\n"
" </div>\n"
" <div class=\"progress main\">\n"
" <div class=\"content\"><span class=\"timecode\">%s</span>%s</div>\n"
" </div>\n"
" </div>\n",
HMML.annotations[AnnotationIndex].time,
Text.Location,
HMML.annotations[AnnotationIndex].time,
Text.Location,
HMML.annotations[AnnotationIndex].time,
Text.Location);
ClaimedMemory -= Text.Size;
CopyBuffer(&Working, &Out);
}
sprintf(Working.Location,
" </div>\n"
" </div>\n"
" <script>\n"
" var player = new Player(document.querySelector(\".player_container\"), onRefChanged);\n"
" window.addEventListener(\"resize\", function() { player.updateSize(); });\n"
" document.addEventListener(\"keypress\", function(ev) {\n"
" switch (ev.key) {\n"
" case 'n':\n"
" case 'd':\n"
" case 's': {\n"
" player.jumpToNextMarker();\n"
" } break;\n"
"\n"
" case 'p':\n"
" case 'a':\n"
" case 'w': {\n"
" player.jumpToPrevMarker();\n"
" } break;\n"
" }\n"
"});\n"
"\n"
"var refTimecodes = document.querySelectorAll(\".refs .ref .timecode\");\n"
"for (var i = 0; i < refTimecodes.length; ++i) {\n"
" refTimecodes[i].addEventListener(\"click\", function(ev) {\n"
" if (player) {\n"
" var time = ev.currentTarget.getAttribute(\"data-timestamp\");\n"
" player.setTime(parseInt(time, 10));\n"
" player.play();\n"
" ev.preventDefault();\n"
" ev.stopPropagation();\n"
" return false;\n"
" }\n"
" });\n"
"}\n"
"\n"
"var refSources = document.querySelectorAll(\".refs .ref\");\n"
"for (var i = 0; i < refSources.length; ++i) {\n"
" refSources[i].addEventListener(\"click\", function(ev) {\n"
" if (player) {\n"
" player.pause();\n"
" }\n"
" });\n"
"}\n"
"\n"
"function onRefChanged(ref) {\n"
" if (ref !== undefined && ref !== null) {\n"
" document.querySelector(\".refs_container\").classList.add(\"current\");\n"
" var refElements = document.querySelectorAll(\".refs .ref\");\n"
" var refs = ref.split(\",\");\n"
"\n"
" for (var i = 0; i < refElements.length; ++i) {\n"
" if (refs.includes(refElements[i].getAttribute(\"data-id\"))) {\n"
" refElements[i].classList.add(\"current\");\n"
" } else {\n"
" refElements[i].classList.remove(\"current\");\n"
" }\n"
" }\n"
" } else {\n"
" document.querySelector(\".refs_container\").classList.remove(\"current\");\n"
" var refs = document.querySelectorAll(\".refs .ref\");\n"
" for (var i = 0; i < refs.length; ++i) {\n"
" refs[i].classList.remove(\"current\");\n"
" }\n"
" }\n"
"}\n"
"\n"
" </script>\n"
" </body>\n"
"</html>\n");
CopyBuffer(&Working, &Out);
hmml_free(&HMML);
FILE *OutFile;
//char *OutFilename;
//sprintf(OutFilename, "%s.html", Args[FileIndex]);
if(!(OutFile = fopen("out.html", "w")))
{
perror(Args[0]);
return 1;
}
fwrite(Out.Location, Out.Ptr - Out.Location, 1, OutFile);
fclose(OutFile);
}
}
free(MemoryArena);
}