@ -14,7 +14,7 @@ typedef struct
version CINERA_APP_VERSION = {
. Major = 0 ,
. Minor = 5 ,
. Patch = 35
. Patch = 36
} ;
// TODO(matt): Copy in the DB 3 stuff from cinera_working.c
@ -83,6 +83,7 @@ enum
{
MODE_ONESHOT = 1 < < 0 ,
MODE_EXAMINE = 1 < < 1 ,
MODE_NOCACHE = 1 < < 2 ,
} modes ;
enum
@ -114,6 +115,55 @@ typedef struct
int Size ;
} arena ;
typedef struct
{
// Universal
char CacheDir [ 256 ] ;
int Edition ;
int LogLevel ;
int Mode ;
int UpdateInterval ;
bool ForceIntegration ;
// Advisedly universal, although could be per-project
char * RootDir ; // Absolute
char * RootURL ;
char * CSSDir ; // Relative to Root{Dir,URL}
char * ImagesDir ; // Relative to Root{Dir,URL}
char * JSDir ; // Relative to Root{Dir,URL}
// Per Project
char * ProjectID ;
char * Theme ;
char * DefaultMedium ;
// Per Project - Input
char * ProjectDir ; // Absolute
char * TemplatesDir ; // Absolute
char * TemplateIndexLocation ; // Relative to TemplatesDir ???
char * TemplatePlayerLocation ; // Relative to TemplatesDir ???
// Per Project - Output
char * BaseDir ; // Absolute
char * BaseURL ;
char * IndexLocation ; // Relative to Base{Dir,URL}
char * PlayerLocation ; // Relative to Base{Dir,URL}
char * PlayerURLPrefix ; /* NOTE(matt): This will become a full blown customisable output URL.
For now it simply replaces the ProjectID */
// Single Edition - Input
char SingleHMMLFilePath [ 256 ] ;
// Single Edition - Output
char * OutLocation ;
char * OutIntegratedLocation ;
} config ;
// NOTE(matt): Globals
config Config = { } ;
arena MemoryArena ;
time_t LastQuoteFetch ;
//
typedef struct
{
char * Location ;
@ -308,53 +358,6 @@ typedef struct
buffer Buffer ;
} template ;
typedef struct
{
// Universal
char CacheDir [ 256 ] ;
int Edition ;
int LogLevel ;
int Mode ;
int UpdateInterval ;
bool ForceIntegration ;
// Advisedly universal, although could be per-project
char * RootDir ; // Absolute
char * RootURL ;
char * CSSDir ; // Relative to Root{Dir,URL}
char * ImagesDir ; // Relative to Root{Dir,URL}
char * JSDir ; // Relative to Root{Dir,URL}
// Per Project
char * ProjectID ;
char * Theme ;
char * DefaultMedium ;
// Per Project - Input
char * ProjectDir ; // Absolute
char * TemplatesDir ; // Absolute
char * TemplateIndexLocation ; // Relative to TemplatesDir ???
char * TemplatePlayerLocation ; // Relative to TemplatesDir ???
// Per Project - Output
char * BaseDir ; // Absolute
char * BaseURL ;
char * IndexLocation ; // Relative to Base{Dir,URL}
char * PlayerLocation ; // Relative to Base{Dir,URL}
char * PlayerURLPrefix ; /* NOTE(matt): This will become a full blown customisable output URL.
For now it simply replaces the ProjectID */
// Single Edition - Input
char SingleHMMLFilePath [ 256 ] ;
// Single Edition - Output
char * OutLocation ;
char * OutIntegratedLocation ;
} config ;
// NOTE(matt): Globals
config Config = { } ;
arena MemoryArena ;
// TODO(matt): Consider putting the ref_info and quote_info into linked lists on the heap, just to avoid all the hardcoded sizes
typedef struct
@ -1735,6 +1738,7 @@ CurlIntoBuffer(char *InPtr, size_t CharLength, size_t Chars, char **OutputPtr)
void
CurlQuotes ( buffer * QuoteStaging , char * QuotesURL )
{
fprintf ( stderr , " \ e[0;35mFetching \ e[0m quotes: %s \n " , QuotesURL ) ;
CURL * curl = curl_easy_init ( ) ;
if ( curl ) {
CURLcode res ;
@ -1800,14 +1804,18 @@ SearchQuotes(buffer *QuoteStaging, int CacheSize, quote_info *Info, int ID)
}
int
BuildQuote ( quote_info * Info , char * Speaker , int ID )
BuildQuote ( quote_info * Info , char * Speaker , int ID , bool ShouldFetchQuotes )
{
// TODO(matt): Rebuild cache option
char QuoteCacheDir [ 256 ] ;
CopyString ( QuoteCacheDir , " %s/quotes " , Config . CacheDir ) ;
char QuoteCachePath [ 256 ] ;
CopyString ( QuoteCachePath , " %s/%s " , QuoteCacheDir , Speaker ) ;
if ( ShouldFetchQuotes )
{
remove ( QuoteCachePath ) ;
}
FILE * QuoteCache ;
char QuotesURL [ 256 ] ;
// TODO(matt): Make the URL configurable
@ -1819,7 +1827,7 @@ BuildQuote(quote_info *Info, char *Speaker, int ID)
if ( MakeDir ( QuoteCacheDir ) = = RC_SUCCESS )
{
CacheAvailable = TRUE ;
} ;
}
if ( ! ( QuoteCache = fopen ( QuoteCachePath , " a+ " ) ) )
{
fprintf ( stderr , " Unable to open quote cache %s: %s \n " , QuoteCachePath , strerror ( errno ) ) ;
@ -2103,6 +2111,8 @@ PrintUsage(char *BinaryLocation, config *DefaultConfig)
" Display (examine) index file and exit \n "
" -f \n "
" Force integration with an incomplete template \n "
" -w \n "
" Force quote cache rebuild \ e[1;30m(memory aid: \" wget \" ) \ e[0m \n "
" \n "
" -l <n> \n "
" Override default log level (%d), where n is from 0 (terse) to 7 (verbose) \n "
@ -2115,12 +2125,12 @@ PrintUsage(char *BinaryLocation, config *DefaultConfig)
" Display this help \n "
" \n "
" Template: \n "
" A complete index t emplate shall contain exactly one each of the following tags: \n "
" A complete Index T emplate shall contain exactly one each of the following tags: \n "
" <!-- __CINERA_INCLUDES__ --> \n "
" to put inside your own <head></head> \n "
" <!-- __CINERA_INDEX__ --> \n "
" \n "
" A complete player t emplate shall contain exactly one each of the following tags: \n "
" A complete Player T emplate shall contain exactly one each of the following tags: \n "
" <!-- __CINERA_INCLUDES__ --> \n "
" to put inside your own <head></head> \n "
" <!-- __CINERA_MENUS__ --> \n "
@ -2128,7 +2138,7 @@ PrintUsage(char *BinaryLocation, config *DefaultConfig)
" <!-- __CINERA_SCRIPT__ --> \n "
" must come after <!-- __CINERA_MENUS__ --> and <!-- __CINERA_PLAYER__ --> \n "
" \n "
" Optional tags available for use in your player t emplate: \n "
" Optional tags available for use in your Player T emplate: \n "
" <!-- __CINERA_TITLE__ --> \n "
" <!-- __CINERA_VIDEO_ID__ --> \n "
" \n "
@ -3232,9 +3242,15 @@ AppendedIdentifier:
HasQuote = TRUE ;
char * Speaker = Anno - > quote . author ? Anno - > quote . author : HMML . metadata . stream_username ? HMML . metadata . stream_username : HMML . metadata . member ;
bool ShouldFetchQuotes = FALSE ;
if ( Config . Mode & MODE_NOCACHE | | ( Config . Edition ! = EDITION_SINGLE & & time ( 0 ) - LastQuoteFetch > 60 * 60 ) )
{
ShouldFetchQuotes = TRUE ;
LastQuoteFetch = time ( 0 ) ;
}
if ( BuildQuote ( & QuoteInfo ,
Speaker ,
Anno - > quote . id ) = = RC_UNFOUND )
Anno - > quote . id , ShouldFetchQuotes ) = = RC_UNFOUND )
{
LogError ( LOG_ERROR , " Quote #%s %d not found: %s:%d " , Speaker , Anno - > quote . id , Filename , Anno - > line ) ;
Filename [ StringLength ( Filename ) - StringLength ( " .hmml " ) ] = ' \0 ' ;
@ -3650,7 +3666,8 @@ AppendedIdentifier:
" <span class= \" help_key \" >?</span><h1>Keyboard Navigation</h1> \n "
" \n "
" <h2>Global Keys</h2> \n "
" <span class= \" help_key \" >W</span>, <span class= \" help_key \" >A</span>, <span class= \" help_key \" >P</span> / <span class= \" help_key \" >S</span>, <span class= \" help_key \" >D</span>, <span class= \" help_key \" >N</span> <span class= \" help_text \" >Jump to previous / next marker</span><br> \n "
" <span class= \" help_key \" >[</span>, <span class= \" help_key \" ><</span> / <span class= \" help_key \" >]</span>, <span class= \" help_key \" >></span> <span class= \" help_text \" >Jump to previous / next episode</span><br> \n "
" <span class= \" help_key \" >W</span>, <span class= \" help_key \" >K</span>, <span class= \" help_key \" >P</span> / <span class= \" help_key \" >S</span>, <span class= \" help_key \" >J</span>, <span class= \" help_key \" >N</span> <span class= \" help_text \" >Jump to previous / next marker</span><br> \n "
" <span class= \" help_key \" >t</span> / <span class= \" help_key \" >T</span> <span class= \" help_text \" >Toggle theatre / SUPERtheatre mode</span><br> \n "
) ;
@ -3715,7 +3732,7 @@ AppendedIdentifier:
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" \n "
" <h2>Movement</h2> \n "
" <h2>In-Menu Movement</h2> \n "
" <div class= \" help_paragraph \" > \n "
" <div class= \" key_block \" > \n "
" <div class= \" key_column \" style= \" flex-grow: 1 \" > \n "
@ -4784,8 +4801,8 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
break ;
}
index_header Header = * ( index_header * ) Index - > Metadata . Buffer . Ptr ;
Index - > Metadata . Buffer . Ptr + = sizeof ( Header ) ;
Index - > Header = * ( index_header * ) Index - > Metadata . Buffer . Ptr ;
Index - > Metadata . Buffer . Ptr + = sizeof ( Index - > Header ) ;
index_metadata * Prev = { 0 } ;
index_metadata * This = { 0 } ;
index_metadata * Next = { 0 } ;
@ -4795,12 +4812,12 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
{
case LINK_INCLUDE :
{
for ( EntryIndex = 0 ; EntryIndex < Header . EntryCount ; + + EntryIndex )
for ( EntryIndex = 0 ; EntryIndex < Index - > Header . EntryCount ; + + EntryIndex )
{
This = ( index_metadata * ) Index - > Metadata . Buffer . Ptr ;
if ( ! StringsDiffer ( This - > BaseFilename , BaseFilename ) )
{
if ( EntryIndex < ( Header . EntryCount - 1 ) )
if ( EntryIndex < ( Index - > Header . EntryCount - 1 ) )
{
Next = ( index_metadata * ) ( Index - > Metadata . Buffer . Ptr + sizeof ( index_metadata ) ) ;
}
@ -4812,17 +4829,13 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
} break ;
case LINK_EXCLUDE :
{
if ( Index - > Header . EntryCount = = 1 )
This = ( index_metadata * ) Index - > Metadata . Buffer . Ptr ;
if ( Index - > Header . EntryCount ! = 1 )
{
This = ( index_metadata * ) Index - > Metadata . Buffer . Ptr ;
}
else
{
This = ( index_metadata * ) Index - > Metadata . Buffer . Ptr ;
Index - > Metadata . Buffer . Ptr + = sizeof ( index_metadata ) ;
Next = ( index_metadata * ) Index - > Metadata . Buffer . Ptr ;
for ( EntryIndex = 1 ; EntryIndex < Header . EntryCount ; + + EntryIndex )
for ( EntryIndex = 1 ; EntryIndex < Index - > Header . EntryCount ; + + EntryIndex )
{
Prev = This ;
This = Next ;
@ -5892,7 +5905,7 @@ main(int ArgC, char **Args)
}
char CommandLineArg ;
while ( ( CommandLineArg = getopt ( ArgC , Args , " a:b:B:c:d:efhi:j:l:m:n:o:p:qr:R:s:t:u:vx:y: " ) ) ! = - 1 )
while ( ( CommandLineArg = getopt ( ArgC , Args , " a:b:B:c:d:efhi:j:l:m:n:o:p:qr:R:s:t:u:vw x:y: " ) ) ! = - 1 )
{
switch ( CommandLineArg )
{
@ -5961,6 +5974,9 @@ main(int ArgC, char **Args)
case ' v ' :
PrintVersions ( ) ;
return RC_SUCCESS ;
case ' w ' :
Config . Mode | = MODE_NOCACHE ;
break ;
case ' x ' :
Config . TemplateIndexLocation = optarg ;
break ;
@ -6226,6 +6242,15 @@ main(int ArgC, char **Args)
while ( MonitorDirectory ( & Index , & CollationBuffers , IndexTemplate , PlayerTemplate , BespokeTemplate , inotifyInstance , WatchDescriptor ) ! = RC_ERROR_FATAL )
{
// TODO(matt): Refetch the quotes and rebuild player pages if needed
//
// Every sixty mins, redownload the quotes and, I suppose, SyncIndexWithInput(). But here we still don't even know
// who the speaker is. To know, we'll probably have to store all quoted speakers in the project's .metadata. Maybe
// postpone this for now, but we will certainly need this to happen
//
// The most ideal solution is possibly that we store quote numbers in the Metadata->Entry, listen for and handle a
// REST PUT request from insobot when a quote changes (unless we're supposed to poll insobot for them?), and rebuild
// the player page(s) accordingly.
sleep ( Config . UpdateInterval ) ;
}
}