Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Bug 821514 - display error message if youtube won't play video #7246

Merged
merged 2 commits into from

3 participants

@davidflanagan

This is the gaia-side patch for bug 821514.

It is based on top of the gaia patches for bug 815791, so this pull request includes two commits. It is only the second commit that we're interested in here, but I don't know how to get github to do that. The first commit will land in bug 815791, and then I can rebase this patch so that it just has the one commit we want.

But the second commit is ready for review, anyway.

@daleharvey
Owner

r+ awesome job

We dont need to split out the pull requests, they are seperate commits so it just means one less merge commit

@daleharvey daleharvey merged commit a2b486b into from
@Pike

This string doesn't confirm with the copy guidelines, see https://etherpad.mozilla.org/gaia-copy-guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
84 apps/video/js/video.js
@@ -6,7 +6,8 @@ var ids = ['player', 'thumbnails', 'overlay', 'overlay-title',
'overlay-text', 'videoControls', 'videoFrame', 'videoBar',
'close', 'play', 'playHead', 'timeSlider', 'elapsedTime',
'video-title', 'duration-text', 'elapsed-text', 'bufferedTime',
- 'slider-wrapper', 'throbber', 'delete-video-button', 'delete-confirmation-button'];
+ 'slider-wrapper', 'throbber', 'delete-video-button',
+ 'delete-confirmation-button'];
ids.forEach(function createElementRef(name) {
dom[toCamelCase(name)] = document.getElementById(name);
@@ -41,10 +42,6 @@ var THUMBNAIL_HEIGHT = 160;
// Enumerating the readyState for html5 video api
var HAVE_NOTHING = 0;
-var activityData; // From an activity call
-var pendingActivity;
-var appStarted = false;
-
var storageState;
var currentOverlay;
@@ -65,9 +62,6 @@ function init() {
storageState = false;
updateDialog();
createThumbnailList();
- if (activityData) {
- startStream();
- }
};
videodb.onscanstart = function() {
@@ -88,9 +82,8 @@ function init() {
event.detail.forEach(videoDeleted);
};
- dom.deleteConfirmationButton.addEventListener('click', deleteSelectedVideoFile, false);
-
- appStarted = true;
+ dom.deleteConfirmationButton.addEventListener('click',
+ deleteSelectedVideoFile, false);
}
function videoAdded(videodata) {
@@ -153,11 +146,11 @@ function videoAdded(videodata) {
dom.thumbnails.addEventListener('contextmenu', function(evt) {
var node = evt.target;
var found = false;
- while (!found && node) {
- if (node.dataset.name) {
+ while (!found && node) {
+ if (node.dataset.name) {
found = true;
selectedVideo = node.dataset.name;
- } else {
+ } else {
node = node.parentNode;
}
}
@@ -208,11 +201,6 @@ function updateDialog() {
}
}
-function startStream() {
- showPlayer(activityData, true);
- activityData = null;
-}
-
function metaDataParser(videofile, callback, metadataError) {
var previewPlayer = document.createElement('video');
@@ -372,12 +360,6 @@ function setVideoPlaying(playing) {
}
}
-function completeActivity(deleteVideo) {
- pendingActivity.postResult({delete: deleteVideo});
- pendingActivity = null;
- dom.thumbnails.classList.remove('hidden');
-}
-
function playerMousedown(event) {
// If we interact with the controls before they fade away,
// cancel the fade
@@ -602,14 +584,9 @@ function playerEnded() {
clearTimeout(endedTimer);
endedTimer = null;
}
- if (pendingActivity) {
- pause();
- dom.player.currentTime = 0;
- setControlsVisibility(true);
- } else {
- dom.player.currentTime = 0;
- document.mozCancelFullScreen();
- }
+
+ dom.player.currentTime = 0;
+ document.mozCancelFullScreen();
}
function play() {
@@ -762,33 +739,6 @@ function formatDuration(duration) {
return '';
}
-function actHandle(activity) {
- var data = activity.source.data;
- var title = 'extras' in data ? (data.extras.title || '') : '';
- switch (activity.source.name) {
- case 'open':
- // Activities are required to specify whether they are inline in the manifest
- // so we know we are inline, dont bother showing thumbnails
- dom.thumbnails.classList.add('hidden');
- pendingActivity = activity;
- var filename = data.src.replace(/^ds\:videos:\/\//, '');
- activityData = {
- fromMediaDB: false,
- name: filename,
- title: title
- };
- break;
- case 'view':
- activityData = {
- url: data.url,
- title: title
- };
- break;
- }
- if (appStarted) {
- startStream();
- }
-}
// The mozRequestFullScreen can fail silently, so we keep asking
// for full screen until we detect that it happens, We limit the
@@ -802,20 +752,12 @@ function requestFullScreen(callback) {
if (++requests > MAX_FULLSCREEN_REQUESTS) {
window.clearInterval(fullscreenTimer);
fullscreenTimer = null;
- if (pendingActivity) {
- pendingActivity.postError('Could not play video');
- pendingActivity = null;
- }
return;
}
dom.videoFrame.mozRequestFullScreen();
}, 500);
}
-if (window.navigator.mozSetMessageHandler) {
- window.navigator.mozSetMessageHandler('activity', actHandle);
-}
-
// When we exit fullscreen mode, stop playing the video.
// This happens automatically when the user uses the back button (because
// back is Escape, which is also the "leave fullscreen mode" command).
@@ -824,11 +766,7 @@ if (window.navigator.mozSetMessageHandler) {
document.addEventListener('mozfullscreenchange', function() {
// We have exited fullscreen
if (document.mozFullScreenElement === null) {
- if (pendingActivity) {
- completeActivity(false);
- } else {
- hidePlayer();
- }
+ hidePlayer();
return;
}
View
352 apps/video/js/view.js
@@ -0,0 +1,352 @@
+'use strict';
+
+/*
+ * This is a stripped down version of video.js that handles the view activity
+ * and displays streaming videos specified by url.
+ *
+ * Unfortunately, there is a fair bit of code duplication between this
+ * file and video.js, but we are too close to the v1 deadline to refactor
+ * the shared code into a single shared file. If the video player UI changes
+ * those changes will have to be made in both video.js and view.js
+ */
+navigator.mozSetMessageHandler('activity', function viewVideo(activity) {
+ var dom = {}; // document elements
+ var screenLock; // keep the screen on when playing
+ var playing = false;
+ var endedTimer;
+ var controlShowing = false;
+ var controlFadeTimeout = null;
+ var dragging = false;
+ var data = activity.source.data;
+ var url = data.url;
+ var title = data.title || '';
+
+ if (data.status === 'fail' || data.errorcode) {
+ // This only happens when we're invoked for youtube videos
+ // Display an error message, but make sure we're localized first
+ if (navigator.mozL10n.readyState === 'complete') {
+ handleError(data.reason);
+ }
+ else {
+ window.addEventListener('localized', function handleLocalized() {
+ window.removeEventListener('localized', handleLocalized);
+ handleError(data.reason);
+ });
+ }
+ }
+ else {
+ initUI();
+ showPlayer(url, title);
+ }
+
+ function handleError(message) {
+ // Remove any HTML tags from the youtube error message
+ var div = document.createElement('div');
+ div.innerHTML = message;
+ message = div.textContent;
+
+ // Get a localized error message prefix
+ var prefix = navigator.mozL10n.get('youtube-error-prefix');
+
+ // Display the error message to the user
+ // XXX Using alert() is simple but ugly.
+ alert(prefix + '\n\n' + message);
+
+ // When the user clicks okay, end the activity.
+ // Do this on a timer so the alert has time to go away.
+ // Otherwise it appears to remain up over the caller and the user
+ // has to dismiss it twice.
+ setTimeout(function() { activity.postResult({}); }, 50);
+ }
+
+ function initUI() {
+ // Fullscreen mode and inline activities don't seem to play well together
+ // so we'll play the video without going into fullscreen mode.
+
+ // Get all the elements we use by their id
+ var ids = ['player', 'videoFrame', 'videoControls',
+ 'close', 'play', 'playHead',
+ 'elapsedTime', 'video-title', 'duration-text', 'elapsed-text',
+ 'slider-wrapper'];
+
+ ids.forEach(function createElementRef(name) {
+ dom[toCamelCase(name)] = document.getElementById(name);
+ });
+
+ function toCamelCase(str) {
+ return str.replace(/\-(.)/g, function replacer(str, p1) {
+ return p1.toUpperCase();
+ });
+ }
+
+ dom.player.mozAudioChannelType = 'content';
+
+ // show|hide controls over the player
+ dom.videoControls.addEventListener('mousedown', playerMousedown);
+
+ // Rescale when window size changes. This should get called when
+ // orientation changes.
+ window.addEventListener('resize', function() {
+ if (dom.player.readyState !== dom.player.HAVE_NOTHING) {
+ setPlayerSize();
+ }
+ });
+
+ dom.player.addEventListener('timeupdate', timeUpdated);
+ dom.player.addEventListener('ended', playerEnded);
+
+ // Set the 'lang' and 'dir' attributes to <html> when the page is translated
+ window.addEventListener('localized', function showBody() {
+ document.documentElement.lang = navigator.mozL10n.language.code;
+ document.documentElement.dir = navigator.mozL10n.language.direction;
+ });
+ }
+
+ function setControlsVisibility(visible) {
+ dom.videoControls.classList[visible ? 'remove' : 'add']('hidden');
+ controlShowing = visible;
+ }
+
+ function playerMousedown(event) {
+ // If we interact with the controls before they fade away,
+ // cancel the fade
+ if (controlFadeTimeout) {
+ clearTimeout(controlFadeTimeout);
+ controlFadeTimeout = null;
+ }
+ if (!controlShowing) {
+ setControlsVisibility(true);
+ return;
+ }
+ if (event.target == dom.play) {
+ if (dom.player.paused)
+ play();
+ else
+ pause();
+ } else if (event.target == dom.close) {
+ done();
+ } else if (event.target == dom.sliderWrapper) {
+ dragSlider(event);
+ } else {
+ setControlsVisibility(false);
+ }
+ }
+
+ function done() {
+ // release our screen lock
+ pause();
+
+ // Release any video resources
+ dom.player.removeAttribute('src');
+ dom.player.load();
+
+ // End the activity
+ activity.postResult({});
+ }
+
+ // Make the video fit the container
+ function setPlayerSize() {
+ var containerWidth = window.innerWidth;
+ var containerHeight = window.innerHeight;
+
+ // Don't do anything if we don't know our size.
+ // This could happen if we get a resize event before our metadata loads
+ if (!dom.player.videoWidth || !dom.player.videoHeight)
+ return;
+
+ var width = dom.player.videoWidth;
+ var height = dom.player.videoHeight;
+ var xscale = containerWidth / width;
+ var yscale = containerHeight / height;
+ var scale = Math.min(xscale, yscale);
+
+ // scale large videos down, but don't scale small videos up
+ if (scale < 1) {
+ width *= scale;
+ height *= scale;
+ }
+
+ var left = ((containerWidth - width) / 2);
+ var top = ((containerHeight - height) / 2);
+
+ var transform = 'translate(' + left + 'px,' + top + 'px)';
+
+ if (scale < 1) {
+ transform += ' scale(' + scale + ')';
+ }
+
+ dom.player.style.transform = transform;
+ }
+
+ // show video player
+ function showPlayer(url, title) {
+ dom.videoTitle.textContent = title || '';
+ dom.player.src = url;
+ dom.player.onloadedmetadata = function() {
+ dom.durationText.textContent = formatDuration(dom.player.duration);
+ timeUpdated();
+
+ dom.play.classList.remove('paused');
+ setPlayerSize();
+
+ dom.player.currentTime = 0;
+
+ // Show the controls briefly then fade out
+ setControlsVisibility(true);
+ controlFadeTimeout = setTimeout(function() {
+ setControlsVisibility(false);
+ }, 2000);
+
+ play();
+ };
+ }
+
+ function playerEnded() {
+ if (dragging) {
+ return;
+ }
+ if (endedTimer) {
+ clearTimeout(endedTimer);
+ endedTimer = null;
+ }
+ // When the video is done, go right back to the calling app
+ done();
+ }
+
+ function play() {
+ // Switch the button icon
+ dom.play.classList.remove('paused');
+
+ // Start playing
+ dom.player.play();
+ playing = true;
+
+ // Don't let the screen go to sleep
+ if (!screenLock)
+ screenLock = navigator.requestWakeLock('screen');
+ }
+
+ function pause() {
+ // Switch the button icon
+ dom.play.classList.add('paused');
+
+ // Stop playing the video
+ dom.player.pause();
+ playing = false;
+
+ // Let the screen go to sleep
+ if (screenLock) {
+ screenLock.unlock();
+ screenLock = null;
+ }
+ }
+
+ // Update the progress bar and play head as the video plays
+ function timeUpdated() {
+ if (controlShowing) {
+ // We can't update a progress bar if we don't know how long
+ // the video is. It is kind of a bug that the <video> element
+ // can't figure this out for ogv videos.
+ if (dom.player.duration === Infinity || dom.player.duration === 0) {
+ return;
+ }
+
+ var percent = (dom.player.currentTime / dom.player.duration) * 100;
+ if (isNaN(percent)) // this happens when we end the activity
+ return;
+ percent += '%';
+
+ dom.elapsedText.textContent = formatDuration(dom.player.currentTime);
+ dom.elapsedTime.style.width = percent;
+ // Don't move the play head if the user is dragging it.
+ if (!dragging)
+ dom.playHead.style.left = percent;
+ }
+
+ // Since we don't always get reliable 'ended' events, see if
+ // we've reached the end this way.
+ // See: https://bugzilla.mozilla.org/show_bug.cgi?id=783512
+ // If we're within 1 second of the end of the video, register
+ // a timeout a half a second after we'd expect an ended event.
+ if (!endedTimer) {
+ if (!dragging && dom.player.currentTime >= dom.player.duration - 1) {
+ var timeUntilEnd = (dom.player.duration - dom.player.currentTime + .5);
+ endedTimer = setTimeout(playerEnded, timeUntilEnd * 1000);
+ }
+ } else if (dragging && dom.player.currentTime < dom.player.duration - 1) {
+ // If there is a timer set and we drag away from the end, cancel the timer
+ clearTimeout(endedTimer);
+ endedTimer = null;
+ }
+ }
+
+ // handle drags on the time slider
+ function dragSlider(e) {
+ var isPaused = dom.player.paused;
+ dragging = true;
+
+ // We can't do anything if we don't know our duration
+ if (dom.player.duration === Infinity)
+ return;
+
+ if (!isPaused) {
+ dom.player.pause();
+ }
+
+ // Capture all mouse moves and the mouse up
+ document.addEventListener('mousemove', mousemoveHandler, true);
+ document.addEventListener('mouseup', mouseupHandler, true);
+
+ function position(event) {
+ var rect = dom.sliderWrapper.getBoundingClientRect();
+ var position = (event.clientX - rect.left) / rect.width;
+ position = Math.max(position, 0);
+ position = Math.min(position, 1);
+ return position;
+ }
+
+ function mouseupHandler(event) {
+ document.removeEventListener('mousemove', mousemoveHandler, true);
+ document.removeEventListener('mouseup', mouseupHandler, true);
+
+ dragging = false;
+
+ dom.playHead.classList.remove('active');
+
+ if (dom.player.currentTime === dom.player.duration) {
+ pause();
+ } else if (!isPaused) {
+ dom.player.play();
+ }
+ }
+
+ function mousemoveHandler(event) {
+ var pos = position(event);
+ var percent = pos * 100 + '%';
+ dom.playHead.classList.add('active');
+ dom.playHead.style.left = percent;
+ dom.elapsedTime.style.width = percent;
+ dom.player.currentTime = dom.player.duration * pos;
+ dom.elapsedText.textContent = formatDuration(dom.player.currentTime);
+ }
+
+ mousemoveHandler(e);
+ }
+
+ function formatDuration(duration) {
+ function padLeft(num, length) {
+ var r = String(num);
+ while (r.length < length) {
+ r = '0' + r;
+ }
+ return r;
+ }
+
+ var minutes = Math.floor(duration / 60);
+ var seconds = Math.round(duration % 60);
+ if (minutes < 60) {
+ return padLeft(minutes, 2) + ':' + padLeft(seconds, 2);
+ }
+ return '';
+ }
+});
View
2  apps/video/locales/video.en-US.properties
@@ -12,3 +12,5 @@ pluggedin-text = Unplug the phone to watch videos.
confirm-delete = Are you sure you want to delete:
delete-video.label = Delete Video?
+
+youtube-error-prefix = YouTube Won't Play Video
View
12 apps/video/manifest.webapp
@@ -37,17 +37,13 @@
"60": "/style/icons/60/Video.png"
},
"activities": {
- "open" : {
- "filters": {
- "type": ["video/webm", "video/mp4", "video/3gpp"]
- },
- "returnValue": true,
- "disposition": "inline"
- },
"view" : {
"filters": {
"type": ["video/webm", "video/mp4", "video/3gpp"]
- }
+ },
+ "href": "view.html",
+ "disposition": "inline",
+ "returnValue": true
}
}
}
View
3  apps/video/style/video.css
@@ -12,8 +12,7 @@ ul {
margin: 0;
height: 100%;
overflow-y: scroll;
- background: -moz-linear-gradient(top, #222 0%,#000 100%);
- background: linear-gradient(top, #222 0%,#000 100%);
+ background: linear-gradient(to bottom, #222 0%,#000 100%);
}
li {
margin: 0;
View
42 apps/video/view.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="pragma" content="no-cache" />
+ <title>Videos</title>
+ <link rel="resource" type="application/l10n" href="locales/locales.ini" />
+ <link rel="stylesheet" type="text/css" href="shared/style/headers.css">
+ <link rel="stylesheet" type="text/css" href="style/video.css" />
+ </head>
+ <body role="application" class="skin-dark">
+ <div id="videoFrame">
+ <video id="player" preload="metadata"></video>
+ <!-- video controls -->
+ <div id="videoControls" class="hidden">
+ <section role="region">
+ <header>
+ <a href="#" id="close"><span class="icon icon-back">back</span></a>
+ <h1 id="video-title"></h1>
+ </header>
+
+ <footer id="videoBar">
+ <button id="play"></button>
+ <div id="timeSlider">
+ <span id="elapsed-text"></span>
+ <div id="slider-wrapper">
+ <div id="elapsedTime" class="progress"></div>
+ <div id="bufferedTime" class="progress"></div>
+ <div id="timeBackground" class="progress"></div>
+ <button id="playHead"></button>
+ </div>
+ <span id="duration-text"></span>
+ </div>
+ </footer>
+ </section>
+ </div>
+ </div>
+
+ <script type="text/javascript" src="shared/js/l10n.js"></script>
+ <script type="text/javascript" src="js/view.js"></script>
+ </body>
+</html>
Something went wrong with that request. Please try again.