cinera.css: Marker and categories style
cinera_player_pre.js: Episode keyboard navigation. Also swap out A for K, and D for J cinera_player_pre.js: Handle the case in onRefChanged() in which the filter_container or filterState is not present cinera.c: Refetch quotes when processing a set of annotations >60 mins after the last fetch Flags: -w Force quote cache rebuild
This commit is contained in:
parent
2cac3ed03b
commit
e7aefbada0
192
README.md
192
README.md
|
@ -45,22 +45,16 @@ directory. Typical operation will involve setting these flags:
|
||||||
-d Project Input Directory, the directory where the .hmml files reside
|
-d Project Input Directory, the directory where the .hmml files reside
|
||||||
-r Root Directory, path shallower than or equal to the CSS, Images and JS
|
-r Root Directory, path shallower than or equal to the CSS, Images and JS
|
||||||
directories
|
directories
|
||||||
-u Root URL, corresponding to the Root Directory (optional if the Output
|
-R Root URL, corresponding to the Root Directory (optional if the Output
|
||||||
Base Directory resides in the Root Directory)
|
Base Directory resides in the Root Directory)
|
||||||
|
-c CSS Directory, relative to Root
|
||||||
|
-i Images Directory, relative to Root
|
||||||
|
-j JS Directory, relative to Root
|
||||||
-b Output Base Directory, location of the table of contents / search page
|
-b Output Base Directory, location of the table of contents / search page
|
||||||
-t Player Template Location
|
-B Output Base URL, corresponding to the Output Base Directory
|
||||||
-x Index Template Location
|
-t Template Directory
|
||||||
|
-x Index Template Location, relative to Template Directory
|
||||||
#### Integration
|
-y Player Template Location, relative to Template Directory
|
||||||
|
|
||||||
CINERA_MODE=INTEGRATE cinera test.hmml
|
|
||||||
|
|
||||||
This will integrate into `template_player.html` (configurable with -t) the
|
|
||||||
player and related elements generated from `test.hmml` and output to
|
|
||||||
`out_integrated.html`
|
|
||||||
|
|
||||||
Feel free to play with `template_player.html` to your heart's content. If you do
|
|
||||||
anything invalid, `cinera` will tell you what's wrong
|
|
||||||
|
|
||||||
#### Templates
|
#### Templates
|
||||||
|
|
||||||
|
@ -76,60 +70,128 @@ Valid tags:
|
||||||
- `<!-- __CINERA_INDEX__ -->` _the table of contents, and search functionality_
|
- `<!-- __CINERA_INDEX__ -->` _the table of contents, and search functionality_
|
||||||
|
|
||||||
*Player Template*
|
*Player Template*
|
||||||
- `<!-- __CINERA_INCLUDES__ -->` _the necessary `.css` and `.js` files, and charset setting_
|
- `<!-- __CINERA_INCLUDES__ -->` _the necessary `.css` and `.js` files, and
|
||||||
- `<!-- __CINERA_MENUS__ -->` _ _the menu bar that typically appears above the player in my samples_
|
charset setting_
|
||||||
|
- `<!-- __CINERA_MENUS__ -->` _ _the menu bar that typically appears above the
|
||||||
|
player in my samples_
|
||||||
- `<!-- __CINERA_PLAYER__ -->` _the player_
|
- `<!-- __CINERA_PLAYER__ -->` _the player_
|
||||||
- `<!-- __CINERA_SCRIPT__ -->` _the filter state objects and `.js` file, which must come after both the MENUS and PLAYER tags_
|
- `<!-- __CINERA_SCRIPT__ -->` _the filter state objects and `.js` file, which
|
||||||
|
must come after both the MENUS and PLAYER tags_
|
||||||
|
|
||||||
|
*Optional tags available for use in your Player Template*
|
||||||
|
- `<!-- __CINERA_TITLE__ -->` _the title of the video, excluding numbering_
|
||||||
|
- `<!-- __CINERA_VIDEO_ID__ -->` _the unique identifer of the video as provided
|
||||||
|
by the VoD platform_
|
||||||
|
|
||||||
|
*Other available tags*
|
||||||
|
- `<!-- __CINERA_PROJECT__ -->` _the full name of the project_
|
||||||
|
- `<!-- __CINERA_URL__ -->` _the URL where we have derived the page will be
|
||||||
|
publically accessibly, only really usable if BaseURL is set (-B)_
|
||||||
|
- `<!-- __CINERA_CUSTOM0__ -->`
|
||||||
|
- `<!-- __CINERA_CUSTOM1__ -->`
|
||||||
|
- `<!-- __CINERA_CUSTOM2__ -->`
|
||||||
|
⋮
|
||||||
|
- `<!-- __CINERA_CUSTOM15__ -->`
|
||||||
|
_Freeform buffers for small snippets of localised information, e.g. a single
|
||||||
|
<a> element or perhaps a <!-- comment --> They correspond to the custom0 to
|
||||||
|
custom15 attributes in the [video] node in your .hmml files 0 to 11 may hold
|
||||||
|
up to 255 characters 12 to 15 may hold up to 1023 characters_
|
||||||
|
|
||||||
|
Feel free to play with templates to your heart's content. If you do anything
|
||||||
|
invalid, `cinera` will tell you what's wrong.
|
||||||
|
|
||||||
#### Arguments
|
#### Arguments
|
||||||
|
|
||||||
Usage: ./cinera [option(s)] filename(s)
|
Usage: ./cinera [option(s)] filename(s)
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
Paths:
|
Paths: (advisedly universal, but may be set per-(sub)project as required)
|
||||||
-r <root directory>
|
-r <root directory>
|
||||||
Override default root directory (".")
|
Override default root directory (".")
|
||||||
-u <root URL>
|
-R <root URL>
|
||||||
Override default root URL ("")
|
Override default root URL ("")
|
||||||
-b <base output directory>
|
IMPORTANT: -r and -R must correspond to the same location
|
||||||
Override project's default base output directory (".")
|
UNSUPPORTED: If you move files from RootDir, the RootURL should
|
||||||
-c <CSS directory path>
|
correspond to the resulting location
|
||||||
Override default CSS directory (""), relative to root
|
|
||||||
-i <images directory path>
|
-c <CSS directory path>
|
||||||
Override default images directory (""), relative to root
|
Override default CSS directory (""), relative to root
|
||||||
-j <JS directory path>
|
-i <images directory path>
|
||||||
Override default JS directory (""), relative to root
|
Override default images directory (""), relative to root
|
||||||
-t <player template location>
|
-j <JS directory path>
|
||||||
Override default player template location ("template_player.html"), relative to root
|
Override default JS directory (""), relative to root
|
||||||
and automatically enable integration
|
|
||||||
-x <index template location>
|
Project Settings:
|
||||||
Override default index template location ("template_index.html"), relative to root
|
-p <project ID>
|
||||||
and automatically enable integration
|
Set the project ID, equal to the "project" field in the HMML files
|
||||||
|
NOTE: Setting the project ID triggers PROJECT EDITION
|
||||||
-o <output location>
|
-m <default medium>
|
||||||
Override default output player location for SINGLE_EDITION ("out.html")
|
Override default default medium ("programming")
|
||||||
-d <project directory>
|
Known project defaults:
|
||||||
Override default project directory (".")
|
book: research
|
||||||
|
pcalc: programming
|
||||||
-f
|
riscy: programming
|
||||||
Force integration with an incomplete template
|
chat: speech
|
||||||
-p <project ID>
|
code: programming
|
||||||
Set the project ID, corresponding to the "project" field in the HMML files
|
intro-to-c: programming
|
||||||
-s <style>
|
misc: admin
|
||||||
Set the style / theme, corresponding to a cinera__*.css file
|
ray: programming
|
||||||
This is equal to the "project" field in the HMML files by default
|
hmdshow: speech
|
||||||
-l <n>
|
lecture: speech
|
||||||
Override default log level (0), where n is from 0 (terse) to 7 (verbose)
|
stream: programming
|
||||||
-m <default medium>
|
special: programming
|
||||||
Override default default medium ("programming")
|
obbg: programming
|
||||||
-U <seconds>
|
sysadmin: admin
|
||||||
Override default update interval ("4")
|
-s <style>
|
||||||
|
Set the style / theme, corresponding to a cinera__*.css file
|
||||||
|
This is equal to the "project" field in the HMML files by default
|
||||||
|
-q
|
||||||
|
Quit after syncing with annotation files in project input directory
|
||||||
|
UNSUPPORTED: This is likely to be removed in the future
|
||||||
|
|
||||||
|
Project Input Paths
|
||||||
|
-d <annotations directory>
|
||||||
|
Override default annotations directory (".")
|
||||||
|
-t <templates directory>
|
||||||
|
Override default templates directory (".")
|
||||||
|
|
||||||
|
-x <index template location>
|
||||||
|
Set index template file path, either absolute or relative to
|
||||||
|
template directory, and enable integration
|
||||||
|
-y <player template location>
|
||||||
|
Set player template file path, either absolute or relative
|
||||||
|
to template directory, and enable integration
|
||||||
|
|
||||||
|
Project Output Paths
|
||||||
|
-b <base output directory>
|
||||||
|
Override project's default base output directory (".")
|
||||||
|
-B <base URL>
|
||||||
|
Override default base URL ("")
|
||||||
|
NOTE: This must be set, if -n or -a are to be used
|
||||||
|
|
||||||
|
-n <index location>
|
||||||
|
Override default index location (""), relative to base
|
||||||
|
-a <player location>
|
||||||
|
Override default player location (""), relative to base
|
||||||
|
NOTE: The PlayerURLPrefix is currently hardcoded in cinera.c but
|
||||||
|
will be configurable in the full configuration system
|
||||||
|
|
||||||
|
Single Edition Output Path
|
||||||
|
-o <output location>
|
||||||
|
Override default output player location ("out.html")
|
||||||
|
|
||||||
|
-e
|
||||||
|
Display (examine) index file and exit
|
||||||
|
-f
|
||||||
|
Force integration with an incomplete template
|
||||||
|
-w
|
||||||
|
Force quote cache rebuild (memory aid: "wget")
|
||||||
|
|
||||||
|
-l <n>
|
||||||
|
Override default log level (0), where n is from 0 (terse) to 7 (verbose)
|
||||||
|
-u <seconds>
|
||||||
|
Override default update interval (4)
|
||||||
-v
|
-v
|
||||||
display version and exit
|
Display version and exit
|
||||||
-h
|
-h
|
||||||
display this help
|
Display this help
|
||||||
|
|
||||||
#### Environment Variables
|
|
||||||
|
|
||||||
CINERA_MODE=INTEGRATE
|
|
||||||
Enable integration
|
|
||||||
|
|
165
cinera/cinera.c
165
cinera/cinera.c
|
@ -14,7 +14,7 @@ typedef struct
|
||||||
version CINERA_APP_VERSION = {
|
version CINERA_APP_VERSION = {
|
||||||
.Major = 0,
|
.Major = 0,
|
||||||
.Minor = 5,
|
.Minor = 5,
|
||||||
.Patch = 35
|
.Patch = 36
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(matt): Copy in the DB 3 stuff from cinera_working.c
|
// TODO(matt): Copy in the DB 3 stuff from cinera_working.c
|
||||||
|
@ -83,6 +83,7 @@ enum
|
||||||
{
|
{
|
||||||
MODE_ONESHOT = 1 << 0,
|
MODE_ONESHOT = 1 << 0,
|
||||||
MODE_EXAMINE = 1 << 1,
|
MODE_EXAMINE = 1 << 1,
|
||||||
|
MODE_NOCACHE = 1 << 2,
|
||||||
} modes;
|
} modes;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -114,6 +115,55 @@ typedef struct
|
||||||
int Size;
|
int Size;
|
||||||
} arena;
|
} 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
|
typedef struct
|
||||||
{
|
{
|
||||||
char *Location;
|
char *Location;
|
||||||
|
@ -308,53 +358,6 @@ typedef struct
|
||||||
buffer Buffer;
|
buffer Buffer;
|
||||||
} template;
|
} 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
|
// 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
|
typedef struct
|
||||||
|
@ -1735,6 +1738,7 @@ CurlIntoBuffer(char *InPtr, size_t CharLength, size_t Chars, char **OutputPtr)
|
||||||
void
|
void
|
||||||
CurlQuotes(buffer *QuoteStaging, char *QuotesURL)
|
CurlQuotes(buffer *QuoteStaging, char *QuotesURL)
|
||||||
{
|
{
|
||||||
|
fprintf(stderr, "\e[0;35mFetching\e[0m quotes: %s\n", QuotesURL);
|
||||||
CURL *curl = curl_easy_init();
|
CURL *curl = curl_easy_init();
|
||||||
if(curl) {
|
if(curl) {
|
||||||
CURLcode res;
|
CURLcode res;
|
||||||
|
@ -1800,14 +1804,18 @@ SearchQuotes(buffer *QuoteStaging, int CacheSize, quote_info *Info, int ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
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];
|
char QuoteCacheDir[256];
|
||||||
CopyString(QuoteCacheDir, "%s/quotes", Config.CacheDir);
|
CopyString(QuoteCacheDir, "%s/quotes", Config.CacheDir);
|
||||||
char QuoteCachePath[256];
|
char QuoteCachePath[256];
|
||||||
CopyString(QuoteCachePath, "%s/%s", QuoteCacheDir, Speaker);
|
CopyString(QuoteCachePath, "%s/%s", QuoteCacheDir, Speaker);
|
||||||
|
|
||||||
|
if(ShouldFetchQuotes)
|
||||||
|
{
|
||||||
|
remove(QuoteCachePath);
|
||||||
|
}
|
||||||
|
|
||||||
FILE *QuoteCache;
|
FILE *QuoteCache;
|
||||||
char QuotesURL[256];
|
char QuotesURL[256];
|
||||||
// TODO(matt): Make the URL configurable
|
// TODO(matt): Make the URL configurable
|
||||||
|
@ -1819,7 +1827,7 @@ BuildQuote(quote_info *Info, char *Speaker, int ID)
|
||||||
if(MakeDir(QuoteCacheDir) == RC_SUCCESS)
|
if(MakeDir(QuoteCacheDir) == RC_SUCCESS)
|
||||||
{
|
{
|
||||||
CacheAvailable = TRUE;
|
CacheAvailable = TRUE;
|
||||||
};
|
}
|
||||||
if(!(QuoteCache = fopen(QuoteCachePath, "a+")))
|
if(!(QuoteCache = fopen(QuoteCachePath, "a+")))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Unable to open quote cache %s: %s\n", QuoteCachePath, strerror(errno));
|
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"
|
" Display (examine) index file and exit\n"
|
||||||
" -f\n"
|
" -f\n"
|
||||||
" Force integration with an incomplete template\n"
|
" Force integration with an incomplete template\n"
|
||||||
|
" -w\n"
|
||||||
|
" Force quote cache rebuild \e[1;30m(memory aid: \"wget\")\e[0m\n"
|
||||||
"\n"
|
"\n"
|
||||||
" -l <n>\n"
|
" -l <n>\n"
|
||||||
" Override default log level (%d), where n is from 0 (terse) to 7 (verbose)\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"
|
" Display this help\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Template:\n"
|
"Template:\n"
|
||||||
" A complete index template shall contain exactly one each of the following tags:\n"
|
" A complete Index Template shall contain exactly one each of the following tags:\n"
|
||||||
" <!-- __CINERA_INCLUDES__ -->\n"
|
" <!-- __CINERA_INCLUDES__ -->\n"
|
||||||
" to put inside your own <head></head>\n"
|
" to put inside your own <head></head>\n"
|
||||||
" <!-- __CINERA_INDEX__ -->\n"
|
" <!-- __CINERA_INDEX__ -->\n"
|
||||||
"\n"
|
"\n"
|
||||||
" A complete player template shall contain exactly one each of the following tags:\n"
|
" A complete Player Template shall contain exactly one each of the following tags:\n"
|
||||||
" <!-- __CINERA_INCLUDES__ -->\n"
|
" <!-- __CINERA_INCLUDES__ -->\n"
|
||||||
" to put inside your own <head></head>\n"
|
" to put inside your own <head></head>\n"
|
||||||
" <!-- __CINERA_MENUS__ -->\n"
|
" <!-- __CINERA_MENUS__ -->\n"
|
||||||
|
@ -2128,7 +2138,7 @@ PrintUsage(char *BinaryLocation, config *DefaultConfig)
|
||||||
" <!-- __CINERA_SCRIPT__ -->\n"
|
" <!-- __CINERA_SCRIPT__ -->\n"
|
||||||
" must come after <!-- __CINERA_MENUS__ --> and <!-- __CINERA_PLAYER__ -->\n"
|
" must come after <!-- __CINERA_MENUS__ --> and <!-- __CINERA_PLAYER__ -->\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Optional tags available for use in your player template:\n"
|
" Optional tags available for use in your Player Template:\n"
|
||||||
" <!-- __CINERA_TITLE__ -->\n"
|
" <!-- __CINERA_TITLE__ -->\n"
|
||||||
" <!-- __CINERA_VIDEO_ID__ -->\n"
|
" <!-- __CINERA_VIDEO_ID__ -->\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -3232,9 +3242,15 @@ AppendedIdentifier:
|
||||||
HasQuote = TRUE;
|
HasQuote = TRUE;
|
||||||
|
|
||||||
char *Speaker = Anno->quote.author ? Anno->quote.author : HMML.metadata.stream_username ? HMML.metadata.stream_username : HMML.metadata.member;
|
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,
|
if(BuildQuote(&QuoteInfo,
|
||||||
Speaker,
|
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);
|
LogError(LOG_ERROR, "Quote #%s %d not found: %s:%d", Speaker, Anno->quote.id, Filename, Anno->line);
|
||||||
Filename[StringLength(Filename) - StringLength(".hmml")] = '\0';
|
Filename[StringLength(Filename) - StringLength(".hmml")] = '\0';
|
||||||
|
@ -3650,7 +3666,8 @@ AppendedIdentifier:
|
||||||
" <span class=\"help_key\">?</span><h1>Keyboard Navigation</h1>\n"
|
" <span class=\"help_key\">?</span><h1>Keyboard Navigation</h1>\n"
|
||||||
"\n"
|
"\n"
|
||||||
" <h2>Global Keys</h2>\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"
|
" <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,
|
CopyStringToBuffer(&CollationBuffers->Menus,
|
||||||
"\n"
|
"\n"
|
||||||
" <h2>Movement</h2>\n"
|
" <h2>In-Menu Movement</h2>\n"
|
||||||
" <div class=\"help_paragraph\">\n"
|
" <div class=\"help_paragraph\">\n"
|
||||||
" <div class=\"key_block\">\n"
|
" <div class=\"key_block\">\n"
|
||||||
" <div class=\"key_column\" style=\"flex-grow: 1\">\n"
|
" <div class=\"key_column\" style=\"flex-grow: 1\">\n"
|
||||||
|
@ -4784,8 +4801,8 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
index_header Header = *(index_header *)Index->Metadata.Buffer.Ptr;
|
Index->Header = *(index_header *)Index->Metadata.Buffer.Ptr;
|
||||||
Index->Metadata.Buffer.Ptr += sizeof(Header);
|
Index->Metadata.Buffer.Ptr += sizeof(Index->Header);
|
||||||
index_metadata *Prev = { 0 };
|
index_metadata *Prev = { 0 };
|
||||||
index_metadata *This = { 0 };
|
index_metadata *This = { 0 };
|
||||||
index_metadata *Next = { 0 };
|
index_metadata *Next = { 0 };
|
||||||
|
@ -4795,12 +4812,12 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
|
||||||
{
|
{
|
||||||
case LINK_INCLUDE:
|
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;
|
This = (index_metadata *)Index->Metadata.Buffer.Ptr;
|
||||||
if(!StringsDiffer(This->BaseFilename, BaseFilename))
|
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));
|
Next = (index_metadata *)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata));
|
||||||
}
|
}
|
||||||
|
@ -4812,17 +4829,13 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
|
||||||
} break;
|
} break;
|
||||||
case LINK_EXCLUDE:
|
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);
|
Index->Metadata.Buffer.Ptr += sizeof(index_metadata);
|
||||||
Next = (index_metadata *)Index->Metadata.Buffer.Ptr;
|
Next = (index_metadata *)Index->Metadata.Buffer.Ptr;
|
||||||
|
|
||||||
for(EntryIndex = 1; EntryIndex < Header.EntryCount; ++EntryIndex)
|
for(EntryIndex = 1; EntryIndex < Index->Header.EntryCount; ++EntryIndex)
|
||||||
{
|
{
|
||||||
Prev = This;
|
Prev = This;
|
||||||
This = Next;
|
This = Next;
|
||||||
|
@ -5892,7 +5905,7 @@ main(int ArgC, char **Args)
|
||||||
}
|
}
|
||||||
|
|
||||||
char CommandLineArg;
|
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:vwx:y:")) != -1)
|
||||||
{
|
{
|
||||||
switch(CommandLineArg)
|
switch(CommandLineArg)
|
||||||
{
|
{
|
||||||
|
@ -5961,6 +5974,9 @@ main(int ArgC, char **Args)
|
||||||
case 'v':
|
case 'v':
|
||||||
PrintVersions();
|
PrintVersions();
|
||||||
return RC_SUCCESS;
|
return RC_SUCCESS;
|
||||||
|
case 'w':
|
||||||
|
Config.Mode |= MODE_NOCACHE;
|
||||||
|
break;
|
||||||
case 'x':
|
case 'x':
|
||||||
Config.TemplateIndexLocation = optarg;
|
Config.TemplateIndexLocation = optarg;
|
||||||
break;
|
break;
|
||||||
|
@ -6226,6 +6242,15 @@ main(int ArgC, char **Args)
|
||||||
|
|
||||||
while(MonitorDirectory(&Index, &CollationBuffers, IndexTemplate, PlayerTemplate, BespokeTemplate, inotifyInstance, WatchDescriptor) != RC_ERROR_FATAL)
|
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);
|
sleep(Config.UpdateInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -528,7 +528,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
|
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent {
|
||||||
display: block;
|
display: inline-block;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,7 +575,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent .cineraCategories {
|
.cineraPlayerContainer .markers_container > .markers .marker .cineraContent .cineraCategories {
|
||||||
margin: 4px;
|
float: right;
|
||||||
|
min-height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cineraMenus > .menu > .filter_container .filter_content {
|
.cineraMenus > .menu > .filter_container .filter_content {
|
||||||
|
|
|
@ -220,6 +220,8 @@ for(var i = 0; i < sourceMenus.length; ++i)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var prevEpisode = playerContainer.querySelector(".episodeMarker.prev");
|
||||||
|
var nextEpisode = playerContainer.querySelector(".episodeMarker.next");
|
||||||
var testMarkers = playerContainer.querySelectorAll(".marker");
|
var testMarkers = playerContainer.querySelectorAll(".marker");
|
||||||
|
|
||||||
window.addEventListener("blur", function(){
|
window.addEventListener("blur", function(){
|
||||||
|
|
|
@ -56,8 +56,6 @@ function Player(htmlContainer, refsCallback) {
|
||||||
|
|
||||||
Player.initializeYoutube(this.onYoutubeReady.bind(this));
|
Player.initializeYoutube(this.onYoutubeReady.bind(this));
|
||||||
this.updateSize();
|
this.updateSize();
|
||||||
|
|
||||||
this.resume();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start playing the video from the current position.
|
// Start playing the video from the current position.
|
||||||
|
@ -68,6 +66,7 @@ Player.prototype.play = function() {
|
||||||
this.youtubePlayer.playVideo();
|
this.youtubePlayer.playVideo();
|
||||||
}
|
}
|
||||||
this.pauseAfterBuffer = false;
|
this.pauseAfterBuffer = false;
|
||||||
|
this.resume();
|
||||||
} else {
|
} else {
|
||||||
this.shouldPlay = true;
|
this.shouldPlay = true;
|
||||||
}
|
}
|
||||||
|
@ -119,6 +118,7 @@ Player.prototype.updateSize = function() {
|
||||||
this.markersContainer.style.height = height;
|
this.markersContainer.style.height = height;
|
||||||
if (this.youtubePlayerReady) {
|
if (this.youtubePlayerReady) {
|
||||||
this.youtubePlayer.setSize(Math.floor(width), Math.floor(height));
|
this.youtubePlayer.setSize(Math.floor(width), Math.floor(height));
|
||||||
|
this.resume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -929,16 +929,30 @@ function handleKey(key) {
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case 'N':
|
case 'N':
|
||||||
case 'D':
|
case 'J':
|
||||||
case 'S': {
|
case 'S': {
|
||||||
player.jumpToNextMarker();
|
player.jumpToNextMarker();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
case 'A':
|
case 'K':
|
||||||
case 'W': {
|
case 'W': {
|
||||||
player.jumpToPrevMarker();
|
player.jumpToPrevMarker();
|
||||||
} break;
|
} break;
|
||||||
|
case '[':
|
||||||
|
case '<': {
|
||||||
|
if(prevEpisode)
|
||||||
|
{
|
||||||
|
location = prevEpisode.href;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case ']':
|
||||||
|
case '>': {
|
||||||
|
if(nextEpisode)
|
||||||
|
{
|
||||||
|
location = nextEpisode.href;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
default: {
|
default: {
|
||||||
gotKey = false;
|
gotKey = false;
|
||||||
} break;
|
} break;
|
||||||
|
@ -1165,6 +1179,22 @@ function resetFade() {
|
||||||
function onRefChanged(ref, element) {
|
function onRefChanged(ref, element) {
|
||||||
if(element.classList.contains("skip"))
|
if(element.classList.contains("skip"))
|
||||||
{
|
{
|
||||||
|
var filterState;
|
||||||
|
var ErrorCount = 0;
|
||||||
|
if(!filter) { console.log("Missing filter_container div"); ErrorCount++; }
|
||||||
|
if(!filterState) { console.log("Missing filterState object"); ErrorCount++; }
|
||||||
|
if(ErrorCount > 0)
|
||||||
|
{
|
||||||
|
switch(ErrorCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
{ console.log("This should have been generated by Cinera along with the following element containing the \"skip\" class:"); } break;
|
||||||
|
default:
|
||||||
|
{ console.log("These should have been generated by Cinera along with the following element containing the \"skip\" class:"); } break;
|
||||||
|
}
|
||||||
|
console.log(element); return;
|
||||||
|
}
|
||||||
|
|
||||||
if(!filter.classList.contains("responsible"))
|
if(!filter.classList.contains("responsible"))
|
||||||
{
|
{
|
||||||
filter.classList.add("responsible");
|
filter.classList.add("responsible");
|
||||||
|
|
Binary file not shown.
|
@ -1,108 +0,0 @@
|
||||||
#ifndef HMML_H_
|
|
||||||
#define HMML_H_
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
// Data structures
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char* member;
|
|
||||||
char* stream_platform;
|
|
||||||
char* stream_username;
|
|
||||||
char* project;
|
|
||||||
char* title;
|
|
||||||
char* vod_platform;
|
|
||||||
char* id;
|
|
||||||
|
|
||||||
char** co_hosts;
|
|
||||||
size_t co_host_count;
|
|
||||||
|
|
||||||
char** guests;
|
|
||||||
size_t guest_count;
|
|
||||||
|
|
||||||
char** annotators;
|
|
||||||
size_t annotator_count;
|
|
||||||
|
|
||||||
char* template;
|
|
||||||
char* medium;
|
|
||||||
|
|
||||||
} HMML_VideoMetaData;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char* site;
|
|
||||||
char* page;
|
|
||||||
char* url;
|
|
||||||
char* title;
|
|
||||||
char* article;
|
|
||||||
char* author;
|
|
||||||
char* editor;
|
|
||||||
char* publisher;
|
|
||||||
char* isbn;
|
|
||||||
int offset;
|
|
||||||
} HMML_Reference;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
HMML_CATEGORY,
|
|
||||||
HMML_MEMBER,
|
|
||||||
HMML_PROJECT,
|
|
||||||
|
|
||||||
HMML_MARKER_COUNT,
|
|
||||||
} HMML_MarkerType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
HMML_MarkerType type;
|
|
||||||
char* marker;
|
|
||||||
char* parameter;
|
|
||||||
char* episode;
|
|
||||||
int offset;
|
|
||||||
} HMML_Marker;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int id;
|
|
||||||
char* author;
|
|
||||||
} HMML_Quote;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int line;
|
|
||||||
char* time;
|
|
||||||
char* text;
|
|
||||||
char* author;
|
|
||||||
|
|
||||||
HMML_Reference* references;
|
|
||||||
size_t reference_count;
|
|
||||||
|
|
||||||
HMML_Marker* markers;
|
|
||||||
size_t marker_count;
|
|
||||||
|
|
||||||
HMML_Quote quote;
|
|
||||||
bool is_quote;
|
|
||||||
} HMML_Annotation;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int line;
|
|
||||||
char* message;
|
|
||||||
} HMML_Error;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bool well_formed;
|
|
||||||
HMML_VideoMetaData metadata;
|
|
||||||
HMML_Annotation* annotations;
|
|
||||||
size_t annotation_count;
|
|
||||||
HMML_Error error;
|
|
||||||
} HMML_Output;
|
|
||||||
|
|
||||||
// Functions
|
|
||||||
|
|
||||||
HMML_Output hmml_parse_file (FILE* file);
|
|
||||||
void hmml_dump (HMML_Output* output);
|
|
||||||
void hmml_free (HMML_Output* output);
|
|
||||||
|
|
||||||
// Version
|
|
||||||
|
|
||||||
extern const struct HMML_Version {
|
|
||||||
int Major, Minor, Patch;
|
|
||||||
} hmml_version;
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue