diff --git a/.gitmodules b/.gitmodules index 460cf2b5b..f5dcbc0c8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/BlackLight/platypush.wiki.git [submodule "platypush/backend/http/static/flag-icons"] path = platypush/backend/http/static/flag-icons - url = https://github.com/lipis/flag-icon-css.git + url = git@github.com:BlackLight/flag-icon-css.git diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/media/controls.scss b/platypush/backend/http/static/css/source/webpanel/plugins/media/controls.scss index e8495f020..835bdf8c5 100644 --- a/platypush/backend/http/static/css/source/webpanel/plugins/media/controls.scss +++ b/platypush/backend/http/static/css/source/webpanel/plugins/media/controls.scss @@ -14,6 +14,14 @@ .item-info { font-size: 1.15em; letter-spacing: .02em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; + + &:hover { + color: $default-hover-fg; + } } } 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 456e07436..58b643721 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 @@ -9,6 +9,7 @@ @import 'webpanel/plugins/media/results'; @import 'webpanel/plugins/media/controls'; @import 'webpanel/plugins/media/info'; +@import 'webpanel/plugins/media/subs'; .media-plugin { display: flex; 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 9822b5fd0..37c158ad4 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 @@ -34,8 +34,8 @@ &:nth-child(odd) { background: rgba(255, 255, 255, 0.0); } &:nth-child(even) { background: $default-bg-3; } - &:hover { background: $hover-bg !important; } - &.selected { background: $selected-bg !important; } + &:hover { background: $hover-bg; } + &.selected { background: $selected-bg; } } } } diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/media/subs.scss b/platypush/backend/http/static/css/source/webpanel/plugins/media/subs.scss new file mode 100644 index 000000000..cea6c8932 --- /dev/null +++ b/platypush/backend/http/static/css/source/webpanel/plugins/media/subs.scss @@ -0,0 +1,60 @@ +.media-plugin { + #media-subs { + .body { + padding: 0; + } + } + + .subs-container { + .loading, .no-results { + display: flex; + align-items: center; + padding: 3rem; + font-size: 1.5em; + } + + .subs { + .list { + max-height: 50vh; + overflow: auto; + } + + .sub { + display: flex; + align-items: center; + padding: .75em .5em; + border-bottom: $default-border-2; + cursor: pointer; + + &:nth-child(odd) { background: rgba(255, 255, 255, 0.0); } + &:nth-child(even) { background: $default-bg-3; } + &.selected { background: $selected-bg; } + + &:hover { + background: $hover-bg; + border-radius: 1em; + } + } + + .controls { + position: relative; + padding: 1.5rem 0; + height: $subs-control-height; + + button { + position: absolute; + right: 0; + margin-right: 1rem; + border: $default-border-2; + box-shadow: $btn-default-shadow; + + &:hover:not([disabled]) { + box-shadow: $btn-hover-default-shadow; + color: $default-hover-fg; + } + } + } + } + } +} + 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 ca5942321..b585011b7 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 @@ -13,3 +13,7 @@ $result-item-icon: #444; $devices-dropdown-z-index: 2; $devices-dropdown-refresh-fg: #666; +$subs-control-height: 4rem; +$btn-default-shadow: 2px 2px 2px #ddd; +$btn-hover-default-shadow: 3px 3px 3px #ddd; + diff --git a/platypush/backend/http/static/flag-icons b/platypush/backend/http/static/flag-icons index c8031a673..c0465f4f9 160000 --- a/platypush/backend/http/static/flag-icons +++ b/platypush/backend/http/static/flag-icons @@ -1 +1 @@ -Subproject commit c8031a673e8428b842199c0dcf66d12b6ed1d1d6 +Subproject commit c0465f4f9ddfee44ad601e906ab541ac0ca4c57d 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 e2a9defdd..47262b8d7 100644 --- a/platypush/backend/http/static/js/plugins/media/handlers/file.js +++ b/platypush/backend/http/static/js/plugins/media/handlers/file.js @@ -19,7 +19,7 @@ MediaHandlers.file = Vue.extend({ { text: 'Play with subtitles', iconClass: 'fas fa-closed-captioning', - action: this.searchSubtiles, + action: this.searchSubtitles, }, { @@ -84,8 +84,23 @@ MediaHandlers.file = Vue.extend({ this.bus.$emit('info', (await this.getMetadata(item))); }, - searchSubtitles: function(item) { + infoLoad: function(url) { + if (!this.matchesUrl(url)) + return; + + this.info(url); }, + + searchSubtitles: function(item) { + this.bus.$emit('search-subs', item); + }, + }, + + created: function() { + const self = this; + setTimeout(() => { + self.infoLoadWatch = self.bus.$on('info-load', this.infoLoad); + }, 1000); }, }); diff --git a/platypush/backend/http/static/js/plugins/media/index.js b/platypush/backend/http/static/js/plugins/media/index.js index 8adff6ba7..6005820e9 100644 --- a/platypush/backend/http/static/js/plugins/media/index.js +++ b/platypush/backend/http/static/js/plugins/media/index.js @@ -59,6 +59,10 @@ Vue.component('media', { loading: false, item: {}, }, + + subsModal: { + visible: false, + }, }; }, @@ -104,8 +108,9 @@ Vue.component('media', { item = await this.startStreaming(item.url); } - let status = await this.selectedDevice.play(item.url); + let status = await this.selectedDevice.play(item.url, item.subtitles); + this.subsModal.visible = false; this.onStatusUpdate({ device: this.selectedDevice, status: status, @@ -172,6 +177,14 @@ Vue.component('media', { return ret; }, + searchSubs: function(item) { + if (typeof item === 'string') + item = {url: item}; + + this.subsModal.visible = true; + this.$refs.subs.search(item); + }, + selectDevice: async function(device) { this.selectedDevice = device; let status = await this.selectedDevice.status(); @@ -254,6 +267,7 @@ Vue.component('media', { this.bus.$on('results-ready', this.onResultsReady); this.bus.$on('status-update', this.onStatusUpdate); this.bus.$on('start-streaming', this.startStreaming); + this.bus.$on('search-subs', this.searchSubs); setInterval(this.timerFunc, 1000); }, diff --git a/platypush/backend/http/static/js/plugins/media/players/browser.js b/platypush/backend/http/static/js/plugins/media/players/browser.js index dbe871dae..31061c04a 100644 --- a/platypush/backend/http/static/js/plugins/media/players/browser.js +++ b/platypush/backend/http/static/js/plugins/media/players/browser.js @@ -15,6 +15,13 @@ MediaPlayers.browser = Vue.extend({ }, }, + subFormats: { + type: Array, + default: () => { + return ['vtt']; + }, + }, + name: { type: String, default: 'Browser', diff --git a/platypush/backend/http/static/js/plugins/media/players/chromecast.js b/platypush/backend/http/static/js/plugins/media/players/chromecast.js index 2e7d24d5a..5834cd33c 100644 --- a/platypush/backend/http/static/js/plugins/media/players/chromecast.js +++ b/platypush/backend/http/static/js/plugins/media/players/chromecast.js @@ -15,6 +15,13 @@ MediaPlayers.chromecast = Vue.extend({ }, }, + subFormats: { + type: Array, + default: () => { + return ['vtt']; + }, + }, + device: { type: null, address: null, diff --git a/platypush/backend/http/static/js/plugins/media/players/local.js b/platypush/backend/http/static/js/plugins/media/players/local.js index 0e232524b..8d9721841 100644 --- a/platypush/backend/http/static/js/plugins/media/players/local.js +++ b/platypush/backend/http/static/js/plugins/media/players/local.js @@ -16,6 +16,13 @@ MediaPlayers.local = Vue.extend({ }, }, + subFormats: { + type: Array, + default: () => { + return ['srt']; + }, + }, + device: { type: Object, default: () => { @@ -46,10 +53,10 @@ MediaPlayers.local = Vue.extend({ return await request(this.pluginPrefix.concat('.status')); }, - play: async function(resource) { + play: async function(resource, subtitles=undefined) { return await request( this.pluginPrefix.concat('.play'), - {resource: resource} + {resource: resource, subtitles: subtitles} ); }, diff --git a/platypush/backend/http/static/js/plugins/media/subs.js b/platypush/backend/http/static/js/plugins/media/subs.js new file mode 100644 index 000000000..68c1e0755 --- /dev/null +++ b/platypush/backend/http/static/js/plugins/media/subs.js @@ -0,0 +1,45 @@ +Vue.component('media-subs', { + template: '#tmpl-media-subs', + props: { + bus: { type: Object }, + subFormats: { + type: Array, + default: () => [], + }, + }, + + data: function() { + return { + loading: false, + media: {}, + items: [], + selectedItem: undefined, + }; + }, + + methods: { + search: async function(media) { + this.loading = true; + + this.media = media; + this.selectedItem = undefined; + this.items = await request('media.subtitles.get_subtitles', {resource: this.media.url}); + + this.loading = false; + }, + + play: async function() { + let args = {link: this.selectedItem.SubDownloadLink}; + + if (this.media.url && this.media.url.startsWith('file://')) + args.media_resource = this.media.url; + + if (this.subFormats.indexOf('srt') < 0) + args.convert_to_vtt = true; + + this.media.subtitles = (await request('media.subtitles.download', args)).filename; + this.bus.$emit('play', this.media); + }, + }, +}); + diff --git a/platypush/backend/http/templates/plugins/media/controls.html b/platypush/backend/http/templates/plugins/media/controls.html index 1e5f76c70..268afb46f 100644 --- a/platypush/backend/http/templates/plugins/media/controls.html +++ b/platypush/backend/http/templates/plugins/media/controls.html @@ -4,7 +4,9 @@