New media webplugin WIP
This commit is contained in:
parent
3bd9bec660
commit
482f6f0765
18 changed files with 274 additions and 127 deletions
|
@ -34,3 +34,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.active-glow {
|
||||||
|
@include animation(active-glow 5s infinite);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes active-glow {
|
||||||
|
0% { background: $active-glow-bg-1; }
|
||||||
|
50% { background: $active-glow-bg-2; }
|
||||||
|
100% { background: $active-glow-bg-1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,8 @@ $fade-in-transition-duration: $fade-transition-duration !default;
|
||||||
$fade-out-transition-duration: $fade-transition-duration !default;
|
$fade-out-transition-duration: $fade-transition-duration !default;
|
||||||
$roll-in-transition-duration: $roll-transition-duration !default;
|
$roll-in-transition-duration: $roll-transition-duration !default;
|
||||||
$roll-out-transition-duration: $roll-transition-duration !default;
|
$roll-out-transition-duration: $roll-transition-duration !default;
|
||||||
|
$active-glow-bg-1: #d4ffe3 !default;
|
||||||
|
$active-glow-bg-2: #9cdfb0 !default;
|
||||||
|
|
||||||
//// Notifications
|
//// Notifications
|
||||||
$notification-bg: rgba(185, 255, 193, 0.9) !default;
|
$notification-bg: rgba(185, 255, 193, 0.9) !default;
|
||||||
|
|
|
@ -10,6 +10,11 @@
|
||||||
@extend .vertical-center;
|
@extend .vertical-center;
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
line-height: 2.6rem;
|
line-height: 2.6rem;
|
||||||
|
|
||||||
|
.item-info {
|
||||||
|
font-size: 1.15em;
|
||||||
|
letter-spacing: .02em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
@ -26,6 +31,10 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.position {
|
||||||
|
margin-top: .75em;
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
padding: 0 1.5rem;
|
padding: 0 1.5rem;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
|
@import 'common/animations';
|
||||||
|
|
||||||
.media-plugin {
|
.media-plugin {
|
||||||
.results {
|
.results {
|
||||||
@include calc(height, '100% - 16rem');
|
|
||||||
position: relative; // For the dropdown menu
|
position: relative; // For the dropdown menu
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@include calc(height, '100%');
|
||||||
|
|
||||||
|
&.resize {
|
||||||
|
@include calc(height, '100% - 16rem');
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
@extend .active-glow;
|
||||||
|
height: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
@import 'common/vars';
|
@import 'common/vars';
|
||||||
@import 'common/mixins';
|
@import 'common/mixins';
|
||||||
@import 'common/layout';
|
@import 'common/layout';
|
||||||
|
@import 'common/animations';
|
||||||
|
|
||||||
@import 'webpanel/plugins/music.mpd/vars';
|
@import 'webpanel/plugins/music.mpd/vars';
|
||||||
|
|
||||||
.music-mpd-container {
|
.music-mpd-container {
|
||||||
|
@ -155,7 +157,7 @@
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
height: 4rem;
|
height: 4rem;
|
||||||
@include animation(active-track 5s infinite);
|
@extend .active-glow;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.move:hover {
|
&.move:hover {
|
||||||
|
@ -449,21 +451,3 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes active-track {
|
|
||||||
0% { background: $active-track-bg-1; }
|
|
||||||
50% { background: $active-track-bg-2; }
|
|
||||||
100% { background: $active-track-bg-1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@-moz-keyframes active-track {
|
|
||||||
0% { background: $active-track-bg-1; }
|
|
||||||
50% { background: $active-track-bg-2; }
|
|
||||||
100% { background: $active-track-bg-1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes active-track {
|
|
||||||
0% { background: $active-track-bg-1; }
|
|
||||||
50% { background: $active-track-bg-2; }
|
|
||||||
100% { background: $active-track-bg-1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,6 @@ $empty-playlist-shadow: 2px 1px rgb(235,235,235);
|
||||||
$playlist-controls-bg: rgba(247,247,247,0.95);
|
$playlist-controls-bg: rgba(247,247,247,0.95);
|
||||||
$playlist-controls-border: $default-border-2;
|
$playlist-controls-border: $default-border-2;
|
||||||
|
|
||||||
$active-track-bg-1: #d4ffe3;
|
|
||||||
$active-track-bg-2: #9cdfb0;
|
|
||||||
|
|
||||||
$move-mode-track-border: 3px dotted rgb(216,156,136);
|
$move-mode-track-border: 3px dotted rgb(216,156,136);
|
||||||
$move-mode-track-bg: rgba(216,156,136,0.3);
|
$move-mode-track-bg: rgba(216,156,136,0.3);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
Vue.component('media-controls', {
|
Vue.component('media-controls', {
|
||||||
template: '#tmpl-media-controls',
|
template: '#tmpl-media-controls',
|
||||||
|
mixins: [mediaUtils],
|
||||||
props: {
|
props: {
|
||||||
bus: { type: Object },
|
bus: { type: Object },
|
||||||
status: {
|
status: {
|
||||||
|
@ -7,8 +8,5 @@ Vue.component('media-controls', {
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,12 @@ MediaHandlers.file = Vue.extend({
|
||||||
action: this.play,
|
action: this.play,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
text: 'Play with subtitles',
|
||||||
|
iconClass: 'fas fa-closed-captioning',
|
||||||
|
action: this.searchSubtiles,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
text: 'Download',
|
text: 'Download',
|
||||||
icon: 'download',
|
icon: 'download',
|
||||||
|
@ -41,6 +47,9 @@ MediaHandlers.file = Vue.extend({
|
||||||
|
|
||||||
info: function(item) {
|
info: function(item) {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
searchSubtitles: function(item) {
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,37 @@
|
||||||
// Will be filled by dynamically loading media type handler scripts
|
// Will be filled by dynamically loading media type handler scripts
|
||||||
const MediaHandlers = {};
|
const MediaHandlers = {};
|
||||||
|
|
||||||
|
const mediaUtils = {
|
||||||
|
methods: {
|
||||||
|
convertTime: function(time) {
|
||||||
|
time = parseFloat(time); // Normalize strings
|
||||||
|
var t = {};
|
||||||
|
t.h = '' + parseInt(time/3600);
|
||||||
|
t.m = '' + parseInt(time/60 - t.h*60);
|
||||||
|
t.s = '' + parseInt(time - (t.h*3600 + t.m*60));
|
||||||
|
|
||||||
|
for (var attr of ['m','s']) {
|
||||||
|
if (parseInt(t[attr]) < 10) {
|
||||||
|
t[attr] = '0' + t[attr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret = [];
|
||||||
|
if (parseInt(t.h)) {
|
||||||
|
ret.push(t.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push(t.m, t.s);
|
||||||
|
return ret.join(':');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
Vue.component('media', {
|
Vue.component('media', {
|
||||||
template: '#tmpl-media',
|
template: '#tmpl-media',
|
||||||
props: ['config','player'],
|
props: ['config','player'],
|
||||||
|
mixins: [mediaUtils],
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
bus: new Vue({}),
|
bus: new Vue({}),
|
||||||
|
@ -54,6 +82,38 @@ Vue.component('media', {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
pause: async function() {
|
||||||
|
let status = await this.selectedDevice.pause();
|
||||||
|
this.onStatusUpdate({
|
||||||
|
device: this.selectedDevice,
|
||||||
|
status: status,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
stop: async function() {
|
||||||
|
let status = await this.selectedDevice.stop();
|
||||||
|
this.onStatusUpdate({
|
||||||
|
device: this.selectedDevice,
|
||||||
|
status: status,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
seek: async function(position) {
|
||||||
|
let status = await this.selectedDevice.seek(position);
|
||||||
|
this.onStatusUpdate({
|
||||||
|
device: this.selectedDevice,
|
||||||
|
status: status,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setVolume: async function(volume) {
|
||||||
|
let status = await this.selectedDevice.setVolume(volume);
|
||||||
|
this.onStatusUpdate({
|
||||||
|
device: this.selectedDevice,
|
||||||
|
status: status,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
info: function(item) {
|
info: function(item) {
|
||||||
// TODO
|
// TODO
|
||||||
console.log(item);
|
console.log(item);
|
||||||
|
@ -78,29 +138,21 @@ Vue.component('media', {
|
||||||
Vue.set(this.status[dev.type], dev.name, status);
|
Vue.set(this.status[dev.type], dev.name, status);
|
||||||
},
|
},
|
||||||
|
|
||||||
onNewPlayingMedia: function(event) {
|
onMediaEvent: async function(event) {
|
||||||
console.log('NEW MEDIA');
|
let status = await request(event.plugin + '.status');
|
||||||
console.log(event);
|
|
||||||
},
|
|
||||||
|
|
||||||
onMediaPlay: function(event) {
|
if (event.resource) {
|
||||||
console.log('PLAY');
|
event.url = event.resource;
|
||||||
console.log(event);
|
delete event.resource;
|
||||||
},
|
}
|
||||||
|
|
||||||
onMediaPause: function(event) {
|
if (event.plugin.startsWith('media.'))
|
||||||
console.log('PAUSE');
|
event.plugin = event.plugin.substr(6);
|
||||||
console.log(event);
|
|
||||||
},
|
|
||||||
|
|
||||||
onMediaStop: function(event) {
|
if (this.status[event.player] && this.status[event.player][event.plugin])
|
||||||
console.log('STOP');
|
this.status[event.player][event.plugin] = status;
|
||||||
console.log(event);
|
else if (this.status[event.plugin] && this.status[event.plugin][event.player])
|
||||||
},
|
this.status[event.plugin][event.player] = status;
|
||||||
|
|
||||||
onMediaSeek: function(event) {
|
|
||||||
console.log('SEEK');
|
|
||||||
console.log(event);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -112,13 +164,18 @@ Vue.component('media', {
|
||||||
MediaHandlers[type].bus = this.bus;
|
MediaHandlers[type].bus = this.bus;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerEventHandler(this.onNewPlayingMedia, 'platypush.message.event.media.NewPlayingMediaEvent');
|
registerEventHandler(this.onMediaEvent,
|
||||||
registerEventHandler(this.onMediaPlay, 'platypush.message.event.media.MediaPlayEvent');
|
'platypush.message.event.media.NewPlayingMediaEvent',
|
||||||
registerEventHandler(this.onMediaPause, 'platypush.message.event.media.MediaPauseEvent');
|
'platypush.message.event.media.MediaPlayEvent',
|
||||||
registerEventHandler(this.onMediaStop, 'platypush.message.event.media.MediaStopEvent');
|
'platypush.message.event.media.MediaPauseEvent',
|
||||||
registerEventHandler(this.onMediaSeek, 'platypush.message.event.media.MediaSeekEvent');
|
'platypush.message.event.media.MediaStopEvent',
|
||||||
|
'platypush.message.event.media.MediaSeekEvent');
|
||||||
|
|
||||||
this.bus.$on('play', this.play);
|
this.bus.$on('play', this.play);
|
||||||
|
this.bus.$on('pause', this.pause);
|
||||||
|
this.bus.$on('stop', this.stop);
|
||||||
|
this.bus.$on('seek', this.seek);
|
||||||
|
this.bus.$on('volume', this.setVolume);
|
||||||
this.bus.$on('info', this.info);
|
this.bus.$on('info', this.info);
|
||||||
this.bus.$on('selected-device', this.selectDevice);
|
this.bus.$on('selected-device', this.selectDevice);
|
||||||
this.bus.$on('results-loading', this.onResultsLoading);
|
this.bus.$on('results-loading', this.onResultsLoading);
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
MediaPlayers.kodi = Vue.extend({
|
||||||
|
props: {
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'kodi',
|
||||||
|
},
|
||||||
|
|
||||||
|
device: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
url: undefined,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
accepts: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
youtube: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
iconClass: {
|
||||||
|
type: String,
|
||||||
|
default: 'fa fa-film',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
host: function() {
|
||||||
|
if (!this.device.url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.device.url.match(/^https?:\/\/([^:]+):(\d+).*$/)[1];
|
||||||
|
},
|
||||||
|
|
||||||
|
name: function() {
|
||||||
|
return this.host;
|
||||||
|
},
|
||||||
|
|
||||||
|
port: function() {
|
||||||
|
if (!this.device.url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseInt(this.device.url.match(/^https?:\/\/([^:]+):(\d+).*$/)[2]);
|
||||||
|
},
|
||||||
|
|
||||||
|
text: function() {
|
||||||
|
return 'Kodi '.concat('[', this.host, ']');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
scan: async function() {
|
||||||
|
if (!('media.kodi' in __plugins__)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ url: __plugins__['media.kodi'].url }
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
status: async function() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
|
||||||
|
play: async function(item) {
|
||||||
|
},
|
||||||
|
|
||||||
|
stop: async function() {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -67,7 +67,7 @@ MediaPlayers.local = Vue.extend({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
volume: async function(volume) {
|
setVolume: async function(volume) {
|
||||||
return await request(
|
return await request(
|
||||||
this.pluginPrefix.concat('.set_volume'),
|
this.pluginPrefix.concat('.set_volume'),
|
||||||
{volume: volume}
|
{volume: volume}
|
||||||
|
|
|
@ -18,6 +18,10 @@ Vue.component('media-results', {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
|
resize: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
|
@ -38,6 +42,7 @@ Vue.component('media-results', {
|
||||||
return {
|
return {
|
||||||
text: item.text,
|
text: item.text,
|
||||||
icon: item.icon,
|
icon: item.icon,
|
||||||
|
iconClass: item.iconClass,
|
||||||
click: function() {
|
click: function() {
|
||||||
item.action(self.selectedItem);
|
item.action(self.selectedItem);
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@ var utils = {
|
||||||
var t = {};
|
var t = {};
|
||||||
t.h = '' + parseInt(time/3600);
|
t.h = '' + parseInt(time/3600);
|
||||||
t.m = '' + parseInt(time/60 - t.h*60);
|
t.m = '' + parseInt(time/60 - t.h*60);
|
||||||
t.s = '' + parseInt(time - t.m*60);
|
t.s = '' + parseInt(time - (t.h*3600 + t.m*60));
|
||||||
|
|
||||||
for (var attr of ['m','s']) {
|
for (var attr of ['m','s']) {
|
||||||
if (parseInt(t[attr]) < 10) {
|
if (parseInt(t[attr]) < 10) {
|
||||||
|
|
|
@ -9,22 +9,28 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-6 playback-controls">
|
<div class="col-6 playback-controls">
|
||||||
<div class="row">
|
<div class="row buttons">
|
||||||
<button>
|
<button v-if="status.state === 'pause'" @click="bus.$emit('pause')">
|
||||||
<i class="fa fa-play"></i>
|
<i class="fa fa-play"></i>
|
||||||
</button>
|
</button>
|
||||||
<button>
|
<button v-if="status.state === 'play'" @click="bus.$emit('pause')">
|
||||||
|
<i class="fa fa-pause"></i>
|
||||||
|
</button>
|
||||||
|
<button v-if="status.state === 'play' || status.state === 'pause'" @click="bus.$emit('stop')">
|
||||||
<i class="fa fa-stop"></i>
|
<i class="fa fa-stop"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row position">
|
||||||
<span class="elapsed-time" v-text="'-:--'"></span>
|
<span class="elapsed-time" v-text="status.position ? convertTime(status.position) : '-:--'"></span>
|
||||||
<input type="range"
|
<input type="range"
|
||||||
|
v-model="status.position"
|
||||||
|
@input="bus.$emit('seek', $event.target.value)"
|
||||||
class="slider seek-slider"
|
class="slider seek-slider"
|
||||||
|
:disabled="!status.seekable || !status.duration"
|
||||||
min="0"
|
min="0"
|
||||||
max="100">
|
:max="status.duration || 0">
|
||||||
<span class="total-time" v-text="'-:--'"></span>
|
<span class="total-time" v-text="status.duration ? convertTime(status.duration) : '-:--'"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -34,9 +40,11 @@
|
||||||
<i class="fa fa-volume-up"></i>
|
<i class="fa fa-volume-up"></i>
|
||||||
</button>
|
</button>
|
||||||
<input type="range"
|
<input type="range"
|
||||||
|
v-model="status.volume"
|
||||||
|
@input="bus.$emit('volume', $event.target.value)"
|
||||||
class="slider volume-slider"
|
class="slider volume-slider"
|
||||||
min="0"
|
min="0"
|
||||||
max="100">
|
:max="status.volume_max || 100">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -27,11 +27,13 @@
|
||||||
:status="selectedDevice && status[selectedDevice.type] && status[selectedDevice.type][selectedDevice.name] ? status[selectedDevice.type][selectedDevice.name] : {}"
|
:status="selectedDevice && status[selectedDevice.type] && status[selectedDevice.type][selectedDevice.name] ? status[selectedDevice.type][selectedDevice.name] : {}"
|
||||||
:searching="loading.results"
|
:searching="loading.results"
|
||||||
:loading="loading.media"
|
:loading="loading.media"
|
||||||
|
:resize="selectedDevice && status[selectedDevice.type] && status[selectedDevice.type][selectedDevice.name] && (status[selectedDevice.type][selectedDevice.name].state === 'play' || status[selectedDevice.type][selectedDevice.name].state === 'pause')"
|
||||||
:results="results">
|
:results="results">
|
||||||
</media-results>
|
</media-results>
|
||||||
|
|
||||||
<media-controls :bus="bus"
|
<media-controls :bus="bus"
|
||||||
:status="selectedDevice && status[selectedDevice.type] && status[selectedDevice.type][selectedDevice.name] ? status[selectedDevice.type][selectedDevice.name] : {}">
|
:status="selectedDevice && status[selectedDevice.type] && status[selectedDevice.type][selectedDevice.name] ? status[selectedDevice.type][selectedDevice.name] : {}"
|
||||||
|
v-if="selectedDevice && status[selectedDevice.type] && status[selectedDevice.type][selectedDevice.name] && (status[selectedDevice.type][selectedDevice.name].state === 'play' || status[selectedDevice.type][selectedDevice.name].state === 'pause')">
|
||||||
</media-controls>
|
</media-controls>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script type="application/javascript" src="{{ url_for('static', filename='js/plugins/media/results.js') }}"></script>
|
<script type="application/javascript" src="{{ url_for('static', filename='js/plugins/media/results.js') }}"></script>
|
||||||
|
|
||||||
<script type="text/x-template" id="tmpl-media-results">
|
<script type="text/x-template" id="tmpl-media-results">
|
||||||
<div class="results">
|
<div class="results" :class="{resize: resize}">
|
||||||
<div class="empty" v-if="searching || loading || !results.length">
|
<div class="empty" v-if="searching || loading || !results.length">
|
||||||
<div class="searching" v-if="searching">Searching</div>
|
<div class="searching" v-if="searching">Searching</div>
|
||||||
<div class="loading" v-else-if="loading">Loading</div>
|
<div class="loading" v-else-if="loading">Loading</div>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
:key="item.url"
|
:key="item.url"
|
||||||
:bus="bus"
|
:bus="bus"
|
||||||
:selected="item.url && item.url === selectedItem.url"
|
:selected="item.url && item.url === selectedItem.url"
|
||||||
:active="item.url && item.url === status.url"
|
:active="(status.state === 'play' || status.state === 'pause') && item.url && item.url === status.url"
|
||||||
:item="item"
|
:item="item"
|
||||||
v-else>
|
v-else>
|
||||||
</media-item>
|
</media-item>
|
||||||
|
|
|
@ -4,8 +4,8 @@ from platypush.message.event import Event
|
||||||
class MediaEvent(Event):
|
class MediaEvent(Event):
|
||||||
""" Base class for media events """
|
""" Base class for media events """
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, player=None, plugin=None, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(player=player, plugin=plugin, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class MediaPlayRequestEvent(MediaEvent):
|
class MediaPlayRequestEvent(MediaEvent):
|
||||||
|
@ -13,13 +13,13 @@ class MediaPlayRequestEvent(MediaEvent):
|
||||||
Event triggered when a new media playback request is received
|
Event triggered when a new media playback request is received
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, resource=None, title=None, *args, **kwargs):
|
def __init__(self, player=None, plugin=None, resource=None, title=None, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param resource: File name or URI of the played video
|
:param resource: File name or URI of the played video
|
||||||
:type resource: str
|
:type resource: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(*args, resource=resource, title=title, **kwargs)
|
super().__init__(*args, player=player, plugin=plugin, resource=resource, title=title, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class MediaPlayEvent(MediaEvent):
|
class MediaPlayEvent(MediaEvent):
|
||||||
|
@ -27,13 +27,13 @@ class MediaPlayEvent(MediaEvent):
|
||||||
Event triggered when a new media content is played
|
Event triggered when a new media content is played
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, resource=None, title=None, *args, **kwargs):
|
def __init__(self, player=None, plugin=None, resource=None, title=None, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param resource: File name or URI of the played video
|
:param resource: File name or URI of the played video
|
||||||
:type resource: str
|
:type resource: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(*args, resource=resource, title=title, **kwargs)
|
super().__init__(*args, player=player, plugin=plugin, resource=resource, title=title, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class MediaStopEvent(MediaEvent):
|
class MediaStopEvent(MediaEvent):
|
||||||
|
@ -41,8 +41,8 @@ class MediaStopEvent(MediaEvent):
|
||||||
Event triggered when a media is stopped
|
Event triggered when a media is stopped
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, player=None, plugin=None, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, player=player, plugin=plugin, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class MediaPauseEvent(MediaEvent):
|
class MediaPauseEvent(MediaEvent):
|
||||||
|
@ -50,8 +50,8 @@ class MediaPauseEvent(MediaEvent):
|
||||||
Event triggered when a media playback is paused
|
Event triggered when a media playback is paused
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, player=None, plugin=None, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, player=player, plugin=plugin, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class MediaSeekEvent(MediaEvent):
|
class MediaSeekEvent(MediaEvent):
|
||||||
|
@ -59,8 +59,8 @@ class MediaSeekEvent(MediaEvent):
|
||||||
Event triggered when the time position in the media changes
|
Event triggered when the time position in the media changes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, position, *args, **kwargs):
|
def __init__(self, position, player=None, plugin=None, *args, **kwargs):
|
||||||
super().__init__(*args, position=position, **kwargs)
|
super().__init__(*args, player=player, plugin=plugin, position=position, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class MediaVolumeChangedEvent(MediaEvent):
|
class MediaVolumeChangedEvent(MediaEvent):
|
||||||
|
@ -68,8 +68,8 @@ class MediaVolumeChangedEvent(MediaEvent):
|
||||||
Event triggered when the media volume changes
|
Event triggered when the media volume changes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, volume, *args, **kwargs):
|
def __init__(self, volume, player=None, plugin=None, *args, **kwargs):
|
||||||
super().__init__(*args, volume=volume, **kwargs)
|
super().__init__(*args, player=player, plugin=plugin, volume=volume, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class MediaMuteChangedEvent(MediaEvent):
|
class MediaMuteChangedEvent(MediaEvent):
|
||||||
|
@ -77,8 +77,8 @@ class MediaMuteChangedEvent(MediaEvent):
|
||||||
Event triggered when the media is muted/unmuted
|
Event triggered when the media is muted/unmuted
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, mute, *args, **kwargs):
|
def __init__(self, mute, player=None, plugin=None, *args, **kwargs):
|
||||||
super().__init__(*args, mute=mute, **kwargs)
|
super().__init__(*args, player=player, plugin=plugin, mute=mute, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class NewPlayingMediaEvent(MediaEvent):
|
class NewPlayingMediaEvent(MediaEvent):
|
||||||
|
@ -86,13 +86,13 @@ class NewPlayingMediaEvent(MediaEvent):
|
||||||
Event triggered when a new media source is being played
|
Event triggered when a new media source is being played
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, resource=None, *args, **kwargs):
|
def __init__(self, player=None, plugin=None, resource=None, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param resource: File name or URI of the played resource
|
:param resource: File name or URI of the played resource
|
||||||
:type resource: str
|
:type resource: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(*args, resource=resource, **kwargs)
|
super().__init__(*args, player=player, plugin=plugin, resource=resource, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -59,6 +59,11 @@ class MediaMpvPlugin(MediaPlugin):
|
||||||
self._player = mpv.MPV(**mpv_args)
|
self._player = mpv.MPV(**mpv_args)
|
||||||
self._player.register_event_callback(self._event_callback())
|
self._player.register_event_callback(self._event_callback())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _post_event(evt_type, **evt):
|
||||||
|
bus = get_bus()
|
||||||
|
bus.post(evt_type(player='local', plugin='media.mpv', **evt))
|
||||||
|
|
||||||
def _event_callback(self):
|
def _event_callback(self):
|
||||||
def callback(event):
|
def callback(event):
|
||||||
from mpv import MpvEventID as Event
|
from mpv import MpvEventID as Event
|
||||||
|
@ -70,17 +75,15 @@ class MediaMpvPlugin(MediaPlugin):
|
||||||
if not evt:
|
if not evt:
|
||||||
return
|
return
|
||||||
|
|
||||||
bus = get_bus()
|
|
||||||
if (evt == Event.FILE_LOADED or evt == Event.START_FILE) and self._get_current_resource():
|
if (evt == Event.FILE_LOADED or evt == Event.START_FILE) and self._get_current_resource():
|
||||||
self._playback_rebounce_event.set()
|
self._playback_rebounce_event.set()
|
||||||
bus.post(NewPlayingMediaEvent(resource=self._get_current_resource(), title=self._player.filename))
|
self._post_event(NewPlayingMediaEvent, resource=self._get_current_resource(), title=self._player.filename)
|
||||||
bus.post(MediaPlayEvent(resource=self._get_current_resource(), title=self._player.filename))
|
|
||||||
elif evt == Event.PLAYBACK_RESTART:
|
elif evt == Event.PLAYBACK_RESTART:
|
||||||
self._playback_rebounce_event.set()
|
self._playback_rebounce_event.set()
|
||||||
elif evt == Event.PAUSE:
|
elif evt == Event.PAUSE:
|
||||||
bus.post(MediaPauseEvent(resource=self._get_current_resource(), title=self._player.filename))
|
self._post_event(MediaPauseEvent, resource=self._get_current_resource(), title=self._player.filename)
|
||||||
elif evt == Event.UNPAUSE:
|
elif evt == Event.UNPAUSE:
|
||||||
bus.post(MediaPlayEvent(resource=self._get_current_resource(), title=self._player.filename))
|
self._post_event(MediaPlayEvent, resource=self._get_current_resource(), title=self._player.filename)
|
||||||
elif evt == Event.SHUTDOWN or (
|
elif evt == Event.SHUTDOWN or (
|
||||||
evt == Event.END_FILE and event.get('event', {}).get('reason')
|
evt == Event.END_FILE and event.get('event', {}).get('reason')
|
||||||
in [EndFile.EOF_OR_INIT_FAILURE, EndFile.ABORTED, EndFile.QUIT]):
|
in [EndFile.EOF_OR_INIT_FAILURE, EndFile.ABORTED, EndFile.QUIT]):
|
||||||
|
@ -90,12 +93,12 @@ class MediaMpvPlugin(MediaPlugin):
|
||||||
return
|
return
|
||||||
|
|
||||||
self._player = None
|
self._player = None
|
||||||
bus.post(MediaStopEvent())
|
self._post_event(MediaStopEvent)
|
||||||
|
|
||||||
for callback in self._on_stop_callbacks:
|
for callback in self._on_stop_callbacks:
|
||||||
callback()
|
callback()
|
||||||
elif evt == Event.SEEK:
|
elif evt == Event.SEEK:
|
||||||
bus.post(MediaSeekEvent(position=self._player.playback_time))
|
self._post_event(MediaSeekEvent, position=self._player.playback_time)
|
||||||
|
|
||||||
return callback
|
return callback
|
||||||
|
|
||||||
|
@ -210,7 +213,7 @@ class MediaMpvPlugin(MediaPlugin):
|
||||||
|
|
||||||
volume = max(0, min([self._player.volume_max, volume]))
|
volume = max(0, min([self._player.volume_max, volume]))
|
||||||
self._player.volume = volume
|
self._player.volume = volume
|
||||||
return {'volume': volume}
|
return self.status()
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def seek(self, position):
|
def seek(self, position):
|
||||||
|
@ -382,31 +385,18 @@ class MediaMpvPlugin(MediaPlugin):
|
||||||
return {'state': PlayerState.STOP.value}
|
return {'state': PlayerState.STOP.value}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'alang': getattr(self._player, 'alang'),
|
|
||||||
'aspect': getattr(self._player, 'aspect'),
|
'aspect': getattr(self._player, 'aspect'),
|
||||||
'audio': getattr(self._player, 'audio'),
|
'audio': getattr(self._player, 'audio'),
|
||||||
'audio_bitrate': getattr(self._player, 'audio_bitrate'),
|
'audio_bitrate': getattr(self._player, 'audio_bitrate'),
|
||||||
'audio_buffer': getattr(self._player, 'audio_buffer'),
|
|
||||||
'audio_channels': getattr(self._player, 'audio_channels'),
|
'audio_channels': getattr(self._player, 'audio_channels'),
|
||||||
'audio_client_name': getattr(self._player, 'audio_client_name'),
|
'audio_codec': getattr(self._player, 'audio_codec_name'),
|
||||||
'audio_codec': getattr(self._player, 'audio_codec'),
|
|
||||||
'audio_codec_name': getattr(self._player, 'audio_codec_name'),
|
|
||||||
'audio_delay': getattr(self._player, 'audio_delay'),
|
'audio_delay': getattr(self._player, 'audio_delay'),
|
||||||
'audio_device': getattr(self._player, 'audio_device'),
|
'audio_output': getattr(self._player, 'current_ao'),
|
||||||
'audio_device_list': getattr(self._player, 'audio_device_list'),
|
|
||||||
'audio_exclusive': getattr(self._player, 'audio_exclusive'),
|
|
||||||
'audio_file_paths': getattr(self._player, 'audio_file_paths'),
|
'audio_file_paths': getattr(self._player, 'audio_file_paths'),
|
||||||
'audio_files': getattr(self._player, 'audio_files'),
|
'audio_files': getattr(self._player, 'audio_files'),
|
||||||
'audio_format': getattr(self._player, 'audio_format'),
|
|
||||||
'audio_out_params': getattr(self._player, 'audio_out_params'),
|
|
||||||
'audio_params': getattr(self._player, 'audio_params'),
|
'audio_params': getattr(self._player, 'audio_params'),
|
||||||
'audio_mixer_device': getattr(self._player, 'alsa_mixer_device'),
|
'audio_mixer': getattr(self._player, 'alsa_mixer_device'),
|
||||||
'audio_mixer_index': getattr(self._player, 'alsa_mixer_index'),
|
|
||||||
'audio_mixer_name': getattr(self._player, 'alsa_mixer_name'),
|
|
||||||
'autosub': getattr(self._player, 'autosub'),
|
|
||||||
'autosync': getattr(self._player, 'autosync'),
|
'autosync': getattr(self._player, 'autosync'),
|
||||||
'background': getattr(self._player, 'background'),
|
|
||||||
'border': getattr(self._player, 'border'),
|
|
||||||
'brightness': getattr(self._player, 'brightness'),
|
'brightness': getattr(self._player, 'brightness'),
|
||||||
'chapter': getattr(self._player, 'chapter'),
|
'chapter': getattr(self._player, 'chapter'),
|
||||||
'chapter_list': getattr(self._player, 'chapter_list'),
|
'chapter_list': getattr(self._player, 'chapter_list'),
|
||||||
|
@ -416,13 +406,11 @@ class MediaMpvPlugin(MediaPlugin):
|
||||||
'clock': getattr(self._player, 'clock'),
|
'clock': getattr(self._player, 'clock'),
|
||||||
'cookies': getattr(self._player, 'cookies'),
|
'cookies': getattr(self._player, 'cookies'),
|
||||||
'cookies_file': getattr(self._player, 'cookies_file'),
|
'cookies_file': getattr(self._player, 'cookies_file'),
|
||||||
'current_ao': getattr(self._player, 'current_ao'),
|
|
||||||
'current_vo': getattr(self._player, 'current_vo'),
|
|
||||||
'delay': getattr(self._player, 'delay'),
|
'delay': getattr(self._player, 'delay'),
|
||||||
'display_names': getattr(self._player, 'display_names'),
|
'displays': getattr(self._player, 'display_names'),
|
||||||
'end': getattr(self._player, 'end'),
|
'duration': getattr(self._player, 'playback_time', 0) +
|
||||||
'endpos': getattr(self._player, 'endpos'),
|
getattr(self._player, 'playtime_remaining', 0)
|
||||||
'eof_reached': getattr(self._player, 'eof_reached'),
|
if getattr(self._player, 'playtime_remaining') else None,
|
||||||
'file_format': getattr(self._player, 'file_format'),
|
'file_format': getattr(self._player, 'file_format'),
|
||||||
'filename': getattr(self._player, 'filename'),
|
'filename': getattr(self._player, 'filename'),
|
||||||
'file_size': getattr(self._player, 'file_size'),
|
'file_size': getattr(self._player, 'file_size'),
|
||||||
|
@ -432,11 +420,8 @@ class MediaMpvPlugin(MediaPlugin):
|
||||||
'height': getattr(self._player, 'height'),
|
'height': getattr(self._player, 'height'),
|
||||||
'idle': getattr(self._player, 'idle'),
|
'idle': getattr(self._player, 'idle'),
|
||||||
'idle_active': getattr(self._player, 'idle_active'),
|
'idle_active': getattr(self._player, 'idle_active'),
|
||||||
'length': getattr(self._player, 'playback_time', 0) + getattr(self._player, 'playtime_remaining', 0)
|
|
||||||
if getattr(self._player, 'playtime_remaining') else None,
|
|
||||||
'loop': getattr(self._player, 'loop'),
|
'loop': getattr(self._player, 'loop'),
|
||||||
'media_title': getattr(self._player, 'loop'),
|
'media_title': getattr(self._player, 'media_title'),
|
||||||
'mpv_configuration': getattr(self._player, 'mpv_configuration'),
|
|
||||||
'mpv_version': getattr(self._player, 'mpv_version'),
|
'mpv_version': getattr(self._player, 'mpv_version'),
|
||||||
'mute': getattr(self._player, 'mute'),
|
'mute': getattr(self._player, 'mute'),
|
||||||
'name': getattr(self._player, 'name'),
|
'name': getattr(self._player, 'name'),
|
||||||
|
@ -464,17 +449,8 @@ class MediaMpvPlugin(MediaPlugin):
|
||||||
'sub_paths': getattr(self._player, 'sub_paths'),
|
'sub_paths': getattr(self._player, 'sub_paths'),
|
||||||
'sub_text': getattr(self._player, 'sub_text'),
|
'sub_text': getattr(self._player, 'sub_text'),
|
||||||
'subdelay': getattr(self._player, 'subdelay'),
|
'subdelay': getattr(self._player, 'subdelay'),
|
||||||
'terminal': getattr(self._player, 'terminal'),
|
|
||||||
'time_start': getattr(self._player, 'time_start'),
|
'time_start': getattr(self._player, 'time_start'),
|
||||||
'title': getattr(self._player, 'filename'),
|
'title': getattr(self._player, 'filename'),
|
||||||
'tv_alsa': getattr(self._player, 'tv_alsa'),
|
|
||||||
'tv_audio': getattr(self._player, 'tv_audio'),
|
|
||||||
'tv_audiorate': getattr(self._player, 'tv_audiorate'),
|
|
||||||
'tv_channels': getattr(self._player, 'tv_channels'),
|
|
||||||
'tv_device': getattr(self._player, 'tv_device'),
|
|
||||||
'tv_height': getattr(self._player, 'tv_height'),
|
|
||||||
'tv_volume': getattr(self._player, 'tv_volume'),
|
|
||||||
'tv_width': getattr(self._player, 'tv_width'),
|
|
||||||
'url': self._get_current_resource(),
|
'url': self._get_current_resource(),
|
||||||
'user_agent': getattr(self._player, 'user_agent'),
|
'user_agent': getattr(self._player, 'user_agent'),
|
||||||
'video': getattr(self._player, 'video'),
|
'video': getattr(self._player, 'video'),
|
||||||
|
@ -482,19 +458,18 @@ class MediaMpvPlugin(MediaPlugin):
|
||||||
'video_align_y': getattr(self._player, 'video_align_y'),
|
'video_align_y': getattr(self._player, 'video_align_y'),
|
||||||
'video_aspect': getattr(self._player, 'video_aspect'),
|
'video_aspect': getattr(self._player, 'video_aspect'),
|
||||||
'video_bitrate': getattr(self._player, 'video_bitrate'),
|
'video_bitrate': getattr(self._player, 'video_bitrate'),
|
||||||
|
'video_output': getattr(self._player, 'current_vo'),
|
||||||
'video_codec': getattr(self._player, 'video_codec'),
|
'video_codec': getattr(self._player, 'video_codec'),
|
||||||
'video_format': getattr(self._player, 'video_format'),
|
'video_format': getattr(self._player, 'video_format'),
|
||||||
'video_params': getattr(self._player, 'video_params'),
|
'video_params': getattr(self._player, 'video_params'),
|
||||||
'video_sync': getattr(self._player, 'video_sync'),
|
'video_sync': getattr(self._player, 'video_sync'),
|
||||||
'video_zoom': getattr(self._player, 'video_zoom'),
|
'video_zoom': getattr(self._player, 'video_zoom'),
|
||||||
'vlang': getattr(self._player, 'vlang'),
|
|
||||||
'volume': getattr(self._player, 'volume'),
|
'volume': getattr(self._player, 'volume'),
|
||||||
'volume_max': getattr(self._player, 'volume_max'),
|
'volume_max': getattr(self._player, 'volume_max'),
|
||||||
'width': getattr(self._player, 'width'),
|
'width': getattr(self._player, 'width'),
|
||||||
'window_minimized': getattr(self._player, 'window_minimized'),
|
'window_minimized': getattr(self._player, 'window_minimized'),
|
||||||
'window_scale': getattr(self._player, 'window_scale'),
|
'window_scale': getattr(self._player, 'window_scale'),
|
||||||
'working_directory': getattr(self._player, 'working_directory'),
|
'working_directory': getattr(self._player, 'working_directory'),
|
||||||
'ytdl': getattr(self._player, 'ytdl'),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def on_stop(self, callback):
|
def on_stop(self, callback):
|
||||||
|
|
Loading…
Reference in a new issue