diff --git a/platypush/backend/http/static/css/media.css b/platypush/backend/http/static/css/media.css index 52e9027468..a2260dabfc 100644 --- a/platypush/backend/http/static/css/media.css +++ b/platypush/backend/http/static/css/media.css @@ -77,3 +77,51 @@ form#video-ctrl { 100% { background: #d4ffe3; } } +button[data-toggle="#media-devices-panel"] { + display: none; +} + + button.remote[data-toggle="#media-devices-panel"] { + color: #34b868; + } + + button.selected[data-toggle="#media-devices-panel"] { + box-shadow: inset 0 2px 0 rgba(0,0,0,0.1), + inset 3px 0 0 rgba(0,0,0,0.1), + inset 0 0 2px rgba(0,0,0,0.1), + inset 0 0 0 1px rgba(0,0,0,0.1); + } + +#media-devices-panel { + display: none; + position: absolute; + padding: 1rem; + background: #f0f0f0; + z-index: 10; + border: 1px solid #d0d0d0; + border-radius: 5px; +} + + #media-devices-panel .cast-device { + padding: 0.5rem; + cursor: pointer; + } + + #media-devices-panel .cast-device-local { + border-bottom: 1px solid #ddd; + margin-bottom: 0.25rem; + } + + #media-devices-panel .cast-device.selected { + font-weight: bold; + color: #34b868; + } + + #media-devices-panel .cast-device:hover { + background-color: #daf8e2 !important; + } + + #media-devices-panel * > .cast-device-icon { + color: #666; + } + diff --git a/platypush/backend/http/static/js/application.js b/platypush/backend/http/static/js/application.js index a8ab4768fd..dc1d58a262 100644 --- a/platypush/backend/http/static/js/application.js +++ b/platypush/backend/http/static/js/application.js @@ -32,6 +32,7 @@ $(document).ready(function() { onWebsocketTimeout(websocket), websocketReconnectMsecs); websocket.onmessage = function(event) { + console.debug(event); for (var listener of eventListeners) { data = event.data; if (typeof event.data === 'string') { @@ -218,6 +219,12 @@ function execute(request, onSuccess, onError, onComplete) { } }, success: function(response, status, xhr) { + if ('errors' in response && response.errors.length) { + if (onError) { + onError(xhr, '500', response.errors); + } + } + if (onSuccess) { onSuccess(response, status, xhr); } diff --git a/platypush/backend/http/static/js/assistant.google.js b/platypush/backend/http/static/js/assistant.google.js index 09fef6bce0..a14b32c3ee 100644 --- a/platypush/backend/http/static/js/assistant.google.js +++ b/platypush/backend/http/static/js/assistant.google.js @@ -1,7 +1,5 @@ $(document).ready(function() { var onEvent = function(event) { - console.log(event); - switch (event.args.type) { case 'platypush.message.event.assistant.ConversationStartEvent': createNotification({ diff --git a/platypush/backend/http/static/js/media.js b/platypush/backend/http/static/js/media.js index 793bd882ec..9effdc5859 100644 --- a/platypush/backend/http/static/js/media.js +++ b/platypush/backend/http/static/js/media.js @@ -4,6 +4,10 @@ $(document).ready(function() { $videoResults = $('#video-results'), $volumeCtrl = $('#video-volume-ctrl'), $ctrlForm = $('#video-ctrl'), + $devsPanel = $('#media-devices-panel'), + $devsBtn = $('button[data-toggle="#media-devices-panel"]'), + $searchBarContainer = $('#media-search-bar-container'), + $mediaBtnsContainer = $('#media-btns-container'), prevVolume = undefined; var updateVideoResults = function(videos) { @@ -38,6 +42,19 @@ $(document).ready(function() { return $iconContainer; }; + var getSelectedDevice = function() { + var device = { isRemote: false, name: undefined }; + var $remoteDevice = $devsPanel.find('.cast-device.selected') + .filter((i, dev) => !$(dev).data('local') && $(dev).data('name')); + + if ($remoteDevice.length) { + device.isRemote = true; + device.name = $remoteDevice.data('name'); + } + + return device; + }; + var initBindings = function() { $searchForm.on('submit', function(event) { var $input = $(this).find('input[name=video-search-text]'); @@ -84,12 +101,18 @@ $(document).ready(function() { var action = $(this).data('action'); var $btn = $(this); - execute( - { - type: 'request', - action: 'media.' + action, - } - ); + var requestArgs = { + type: 'request', + action: 'media.' + action, + }; + + var selectedDevice = getSelectedDevice(); + if (selectedDevice.isRemote) { + requestArgs.action = 'media.chromecast.' + action; + requestArgs.args = { 'chromecast': selectedDevice.name }; + } + + execute(requestArgs); }); $volumeCtrl.on('mousedown touchstart', function(event) { @@ -97,13 +120,19 @@ $(document).ready(function() { }); $volumeCtrl.on('mouseup touchend', function(event) { - execute( - { - type: 'request', - action: 'media.set_volume', - args: { volume: $(this).val() } - }, + var requestArgs = { + type: 'request', + action: 'media.set_volume', + args: { volume: $(this).val() }, + }; + var selectedDevice = getSelectedDevice(); + if (selectedDevice.isRemote) { + requestArgs.action = 'media.chromecast.set_volume', + requestArgs.args.chromecast = selectedDevice.name; + } + + execute(requestArgs, onSuccess=undefined, onError = function() { $volumeCtrl.val(prevVolume); @@ -120,14 +149,21 @@ $(document).ready(function() { return false; } + var requestArgs = { + type: 'request', + action: 'media.play', + args: { resource: $item.data('url') }, + }; + + var selectedDevice = getSelectedDevice(); + if (selectedDevice.isRemote) { + requestArgs.action = 'media.chromecast.play'; + requestArgs.args.chromecast = selectedDevice.name; + } + $videoResults.text('Loading video...'); execute( - { - type: 'request', - action: 'media.play', - args: { resource: $item.data('url') }, - }, - + requestArgs, function() { $videoResults.html(results); $item.siblings().removeClass('active'); @@ -139,9 +175,82 @@ $(document).ready(function() { } ); }); + + $devsBtn.on('click touch', function() { + $(this).toggleClass('selected'); + $devsPanel.css('top', ($(this).position().top + $(this).outerHeight()) + 'px'); + $devsPanel.css('left', ($(this).position().left) + 'px'); + $devsPanel.toggle(); + return false; + }); + + $devsPanel.on('mouseup touchend', '.cast-device', function() { + var $devices = $devsPanel.find('.cast-device'); + var $curSelected = $devices.filter((i, d) => $(d).hasClass('selected')); + + if ($curSelected.data('name') !== $(this).data('name')) { + $curSelected.removeClass('selected'); + $(this).addClass('selected'); + + if (!$(this).data('local')) { + $devsBtn.addClass('remote'); + } else { + $devsBtn.removeClass('remote'); + } + } + + $devsPanel.hide(); + $devsBtn.removeClass('selected'); + }); + }; + + var initRemoteDevices = function() { + execute( + { + type: 'request', + action: 'media.chromecast.get_chromecasts', + }, + + function(results) { + if (!results || results.response.errors.length) { + return; + } + + $searchBarContainer.removeClass('eleven').addClass('nine'); + $mediaBtnsContainer.removeClass('one').addClass('three'); + $devsBtn.show(); + + results = results.response.output; + for (var cast of results) { + var $cast = $('
').addClass('row cast-device') + .addClass('cast-device-' + cast.type).data('name', cast.name); + + var icon = 'question'; + switch (cast.type) { + case 'cast': icon = 'tv'; break; + case 'audio': icon = 'volume-up'; break; + } + + var $castIcon = $('').addClass('fa fa-' + icon) + .addClass('cast-device-icon'); + var $castName = $('').addClass('cast-device-name') + .text(cast.name); + + var $iconContainer = $('').addClass('two columns'); + var $nameContainer = $('').addClass('ten columns'); + $castIcon.appendTo($iconContainer); + $castName.appendTo($nameContainer); + + $iconContainer.appendTo($cast); + $nameContainer.appendTo($cast); + $cast.appendTo($devsPanel); + } + } + ); }; var init = function() { + initRemoteDevices(); initBindings(); }; diff --git a/platypush/backend/http/templates/plugins/media.html b/platypush/backend/http/templates/plugins/media.html index 351d983679..7286a2313f 100644 --- a/platypush/backend/http/templates/plugins/media.html +++ b/platypush/backend/http/templates/plugins/media.html @@ -10,13 +10,27 @@