mirror of https://github.com/ekimekim/wubloader
Merge pull request #75 from ekimekim/gunner/thrimbletrimmer-integration
Gunner/thrimbletrimmer integrationmike/render-dashboard
commit
54f6a707f5
@ -1,4 +1,5 @@
|
||||
# nginx container contains config that exposes all the various services metrics
|
||||
FROM nginx:latest
|
||||
ADD nginx/generate-config /
|
||||
COPY thrimbletrimmer /etc/nginx/html/thrimbletrimmer
|
||||
ENTRYPOINT ["/bin/sh", "-c", "/generate-config && nginx -g \"daemon off;\""]
|
||||
|
@ -0,0 +1,142 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Thrimbletrimmer Goes Forth</title>
|
||||
<meta name="description" content="">
|
||||
|
||||
<meta name="google-signin-client_id" content="345276493482-r84m2giavk10glnmqna0lbq8e1hdaus0.apps.googleusercontent.com">
|
||||
<script src="https://apis.google.com/js/platform.js?onload=onGLoad" async defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- https://developers.google.com/identity/sign-in/web/sign-in -->
|
||||
<div class="g-signin2" data-onsuccess="onSignIn"></div>
|
||||
<a href="#" onclick="signOut();">Sign out</a>
|
||||
|
||||
<script>
|
||||
var user;
|
||||
function onSignIn(googleUser) {
|
||||
user = googleUser;
|
||||
var profile = googleUser.getBasicProfile();
|
||||
console.log('ID: ' + profile.getId()); // Do not send to your backend! Use an ID token instead.
|
||||
console.log('Name: ' + profile.getName());
|
||||
console.log('Image URL: ' + profile.getImageUrl());
|
||||
console.log('Email: ' + profile.getEmail()); // This is null if the 'email' scope is not present.
|
||||
console.log('ID Token: ' + googleUser.getAuthResponse().id_token);
|
||||
}
|
||||
function signOut() {
|
||||
user = null;
|
||||
var auth2 = gapi.auth2.getAuthInstance();
|
||||
auth2.signOut().then(function () {
|
||||
console.log('User signed out.');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<div>
|
||||
<br/>
|
||||
<input id="server" value="" />
|
||||
<input id="rowId" value="04860aa4-d6a5-11e9-9d36-2a2ae2dbcce4" />
|
||||
<input type="button" onclick="getRow()" value="Get Row"/>
|
||||
<a href="/thrimshim">All Rows</a>
|
||||
<br/>
|
||||
<textarea id="rowdump"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<br/>
|
||||
<input type="button" onclick="testAuth()" value="Test Auth" /><br />
|
||||
<input type="button" onclick="testSubmit()" value="Test Submit" /><br />
|
||||
<input type="button" onclick="testManualLink()" value="Test Manual Link" /><input id="manualLink" value="google.ca"/><br />
|
||||
<input type="button" onclick="testReset()" value="Test Reset" /><br />
|
||||
</div>
|
||||
<script>
|
||||
function getRow() {
|
||||
fetch(document.getElementById("server").value+"/thrimshim/"+document.getElementById("rowId").value).then(function (response) {
|
||||
response.text().then(function(data) {
|
||||
document.getElementById("rowdump").value=data;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testAuth() {
|
||||
fetch(document.getElementById("server").value+"/thrimshim/auth-test", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
test: "123 Hello World?",
|
||||
token: user.getAuthResponse().id_token
|
||||
})
|
||||
}).then(function(response) {
|
||||
if (response.status !== 200) {
|
||||
console.log('Looks like there was a problem. Status Code: ' + response.status);
|
||||
return;
|
||||
}
|
||||
|
||||
// Examine the text in the response
|
||||
response.json().then(function(data) {
|
||||
console.log(data);
|
||||
});
|
||||
}).catch(function(err) {
|
||||
console.log('Fetch Error :-S', err);
|
||||
});
|
||||
}
|
||||
|
||||
function testSubmit() {
|
||||
var wubData = {
|
||||
allow_holes: "true",
|
||||
experimental: "true",
|
||||
state: "EDITED",
|
||||
token: user.getAuthResponse().id_token,
|
||||
upload_location: "YouTube",
|
||||
uploader_whitelist: null,
|
||||
video_channel: "rpglimitbreak",
|
||||
video_description: "Test Description",
|
||||
video_end: "2019-09-14T03:54:28.546",
|
||||
video_quality: "source",
|
||||
video_start: "2019-09-14T03:48:06.546",
|
||||
video_title: "Test"
|
||||
}
|
||||
|
||||
//Submit to thrimshim
|
||||
fetch(document.getElementById("server").value+"/thrimshim/"+document.getElementById("rowId").value, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(wubData)
|
||||
}).then(data => console.log(data));
|
||||
|
||||
}
|
||||
|
||||
function testManualLink() {
|
||||
fetch(document.getElementById("server").value+"/thrimshim/manual-link/"+document.getElementById("rowId").value, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
link: document.getElementById("manualLink").value,
|
||||
token: user.getAuthResponse().id_token
|
||||
})
|
||||
}).then(data => console.log(data));
|
||||
}
|
||||
|
||||
function testReset() {
|
||||
fetch(document.getElementById("server").value+"/thrimshim/reset/"+document.getElementById("rowId").value, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({token: user.getAuthResponse().id_token})
|
||||
}).then(data => console.log(data));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,247 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Thrimbletrimmer Goes Forth</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<!-- <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
|
||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script> -->
|
||||
<style>
|
||||
html, body {
|
||||
height:100%;
|
||||
margin:0px;
|
||||
background-color:darkgrey;
|
||||
}
|
||||
|
||||
.sectionContainer {
|
||||
margin:24px auto;
|
||||
padding:32px;
|
||||
border-radius:4px;
|
||||
background:white;
|
||||
box-shadow:0 0 34px rgba(0,0,0,.26);
|
||||
max-width:1280px;
|
||||
}
|
||||
|
||||
#QueueTable {
|
||||
width:100%;
|
||||
border-collapse: collapse;
|
||||
text-align: center;
|
||||
}
|
||||
#QueueTable, #QueueTable th, #QueueTable td {
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
#QueueTable tr:nth-child(even) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
#QueueTable tr:hover {
|
||||
background-color:#dddddd;
|
||||
}
|
||||
#QueueTable th {
|
||||
line-height:32px;
|
||||
}
|
||||
|
||||
.filterMenu {
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
background-color: rgb(221, 221, 221);
|
||||
border-radius: 5px 5px 0px 0px;
|
||||
}
|
||||
.filterList {
|
||||
display:none;
|
||||
List-style-type:none;
|
||||
margin:10px 0px 0px -10px;
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
position:absolute;
|
||||
background-color:white;
|
||||
min-width:100px;
|
||||
}
|
||||
.filterMenu:hover .filterList {
|
||||
display:inline-block;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
/* Styles based on event category */
|
||||
#QueueTable .RDP {
|
||||
background-color: #f4cccc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<!-- <div class="sectionContainer" style="display:none;">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Stream</th>
|
||||
<th>Start Time</th>
|
||||
<th>End Time</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input id="StreamName" value="gamesdonequick" /></td>
|
||||
<td><input id="StreamStart" value="2019-01-06T16:00:00" /></td>
|
||||
<td><input id="StreamEnd" value="2019-01-06T17:00:00" /></td>
|
||||
<td><input type="button" value="Load Playlist" onclick="loadPlaylist()" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="javascript:alert('seabats, lunarjade, gamesdonequick');">Streams</a></td>
|
||||
<td><a href="javascript:window.open('/files/' + document.getElementById('StreamName').value + '/source', '_blank');">Hours</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div> -->
|
||||
|
||||
<div class="sectionContainer">
|
||||
<h1 style="color:#1976d2;font-size:34px;line-height:38px;">Wubloader Queue</h1>
|
||||
<!-- Add in filters based on time period, category, and state -->
|
||||
<!-- Throw in JQueryUI to run the filter menus, not worth re-inventing the wheel -->
|
||||
|
||||
<style>
|
||||
.ui-menu { width: 150px; }
|
||||
</style>
|
||||
<div id="FiltersMenu">
|
||||
<div class="filterMenu" style="padding-bottom:7px;">
|
||||
<input id="DateFilterStart" placeholder="Start Date" />
|
||||
</div>
|
||||
<div class="filterMenu" style="padding-bottom:7px;">
|
||||
<input id="DateFilterEnd" placeholder="End Date" />
|
||||
</div>
|
||||
<div id="CategoryFiltersMenu" class="filterMenu">
|
||||
<div>Category Filter</div>
|
||||
<ul id="CategoryFilter" class="filterList"></ul>
|
||||
</div>
|
||||
<div id="StateFiltersMenu" class="filterMenu">
|
||||
<div>State Filter</div>
|
||||
<ul id="StateFilter" class="filterList"></ul>
|
||||
</div>
|
||||
<input type="button" value="Apply Filters" onclick="applyFilters()"/>
|
||||
<input type="button" value="Reset Filters" onclick="window.location.href = '/thrimbletrimmer/dashboard.html'"/>
|
||||
</div>
|
||||
<table id="QueueTable">
|
||||
<tr>
|
||||
<th>Start Time</th>
|
||||
<th>End Time</th>
|
||||
<th>Category</th>
|
||||
<th>Description</th>
|
||||
<th>State</th>
|
||||
<th>Edit</th>
|
||||
<th>Link</th>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// var startOfHour = new Date(new Date().setMinutes(0,0,0));
|
||||
// document.getElementById("StreamStart").value = new Date(startOfHour.getTime() - 1000*60*60).toISOString().substring(0,19);
|
||||
// document.getElementById("StreamEnd").value = startOfHour.toISOString().substring(0,19);
|
||||
function sanitizeInput(input) {
|
||||
let tempElement = document.createElement("SPAN");
|
||||
tempElement.appendChild(document.createTextNode(input));
|
||||
return tempElement.innerHTML;
|
||||
}
|
||||
|
||||
async function getEvents() {
|
||||
let response = await fetch("/thrimshim");
|
||||
let data = await response.json();
|
||||
//Sanitize user inputs that could be used for xss
|
||||
data.forEach(event => {
|
||||
event.category = sanitizeInput(event.category);
|
||||
event.description = sanitizeInput(event.description);
|
||||
})
|
||||
return data;
|
||||
}
|
||||
|
||||
function populateFilters(events) {
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
//Set up Date filters
|
||||
if (urlParams.has("start")) {
|
||||
document.getElementById("DateFilterStart").value = urlParams.get("start");
|
||||
}
|
||||
if (urlParams.has("end")) {
|
||||
document.getElementById("DateFilterEnd").value = urlParams.get("end");
|
||||
}
|
||||
|
||||
//Set up Category filters
|
||||
let categoryFilters = urlParams.has("category") ? urlParams.get("category").split(","):null;
|
||||
new Set(events.map(event => event.category).sort()).forEach((category, index) => {
|
||||
let row = document.createElement("TR");
|
||||
row.innerHTML = `<li><input type="checkbox" name="categoryCheckbox-${index}" filtervalue="${category}" ${!categoryFilters || categoryFilters.indexOf(category) >=0 ? "checked":""}><label for="categoryCheckbox-${index}">${category}</label></li>`;
|
||||
document.getElementById('CategoryFilter').appendChild(row);
|
||||
});
|
||||
|
||||
//Set up State filters
|
||||
let stateFilters = urlParams.has("state") ? urlParams.get("state").split(","):null;
|
||||
//new Set(events.map(event => event.state).sort()).forEach((state, index) => {
|
||||
["UNEDITED", "EDITED", "CLAIMED", "FINALIZING", "TRANSCODING", "DONE"].forEach((state, index) => {
|
||||
let row = document.createElement("TR");
|
||||
row.innerHTML = `<li><input type="checkbox" name="stateCheckbox-${index}" filtervalue="${state}" ${!stateFilters || stateFilters.indexOf(state) >=0 ? "checked":""}><label for="stateCheckbox-${index}">${state}</label></li>`;
|
||||
document.getElementById('StateFilter').appendChild(row);
|
||||
});
|
||||
return events;
|
||||
}
|
||||
|
||||
function filterEvents(events) {
|
||||
if(!window.location.search) { return events; }
|
||||
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
if(urlParams.has("start")) {
|
||||
let startDate = new Date(urlParams.get("start"));
|
||||
events = events.filter(event => new Date(event.event_start) >= startDate);
|
||||
}
|
||||
|
||||
if(urlParams.has("end")) {
|
||||
let endDate = new Date(urlParams.get("end"));
|
||||
events = events.filter(event => new Date(event.event_start) <= endDate);
|
||||
}
|
||||
|
||||
if(urlParams.has("category")) {
|
||||
events = events.filter(event => urlParams.get("category").split(",").indexOf(event.category) >= 0);
|
||||
}
|
||||
|
||||
if(urlParams.has("state")) {
|
||||
events = events.filter(event => urlParams.get("state").split(",").indexOf(event.state) >= 0);
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
function populateTable (events) {
|
||||
events.forEach(event => {
|
||||
let row = document.createElement("TR");
|
||||
row.innerHTML = `
|
||||
<td>${event.event_start}</td>
|
||||
<td>${event.event_end}</td>
|
||||
<td class="${event.category.replace(/ /g, "")}">${event.category}</td>
|
||||
<td>${event.description}</td>
|
||||
<td>${event.state}</td>
|
||||
<td><a href="/thrimbletrimmer?id=${event.id}">Edit</a></td>
|
||||
<td>${event.video_link ? "<a href='"+event.video_link+"'>"+event.video_link+"</a>":""}</td>
|
||||
`;
|
||||
document.getElementById('QueueTable').appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
function applyFilters() {
|
||||
let startDateFilter = document.getElementById("DateFilterStart").value;
|
||||
let endDateFilter = document.getElementById("DateFilterEnd").value;
|
||||
let categoryfilters = Array.from(document.querySelectorAll("#CategoryFilter :checked")).map(checkbox => checkbox.getAttribute("filtervalue")).join();
|
||||
let statefilters = Array.from(document.querySelectorAll("#StateFilter :checked")).map(checkbox => checkbox.getAttribute("filtervalue")).join();
|
||||
window.location.href = `/thrimbletrimmer/dashboard.html?${startDateFilter ? "start="+startDateFilter+"&":""}${endDateFilter ? "end="+endDateFilter+"&":""}category=${categoryfilters}&state=${statefilters}`;
|
||||
}
|
||||
|
||||
//On Page Load
|
||||
getEvents().then(populateFilters).then(filterEvents).then(populateTable);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,163 @@
|
||||
|
||||
<!--
|
||||
TODOs:
|
||||
Move Google sign-in/out buttons, and make it clear when you're not signed in.
|
||||
Move "Reset" and "Manual Link" options in here.
|
||||
Clean up the Options/Input header, move parts of it into a hidden "Advanced" menu.
|
||||
Create a cleaner player-only version of the page, or find a way to make it a version of this one via URL switch.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Thrimbletrimmer Goes Forth</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="google-signin-client_id" content="345276493482-r84m2giavk10glnmqna0lbq8e1hdaus0.apps.googleusercontent.com">
|
||||
|
||||
<link href="/thrimbletrimmer/plugins/video.js/dist/video-js.min.css" rel="stylesheet">
|
||||
<link href="/thrimbletrimmer/plugins/videojs-hls-quality-selector/dist/videojs-hls-quality-selector.css" rel="stylesheet">
|
||||
<link href="/thrimbletrimmer/plugins/videojs-trimming-controls/dist/videojs-trimming-controls.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<!-- <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script> -->
|
||||
<script src="/thrimbletrimmer/plugins/video.js/dist/video.min.js"></script>
|
||||
<script src="/thrimbletrimmer/plugins/videojs-contrib-quality-levels/dist/videojs-contrib-quality-levels.min.js"></script>
|
||||
<script src="/thrimbletrimmer/plugins/videojs-hls-quality-selector/dist/videojs-hls-quality-selector.min.js"></script>
|
||||
<script src="/thrimbletrimmer/plugins/videojs-trimming-controls/dist/videojs-trimming-controls.js"></script>
|
||||
|
||||
<script src="https://apis.google.com/js/platform.js?onload=onGLoad" async defer></script>
|
||||
|
||||
<link href="/thrimbletrimmer/styles/style.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div style="max-width:1280px;margin:auto;padding-top:25px;">
|
||||
<table id="wubloaderInputTable">
|
||||
<tr>
|
||||
<th>Stream</th>
|
||||
<th>Start Time</th>
|
||||
<th>End Time</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input id="StreamName" value="desertbus" /></td>
|
||||
<td><input id="StreamStart" style="display:none;" class="UTCTimeInput" value="" /><input id="BusTimeStart" class="BusTimeInput" value="0:00" /></td>
|
||||
<td><input id="StreamEnd" style="display:none;" class="UTCTimeInput" value="" /><input id="BusTimeEnd" class="BusTimeInput" value="1:00" /></td>
|
||||
<td><input type="button" value="Load Playlist" onclick="loadPlaylist()" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>
|
||||
<input type="radio" id="BusTimeToggleUTC" name="BusTimeToggle" value="UTC" onclick="toggleTimeInput(this.value)"> UTC
|
||||
<input type="radio" id="BusTimeToggleBus" name="BusTimeToggle" value="BUSTIME" onclick="toggleTimeInput(this.value)" checked="checked"> Bustime
|
||||
</td>
|
||||
<td><a id="AdvancedOptionsButton" href="JavaScript:toggleHiddenPane('wubloaderAdvancedInputTable');">Advanced Submit Options</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table id="wubloaderAdvancedInputTable" style="display:none;">
|
||||
<tr>
|
||||
<td><a href="javascript:window.open('/files');">Streams</a></td>
|
||||
<td><a href="javascript:window.open('/files/' + document.getElementById('StreamName').value + '/source', '_blank');">Hours</a></td>
|
||||
</tr>
|
||||
<tr><td>Allow Holes: </td><td><input id="AllowHoles" type="checkbox" checked /></td></tr>
|
||||
<tr><td>Quality Level: </td><td><select id="qualityLevel"></select></td></tr>
|
||||
<tr><td>Upload Location: </td><td><select id="uploadLocation"><option value="YouTube" selected>YouTube</option></select></td></tr>
|
||||
<tr><td>Uploader Whitelist: </td><td><input id="uploaderWhitelist" title="Uploader Whitelist" /></td></tr>
|
||||
<tr>
|
||||
<td>ThrimShim ID:</td>
|
||||
<td><input id="hiddenSubmissionID" value="" /></td>
|
||||
<td><input type="button" value="Load Event" onclick="window.location.search = '?id='+document.getElementById('hiddenSubmissionID').value"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div id="EditorContainer">
|
||||
<video id="my-player" class="video-js" controls preload="auto" style="display:none">
|
||||
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<div id="EditorDetailsPane">
|
||||
<div>
|
||||
Title: <br />
|
||||
<input type="text" id="VideoTitle" value="DB2019 - " maxlength="91" />
|
||||
</div>
|
||||
<div>
|
||||
Description:<br/>
|
||||
<textarea id="VideoDescription" ></textarea>
|
||||
</div>
|
||||
<input type="button" id="SubmitButton" value="Submit" onclick="thrimbletrimmerSubmit()"/>
|
||||
<input type="button" id="DownloadButton" value="Download" onclick="thrimbletrimmerDownload()"/>
|
||||
<a href="/thrimbletrimmer/dashboard.html">Go To Dashboard</a> |
|
||||
<a id="ManualLinkButton" href="JavaScript:toggleHiddenPane('ManualLinkPane');">Manual Link</a> |
|
||||
<a id="ResetButton" href="JavaScript:thrimbletrimmerResetLink();">Reset Edits</a>
|
||||
<a id="HelpButton" style="float:right;" href="JavaScript:toggleHiddenPane('HelpPane');">Help</a>
|
||||
<a id="UltrawideButton" style="float:right;margin-right:10px;" href="JavaScript:toggleUltrawide();">Ultrawide</a>
|
||||
</div>
|
||||
<div id="ManualLinkPane" style="display:none">
|
||||
<input id="ManualLink" /> <input type="button" onclick="thrimbletrimmerManualLink()" value="Set Link" />
|
||||
</div>
|
||||
<div id="HelpPane" style="display:none;">
|
||||
<ul>
|
||||
<li>J/K/L - Back 10 seconds, Play/Pause, Advance 10 seconds</li>
|
||||
<li>LeftArrow/RightArrow - Back 5 seconds, Advance 5 seconds</li>
|
||||
<li>,/. - Back 0.1 seconds, Advance 0.1 seconds</li>
|
||||
<li>I/O - Set start of trim at playhead, set end of trim at playhead</li>
|
||||
<li>0-9 - Jump to 0% - 90% through the video.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<iframe id="outputFile" style="display:none;"></iframe>
|
||||
|
||||
<script src="/thrimbletrimmer/scripts/playerSetup.js"></script>
|
||||
<script src="/thrimbletrimmer/scripts/keyboardShortcuts.js"></script>
|
||||
<script src="/thrimbletrimmer/scripts/IO.js"></script>
|
||||
|
||||
<script>pageSetup();</script>
|
||||
<script>
|
||||
function toggleHiddenPane(paneID) {
|
||||
var pane = document.getElementById(paneID);
|
||||
pane.style.display = (pane.style.display === "none") ? "block":"none";
|
||||
}
|
||||
function toggleUltrawide() {
|
||||
var body = document.getElementsByTagName("Body")[0];
|
||||
body.classList.contains("ultrawide") ? body.classList.remove("ultrawide"):body.classList.add("ultrawide");
|
||||
}
|
||||
function toggleTimeInput(toggleInput) {
|
||||
if(toggleInput == "UTC") {
|
||||
document.getElementById("BusTimeStart").style.display = "none";
|
||||
document.getElementById("BusTimeEnd").style.display = "none";
|
||||
document.getElementById("StreamStart").style.display = "";
|
||||
document.getElementById("StreamEnd").style.display = "";
|
||||
} else {
|
||||
document.getElementById("StreamStart").style.display = "none";
|
||||
document.getElementById("StreamEnd").style.display = "none";
|
||||
document.getElementById("BusTimeStart").style.display = "";
|
||||
document.getElementById("BusTimeEnd").style.display = "";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="g-signin2" data-onsuccess="onSignIn"></div>
|
||||
<a href="#" onclick="signOut();">Sign out</a>
|
||||
<script>
|
||||
var user;
|
||||
function onSignIn(googleUser) {
|
||||
user = googleUser;
|
||||
var profile = googleUser.getBasicProfile();
|
||||
console.log('ID: ' + profile.getId()); // Do not send to your backend! Use an ID token instead.
|
||||
console.log('Name: ' + profile.getName());
|
||||
console.log('Image URL: ' + profile.getImageUrl());
|
||||
console.log('Email: ' + profile.getEmail()); // This is null if the 'email' scope is not present.
|
||||
console.log('ID Token: ' + googleUser.getAuthResponse().id_token);
|
||||
}
|
||||
function signOut() {
|
||||
user = null;
|
||||
var auth2 = gapi.auth2.getAuthInstance();
|
||||
auth2.signOut().then(function () {
|
||||
console.log('User signed out.');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,2 @@
|
||||
/*! @name videojs-contrib-quality-levels @version 2.0.9 @license Apache-2.0 */
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("video.js"),require("global/document")):"function"==typeof define&&define.amd?define(["video.js","global/document"],t):e.videojsContribQualityLevels=t(e.videojs,e.document)}(this,function(e,t){"use strict";function n(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}e=e&&e.hasOwnProperty("default")?e.default:e,t=t&&t.hasOwnProperty("default")?t.default:t;var r=function(r){var i,l;function o(){var i,l=n(n(i=r.call(this)||this));if(e.browser.IS_IE8)for(var s in l=t.createElement("custom"),o.prototype)"constructor"!==s&&(l[s]=o.prototype[s]);return l.levels_=[],l.selectedIndex_=-1,Object.defineProperty(l,"selectedIndex",{get:function(){return l.selectedIndex_}}),Object.defineProperty(l,"length",{get:function(){return l.levels_.length}}),l||n(i)}l=r,(i=o).prototype=Object.create(l.prototype),i.prototype.constructor=i,i.__proto__=l;var s=o.prototype;return s.addQualityLevel=function(n){var r=this.getQualityLevelById(n.id);if(r)return r;var i=this.levels_.length;return r=new function n(r){var i=this;if(e.browser.IS_IE8)for(var l in i=t.createElement("custom"),n.prototype)"constructor"!==l&&(i[l]=n.prototype[l]);return i.id=r.id,i.label=i.id,i.width=r.width,i.height=r.height,i.bitrate=r.bandwidth,i.enabled_=r.enabled,Object.defineProperty(i,"enabled",{get:function(){return i.enabled_()},set:function(e){i.enabled_(e)}}),i}(n),""+i in this||Object.defineProperty(this,i,{get:function(){return this.levels_[i]}}),this.levels_.push(r),this.trigger({qualityLevel:r,type:"addqualitylevel"}),r},s.removeQualityLevel=function(e){for(var t=null,n=0,r=this.length;n<r;n++)if(this[n]===e){t=this.levels_.splice(n,1)[0],this.selectedIndex_===n?this.selectedIndex_=-1:this.selectedIndex_>n&&this.selectedIndex_--;break}return t&&this.trigger({qualityLevel:e,type:"removequalitylevel"}),t},s.getQualityLevelById=function(e){for(var t=0,n=this.length;t<n;t++){var r=this[t];if(r.id===e)return r}return null},s.dispose=function(){this.selectedIndex_=-1,this.levels_.length=0},o}(e.EventTarget);for(var i in r.prototype.allowedEvents_={change:"change",addqualitylevel:"addqualitylevel",removequalitylevel:"removequalitylevel"},r.prototype.allowedEvents_)r.prototype["on"+i]=null;var l=function(t){return n=this,e.mergeOptions({},t),i=n.qualityLevels,l=new r,n.on("dispose",function e(){l.dispose(),n.qualityLevels=i,n.off("dispose",e)}),n.qualityLevels=function(){return l},n.qualityLevels.VERSION="2.0.9",l;var n,i,l};return(e.registerPlugin||e.plugin)("qualityLevels",l),l.VERSION="2.0.9",l});
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* videojs-hls-quality-selector
|
||||
* @version 1.0.4
|
||||
* @copyright 2019 Chris Boustead (chris@forgemotion.com)
|
||||
* @license MIT
|
||||
*/
|
||||
.video-js.vjs-hls-quality-selector{display:block}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,456 @@
|
||||
/*! @name videojs-trimming-controls @version 0.0.0 @license MIT */
|
||||
'use strict';
|
||||
|
||||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
||||
|
||||
var videojs = _interopDefault(require('video.js'));
|
||||
|
||||
function _inheritsLoose(subClass, superClass) {
|
||||
subClass.prototype = Object.create(superClass.prototype);
|
||||
subClass.prototype.constructor = subClass;
|
||||
subClass.__proto__ = superClass;
|
||||
}
|
||||
|
||||
var version = "0.0.0";
|
||||
|
||||
var Plugin = videojs.getPlugin('plugin'); // Default options for the plugin.
|
||||
|
||||
var defaults = {
|
||||
startTrim: 60,
|
||||
endTrim: 120,
|
||||
limitPlayback: false
|
||||
};
|
||||
/**
|
||||
* An advanced Video.js plugin. For more information on the API
|
||||
*
|
||||
* See: https://blog.videojs.com/feature-spotlight-advanced-plugins/
|
||||
*/
|
||||
|
||||
var TrimmingControls =
|
||||
/*#__PURE__*/
|
||||
function (_Plugin) {
|
||||
_inheritsLoose(TrimmingControls, _Plugin);
|
||||
|
||||
/**
|
||||
* Create a TrimmingControls plugin instance.
|
||||
*
|
||||
* @param {Player} player
|
||||
* A Video.js Player instance.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* An optional options object.
|
||||
*
|
||||
* While not a core part of the Video.js plugin architecture, a
|
||||
* second argument of options is a convenient way to accept inputs
|
||||
* from your plugin's caller.
|
||||
*/
|
||||
function TrimmingControls(player, options) {
|
||||
var _this;
|
||||
|
||||
// the parent class will add player under this.player
|
||||
_this = _Plugin.call(this, player) || this;
|
||||
_this.options = videojs.mergeOptions(defaults, options);
|
||||
|
||||
_this.createTrimmingControls();
|
||||
|
||||
player.ready(function () {
|
||||
setTimeout(function () {
|
||||
_this.updateTrimTimes(_this.options.startTrim, _this.options.endTrim);
|
||||
}, 100);
|
||||
player.on("timeupdate", function () {
|
||||
if (_this.options.limitPlayback && _this.player.currentTime() >= _this.options.endTrim) {
|
||||
_this.player.currentTime(_this.options.endTrim);
|
||||
|
||||
_this.player.pause();
|
||||
}
|
||||
});
|
||||
player.on('playing', function () {
|
||||
videojs.log('playback began!');
|
||||
|
||||
_this.updateTrimTimes(_this.options.startTrim, _this.options.endTrim);
|
||||
});
|
||||
});
|
||||
return _this;
|
||||
}
|
||||
|
||||
var _proto = TrimmingControls.prototype;
|
||||
|
||||
_proto.createTrimmingControls = function createTrimmingControls() {
|
||||
var player = this.player;
|
||||
var videoJsComponentClass = videojs.getComponent('Component');
|
||||
/**
|
||||
* Extend vjs button class for quality button.
|
||||
*/
|
||||
|
||||
var TrimControlBarClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJsComponentClas) {
|
||||
_inheritsLoose(TrimControlBarClass, _videoJsComponentClas);
|
||||
|
||||
/**
|
||||
* Component constructor.
|
||||
*/
|
||||
function TrimControlBarClass() {
|
||||
return _videoJsComponentClas.call(this, player, {
|
||||
title: player.localize('Trimming Controls')
|
||||
}) || this;
|
||||
}
|
||||
|
||||
var _proto2 = TrimControlBarClass.prototype;
|
||||
|
||||
_proto2.createEl = function createEl() {
|
||||
return videojs.dom.createEl('div', {
|
||||
// Prefixing classes of elements within a player with "vjs-" is a convention used in Video.js.
|
||||
className: 'vjs-control-bar vjs-trimming-controls',
|
||||
dir: 'ltr'
|
||||
});
|
||||
};
|
||||
|
||||
return TrimControlBarClass;
|
||||
}(videoJsComponentClass);
|
||||
|
||||
var videoJSSpacerClass = videojs.getComponent('Spacer');
|
||||
var videoJSButtonClass = videojs.getComponent('Button');
|
||||
|
||||
var GoToButtonClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass) {
|
||||
_inheritsLoose(GoToButtonClass, _videoJSButtonClass);
|
||||
|
||||
function GoToButtonClass(_plugin, _targetPosition, _text) {
|
||||
var _this2;
|
||||
|
||||
_this2 = _videoJSButtonClass.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this2.trimmingControls = _plugin;
|
||||
_this2.targetPosition = _targetPosition;
|
||||
|
||||
_this2.controlText(_text);
|
||||
|
||||
_this2.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this2;
|
||||
}
|
||||
|
||||
var _proto3 = GoToButtonClass.prototype;
|
||||
|
||||
_proto3.handleClick = function handleClick() {
|
||||
if (this.targetPosition == 0) {
|
||||
this.player().currentTime(this.trimmingControls.options.startTrim);
|
||||
} else if (this.targetPosition == 1) {
|
||||
this.player().currentTime(this.trimmingControls.options.endTrim);
|
||||
}
|
||||
};
|
||||
|
||||
return GoToButtonClass;
|
||||
}(videoJSButtonClass);
|
||||
|
||||
var TrimButtonClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass2) {
|
||||
_inheritsLoose(TrimButtonClass, _videoJSButtonClass2);
|
||||
|
||||
function TrimButtonClass(_plugin, _targetPosition, _text) {
|
||||
var _this3;
|
||||
|
||||
_this3 = _videoJSButtonClass2.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this3.trimmingControls = _plugin;
|
||||
_this3.targetPosition = _targetPosition;
|
||||
|
||||
_this3.controlText(_text);
|
||||
|
||||
_this3.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this3;
|
||||
}
|
||||
|
||||
var _proto4 = TrimButtonClass.prototype;
|
||||
|
||||
_proto4.handleClick = function handleClick() {
|
||||
if (this.targetPosition == 0) {
|
||||
this.trimmingControls.updateTrimTimes(this.player().currentTime(), this.trimmingControls.options.endTrim);
|
||||
} else if (this.targetPosition == 1) {
|
||||
this.trimmingControls.updateTrimTimes(this.trimmingControls.options.startTrim, this.player().currentTime());
|
||||
}
|
||||
};
|
||||
|
||||
return TrimButtonClass;
|
||||
}(videoJSButtonClass);
|
||||
|
||||
var TrimTimeDisplayClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJsComponentClas2) {
|
||||
_inheritsLoose(TrimTimeDisplayClass, _videoJsComponentClas2);
|
||||
|
||||
function TrimTimeDisplayClass(_defaultTime) {
|
||||
var _this4;
|
||||
|
||||
_this4 = _videoJsComponentClas2.call(this, player, {}) || this;
|
||||
|
||||
_this4.updateTimeContent(_defaultTime);
|
||||
|
||||
return _this4;
|
||||
}
|
||||
|
||||
var _proto5 = TrimTimeDisplayClass.prototype;
|
||||
|
||||
_proto5.createEl = function createEl() {
|
||||
return videojs.dom.createEl('input', {
|
||||
// Prefixing classes of elements within a player with "vjs-" is a convention used in Video.js.
|
||||
className: 'vjs-time-display'
|
||||
});
|
||||
};
|
||||
|
||||
_proto5.updateTimeContent = function updateTimeContent(timeInSeconds) {
|
||||
videojs.dom.emptyEl(this.el()); //this.controlText(videojs.formatTime(timeInSeconds, 600))
|
||||
//videojs.dom.appendContent(this.el(), videojs.formatTime(timeInSeconds, 600));
|
||||
//videojs.dom.textContent(this.el(), videojs.formatTime(timeInSeconds, 600));
|
||||
|
||||
this.el().value = videojs.formatTime(timeInSeconds, 600.01);
|
||||
};
|
||||
|
||||
return TrimTimeDisplayClass;
|
||||
}(videoJsComponentClass);
|
||||
|
||||
var FrameButtonClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass3) {
|
||||
_inheritsLoose(FrameButtonClass, _videoJSButtonClass3);
|
||||
|
||||
function FrameButtonClass(_plugin, _targetPosition, _text) {
|
||||
var _this5;
|
||||
|
||||
_this5 = _videoJSButtonClass3.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this5.trimmingControls = _plugin;
|
||||
_this5.targetPosition = _targetPosition;
|
||||
|
||||
_this5.controlText(_text);
|
||||
|
||||
_this5.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this5;
|
||||
}
|
||||
|
||||
var _proto6 = FrameButtonClass.prototype;
|
||||
|
||||
_proto6.handleClick = function handleClick() {
|
||||
if (this.targetPosition == 0) {
|
||||
this.player().currentTime(this.player().currentTime() - 0.1);
|
||||
} else if (this.targetPosition == 1) {
|
||||
this.player().currentTime(this.player().currentTime() + 0.1);
|
||||
}
|
||||
};
|
||||
|
||||
return FrameButtonClass;
|
||||
}(videoJSButtonClass);
|
||||
|
||||
var videoJSPlayToggleClass = videojs.getComponent('PlayToggle');
|
||||
|
||||
var playbackEndToggleClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass4) {
|
||||
_inheritsLoose(playbackEndToggleClass, _videoJSButtonClass4);
|
||||
|
||||
function playbackEndToggleClass(_plugin, _text) {
|
||||
var _this6;
|
||||
|
||||
_this6 = _videoJSButtonClass4.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this6.trimmingControls = _plugin;
|
||||
|
||||
_this6.controlText(_text);
|
||||
|
||||
_this6.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this6;
|
||||
}
|
||||
|
||||
var _proto7 = playbackEndToggleClass.prototype;
|
||||
|
||||
_proto7.handleClick = function handleClick() {
|
||||
this.trimmingControls.options.limitPlayback = !this.trimmingControls.options.limitPlayback;
|
||||
this.toggleClass('playbackLimited');
|
||||
};
|
||||
|
||||
return playbackEndToggleClass;
|
||||
}(videoJSButtonClass); //Creating Trimming Seek Bar
|
||||
|
||||
|
||||
this._trimSeekControlBar = new TrimControlBarClass();
|
||||
var trimSeekControlBarInstance = player.addChild(this._trimSeekControlBar, {
|
||||
componentClass: 'trimControlBar'
|
||||
}, player.children().length);
|
||||
trimSeekControlBarInstance.addClass('vljs-trim-seek');
|
||||
trimSeekControlBarInstance.el().innerHTML = '<div id="trimBarPlaceholderContainer"><div id="trimBarPlaceholder"></div></div>'; // //Spacer
|
||||
// this._spacer1 = new videoJSSpacerClass();
|
||||
// const spacer1Instance = this._trimSeekControlBar.addChild(this._spacer1, {componentClass: 'spacer'}, 0);
|
||||
// spacer1Instance.setAttribute("style", "flex: 0 0 158px");
|
||||
// //Spacer
|
||||
// this._spacer1 = new videoJSSpacerClass();
|
||||
// const spacer2Instance = this._trimSeekControlBar.addChild(this._spacer1, {componentClass: 'spacer'}, 2);
|
||||
// spacer2Instance.setAttribute("style", "flex: 0 0 178px");
|
||||
//Creating Trimming Controls Bar
|
||||
|
||||
this._trimControlBar = new TrimControlBarClass();
|
||||
var trimControlBarInstance = player.addChild(this._trimControlBar, {
|
||||
componentClass: 'trimControlBar'
|
||||
}, player.children().length);
|
||||
trimControlBarInstance.addClass('vljs-trim-buttons'); //Trim Bar Controls order: spacer,GoTo,Time,SetPlayhead,frameadjust,playpause,frameAdjust,setPlayhead,time,GoTo, endplayback
|
||||
//Spacer for balance
|
||||
|
||||
this._spacer = new videoJSSpacerClass();
|
||||
|
||||
var spacerInstance = this._trimControlBar.addChild(this._spacer, {
|
||||
componentClass: 'spacer'
|
||||
}, 0); //Go To start of Trim
|
||||
|
||||
|
||||
this._startGoToButton = new GoToButtonClass(this, 0, "Go to start of trim segment");
|
||||
|
||||
var startGoToButtonInstance = this._trimControlBar.addChild(this._startGoToButton, {
|
||||
componentClass: 'goToButton'
|
||||
}, 5);
|
||||
|
||||
startGoToButtonInstance.addClass('vljs-trimming-button');
|
||||
startGoToButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "skip_previous"; //Create trim start time display
|
||||
|
||||
this._startTrimTimeDisplay = new TrimTimeDisplayClass(this.options.startTrim);
|
||||
|
||||
var startTrimTimeDisplayInstance = this._trimControlBar.addChild(this._startTrimTimeDisplay, {
|
||||
componentClass: 'trimTimeDisplay'
|
||||
}, 10);
|
||||
|
||||
startTrimTimeDisplayInstance.on("change", function () {
|
||||
this.player_.trimmingControls().setTimestamps(startTrimTimeDisplayInstance.el().value, 0);
|
||||
}); //Create set start at playhead button
|
||||
|
||||
this._startTrimButton = new TrimButtonClass(this, 0, "Set trim start at playhead");
|
||||
|
||||
var startTrimButtonInstance = this._trimControlBar.addChild(this._startTrimButton, {
|
||||
componentClass: 'trimButton'
|
||||
}, 20);
|
||||
|
||||
startTrimButtonInstance.addClass('vljs-trimming-button');
|
||||
startTrimButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "edit"; //Create Frame Back Button
|
||||
|
||||
this._frameBackButton = new FrameButtonClass(this, 0, "Move back 1 frame");
|
||||
|
||||
var frameBackButtonInstance = this._trimControlBar.addChild(this._frameBackButton, {
|
||||
componentClass: 'frameButton'
|
||||
}, 22);
|
||||
|
||||
frameBackButtonInstance.addClass('vljs-trimming-button');
|
||||
frameBackButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "fast_rewind"; //Create Play/Pause Button
|
||||
|
||||
this._playPauseButton = new videoJSPlayToggleClass(this.player);
|
||||
|
||||
var playPauseButtonInstance = this._trimControlBar.addChild(this._playPauseButton, {
|
||||
componentClass: 'playPauseButton'
|
||||
}, 25); //Create Frame Forward Button
|
||||
|
||||
|
||||
this._frameForwardButton = new FrameButtonClass(this, 1, "Move forward 1 frame");
|
||||
|
||||
var frameForwardButtonInstance = this._trimControlBar.addChild(this._frameForwardButton, {
|
||||
componentClass: 'frameButton'
|
||||
}, 27);
|
||||
|
||||
frameForwardButtonInstance.addClass('vljs-trimming-button');
|
||||
frameForwardButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "fast_forward"; //Create set end at playhead button
|
||||
|
||||
this._endTrimButton = new TrimButtonClass(this, 1, "Set trim end at playhead");
|
||||
|
||||
var endTrimButtonInstance = this._trimControlBar.addChild(this._endTrimButton, {
|
||||
componentClass: 'trimButton'
|
||||
}, 30);
|
||||
|
||||
endTrimButtonInstance.addClass('vljs-trimming-button');
|
||||
endTrimButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "edit"; //Create trim end time display
|
||||
|
||||
this._endTrimTimeDisplay = new TrimTimeDisplayClass(this.options.endTrim);
|
||||
|
||||
var endTrimTimeDisplayInstance = this._trimControlBar.addChild(this._endTrimTimeDisplay, {
|
||||
componentClass: 'trimTimeDisplay'
|
||||
}, 40);
|
||||
|
||||
endTrimTimeDisplayInstance.on("change", function () {
|
||||
this.player_.trimmingControls().setTimestamps(endTrimTimeDisplayInstance.el().value, 1);
|
||||
}); //Go To end of Trim
|
||||
|
||||
this._endGoToButton = new GoToButtonClass(this, 1, "Go to end of trim segment");
|
||||
|
||||
var endGoToButtonInstance = this._trimControlBar.addChild(this._endGoToButton, {
|
||||
componentClass: 'goToButton'
|
||||
}, 50);
|
||||
|
||||
endGoToButtonInstance.addClass('vljs-trimming-button');
|
||||
endGoToButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "skip_next"; //End playback at trim endpoint
|
||||
|
||||
this._playbackEndToggle = new playbackEndToggleClass(this, "End playback at trim endpoint");
|
||||
|
||||
var playbackEndToggleInstance = this._trimControlBar.addChild(this._playbackEndToggle, {
|
||||
componentClass: 'playbackEndToggle'
|
||||
}, 60);
|
||||
|
||||
playbackEndToggleInstance.addClass('vljs-trimming-button');
|
||||
playbackEndToggleInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "stop";
|
||||
};
|
||||
|
||||
_proto.updateTrimTimes = function updateTrimTimes(startValue, endValue) {
|
||||
//Update stored values
|
||||
this.options.startTrim = startValue;
|
||||
this.options.endTrim = endValue; //Update timestamp displays
|
||||
|
||||
this._startTrimTimeDisplay.updateTimeContent(startValue);
|
||||
|
||||
this._endTrimTimeDisplay.updateTimeContent(endValue); //Update slider
|
||||
|
||||
|
||||
document.getElementById("trimBarPlaceholder").style["marginLeft"] = startValue / this.player.duration() * 100 + "%";
|
||||
document.getElementById("trimBarPlaceholder").style["marginRight"] = 100 - endValue / this.player.duration() * 100 + "%";
|
||||
};
|
||||
|
||||
_proto.setTimestamps = function setTimestamps(value, index) {
|
||||
if (/^\d*:?\d*:?\d*\.?\d*$/.test(value)) {
|
||||
if (index === 0) {
|
||||
this.updateTrimTimes(this.getSeconds(value), this.options.endTrim);
|
||||
} else if (index === 1) {
|
||||
this.updateTrimTimes(this.options.startTrim, this.getSeconds(value));
|
||||
}
|
||||
} else {
|
||||
this._startTrimTimeDisplay.updateTimeContent(startValue);
|
||||
|
||||
this._endTrimTimeDisplay.updateTimeContent(endValue);
|
||||
}
|
||||
};
|
||||
|
||||
_proto.getSeconds = function getSeconds(time) {
|
||||
var timeArr = time.split(':'),
|
||||
//Array of hours, minutes, and seconds.
|
||||
s = 0,
|
||||
//Seconds total
|
||||
m = 1; //Multiplier
|
||||
|
||||
while (timeArr.length > 0) {
|
||||
//Iterate through time segments starting from the seconds,
|
||||
s += m * timeArr.pop(); //multiply as appropriate, and add to seconds total,
|
||||
|
||||
m *= 60; //increase multiplier.
|
||||
}
|
||||
|
||||
return s;
|
||||
};
|
||||
|
||||
return TrimmingControls;
|
||||
}(Plugin); // Define default values for the plugin's `state` object here.
|
||||
|
||||
|
||||
TrimmingControls.defaultState = {}; // Include the version number.
|
||||
|
||||
TrimmingControls.VERSION = version; // Register the plugin with video.js.
|
||||
|
||||
videojs.registerPlugin('trimmingControls', TrimmingControls);
|
||||
|
||||
module.exports = TrimmingControls;
|
@ -0,0 +1 @@
|
||||
.video-js .vjs-control-bar{bottom:-3em;padding-top:3px}.video-js .vjs-progress-control.vjs-control{position:absolute;width:100%;margin-top:-16px}.video-js .vjs-current-time.vjs-time-control.vjs-control{flex:auto;text-align:right}.video-js .vjs-duration.vjs-time-control.vjs-control{flex:auto;text-align:left}.video-js .vjs-control-bar.vjs-trimming-controls.vljs-trim-seek{bottom:-6em;padding-top:0px}.video-js #trimBarPlaceholderContainer{background-color:rgba(115,133,159,0.5);margin-top:5px;margin-left:10px;margin-right:10px;height:3px;width:100%}.video-js #trimBarPlaceholder{background-color:darkorange;height:3px}.video-js .vjs-control-bar.vjs-trimming-controls.vljs-trim-buttons{bottom:-9em;padding-top:0px;justify-content:space-evenly}.video-js .vjs-time-display{width:50px;height:27px;text-align:center}.video-js .vljs-trimming-button .vjs-icon-placeholder{cursor:pointer}.video-js .playbackLimited{color:darkred}
|
@ -0,0 +1,452 @@
|
||||
/*! @name videojs-trimming-controls @version 0.0.0 @license MIT */
|
||||
import videojs from 'video.js';
|
||||
|
||||
function _inheritsLoose(subClass, superClass) {
|
||||
subClass.prototype = Object.create(superClass.prototype);
|
||||
subClass.prototype.constructor = subClass;
|
||||
subClass.__proto__ = superClass;
|
||||
}
|
||||
|
||||
var version = "0.0.0";
|
||||
|
||||
var Plugin = videojs.getPlugin('plugin'); // Default options for the plugin.
|
||||
|
||||
var defaults = {
|
||||
startTrim: 60,
|
||||
endTrim: 120,
|
||||
limitPlayback: false
|
||||
};
|
||||
/**
|
||||
* An advanced Video.js plugin. For more information on the API
|
||||
*
|
||||
* See: https://blog.videojs.com/feature-spotlight-advanced-plugins/
|
||||
*/
|
||||
|
||||
var TrimmingControls =
|
||||
/*#__PURE__*/
|
||||
function (_Plugin) {
|
||||
_inheritsLoose(TrimmingControls, _Plugin);
|
||||
|
||||
/**
|
||||
* Create a TrimmingControls plugin instance.
|
||||
*
|
||||
* @param {Player} player
|
||||
* A Video.js Player instance.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* An optional options object.
|
||||
*
|
||||
* While not a core part of the Video.js plugin architecture, a
|
||||
* second argument of options is a convenient way to accept inputs
|
||||
* from your plugin's caller.
|
||||
*/
|
||||
function TrimmingControls(player, options) {
|
||||
var _this;
|
||||
|
||||
// the parent class will add player under this.player
|
||||
_this = _Plugin.call(this, player) || this;
|
||||
_this.options = videojs.mergeOptions(defaults, options);
|
||||
|
||||
_this.createTrimmingControls();
|
||||
|
||||
player.ready(function () {
|
||||
setTimeout(function () {
|
||||
_this.updateTrimTimes(_this.options.startTrim, _this.options.endTrim);
|
||||
}, 100);
|
||||
player.on("timeupdate", function () {
|
||||
if (_this.options.limitPlayback && _this.player.currentTime() >= _this.options.endTrim) {
|
||||
_this.player.currentTime(_this.options.endTrim);
|
||||
|
||||
_this.player.pause();
|
||||
}
|
||||
});
|
||||
player.on('playing', function () {
|
||||
videojs.log('playback began!');
|
||||
|
||||
_this.updateTrimTimes(_this.options.startTrim, _this.options.endTrim);
|
||||
});
|
||||
});
|
||||
return _this;
|
||||
}
|
||||
|
||||
var _proto = TrimmingControls.prototype;
|
||||
|
||||
_proto.createTrimmingControls = function createTrimmingControls() {
|
||||
var player = this.player;
|
||||
var videoJsComponentClass = videojs.getComponent('Component');
|
||||
/**
|
||||
* Extend vjs button class for quality button.
|
||||
*/
|
||||
|
||||
var TrimControlBarClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJsComponentClas) {
|
||||
_inheritsLoose(TrimControlBarClass, _videoJsComponentClas);
|
||||
|
||||
/**
|
||||
* Component constructor.
|
||||
*/
|
||||
function TrimControlBarClass() {
|
||||
return _videoJsComponentClas.call(this, player, {
|
||||
title: player.localize('Trimming Controls')
|
||||
}) || this;
|
||||
}
|
||||
|
||||
var _proto2 = TrimControlBarClass.prototype;
|
||||
|
||||
_proto2.createEl = function createEl() {
|
||||
return videojs.dom.createEl('div', {
|
||||
// Prefixing classes of elements within a player with "vjs-" is a convention used in Video.js.
|
||||
className: 'vjs-control-bar vjs-trimming-controls',
|
||||
dir: 'ltr'
|
||||
});
|
||||
};
|
||||
|
||||
return TrimControlBarClass;
|
||||
}(videoJsComponentClass);
|
||||
|
||||
var videoJSSpacerClass = videojs.getComponent('Spacer');
|
||||
var videoJSButtonClass = videojs.getComponent('Button');
|
||||
|
||||
var GoToButtonClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass) {
|
||||
_inheritsLoose(GoToButtonClass, _videoJSButtonClass);
|
||||
|
||||
function GoToButtonClass(_plugin, _targetPosition, _text) {
|
||||
var _this2;
|
||||
|
||||
_this2 = _videoJSButtonClass.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this2.trimmingControls = _plugin;
|
||||
_this2.targetPosition = _targetPosition;
|
||||
|
||||
_this2.controlText(_text);
|
||||
|
||||
_this2.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this2;
|
||||
}
|
||||
|
||||
var _proto3 = GoToButtonClass.prototype;
|
||||
|
||||
_proto3.handleClick = function handleClick() {
|
||||
if (this.targetPosition == 0) {
|
||||
this.player().currentTime(this.trimmingControls.options.startTrim);
|
||||
} else if (this.targetPosition == 1) {
|
||||
this.player().currentTime(this.trimmingControls.options.endTrim);
|
||||
}
|
||||
};
|
||||
|
||||
return GoToButtonClass;
|
||||
}(videoJSButtonClass);
|
||||
|
||||
var TrimButtonClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass2) {
|
||||
_inheritsLoose(TrimButtonClass, _videoJSButtonClass2);
|
||||
|
||||
function TrimButtonClass(_plugin, _targetPosition, _text) {
|
||||
var _this3;
|
||||
|
||||
_this3 = _videoJSButtonClass2.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this3.trimmingControls = _plugin;
|
||||
_this3.targetPosition = _targetPosition;
|
||||
|
||||
_this3.controlText(_text);
|
||||
|
||||
_this3.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this3;
|
||||
}
|
||||
|
||||
var _proto4 = TrimButtonClass.prototype;
|
||||
|
||||
_proto4.handleClick = function handleClick() {
|
||||
if (this.targetPosition == 0) {
|
||||
this.trimmingControls.updateTrimTimes(this.player().currentTime(), this.trimmingControls.options.endTrim);
|
||||
} else if (this.targetPosition == 1) {
|
||||
this.trimmingControls.updateTrimTimes(this.trimmingControls.options.startTrim, this.player().currentTime());
|
||||
}
|
||||
};
|
||||
|
||||
return TrimButtonClass;
|
||||
}(videoJSButtonClass);
|
||||
|
||||
var TrimTimeDisplayClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJsComponentClas2) {
|
||||
_inheritsLoose(TrimTimeDisplayClass, _videoJsComponentClas2);
|
||||
|
||||
function TrimTimeDisplayClass(_defaultTime) {
|
||||
var _this4;
|
||||
|
||||
_this4 = _videoJsComponentClas2.call(this, player, {}) || this;
|
||||
|
||||
_this4.updateTimeContent(_defaultTime);
|
||||
|
||||
return _this4;
|
||||
}
|
||||
|
||||
var _proto5 = TrimTimeDisplayClass.prototype;
|
||||
|
||||
_proto5.createEl = function createEl() {
|
||||
return videojs.dom.createEl('input', {
|
||||
// Prefixing classes of elements within a player with "vjs-" is a convention used in Video.js.
|
||||
className: 'vjs-time-display'
|
||||
});
|
||||
};
|
||||
|
||||
_proto5.updateTimeContent = function updateTimeContent(timeInSeconds) {
|
||||
videojs.dom.emptyEl(this.el()); //this.controlText(videojs.formatTime(timeInSeconds, 600))
|
||||
//videojs.dom.appendContent(this.el(), videojs.formatTime(timeInSeconds, 600));
|
||||
//videojs.dom.textContent(this.el(), videojs.formatTime(timeInSeconds, 600));
|
||||
|
||||
this.el().value = videojs.formatTime(timeInSeconds, 600.01);
|
||||
};
|
||||
|
||||
return TrimTimeDisplayClass;
|
||||
}(videoJsComponentClass);
|
||||
|
||||
var FrameButtonClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass3) {
|
||||
_inheritsLoose(FrameButtonClass, _videoJSButtonClass3);
|
||||
|
||||
function FrameButtonClass(_plugin, _targetPosition, _text) {
|
||||
var _this5;
|
||||
|
||||
_this5 = _videoJSButtonClass3.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this5.trimmingControls = _plugin;
|
||||
_this5.targetPosition = _targetPosition;
|
||||
|
||||
_this5.controlText(_text);
|
||||
|
||||
_this5.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this5;
|
||||
}
|
||||
|
||||
var _proto6 = FrameButtonClass.prototype;
|
||||
|
||||
_proto6.handleClick = function handleClick() {
|
||||
if (this.targetPosition == 0) {
|
||||
this.player().currentTime(this.player().currentTime() - 0.1);
|
||||
} else if (this.targetPosition == 1) {
|
||||
this.player().currentTime(this.player().currentTime() + 0.1);
|
||||
}
|
||||
};
|
||||
|
||||
return FrameButtonClass;
|
||||
}(videoJSButtonClass);
|
||||
|
||||
var videoJSPlayToggleClass = videojs.getComponent('PlayToggle');
|
||||
|
||||
var playbackEndToggleClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass4) {
|
||||
_inheritsLoose(playbackEndToggleClass, _videoJSButtonClass4);
|
||||
|
||||
function playbackEndToggleClass(_plugin, _text) {
|
||||
var _this6;
|
||||
|
||||
_this6 = _videoJSButtonClass4.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this6.trimmingControls = _plugin;
|
||||
|
||||
_this6.controlText(_text);
|
||||
|
||||
_this6.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this6;
|
||||
}
|
||||
|
||||
var _proto7 = playbackEndToggleClass.prototype;
|
||||
|
||||
_proto7.handleClick = function handleClick() {
|
||||
this.trimmingControls.options.limitPlayback = !this.trimmingControls.options.limitPlayback;
|
||||
this.toggleClass('playbackLimited');
|
||||
};
|
||||
|
||||
return playbackEndToggleClass;
|
||||
}(videoJSButtonClass); //Creating Trimming Seek Bar
|
||||
|
||||
|
||||
this._trimSeekControlBar = new TrimControlBarClass();
|
||||
var trimSeekControlBarInstance = player.addChild(this._trimSeekControlBar, {
|
||||
componentClass: 'trimControlBar'
|
||||
}, player.children().length);
|
||||
trimSeekControlBarInstance.addClass('vljs-trim-seek');
|
||||
trimSeekControlBarInstance.el().innerHTML = '<div id="trimBarPlaceholderContainer"><div id="trimBarPlaceholder"></div></div>'; // //Spacer
|
||||
// this._spacer1 = new videoJSSpacerClass();
|
||||
// const spacer1Instance = this._trimSeekControlBar.addChild(this._spacer1, {componentClass: 'spacer'}, 0);
|
||||
// spacer1Instance.setAttribute("style", "flex: 0 0 158px");
|
||||
// //Spacer
|
||||
// this._spacer1 = new videoJSSpacerClass();
|
||||
// const spacer2Instance = this._trimSeekControlBar.addChild(this._spacer1, {componentClass: 'spacer'}, 2);
|
||||
// spacer2Instance.setAttribute("style", "flex: 0 0 178px");
|
||||
//Creating Trimming Controls Bar
|
||||
|
||||
this._trimControlBar = new TrimControlBarClass();
|
||||
var trimControlBarInstance = player.addChild(this._trimControlBar, {
|
||||
componentClass: 'trimControlBar'
|
||||
}, player.children().length);
|
||||
trimControlBarInstance.addClass('vljs-trim-buttons'); //Trim Bar Controls order: spacer,GoTo,Time,SetPlayhead,frameadjust,playpause,frameAdjust,setPlayhead,time,GoTo, endplayback
|
||||
//Spacer for balance
|
||||
|
||||
this._spacer = new videoJSSpacerClass();
|
||||
|
||||
var spacerInstance = this._trimControlBar.addChild(this._spacer, {
|
||||
componentClass: 'spacer'
|
||||
}, 0); //Go To start of Trim
|
||||
|
||||
|
||||
this._startGoToButton = new GoToButtonClass(this, 0, "Go to start of trim segment");
|
||||
|
||||
var startGoToButtonInstance = this._trimControlBar.addChild(this._startGoToButton, {
|
||||
componentClass: 'goToButton'
|
||||
}, 5);
|
||||
|
||||
startGoToButtonInstance.addClass('vljs-trimming-button');
|
||||
startGoToButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "skip_previous"; //Create trim start time display
|
||||
|
||||
this._startTrimTimeDisplay = new TrimTimeDisplayClass(this.options.startTrim);
|
||||
|
||||
var startTrimTimeDisplayInstance = this._trimControlBar.addChild(this._startTrimTimeDisplay, {
|
||||
componentClass: 'trimTimeDisplay'
|
||||
}, 10);
|
||||
|
||||
startTrimTimeDisplayInstance.on("change", function () {
|
||||
this.player_.trimmingControls().setTimestamps(startTrimTimeDisplayInstance.el().value, 0);
|
||||
}); //Create set start at playhead button
|
||||
|
||||
this._startTrimButton = new TrimButtonClass(this, 0, "Set trim start at playhead");
|
||||
|
||||
var startTrimButtonInstance = this._trimControlBar.addChild(this._startTrimButton, {
|
||||
componentClass: 'trimButton'
|
||||
}, 20);
|
||||
|
||||
startTrimButtonInstance.addClass('vljs-trimming-button');
|
||||
startTrimButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "edit"; //Create Frame Back Button
|
||||
|
||||
this._frameBackButton = new FrameButtonClass(this, 0, "Move back 1 frame");
|
||||
|
||||
var frameBackButtonInstance = this._trimControlBar.addChild(this._frameBackButton, {
|
||||
componentClass: 'frameButton'
|
||||
}, 22);
|
||||
|
||||
frameBackButtonInstance.addClass('vljs-trimming-button');
|
||||
frameBackButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "fast_rewind"; //Create Play/Pause Button
|
||||
|
||||
this._playPauseButton = new videoJSPlayToggleClass(this.player);
|
||||
|
||||
var playPauseButtonInstance = this._trimControlBar.addChild(this._playPauseButton, {
|
||||
componentClass: 'playPauseButton'
|
||||
}, 25); //Create Frame Forward Button
|
||||
|
||||
|
||||
this._frameForwardButton = new FrameButtonClass(this, 1, "Move forward 1 frame");
|
||||
|
||||
var frameForwardButtonInstance = this._trimControlBar.addChild(this._frameForwardButton, {
|
||||
componentClass: 'frameButton'
|
||||
}, 27);
|
||||
|
||||
frameForwardButtonInstance.addClass('vljs-trimming-button');
|
||||
frameForwardButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "fast_forward"; //Create set end at playhead button
|
||||
|
||||
this._endTrimButton = new TrimButtonClass(this, 1, "Set trim end at playhead");
|
||||
|
||||
var endTrimButtonInstance = this._trimControlBar.addChild(this._endTrimButton, {
|
||||
componentClass: 'trimButton'
|
||||
}, 30);
|
||||
|
||||
endTrimButtonInstance.addClass('vljs-trimming-button');
|
||||
endTrimButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "edit"; //Create trim end time display
|
||||
|
||||
this._endTrimTimeDisplay = new TrimTimeDisplayClass(this.options.endTrim);
|
||||
|
||||
var endTrimTimeDisplayInstance = this._trimControlBar.addChild(this._endTrimTimeDisplay, {
|
||||
componentClass: 'trimTimeDisplay'
|
||||
}, 40);
|
||||
|
||||
endTrimTimeDisplayInstance.on("change", function () {
|
||||
this.player_.trimmingControls().setTimestamps(endTrimTimeDisplayInstance.el().value, 1);
|
||||
}); //Go To end of Trim
|
||||
|
||||
this._endGoToButton = new GoToButtonClass(this, 1, "Go to end of trim segment");
|
||||
|
||||
var endGoToButtonInstance = this._trimControlBar.addChild(this._endGoToButton, {
|
||||
componentClass: 'goToButton'
|
||||
}, 50);
|
||||
|
||||
endGoToButtonInstance.addClass('vljs-trimming-button');
|
||||
endGoToButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "skip_next"; //End playback at trim endpoint
|
||||
|
||||
this._playbackEndToggle = new playbackEndToggleClass(this, "End playback at trim endpoint");
|
||||
|
||||
var playbackEndToggleInstance = this._trimControlBar.addChild(this._playbackEndToggle, {
|
||||
componentClass: 'playbackEndToggle'
|
||||
}, 60);
|
||||
|
||||
playbackEndToggleInstance.addClass('vljs-trimming-button');
|
||||
playbackEndToggleInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "stop";
|
||||
};
|
||||
|
||||
_proto.updateTrimTimes = function updateTrimTimes(startValue, endValue) {
|
||||
//Update stored values
|
||||
this.options.startTrim = startValue;
|
||||
this.options.endTrim = endValue; //Update timestamp displays
|
||||
|
||||
this._startTrimTimeDisplay.updateTimeContent(startValue);
|
||||
|
||||
this._endTrimTimeDisplay.updateTimeContent(endValue); //Update slider
|
||||
|
||||
|
||||
document.getElementById("trimBarPlaceholder").style["marginLeft"] = startValue / this.player.duration() * 100 + "%";
|
||||
document.getElementById("trimBarPlaceholder").style["marginRight"] = 100 - endValue / this.player.duration() * 100 + "%";
|
||||
};
|
||||
|
||||
_proto.setTimestamps = function setTimestamps(value, index) {
|
||||
if (/^\d*:?\d*:?\d*\.?\d*$/.test(value)) {
|
||||
if (index === 0) {
|
||||
this.updateTrimTimes(this.getSeconds(value), this.options.endTrim);
|
||||
} else if (index === 1) {
|
||||
this.updateTrimTimes(this.options.startTrim, this.getSeconds(value));
|
||||
}
|
||||
} else {
|
||||
this._startTrimTimeDisplay.updateTimeContent(startValue);
|
||||
|
||||
this._endTrimTimeDisplay.updateTimeContent(endValue);
|
||||
}
|
||||
};
|
||||
|
||||
_proto.getSeconds = function getSeconds(time) {
|
||||
var timeArr = time.split(':'),
|
||||
//Array of hours, minutes, and seconds.
|
||||
s = 0,
|
||||
//Seconds total
|
||||
m = 1; //Multiplier
|
||||
|
||||
while (timeArr.length > 0) {
|
||||
//Iterate through time segments starting from the seconds,
|
||||
s += m * timeArr.pop(); //multiply as appropriate, and add to seconds total,
|
||||
|
||||
m *= 60; //increase multiplier.
|
||||
}
|
||||
|
||||
return s;
|
||||
};
|
||||
|
||||
return TrimmingControls;
|
||||
}(Plugin); // Define default values for the plugin's `state` object here.
|
||||
|
||||
|
||||
TrimmingControls.defaultState = {}; // Include the version number.
|
||||
|
||||
TrimmingControls.VERSION = version; // Register the plugin with video.js.
|
||||
|
||||
videojs.registerPlugin('trimmingControls', TrimmingControls);
|
||||
|
||||
export default TrimmingControls;
|
@ -0,0 +1,460 @@
|
||||
/*! @name videojs-trimming-controls @version 0.0.0 @license MIT */
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['video.js'], factory) :
|
||||
(global.videojsTrimmingControls = factory(global.videojs));
|
||||
}(this, (function (videojs) { 'use strict';
|
||||
|
||||
videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
|
||||
|
||||
function _inheritsLoose(subClass, superClass) {
|
||||
subClass.prototype = Object.create(superClass.prototype);
|
||||
subClass.prototype.constructor = subClass;
|
||||
subClass.__proto__ = superClass;
|
||||
}
|
||||
|
||||
var version = "0.0.0";
|
||||
|
||||
var Plugin = videojs.getPlugin('plugin'); // Default options for the plugin.
|
||||
|
||||
var defaults = {
|
||||
startTrim: 60,
|
||||
endTrim: 120,
|
||||
limitPlayback: false
|
||||
};
|
||||
/**
|
||||
* An advanced Video.js plugin. For more information on the API
|
||||
*
|
||||
* See: https://blog.videojs.com/feature-spotlight-advanced-plugins/
|
||||
*/
|
||||
|
||||
var TrimmingControls =
|
||||
/*#__PURE__*/
|
||||
function (_Plugin) {
|
||||
_inheritsLoose(TrimmingControls, _Plugin);
|
||||
|
||||
/**
|
||||
* Create a TrimmingControls plugin instance.
|
||||
*
|
||||
* @param {Player} player
|
||||
* A Video.js Player instance.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* An optional options object.
|
||||
*
|
||||
* While not a core part of the Video.js plugin architecture, a
|
||||
* second argument of options is a convenient way to accept inputs
|
||||
* from your plugin's caller.
|
||||
*/
|
||||
function TrimmingControls(player, options) {
|
||||
var _this;
|
||||
|
||||
// the parent class will add player under this.player
|
||||
_this = _Plugin.call(this, player) || this;
|
||||
_this.options = videojs.mergeOptions(defaults, options);
|
||||
|
||||
_this.createTrimmingControls();
|
||||
|
||||
player.ready(function () {
|
||||
setTimeout(function () {
|
||||
_this.updateTrimTimes(_this.options.startTrim, _this.options.endTrim);
|
||||
}, 100);
|
||||
player.on("timeupdate", function () {
|
||||
if (_this.options.limitPlayback && _this.player.currentTime() >= _this.options.endTrim) {
|
||||
_this.player.currentTime(_this.options.endTrim);
|
||||
|
||||
_this.player.pause();
|
||||
}
|
||||
});
|
||||
player.on('playing', function () {
|
||||
videojs.log('playback began!');
|
||||
|
||||
_this.updateTrimTimes(_this.options.startTrim, _this.options.endTrim);
|
||||
});
|
||||
});
|
||||
return _this;
|
||||
}
|
||||
|
||||
var _proto = TrimmingControls.prototype;
|
||||
|
||||
_proto.createTrimmingControls = function createTrimmingControls() {
|
||||
var player = this.player;
|
||||
var videoJsComponentClass = videojs.getComponent('Component');
|
||||
/**
|
||||
* Extend vjs button class for quality button.
|
||||
*/
|
||||
|
||||
var TrimControlBarClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJsComponentClas) {
|
||||
_inheritsLoose(TrimControlBarClass, _videoJsComponentClas);
|
||||
|
||||
/**
|
||||
* Component constructor.
|
||||
*/
|
||||
function TrimControlBarClass() {
|
||||
return _videoJsComponentClas.call(this, player, {
|
||||
title: player.localize('Trimming Controls')
|
||||
}) || this;
|
||||
}
|
||||
|
||||
var _proto2 = TrimControlBarClass.prototype;
|
||||
|
||||
_proto2.createEl = function createEl() {
|
||||
return videojs.dom.createEl('div', {
|
||||
// Prefixing classes of elements within a player with "vjs-" is a convention used in Video.js.
|
||||
className: 'vjs-control-bar vjs-trimming-controls',
|
||||
dir: 'ltr'
|
||||
});
|
||||
};
|
||||
|
||||
return TrimControlBarClass;
|
||||
}(videoJsComponentClass);
|
||||
|
||||
var videoJSSpacerClass = videojs.getComponent('Spacer');
|
||||
var videoJSButtonClass = videojs.getComponent('Button');
|
||||
|
||||
var GoToButtonClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass) {
|
||||
_inheritsLoose(GoToButtonClass, _videoJSButtonClass);
|
||||
|
||||
function GoToButtonClass(_plugin, _targetPosition, _text) {
|
||||
var _this2;
|
||||
|
||||
_this2 = _videoJSButtonClass.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this2.trimmingControls = _plugin;
|
||||
_this2.targetPosition = _targetPosition;
|
||||
|
||||
_this2.controlText(_text);
|
||||
|
||||
_this2.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this2;
|
||||
}
|
||||
|
||||
var _proto3 = GoToButtonClass.prototype;
|
||||
|
||||
_proto3.handleClick = function handleClick() {
|
||||
if (this.targetPosition == 0) {
|
||||
this.player().currentTime(this.trimmingControls.options.startTrim);
|
||||
} else if (this.targetPosition == 1) {
|
||||
this.player().currentTime(this.trimmingControls.options.endTrim);
|
||||
}
|
||||
};
|
||||
|
||||
return GoToButtonClass;
|
||||
}(videoJSButtonClass);
|
||||
|
||||
var TrimButtonClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass2) {
|
||||
_inheritsLoose(TrimButtonClass, _videoJSButtonClass2);
|
||||
|
||||
function TrimButtonClass(_plugin, _targetPosition, _text) {
|
||||
var _this3;
|
||||
|
||||
_this3 = _videoJSButtonClass2.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this3.trimmingControls = _plugin;
|
||||
_this3.targetPosition = _targetPosition;
|
||||
|
||||
_this3.controlText(_text);
|
||||
|
||||
_this3.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this3;
|
||||
}
|
||||
|
||||
var _proto4 = TrimButtonClass.prototype;
|
||||
|
||||
_proto4.handleClick = function handleClick() {
|
||||
if (this.targetPosition == 0) {
|
||||
this.trimmingControls.updateTrimTimes(this.player().currentTime(), this.trimmingControls.options.endTrim);
|
||||
} else if (this.targetPosition == 1) {
|
||||
this.trimmingControls.updateTrimTimes(this.trimmingControls.options.startTrim, this.player().currentTime());
|
||||
}
|
||||
};
|
||||
|
||||
return TrimButtonClass;
|
||||
}(videoJSButtonClass);
|
||||
|
||||
var TrimTimeDisplayClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJsComponentClas2) {
|
||||
_inheritsLoose(TrimTimeDisplayClass, _videoJsComponentClas2);
|
||||
|
||||
function TrimTimeDisplayClass(_defaultTime) {
|
||||
var _this4;
|
||||
|
||||
_this4 = _videoJsComponentClas2.call(this, player, {}) || this;
|
||||
|
||||
_this4.updateTimeContent(_defaultTime);
|
||||
|
||||
return _this4;
|
||||
}
|
||||
|
||||
var _proto5 = TrimTimeDisplayClass.prototype;
|
||||
|
||||
_proto5.createEl = function createEl() {
|
||||
return videojs.dom.createEl('input', {
|
||||
// Prefixing classes of elements within a player with "vjs-" is a convention used in Video.js.
|
||||
className: 'vjs-time-display'
|
||||
});
|
||||
};
|
||||
|
||||
_proto5.updateTimeContent = function updateTimeContent(timeInSeconds) {
|
||||
videojs.dom.emptyEl(this.el()); //this.controlText(videojs.formatTime(timeInSeconds, 600))
|
||||
//videojs.dom.appendContent(this.el(), videojs.formatTime(timeInSeconds, 600));
|
||||
//videojs.dom.textContent(this.el(), videojs.formatTime(timeInSeconds, 600));
|
||||
|
||||
this.el().value = videojs.formatTime(timeInSeconds, 600.01);
|
||||
};
|
||||
|
||||
return TrimTimeDisplayClass;
|
||||
}(videoJsComponentClass);
|
||||
|
||||
var FrameButtonClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass3) {
|
||||
_inheritsLoose(FrameButtonClass, _videoJSButtonClass3);
|
||||
|
||||
function FrameButtonClass(_plugin, _targetPosition, _text) {
|
||||
var _this5;
|
||||
|
||||
_this5 = _videoJSButtonClass3.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this5.trimmingControls = _plugin;
|
||||
_this5.targetPosition = _targetPosition;
|
||||
|
||||
_this5.controlText(_text);
|
||||
|
||||
_this5.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this5;
|
||||
}
|
||||
|
||||
var _proto6 = FrameButtonClass.prototype;
|
||||
|
||||
_proto6.handleClick = function handleClick() {
|
||||
if (this.targetPosition == 0) {
|
||||
this.player().currentTime(this.player().currentTime() - 0.1);
|
||||
} else if (this.targetPosition == 1) {
|
||||
this.player().currentTime(this.player().currentTime() + 0.1);
|
||||
}
|
||||
};
|
||||
|
||||
return FrameButtonClass;
|
||||
}(videoJSButtonClass);
|
||||
|
||||
var videoJSPlayToggleClass = videojs.getComponent('PlayToggle');
|
||||
|
||||
var playbackEndToggleClass =
|
||||
/*#__PURE__*/
|
||||
function (_videoJSButtonClass4) {
|
||||
_inheritsLoose(playbackEndToggleClass, _videoJSButtonClass4);
|
||||
|
||||
function playbackEndToggleClass(_plugin, _text) {
|
||||
var _this6;
|
||||
|
||||
_this6 = _videoJSButtonClass4.call(this, player, {// title: player.localize('Trim Button'),
|
||||
// label: "Trim Here"
|
||||
}) || this;
|
||||
_this6.trimmingControls = _plugin;
|
||||
|
||||
_this6.controlText(_text);
|
||||
|
||||
_this6.el().getElementsByClassName('vjs-icon-placeholder')[0].classList += " material-icons";
|
||||
return _this6;
|
||||
}
|
||||
|
||||
var _proto7 = playbackEndToggleClass.prototype;
|
||||
|
||||
_proto7.handleClick = function handleClick() {
|
||||
this.trimmingControls.options.limitPlayback = !this.trimmingControls.options.limitPlayback;
|
||||
this.toggleClass('playbackLimited');
|
||||
};
|
||||
|
||||
return playbackEndToggleClass;
|
||||
}(videoJSButtonClass); //Creating Trimming Seek Bar
|
||||
|
||||
|
||||
this._trimSeekControlBar = new TrimControlBarClass();
|
||||
var trimSeekControlBarInstance = player.addChild(this._trimSeekControlBar, {
|
||||
componentClass: 'trimControlBar'
|
||||
}, player.children().length);
|
||||
trimSeekControlBarInstance.addClass('vljs-trim-seek');
|
||||
trimSeekControlBarInstance.el().innerHTML = '<div id="trimBarPlaceholderContainer"><div id="trimBarPlaceholder"></div></div>'; // //Spacer
|
||||
// this._spacer1 = new videoJSSpacerClass();
|
||||
// const spacer1Instance = this._trimSeekControlBar.addChild(this._spacer1, {componentClass: 'spacer'}, 0);
|
||||
// spacer1Instance.setAttribute("style", "flex: 0 0 158px");
|
||||
// //Spacer
|
||||
// this._spacer1 = new videoJSSpacerClass();
|
||||
// const spacer2Instance = this._trimSeekControlBar.addChild(this._spacer1, {componentClass: 'spacer'}, 2);
|
||||
// spacer2Instance.setAttribute("style", "flex: 0 0 178px");
|
||||
//Creating Trimming Controls Bar
|
||||
|
||||
this._trimControlBar = new TrimControlBarClass();
|
||||
var trimControlBarInstance = player.addChild(this._trimControlBar, {
|
||||
componentClass: 'trimControlBar'
|
||||
}, player.children().length);
|
||||
trimControlBarInstance.addClass('vljs-trim-buttons'); //Trim Bar Controls order: spacer,GoTo,Time,SetPlayhead,frameadjust,playpause,frameAdjust,setPlayhead,time,GoTo, endplayback
|
||||
//Spacer for balance
|
||||
|
||||
this._spacer = new videoJSSpacerClass();
|
||||
|
||||
var spacerInstance = this._trimControlBar.addChild(this._spacer, {
|
||||
componentClass: 'spacer'
|
||||
}, 0); //Go To start of Trim
|
||||
|
||||
|
||||
this._startGoToButton = new GoToButtonClass(this, 0, "Go to start of trim segment");
|
||||
|
||||
var startGoToButtonInstance = this._trimControlBar.addChild(this._startGoToButton, {
|
||||
componentClass: 'goToButton'
|
||||
}, 5);
|
||||
|
||||
startGoToButtonInstance.addClass('vljs-trimming-button');
|
||||
startGoToButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "skip_previous"; //Create trim start time display
|
||||
|
||||
this._startTrimTimeDisplay = new TrimTimeDisplayClass(this.options.startTrim);
|
||||
|
||||
var startTrimTimeDisplayInstance = this._trimControlBar.addChild(this._startTrimTimeDisplay, {
|
||||
componentClass: 'trimTimeDisplay'
|
||||
}, 10);
|
||||
|
||||
startTrimTimeDisplayInstance.on("change", function () {
|
||||
this.player_.trimmingControls().setTimestamps(startTrimTimeDisplayInstance.el().value, 0);
|
||||
}); //Create set start at playhead button
|
||||
|
||||
this._startTrimButton = new TrimButtonClass(this, 0, "Set trim start at playhead");
|
||||
|
||||
var startTrimButtonInstance = this._trimControlBar.addChild(this._startTrimButton, {
|
||||
componentClass: 'trimButton'
|
||||
}, 20);
|
||||
|
||||
startTrimButtonInstance.addClass('vljs-trimming-button');
|
||||
startTrimButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "edit"; //Create Frame Back Button
|
||||
|
||||
this._frameBackButton = new FrameButtonClass(this, 0, "Move back 1 frame");
|
||||
|
||||
var frameBackButtonInstance = this._trimControlBar.addChild(this._frameBackButton, {
|
||||
componentClass: 'frameButton'
|
||||
}, 22);
|
||||
|
||||
frameBackButtonInstance.addClass('vljs-trimming-button');
|
||||
frameBackButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "fast_rewind"; //Create Play/Pause Button
|
||||
|
||||
this._playPauseButton = new videoJSPlayToggleClass(this.player);
|
||||
|
||||
var playPauseButtonInstance = this._trimControlBar.addChild(this._playPauseButton, {
|
||||
componentClass: 'playPauseButton'
|
||||
}, 25); //Create Frame Forward Button
|
||||
|
||||
|
||||
this._frameForwardButton = new FrameButtonClass(this, 1, "Move forward 1 frame");
|
||||
|
||||
var frameForwardButtonInstance = this._trimControlBar.addChild(this._frameForwardButton, {
|
||||
componentClass: 'frameButton'
|
||||
}, 27);
|
||||
|
||||
frameForwardButtonInstance.addClass('vljs-trimming-button');
|
||||
frameForwardButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "fast_forward"; //Create set end at playhead button
|
||||
|
||||
this._endTrimButton = new TrimButtonClass(this, 1, "Set trim end at playhead");
|
||||
|
||||
var endTrimButtonInstance = this._trimControlBar.addChild(this._endTrimButton, {
|
||||
componentClass: 'trimButton'
|
||||
}, 30);
|
||||
|
||||
endTrimButtonInstance.addClass('vljs-trimming-button');
|
||||
endTrimButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "edit"; //Create trim end time display
|
||||
|
||||
this._endTrimTimeDisplay = new TrimTimeDisplayClass(this.options.endTrim);
|
||||
|
||||
var endTrimTimeDisplayInstance = this._trimControlBar.addChild(this._endTrimTimeDisplay, {
|
||||
componentClass: 'trimTimeDisplay'
|
||||
}, 40);
|
||||
|
||||
endTrimTimeDisplayInstance.on("change", function () {
|
||||
this.player_.trimmingControls().setTimestamps(endTrimTimeDisplayInstance.el().value, 1);
|
||||
}); //Go To end of Trim
|
||||
|
||||
this._endGoToButton = new GoToButtonClass(this, 1, "Go to end of trim segment");
|
||||
|
||||
var endGoToButtonInstance = this._trimControlBar.addChild(this._endGoToButton, {
|
||||
componentClass: 'goToButton'
|
||||
}, 50);
|
||||
|
||||
endGoToButtonInstance.addClass('vljs-trimming-button');
|
||||
endGoToButtonInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "skip_next"; //End playback at trim endpoint
|
||||
|
||||
this._playbackEndToggle = new playbackEndToggleClass(this, "End playback at trim endpoint");
|
||||
|
||||
var playbackEndToggleInstance = this._trimControlBar.addChild(this._playbackEndToggle, {
|
||||
componentClass: 'playbackEndToggle'
|
||||
}, 60);
|
||||
|
||||
playbackEndToggleInstance.addClass('vljs-trimming-button');
|
||||
playbackEndToggleInstance.el().getElementsByClassName('vjs-icon-placeholder')[0].innerText = "stop";
|
||||
};
|
||||
|
||||
_proto.updateTrimTimes = function updateTrimTimes(startValue, endValue) {
|
||||
//Update stored values
|
||||
this.options.startTrim = startValue;
|
||||
this.options.endTrim = endValue; //Update timestamp displays
|
||||
|
||||
this._startTrimTimeDisplay.updateTimeContent(startValue);
|
||||
|
||||
this._endTrimTimeDisplay.updateTimeContent(endValue); //Update slider
|
||||
|
||||
|
||||
document.getElementById("trimBarPlaceholder").style["marginLeft"] = startValue / this.player.duration() * 100 + "%";
|
||||
document.getElementById("trimBarPlaceholder").style["marginRight"] = 100 - endValue / this.player.duration() * 100 + "%";
|
||||
};
|
||||
|
||||
_proto.setTimestamps = function setTimestamps(value, index) {
|
||||
if (/^\d*:?\d*:?\d*\.?\d*$/.test(value)) {
|
||||
if (index === 0) {
|
||||
this.updateTrimTimes(this.getSeconds(value), this.options.endTrim);
|
||||
} else if (index === 1) {
|
||||
this.updateTrimTimes(this.options.startTrim, this.getSeconds(value));
|
||||
}
|
||||
} else {
|
||||
this._startTrimTimeDisplay.updateTimeContent(startValue);
|
||||
|
||||
this._endTrimTimeDisplay.updateTimeContent(endValue);
|
||||
}
|
||||
};
|
||||
|
||||
_proto.getSeconds = function getSeconds(time) {
|
||||
var timeArr = time.split(':'),
|
||||
//Array of hours, minutes, and seconds.
|
||||
s = 0,
|
||||
//Seconds total
|
||||
m = 1; //Multiplier
|
||||
|
||||
while (timeArr.length > 0) {
|
||||
//Iterate through time segments starting from the seconds,
|
||||
s += m * timeArr.pop(); //multiply as appropriate, and add to seconds total,
|
||||
|
||||
m *= 60; //increase multiplier.
|
||||
}
|
||||
|
||||
return s;
|
||||
};
|
||||
|
||||
return TrimmingControls;
|
||||
}(Plugin); // Define default values for the plugin's `state` object here.
|
||||
|
||||
|
||||
TrimmingControls.defaultState = {}; // Include the version number.
|
||||
|
||||
TrimmingControls.VERSION = version; // Register the plugin with video.js.
|
||||
|
||||
videojs.registerPlugin('trimmingControls', TrimmingControls);
|
||||
|
||||
return TrimmingControls;
|
||||
|
||||
})));
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,168 @@
|
||||
var desertBusStart = new Date("1970-01-01T00:00:00Z");
|
||||
|
||||
pageSetup = function() {
|
||||
//Get values from ThrimShim
|
||||
if(/id=/.test(document.location.search)) {
|
||||
var rowId = /id=(.*)(?:&|$)/.exec(document.location.search)[1];
|
||||
fetch("/thrimshim/"+rowId).then(data => data.json()).then(function (data) {
|
||||
if (!data) {
|
||||
alert("No video available for stream.");
|
||||
return;
|
||||
}
|
||||
//data = testThrimShim;
|
||||
desertBusStart = new Date(data.bustime_start);
|
||||
document.getElementById("hiddenSubmissionID").value = data.id;
|
||||
document.getElementById("StreamName").value = data.video_channel ? data.video_channel:document.getElementById("StreamName").value;
|
||||
document.getElementById("StreamStart").value = data.event_start;
|
||||
document.getElementById("BusTimeStart").value = (new Date(data.event_start+"Z") < desertBusStart ? "-":"") + videojs.formatTime(Math.abs((new Date(data.event_start+"Z") - desertBusStart)/1000), 600.01).padStart(7, "0:");
|
||||
document.getElementById("StreamEnd").value = data.event_end;
|
||||
document.getElementById("BusTimeEnd").value = (new Date(data.event_end+"Z") < desertBusStart ? "-":"") + videojs.formatTime(Math.abs((new Date(data.event_end+"Z") - desertBusStart)/1000), 600.01).padStart(7, "0:");
|
||||
document.getElementById("VideoTitle").value = data.video_title ? data.video_title:document.getElementById("VideoTitle").value;
|
||||
document.getElementById("VideoDescription").value = data.video_description ? data.video_description:data.description;
|
||||
|
||||
loadPlaylist(data.video_start, data.video_end);
|
||||
});
|
||||
}
|
||||
else {
|
||||
document.getElementById('SubmitButton').disabled = true;
|
||||
|
||||
var startOfHour = new Date(new Date().setMinutes(0,0,0));
|
||||
document.getElementById("StreamStart").value = new Date(startOfHour.getTime() - 1000*60*60).toISOString().substring(0,19);
|
||||
document.getElementById("StreamEnd").value = startOfHour.toISOString().substring(0,19);
|
||||
|
||||
loadPlaylist();
|
||||
}
|
||||
};
|
||||
|
||||
loadPlaylist = function(startTrim, endTrim) {
|
||||
var playlist = "/playlist/" + document.getElementById("StreamName").value + ".m3u8";
|
||||
|
||||
if(document.getElementById("BusTimeToggleBus").checked) {
|
||||
var streamStart = desertBusStart;
|
||||
var busTimeStart = document.getElementById("BusTimeStart").value;
|
||||
var busTimeEnd = document.getElementById("BusTimeEnd").value;
|
||||
|
||||
//Convert BusTime to milliseconds from start of stream
|
||||
busTimeStart = (parseInt(busTimeStart.split(':')[0]) + busTimeStart.split(':')[1]/60) * 1000 * 60 * 60;
|
||||
busTimeEnd = (parseInt(busTimeEnd.split(':')[0]) + busTimeEnd.split(':')[1]/60) * 1000 * 60 * 60;
|
||||
|
||||
document.getElementById("StreamStart").value = new Date(streamStart.getTime() + busTimeStart).toISOString().substring(0,19);
|
||||
document.getElementById("StreamEnd").value = new Date(streamStart.getTime() + busTimeEnd).toISOString().substring(0,19);
|
||||
}
|
||||
|
||||
var streamStart = document.getElementById("StreamStart").value ? "start="+document.getElementById("StreamStart").value:null;
|
||||
var streamEnd = document.getElementById("StreamEnd").value ? "end="+document.getElementById("StreamEnd").value:null;
|
||||
var queryString = (streamStart || streamEnd) ? "?" + [streamStart, streamEnd].filter((a) => !!a).join("&"):"";
|
||||
|
||||
setupPlayer(playlist + queryString, startTrim, endTrim);
|
||||
|
||||
//Get quality levels for advanced properties.
|
||||
document.getElementById('qualityLevel').innerHTML = "";
|
||||
fetch('/files/' + document.getElementById('StreamName').value).then(data => data.json()).then(function (data) {
|
||||
if (!data.length) {
|
||||
console.log("Could not retrieve quality levels");
|
||||
return;
|
||||
}
|
||||
var qualityLevels = data.sort().reverse();
|
||||
qualityLevels.forEach(function(level, index) {
|
||||
document.getElementById('qualityLevel').innerHTML += '<option value="'+level+'" '+(index==0 ? 'selected':'')+'>'+level+'</option>';
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
thrimbletrimmerSubmit = function() {
|
||||
document.getElementById('SubmitButton').disabled = true;
|
||||
if(player.trimmingControls().options.startTrim >= player.trimmingControls().options.endTrim) {
|
||||
alert("End Time must be greater than Start Time");
|
||||
document.getElementById('SubmitButton').disabled = false;
|
||||
} else {
|
||||
var discontinuities = mapDiscontinuities();
|
||||
|
||||
var wubData = {
|
||||
video_start:getRealTimeForPlayerTime(discontinuities, player.trimmingControls().options.startTrim).replace('Z',''),
|
||||
video_end:getRealTimeForPlayerTime(discontinuities, player.trimmingControls().options.endTrim).replace('Z',''),
|
||||
video_title:document.getElementById("VideoTitle").value,
|
||||
video_description:document.getElementById("VideoDescription").value,
|
||||
allow_holes:String(document.getElementById('AllowHoles').checked),
|
||||
upload_location:document.getElementById('uploadLocation').value,
|
||||
video_channel:document.getElementById("StreamName").value,
|
||||
video_quality:document.getElementById('qualityLevel').options[document.getElementById('qualityLevel').options.selectedIndex].value,
|
||||
uploader_whitelist:(document.getElementById('uploaderWhitelist').value ? document.getElementById('uploaderWhitelist').value.split(','):null),
|
||||
state:"EDITED",
|
||||
token: user.getAuthResponse().id_token
|
||||
};
|
||||
// state_columns = ['state', 'uploader', 'error', 'video_link']
|
||||
console.log(wubData);
|
||||
console.log(JSON.stringify(wubData));
|
||||
|
||||
//Submit to thrimshim
|
||||
var rowId = /id=(.*)(?:&|$)/.exec(document.location.search)[1];
|
||||
fetch("/thrimshim/"+rowId, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(wubData)
|
||||
})
|
||||
.then(response => { if (!response.ok) { throw Error(response.statusText); }; return response; })
|
||||
.then(data => { console.log(data); setTimeout(() => { window.location.href = '/thrimbletrimmer/dashboard.html'; }, 500); })
|
||||
.catch(error => { console.log(error); alert(error); });
|
||||
}
|
||||
};
|
||||
|
||||
thrimbletrimmerDownload = function() {
|
||||
document.getElementById('SubmitButton').disabled = true;
|
||||
if(player.trimmingControls().options.startTrim >= player.trimmingControls().options.endTrim) {
|
||||
alert("End Time must be greater than Start Time");
|
||||
document.getElementById('SubmitButton').disabled = false;
|
||||
} else {
|
||||
var discontinuities = mapDiscontinuities();
|
||||
|
||||
var downloadStart = getRealTimeForPlayerTime(discontinuities, player.trimmingControls().options.startTrim);
|
||||
var downloadEnd = getRealTimeForPlayerTime(discontinuities, player.trimmingControls().options.endTrim);
|
||||
|
||||
var targetURL = "/cut/" + document.getElementById("StreamName").value +
|
||||
"/"+document.getElementById('qualityLevel').options[document.getElementById('qualityLevel').options.selectedIndex].value+".ts" +
|
||||
"?start=" + downloadStart +
|
||||
"&end=" + downloadEnd +
|
||||
"&allow_holes=" + String(document.getElementById('AllowHoles').checked);
|
||||
console.log(targetURL);
|
||||
document.getElementById('outputFile').src = targetURL;
|
||||
}
|
||||
};
|
||||
|
||||
thrimbletrimmerManualLink = function() {
|
||||
var rowId = /id=(.*)(?:&|$)/.exec(document.location.search)[1];
|
||||
fetch("/thrimshim/manual-link/"+rowId, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
link: document.getElementById("ManualLink").value,
|
||||
token: user.getAuthResponse().id_token
|
||||
})
|
||||
})
|
||||
.then(response => { if (!response.ok) { throw Error(response.statusText); }; return response; })
|
||||
.then(data => { console.log(data); setTimeout(() => { alert("Manual link set"); }, 500); })
|
||||
.catch(error => { console.log(error); alert(error); });
|
||||
};
|
||||
|
||||
thrimbletrimmerResetLink = function() {
|
||||
var rowId = /id=(.*)(?:&|$)/.exec(document.location.search)[1];
|
||||
if(confirm('Are you sure you want to reset this event?')) {
|
||||
fetch("/thrimshim/reset/"+rowId, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({token: user.getAuthResponse().id_token})
|
||||
})
|
||||
.then(response => { if (!response.ok) { throw Error(response.statusText); }; return response; })
|
||||
.then(data => { console.log(data); setTimeout(() => { window.location.reload() }, 500); })
|
||||
.catch(error => { console.log(error); alert(error); });
|
||||
}
|
||||
};
|
@ -0,0 +1,77 @@
|
||||
document.addEventListener('keypress', (event) => {
|
||||
//if(event.target.nodeName == "BODY") {
|
||||
if(event.target.nodeName !== "INPUT" && event.target.nodeName !== "TEXTAREA") {
|
||||
switch(event.key) {
|
||||
case "j":
|
||||
player.currentTime(player.currentTime()-10);
|
||||
break;
|
||||
case "k":
|
||||
player.paused() ? player.play():player.pause();
|
||||
break;
|
||||
case "l":
|
||||
player.currentTime(player.currentTime()+10);
|
||||
break;
|
||||
case ",":
|
||||
player.currentTime(player.currentTime()-0.1);
|
||||
break;
|
||||
case ".":
|
||||
player.currentTime(player.currentTime()+0.1);
|
||||
break;
|
||||
case "i":
|
||||
player.trimmingControls().updateTrimTimes(player.currentTime(), player.trimmingControls().options.endTrim);
|
||||
break;
|
||||
case "o":
|
||||
player.trimmingControls().updateTrimTimes(player.trimmingControls().options.startTrim, player.currentTime());
|
||||
break;
|
||||
case "0":
|
||||
player.currentTime(0);
|
||||
break;
|
||||
case "1":
|
||||
player.currentTime(player.duration()*0.1);
|
||||
break;
|
||||
case "2":
|
||||
player.currentTime(player.duration()*0.2);
|
||||
break;
|
||||
case "3":
|
||||
player.currentTime(player.duration()*0.3);
|
||||
break;
|
||||
case "4":
|
||||
player.currentTime(player.duration()*0.4);
|
||||
break;
|
||||
case "5":
|
||||
player.currentTime(player.duration()*0.5);
|
||||
break;
|
||||
case "6":
|
||||
player.currentTime(player.duration()*0.6);
|
||||
break;
|
||||
case "7":
|
||||
player.currentTime(player.duration()*0.7);
|
||||
break;
|
||||
case "8":
|
||||
player.currentTime(player.duration()*0.8);
|
||||
break;
|
||||
case "9":
|
||||
player.currentTime(player.duration()*0.9);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// const keyName = event.key;
|
||||
// console.log('keypress event\n\n' + 'key: ' + keyName);
|
||||
// console.log(event.target.nodeName);
|
||||
});
|
||||
|
||||
//Arrow keys only detected on keydown, keypress only works in "some" browsers
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if(event.target.nodeName !== "INPUT" && event.target.nodeName !== "TEXTAREA") {
|
||||
switch(event.keyCode) {
|
||||
case 37:
|
||||
player.currentTime(player.currentTime()-5);
|
||||
break;
|
||||
case 39:
|
||||
player.currentTime(player.currentTime()+5);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
});
|
@ -0,0 +1,92 @@
|
||||
var player = null;
|
||||
|
||||
function setupPlayer(source, startTrim, endTrim) {
|
||||
document.getElementById("my-player").style.display = "";
|
||||
//Make poster of DB logo in correct aspect ratio, to control initial size of fluid container.
|
||||
var options = {
|
||||
sources: [{ src: source }],
|
||||
liveui: true,
|
||||
//fluid:true,
|
||||
controls:true,
|
||||
autoplay:false,
|
||||
width:1280,
|
||||
height:420,
|
||||
playbackRates: [0.5, 1, 1.25, 1.5, 2],
|
||||
inactivityTimeout: 0,
|
||||
controlBar: {
|
||||
fullscreenToggle: false,
|
||||
volumePanel: {
|
||||
inline: false
|
||||
}
|
||||
}
|
||||
};
|
||||
if(player) { //Destroy and recreate the player if it already exists.
|
||||
player.dispose();
|
||||
document.getElementById("EditorContainer").innerHTML = `
|
||||
<video id="my-player" class="video-js" controls disablePictureInPicture preload="auto">
|
||||
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
|
||||
</video>
|
||||
`;
|
||||
}
|
||||
player = videojs('my-player', options, function onPlayerReady() {
|
||||
videojs.log('Your player is ready!');
|
||||
|
||||
// In this context, `this` is the player that was created by Video.js.
|
||||
this.on('ready', function() {
|
||||
//this.play();
|
||||
});
|
||||
|
||||
this.vhs.playlists.on('loadedmetadata', function() {
|
||||
// setTimeout(function() { player.play(); }, 1000);
|
||||
player.hasStarted(true); //So it displays all the controls.
|
||||
stream_start = player.vhs.playlists.master.playlists.filter(playlist => typeof playlist.discontinuityStarts !== "undefined")[0].dateTimeObject;
|
||||
startTrim = startTrim ? (new Date(startTrim+"Z")-stream_start)/1000:0;
|
||||
endTrim = endTrim ? (new Date(endTrim+"Z")-stream_start)/1000:player.duration();
|
||||
var trimmingControls = player.trimmingControls({ startTrim:startTrim, endTrim:endTrim });
|
||||
});
|
||||
|
||||
// How about an event listener?
|
||||
this.on('ended', function() {
|
||||
videojs.log('Awww...over so soon?!');
|
||||
});
|
||||
|
||||
this.on('error', function() {
|
||||
videojs.log("Could not load video stream");
|
||||
alert("No video available for stream.");
|
||||
document.getElementById("my-player").style.display = "none";
|
||||
})
|
||||
});
|
||||
var hlsQS = player.hlsQualitySelector();
|
||||
//var trimmingControls = player.trimmingControls({ startTrim:(startTrim ? startTrim:0), endTrim:(endTrim ? endTrim:player.duration()) });
|
||||
}
|
||||
|
||||
mapDiscontinuities = function() {
|
||||
var playlist = player.vhs.playlists.master.playlists.filter(playlist => typeof playlist.discontinuityStarts !== "undefined")[0]; //Only one of the playlists will have the discontinuity or stream start objects, and it's not necessarily the first one or the source one.
|
||||
var discontinuities = playlist.discontinuityStarts.map(segmentIndex => { return {segmentIndex:segmentIndex, segmentTimestamp:playlist.segments[segmentIndex].dateTimeObject, playbackIndex:null}; });
|
||||
//var lastDiscontinuity = Math.max(...playlist.discontinuityStarts);
|
||||
var lastDiscontinuity = playlist.discontinuityStarts.slice(-1).pop(); //Assumes discontinuities are sorted in ascending order.
|
||||
|
||||
var durationMarker = 0;
|
||||
for (var index = 0; index <= lastDiscontinuity; index++) {
|
||||
let segment = playlist.segments[index];
|
||||
if(segment.discontinuity) {
|
||||
discontinuities.find(discontinuity => discontinuity.segmentIndex == index).playbackIndex = durationMarker;
|
||||
}
|
||||
durationMarker += segment.duration;
|
||||
}
|
||||
|
||||
return discontinuities;
|
||||
};
|
||||
|
||||
getRealTimeForPlayerTime = function(discontinuities, playbackIndex) {
|
||||
var streamStart = player.vhs.playlists.master.playlists.filter(playlist => typeof playlist.dateTimeObject !== "undefined")[0].dateTimeObject; //Only one of the playlists will have the discontinuity or stream start objects, and it's not necessarily the first one or the source one.
|
||||
|
||||
//Find last discontinuity before playbackIndex
|
||||
var lastDiscontinuity = discontinuities.filter(discontinuity => discontinuity.playbackIndex < playbackIndex).slice(-1).pop();
|
||||
if(lastDiscontinuity) {
|
||||
streamStart = lastDiscontinuity.segmentTimestamp;
|
||||
playbackIndex -= lastDiscontinuity.playbackIndex;
|
||||
}
|
||||
|
||||
return new Date(streamStart.getTime()+playbackIndex*1000).toISOString();
|
||||
};
|
@ -0,0 +1,37 @@
|
||||
html, body {
|
||||
height:100%;
|
||||
margin:0px;
|
||||
background-color:darkgrey;
|
||||
}
|
||||
body.ultrawide > div { max-width:100% !important; }
|
||||
body.ultrawide .my-player-dimensions { width:100% !important; }
|
||||
|
||||
.video-js .vjs-control-bar {
|
||||
background-color:#2b333f;
|
||||
}
|
||||
.video-js .vjs-time-control {
|
||||
display: block;
|
||||
}
|
||||
.video-js .vjs-remaining-time {
|
||||
display: none;
|
||||
}
|
||||
.video-js .vjs-current-time.vjs-time-control, .video-js .vjs-duration.vjs-time-control {
|
||||
padding:0em;
|
||||
}
|
||||
.video-js .vjs-progress-control .vjs-mouse-display {
|
||||
z-index: 100;
|
||||
}
|
||||
.vjs-menu-button-popup .vjs-menu {
|
||||
bottom:-3px;
|
||||
}
|
||||
|
||||
#EditorDetailsPane {
|
||||
margin-top:100px;
|
||||
}
|
||||
#VideoTitle, #VideoDescription {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
*:focus {
|
||||
outline:none;
|
||||
}
|
Loading…
Reference in New Issue