Skip to content

Instantly share code, notes, and snippets.

@websemantics
Created October 21, 2015 13:23
Show Gist options
  • Save websemantics/94f0fcb43458ccff5535 to your computer and use it in GitHub Desktop.
Save websemantics/94f0fcb43458ccff5535 to your computer and use it in GitHub Desktop.
A Polymer 0.5 element for Vimeo video uploads that supports Browser and Cordova apps.
<!--
* vimeo-upload
*
* The `vimeo-upload` element enabling you to upload videos to Vimeo
*
* Examples
*
* Manual upload with a `Video Upload` button once a video file is selected:
*
* <vimeo-upload></vimeo-upload>
*
* Automatic upload on video file select, with a custom title, and 'unlisted' privacy:
*
* <vimeo-upload
* auto
* videoTitle="My Awesome Video"
* privacyStatus="unlisted">
* </vimeo-upload>
*
* @link http://websemantics.ca
* @author WebSemantics, Inc. <hi@websemantics.ca>
* @author Adnan Sagar, PhD <msagar@websemantics.ca>
* @copyright 2015 WebSemantics, All rights reserved.
-->
<polymer-element name="vimeo-upload" attributes="auto selectedFile isCordovaApp accessToken videoTitle description privacyStatus videoId url">
<template>
<div style="display: {{ authenticated ? 'block' : 'none' }}">
<template if="{{!isCordovaApp}}">
<input type="file" id="file" class="button" accept="video/*" on-change="{{handleFileChanged}}">
</template>
<template if="{{isCordovaApp}}">
<paper-button raised on-click="{{chooseFile}}">Choose File</paper-button>
</template>
<div style="width:140px;padding-top:10px">
<paper-button raised on-click="{{handleUploadClicked}}" class="upload" style="display: {{ auto ? 'none' : 'block' }}" disabled?="{{ !selectedFile }}">Upload</paper-button>
</div>
</div>
</template>
<script>
(function() {
var STATUS_POLLING_ITERVAL_MILLIS = 60 * 1000; // One minute.
Polymer('vimeo-upload', {
/**
* Fired when the upload begins.
*
* `e.detail` is set to the
* [file](https://developer.mozilla.org/en-US/docs/Web/API/File)
* being uploaded.
*
* @event vimeo-upload-start
* @param {Object} e Event parameters.
*/
/**
* Fired while the upload is in progress.
*
* `e.detail.progressEvent` is set to the corresponding
* [XMLHttpRequestProgressEvent](http://www.w3.org/TR/progress-events/).
*
* `e.detail.estimatedSecondsRemaining` is set to an estimate of the time remaining
* in the upload, based on the average upload speed so far.
*
* `e.detail.bytesPerSecond` is set to the average number of bytes sent per second
* sent so far.
*
* `e.fractionComplete` is set to the fraction of the upload that's complete, in the range [0, 1].
*
* @event vimeo-upload-progress
* @param {Object} e Event parameters.
*/
/**
* Fired when Vimeo upload has failed.
*
* Since the actual upload failed, it's not possible for the Vimeo server to
* attempt to process the video, and no `vimeo-processing-poll`
* events will be fired.
*
* `e.detail` is set to a string explaining what went wrong.
*
* @event vimeo-upload-fail
* @param {Object} e Event parameters
*/
/**
* Fired when video upload has completed, and Vimeo has begun processing the video.
*
* At this point, the video is not yet playable, and there is no guarantee that
* the server-side Vimeo processing will succeed.
*
* One or more `vimeo-processing-poll` events will then be fired after this event,
* followed by either a `vimeo-processing-complete` or `vimeo-processing-fail`.
*
* `e.detail` is set to the Vimeo video id of the video.
*
* @event vimeo-upload-complete
* @param {Object} e Event parameters.
*/
/**
* Fired while server-side processing is in progress.
*
* Server-side processing can take an
* [unpredictable amount of time](https://support.google.com/youtube/answer/71674?hl=en&ref_topic=2888603),
* and these events will be periodically fired each time the processing status is polled.
*
* `e.detail` is set to a
* [status](https://developers.google.com/youtube/v3/docs/videos#status)
* object.
*
* @event vimeo-processing-poll
* @param {Object} e Event parameters
*/
/**
* Fired when server-side processing is successful and the video is
* available for playback on Vimeo.
*
* The video can be played at `http://youtu.be/VIDEO_ID` and can be
* embedded using the
* [`google-youtube`](https://github.com/GoogleWebComponents/google-youtube) element.
*
* `e.detail` is set to the Vimeo video id of the video.
*
* @event vimeo-processing-complete
* @param {Object} e Event parameters
*/
/**
* Fired when the video
* [failed transcoding](https://support.google.com/youtube/topic/2888603?hl=en&ref_topic=16547)
* and can't be played on Vimeo.
*
* `e.detail` is set to a
* [status](https://developers.google.com/youtube/v3/docs/videos#status)
* object which has more details about the failure.
*
* @event vimeo-processing-fail
* @param {Object} e Event parameters
*/
/**
* Whether files should be automatically uploaded.
*
* @attribute auto
* @type boolean
* @default false
*/
auto: false,
/**
* Whether the user has authenticated or not (Yes always)
*
* @attribute authenticated
* @type boolean
*/
authenticated: true,
/**
* The title for the new Vimeo video.
*
* @attribute title
* @type string
* @default 'Untitled Video'
*/
videoTitle: 'Untitled Video',
/**
* The description for the new Vimeo video.
*
* @attribute description
* @type string
* @default 'Uploaded via a web component! Check out https://github.com/GoogleWebComponents/vimeo-upload'
*/
description: 'Uploaded via a web component! Check out https://github.com/GoogleWebComponents/vimeo-upload',
/**
* The [privacy setting](https://support.google.com/youtube/answer/157177?hl=en)
* for the new video.
*
* Valid values are 'public', 'private', and 'unlisted'.
*
* @attribute privacyStatus
* @type string
* @default 'public'
*/
privacyStatus: 'public',
/**
* The file location in case of a Cordova app
*
* @attribute realUrl
* @type string
* @default 'public'
*/
realUrl: null,
/**
* The id of the new video.
*
* This is set as soon as a `vimeo-upload-complete` event is fired.
*
* @attribute videoId
* @type string
* @default ''
*/
videoId: '',
uploadStartTime: 0,
created: function() {
},
ready: function() {
// summery:
// Get the upload ticket first
},
/**
* Uploads a video file to Vimeo.
*
* `this.accessToken` must already be set to a valid OAuth 2 access token.
*
* @method uploadFile
* @param {object} file File object corresponding to the video to upload.
*/
uploadFile: function(file) {
// var metadata = {
// snippet: {
// title: this.videoTitle,
// description: this.description
// },
// status: {
// privacyStatus: this.privacyStatus
// }
// };
var uploader = new MediaUploader({
baseUrl: this.url,
file: file,
token: this.accessToken,
isCordovaApp : this.isCordovaApp,
realUrl : this.realUrl,
// metadata: metadata,
// params: {
// part: Object.keys(metadata).join(',')
// },
onError: function(data) {
var message = data;
// Assuming the error is raised by the Vimeo API, data will be
// a JSON string with error.message set. I am not 100% sure that's the
// only time onError will be raised, though.
try {
var errorResponse = JSON.parse(data);
message = errorResponse.error;
this.fire('handle_error', {error : message});
} finally {
this.fire('vimeo-upload-fail', message);
}
}.bind(this),
onProgress: function(data) {
var currentTime = Date.now();
var bytesUploaded = data.loaded;
var totalBytes = data.total;
// The times are in millis, so we need to divide by 1000 to get seconds.
var bytesPerSecond = bytesUploaded / ((currentTime - this.uploadStartTime) / 1000);
var estimatedSecondsRemaining = (totalBytes - bytesUploaded) / bytesPerSecond;
var fractionComplete = bytesUploaded / totalBytes;
this.fire('vimeo-upload-progress', {
progressEvent: data,
bytesPerSecond: bytesPerSecond,
estimatedSecondsRemaining: estimatedSecondsRemaining,
fractionComplete: fractionComplete
});
}.bind(this),
onComplete: function(videoId) {
this.fire('vimeo-upload-complete', videoId);
this.videoId = videoId;
// this.pollForVideoStatus();
}.bind(this)
});
this.fire('vimeo-upload-start', file);
// This won't correspond to the *exact* start of the upload, but it should be close enough.
this.uploadStartTime = Date.now();
uploader.upload();
},
chooseFile: function() {
// Summery
// This function handles Cordova apps choose file action
var self = this;
var pictureSource = navigator.camera.PictureSourceType;
var destinationType = navigator.camera.DestinationType;
var source = pictureSource.PHOTOLIBRARY;
// Retrieve video file location from specified source
navigator.camera.getPicture(
// Success
function(imageURI){
self.handleCordovaFileChanged(imageURI);
},
// Fail
function(message){
this.fire('handle_error', {error : message});
},
{ quality: 50,
destinationType: destinationType.FILE_URI,
mediaType: navigator.camera.MediaType.VIDEO,
sourceType: source });
},
handleCordovaFileChanged: function(imageURI) {
// summery:
// Process the file to get metadata, type size etc
var self = this;
window.resolveLocalFileSystemURL(imageURI,
// Success
function(fileEntry){
var realUrl = fileEntry.toURL();
fileEntry.file(function(file) {
self.realUrl = realUrl;
self.handleFileChanged(null, file);
});
},
// Fail
function(e){
// Do nothing
});
},
handleFileChanged: function(e, file) {
this.selectedFile = file || this.$.file.files[0];
this.fire('vimeo-file-changed', this.selectedFile);
if (this.auto) {
this.uploadFile(this.selectedFile);
}
},
handleUploadClicked: function() {
this.uploadFile(this.selectedFile);
},
pollForVideoStatus: function() {
// summery:
// TO BE IMPLEMENTED! Adnan @ Dec 25 2014
this.gapi.client.request({
path: '/youtube/v3/videos',
params: {
part: 'status',
id: this.videoId
},
callback: function(response) {
if (response.error) {
// Not exactly sure how to handle this one, since it means the status polling failed.
setTimeout(this.pollForVideoStatus.bind(this), STATUS_POLLING_ITERVAL_MILLIS);
} else {
var status = response.items[0].status;
switch (status.uploadStatus) {
// This is a non-final status, so we need to poll again.
case 'uploaded':
this.fire('vimeo-processing-poll', status);
setTimeout(this.pollForVideoStatus.bind(this), STATUS_POLLING_ITERVAL_MILLIS);
break;
// The video was successfully transcoded and is available.
case 'processed':
this.fire('vimeo-processing-complete', this.videoId);
break;
// All other statuses indicate a permanent transcoding failure.
default:
this.fire('vimeo-processing-fail', status);
break;
}
}
}.bind(this)
});
}
});
})();
</script>
</polymer-element>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment