Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
525 lines (494 sloc) 20.1 KB
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Zencoder Dropzone</title>
<!-- jQuery Include -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- Filepicker.io Include -->
<script type="text/javascript" src="//api.filepicker.io/v1/filepicker.js"></script>
<style type='text/css'>
html, body {
margin: 0;
padding: 0;
background-color: #1F333E;
color: #fff;
height: 100%;
font-family: 'HelveticaNeue-UltraLight','Helvetica Neue UltraLight','Helvetica Neue','Open-Light',sans-serif;
}
#content {
height: 100%;
width: 100%;
}
/* Basic styles */
.hidden { display: none }
h1 { font-weight: 300 }
/* We need this structure for completely centered text */
.container {
height: 100%;
width: 100%;
display: table;
}
.text {
display: table-cell;
vertical-align: middle;
text-align: center;
}
/* Setting height/width to 100% will cause scrollbars, so absolutely position to the edges */
.full {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 75px;
}
/* Setup */
#setup {
/* Nothing special here yet */
}
#setup input {
width: 80%;
height: 50px;
text-align: center;
font-size: 40px;
color: #1E323D;
background-color: #609FC1;
border: 3px solid #fff;
}
#setup input:focus { outline: none; }
#setup input.error { border-color: #EF0000;}
.error { color: #EF0000; }
/* Styling placeholder text still takes vendor prefixes :( */
::-webkit-input-placeholder {
color: #47758F;
}
:-moz-placeholder { /* Firefox 18- */
color: #47758F;
}
::-moz-placeholder { /* Firefox 19+ */
color: #47758F;
}
:-ms-input-placeholder {
color: #47758F;
}
/* Drop Zone */
#dropZone {
cursor: pointer;
}
.normal {
border: 3px dashed #fff;
background-color: #47758F;
}
.over {
border: 3px solid #fff;
background-color: #609FC1;
}
/* History Bar */
#historyBar {
position: absolute;
bottom: 0px;
left: 0;
right: 0;
height: 75px;
background-color: #1E323D;
overflow-x: scroll;
}
#navigation {
position: absolute;
right: 0;
bottom: 0;
height: 75px;
width: 25px;
background-color: #609FC1;
}
.navigationBtns {
width: 25px;
position: absolute;
left: 0;
bottom: 0;
z-index: 2;
}
.navigationBtns img { margin-top: 5px;}
.navigationContent {
display: none;
z-index: 1;
}
.navigationContent a { color: #fff; }
.navigationContent a:visited { color: #fff; }
/* Video List */
#videoList {
text-align: left;
width: 100%;
white-space: nowrap
}
#videoList a {
padding-left: 10px;
display: inline-block;
}
#videoList img {
height: 50px;
width: auto;
}
#videoList a:last-child { padding-right: 35px; }
#videoList h3 { margin-left: 20px;}
/* Request Editor */
.dialog {
position: absolute;
left: 50%;
top: 50px;
border: 1px solid #eee;
background: #fff;
z-index: 1000;
color: #000;
}
.dialog .content {
padding: 15px 20px;
}
.dialog h3 {
padding: 0;
margin: 0;
}
.dialog p {
margin: 0;
padding: 0;
font-size: .9em;
}
#requestEditor {
width: 400px;
margin-left: -200px;
}
#requestEditor textarea {
width: 394px;
height: 400px;
}
#requestEditor .controls {
float: right;
}
</style>
<script type="text/javascript">
var Zencoder = {};
Zencoder.base_url = "https://app.zencoder.com/api/v2/";
Zencoder.base_job_url = function(id) {
var url = 'https://app.zencoder.com/jobs/' + id;
return url;
}
// Pull some stuff out of localStorage if available
Zencoder.jobs = localStorage.jobs ? JSON.parse(localStorage.jobs) : null;
/* Set up the default template if there isn't one in localStorage */
if (localStorage.request_template) {
Zencoder.request_template = JSON.parse(localStorage.request_template);
} else {
/* Default request template. Keep it simple, but with a thumb for the history bar. */
var request = {
input: 'Ignore -- This is filled in upon successful upload',
}
localStorage.request_template = JSON.stringify(request)
Zencoder.request_template = request;
}
// Now it's time to build a request off of the template
// We want to add at least one thumbnail if none are in the request already
Zencoder.request = function(file) {
Zencoder.request_template.input = file.url
if (!Zencoder.request_template.outputs || !Zencoder.request_template.outputs[0].thumbnails) {
// If there is no outputs array, create one first (with a hash inside), then add thumbnails.
if (!Zencoder.request_template.outputs) { Zencoder.request_template.outputs = [{}] }
Zencoder.request_template.outputs[0].thumbnails = {
number: 1,
public: true
}
}
return Zencoder.request_template;
}
$(function(){
// Check for an API key in localStorage first.
Zencoder.api_key = localStorage.getItem('zencoder-api-key');
if (Zencoder.api_key == null) {
$('#api-key-form').submit(function(e){
e.preventDefault();
Zencoder.api_key = $('#api-key').val();
localStorage.setItem('zencoder-api-key', Zencoder.api_key);
// Once the API key is set, show the drop zone
$('#setup').fadeOut(function(){
$('#dropZone, #historyBar, #navigation').fadeIn();
Zencoder.utils.listJobs(Zencoder.jobs);
});
});
} else {
$('#setup').hide();
$('#dropZone, #historyBar, #navigation').fadeIn();
Zencoder.utils.listJobs(Zencoder.jobs);
}
// Set your Filepicker.io API key
filepicker.setKey('AACRnG3VDSwyJfHqeN06ez');
// We'll be referencing these elements a few times,
// so we might as well put them in vars.
var $dz = $('#dropZone');
var $dzResult = $('#localDropResult');
var acceptedExtensions = ['3g2','3gp','3gp2','3gpp','3gpp2','aac','ac3','eac3','ec3','f4a','f4b','f4v','flv','highwinds','m4a','m4b','m4r','m4v','mkv','mov','mp3','mp4','oga','ogg','ogv','ogx','ts','webm','wma','wmv'];
// Set up our Filepicker.io Drop Pane.
filepicker.makeDropPane($dz, {
multiple: false,
extensions: acceptedExtensions,
location: 's3',
dragEnter: function() {
$dz.find('h1').text('Drop to upload');
$dz.removeClass('normal').addClass('over');
},
dragLeave: function() {
$dz.find('h1').text('Drop files here');
$dz.removeClass('over').addClass('normal');
},
onSuccess: function(fpfiles) {
$dz.find('h1').text('File uploaded. Encoding...');
$dz.find('p').text('');
Zencoder.utils.createJob(fpfiles[0], $dz);
},
onError: function(type, message) {
$dzResult.text('('+type+') '+ message);
},
onProgress: function(percentage) {
$dz.find('h1').text('Uploading ('+percentage+'%)');
}
});
// We've got a drop pane...but what if we also made the whole thing clickable for normal upload
$dz.click(function(e){
filepicker.pickAndStore({extensions:acceptedExtensions,openTo:'COMPUTER'},{location:"S3"}, function(fpfiles){
$dz.find('h1').text('File uploaded. Encoding...');
$dz.find('p').text('');
Zencoder.utils.createJob(fpfiles[0], $dz);
});
});
// SETTINGS INTERFACE
// Pull out the settings board
$('#settingsBtn').click(function(e) {
e.preventDefault();
var toggleSettings = $('#navigation').width() == 200 ? '25px' : '200px';
$('#navigation').animate(
{ width: toggleSettings },
function() {
$('.navigationContent').toggle();
}
);
});
/* Clear all the data and refresh the page */
$('#clearData').click(function(e) {
e.preventDefault();
localStorage.clear();
document.location.reload();
});
/* Request Editor Modal */
$('#showRequestEditor').click(function(e){
e.preventDefault();
// Show the editor and populate it with what's in localStorage
$('#requestEditor').fadeIn(function(){
$('#requestEditor textarea').val(JSON.stringify(Zencoder.request_template));
$('#cancelEditRequest').click(function(e){
e.preventDefault();
$('#requestEditor').fadeOut();
});
$('#saveRequest').click(function(e){
e.preventDefault();
var newRequest = $('#requestEditor textarea').val();
var updateResult = Zencoder.utils.updateRequestTemplate(newRequest);
if (updateResult) {
console.log('New template saved...');
$('#requestEditor').fadeOut();
} else {
console.log('Something went wrong...')
}
})
});
})
});
// It's just a demo, but let's put our functions in Zencoder.utils to be good JS citizens anyway.
Zencoder.utils = {};
// Send the create request to Zencoder
Zencoder.utils.createJob = function(file, $element) {
// Let's use $.ajax instead of $.post so we can specify custom headers.
$.ajax({
url: Zencoder.base_url + 'jobs',
type: 'POST',
data: JSON.stringify(Zencoder.request(file)),
headers: { "Zencoder-Api-Key": Zencoder.api_key },
dataType: 'json',
success: function(data) {
console.log(data);
// Once the file is uploaded, start polling Zencoder for progress
Zencoder.utils.pollZencoder(data.id, $element);
},
error: function(data) {
console.log(data);
}
});
}
// Poll the Zencoder API for progress
Zencoder.utils.pollZencoder = function(jobId, $element) {
$.ajax({
url: Zencoder.base_url + 'jobs/' + jobId + '/progress' ,
type: 'GET',
headers: { "Zencoder-Api-Key": Zencoder.api_key },
//dataType: 'json',
success: function(data) {
if (data.state != 'finished') {
console.log(data);
// We don't want to update progress while the job is still queued
if (data.state != 'waiting') {
$element.find('h1').text('Encoding ('+ data.progress.toFixed(2) +'%)');
}
// Since the job isn't finished, wait 3 seconds and poll again
setTimeout(function() { Zencoder.utils.pollZencoder(jobId, $element) }, 3000);
} else {
// Job is finished, so let the user know.
$element.find('h1').html('Finished. <a href="https://app.zencoder.com/jobs/'+ jobId +'">View Job</a>');
$element.find('p').text('You can drag another file or click to encode another at any time.')
Zencoder.utils.updateJobs();
}
},
error: function(data) {
console.log(data)
}
});
}
// Update the Jobs object
Zencoder.utils.updateJobs = function() {
console.log('Checking for new jobs...');
$.ajax({
url: Zencoder.base_url + 'jobs/',
type: 'GET',
headers: { "Zencoder-Api-Key": Zencoder.api_key },
success: function(data) {
// We'll be comparing values from Zencoder.jobs and data.job, so make sure exist first (and have things in them).
// If they do, see if the newest local job is the same as the newest retrieved job.
if (data && Zencoder.jobs && (data.length > 0) && (Zencoder.jobs.length > 0) && (Zencoder.jobs[0].job.id == data[0].job.id)) {
console.log('Jobs are already up to date...')
} else {
// There are either no jobs or new jobs
if (data.length == 0) { // No jobs were returned
$('#videoList').html('<h3>You have no jobs! Go ahead and upload one...</h3>');
} else { // New jobs were found, so replace local information with the stuff we just received
Zencoder.jobs = data;
localStorage.setItem('jobs', JSON.stringify(Zencoder.jobs));
Zencoder.utils.listJobs(Zencoder.jobs);
}
}
},
error: function(data) {
var errorMsg;
if (data.status == 401) {
// API key was incorrect...
localStorage.clear();
errorMsg = 'Invalid Zencoder API Key';
} else {
errorMsg = "Something went wrong!"
}
$('#dropZone, #historyBar, #navigation').hide();
// Let the user know what the error was
$('#api-key').addClass('error');
$('.alert').replaceWith('<p class="alert error">'+ errorMsg + '</p>');
$('#setup').fadeIn();
}
});
}
// List the last 50 jobs along the bottom
Zencoder.utils.listJobs = function(jobs) {
if (jobs) {
// There are jobs, so it's time to update them.
// Remove whatever's currently there
$('#videoList').empty();
$.each(jobs, function(index, value){
if (value.job.thumbnails.length == 0) {
// Job didn't have an image, so display a placeholder
$('#videoList').append('<a href="'+ Zencoder.base_job_url(value.job.id) + '/"><img src="./images/place.jpg"/></a>');
} else {
// Job has a thumbnail, so display it.
$('#videoList').append('<a href="'+ Zencoder.base_job_url(value.job.id) +'/"><img src="'+ value.job.thumbnails[0].url +'" onError="this.onerror=null;this.src=\'./images/place.jpg\';"/></a>');
}
});
// updateJobs() is never called directly, so we could have just displayed out of date local info.
this.updateJobs();
} else {
// No jobs, so make sure there isn't anything new with Zencoder
this.updateJobs();
}
}
// Mechanism to update the request template
Zencoder.utils.updateRequestTemplate = function(request) {
if (request) {
try {
// Make sure the new template is valid JSON
var newRequest = JSON.parse(request);
} catch(e) {
console.log(e);
return false;
}
// We know we've got a valid template, so update Zencoder.request_template and localStorage.requestTemplate
localStorage.request_template = JSON.stringify(newRequest);
Zencoder.request_template = newRequest
return true;
}
}
</script>
</head>
<body>
<div id="content">
<!-- Get the user's API key before anything else -->
<div id="setup" class="full">
<div class="container">
<div class="text">
<form id="api-key-form">
<input type="text" id="api-key" placeholder="Zencoder API Key"><br />
<p>Press enter when done.</p>
<p class='alert'></p>
</form>
</div>
</div>
</div>
<!-- Drop zone needs to be hidden by default. We'll unhide it when the API key is put in -->
<div id="dropZone" class="full normal hidden">
<div class="container">
<div class="text">
<h1>Drop files here</h1>
<p>Or click to browse</p>
</div>
</div>
</div>
<!-- Show the last 50 jobs processed -->
<div id="historyBar" class="carousel hidden">
<div class="container">
<div class="text" id="videoList">
<h3 id="no-jobs">Loading recent jobs...</h3>
</div>
</div>
</div>
<!-- Navigation pop out -->
<div id="navigation" class="hidden">
<div class="container">
<div class="text navigationBtns">
<p>
<a href="http://app.zencoder.com/"><img src="./images/outgoing.png"/></a>
<a href="#" id="settingsBtn"><img src="./images/settings.png"/></a>
</p>
</div>
<div class="text navigationContent">
<p>
<a href="#" id="showRequestEditor">Edit Request</a><br/>
<a href="#" id="clearData">Clear All Data</a>
</p>
</div>
</div>
</div>
<!-- Request Editor Modal -->
<div id="requestEditor" class="dialog hidden">
<div class="header">
<h3>Modify Your Request</h3>
</div>
<form>
<textarea></textarea>
<span class="controls">
<a href="#" id="cancelEditRequest">Cancel</a>
<a href="#" id="saveRequest">Update Request</a>
</form>
</div>
</div>
</body>
</html>