cinera.c: Add "direct" video support
Fixes: • Rename Annotation → Timestamp in cinera_player_*.js • Redo VideoIsPrivate() to only return once, at the end
This commit is contained in:
parent
012d1608a0
commit
c839e2ed85
|
@ -22,8 +22,8 @@ typedef struct
|
|||
|
||||
version CINERA_APP_VERSION = {
|
||||
.Major = 0,
|
||||
.Minor = 9,
|
||||
.Patch = 2
|
||||
.Minor = 10,
|
||||
.Patch = 0
|
||||
};
|
||||
|
||||
#include <stdarg.h> // NOTE(matt): varargs
|
||||
|
@ -103,7 +103,8 @@ clock_t TIMING_START;
|
|||
#define MAX_BASE_DIR_LENGTH 128
|
||||
#define MAX_BASE_URL_LENGTH 128
|
||||
#define MAX_RELATIVE_PAGE_LOCATION_LENGTH 32
|
||||
#define MAX_VOD_ID_LENGTH 32
|
||||
#define MAX_VOD_ID_LENGTH 256 // TODO(matt): This was 32, upped to 256 to fit full paths of "raw" videos.
|
||||
// It hasn't mattered, but will do when the database contains everything.
|
||||
|
||||
#define MAX_ROOT_DIR_LENGTH 128
|
||||
#define MAX_ROOT_URL_LENGTH 128
|
||||
|
@ -1493,7 +1494,7 @@ fill the slots left vacant by positioned roles in the order in which they are co
|
|||
{ "title_suffix", "Currently not implemented, probably to be removed." },
|
||||
{ "unit", "This works in conjunction with the numbering_scheme. It is a freely-configurable string - e.g. \"Day\", \"Session\", \"Episode\", \"Meeting\" - which is written on the search page, \
|
||||
preceding the derived number of each entry. If the unit is not set, then the entries will not be numbered." },
|
||||
{ "vod_platform", "Possible VOD platforms: \"vimeo\", \"youtube\"." },
|
||||
{ "vod_platform", "Possible VOD platforms: \"direct\" (for .webm, .mp4, etc. files), \"vimeo\", \"youtube\"." },
|
||||
{ "url", "The URL where viewers may support the person, e.g. their page on a crowd funding site, the \"pledge\" page on their own website." },
|
||||
};
|
||||
|
||||
|
@ -2378,6 +2379,7 @@ GetIconTypeFromString(string *Filename, token *T)
|
|||
char *VODPlatformStrings[] =
|
||||
{
|
||||
0,
|
||||
"direct",
|
||||
"vimeo",
|
||||
"youtube",
|
||||
};
|
||||
|
@ -2385,6 +2387,7 @@ char *VODPlatformStrings[] =
|
|||
typedef enum
|
||||
{
|
||||
VP_DEFAULT_UNSET,
|
||||
VP_DIRECT,
|
||||
VP_VIMEO,
|
||||
VP_YOUTUBE,
|
||||
VP_COUNT,
|
||||
|
@ -9010,12 +9013,11 @@ ExamineDB(void)
|
|||
bool
|
||||
VideoIsPrivate(vod_platform VODPlatform, char *VideoID)
|
||||
{
|
||||
// TODO(matt): Redo this to only return once, at the end
|
||||
|
||||
bool Result = FALSE;
|
||||
// NOTE(matt): Currently only supports YouTube
|
||||
// NOTE(matt): Stack-string
|
||||
if(VODPlatform == VP_YOUTUBE)
|
||||
{
|
||||
// NOTE(matt): Stack-string
|
||||
char Message[128];
|
||||
CopyString(Message, sizeof(Message), "%sChecking%s privacy status of: https://youtube.com/watch?v=%s", ColourStrings[CS_ONGOING], ColourStrings[CS_END], VideoID);
|
||||
fprintf(stderr, "%s", Message);
|
||||
|
@ -9047,34 +9049,32 @@ VideoIsPrivate(vod_platform VODPlatform, char *VideoID)
|
|||
SeekBufferForString(&VideoAPIResponse, "\"totalResults\": ", C_SEEK_FORWARDS, C_SEEK_AFTER);
|
||||
if(*VideoAPIResponse.Ptr == '0')
|
||||
{
|
||||
DeclaimBuffer(&VideoAPIResponse);
|
||||
// printf("Private video: https://youtube.com/watch?v=%s\n", VideoID);
|
||||
ClearTerminalRow(MessageLength);
|
||||
return TRUE;
|
||||
Result = TRUE;
|
||||
}
|
||||
|
||||
VideoAPIResponse.Ptr = VideoAPIResponse.Location;
|
||||
SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER);
|
||||
SeekBufferForString(&VideoAPIResponse, "\"privacyStatus\": \"", C_SEEK_FORWARDS, C_SEEK_AFTER);
|
||||
// NOTE(matt): Stack-string
|
||||
char Status[16];
|
||||
CopyStringNoFormatT(Status, sizeof(Status), VideoAPIResponse.Ptr, '\"');
|
||||
if(!StringsDiffer0(Status, "public"))
|
||||
else
|
||||
{
|
||||
DeclaimBuffer(&VideoAPIResponse);
|
||||
ClearTerminalRow(MessageLength);
|
||||
return FALSE;
|
||||
VideoAPIResponse.Ptr = VideoAPIResponse.Location;
|
||||
SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER);
|
||||
SeekBufferForString(&VideoAPIResponse, "\"privacyStatus\": \"", C_SEEK_FORWARDS, C_SEEK_AFTER);
|
||||
// NOTE(matt): Stack-string
|
||||
char Status[16];
|
||||
CopyStringNoFormatT(Status, sizeof(Status), VideoAPIResponse.Ptr, '\"');
|
||||
if(!StringsDiffer0(Status, "public"))
|
||||
{
|
||||
Result = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
DeclaimBuffer(&VideoAPIResponse);
|
||||
// printf("Unlisted video: https://youtube.com/watch?v=%s\n", VideoID);
|
||||
ClearTerminalRow(MessageLength);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -823,6 +823,14 @@ ul.cineraNavPlain li.current > a {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cineraPlayerContainer .video_container .direct_video {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.cineraPlayerContainer .video_container .direct_video video {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.cineraPlayerContainer .markers_container {
|
||||
flex-shrink: 1;
|
||||
overflow-y: scroll;
|
||||
|
|
|
@ -206,7 +206,7 @@ if(titleBar)
|
|||
}
|
||||
|
||||
linkMenu = titleBar.querySelector(".link_container");
|
||||
linkAnnotation = true;
|
||||
linkTimestamp = true;
|
||||
if(linkMenu)
|
||||
{
|
||||
menuState.push(linkMenu);
|
||||
|
@ -382,12 +382,12 @@ for(var i = 0; i < topicDots.length; ++i)
|
|||
setDotLightness(topicDots[i]);
|
||||
}
|
||||
|
||||
var lastAnnotationStorageItem = "cineraTimecode_" + window.location.pathname;
|
||||
var lastAnnotation;
|
||||
var lastTimestampStorageItem = "cineraTimecode_" + window.location.pathname;
|
||||
var lastTimestamp;
|
||||
if(location.hash) {
|
||||
player.setTimeThenPlay(location.hash.startsWith('#') ? location.hash.substr(1) : location.hash);
|
||||
}
|
||||
else if(lastAnnotation = localStorage.getItem(lastAnnotationStorageItem))
|
||||
else if(lastTimestamp = localStorage.getItem(lastTimestampStorageItem))
|
||||
{
|
||||
player.setTimeThenPlay(lastAnnotation);
|
||||
player.setTimeThenPlay(lastTimestamp);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
// Will be called when the player enters a marker that has a `data-ref` attribute. The value of `data-ref` will be passed to the function.
|
||||
// When leaving a marker that a `data-ref` attribute, and entering a marker without one (or not entering a new marker at all), the function will be called with `null`.
|
||||
var vod_platform = {
|
||||
VIMEO: 0,
|
||||
YOUTUBE: 1,
|
||||
DIRECT: 0,
|
||||
VIMEO: 1,
|
||||
YOUTUBE: 2,
|
||||
};
|
||||
|
||||
function
|
||||
|
@ -12,6 +13,7 @@ GetVODPlatformFromString(VODPlatformString)
|
|||
var Result = null;
|
||||
switch(VODPlatformString)
|
||||
{
|
||||
case "direct": Result = vod_platform.DIRECT; break;
|
||||
case "vimeo": Result = vod_platform.VIMEO; break;
|
||||
case "youtube": Result = vod_platform.YOUTUBE; break;
|
||||
default: break;
|
||||
|
@ -58,6 +60,7 @@ function Player(htmlContainer, refsCallback) {
|
|||
this.platformPlayer = null;
|
||||
this.platformPlayerReady = false;
|
||||
|
||||
this.duration = null;
|
||||
this.playing = false;
|
||||
this.shouldPlay = false;
|
||||
this.buffering = false;
|
||||
|
@ -88,6 +91,10 @@ Player.prototype.play = function() {
|
|||
if (!this.playing) {
|
||||
switch(this.vod_platform)
|
||||
{
|
||||
case vod_platform.DIRECT:
|
||||
{
|
||||
this.platformPlayer.play();
|
||||
} break;
|
||||
case vod_platform.VIMEO:
|
||||
{
|
||||
this.platformPlayer.play();
|
||||
|
@ -111,6 +118,10 @@ Player.prototype.pause = function() {
|
|||
if (this.playing) {
|
||||
switch(this.vod_platform)
|
||||
{
|
||||
case vod_platform.DIRECT:
|
||||
{
|
||||
this.platformPlayer.pause();
|
||||
} break;
|
||||
case vod_platform.VIMEO:
|
||||
{
|
||||
this.platformPlayer.pause();
|
||||
|
@ -134,6 +145,15 @@ Player.prototype.setTimeThenPlay = function(time) {
|
|||
this.currentTime = time;
|
||||
switch(this.vod_platform)
|
||||
{
|
||||
case vod_platform.DIRECT:
|
||||
{
|
||||
if (this.platformPlayerReady) {
|
||||
this.currentTime = Math.max(0, Math.min(this.currentTime, this.duration));
|
||||
this.platformPlayer.currentTime = this.currentTime;
|
||||
this.updateProgress();
|
||||
this.play();
|
||||
}
|
||||
} break;
|
||||
case vod_platform.VIMEO:
|
||||
{
|
||||
if (this.platformPlayerReady) {
|
||||
|
@ -537,7 +557,7 @@ ConnectMobileControls(player)
|
|||
});
|
||||
}
|
||||
|
||||
// Call this after changing the size of the video container in order to update the youtube player.
|
||||
// Call this after changing the size of the video container in order to update the platform player.
|
||||
Player.prototype.updateSize = function() {
|
||||
var width = 0;
|
||||
var height = 0;
|
||||
|
@ -567,16 +587,22 @@ Player.prototype.updateSize = function() {
|
|||
height = this.videoContainer.offsetHeight;
|
||||
}
|
||||
|
||||
switch(this.vod_platform)
|
||||
if(this.platformPlayerReady)
|
||||
{
|
||||
case vod_platform.VIMEO: /* NOTE(matt): It responds automatically */ break;
|
||||
case vod_platform.YOUTUBE:
|
||||
{
|
||||
if (this.platformPlayerReady) {
|
||||
switch(this.vod_platform)
|
||||
{
|
||||
case vod_platform.DIRECT:
|
||||
{
|
||||
this.platformPlayer.setAttribute("width", Math.floor(width));
|
||||
this.platformPlayer.setAttribute("height", Math.floor(height));
|
||||
} break;
|
||||
case vod_platform.VIMEO: break; // NOTE(matt): It responds automatically
|
||||
case vod_platform.YOUTUBE:
|
||||
{
|
||||
this.platformPlayer.setSize(Math.floor(width), Math.floor(height));
|
||||
}
|
||||
} break;
|
||||
default: break;
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -601,6 +627,7 @@ Player.prototype.resume = function() {
|
|||
Player.initializePlatform = function(platform_id, callback) {
|
||||
switch(platform_id)
|
||||
{
|
||||
case vod_platform.DIRECT:
|
||||
case vod_platform.VIMEO:
|
||||
{
|
||||
callback();
|
||||
|
@ -688,11 +715,11 @@ Player.prototype.updateProgress = function() {
|
|||
if (this.currentMarker) {
|
||||
if(this.currentMarkerIdx == this.markers.length - 1)
|
||||
{
|
||||
localStorage.removeItem(lastAnnotationStorageItem);
|
||||
localStorage.removeItem(lastTimestampStorageItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
localStorage.setItem(lastAnnotationStorageItem, this.currentMarker.timestamp);
|
||||
localStorage.setItem(lastTimestampStorageItem, this.currentMarker.timestamp);
|
||||
}
|
||||
this.currentMarker.el.classList.add("current");
|
||||
this.scrollTo = this.currentMarker.el.offsetTop + this.currentMarker.el.offsetHeight/2.0;
|
||||
|
@ -708,6 +735,11 @@ Player.prototype.doFrame = function() {
|
|||
if (this.playing) {
|
||||
switch(this.vod_platform)
|
||||
{
|
||||
case vod_platform.DIRECT:
|
||||
{
|
||||
this.currentTime = this.platformPlayer.currentTime;
|
||||
this.updateProgress();
|
||||
} break;
|
||||
case vod_platform.VIMEO:
|
||||
{
|
||||
var Parent = this;
|
||||
|
@ -744,6 +776,10 @@ Player.prototype.doFrame = function() {
|
|||
Player.prototype.setStyleAndQuality = function() {
|
||||
switch(this.vod_platform)
|
||||
{
|
||||
case vod_platform.DIRECT:
|
||||
{
|
||||
// NOTE(matt): onPlatformReady() has set the width and height
|
||||
} break;
|
||||
case vod_platform.VIMEO:
|
||||
{
|
||||
this.platformPlayer.setQuality("1080p");
|
||||
|
@ -777,6 +813,10 @@ Player.prototype.setDurationThenAutoplay = function(Duration) {
|
|||
Player.prototype.acquireDurationThenAutoplay = function() {
|
||||
switch(this.vod_platform)
|
||||
{
|
||||
case vod_platform.DIRECT:
|
||||
{
|
||||
this.setDurationThenAutoplay(this.platformPlayer.duration);
|
||||
} break;
|
||||
case vod_platform.VIMEO:
|
||||
{
|
||||
var Parent = this;
|
||||
|
@ -804,6 +844,10 @@ Player.prototype.onPlaybackRateChange = function(Rate)
|
|||
this.speed = Rate;
|
||||
}
|
||||
|
||||
Player.prototype.onDirectPlayerPlaybackRateChange = function(ev) {
|
||||
this.onPlaybackRateChange(this.platformPlayer.playbackRate);
|
||||
};
|
||||
|
||||
Player.prototype.onVimeoPlayerPlaybackRateChange = function(ev) {
|
||||
this.onPlaybackRateChange(ev.playbackRate);
|
||||
};
|
||||
|
@ -815,7 +859,6 @@ Player.prototype.onYouTubePlayerPlaybackRateChange = function(ev) {
|
|||
Player.prototype.onStateBufferStart = function()
|
||||
{
|
||||
this.buffering = true;
|
||||
this.playing = false;
|
||||
this.updateProgress();
|
||||
}
|
||||
|
||||
|
@ -829,7 +872,7 @@ Player.prototype.onStateEnded = function()
|
|||
{
|
||||
this.buffering = false;
|
||||
this.playing = false;
|
||||
localStorage.removeItem(lastAnnotationStorageItem);
|
||||
localStorage.removeItem(lastTimestampStorageItem);
|
||||
this.currentTime = null;
|
||||
this.updateProgress();
|
||||
}
|
||||
|
@ -854,6 +897,16 @@ Player.prototype.onStatePlaying = function(CurrentTime)
|
|||
}
|
||||
}
|
||||
|
||||
Player.prototype.onDirectPlayerStateChange_Paused = function(ev)
|
||||
{
|
||||
this.onStatePaused(this.platformPlayer.currentTime);
|
||||
}
|
||||
|
||||
Player.prototype.onDirectPlayerStateChange_Playing = function(ev)
|
||||
{
|
||||
this.onStatePlaying(this.platformPlayer.currentTime);
|
||||
}
|
||||
|
||||
Player.prototype.onVimeoPlayerStateChange_Paused = function(ev)
|
||||
{
|
||||
this.onStatePaused(ev.seconds);
|
||||
|
@ -878,10 +931,29 @@ Player.prototype.onYouTubePlayerStateChange = function(ev) {
|
|||
Player.prototype.onPlatformReady = function() {
|
||||
var platformPlayerDiv = document.createElement("DIV");
|
||||
platformPlayerDiv.id = "platform_player_" + Player.platformPlayerCount++;
|
||||
this.videoContainer.appendChild(platformPlayerDiv);
|
||||
var platformPlayerDivPlaced = this.videoContainer.appendChild(platformPlayerDiv);
|
||||
|
||||
switch(this.vod_platform)
|
||||
{
|
||||
case vod_platform.DIRECT:
|
||||
{
|
||||
platformPlayerDivPlaced.classList.add("direct_video");
|
||||
var videoNode = document.createElement("VIDEO");
|
||||
videoNode.controls = true;
|
||||
videoNode.setAttribute("width", this.videoContainer.offsetWidth);
|
||||
videoNode.setAttribute("height", this.videoContainer.offsetWidth / 16 * 9);
|
||||
var videoSourceNode = document.createElement("SOURCE");
|
||||
videoSourceNode.setAttribute("src", this.videoContainer.getAttribute("data-videoId"));
|
||||
videoNode.appendChild(videoSourceNode);
|
||||
this.platformPlayer = platformPlayerDiv.appendChild(videoNode);
|
||||
this.platformPlayer.addEventListener("loadedmetadata", this.onPlatformPlayerReady.bind(this));
|
||||
this.platformPlayer.addEventListener("ratechange", this.onDirectPlayerPlaybackRateChange.bind(this));
|
||||
this.platformPlayer.addEventListener("play", this.onDirectPlayerStateChange_Playing.bind(this));
|
||||
this.platformPlayer.addEventListener("pause", this.onDirectPlayerStateChange_Paused.bind(this));
|
||||
this.platformPlayer.addEventListener("waiting", this.onStateBufferStart.bind(this));
|
||||
this.platformPlayer.addEventListener("playing", this.onStateBufferEnd.bind(this));
|
||||
this.platformPlayer.addEventListener("ended", this.onStateEnded.bind(this));
|
||||
} break;
|
||||
case vod_platform.VIMEO:
|
||||
{
|
||||
this.videoContainer.style.position = "relative";
|
||||
|
@ -938,7 +1010,7 @@ function updateLink()
|
|||
{
|
||||
if(link && player)
|
||||
{
|
||||
if(linkAnnotation == true)
|
||||
if(linkTimestamp == true)
|
||||
{
|
||||
if(player.currentMarker)
|
||||
{
|
||||
|
@ -953,6 +1025,10 @@ function updateLink()
|
|||
{
|
||||
switch(player.vod_platform)
|
||||
{
|
||||
case vod_platform.DIRECT:
|
||||
{
|
||||
link.value = baseURL + "#" + Math.round(player.platformPlayer.currentTime);
|
||||
} break;
|
||||
case vod_platform.VIMEO:
|
||||
{
|
||||
player.platformPlayer.getCurrentTime()
|
||||
|
@ -972,8 +1048,8 @@ function updateLink()
|
|||
|
||||
function toggleLinkMode(linkMode, link)
|
||||
{
|
||||
linkAnnotation = !linkAnnotation;
|
||||
if(linkAnnotation == true)
|
||||
linkTimestamp = !linkTimestamp;
|
||||
if(linkTimestamp == true)
|
||||
{
|
||||
linkMode.textContent = "Link to: current timestamp";
|
||||
}
|
||||
|
@ -1721,7 +1797,7 @@ function handleKey(key) {
|
|||
case 'Y': {
|
||||
if(cineraLink)
|
||||
{
|
||||
if(linkAnnotation == false && player.playing)
|
||||
if(linkTimestamp == false && player.playing)
|
||||
{
|
||||
player.pause();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue