diff --git a/platypush/backend/http/static/css/source/common/vars.scss b/platypush/backend/http/static/css/source/common/vars.scss index beb90026..ae0c0c90 100644 --- a/platypush/backend/http/static/css/source/common/vars.scss +++ b/platypush/backend/http/static/css/source/common/vars.scss @@ -10,6 +10,7 @@ $default-fg-3: #888888 !default; $default-font-size: 1.5rem !default; $default-shadow: 2px 2px 2px #ccc !default; $default-hover-fg: #35b870 !default; +$default-hover-fg-2: #38cf80 !default; $default-font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif !default; $default-border: 1px solid #e1e4e8 !default; diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/media/devices.scss b/platypush/backend/http/static/css/source/webpanel/plugins/media/devices.scss index de8a1ddd..186c7a83 100644 --- a/platypush/backend/http/static/css/source/webpanel/plugins/media/devices.scss +++ b/platypush/backend/http/static/css/source/webpanel/plugins/media/devices.scss @@ -4,6 +4,16 @@ button { padding: .5rem; + margin-right: .5rem; + + &.selected { + background: initial; + color: $default-hover-fg; + + &:hover { + color: $default-hover-fg-2; + } + } } .dropdown { @@ -12,6 +22,11 @@ align-items: center; cursor: pointer; + .text { + text-align: left; + margin-left: 2rem; + } + &:hover { background: $hover-bg } diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/media/index.scss b/platypush/backend/http/static/css/source/webpanel/plugins/media/index.scss index b75bfdb9..fb81dc59 100644 --- a/platypush/backend/http/static/css/source/webpanel/plugins/media/index.scss +++ b/platypush/backend/http/static/css/source/webpanel/plugins/media/index.scss @@ -15,6 +15,24 @@ height: inherit; letter-spacing: .03rem; + .dropdown { + z-index: $devices-dropdown-z-index; + + .item { + display: flex; + align-item: center; + cursor: pointer; + + .text { + margin-left: 1rem; + } + + &:hover { + background: $hover-bg; + } + } + } + input[type=text] { width: 100%; diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/media/results.scss b/platypush/backend/http/static/css/source/webpanel/plugins/media/results.scss index 981a1a74..d72d2edf 100644 --- a/platypush/backend/http/static/css/source/webpanel/plugins/media/results.scss +++ b/platypush/backend/http/static/css/source/webpanel/plugins/media/results.scss @@ -1,6 +1,7 @@ .media-plugin { .results { @include calc(height, '100% - 16rem'); + position: relative; // For the dropdown menu overflow: auto; .empty { diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/media/vars.scss b/platypush/backend/http/static/css/source/webpanel/plugins/media/vars.scss index 0edcb400..32d02725 100644 --- a/platypush/backend/http/static/css/source/webpanel/plugins/media/vars.scss +++ b/platypush/backend/http/static/css/source/webpanel/plugins/media/vars.scss @@ -9,3 +9,5 @@ $control-time-color: #666; $empty-results-color: #506050; +$devices-dropdown-z-index: 2; + diff --git a/platypush/backend/http/static/js/plugins/media/controls.js b/platypush/backend/http/static/js/plugins/media/controls.js index b7c7a308..ce4ae69f 100644 --- a/platypush/backend/http/static/js/plugins/media/controls.js +++ b/platypush/backend/http/static/js/plugins/media/controls.js @@ -5,7 +5,7 @@ Vue.component('media-controls', { item: { type: Object, default: () => {}, - } + }, }, methods: { diff --git a/platypush/backend/http/static/js/plugins/media/devices.js b/platypush/backend/http/static/js/plugins/media/devices.js index 387a069a..52afa5d2 100644 --- a/platypush/backend/http/static/js/plugins/media/devices.js +++ b/platypush/backend/http/static/js/plugins/media/devices.js @@ -2,11 +2,13 @@ Vue.component('media-devices', { template: '#tmpl-media-devices', props: { bus: { type: Object }, + playerPlugin: { type: String }, }, data: function() { return { showDevicesMenu: false, + selectedDevice: {}, }; }, @@ -14,26 +16,47 @@ Vue.component('media-devices', { dropdownItems: function() { var items = [ { - text: 'Local player', + name: this.playerPlugin, + text: this.playerPlugin, + type: 'local', icon: 'desktop', }, { + name: 'browser', text: 'Browser', + type: 'browser', icon: 'laptop', }, ]; + const self = this; + const onClick = (item) => { + return () => { + self.selectDevice(item); + }; + }; + + for (var i=0; i < items.length; i++) { + items[i].click = onClick(items[i]); + } + return items; }, }, methods: { + selectDevice: function(device) { + this.selectedDevice = device; + this.bus.$emit('selected-device', device); + }, + openDevicesMenu: function() { openDropdown(this.$refs.menu); }, }, created: function() { + this.selectDevice(this.dropdownItems.filter(_ => _.type === 'local')[0]); }, }); diff --git a/platypush/backend/http/static/js/plugins/media/handlers/file.js b/platypush/backend/http/static/js/plugins/media/handlers/file.js index 475f8a02..029e8447 100644 --- a/platypush/backend/http/static/js/plugins/media/handlers/file.js +++ b/platypush/backend/http/static/js/plugins/media/handlers/file.js @@ -1,8 +1,26 @@ mediaHandlers.file = { - icon: 'hdd', + iconClass: 'fa fa-hdd', - matchesUrl: function(url) { - return url.startsWith('file:///') || url.startsWith('/'); - }, + actions: [ + { + text: 'Play', + icon: 'play', + action: 'play', + }, + + { + text: 'Download', + icon: 'download', + action: function(item, bus) { + bus.$emit('download', item); + }, + }, + + { + text: 'View info', + icon: 'info', + action: 'info', + }, + ], }; diff --git a/platypush/backend/http/static/js/plugins/media/handlers/torrent.js b/platypush/backend/http/static/js/plugins/media/handlers/torrent.js index 43f9608c..5787f89f 100644 --- a/platypush/backend/http/static/js/plugins/media/handlers/torrent.js +++ b/platypush/backend/http/static/js/plugins/media/handlers/torrent.js @@ -1,8 +1,25 @@ mediaHandlers.torrent = { - icon: 'magnet', + iconClass: 'fa fa-magnet', - matchesUrl: function(url) { - return url.startsWith('magnet:?') || url.endsWith('.torrent'); - }, + actions: [ + { + text: 'Play', + icon: 'play', + action: 'play', + }, + + { + text: 'Download', + icon: 'download', + action: function(item) { + }, + }, + + { + text: 'View info', + icon: 'info', + action: 'info', + }, + ], }; diff --git a/platypush/backend/http/static/js/plugins/media/handlers/youtube.js b/platypush/backend/http/static/js/plugins/media/handlers/youtube.js index 820b5e35..bd33328e 100644 --- a/platypush/backend/http/static/js/plugins/media/handlers/youtube.js +++ b/platypush/backend/http/static/js/plugins/media/handlers/youtube.js @@ -1,10 +1,25 @@ mediaHandlers.youtube = { - icon: 'youtube', + iconClass: 'fab fa-youtube', - matchesUrl: function(url) { - return url.startsWith('https://youtube.com/watch?v=') || - url.startsWith('https://www.youtube.com/watch?v=') || - url.startsWith('https://youtu.be/'); - }, + actions: [ + { + text: 'Play', + icon: 'play', + action: 'play', + }, + + { + text: 'Download', + icon: 'download', + action: function(item) { + }, + }, + + { + text: 'View info', + icon: 'info', + action: 'info', + }, + ], }; diff --git a/platypush/backend/http/static/js/plugins/media/index.js b/platypush/backend/http/static/js/plugins/media/index.js index 2ba800be..edf3e3dd 100644 --- a/platypush/backend/http/static/js/plugins/media/index.js +++ b/platypush/backend/http/static/js/plugins/media/index.js @@ -9,8 +9,10 @@ Vue.component('media', { bus: new Vue({}), results: [], currentItem: {}, + selectedDevice: undefined, loading: { results: false, + media: false, }, }; }, @@ -33,22 +35,31 @@ Vue.component('media', { this.loading.results = false; for (var i=0; i < results.length; i++) { - results[i].handler = {}; - - for (const hndl of Object.values(mediaHandlers)) { - if (hndl.matchesUrl(results[i].url)) { - results[i].handler = hndl; - } - } + results[i].handler = mediaHandlers[results[i].type]; } this.results = results; }, + + play: async function(item) { + }, + + info: function(item) { + // TODO + console.log(item); + }, + + selectDevice: function(device) { + this.selectedDevice = device; + }, }, created: function() { this.refresh(); + this.bus.$on('play', this.play); + this.bus.$on('info', this.info); + this.bus.$on('selected-device', this.selectDevice); this.bus.$on('results-loading', this.onResultsLoading); this.bus.$on('results-ready', this.onResultsReady); }, diff --git a/platypush/backend/http/static/js/plugins/media/item.js b/platypush/backend/http/static/js/plugins/media/item.js index 82106282..c4ab9c15 100644 --- a/platypush/backend/http/static/js/plugins/media/item.js +++ b/platypush/backend/http/static/js/plugins/media/item.js @@ -2,10 +2,27 @@ Vue.component('media-item', { template: '#tmpl-media-item', props: { bus: { type: Object }, + + selected: { + type: Boolean, + default: false, + }, + + active: { + type: Boolean, + default: false, + }, + item: { type: Object, default: () => {}, } }, + + methods: { + onClick: function(event) { + this.bus.$emit('result-clicked', this.item); + }, + }, }); diff --git a/platypush/backend/http/static/js/plugins/media/results.js b/platypush/backend/http/static/js/plugins/media/results.js index 6e714a71..74dde21e 100644 --- a/platypush/backend/http/static/js/plugins/media/results.js +++ b/platypush/backend/http/static/js/plugins/media/results.js @@ -2,6 +2,10 @@ Vue.component('media-results', { template: '#tmpl-media-results', props: { bus: { type: Object }, + searching: { + type: Boolean, + default: false, + }, loading: { type: Boolean, default: false, @@ -12,7 +16,58 @@ Vue.component('media-results', { }, }, + data: function() { + return { + selectedItem: {}, + currentItem: {}, + }; + }, + + computed: { + mediaItemDropdownItems: function() { + if (!Object.keys(this.selectedItem).length) { + return []; + } + + const self = this; + + return this.selectedItem.handler.actions.map(action => { + return { + text: action.text, + icon: action.icon, + click: function() { + if (action.action instanceof Function) { + action.action(self.selectedItem, self.bus); + } else if (typeof(action.action) === 'string') { + self[action.action](self.selectedItem); + } + }, + }; + }); + }, + }, + methods: { + itemClicked: function(item) { + if (this.selectedItem.length && this.selectedItem.url === item.url) { + return; + } + + this.selectedItem = item; + openDropdown(this.$refs.mediaItemDropdown); + }, + + play: function(item) { + this.bus.$emit('play', item); + }, + + info: function(item) { + this.bus.$emit('info', item); + }, + }, + + created: function() { + this.bus.$on('result-clicked', this.itemClicked); }, }); diff --git a/platypush/backend/http/static/js/plugins/media/search.js b/platypush/backend/http/static/js/plugins/media/search.js index 09262bf3..f3fd82ae 100644 --- a/platypush/backend/http/static/js/plugins/media/search.js +++ b/platypush/backend/http/static/js/plugins/media/search.js @@ -3,6 +3,7 @@ Vue.component('media-search', { props: { bus: { type: Object }, supportedTypes: { type: Object }, + playerPlugin: { type: String }, }, data: function() { diff --git a/platypush/backend/http/templates/plugins/media/devices.html b/platypush/backend/http/templates/plugins/media/devices.html index 18a39fb8..0638b988 100644 --- a/platypush/backend/http/templates/plugins/media/devices.html +++ b/platypush/backend/http/templates/plugins/media/devices.html @@ -2,8 +2,12 @@ diff --git a/platypush/backend/http/templates/plugins/media/results.html b/platypush/backend/http/templates/plugins/media/results.html index eb454608..d4684895 100644 --- a/platypush/backend/http/templates/plugins/media/results.html +++ b/platypush/backend/http/templates/plugins/media/results.html @@ -2,17 +2,25 @@ diff --git a/platypush/backend/http/templates/plugins/media/search.html b/platypush/backend/http/templates/plugins/media/search.html index becb3352..98ed8192 100644 --- a/platypush/backend/http/templates/plugins/media/search.html +++ b/platypush/backend/http/templates/plugins/media/search.html @@ -20,7 +20,10 @@
- + +
diff --git a/platypush/plugins/media/__init__.py b/platypush/plugins/media/__init__.py index 3a273630..1fcb5e05 100644 --- a/platypush/plugins/media/__init__.py +++ b/platypush/plugins/media/__init__.py @@ -176,7 +176,7 @@ class MediaPlugin(Plugin): if self._is_playing_torrent: try: get_plugin('media.webtorrent').quit() - except: + except Exception as e: self.logger.warning('Cannot quit the webtorrent instance: {}'. format(str(e))) @@ -305,9 +305,13 @@ class MediaPlugin(Plugin): format(query, media_type)) flattened_results = [] + for media_type in self._supported_media_types: if media_type in results: + for result in results[media_type]: + result['type'] = media_type flattened_results += results[media_type] + results = flattened_results if results: