Get Video Player Data Using New API (#151)

* Get  video player data using new API

* Spelling

* Removed extra data from play button

* Reworked createPlayer, switched functions to API

* Add theme to scrollbar

* Removed extra metadata from playlist page

* Removed extra metadat from channel page

* Reworked createPlayer, switched functions to API

* Update style.css

* Changed watched indicator to match createVideo()

* Fixed createPlayer() watched button

* Fix watched indicator duplication

* Minor clean up

* Removed player-wrapper background

* Added video/channel info to generated player

* Removed description due to textReveal() conflict

* Mark video as played at 90% playback

* Groundwork for saving video playback

* Add half and empty stars to getStarRating()

* Check videoProgress input.

* Added last refresh and date published

* Switched date in create functions to API

* Fomatted dates to match the old format

* Remove console log from formatDates()

* Cleaned up error on video player close

* Added check for ryd dislikes/rating

* Refined ryd check

* Simplified player

* Added player stats css formatting

* Formatting for playlist name/link

* Add playlist title/link to player

* Commented out no longer used code

* Fix missing end `"` on video-player class

* Additional playlist error checking

* Change setting video progress to html method

* center thumbs icon, add eye icon for watched

* add playerStats builder example, change some spacing

* Removed `-` before playlist, reordered cast button

* Minor cleanup of unused code.

* Corrected POST data formating

* consolidate video api calls into one

* remove redundant api calls for search result population

* do some jshint

* shorten unit and add K to formatNumbers

Co-authored-by: simon <simobilleter@gmail.com>
This commit is contained in:
Nathan DeTar 2022-02-05 03:26:31 -08:00 committed by GitHub
parent 8591c44ef2
commit 9079a2a78b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 330 additions and 133 deletions

View File

@ -106,7 +106,7 @@
{% if results %} {% if results %}
{% for video in results %} {% for video in results %}
<div class="video-item {{ view_style }}"> <div class="video-item {{ view_style }}">
<a href="#player" data-src="/media/{{ video.source.media_url }}" data-thumb="/cache/{{ video.source.vid_thumb_url }}" data-title="{{ video.source.title }}" data-channel="{{ video.source.channel.channel_name }}" data-channel-id="{{ video.source.channel.channel_id }}" data-id="{{ video.source.youtube_id }}" onclick="createPlayer(this)"> <a href="#player" data-id="{{ video.source.youtube_id }}" onclick="createPlayer(this)">
<div class="video-thumb-wrap {{ view_style }}"> <div class="video-thumb-wrap {{ view_style }}">
<div class="video-thumb"> <div class="video-thumb">
<img src="/cache/{{ video.source.vid_thumb_url }}" alt="video-thumb"> <img src="/cache/{{ video.source.vid_thumb_url }}" alt="video-thumb">

View File

@ -45,7 +45,7 @@
{% if results %} {% if results %}
{% for video in results %} {% for video in results %}
<div class="video-item {{ view_style }}"> <div class="video-item {{ view_style }}">
<a href="#player" data-src="/media/{{ video.source.media_url }}" data-thumb="/cache/{{ video.source.vid_thumb_url }}" data-title="{{ video.source.title }}" data-channel="{{ video.source.channel.channel_name }}" data-channel-id="{{ video.source.channel.channel_id }}" data-id="{{ video.source.youtube_id }}" onclick="createPlayer(this)"> <a href="#player" data-id="{{ video.source.youtube_id }}" onclick="createPlayer(this)">
<div class="video-thumb-wrap {{ view_style }}"> <div class="video-thumb-wrap {{ view_style }}">
<div class="video-thumb"> <div class="video-thumb">
<img src="/cache/{{ video.source.vid_thumb_url }}" alt="video-thumb"> <img src="/cache/{{ video.source.vid_thumb_url }}" alt="video-thumb">

View File

@ -87,7 +87,7 @@
{% if results %} {% if results %}
{% for video in results %} {% for video in results %}
<div class="video-item {{ view_style }}"> <div class="video-item {{ view_style }}">
<a href="#player" data-src="/media/{{ video.source.media_url }}" data-thumb="/cache/{{ video.source.vid_thumb_url }}" data-title="{{ video.source.title }}" data-channel="{{ video.source.channel.channel_name }}" data-channel-id="{{ video.source.channel.channel_id }}" data-id="{{ video.source.youtube_id }}" onclick="createPlayer(this)"> <a href="#player" data-id="{{ video.source.youtube_id }}" onclick="createPlayer(this)">
<div class="video-thumb-wrap {{ view_style }}"> <div class="video-thumb-wrap {{ view_style }}">
<div class="video-thumb"> <div class="video-thumb">
<img src="/cache/{{ video.source.vid_thumb_url }}" alt="video-thumb"> <img src="/cache/{{ video.source.vid_thumb_url }}" alt="video-thumb">

View File

@ -6,7 +6,7 @@
<video <video
src="/media/{{ video.media_url }}" src="/media/{{ video.media_url }}"
poster="/cache/{{ video.vid_thumb_url }}" controls preload="false" poster="/cache/{{ video.vid_thumb_url }}" controls preload="false"
type='video/mp4' width="100%" playsinline id="video-item"> type='video/mp4' width="100%" playsinline id="video-item" ontimeupdate="onVideoProgress('{{ video.youtube_id }}')" onloadedmetadata="setVideoProgress(0)">
</video> </video>
</div> </div>
<div class="boxed-content"> <div class="boxed-content">

View File

@ -0,0 +1,3 @@
{
"esversion": 6
}

View File

@ -456,6 +456,17 @@ button:hover {
text-transform: uppercase; text-transform: uppercase;
} }
.player-stats {
float: right;
display: flex;
align-items: center;
margin-top: 10px;
}
.player-stats span {
margin: 0 5px;
}
.video-desc-player { .video-desc-player {
margin-bottom: 8px; margin-bottom: 8px;
display: flex; display: flex;
@ -639,7 +650,7 @@ button:hover {
filter: var(--img-filter); filter: var(--img-filter);
} }
.thumb-icon.dislike img { .dislike {
transform: rotate(180deg); transform: rotate(180deg);
} }

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="500"
height="500"
viewBox="0 0 132.29197 132.29167"
version="1.1"
id="svg1303"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="Icons_seen.svg">
<defs
id="defs1297" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.0105705"
inkscape:cx="84.208758"
inkscape:cy="136.94831"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata1300">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-164.70764)">
<path
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
d="M 66.145984,191.3255 A 71.797122,73.404487 0 0 0 2.5025987,230.88314 71.797122,73.404487 0 0 0 66.145984,270.38145 71.797122,73.404487 0 0 0 129.78937,230.82387 71.797122,73.404487 0 0 0 66.145984,191.3255 Z m -4.921549,7.22394 a 32.724755,32.724755 0 0 0 -13.334395,5.17759 12.828107,12.828107 0 0 1 2.180958,-0.18652 12.828107,12.828107 0 0 1 12.828141,12.82816 12.828107,12.828107 0 0 1 -12.828141,12.82822 12.828107,12.828107 0 0 1 -12.828192,-12.82822 12.828107,12.828107 0 0 1 0.04509,-0.91548 32.724755,32.724755 0 0 0 -3.86581,15.39964 32.724755,32.724755 0 0 0 27.161922,32.24981 A 59.09757,60.420619 0 0 1 13.7604,230.8774 59.09757,60.420619 0 0 1 61.224664,198.54948 Z m 10.482345,0.0563 a 59.09757,60.420619 0 0 1 46.82502,32.22523 59.09757,60.420619 0 0 1 -47.393377,32.32497 32.724755,32.724755 0 0 0 27.73169,-32.30187 32.724755,32.724755 0 0 0 -27.163333,-32.24833 z"
id="path1091"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -15,7 +15,7 @@
version="1.1" version="1.1"
id="svg1303" id="svg1303"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)" inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="Icons_thumbs.svg"> sodipodi:docname="icon_thumb.svg">
<defs <defs
id="defs1297" /> id="defs1297" />
<sodipodi:namedview <sodipodi:namedview
@ -25,18 +25,31 @@
borderopacity="1.0" borderopacity="1.0"
inkscape:pageopacity="0.0" inkscape:pageopacity="0.0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="0.71458126" inkscape:zoom="1.5046797"
inkscape:cx="-133.80736" inkscape:cx="35.718548"
inkscape:cy="163.34297" inkscape:cy="203.39339"
inkscape:document-units="mm" inkscape:document-units="mm"
inkscape:current-layer="layer1" inkscape:current-layer="layer1"
showgrid="false" showgrid="false"
units="px" units="px"
inkscape:window-width="1920" inkscape:window-width="1920"
inkscape:window-height="1017" inkscape:window-height="1009"
inkscape:window-x="-8" inkscape:window-x="-8"
inkscape:window-y="-8" inkscape:window-y="-8"
inkscape:window-maximized="1" /> inkscape:window-maximized="1"
showguides="true"
inkscape:guide-bbox="true">
<sodipodi:guide
position="80.99058,65.965029"
orientation="0,1"
id="guide816"
inkscape:locked="false" />
<sodipodi:guide
position="65.965178,49.107299"
orientation="1,0"
id="guide818"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata <metadata
id="metadata1300"> id="metadata1300">
<rdf:RDF> <rdf:RDF>
@ -55,8 +68,8 @@
id="layer1" id="layer1"
transform="translate(0,-164.70764)"> transform="translate(0,-164.70764)">
<path <path
style="opacity:1;fill:#1a1a1a;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
d="m 74.762046,169.84647 c -0.757275,-0.0266 -1.556205,0.0266 -2.398918,0.17006 -17.977757,2.9811 5.863528,29.19697 -15.075776,44.15933 -17.503229,14.60104 -23.870308,10.01972 -24.044759,14.80451 v 47.00292 c 2.623747,6.94761 20.093043,14.05087 38.660635,15.56434 18.567588,1.51346 44.529822,-2.41107 44.816272,-10.49357 0.15245,-7.31882 -4.01785,-7.48655 -4.01785,-7.48655 0,0 9.14077,-0.56532 9.44575,-8.4939 0.15212,-7.77626 -8.85445,-8.88809 -8.85445,-8.88809 0,0 11.53636,-0.3275 11.53636,-8.34902 0,-8.20546 -8.87381,-8.85175 -11.6189,-8.99092 1.70324,-0.10363 8.42562,-2.42967 8.3861,-10.53741 -0.0399,-8.37053 -12.87508,-7.67987 -35.956784,-9.20144 2.66326,-15.52873 4.52035,-48.76031 -10.87768,-49.26032 z m -63.387663,53.22425 c -2.167221,0 -3.911718,1.45375 -3.911718,3.25977 v 53.07599 c 0,1.80609 1.744497,3.25983 3.911718,3.25983 h 14.602385 c 2.167228,0 3.911725,-1.45374 3.911725,-3.25983 v -53.07599 c 0,-1.80602 -1.744497,-3.25977 -3.911725,-3.25977 z" d="m 72.35681,178.12175 c -0.545876,-0.0192 -1.121779,0.0192 -1.729242,0.12259 -12.959134,2.14891 4.226681,21.04642 -10.867262,31.83193 -12.617074,10.52505 -17.206737,7.22264 -17.332488,10.67172 v 33.88171 c 1.891308,5.00813 14.483922,10.12847 27.868234,11.21944 13.384309,1.09096 32.098988,-1.73801 32.305468,-7.56421 0.10989,-5.27572 -2.896223,-5.39662 -2.896223,-5.39662 0,0 6.589043,-0.40752 6.808883,-6.12278 0.10966,-5.60545 -6.38265,-6.40691 -6.38265,-6.40691 0,0 8.31589,-0.23607 8.31589,-6.01832 0,-5.91485 -6.39662,-6.38072 -8.37539,-6.48104 1.22776,-0.0747 6.07353,-1.75141 6.04505,-7.59582 -0.0287,-6.03384 -9.280896,-5.53597 -25.919173,-6.63279 1.919791,-11.19377 3.258461,-35.14851 -7.841097,-35.50894 z m -45.69253,38.36631 c -1.562226,0 -2.819733,1.04793 -2.819733,2.34979 v 38.25943 c 0,1.30191 1.257507,2.34983 2.819733,2.34983 h 10.526021 c 1.562231,0 2.819739,-1.04792 2.819739,-2.34983 v -38.25943 c 0,-1.30186 -1.257508,-2.34979 -2.819739,-2.34979 z"
id="rect1278" id="rect1278"
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
</g> </g>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -9,6 +9,7 @@ function sortChange(sortValue) {
} }
function isWatched(youtube_id) { function isWatched(youtube_id) {
// sendVideoProgress(youtube_id, 0); // Reset video progress on watched;
var payload = JSON.stringify({'watched': youtube_id}); var payload = JSON.stringify({'watched': youtube_id});
sendPost(payload); sendPost(payload);
var seenIcon = document.createElement('img'); var seenIcon = document.createElement('img');
@ -33,6 +34,7 @@ function isWatchedButton(button) {
} }
function isUnwatched(youtube_id) { function isUnwatched(youtube_id) {
// sendVideoProgress(youtube_id, 0); // Reset video progress on unwatched;
var payload = JSON.stringify({'un_watched': youtube_id}); var payload = JSON.stringify({'un_watched': youtube_id});
sendPost(payload); sendPost(payload);
var unseenIcon = document.createElement('img'); var unseenIcon = document.createElement('img');
@ -188,7 +190,7 @@ function reEmbed() {
function dbBackup() { function dbBackup() {
var payload = JSON.stringify({'db-backup': true}); var payload = JSON.stringify({'db-backup': true});
sendPost(payload) sendPost(payload);
// clear button // clear button
var message = document.createElement('p'); var message = document.createElement('p');
message.innerText = 'backing up archive'; message.innerText = 'backing up archive';
@ -286,161 +288,266 @@ function cancelDelete() {
// player // player
function createPlayer(button) { function createPlayer(button) {
var mediaUrl = button.getAttribute('data-src'); var videoId = button.getAttribute('data-id');
var mediaThumb = button.getAttribute('data-thumb'); var videoData = getVideoData(videoId);
var mediaTitle = button.getAttribute('data-title'); var videoUrl = videoData.media_url;
var mediaChannel = button.getAttribute('data-channel'); var videoThumbUrl = videoData.vid_thumb_url;
var mediaChannelId = button.getAttribute('data-channel-id'); var videoName = videoData.title;
var dataId = button.getAttribute('data-id');
// get watched status var playlist = '';
var playedStatus = document.createDocumentFragment(); var videoPlaylists = videoData.playlist; // Array of playlists the video is in
playedStatus.appendChild(document.getElementById(dataId)); if (typeof(videoPlaylists) != 'undefined') {
// create player var subbedPlaylists = getSubbedPlaylists(videoPlaylists); // Array of playlist the video is in that are subscribed
removePlayer(); if (subbedPlaylists.length != 0) {
var playerElement = document.createElement('div'); var playlistData = getPlaylistData(subbedPlaylists[0]); // Playlist data for first subscribed playlist
playerElement.classList.add("video-player"); var playlistId = playlistData.playlist_id;
// var playerElement = document.getElementById('player'); var playlistName = playlistData.playlist_name;
playerElement.setAttribute('data-id', dataId); var playlist = `<h5><a href="/playlist/${playlistId}/">${playlistName}</a></h5>`;
// playerElement.innerHTML = '';
var videoPlayer = document.createElement('video');
videoPlayer.setAttribute('src', mediaUrl);
videoPlayer.setAttribute('controls', true);
videoPlayer.setAttribute('autoplay', true);
videoPlayer.setAttribute('width', '100%');
videoPlayer.setAttribute('playsinline', true);
videoPlayer.setAttribute('poster', mediaThumb);
videoPlayer.setAttribute('id', 'video-item'); // Set ID to get URL for casting
playerElement.appendChild(videoPlayer);
// title bar
var titleBar = document.createElement('div');
titleBar.classList.add('player-title', 'boxed-content');
// close
var closeButton = document.createElement('img');
closeButton.className = 'close-button';
closeButton.setAttribute('src', "/static/img/icon-close.svg");
closeButton.setAttribute('alt', 'close-icon');
closeButton.setAttribute('data', dataId);
closeButton.setAttribute('onclick', "removePlayer()");
closeButton.setAttribute('title', 'Close player');
titleBar.appendChild(closeButton);
// played
titleBar.appendChild(playedStatus);
// channel title
var channelTitleLink = document.createElement('a');
channelTitleLink.setAttribute('href', '/channel/' + mediaChannelId + '/');
var channelTitle = document.createElement('h3');
channelTitle.innerText = mediaChannel;
channelTitleLink.appendChild(channelTitle);
titleBar.appendChild(channelTitleLink);
// video title
var videoTitleLink = document.createElement('a');
videoTitleLink.setAttribute('href', '/video/' + dataId + '/');
var videoTitle = document.createElement('h2');
videoTitle.setAttribute('id', "video-title"); // Set ID to get title for casting
videoTitle.innerText = mediaTitle;
var castScript = document.getElementById('cast-script'); // Get cast-script
if(typeof(castScript) != 'undefined' && castScript != null) { // Check if cast integration is enabled
var castButton = document.createElement("google-cast-launcher"); // Create cast button
castButton.setAttribute('id', "castbutton"); // Set ID to apply theme
titleBar.appendChild(castButton); // Add cast button to title
} }
videoTitleLink.appendChild(videoTitle); }
titleBar.appendChild(videoTitleLink);
// add titlebar var videoProgress = videoData.player.progress; // Groundwork for saving video position, change once progress variable is added to API
playerElement.appendChild(titleBar); var videoViews = formatNumbers(videoData.stats.view_count);
// add whole
document.getElementById("player").appendChild(playerElement); var channelId = videoData.channel.channel_id;
var channelName = videoData.channel.channel_name;
removePlayer();
document.getElementById(videoId).outerHTML = ''; // Remove watch indicator from video info
// If cast integration is enabled create cast button
var castButton = ``;
var castScript = document.getElementById('cast-script');
if (typeof(castScript) != 'undefined' && castScript != null) {
var castButton = `<google-cast-launcher id="castbutton"></google-cast-launcher>`;
}
// Watched indicator
if (videoData.player.is_watched) {
var playerState = "seen";
var watchedFunction = "Unwatched";
} else {
var playerState = "unseen";
var watchedFunction = "Watched";
}
var playerStats = `<div class="thumb-icon player-stats"><img src="/static/img/icon-eye.svg" alt="views icon"><span>${videoViews}</span>`;
if (videoData.stats.like_count) {
var likes = formatNumbers(videoData.stats.like_count);
playerStats += `<span>|</span><img src="/static/img/icon-thumb.svg" alt="thumbs-up"><span>${likes}</span>`;
}
if (videoData.stats.dislike_count) {
var dislikes = formatNumbers(videoData.stats.dislike_count);
playerStats += `<span>|</span><img class="dislike" src="/static/img/icon-thumb.svg" alt="thumbs-down"><span>${dislikes}</span>`;
}
playerStats += "</div>";
const markup = `
<div class="video-player" data-id="${videoId}">
<video src="${videoUrl}#t=${videoProgress}" poster="${videoThumbUrl}" ontimeupdate="onVideoProgress('${videoId}')" controls autoplay type='video/mp4' width="100%" playsinline id="video-item"></video>
<div class="player-title boxed-content">
<img class="close-button" src="/static/img/icon-close.svg" alt="close-icon" data="${videoId}" onclick="removePlayer()" title="Close player">
<img src="/static/img/icon-${playerState}.svg" alt="${playerState}-icon" id="${videoId}" onclick="is${watchedFunction}(this.id)" class="${playerState}-icon" title="Mark as ${watchedFunction}">
${castButton}
${playerStats}
<div class="player-channel-playlist">
<h3><a href="/channel/${channelId}/">${channelName}</a></h3>
${playlist}
</div>
<a href="/video/${videoId}/"><h2 id="video-title">${videoName}</h2></a>
</div>
</div>
`;
const divPlayer = document.getElementById("player");
divPlayer.innerHTML = markup;
}
// Set video progress in seconds
function setVideoProgress(videoProgress) {
if (isNaN(videoProgress)) {
videoProgress = 0;
}
var videoElement = document.getElementById("video-item");
videoElement.currentTime = videoProgress;
}
// Runs on video playback, marks video as watched if video gets to 90% or higher, WIP sends position to api
function onVideoProgress(videoId) {
var videoElement = document.getElementById("video-item");
if (videoElement != null) {
if ((videoElement.currentTime % 10).toFixed(1) <= 0.2) { // Check progress every 10 seconds or else progress is checked a few times a second
// sendVideoProgress(videoId, videoElement.currentTime); // Groundwork for saving video position
if ((videoElement.currentTime / videoElement.duration) >= 0.90) {
isWatched(videoId);
}
}
}
}
// Groundwork for saving video position
function sendVideoProgress(videoId, videoProgress) {
var apiEndpoint = "/api/video/";
if (isNaN(videoProgress)) {
videoProgress = 0;
}
var data = {
"data": [{
"youtube_id": videoId,
"player": {
"progress": videoProgress
}
}]
};
videoData = apiRequest(apiEndpoint, "POST", data);
}
// Format numbers for frontend
function formatNumbers(number) {
var numberUnformatted = parseFloat(number);
if (numberUnformatted > 999999999) {
var numberFormatted = (numberUnformatted / 1000000000).toFixed(1).toString() + "B";
} else if (numberUnformatted > 999999) {
var numberFormatted = (numberUnformatted / 1000000).toFixed(1).toString() + "M";
} else if (numberUnformatted > 999) {
var numberFormatted = (numberUnformatted / 1000).toFixed(1).toString() + "K";
} else {
var numberFormatted = numberUnformatted;
}
return numberFormatted;
}
// Gets video data in JSON format when passed video ID
function getVideoData(videoId) {
var apiEndpoint = "/api/video/" + videoId + "/";
videoData = apiRequest(apiEndpoint, "GET");
return videoData.data;
}
// Gets channel data in JSON format when passed channel ID
function getChannelData(channelId) {
var apiEndpoint = "/api/channel/" + channelId + "/";
channelData = apiRequest(apiEndpoint, "GET");
return channelData.data;
}
// Gets playlist data in JSON format when passed playlist ID
function getPlaylistData(playlistId) {
var apiEndpoint = "/api/playlist/" + playlistId + "/";
playlistData = apiRequest(apiEndpoint, "GET");
return playlistData.data;
}
// Given an array of playlist ids it returns an array of subbed playlist ids from that list
function getSubbedPlaylists(videoPlaylists) {
var subbedPlaylists = [];
for (var i = 0; i < videoPlaylists.length; i++) {
if(getPlaylistData(videoPlaylists[i]).playlist_subscribed) {
subbedPlaylists.push(videoPlaylists[i]);
}
}
return subbedPlaylists;
}
// Makes api requests when passed an endpoint and method ("GET" or "POST")
function apiRequest(apiEndpoint, method, data) {
const xhttp = new XMLHttpRequest();
var sessionToken = getCookie("sessionid");
xhttp.open(method, apiEndpoint, false);
xhttp.setRequestHeader("Authorization", "Token " + sessionToken);
xhttp.setRequestHeader("Content-Type", "application/json");
xhttp.send(JSON.stringify(data));
return JSON.parse(xhttp.responseText);
} }
function removePlayer() { function removePlayer() {
var playerElement = document.getElementById('player'); var playerElement = document.getElementById('player');
if (playerElement.hasChildNodes()) { if (playerElement.hasChildNodes()) {
var youtubeId = playerElement.childNodes[0].getAttribute("data-id"); var youtubeId = playerElement.childNodes[1].getAttribute("data-id");
var playedStatus = document.createDocumentFragment(); var playedStatus = document.createDocumentFragment();
var playedBox = document.getElementById(youtubeId); var playedBox = document.getElementById(youtubeId);
if (playedBox) { if (playedBox) {
playedStatus.appendChild(playedBox); playedStatus.appendChild(playedBox);
}; }
playerElement.innerHTML = ''; playerElement.innerHTML = '';
// append played status // append played status
var videoInfo = document.getElementById('video-info-' + youtubeId); var videoInfo = document.getElementById('video-info-' + youtubeId);
videoInfo.insertBefore(playedStatus, videoInfo.firstChild); videoInfo.insertBefore(playedStatus, videoInfo.firstChild);
}; }
} }
// multi search form // multi search form
function searchMulti(query) { function searchMulti(query) {
if (query.length > 1) { if (query.length > 1) {
var payload = JSON.stringify({'multi_search': query}) var payload = JSON.stringify({'multi_search': query});
var http = new XMLHttpRequest(); var http = new XMLHttpRequest();
http.onreadystatechange = function() { http.onreadystatechange = function() {
if (http.readyState === 4) { if (http.readyState === 4) {
allResults = JSON.parse(http.response)['results']; allResults = JSON.parse(http.response).results;
populateMultiSearchResults(allResults); populateMultiSearchResults(allResults);
}; }
}; };
http.open("POST", "/process/", true); http.open("POST", "/process/", true);
http.setRequestHeader("X-CSRFToken", getCookie("csrftoken")); http.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
http.setRequestHeader("Content-type", "application/json"); http.setRequestHeader("Content-type", "application/json");
http.send(payload); http.send(payload);
}; }
} }
function getViewDefaults(view) { function getViewDefaults(view) {
var defaultView = document.getElementById("id_" + view).value; var defaultView = document.getElementById("id_" + view).value;
return defaultView return defaultView;
} }
function populateMultiSearchResults(allResults) { function populateMultiSearchResults(allResults) {
// videos // videos
var defaultVideo = getViewDefaults("home"); var defaultVideo = getViewDefaults("home");
var allVideos = allResults["video_results"]; var allVideos = allResults.video_results;
var videoBox = document.getElementById("video-results"); var videoBox = document.getElementById("video-results");
videoBox.innerHTML = ""; videoBox.innerHTML = "";
for (let index = 0; index < allVideos.length; index++) { for (let index = 0; index < allVideos.length; index++) {
const video = allVideos[index]["source"]; const video = allVideos[index].source;
const videoDiv = createVideo(video, defaultVideo); const videoDiv = createVideo(video, defaultVideo);
videoBox.appendChild(videoDiv); videoBox.appendChild(videoDiv);
}; }
// channels // channels
var defaultChannel = getViewDefaults("channel"); var defaultChannel = getViewDefaults("channel");
var allChannels = allResults["channel_results"]; var allChannels = allResults.channel_results;
var channelBox = document.getElementById("channel-results"); var channelBox = document.getElementById("channel-results");
channelBox.innerHTML = ""; channelBox.innerHTML = "";
for (let index = 0; index < allChannels.length; index++) { for (let index = 0; index < allChannels.length; index++) {
const channel = allChannels[index]["source"]; const channel = allChannels[index].source;
const channelDiv = createChannel(channel, defaultChannel); const channelDiv = createChannel(channel, defaultChannel);
channelBox.appendChild(channelDiv); channelBox.appendChild(channelDiv);
}; }
// playlists // playlists
var defaultPlaylist = getViewDefaults("playlist"); var defaultPlaylist = getViewDefaults("playlist");
var allPlaylists = allResults["playlist_results"]; var allPlaylists = allResults.playlist_results;
var playlistBox = document.getElementById("playlist-results"); var playlistBox = document.getElementById("playlist-results");
playlistBox.innerHTML = ""; playlistBox.innerHTML = "";
for (let index = 0; index < allPlaylists.length; index++) { for (let index = 0; index < allPlaylists.length; index++) {
const playlist = allPlaylists[index]["source"]; const playlist = allPlaylists[index].source;
const playlistDiv = createPlaylist(playlist, defaultPlaylist); const playlistDiv = createPlaylist(playlist, defaultPlaylist);
playlistBox.appendChild(playlistDiv); playlistBox.appendChild(playlistDiv);
}; }
} }
function createVideo(video, viewStyle) { function createVideo(video, viewStyle) {
// create video item div from template // create video item div from template
const videoId = video["youtube_id"]; const videoId = video.youtube_id;
const mediaUrl = video["media_url"]; const mediaUrl = video.media_url;
const thumbUrl = "/cache/" + video["vid_thumb_url"]; const thumbUrl = "/cache/" + video.vid_thumb_url;
const videoTitle = video["title"]; const videoTitle = video.title;
const videoPublished = video["published"]; const videoPublished = video.published;
const videoDuration = video["player"]["duration_str"]; const videoDuration = video.player.duration_str;
if (video["player"]["watched"]) { if (video.player.watched) {
var playerState = "seen"; var playerState = "seen";
} else { } else {
var playerState = "unseen"; var playerState = "unseen";
}; };
const channelId = video["channel"]["channel_id"]; const channelId = video.channel.channel_id;
const channelName = video["channel"]["channel_name"]; const channelName = video.channel.channel_name;
// build markup // build markup
const markup = ` const markup = `
<a href="#player" data-src="/media/${mediaUrl}" data-thumb="${thumbUrl}" data-title="${videoTitle}" data-channel="${channelName}" data-channel-id="${channelId}" data-id="${videoId}" onclick="createPlayer(this)"> <a href="#player" data-src="/media/${mediaUrl}" data-thumb="${thumbUrl}" data-title="${videoTitle}" data-channel="${channelName}" data-channel-id="${channelId}" data-id="${videoId}" onclick="createPlayer(this)">
@ -463,25 +570,25 @@ function createVideo(video, viewStyle) {
<a class="video-more" href="/video/${videoId}/"><h2>${videoTitle}</h2></a> <a class="video-more" href="/video/${videoId}/"><h2>${videoTitle}</h2></a>
</div> </div>
</div> </div>
` `;
const videoDiv = document.createElement("div"); const videoDiv = document.createElement("div");
videoDiv.setAttribute("class", "video-item " + viewStyle); videoDiv.setAttribute("class", "video-item " + viewStyle);
videoDiv.innerHTML = markup videoDiv.innerHTML = markup;
return videoDiv return videoDiv;
} }
function createChannel(channel, viewStyle) { function createChannel(channel, viewStyle) {
// create channel item div from template // create channel item div from template
const channelId = channel["channel_id"]; const channelId = channel.channel_id;
const channelName = channel["channel_name"]; const channelName = channel.channel_name;
const channelSubs = channel["channel_subs"]; const channelSubs = channel.channel_subs;
const channelLastRefresh = channel["channel_last_refresh"]; const channelLastRefresh = channel.channel_last_refresh;
if (channel["channel_subscribed"]) { if (channel.channel_subscribed) {
var button = `<button class="unsubscribe" type="button" id="${channelId}" onclick="unsubscribe(this.id)" title="Unsubscribe from ${channelName}">Unsubscribe</button>` var button = `<button class="unsubscribe" type="button" id="${channelId}" onclick="unsubscribe(this.id)" title="Unsubscribe from ${channelName}">Unsubscribe</button>`;
} else { } else {
var button = `<button type="button" id="${channelId}" onclick="subscribe(this.id)" title="Subscribe to ${channelName}">Subscribe</button>` var button = `<button type="button" id="${channelId}" onclick="subscribe(this.id)" title="Subscribe to ${channelName}">Subscribe</button>`;
}; }
// build markup // build markup
const markup = ` const markup = `
<div class="channel-banner ${viewStyle}"> <div class="channel-banner ${viewStyle}">
@ -508,25 +615,25 @@ function createChannel(channel, viewStyle) {
</div> </div>
</div> </div>
</div> </div>
` `;
const channelDiv = document.createElement("div"); const channelDiv = document.createElement("div");
channelDiv.setAttribute("class", "channel-item " + viewStyle); channelDiv.setAttribute("class", "channel-item " + viewStyle);
channelDiv.innerHTML = markup; channelDiv.innerHTML = markup;
return channelDiv return channelDiv;
} }
function createPlaylist(playlist, viewStyle) { function createPlaylist(playlist, viewStyle) {
// create playlist item div from template // create playlist item div from template
const playlistId = playlist["playlist_id"]; const playlistId = playlist.playlist_id;
const playlistName = playlist["playlist_name"]; const playlistName = playlist.playlist_name;
const playlistChannelId = playlist["playlist_channel_id"]; const playlistChannelId = playlist.playlist_channel_id;
const playlistChannel = playlist["playlist_channel"]; const playlistChannel = playlist.playlist_channel;
const playlistLastRefresh = playlist["playlist_last_refresh"]; const playlistLastRefresh = playlist.playlist_last_refresh;
if (playlist["playlist_subscribed"]) { if (playlist.playlist_subscribed) {
var button = `<button class="unsubscribe" type="button" id="${playlistId}" onclick="unsubscribe(this.id)" title="Unsubscribe from ${playlistName}">Unsubscribe</button>` var button = `<button class="unsubscribe" type="button" id="${playlistId}" onclick="unsubscribe(this.id)" title="Unsubscribe from ${playlistName}">Unsubscribe</button>`;
} else { } else {
var button = `<button type="button" id="${playlistId}" onclick="subscribe(this.id)" title="Subscribe to ${playlistName}">Subscribe</button>` var button = `<button type="button" id="${playlistId}" onclick="subscribe(this.id)" title="Subscribe to ${playlistName}">Subscribe</button>`;
}; }
const markup = ` const markup = `
<div class="playlist-thumbnail"> <div class="playlist-thumbnail">
<a href="/playlist/${playlistId}/"> <a href="/playlist/${playlistId}/">
@ -539,11 +646,11 @@ function createPlaylist(playlist, viewStyle) {
<p>Last refreshed: ${playlistLastRefresh}</p> <p>Last refreshed: ${playlistLastRefresh}</p>
${button} ${button}
</div> </div>
` `;
const playlistDiv = document.createElement("div"); const playlistDiv = document.createElement("div");
playlistDiv.setAttribute("class", "playlist-item " + viewStyle); playlistDiv.setAttribute("class", "playlist-item " + viewStyle);
playlistDiv.innerHTML = markup; playlistDiv.innerHTML = markup;
return playlistDiv return playlistDiv;
} }
@ -565,8 +672,8 @@ function getCookie(c_name) {
c_end = document.cookie.indexOf(";", c_start); c_end = document.cookie.indexOf(";", c_start);
if (c_end == -1) c_end = document.cookie.length; if (c_end == -1) c_end = document.cookie.length;
return unescape(document.cookie.substring(c_start,c_end)); return unescape(document.cookie.substring(c_start,c_end));
}; }
}; }
return ""; return "";
} }
@ -582,17 +689,17 @@ function textReveal() {
} else { } else {
textBox.style.height = 'unset'; textBox.style.height = 'unset';
button.innerText = 'Hide'; button.innerText = 'Hide';
}; }
} }
function showForm() { function showForm() {
var formElement = document.getElementById('hidden-form'); var formElement = document.getElementById('hidden-form');
var displayStyle = formElement.style.display var displayStyle = formElement.style.display;
if (displayStyle === "") { if (displayStyle === "") {
formElement.style.display = 'block'; formElement.style.display = 'block';
} else { } else {
formElement.style.display = ""; formElement.style.display = "";
}; }
animate('animate-icon', 'pulse-img'); animate('animate-icon', 'pulse-img');
} }
@ -602,5 +709,5 @@ function animate(elementId, animationClass) {
toAnimate.className = animationClass; toAnimate.className = animationClass;
} else { } else {
toAnimate.classList.remove(animationClass); toAnimate.classList.remove(animationClass);
}; }
} }