forked from platypush/platypush
Migrating media plugins [WIP]
This commit is contained in:
parent
67d3b40772
commit
6ae76f1f38
67 changed files with 1294 additions and 87 deletions
1
platypush/backend/http/dist/icons/kodi.svg
vendored
Normal file
1
platypush/backend/http/dist/icons/kodi.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><path d="M25.14 2c-.76 0-1.47.3-2 .84l-5.85 5.93a.969.969 0 0 0-.29.7v20.69c0 .4.25.77.62.92a1 1 0 0 0 .38.08c.26 0 .52-.11.71-.3l17.35-17.58a1 1 0 0 0 0-1.4l-8.93-9.04C26.6 2.3 25.89 2 25.14 2zM11.122 15.813a1 1 0 0 0-.8.257L2.9 22.96C2.32 23.5 2 24.22 2 25c0 .77.32 1.49.9 2.03l7.42 6.9c.19.17.43.27.68.27.14 0 .27-.03.4-.09.36-.16.6-.52.6-.91V16.8c0-.4-.24-.76-.6-.91a.982.982 0 0 0-.279-.078zm29.473.962c-.261 0-.523.096-.713.286l-8.592 8.68a.996.996 0 0 0 0 1.41l7.16 7.23c.19.19.451.299.711.299.27 0 .52-.11.71-.3l7.31-7.37a2.871 2.871 0 0 0 0-4.02l-5.88-5.93a.985.985 0 0 0-.706-.285zm-13.768 13.86a.985.985 0 0 0-.707.285l-8.83 8.91a.984.984 0 0 0 0 1.4l5.88 5.94c.54.53 1.25.83 2 .83.76 0 1.47-.3 2-.83l7.542-7.6a.996.996 0 0 0 0-1.41l-7.172-7.24a1.007 1.007 0 0 0-.713-.285z"/><metadata><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:dc="http://purl.org/dc/elements/1.1/"><rdf:Description about="https://iconscout.com/legal#licenses" dc:title="kodi,filled" dc:description="kodi,filled" dc:publisher="Iconscout" dc:date="2017-12-09" dc:format="image/svg+xml" dc:language="en"><dc:creator><rdf:Bag><rdf:li>Icons8</rdf:li></rdf:Bag></dc:creator></rdf:Description></rdf:RDF></metadata></svg>
|
After Width: | Height: | Size: 1.3 KiB |
2
platypush/backend/http/dist/index.html
vendored
2
platypush/backend/http/dist/index.html
vendored
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>platypush</title><link href="/static/css/chunk-24ff873d.64d9bc0b.css" rel="prefetch"><link href="/static/css/chunk-3b44ec4e.0c4a18da.css" rel="prefetch"><link href="/static/css/chunk-45939517.e4a1ddf3.css" rel="prefetch"><link href="/static/css/chunk-4bbbb9a3.3108d379.css" rel="prefetch"><link href="/static/css/chunk-4c0b0f48.009b6a70.css" rel="prefetch"><link href="/static/css/chunk-4eeb8349.2026dd4f.css" rel="prefetch"><link href="/static/css/chunk-53360c78.c486a396.css" rel="prefetch"><link href="/static/css/chunk-5fea187e.d6e3f8eb.css" rel="prefetch"><link href="/static/css/chunk-62a3d08e.6cb54f10.css" rel="prefetch"><link href="/static/css/chunk-d8561e02.b52f89a0.css" rel="prefetch"><link href="/static/css/chunk-e8078048.c6785c78.css" rel="prefetch"><link href="/static/js/chunk-24ff873d.691c883d.js" rel="prefetch"><link href="/static/js/chunk-2d2091df.1e51ae4c.js" rel="prefetch"><link href="/static/js/chunk-2d21da1a.adf909a2.js" rel="prefetch"><link href="/static/js/chunk-3b44ec4e.904c7e10.js" rel="prefetch"><link href="/static/js/chunk-45939517.c0034c6b.js" rel="prefetch"><link href="/static/js/chunk-4bbbb9a3.251fff37.js" rel="prefetch"><link href="/static/js/chunk-4c0b0f48.366980a2.js" rel="prefetch"><link href="/static/js/chunk-4eeb8349.5c94d58c.js" rel="prefetch"><link href="/static/js/chunk-53360c78.51ee7c96.js" rel="prefetch"><link href="/static/js/chunk-5fea187e.4466d92f.js" rel="prefetch"><link href="/static/js/chunk-62a3d08e.17d3c86d.js" rel="prefetch"><link href="/static/js/chunk-d8561e02.1e366cb3.js" rel="prefetch"><link href="/static/js/chunk-e8078048.ce29b8d4.js" rel="prefetch"><link href="/static/css/app.4868c461.css" rel="preload" as="style"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="preload" as="style"><link href="/static/js/app.3770dd06.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.30e3a6cb.js" rel="preload" as="script"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="stylesheet"><link href="/static/css/app.4868c461.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.30e3a6cb.js"></script><script src="/static/js/app.3770dd06.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>platypush</title><link href="/static/css/chunk-24ff873d.934c66a7.css" rel="prefetch"><link href="/static/css/chunk-3b44ec4e.120a35b6.css" rel="prefetch"><link href="/static/css/chunk-3ffdd2f0.63f19efc.css" rel="prefetch"><link href="/static/css/chunk-45939517.f01c29cc.css" rel="prefetch"><link href="/static/css/chunk-4bbbb9a3.1a24453a.css" rel="prefetch"><link href="/static/css/chunk-4c0b0f48.6b856cb1.css" rel="prefetch"><link href="/static/css/chunk-4eeb8349.804fa9fd.css" rel="prefetch"><link href="/static/css/chunk-53360c78.d5bec80e.css" rel="prefetch"><link href="/static/css/chunk-62a3d08e.a5b70794.css" rel="prefetch"><link href="/static/css/chunk-a60951ae.5863e0d4.css" rel="prefetch"><link href="/static/css/chunk-d8561e02.105050d2.css" rel="prefetch"><link href="/static/css/chunk-e8078048.9f279ae9.css" rel="prefetch"><link href="/static/css/chunk-fa962a0a.4d6f6ce2.css" rel="prefetch"><link href="/static/js/chunk-24ff873d.691c883d.js" rel="prefetch"><link href="/static/js/chunk-2d0b270c.795e924c.js" rel="prefetch"><link href="/static/js/chunk-2d0c1eb0.6a91d527.js" rel="prefetch"><link href="/static/js/chunk-2d2091df.1e51ae4c.js" rel="prefetch"><link href="/static/js/chunk-2d21b0dc.df7794a5.js" rel="prefetch"><link href="/static/js/chunk-2d21da1a.adf909a2.js" rel="prefetch"><link href="/static/js/chunk-2d231217.ec7b8ee5.js" rel="prefetch"><link href="/static/js/chunk-3b44ec4e.904c7e10.js" rel="prefetch"><link href="/static/js/chunk-3ffdd2f0.c56c2562.js" rel="prefetch"><link href="/static/js/chunk-45939517.c0034c6b.js" rel="prefetch"><link href="/static/js/chunk-4bbbb9a3.251fff37.js" rel="prefetch"><link href="/static/js/chunk-4c0b0f48.366980a2.js" rel="prefetch"><link href="/static/js/chunk-4eeb8349.5c94d58c.js" rel="prefetch"><link href="/static/js/chunk-53360c78.51ee7c96.js" rel="prefetch"><link href="/static/js/chunk-62a3d08e.17d3c86d.js" rel="prefetch"><link href="/static/js/chunk-a60951ae.8bfe85bc.js" rel="prefetch"><link href="/static/js/chunk-d8561e02.1e366cb3.js" rel="prefetch"><link href="/static/js/chunk-e8078048.ce29b8d4.js" rel="prefetch"><link href="/static/js/chunk-fa962a0a.314e65ea.js" rel="prefetch"><link href="/static/css/app.fd1d75f5.css" rel="preload" as="style"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="preload" as="style"><link href="/static/js/app.0247d3bf.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.ac361ae9.js" rel="preload" as="script"><link href="/static/css/chunk-vendors.5dad8b00.css" rel="stylesheet"><link href="/static/css/app.fd1d75f5.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.ac361ae9.js"></script><script src="/static/js/app.0247d3bf.js"></script></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
7
platypush/backend/http/dist/static/css/chunk-a60951ae.5863e0d4.css
vendored
Normal file
7
platypush/backend/http/dist/static/css/chunk-a60951ae.5863e0d4.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
9
platypush/backend/http/dist/static/css/chunk-fa962a0a.4d6f6ce2.css
vendored
Normal file
9
platypush/backend/http/dist/static/css/chunk-fa962a0a.4d6f6ce2.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2
platypush/backend/http/dist/static/js/app.0247d3bf.js
vendored
Normal file
2
platypush/backend/http/dist/static/js/app.0247d3bf.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/dist/static/js/app.0247d3bf.js.map
vendored
Normal file
1
platypush/backend/http/dist/static/js/app.0247d3bf.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
platypush/backend/http/dist/static/js/chunk-2d0b270c.795e924c.js
vendored
Normal file
2
platypush/backend/http/dist/static/js/chunk-2d0b270c.795e924c.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0b270c"],{"23b7":function(e,a,n){"use strict";n.r(a);var c=n("7a23"),d=Object(c["K"])("data-v-52effd7c"),t=d((function(e,a,n,d,t,i){var p=Object(c["z"])("Media");return Object(c["r"])(),Object(c["e"])(p,{"plugin-name":"media.mpv"})})),i=n("3951"),p={name:"MediaMpv",components:{Media:i["default"]}};p.render=t,p.__scopeId="data-v-52effd7c";a["default"]=p}}]);
|
||||
//# sourceMappingURL=chunk-2d0b270c.795e924c.js.map
|
1
platypush/backend/http/dist/static/js/chunk-2d0b270c.795e924c.js.map
vendored
Normal file
1
platypush/backend/http/dist/static/js/chunk-2d0b270c.795e924c.js.map
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["webpack:///./src/components/panels/MediaMpv/Index.vue","webpack:///./src/components/panels/MediaMpv/Index.vue?ac2c"],"names":["plugin-name","name","components","Media","render","__scopeId"],"mappings":"8PACE,eAAiC,GAA1BA,cAAY,iB,YAMN,GACbC,KAAM,WACNC,WAAY,CAACC,MAAA,eCNf,EAAOC,OAAS,EAChB,EAAOC,UAAY,kBAEJ","file":"static/js/chunk-2d0b270c.795e924c.js","sourcesContent":["<template>\n <Media plugin-name=\"media.mpv\" />\n</template>\n\n<script>\nimport Media from '@/components/panels/Media/Index'\n\nexport default {\n name: \"MediaMpv\",\n components: {Media},\n}\n</script>\n\n<style scoped>\n\n</style>\n","import { render } from \"./Index.vue?vue&type=template&id=52effd7c&scoped=true&bindings={}\"\nimport script from \"./Index.vue?vue&type=script&lang=js\"\nexport * from \"./Index.vue?vue&type=script&lang=js\"\nscript.render = render\nscript.__scopeId = \"data-v-52effd7c\"\n\nexport default script"],"sourceRoot":""}
|
2
platypush/backend/http/dist/static/js/chunk-2d0c1eb0.6a91d527.js
vendored
Normal file
2
platypush/backend/http/dist/static/js/chunk-2d0c1eb0.6a91d527.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0c1eb0"],{"47a8":function(e,a,n){"use strict";n.r(a);var t=n("7a23"),c=Object(t["K"])("data-v-08ab61b7"),d=c((function(e,a,n,c,d,b){var r=Object(t["z"])("Media");return Object(t["r"])(),Object(t["e"])(r,{"plugin-name":"media.mplayer"})})),b=n("3951"),r={name:"MediaMplayer",components:{Media:b["default"]}};r.render=d,r.__scopeId="data-v-08ab61b7";a["default"]=r}}]);
|
||||
//# sourceMappingURL=chunk-2d0c1eb0.6a91d527.js.map
|
1
platypush/backend/http/dist/static/js/chunk-2d0c1eb0.6a91d527.js.map
vendored
Normal file
1
platypush/backend/http/dist/static/js/chunk-2d0c1eb0.6a91d527.js.map
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["webpack:///./src/components/panels/MediaMplayer/Index.vue","webpack:///./src/components/panels/MediaMplayer/Index.vue?9e10"],"names":["plugin-name","name","components","Media","render","__scopeId"],"mappings":"8PACE,eAAqC,GAA9BA,cAAY,qB,YAMN,GACbC,KAAM,eACNC,WAAY,CAACC,MAAA,eCNf,EAAOC,OAAS,EAChB,EAAOC,UAAY,kBAEJ","file":"static/js/chunk-2d0c1eb0.6a91d527.js","sourcesContent":["<template>\n <Media plugin-name=\"media.mplayer\" />\n</template>\n\n<script>\nimport Media from '@/components/panels/Media/Index'\n\nexport default {\n name: \"MediaMplayer\",\n components: {Media},\n}\n</script>\n\n<style scoped>\n\n</style>\n","import { render } from \"./Index.vue?vue&type=template&id=08ab61b7&scoped=true&bindings={}\"\nimport script from \"./Index.vue?vue&type=script&lang=js\"\nexport * from \"./Index.vue?vue&type=script&lang=js\"\nscript.render = render\nscript.__scopeId = \"data-v-08ab61b7\"\n\nexport default script"],"sourceRoot":""}
|
2
platypush/backend/http/dist/static/js/chunk-2d21b0dc.df7794a5.js
vendored
Normal file
2
platypush/backend/http/dist/static/js/chunk-2d21b0dc.df7794a5.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d21b0dc"],{bdae:function(e,a,n){"use strict";n.r(a);var c=n("7a23"),d=Object(c["K"])("data-v-9233e214"),t=d((function(e,a,n,d,t,i){var o=Object(c["z"])("Media");return Object(c["r"])(),Object(c["e"])(o,{"plugin-name":"media.vlc"})})),i=n("3951"),o={name:"MediaVlc",components:{Media:i["default"]}};o.render=t,o.__scopeId="data-v-9233e214";a["default"]=o}}]);
|
||||
//# sourceMappingURL=chunk-2d21b0dc.df7794a5.js.map
|
1
platypush/backend/http/dist/static/js/chunk-2d21b0dc.df7794a5.js.map
vendored
Normal file
1
platypush/backend/http/dist/static/js/chunk-2d21b0dc.df7794a5.js.map
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["webpack:///./src/components/panels/MediaVlc/Index.vue","webpack:///./src/components/panels/MediaVlc/Index.vue?e504"],"names":["plugin-name","name","components","Media","render","__scopeId"],"mappings":"4PACE,eAAiC,GAA1BA,cAAY,iB,YAMN,GACbC,KAAM,WACNC,WAAY,CAACC,MAAA,eCNf,EAAOC,OAAS,EAChB,EAAOC,UAAY,kBAEJ","file":"static/js/chunk-2d21b0dc.df7794a5.js","sourcesContent":["<template>\n <Media plugin-name=\"media.vlc\" />\n</template>\n\n<script>\nimport Media from '@/components/panels/Media/Index'\n\nexport default {\n name: \"MediaVlc\",\n components: {Media},\n}\n</script>\n\n<style scoped>\n\n</style>\n","import { render } from \"./Index.vue?vue&type=template&id=9233e214&scoped=true&bindings={}\"\nimport script from \"./Index.vue?vue&type=script&lang=js\"\nexport * from \"./Index.vue?vue&type=script&lang=js\"\nscript.render = render\nscript.__scopeId = \"data-v-9233e214\"\n\nexport default script"],"sourceRoot":""}
|
2
platypush/backend/http/dist/static/js/chunk-2d231217.ec7b8ee5.js
vendored
Normal file
2
platypush/backend/http/dist/static/js/chunk-2d231217.ec7b8ee5.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d231217"],{eede:function(e,a,d){"use strict";d.r(a);var n=d("7a23"),c=Object(n["K"])("data-v-7264d7fc"),t=c((function(e,a,d,c,t,i){var o=Object(n["z"])("Media");return Object(n["r"])(),Object(n["e"])(o,{"plugin-name":"media.omxplayer"})})),i=d("3951"),o={name:"MediaMpv",components:{Media:i["default"]}};o.render=t,o.__scopeId="data-v-7264d7fc";a["default"]=o}}]);
|
||||
//# sourceMappingURL=chunk-2d231217.ec7b8ee5.js.map
|
1
platypush/backend/http/dist/static/js/chunk-2d231217.ec7b8ee5.js.map
vendored
Normal file
1
platypush/backend/http/dist/static/js/chunk-2d231217.ec7b8ee5.js.map
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["webpack:///./src/components/panels/MediaOmxplayer/Index.vue","webpack:///./src/components/panels/MediaOmxplayer/Index.vue?207d"],"names":["plugin-name","name","components","Media","render","__scopeId"],"mappings":"4PACE,eAAuC,GAAhCA,cAAY,uB,YAMN,GACbC,KAAM,WACNC,WAAY,CAACC,MAAA,eCNf,EAAOC,OAAS,EAChB,EAAOC,UAAY,kBAEJ","file":"static/js/chunk-2d231217.ec7b8ee5.js","sourcesContent":["<template>\n <Media plugin-name=\"media.omxplayer\" />\n</template>\n\n<script>\nimport Media from '@/components/panels/Media/Index'\n\nexport default {\n name: \"MediaMpv\",\n components: {Media},\n}\n</script>\n\n<style scoped>\n\n</style>\n","import { render } from \"./Index.vue?vue&type=template&id=7264d7fc&scoped=true&bindings={}\"\nimport script from \"./Index.vue?vue&type=script&lang=js\"\nexport * from \"./Index.vue?vue&type=script&lang=js\"\nscript.render = render\nscript.__scopeId = \"data-v-7264d7fc\"\n\nexport default script"],"sourceRoot":""}
|
2
platypush/backend/http/dist/static/js/chunk-3ffdd2f0.c56c2562.js
vendored
Normal file
2
platypush/backend/http/dist/static/js/chunk-3ffdd2f0.c56c2562.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/dist/static/js/chunk-3ffdd2f0.c56c2562.js.map
vendored
Normal file
1
platypush/backend/http/dist/static/js/chunk-3ffdd2f0.c56c2562.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
platypush/backend/http/dist/static/js/chunk-a60951ae.8bfe85bc.js
vendored
Normal file
2
platypush/backend/http/dist/static/js/chunk-a60951ae.8bfe85bc.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/dist/static/js/chunk-a60951ae.8bfe85bc.js.map
vendored
Normal file
1
platypush/backend/http/dist/static/js/chunk-a60951ae.8bfe85bc.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
platypush/backend/http/dist/static/js/chunk-fa962a0a.314e65ea.js
vendored
Normal file
2
platypush/backend/http/dist/static/js/chunk-fa962a0a.314e65ea.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/dist/static/js/chunk-fa962a0a.314e65ea.js.map
vendored
Normal file
1
platypush/backend/http/dist/static/js/chunk-fa962a0a.314e65ea.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/public/icons/kodi.svg
Normal file
1
platypush/backend/http/webapp/public/icons/kodi.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"><path d="M25.14 2c-.76 0-1.47.3-2 .84l-5.85 5.93a.969.969 0 0 0-.29.7v20.69c0 .4.25.77.62.92a1 1 0 0 0 .38.08c.26 0 .52-.11.71-.3l17.35-17.58a1 1 0 0 0 0-1.4l-8.93-9.04C26.6 2.3 25.89 2 25.14 2zM11.122 15.813a1 1 0 0 0-.8.257L2.9 22.96C2.32 23.5 2 24.22 2 25c0 .77.32 1.49.9 2.03l7.42 6.9c.19.17.43.27.68.27.14 0 .27-.03.4-.09.36-.16.6-.52.6-.91V16.8c0-.4-.24-.76-.6-.91a.982.982 0 0 0-.279-.078zm29.473.962c-.261 0-.523.096-.713.286l-8.592 8.68a.996.996 0 0 0 0 1.41l7.16 7.23c.19.19.451.299.711.299.27 0 .52-.11.71-.3l7.31-7.37a2.871 2.871 0 0 0 0-4.02l-5.88-5.93a.985.985 0 0 0-.706-.285zm-13.768 13.86a.985.985 0 0 0-.707.285l-8.83 8.91a.984.984 0 0 0 0 1.4l5.88 5.94c.54.53 1.25.83 2 .83.76 0 1.47-.3 2-.83l7.542-7.6a.996.996 0 0 0 0-1.41l-7.172-7.24a1.007 1.007 0 0 0-.713-.285z"/><metadata><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:dc="http://purl.org/dc/elements/1.1/"><rdf:Description about="https://iconscout.com/legal#licenses" dc:title="kodi,filled" dc:description="kodi,filled" dc:publisher="Iconscout" dc:date="2017-12-09" dc:format="image/svg+xml" dc:language="en"><dc:creator><rdf:Bag><rdf:li>Icons8</rdf:li></rdf:Bag></dc:creator></rdf:Description></rdf:RDF></metadata></svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -3,8 +3,20 @@
|
|||
"light.hue": {
|
||||
"class": "fas fa-lightbulb"
|
||||
},
|
||||
"media.omxplayer": {
|
||||
"class": "fa fa-film"
|
||||
},
|
||||
"media.mplayer": {
|
||||
"class": "fa fa-film"
|
||||
},
|
||||
"media.mpv": {
|
||||
"class": "fa fa-film"
|
||||
},
|
||||
"media.vlc": {
|
||||
"class": "fa fa-film"
|
||||
},
|
||||
"music.mpd": {
|
||||
"class": "fas fa-music"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
</div>
|
||||
<div class="col-6">
|
||||
<div class="buttons">
|
||||
<button @click="$emit('previous')" title="Play previous track" v-if="buttons.previous">
|
||||
<button @click="$emit('previous')" title="Play previous track" v-if="buttons_.previous">
|
||||
<i class="icon fa fa-step-backward"></i>
|
||||
</button>
|
||||
<button @click="$emit('stop')" v-if="buttons.stop && status.state !== 'stop'" title="Stop playback">
|
||||
<button @click="$emit('stop')" v-if="buttons_.stop && status.state !== 'stop'" title="Stop playback">
|
||||
<i class="icon fa fa-stop"></i>
|
||||
</button>
|
||||
<button @click="$emit('next')" title="Play next track" v-if="buttons.next">
|
||||
<button @click="$emit('next')" title="Play next track" v-if="buttons_.next">
|
||||
<i class="icon fa fa-step-forward"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -35,17 +35,17 @@
|
|||
|
||||
<div class="col-3 list-controls">
|
||||
<button @click="$emit('consume', !status.consume)" :class="{enabled: status.consume}"
|
||||
title="Toggle consume mode" v-if="buttons.consume">
|
||||
title="Toggle consume mode" v-if="buttons_.consume">
|
||||
<i class="icon fa fa-utensils"></i>
|
||||
</button>
|
||||
|
||||
<button @click="$emit('random', !status.random)" :class="{enabled: status.random}"
|
||||
title="Toggle shuffle" v-if="buttons.random">
|
||||
title="Toggle shuffle" v-if="buttons_.random">
|
||||
<i class="icon fa fa-random"></i>
|
||||
</button>
|
||||
|
||||
<button @click="$emit('repeat', !status.repeat)" :class="{enabled: status.repeat}"
|
||||
title="Toggle repeat" v-if="buttons.repeat">
|
||||
title="Toggle repeat" v-if="buttons_.repeat">
|
||||
<i class="icon fa fa-redo"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -81,6 +81,7 @@
|
|||
<div class="title">
|
||||
<a :href="$route.fullPath" v-text="track.title"
|
||||
@click.prevent="$emit('search', {artist: track.artist, album: track.album})" v-if="track.album"></a>
|
||||
<a :href="track.url" v-text="track.title" v-else-if="track.url"></a>
|
||||
<span v-text="track.title" v-else></span>
|
||||
</div>
|
||||
<div class="artist" v-if="track.artist">
|
||||
|
@ -91,7 +92,7 @@
|
|||
|
||||
<div class="playback-controls desktop col-6">
|
||||
<div class="row buttons">
|
||||
<button @click="$emit('previous')" title="Play previous track" v-if="buttons.previous">
|
||||
<button @click="$emit('previous')" title="Play previous track" v-if="buttons_.previous">
|
||||
<i class="icon fa fa-step-backward"></i>
|
||||
</button>
|
||||
<button @click="$emit(status.state === 'play' ? 'pause' : 'play')"
|
||||
|
@ -99,10 +100,10 @@
|
|||
<i class="icon play-pause fa fa-pause" v-if="status.state === 'play'"></i>
|
||||
<i class="icon play-pause fa fa-play" v-else></i>
|
||||
</button>
|
||||
<button @click="$emit('stop')" v-if="buttons.stop && status.state !== 'stop'" title="Stop playback">
|
||||
<button @click="$emit('stop')" v-if="buttons_.stop && status.state !== 'stop'" title="Stop playback">
|
||||
<i class="icon fa fa-stop"></i>
|
||||
</button>
|
||||
<button @click="$emit('next')" title="Play next track" v-if="buttons.next">
|
||||
<button @click="$emit('next')" title="Play next track" v-if="buttons_.next">
|
||||
<i class="icon fa fa-step-forward"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -131,13 +132,13 @@
|
|||
|
||||
<div class="col-3 pull-right desktop">
|
||||
<div class="row list-controls">
|
||||
<button @click="$emit('consume')" :class="{enabled: status.consume}" title="Toggle consume mode" v-if="buttons.consume">
|
||||
<button @click="$emit('consume')" :class="{enabled: status.consume}" title="Toggle consume mode" v-if="buttons_.consume">
|
||||
<i class="icon fa fa-utensils"></i>
|
||||
</button>
|
||||
<button @click="$emit('random')" :class="{enabled: status.random}" title="Toggle shuffle" v-if="buttons.random">
|
||||
<button @click="$emit('random')" :class="{enabled: status.random}" title="Toggle shuffle" v-if="buttons_.random">
|
||||
<i class="icon fa fa-random"></i>
|
||||
</button>
|
||||
<button @click="$emit('repeat')" :class="{enabled: status.repeat}" title="Toggle repeat" v-if="buttons.repeat">
|
||||
<button @click="$emit('repeat')" :class="{enabled: status.repeat}" title="Toggle repeat" v-if="buttons_.repeat">
|
||||
<i class="icon fa fa-redo"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -202,10 +203,20 @@ export default {
|
|||
},
|
||||
|
||||
data() {
|
||||
const buttons = Object.keys(this.buttons)?.length ? this.buttons : {
|
||||
previous: true,
|
||||
next: true,
|
||||
stop: true,
|
||||
consume: true,
|
||||
random: true,
|
||||
repeat: true,
|
||||
}
|
||||
|
||||
return {
|
||||
expanded: false,
|
||||
lastSync: 0,
|
||||
elapsed: this.status?.elapsed,
|
||||
elapsed: this.status?.elapsed || this.status?.position,
|
||||
buttons_: buttons,
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -235,8 +246,11 @@ export default {
|
|||
})
|
||||
|
||||
setInterval(() => {
|
||||
if (self.status?.state === 'play')
|
||||
self.elapsed = (self.status?.elapsed || 0) + Math.round(this.getTime() - self.lastSync)
|
||||
if (self.status?.state !== 'stop') {
|
||||
self.elapsed = (self.status?.elapsed || self.status?.position || 0)
|
||||
if (self.status?.state === 'play')
|
||||
self.elapsed += Math.round(this.getTime() - self.lastSync)
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
}
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
<slot />
|
||||
</div>
|
||||
<div class="controls-container">
|
||||
<Controls :status="status" :track="track" @play="$emit('play', $event)" @pause="$emit('pause', $event)"
|
||||
@stop="$emit('stop')" @previous="$emit('previous')" @next="$emit('next')" @seek="$emit('seek', $event)"
|
||||
@set-volume="$emit('set-volume', $event)" @consume="$emit('consume', $event)"
|
||||
@repeat="$emit('repeat', $event)" @random="$emit('random', $event)" @search="$emit('search', $event)" />
|
||||
<Controls :status="status" :track="track" :buttons="buttons" @play="$emit('play', $event)"
|
||||
@pause="$emit('pause', $event)" @stop="$emit('stop')" @previous="$emit('previous')"
|
||||
@next="$emit('next')" @seek="$emit('seek', $event)" @set-volume="$emit('set-volume', $event)"
|
||||
@consume="$emit('consume', $event)" @repeat="$emit('repeat', $event)" @random="$emit('random', $event)"
|
||||
@search="$emit('search', $event)"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -33,6 +34,10 @@ export default {
|
|||
track: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
buttons: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<i :class="icons[name].class" v-if="icons[name]?.class" />
|
||||
<i class="fas fa-puzzle-piece" v-else />
|
||||
</span>
|
||||
<span class="name" v-if="!collapsed">{{ displayName(name) }}</span>
|
||||
<span class="name" v-if="!collapsed" v-text="name" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -44,10 +44,6 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
displayName(name) {
|
||||
return name.split('.').map((token) => token[0].toUpperCase() + token.slice(1)).join(' ')
|
||||
},
|
||||
|
||||
onItemClick(name) {
|
||||
this.$emit('select', name)
|
||||
this.collapsed = true
|
||||
|
@ -124,10 +120,6 @@ nav {
|
|||
.icon {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.name {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
|
||||
.toggler {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="dropdown-container" ref="container">
|
||||
<button :title="title" ref="button" @click.stop="toggle">
|
||||
<button :title="title" ref="button" @click.stop="toggle($event)">
|
||||
<i class="icon" :class="iconClass" v-if="iconClass" />
|
||||
<span class="text" v-text="text" v-if="text" />
|
||||
</button>
|
||||
|
@ -83,7 +83,9 @@ export default {
|
|||
}, 10)
|
||||
},
|
||||
|
||||
toggle() {
|
||||
toggle(event) {
|
||||
event.stopPropagation()
|
||||
this.$emit('click')
|
||||
this.visible ? this.close() : this.open()
|
||||
},
|
||||
},
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
<template>
|
||||
<div class="header" :class="{'with-filter': filterVisible}">
|
||||
<div class="row">
|
||||
<div class="col-7 left side">
|
||||
<button title="Filter" @click="filterVisible = !filterVisible">
|
||||
<i class="fa fa-filter" />
|
||||
</button>
|
||||
|
||||
<form @submit.prevent="search">
|
||||
<label class="search-box">
|
||||
<input type="search" placeholder="Search" v-model="query">
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-5 right side">
|
||||
<Players :plugin-name="pluginName" @select="$emit('select-player', $event)"
|
||||
@status="$emit('player-status', $event)" />
|
||||
<Dropdown title="Actions" icon-class="fa fa-ellipsis-h">
|
||||
<DropdownItem text="Play URL" icon-class="fa fa-play-circle" />
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row filter fade-in" :class="{hidden: !filterVisible}">
|
||||
<label v-for="source in Object.keys(sources)" :key="source">
|
||||
<input type="checkbox" v-model="sources[source]" />
|
||||
{{ source }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from "@/components/elements/Dropdown";
|
||||
import DropdownItem from "@/components/elements/DropdownItem";
|
||||
import Players from "@/components/panels/Media/Players";
|
||||
export default {
|
||||
name: "Header",
|
||||
components: {Players, DropdownItem, Dropdown},
|
||||
emits: ['search', 'select-player', 'player-status'],
|
||||
|
||||
props: {
|
||||
pluginName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
filterVisible: false,
|
||||
query: '',
|
||||
sources: {
|
||||
'file': true,
|
||||
'youtube': true,
|
||||
'torrent': true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
search() {
|
||||
const types = Object.keys(this.sources).filter((source) => this.sources[source])
|
||||
if (!this.query?.length || !types?.length)
|
||||
return
|
||||
|
||||
this.$emit('search', {
|
||||
query: this.query,
|
||||
types: types,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$media-header-height: 3.3em;
|
||||
$filter-header-height: 3em;
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
height: $media-header-height;
|
||||
position: relative;
|
||||
background: $menu-panel-bg;
|
||||
padding: .5em;
|
||||
box-shadow: $border-shadow-bottom;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&.with-filter {
|
||||
height: calc(#{$media-header-height} + #{$filter-header-height});
|
||||
}
|
||||
|
||||
.side {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
&.right {
|
||||
justify-content: right;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(button) {
|
||||
background: none;
|
||||
padding: 0 .25em;
|
||||
border: 0;
|
||||
margin-right: .25em;
|
||||
|
||||
&:hover {
|
||||
color: $default-hover-fg-2;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
background: initial;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
width: 100%;
|
||||
margin-left: .5em;
|
||||
|
||||
input[type=search] {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.filter {
|
||||
position: absolute;
|
||||
top: $media-header-height;
|
||||
height: $filter-header-height;
|
||||
padding-bottom: 1em;
|
||||
|
||||
label {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
margin-right: 1em;
|
||||
|
||||
input {
|
||||
margin-right: .5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,151 @@
|
|||
<template>
|
||||
<keep-alive>
|
||||
<div class="media-plugin fade-in">
|
||||
<Loading v-if="loading" />
|
||||
|
||||
<MediaView :plugin-name="pluginName" :status="selectedPlayer?.status || {}" :track="selectedPlayer?.status || {}"
|
||||
:buttons="mediaButtons" @play="pause" @pause="pause" @stop="stop" @set-volume="setVolume"
|
||||
@seek="seek" @search="search">
|
||||
<main>
|
||||
<Header :plugin-name="pluginName" @search="search" @select-player="selectedPlayer = $event"
|
||||
@player-status="onStatusUpdate" />
|
||||
<Results :results="results" :selected-result="selectedResult" @select="selectedResult = $event"
|
||||
@play="play" @info="$refs.mediaInfo.isVisible = true" />
|
||||
</main>
|
||||
</MediaView>
|
||||
|
||||
<div class="media-info-container">
|
||||
<Modal title="Media info" ref="mediaInfo">
|
||||
<Info :item="results[selectedResult]" v-if="selectedResult != null" />
|
||||
</Modal>
|
||||
</div>
|
||||
</div>
|
||||
</keep-alive>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Loading from "@/components/Loading";
|
||||
import Modal from "@/components/Modal";
|
||||
import Utils from "@/Utils";
|
||||
import MediaView from "@/components/Media/View";
|
||||
import Header from "@/components/panels/Media/Header";
|
||||
import Info from "@/components/panels/Media/Info";
|
||||
import Results from "@/components/panels/Media/Results";
|
||||
|
||||
export default {
|
||||
name: "Media",
|
||||
mixins: [Utils],
|
||||
components: {Loading, MediaView, Header, Results, Modal, Info},
|
||||
props: {
|
||||
pluginName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
mediaButtons: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
previous: false,
|
||||
next: false,
|
||||
stop: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
results: [],
|
||||
selectedResult: null,
|
||||
selectedPlayer: null,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async search(event) {
|
||||
this.loading = true
|
||||
|
||||
try {
|
||||
this.results = await this.request(`${this.pluginName}.search`, event)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
async play(item) {
|
||||
await this.selectedPlayer.component.play(item, this.selectedPlayer)
|
||||
await this.refresh()
|
||||
},
|
||||
|
||||
async pause() {
|
||||
await this.selectedPlayer.component.pause(this.selectedPlayer)
|
||||
await this.refresh()
|
||||
},
|
||||
|
||||
async stop() {
|
||||
await this.selectedPlayer.component.stop(this.selectedPlayer)
|
||||
await this.refresh()
|
||||
},
|
||||
|
||||
async setVolume(volume) {
|
||||
await this.selectedPlayer.component.setVolume(volume, this.selectedPlayer)
|
||||
await this.refresh()
|
||||
},
|
||||
|
||||
async seek(position) {
|
||||
await this.selectedPlayer.component.seek(position, this.selectedPlayer)
|
||||
await this.refresh()
|
||||
},
|
||||
|
||||
async refresh() {
|
||||
this.selectedPlayer.status = await this.selectedPlayer.component.status(this.selectedPlayer)
|
||||
},
|
||||
|
||||
onStatusUpdate(status) {
|
||||
if (!this.selectedPlayer)
|
||||
return
|
||||
|
||||
this.selectedPlayer.status = status
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$watch(() => this.selectedPlayer, (player) => {
|
||||
if (player)
|
||||
this.refresh()
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.media-plugin {
|
||||
width: 100%;
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.loading) {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
::v-deep(.media-info-container) {
|
||||
.modal-container {
|
||||
.content {
|
||||
max-width: 75%;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding: 1em .5em;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,159 @@
|
|||
<template>
|
||||
<div class="row" v-if="item?.title">
|
||||
<div class="left side">Title</div>
|
||||
<div class="right side">
|
||||
<a :href="`https://www.imdb.com/title/${item.imdb_id}`" target="_blank" v-if="item.imdb_id"
|
||||
v-text="item.title" />
|
||||
<span v-else v-text="item.title" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.synopsis">
|
||||
<div class="left side">Synopsis</div>
|
||||
<div class="right side" v-text="item.synopsis" />
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.description">
|
||||
<div class="left side">Description</div>
|
||||
<div class="right side" v-text="item.description" />
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.channelId">
|
||||
<div class="left side">Channel</div>
|
||||
<div class="right side">
|
||||
<a :href="`https://www.youtube.com/channel/${item.channelId}`" target="_blank"
|
||||
v-text="item.channelTitle || `https://www.youtube.com/channel/${item.channelId}`" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.year">
|
||||
<div class="left side">Year</div>
|
||||
<div class="right side" v-text="item.year" />
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.publishedAt">
|
||||
<div class="left side">Published at</div>
|
||||
<div class="right side" v-text="formatDate(item.publishedAt, true)" />
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.file">
|
||||
<div class="left side">File</div>
|
||||
<div class="right side" v-text="item.file" />
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.url">
|
||||
<div class="left side">URL</div>
|
||||
<div class="right side url">
|
||||
<a :href="item.url" target="_blank" v-text="item.url" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.trailer">
|
||||
<div class="left side">Trailer</div>
|
||||
<div class="right side url">
|
||||
<a :href="item.trailer" target="_blank" v-text="item.trailer" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.size">
|
||||
<div class="left side">Size</div>
|
||||
<div class="right side" v-text="convertSize(item.size)" />
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.quality">
|
||||
<div class="left side">Quality</div>
|
||||
<div class="right side" v-text="item.quality" />
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.seeds">
|
||||
<div class="left side">Seeds</div>
|
||||
<div class="right side" v-text="item.seeds" />
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.peers">
|
||||
<div class="left side">Peers</div>
|
||||
<div class="right side" v-text="item.peers" />
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="item?.language">
|
||||
<div class="left side">Language</div>
|
||||
<div class="right side" v-text="item.language" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Utils from "@/Utils";
|
||||
|
||||
export default {
|
||||
name: "Info",
|
||||
mixins: [Utils],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.row {
|
||||
display: flex;
|
||||
min-height: 3em;
|
||||
padding: .5em 1em;
|
||||
|
||||
@include until ($tablet) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@include from ($tablet) {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: $default-border-2;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $hover-bg;
|
||||
border-radius: .5em;
|
||||
}
|
||||
|
||||
.side {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
|
||||
&.url {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@include until ($tablet) {
|
||||
display: flex;
|
||||
|
||||
&.left {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&.right {
|
||||
justify-content: left;
|
||||
}
|
||||
}
|
||||
|
||||
@include from ($tablet) {
|
||||
display: inline-flex;
|
||||
|
||||
&.left {
|
||||
width: 22%;
|
||||
margin-right: 3%;
|
||||
}
|
||||
|
||||
&.right {
|
||||
width: 75%;
|
||||
justify-content: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,141 @@
|
|||
<template>
|
||||
<div class="plugins">
|
||||
<Chromecast :player="selectedPlayer?.pluginName === 'media.chromecast' ? selectedPlayer : null"
|
||||
ref="chromecastPlugin" @status="$emit('status', $event)" />
|
||||
<Kodi :player="selectedPlayer?.pluginName === 'media.kodi' ? selectedPlayer : null" ref="kodiPlugin"
|
||||
@status="$emit('status', $event)" />
|
||||
<Mplayer :player="selectedPlayer?.pluginName === 'media.mplayer' ? selectedPlayer : null" ref="mplayerPlugin"
|
||||
@status="$emit('status', $event)" />
|
||||
<Mpv :player="selectedPlayer?.pluginName === 'media.mpv' ? selectedPlayer : null" ref="mpvPlugin"
|
||||
@status="$emit('status', $event)" />
|
||||
<Omxplayer :player="selectedPlayer?.pluginName === 'media.omxplayer' ? selectedPlayer : null" ref="omxplayerPlugin"
|
||||
@status="$emit('status', $event)" />
|
||||
<Vlc :player="selectedPlayer?.pluginName === 'media.vlc' ? selectedPlayer : null" ref="vlcPlugin"
|
||||
@status="$emit('status', $event)" />
|
||||
</div>
|
||||
|
||||
<div class="players">
|
||||
<Dropdown :title="selectedPlayer?.name || 'Players'"
|
||||
:icon-class="selectedPlayer ? selectedPlayer.iconClass : 'fab fa-chromecast'">
|
||||
<Loading v-if="loading" />
|
||||
|
||||
<div class="refresh">
|
||||
<DropdownItem text="Refresh" icon-class="fa fa-sync-alt" @click="refresh" />
|
||||
</div>
|
||||
|
||||
<div class="no-results" v-if="!players?.length">No players found</div>
|
||||
|
||||
<div class="player" v-for="(player, i) in players" :key="i"
|
||||
:class="{selected: selectedPlayer != null && selectedPlayer.pluginName === player.pluginName
|
||||
&& selectedPlayer.name === player.name}">
|
||||
<DropdownItem :text="player.name" :icon-class="player.iconClass" @click="select(player)" />
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from "@/components/elements/Dropdown";
|
||||
import DropdownItem from "@/components/elements/DropdownItem";
|
||||
import Loading from "@/components/Loading";
|
||||
|
||||
import Chromecast from "@/components/panels/Media/Players/Chromecast"
|
||||
import Kodi from "@/components/panels/Media/Players/Kodi";
|
||||
import Mplayer from "@/components/panels/Media/Players/Mplayer";
|
||||
import Mpv from "@/components/panels/Media/Players/Mpv";
|
||||
import Omxplayer from "@/components/panels/Media/Players/Omxplayer";
|
||||
import Vlc from "@/components/panels/Media/Players/Vlc";
|
||||
|
||||
export default {
|
||||
name: "Players",
|
||||
components: {Loading, DropdownItem, Dropdown, Chromecast, Kodi, Mplayer, Mpv, Omxplayer, Vlc},
|
||||
emits: ['select', 'status'],
|
||||
|
||||
props: {
|
||||
pluginName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
players: [],
|
||||
selectedPlayer: null,
|
||||
config: {},
|
||||
plugins: [],
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
loadPlugins() {
|
||||
this.plugins = Object.entries(this.$refs).filter((p) => p[0].endsWith('Plugin')).map((p) => p[1])
|
||||
},
|
||||
|
||||
async refresh() {
|
||||
this.players = []
|
||||
this.loading = true
|
||||
const config = this.$root.config
|
||||
|
||||
try {
|
||||
await Promise.all(this.plugins.map(async (plugin) => {
|
||||
if (!(plugin.pluginName in config))
|
||||
return
|
||||
|
||||
const players = await plugin.getPlayers()
|
||||
this.players.push(...players)
|
||||
|
||||
if (this.selectedPlayer == null && plugin.pluginName === this.pluginName && players.length > 0) {
|
||||
this.select(players[0])
|
||||
}
|
||||
}))
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
select(player) {
|
||||
this.selectedPlayer = player
|
||||
this.$emit('select', player)
|
||||
},
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
await this.loadPlugins()
|
||||
await this.refresh()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.plugins {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.players {
|
||||
::v-deep(.dropdown) {
|
||||
.item {
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 1em !important;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.refresh) {
|
||||
font-weight: bold;
|
||||
font-size: .8em;
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
::v-deep(.player.selected) {
|
||||
color: $selected-fg;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Mixin from "@/components/panels/Media/Players/Mixin";
|
||||
|
||||
export default {
|
||||
name: "Chromecast",
|
||||
mixins: [Mixin],
|
||||
data() {
|
||||
return {
|
||||
name: 'Chromecast',
|
||||
pluginName: 'media.chromecast',
|
||||
iconClass: 'fab fa-chromecast',
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async getPlayers() {
|
||||
const devices = await this.request(`${this.pluginName}.get_chromecasts`)
|
||||
return Promise.all(devices.map(async (device) => {
|
||||
return {
|
||||
...device,
|
||||
iconClass: device.type === 'audio' ? 'fa fa-volume-up' : 'fab fa-chromecast',
|
||||
pluginName: this.pluginName,
|
||||
status: this.request(`${this.pluginName}.status`, {chromecast: device.name}),
|
||||
component: this,
|
||||
}
|
||||
}))
|
||||
},
|
||||
|
||||
getPlayerName(player) {
|
||||
if (typeof player === 'string')
|
||||
return player
|
||||
|
||||
if (!player)
|
||||
return this.player?.name
|
||||
|
||||
return player?.name
|
||||
},
|
||||
|
||||
async status(player) {
|
||||
return await this.request(`${this.pluginName}.status`, {chromecast: this.getPlayerName(player)})
|
||||
},
|
||||
|
||||
async play(resource, player) {
|
||||
if (!resource) {
|
||||
return await this.pause(player)
|
||||
}
|
||||
|
||||
return await this.request(`${this.pluginName}.play`, {resource: resource.url, chromecast: this.getPlayerName(player)})
|
||||
},
|
||||
|
||||
async pause(player) {
|
||||
return await this.request(`${this.pluginName}.pause`, {chromecast: this.getPlayerName(player)})
|
||||
},
|
||||
|
||||
async stop(player) {
|
||||
return await this.request(`${this.pluginName}.stop`, {chromecast: this.getPlayerName(player)})
|
||||
},
|
||||
|
||||
async setVolume(volume, player) {
|
||||
return await this.request(`${this.pluginName}.set_volume`, {volume: volume, chromecast: this.getPlayerName(player)})
|
||||
},
|
||||
|
||||
async seek(position, player) {
|
||||
return await this.request(`${this.pluginName}.seek`, {position: position, chromecast: this.getPlayerName(player)})
|
||||
},
|
||||
|
||||
async onMediaEvent(event) {
|
||||
if (event.plugin !== this.pluginName)
|
||||
return false
|
||||
|
||||
this.$emit('status', await this.status(event.player))
|
||||
return true
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Mixin from "@/components/panels/Media/Players/Mixin";
|
||||
|
||||
export default {
|
||||
name: "Kodi",
|
||||
mixins: [Mixin],
|
||||
data() {
|
||||
return {
|
||||
iconClass: 'fa fa-kodi',
|
||||
name: 'Kodi',
|
||||
pluginName: 'media.kodi',
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async getPlayers() {
|
||||
return [{
|
||||
iconClass: this.iconClass,
|
||||
pluginName: this.pluginName,
|
||||
name: this.$root.config['media.kodi']?.host || this.name,
|
||||
component: this,
|
||||
status: await this.request(`${this.pluginName}.status`)
|
||||
}]
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,102 @@
|
|||
<script>
|
||||
import Utils from "@/Utils";
|
||||
|
||||
export default {
|
||||
name: "Mixin",
|
||||
mixins: [Utils],
|
||||
emits: ['status'],
|
||||
|
||||
props: {
|
||||
player: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
iconClass: null,
|
||||
name: null,
|
||||
pluginName: null,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async getPlayers() {
|
||||
return [{
|
||||
iconClass: this.iconClass,
|
||||
name: this.name,
|
||||
pluginName: this.pluginName,
|
||||
component: this,
|
||||
status: await this.status(),
|
||||
}]
|
||||
},
|
||||
|
||||
async status() {
|
||||
return await this.request(`${this.pluginName}.status`)
|
||||
},
|
||||
|
||||
async play(resource) {
|
||||
if (!resource) {
|
||||
return await this.pause()
|
||||
}
|
||||
|
||||
return await this.request(`${this.pluginName}.play`, {resource: resource.url})
|
||||
},
|
||||
|
||||
async pause() {
|
||||
return await this.request(`${this.pluginName}.pause`)
|
||||
},
|
||||
|
||||
async stop() {
|
||||
return await this.request(`${this.pluginName}.stop`)
|
||||
},
|
||||
|
||||
async setVolume(volume) {
|
||||
return await this.request(`${this.pluginName}.set_volume`, {volume: volume})
|
||||
},
|
||||
|
||||
async seek(position) {
|
||||
return await this.request(`${this.pluginName}.seek`, {position: position})
|
||||
},
|
||||
|
||||
async onNewMedia(event) {
|
||||
const isMine = await this.onMediaEvent(event)
|
||||
|
||||
if (isMine && event.title) {
|
||||
this.notify({
|
||||
title: event.player || event.device || this.player?.name || this.name || this.pluginName,
|
||||
text: event.title,
|
||||
image: {
|
||||
iconClass: this.iconClass || 'fa fa-play',
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async onMediaEvent(event) {
|
||||
if (event.plugin !== this.pluginName)
|
||||
return false
|
||||
|
||||
this.$emit('status', await this.status())
|
||||
return true
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.subscribe(this.onNewMedia, `on-new-media-${this.pluginName}`,
|
||||
'platypush.message.event.media.NewPlayingMediaEvent')
|
||||
|
||||
this.subscribe(this.onMediaEvent, `on-media-event-${this.pluginName}`,
|
||||
'platypush.message.event.media.MediaPlayEvent',
|
||||
'platypush.message.event.media.MediaStopEvent',
|
||||
'platypush.message.event.media.MediaPauseEvent',
|
||||
'platypush.message.event.media.MediaSeekEvent',
|
||||
'platypush.message.event.media.MediaVolumeChangedEvent',
|
||||
'platypush.message.event.media.MediaMuteChangedEvent')
|
||||
},
|
||||
|
||||
destroy() {
|
||||
this.unsubscribe(`on-media-event-${this.pluginName}`)
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Mixin from "@/components/panels/Media/Players/Mixin";
|
||||
|
||||
export default {
|
||||
name: "Mplayer",
|
||||
mixins: [Mixin],
|
||||
data() {
|
||||
return {
|
||||
iconClass: 'fa fa-tv',
|
||||
name: 'MPlayer',
|
||||
pluginName: 'media.mplayer',
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Mixin from "@/components/panels/Media/Players/Mixin";
|
||||
|
||||
export default {
|
||||
name: "Mpv",
|
||||
mixins: [Mixin],
|
||||
data() {
|
||||
return {
|
||||
iconClass: 'fa fa-tv',
|
||||
name: 'mpv',
|
||||
pluginName: 'media.mpv',
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Mixin from "@/components/panels/Media/Players/Mixin";
|
||||
|
||||
export default {
|
||||
name: "Omxplayer",
|
||||
mixins: [Mixin],
|
||||
data() {
|
||||
return {
|
||||
iconClass: 'fa fa-tv',
|
||||
name: 'OMXPlayer',
|
||||
pluginName: 'media.omxplayer',
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Mixin from "@/components/panels/Media/Players/Mixin";
|
||||
|
||||
export default {
|
||||
name: "Vlc",
|
||||
mixins: [Mixin],
|
||||
data() {
|
||||
return {
|
||||
iconClass: 'fa fa-tv',
|
||||
name: 'VLC',
|
||||
pluginName: 'media.vlc',
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<div class="media-results">
|
||||
<div class="row item" :class="{selected: selectedResult === i}" v-for="(result, i) in results" :key="i"
|
||||
@click="$emit('select', i)">
|
||||
<div class="col-10 left side">
|
||||
<div class="icon">
|
||||
<i :class="typeIcons[result.type]" />
|
||||
</div>
|
||||
<div class="title" v-text="result.title" />
|
||||
</div>
|
||||
|
||||
<div class="col-2 right side">
|
||||
<Dropdown title="Actions" icon-class="fa fa-ellipsis-h" @click="$emit('select', i)">
|
||||
<DropdownItem icon-class="fa fa-play" text="Play" @click="$emit('play', result)" />
|
||||
<DropdownItem icon-class="fas fa-closed-captioning" text="Play with captions" />
|
||||
<DropdownItem icon-class="fa fa-info" text="Info" @click="$emit('info', result)" />
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from "@/components/elements/Dropdown";
|
||||
import DropdownItem from "@/components/elements/DropdownItem";
|
||||
|
||||
export default {
|
||||
name: "Results",
|
||||
components: {Dropdown, DropdownItem},
|
||||
emits: ['select', 'info', 'play'],
|
||||
props: {
|
||||
results: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
selectedResult: {
|
||||
type: Number,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
typeIcons: {
|
||||
'file': 'fa fa-hdd',
|
||||
'torrent': 'fa fa-magnet',
|
||||
'youtube': 'fab fa-youtube',
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "items";
|
||||
|
||||
.media-results {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: $background-color;
|
||||
overflow: auto;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.selected {
|
||||
background: $selected-bg;
|
||||
}
|
||||
|
||||
.side {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
&.right {
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
::v-deep(.dropdown-container) {
|
||||
.item {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
opacity: .7;
|
||||
|
||||
&:hover {
|
||||
color: $default-hover-fg-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,60 @@
|
|||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1em .25em .5em .25em;
|
||||
box-shadow: 0 2.5px 2px -1px $default-shadow-color;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: $hover-bg;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: $active-bg;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: $selected-bg;
|
||||
}
|
||||
|
||||
&.dragover {
|
||||
border-top: 2px solid $default-hover-fg;
|
||||
}
|
||||
|
||||
&::selection {
|
||||
background: rgba(0, 0, 0, 0) !important;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1em;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.right-side {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
.duration,
|
||||
.actions {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.duration {
|
||||
font-size: .85em;
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.actions {
|
||||
::v-deep(button) {
|
||||
opacity: .65;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: $default-fg-3;
|
||||
margin-right: .75em;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<Media plugin-name="media.mplayer" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Media from '@/components/panels/Media/Index'
|
||||
|
||||
export default {
|
||||
name: "MediaMplayer",
|
||||
components: {Media},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<Media plugin-name="media.mpv" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Media from '@/components/panels/Media/Index'
|
||||
|
||||
export default {
|
||||
name: "MediaMpv",
|
||||
components: {Media},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<Media plugin-name="media.omxplayer" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Media from '@/components/panels/Media/Index'
|
||||
|
||||
export default {
|
||||
name: "MediaMpv",
|
||||
components: {Media},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<Media plugin-name="media.vlc" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Media from '@/components/panels/Media/Index'
|
||||
|
||||
export default {
|
||||
name: "MediaVlc",
|
||||
components: {Media},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -5,3 +5,4 @@
|
|||
@import "components.scss";
|
||||
@import "inputs.scss";
|
||||
@import "animations.scss";
|
||||
@import "icons.scss";
|
||||
|
|
8
platypush/backend/http/webapp/src/style/icons.scss
Normal file
8
platypush/backend/http/webapp/src/style/icons.scss
Normal file
|
@ -0,0 +1,8 @@
|
|||
.fa.fa-kodi:before {
|
||||
content: ' ';
|
||||
background: url('/icons/kodi.svg');
|
||||
background-size: 1em 1em;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
display: inline-block;
|
||||
}
|
|
@ -6,7 +6,10 @@ $default-bg-4: #f1f3f2 !default;
|
|||
$default-bg-5: #edf0ee !default;
|
||||
$default-bg-6: #e4eae8 !default;
|
||||
$default-bg-7: #e4e4e4 !default;
|
||||
|
||||
$default-fg: black !default;
|
||||
$default-fg-2: #23513a !default;
|
||||
$default-fg-3: #195331b3 !default;
|
||||
|
||||
//// Notifications
|
||||
$notification-bg: rgba(185, 255, 193, 0.9) !default;
|
||||
|
|
|
@ -2,11 +2,17 @@
|
|||
export default {
|
||||
name: "DateTime",
|
||||
methods: {
|
||||
formatDate(date) {
|
||||
return date.toDateString().substring(0, 10)
|
||||
formatDate(date, year=false) {
|
||||
if (typeof date === 'string')
|
||||
date = new Date(Date.parse(date))
|
||||
|
||||
return date.toDateString().substring(0, year ? 14 : 10)
|
||||
},
|
||||
|
||||
formatTime(date, seconds=true) {
|
||||
if (typeof date === 'string')
|
||||
date = new Date(Date.parse(date))
|
||||
|
||||
return date.toTimeString().substring(0, seconds ? 8 : 5)
|
||||
},
|
||||
},
|
||||
|
|
|
@ -15,6 +15,28 @@ export default {
|
|||
|
||||
return !!value
|
||||
},
|
||||
|
||||
convertSize(value) {
|
||||
if (typeof value === 'string')
|
||||
value = parseInt(value)
|
||||
|
||||
let unit = null
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
|
||||
units.forEach((u, i) => {
|
||||
if (value <= 1024 && unit == null) {
|
||||
unit = u
|
||||
} else if (value > 1024) {
|
||||
if (i === units.length-1) {
|
||||
unit = u
|
||||
} else {
|
||||
value = value/1024
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return `${value.toFixed(2)} ${unit}`
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -156,7 +156,6 @@ class MediaChromecastPlugin(MediaPlugin):
|
|||
:type callback: func
|
||||
"""
|
||||
|
||||
import pychromecast
|
||||
self.chromecasts.update({
|
||||
cast.device.friendly_name: cast
|
||||
for cast in self._get_chromecasts(tries=tries, retry_wait=retry_wait,
|
||||
|
|
Loading…
Reference in a new issue