pifi 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5f23359fd7fec0b731f4bcfa9ae43aa2b9beadf4285a0f9712669d467c228bb
4
- data.tar.gz: 0f538b70d4ca73e0e4fc0cb311606771619f479e5b9808ff0856f91ad11a88c8
3
+ metadata.gz: '0970b7eb865277cc7212873ae932c5072d1940d7185631b89f503c0950bb762b'
4
+ data.tar.gz: 692210950b53ce6990daeb93d130aaddd13f88fa797e833a179186020e3fbe91
5
5
  SHA512:
6
- metadata.gz: 8eef993856b6ed226b168f64b5ec7a630ff64891d8870be6904637280fc1821bda4cd2169fe0822c35f52cf3655881d3e760039a3940b0482a7713499f22333b
7
- data.tar.gz: 9ded257c363d169983a99eaa66d500aed7e8daead81af1d96a7f6c406fe024e0c89cd4475f062221b78c3beb607f9404d64186addba7e6fbd7cd83cb16df3dd5
6
+ metadata.gz: cff5d101d9b520c7c65d18bcecc4f3908f1c71872dd0447f6e3e140ce9fdd70478e49c27bdcdda6ffcc8597241fcfcd8948fb8211359d2fcdc5206c1121748e1
7
+ data.tar.gz: 6b8297a9d67ac346aa3c5b4c0047f7f83434de329b981be85d2513f0715873c976d7b177e8e1e7bfff16b456f234d961b8cf8313206a5d8cb345872a845d75d3
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "files": {
3
3
  "main.css": "/static/css/main.a50e54d2.chunk.css",
4
- "main.js": "/static/js/main.54482466.chunk.js",
5
- "main.js.map": "/static/js/main.54482466.chunk.js.map",
4
+ "main.js": "/static/js/main.52997fad.chunk.js",
5
+ "main.js.map": "/static/js/main.52997fad.chunk.js.map",
6
6
  "runtime-main.js": "/static/js/runtime-main.f04a0f25.js",
7
7
  "runtime-main.js.map": "/static/js/runtime-main.f04a0f25.js.map",
8
8
  "static/css/2.5dbdccff.chunk.css": "/static/css/2.5dbdccff.chunk.css",
9
9
  "static/js/2.2b057ab5.chunk.js": "/static/js/2.2b057ab5.chunk.js",
10
10
  "static/js/2.2b057ab5.chunk.js.map": "/static/js/2.2b057ab5.chunk.js.map",
11
11
  "index.html": "/index.html",
12
- "precache-manifest.c6c8e4145c1ed2fcfcefd9d025331f85.js": "/precache-manifest.c6c8e4145c1ed2fcfcefd9d025331f85.js",
12
+ "precache-manifest.2b9510afeb80c583686b4fe78607222c.js": "/precache-manifest.2b9510afeb80c583686b4fe78607222c.js",
13
13
  "service-worker.js": "/service-worker.js",
14
14
  "static/css/2.5dbdccff.chunk.css.map": "/static/css/2.5dbdccff.chunk.css.map",
15
15
  "static/css/main.a50e54d2.chunk.css.map": "/static/css/main.a50e54d2.chunk.css.map",
@@ -21,6 +21,6 @@
21
21
  "static/css/2.5dbdccff.chunk.css",
22
22
  "static/js/2.2b057ab5.chunk.js",
23
23
  "static/css/main.a50e54d2.chunk.css",
24
- "static/js/main.54482466.chunk.js"
24
+ "static/js/main.52997fad.chunk.js"
25
25
  ]
26
26
  }
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="description" content="MPD web client to listen to radio"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5"><meta name="apple-mobile-web-app-title" content="PiFi Radio"><meta name="application-name" content="PiFi Radio"><meta name="msapplication-TileColor" content="#2b5797"><meta name="theme-color" content="#375a7f"><title>PiFi Radio</title><link href="" rel="stylesheet" title="theme"><link href="/static/css/2.5dbdccff.chunk.css" rel="stylesheet"><link href="/static/css/main.a50e54d2.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],Object.prototype.hasOwnProperty.call(a,t)&&a[t]&&i.push(a[t][0]),a[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),p()}function p(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==a[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},a={1:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=this.webpackJsonppifi_frontend=this.webpackJsonppifi_frontend||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;p()}([])</script><script src="/static/js/2.2b057ab5.chunk.js"></script><script src="/static/js/main.54482466.chunk.js"></script></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="description" content="MPD web client to listen to radio"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5"><meta name="apple-mobile-web-app-title" content="PiFi Radio"><meta name="application-name" content="PiFi Radio"><meta name="msapplication-TileColor" content="#2b5797"><meta name="theme-color" content="#375a7f"><title>PiFi Radio</title><link href="" rel="stylesheet" title="theme"><link href="/static/css/2.5dbdccff.chunk.css" rel="stylesheet"><link href="/static/css/main.a50e54d2.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],Object.prototype.hasOwnProperty.call(a,t)&&a[t]&&i.push(a[t][0]),a[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),p()}function p(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==a[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},a={1:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=this.webpackJsonppifi_frontend=this.webpackJsonppifi_frontend||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;p()}([])</script><script src="/static/js/2.2b057ab5.chunk.js"></script><script src="/static/js/main.52997fad.chunk.js"></script></body></html>
@@ -1,6 +1,6 @@
1
1
  self.__precacheManifest = (self.__precacheManifest || []).concat([
2
2
  {
3
- "revision": "ddb46863b2b7f72ccfb1c141a6f582f6",
3
+ "revision": "f6800372b6796e310a9d36c57acb5ba8",
4
4
  "url": "/index.html"
5
5
  },
6
6
  {
@@ -8,7 +8,7 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
8
8
  "url": "/static/css/2.5dbdccff.chunk.css"
9
9
  },
10
10
  {
11
- "revision": "5923c39aa02f7c4872e0",
11
+ "revision": "627d6a31f3e99d73bbff",
12
12
  "url": "/static/css/main.a50e54d2.chunk.css"
13
13
  },
14
14
  {
@@ -20,8 +20,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
20
20
  "url": "/static/js/2.2b057ab5.chunk.js.LICENSE"
21
21
  },
22
22
  {
23
- "revision": "5923c39aa02f7c4872e0",
24
- "url": "/static/js/main.54482466.chunk.js"
23
+ "revision": "627d6a31f3e99d73bbff",
24
+ "url": "/static/js/main.52997fad.chunk.js"
25
25
  },
26
26
  {
27
27
  "revision": "92793784c9eb98d269de",
@@ -14,7 +14,7 @@
14
14
  importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15
15
 
16
16
  importScripts(
17
- "/precache-manifest.c6c8e4145c1ed2fcfcefd9d025331f85.js"
17
+ "/precache-manifest.2b9510afeb80c583686b4fe78607222c.js"
18
18
  );
19
19
 
20
20
  self.addEventListener('message', (event) => {
@@ -0,0 +1,2 @@
1
+ (this.webpackJsonppifi_frontend=this.webpackJsonppifi_frontend||[]).push([[0],{18:function(e){e.exports=JSON.parse('{"b":["en","fr-FR","nl-NL","pt-BR"],"c":1000,"a":2500,"d":3000}')},42:function(e,t,a){e.exports=a.p+"static/media/logo.91554ce9.svg"},46:function(e,t,a){e.exports=a(95)},60:function(e,t,a){},81:function(e,t,a){},82:function(e,t,a){},83:function(e,t,a){},84:function(e,t,a){},85:function(e,t,a){},86:function(e,t,a){},87:function(e,t,a){},88:function(e,t,a){},90:function(e,t,a){},95:function(e,t,a){"use strict";a.r(t);var n=a(0),r=a.n(n),l=a(20),c=a.n(l),o=a(6),s=a.n(o),i=a(3),u=a(4),m=a(8),d=a(10),p=a(11),b=a(2),v=(a(60),a(42)),f=a.n(v),h=Object(b.c)()((function(e){var t=e.t;return r.a.createElement("nav",{className:"navbar navbar-expand-sm navbar-dark bg-primary "},r.a.createElement("a",{className:"navbar-brand",href:"/"},r.a.createElement("img",{src:f.a,width:"30",height:"30",className:"d-inline-block align-text-bottom mr-2",alt:""}),"PiFi Radio"),r.a.createElement("button",{className:"navbar-toggler",type:"button","data-toggle":"collapse","data-target":"#navbarNavAltMarkup","aria-controls":"navbarNavAltMarkup","aria-expanded":"false","aria-label":"Toggle navigation"},r.a.createElement("span",{className:"navbar-toggler-icon"})),r.a.createElement("div",{className:"collapse navbar-collapse",id:"navbarNavAltMarkup"},r.a.createElement("div",{className:"navbar-nav pt-1 ml-2"},r.a.createElement("button",{className:"btn btn-link nav-link","data-toggle":"modal","data-target":"#url-dialog"},t("playURL")),r.a.createElement("button",{className:"btn btn-link nav-link","data-toggle":"modal","data-target":"#settings"},t("settings")))))})),g=Object(b.c)()((function(e){var t=e.playerStatus,a=e.t,n=t.playing,l=t.title;return r.a.createElement("div",{className:"text-center"},r.a.createElement("h5",{className:"small text-uppercase"},a(n?"playing":"stopped")),r.a.createElement("h3",{className:"ellipsis"},l))})),y=a(15),E=a(14),k=a(27),N=a.n(k),w=a(5);N.a.interceptors.response.use(null,(function(e){var t=e.response&&e.response.status>=400&&e.response.status<500,a=!e.response;return t||a||w.b.error(r.a.createElement(b.a,null,(function(e,t){t.i18n;return r.a.createElement("p",null,e("errorUnexpected"))}))),e.response&&403===e.response.status&&w.b.error(r.a.createElement(b.a,null,(function(e,t){t.i18n;return r.a.createElement("p",null,e("errorForbidden"))}))),Promise.reject(e)}));var S={get:N.a.get,post:N.a.post},O="".concat("/api","/player");function j(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,a=new FormData;return a.set("method",e),t&&a.set("params",t),a}function C(){return S.post(O,j("play"))}function x(){return S.post(O,j("stop"))}function _(e){return S.post(O,j("change_vol",e))}function L(e){return S.post(O,j("play_radios",e))}function P(e){return S.post(O,j("play_urls",e))}var R=function(e){return e.playing?r.a.createElement("button",{className:"btn btn-danger",onClick:x},r.a.createElement(y.a,{icon:E.d})):r.a.createElement("button",{className:"btn btn-dark",onClick:C},r.a.createElement(y.a,{icon:E.c}))},T=a(18),A=(a(81),Object(b.c)()((function(e){var t=e.playerStatus,a=e.t,n=t.vol<0,l=function(e,t){return r.a.createElement("button",{className:"btn btn-dark p-3",disabled:n,onClick:function(){return function(e){var t,n,r,l;return s.a.async((function(c){for(;;)switch(c.prev=c.next){case 0:return c.next=2,s.a.awrap(_(e));case 2:t=c.sent,n=t.data,"vol",r="".concat(a("volume"),": ").concat(n,"%"),l={toastId:"vol",autoClose:T.d},w.b.isActive("vol")?w.b.update("vol",{render:r}):w.b.info(r,l);case 8:case"end":return c.stop()}}))}(e)}},r.a.createElement(y.a,{icon:t}))};return r.a.createElement("div",{className:"player-controls btn-group w-100"},l("-5",E.e),l("+5",E.f),r.a.createElement(R,{playing:t.playing}))}))),I=(a(82),function(){var e=document.querySelector("nav").clientHeight;document.querySelector(".player").style.top=e+24+"px"}),B=function(e){var t=e.playerStatus;return Object(n.useEffect)(I,[t]),r.a.createElement("div",{className:"player p-2"},r.a.createElement(g,{playerStatus:t}),r.a.createElement(A,{playerStatus:t}))},F=Object(b.c)()((function(e){var t=e.value,a=e.onChange,n=e.t;return r.a.createElement("input",{className:"form-control mb-4",type:"text",id:"query",placeholder:n("search"),autoComplete:"off",value:t,onChange:function(e){return a(e.target.value)},onFocus:function(){return a("")}})})),U=(a(83),function(){return r.a.createElement("div",{className:"loader"},r.a.createElement("div",{className:"spinner-border",role:"status"},r.a.createElement("span",{className:"sr-only"},"Loading...")))}),q="".concat("/api","/streams");a(84);var M=function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={streams:{},loading:!0,query:""},a.isPlaying=function(e){return e===a.props.playerStatus.title&&a.props.playerStatus.playing},a.handleItemClick=function(e){var t,n,r;return s.a.async((function(l){for(;;)switch(l.prev=l.next){case 0:if(t=a.props,n=t.t,r=t.onBackdrop,!a.isPlaying(e)){l.next=3;break}return l.abrupt("return");case 3:return r(n("tunning"),e),l.prev=4,l.next=7,s.a.awrap(L(e));case 7:l.next=12;break;case 9:l.prev=9,l.t0=l.catch(4),l.t0.response&&400===l.t0.response.status&&w.b.error(n("errorNotFound"));case 12:case"end":return l.stop()}}),null,null,[[4,9]])},a.getItemClasses=function(e){var t="streams__item ellipsis list-group-item ";return a.isPlaying(e)?t+"active":t+"list-group-item-action"},a.handleSearch=function(e){a.setState({query:e})},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"componentDidMount",value:function(){var e,t;return s.a.async((function(a){for(;;)switch(a.prev=a.next){case 0:return a.next=2,s.a.awrap(S.get(q));case 2:e=a.sent,t=e.data,this.setState({streams:t,loading:!1});case 5:case"end":return a.stop()}}),null,this)}},{key:"filteredStreams",value:function(){var e=this.state,t=e.streams,a=e.query;if(""===a)return t;var n={};for(var r in t)r.toLowerCase().includes(a.toLowerCase())&&!t[r]&&(n[r]=t[r]);return n}},{key:"renderList",value:function(){var e=this,t=this.filteredStreams();return 0===Object.keys(t).length?r.a.createElement("h4",{className:"p-4"},this.props.t("noStreams")):r.a.createElement("ul",{className:"list-group list-group-flush"},Object.keys(t).map((function(a){return t[a]?r.a.createElement("li",{className:"streams__header ellipsis",key:a},a):r.a.createElement("li",{className:e.getItemClasses(a),key:a,onClick:function(){return e.handleItemClick(a)}},a)})))}},{key:"render",value:function(){return this.state.loading?r.a.createElement(U,null):r.a.createElement("div",{className:"streams p-2"},r.a.createElement(F,{value:this.state.query,onChange:this.handleSearch}),this.renderList())}}]),t}(n.Component),D=Object(b.c)()(M),Y=(a(85),function(e){var t=e.playerStatus,a=t.title,n=t.playing;return r.a.createElement("div",{className:"mini-player p-3"},r.a.createElement("div",{className:"mini-player__left ellipsis pr-3"},r.a.createElement(y.a,{icon:E.b,className:"drawer__toggler mr-3"}),r.a.createElement("span",null,a)),r.a.createElement(R,{playing:n}))}),W=(a(86),function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={open:!1},a.toggle=function(){a.setState({open:!a.state.open})},a.handleClick=function(e){var t=e.target;("BUTTON"===t.tagName||"I"===t.tagName)&&!t.className.includes("drawer__toggler")||a.toggle()},a.handleTouchStart=function(e){a.setState({touchStartY:e.touches[0].clientY})},a.handleTouchMove=function(e){var t=a.state,n=t.open,r=t.touchStartY,l=e.changedTouches[0].clientY,c=l<r;l>r&&n?a.toggle():c&&!n&&a.toggle()},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"render",value:function(){var e=this.props.playerStatus,t="drawer fixed-bottom bg-secondary border-top border-secondary";return this.state.open?(t+=" drawer--open",document.body.classList.add("body--drawer")):document.body.classList.remove("body--drawer"),r.a.createElement("div",{className:t,onClick:this.handleClick,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove},this.state.open?r.a.createElement(r.a.Fragment,null,r.a.createElement(y.a,{icon:E.a,className:"drawer__toggler fa-lg mb-3"}),r.a.createElement(B,{playerStatus:e})," "):r.a.createElement(Y,{playerStatus:e}))}}]),t}(n.Component)),J=function(e){var t=e.id,a=e.title,n=e.footer,l=e.children;return r.a.createElement("div",{className:"modal fade",id:t,tabIndex:"-1",role:"dialog","aria-labelledby":"staticBackdropLabel","aria-hidden":"true"},r.a.createElement("div",{className:"modal-dialog",role:"document"},r.a.createElement("div",{className:"modal-content"},r.a.createElement("div",{className:"modal-header"},r.a.createElement("h5",{className:"modal-title",id:"staticBackdropLabel"},a),r.a.createElement("button",{type:"button",className:"close","data-dismiss":"modal","aria-label":"Close"},r.a.createElement("span",{"aria-hidden":"true"},"\xd7"))),r.a.createElement("div",{className:"modal-body"},l),r.a.createElement("div",{className:"modal-footer"},n))))},K="darkly",V="theme",G=function(e){return"https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/".concat(e,"/bootstrap.min.css")},H=[{id:"darkly",name:"Darkly",themeColor:"#375a7f"},{id:"lux",name:"Lux",themeColor:"#1a1a1a"},{id:"litera",name:"Litera",themeColor:"#4582ec"}];function $(){var e=z(),t=G(e),a=H.find((function(t){return t.id===e})).themeColor;document.querySelector('link[title="theme"]').setAttribute("href",t),document.querySelector('meta[name="theme-color"]').setAttribute("content",a)}function z(){var e=localStorage.getItem(V);return""===e?K:0===H.filter((function(t){return t.id===e})).length?(localStorage.removeItem(V),K):e}var Q=a(22),X=a(43),Z=function(e){var t=e.id,a=e.label,n=e.row,l=e.data,c=Object(X.a)(e,["id","label","row","data"]);return r.a.createElement("div",{className:n?"form-group row":"form-group"},r.a.createElement("label",{htmlFor:t,className:"col-sm-2 col-form-label"},a),r.a.createElement("div",{className:"col-sm-10"},r.a.createElement("select",Object.assign({id:t,className:"form-control"},c),l.map((function(e){return r.a.createElement("option",{key:e.id||e,value:e.id||e},e.name||e)})))))},ee=Object(b.c)()((function(e){var t=e.t;return r.a.createElement(J,{id:"settings",title:t("settings"),footer:r.a.createElement("button",{className:"btn btn-secondary","data-dismiss":"modal"},t("close"))},r.a.createElement(Z,{id:"theme-select",label:t("theme"),data:H,row:!0,value:z(),onChange:function(e){var t;t=e.target.value,localStorage.setItem(V,t),$()}}),r.a.createElement(Z,{id:"language-select",label:t("language"),data:T.b,row:!0,value:Q.a.language,onChange:function(e){Q.a.changeLanguage(e.target.value)}}))})),te=a(19),ae=Object(b.c)()((function(e){var t=e.t,a=Object(n.useState)(""),l=Object(te.a)(a,2),c=l[0],o=l[1],i=function(){""!==c&&(u(c),o(""))},u=function(e){var a,n;return s.a.async((function(r){for(;;)switch(r.prev=r.next){case 0:return a={type:w.b.TYPE.ERROR,render:t("errorNotFound")},n=Object(w.b)(t("tryingURL")),r.prev=2,r.next=5,s.a.awrap(P(e));case 5:r.next=10;break;case 7:r.prev=7,r.t0=r.catch(2),r.t0.response&&400===r.t0.response.status&&w.b.update(n,a);case 10:case"end":return r.stop()}}),null,null,[[2,7]])};return r.a.createElement(J,{id:"url-dialog",title:t("playURL"),footer:r.a.createElement(r.a.Fragment,null,r.a.createElement("button",{className:"btn btn-primary","data-dismiss":"modal",onClick:i},"OK"),r.a.createElement("button",{className:"btn btn-secondary",onClick:function(){return o("")},"data-dismiss":"modal"},"Cancel"))},r.a.createElement("input",{className:"form-control mb-4",type:"text",placeholder:"URL",value:c,onChange:function(e){var t=e.target;o(t.value)},onKeyDown:function(e){"Enter"===e.key&&i()}}))})),ne=(a(87),function(e){var t=e.title,a=e.body,l=Object(n.useState)(!1),c=Object(te.a)(l,2),o=c[0],s=c[1];Object(n.useEffect)((function(){t?(document.body.style.paddingRight=window.innerWidth-document.documentElement.clientWidth+"px",document.body.classList.add("body--backdrop"),s(!0)):!t&&o&&(document.body.style.paddingRight=0,document.body.classList.remove("body--backdrop"),s(!1))}),[t,a]);var i="backdrop p-2 text-white"+(t?" backdrop--visible":"");return r.a.createElement("div",{className:i},r.a.createElement("h3",{className:"text-white"},t),r.a.createElement("h5",{className:"text-white"},a))}),re=(a(88),a(89),function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={playerStatus:{},loading:!0,networkError:!1,backdrop:{}},a.handleBackdrop=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";a.setState({backdrop:{title:e,body:t}}),setTimeout((function(){return a.setState({backdrop:{}})}),T.a)},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"componentDidMount",value:function(){this.updatePlayerStatus()}},{key:"updatePlayerStatus",value:function(){var e,t,a=this;return s.a.async((function(n){for(;;)switch(n.prev=n.next){case 0:return n.prev=0,n.next=3,s.a.awrap(S.get(O));case 3:e=n.sent,t=e.data,this.setState({playerStatus:t,loading:!1,networkError:!1}),n.next=11;break;case 8:n.prev=8,n.t0=n.catch(0),this.setState({loading:!1,networkError:!0});case 11:setTimeout((function(){return a.updatePlayerStatus()}),T.c);case 12:case"end":return n.stop()}}),null,this,[[0,8]])}},{key:"render",value:function(){var e=this.state,t=e.loading,a=e.networkError,n=e.backdrop,l=e.playerStatus,c=this.props.t;return a?r.a.createElement(ne,{title:c("errorNetwork")}):t?r.a.createElement(U,null):l.con_mpd?r.a.createElement("div",{className:"app"},r.a.createElement(w.a,null),r.a.createElement(ne,{title:n.title,body:n.body}),r.a.createElement(h,null),r.a.createElement(W,{playerStatus:l}),r.a.createElement("main",{className:"app-main p-4"},r.a.createElement(B,{playerStatus:l}),r.a.createElement(D,{onBackdrop:this.handleBackdrop,playerStatus:l}),r.a.createElement(ee,null),r.a.createElement(ae,null))):r.a.createElement(ne,{title:c("disconnectedMPD")})}}]),t}(n.Component)),le=Object(b.c)()(re);Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));var ce=a(44),oe=a(45);Q.a.use(ce.a).use(oe.a).use(b.b).init({fallbackLng:"en",debug:Object({NODE_ENV:"production",PUBLIC_URL:"",REACT_APP_API_URL:"/api"}).REACT_APP_I18N_DEBUG,interpolation:{escapeValue:!1}});Q.a,a(90),a(91),a(92);$(),c.a.render(r.a.createElement(n.Suspense,{fallback:r.a.createElement(U,null)},r.a.createElement(le,null)),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then((function(e){e.unregister()}))}},[[46,1,2]]]);
2
+ //# sourceMappingURL=main.52997fad.chunk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["logo.svg","components/navBar.jsx","components/playerStatus.jsx","services/httpService.js","services/playerService.js","components/playStopControl.jsx","components/playerControls.jsx","components/player.jsx","components/common/searchBox.jsx","components/loader.jsx","services/streamsService.js","components/streams.jsx","components/miniPlayer.jsx","components/drawer.jsx","components/common/modal.jsx","theme.js","components/common/select.jsx","components/settings.jsx","components/urlDialog.jsx","components/backdrop.jsx","App.js","serviceWorker.js","i18n.js","index.js"],"names":["module","exports","withTranslation","t","className","href","src","logo","width","height","alt","type","data-toggle","data-target","aria-controls","aria-expanded","aria-label","id","playerStatus","playing","title","axios","interceptors","response","use","error","expectedError","status","networkError","toast","i18n","Promise","reject","get","post","apiEndpoint","process","body","method","params","FormData","set","play","http","stop","changeVol","delta","playRadio","name","playURL","url","PlayStopControl","onClick","icon","faStop","faPlay","volDisabled","vol","renderVolButton","disabled","a","async","data","toastMsg","toastOpts","toastId","autoClose","volTimeout","isActive","update","render","info","handleVolChange","faVolumeDown","faVolumeUp","positionPlayerTop","navHeight","document","querySelector","clientHeight","style","top","Player","useEffect","value","onChange","placeholder","autoComplete","e","target","onFocus","Loader","role","Streams","state","streams","loading","query","isPlaying","props","handleItemClick","onBackdrop","getItemClasses","classes","handleSearch","setState","this","filtered","k","toLowerCase","includes","filteredStreams","Object","keys","length","map","key","renderList","Component","MiniPlayer","faChevronUp","Drawer","open","toggle","handleClick","tagName","handleTouchStart","touchStartY","touches","clientY","handleTouchMove","touchEndY","changedTouches","touchUp","classList","add","remove","onTouchStart","onTouchMove","Fragment","faChevronDown","Modal","footer","children","tabIndex","aria-labelledby","aria-hidden","data-dismiss","DEFAULT_THEME","STORAGE_KEY","getThemePath","themeId","themes","themeColor","applyTheme","getThemeId","themePath","find","setAttribute","localId","localStorage","getItem","filter","removeItem","Select","label","row","rest","htmlFor","d","setItem","languages","i18next","language","changeLanguage","useState","setURL","handleOK","doPlayURL","errorToastOpts","TYPE","ERROR","input","onKeyDown","Backdrop","bodyStyled","setBodyStyled","paddingRight","window","innerWidth","documentElement","clientWidth","App","backdrop","handleBackdrop","setTimeout","backdropTimeout","updatePlayerStatus","updateInterval","con_mpd","Boolean","location","hostname","match","Backend","LanguageDetector","initReactI18next","init","fallbackLng","debug","REACT_APP_I18N_DEBUG","interpolation","escapeValue","ReactDOM","fallback","getElementById","navigator","serviceWorker","ready","then","registration","unregister"],"mappings":"0MAAAA,EAAOC,QAAU,IAA0B,kC,uZCmD5BC,iBA9CA,SAAC,GAAD,IAAGC,EAAH,EAAGA,EAAH,OACb,yBAAKC,UAAU,mDACb,uBAAGA,UAAU,eAAeC,KAAK,KAC/B,yBACEC,IAAKC,IACLC,MAAM,KACNC,OAAO,KACPL,UAAU,wCACVM,IAAI,KANR,cAUA,4BACEN,UAAU,iBACVO,KAAK,SACLC,cAAY,WACZC,cAAY,sBACZC,gBAAc,qBACdC,gBAAc,QACdC,aAAW,qBAEX,0BAAMZ,UAAU,yBAElB,yBAAKA,UAAU,2BAA2Ba,GAAG,sBAC3C,yBAAKb,UAAU,wBAGb,4BACEA,UAAU,wBACVQ,cAAY,QACZC,cAAY,eAEXV,EAAE,YAGL,4BACEC,UAAU,wBACVQ,cAAY,QACZC,cAAY,aAEXV,EAAE,mBC7BED,iBAbM,SAAC,GAAyB,IAAvBgB,EAAsB,EAAtBA,aAAcf,EAAQ,EAARA,EAC5BgB,EAAmBD,EAAnBC,QAASC,EAAUF,EAAVE,MAEjB,OACE,yBAAKhB,UAAU,eACb,wBAAIA,UAAU,wBACDD,EAAVgB,EAAY,UAAe,YAE9B,wBAAIf,UAAU,YAAYgB,O,wCCNhCC,IAAMC,aAAaC,SAASC,IAAI,MAAM,SAAAC,GACpC,IAAMC,EACJD,EAAMF,UACNE,EAAMF,SAASI,QAAU,KACzBF,EAAMF,SAASI,OAAS,IAGpBC,GAAgBH,EAAMF,SAgB5B,OAbKG,GAAkBE,GACrBC,IAAMJ,MACJ,kBAAC,IAAD,MACG,SAACtB,EAAD,KAAM2B,KAAN,OAAiB,2BAAI3B,EAAE,wBAK1BsB,EAAMF,UAAsC,MAA1BE,EAAMF,SAASI,QACnCE,IAAMJ,MACJ,kBAAC,IAAD,MAAc,SAACtB,EAAD,KAAM2B,KAAN,OAAiB,2BAAI3B,EAAE,uBAGlC4B,QAAQC,OAAOP,MAGT,OACbQ,IAAKZ,IAAMY,IACXC,KAAMb,IAAMa,MC/BRC,EAAW,UAAMC,OAAN,WAEjB,SAASC,EAAKC,GAAwB,IAAhBC,EAAe,uDAAN,KACvBF,EAAO,IAAIG,SAGjB,OAFAH,EAAKI,IAAI,SAAUH,GACfC,GAAQF,EAAKI,IAAI,SAAUF,GACxBF,EAOF,SAASK,IACd,OAAOC,EAAKT,KAAKC,EAAaE,EAAK,SAG9B,SAASO,IACd,OAAOD,EAAKT,KAAKC,EAAaE,EAAK,SAG9B,SAASQ,EAAUC,GACxB,OAAOH,EAAKT,KAAKC,EAAaE,EAAK,aAAcS,IAG5C,SAASC,EAAUC,GACxB,OAAOL,EAAKT,KAAKC,EAAaE,EAAK,cAAeW,IAG7C,SAASC,EAAQC,GACtB,OAAOP,EAAKT,KAAKC,EAAaE,EAAK,YAAaa,IC3BlD,IAWeC,EAXS,SAAC,GAAD,SAAGhC,QAEvB,4BAAQf,UAAU,iBAAiBgD,QAASR,GAC1C,kBAAC,IAAD,CAAiBS,KAAMC,OAGzB,4BAAQlD,UAAU,eAAegD,QAASV,GACxC,kBAAC,IAAD,CAAiBW,KAAME,Q,QC8BdrD,G,MAAAA,eAhCQ,SAAC,GAAyB,IAAvBgB,EAAsB,EAAtBA,aAAcf,EAAQ,EAARA,EAChCqD,EAActC,EAAauC,IAAM,EAYjCC,EAAkB,SAACZ,EAAOO,GAAR,OACtB,4BACEjD,UAAU,mBACVuD,SAAUH,EACVJ,QAAS,kBAdW,SAAMN,GAAN,qBAAAc,EAAAC,OAAA,kEAAAD,EAAA,MACMf,EAAUC,IADhB,gBACRW,EADQ,EACdK,KAEQ,MACVC,EAJgB,UAIF5D,EAAE,UAJA,aAIcsD,EAJd,KAKhBO,EAAY,CAAEC,QAFJ,MAEaC,UAAWC,KACpCtC,IAAMuC,SAHM,OAGavC,IAAMwC,OAHnB,MAGmC,CAAEC,OAAQP,IACxDlC,IAAM0C,KAAKR,EAAUC,GAPJ,qCAcLQ,CAAgB1B,KAE/B,kBAAC,IAAD,CAAiBO,KAAMA,MAI3B,OACE,yBAAKjD,UAAU,mCACZsD,EAAgB,KAAMe,KACtBf,EAAgB,KAAMgB,KACvB,kBAAC,EAAD,CAAiBvD,QAASD,EAAaC,eCnBvCwD,G,MAAoB,WACxB,IAAMC,EAAYC,SAASC,cAAc,OAAOC,aAEhDF,SAASC,cAAc,WAAWE,MAAMC,IAAML,EAD5B,GACoD,OAGzDM,EAnBA,SAAC,GAAsB,IAApBhE,EAAmB,EAAnBA,aAGhB,OAFAiE,oBAAUR,EAAmB,CAACzD,IAG5B,yBAAKd,UAAU,cACb,kBAAC,EAAD,CAAcc,aAAcA,IAC5B,kBAAC,EAAD,CAAgBA,aAAcA,MCOrBhB,iBAfG,SAAC,GAA4B,IAA1BkF,EAAyB,EAAzBA,MAAOC,EAAkB,EAAlBA,SAAUlF,EAAQ,EAARA,EACpC,OACE,2BACEC,UAAU,oBACVO,KAAK,OACLM,GAAG,QACHqE,YAAanF,EAAE,UACfoF,aAAa,MACbH,MAAOA,EACPC,SAAU,SAAAG,GAAC,OAAIH,EAASG,EAAEC,OAAOL,QACjCM,QAAS,kBAAML,EAAS,UCFfM,G,MARA,kBACb,yBAAKvF,UAAU,UACb,yBAAKA,UAAU,iBAAiBwF,KAAK,UACnC,0BAAMxF,UAAU,WAAhB,kBCJA+B,EAAW,UAAMC,OAAN,Y,UCOXyD,E,2MACJC,MAAQ,CAAEC,QAAS,GAAIC,SAAS,EAAMC,MAAO,I,EAO7CC,UAAY,SAAAlD,GACV,OACEA,IAAS,EAAKmD,MAAMjF,aAAaE,OAAS,EAAK+E,MAAMjF,aAAaC,S,EAItEiF,gBAAkB,SAAMpD,GAAN,mBAAAY,EAAAC,OAAA,qDACU,EAAKsC,MAAvBhG,EADQ,EACRA,EAAGkG,EADK,EACLA,YAEP,EAAKH,UAAUlD,GAHH,wDAKhBqD,EAAWlG,EAAE,WAAY6C,GALT,oBAAAY,EAAA,MAORb,EAAUC,IAPF,uDASV,KAAGzB,UAAmC,MAAvB,KAAGA,SAASI,QAC7BE,IAAMJ,MAAMtB,EAAE,kBAVF,yD,EAclBmG,eAAiB,SAAAtD,GACf,IAAMuD,EAAU,0CAChB,OAAO,EAAKL,UAAUlD,GAClBuD,EAAU,SACVA,EAAU,0B,EAGhBC,aAAe,SAAAP,GACb,EAAKQ,SAAS,CAAER,W,uLDxCXtD,EAAKV,IAAIE,I,gBCQA4D,E,EAANjC,KACR4C,KAAKD,SAAS,CAAEV,UAASC,SAAS,I,yFAkCjB,IAAD,EACWU,KAAKZ,MAAxBC,EADQ,EACRA,QAASE,EADD,EACCA,MAEjB,GAAc,KAAVA,EAAc,OAAOF,EAEzB,IAAIY,EAAW,GACf,IAAK,IAAIC,KAAKb,EACRa,EAAEC,cAAcC,SAASb,EAAMY,iBAAmBd,EAAQa,KAC5DD,EAASC,GAAKb,EAAQa,IAG1B,OAAOD,I,mCAGK,IAAD,OACLZ,EAAUW,KAAKK,kBAErB,OAAoC,IAAhCC,OAAOC,KAAKlB,GAASmB,OAChB,wBAAI9G,UAAU,OAAOsG,KAAKP,MAAMhG,EAAE,cAGzC,wBAAIC,UAAU,+BACX4G,OAAOC,KAAKlB,GAASoB,KAAI,SAAAnE,GAAI,OAC5B+C,EAAQ/C,GACN,wBAAI5C,UAAU,2BAA2BgH,IAAKpE,GAC3CA,GAGH,wBACE5C,UAAW,EAAKkG,eAAetD,GAC/BoE,IAAKpE,EACLI,QAAS,kBAAM,EAAKgD,gBAAgBpD,KAEnCA,S,+BASX,OAAI0D,KAAKZ,MAAME,QAAgB,kBAAC,EAAD,MAG7B,yBAAK5F,UAAU,eACb,kBAAC,EAAD,CAAWgF,MAAOsB,KAAKZ,MAAMG,MAAOZ,SAAUqB,KAAKF,eAClDE,KAAKW,kB,GAtFQC,aA4FPpH,gBAAkB2F,GClFlB0B,G,MAbI,SAAC,GAAsB,IAApBrG,EAAmB,EAAnBA,aACZE,EAAmBF,EAAnBE,MAAOD,EAAYD,EAAZC,QACf,OACE,yBAAKf,UAAU,mBACb,yBAAKA,UAAU,mCACb,kBAAC,IAAD,CAAiBiD,KAAMmE,IAAapH,UAAU,yBAC9C,8BAAOgB,IAET,kBAAC,EAAD,CAAiBD,QAASA,OCwDjBsG,G,iNA9Db3B,MAAQ,CAAE4B,MAAM,G,EAEhBC,OAAS,WACP,EAAKlB,SAAS,CAAEiB,MAAO,EAAK5B,MAAM4B,Q,EAGpCE,YAAc,YAAiB,IAAdnC,EAAa,EAAbA,QAEO,WAAnBA,EAAOoC,SAA2C,MAAnBpC,EAAOoC,WACtCpC,EAAOrF,UAAU0G,SAAS,oBAG7B,EAAKa,U,EAGPG,iBAAmB,SAAAtC,GACjB,EAAKiB,SAAS,CAAEsB,YAAavC,EAAEwC,QAAQ,GAAGC,W,EAG5CC,gBAAkB,SAAA1C,GAAM,IAAD,EACS,EAAKM,MAA3B4B,EADa,EACbA,KAAMK,EADO,EACPA,YACRI,EAAY3C,EAAE4C,eAAe,GAAGH,QAChCI,EAAUF,EAAYJ,EACVI,EAAYJ,GAEbL,EAAM,EAAKC,SACnBU,IAAYX,GAAM,EAAKC,U,wEAGxB,IACAzG,EAAiBwF,KAAKP,MAAtBjF,aACJqF,EACF,+DAOF,OALIG,KAAKZ,MAAM4B,MACbnB,GAAW,gBACX1B,SAASxC,KAAKiG,UAAUC,IAAI,iBACvB1D,SAASxC,KAAKiG,UAAUE,OAAO,gBAGpC,yBACEpI,UAAWmG,EACXnD,QAASsD,KAAKkB,YACda,aAAc/B,KAAKoB,iBACnBY,YAAahC,KAAKwB,iBAEjBxB,KAAKZ,MAAM4B,KACV,kBAAC,IAAMiB,SAAP,KACE,kBAAC,IAAD,CACEtF,KAAMuF,IACNxI,UAAU,+BAEZ,kBAAC,EAAD,CAAQc,aAAcA,IAAiB,KAGzC,kBAAC,EAAD,CAAYA,aAAcA,S,GAxDfoG,cC4BNuB,EAjCD,SAAC,GAAqC,IAAnC5H,EAAkC,EAAlCA,GAAIG,EAA8B,EAA9BA,MAAO0H,EAAuB,EAAvBA,OAAQC,EAAe,EAAfA,SAClC,OACE,yBACE3I,UAAU,aACVa,GAAIA,EACJ+H,SAAS,KACTpD,KAAK,SACLqD,kBAAgB,sBAChBC,cAAY,QAEZ,yBAAK9I,UAAU,eAAewF,KAAK,YACjC,yBAAKxF,UAAU,iBACb,yBAAKA,UAAU,gBACb,wBAAIA,UAAU,cAAca,GAAG,uBAC5BG,GAEH,4BACET,KAAK,SACLP,UAAU,QACV+I,eAAa,QACbnI,aAAW,SAEX,0BAAMkI,cAAY,QAAlB,UAGJ,yBAAK9I,UAAU,cAAc2I,GAC7B,yBAAK3I,UAAU,gBAAgB0I,OC5BnCM,EAAgB,SAEhBC,EAAc,QAEdC,EAAe,SAAAC,GAAO,oEAC6BA,EAD7B,uBAGfC,EAAS,CACpB,CAAEvI,GAAI,SAAU+B,KAAM,SAAUyG,WAAY,WAC5C,CAAExI,GAAI,MAAO+B,KAAM,MAAOyG,WAAY,WACtC,CAAExI,GAAI,SAAU+B,KAAM,SAAUyG,WAAY,YAQvC,SAASC,IACd,IAAMH,EAAUI,IACVC,EAAYN,EAAaC,GACzBE,EAAaD,EAAOK,MAAK,SAAA1J,GAAC,OAAIA,EAAEc,KAAOsI,KAASE,WAEvC5E,SAASC,cAAc,uBAC/BgF,aAAa,OAAQF,GAEb/E,SAASC,cAAc,4BAC/BgF,aAAa,UAAWL,GAG1B,SAASE,IACd,IAAMI,EAAUC,aAAaC,QAAQZ,GAErC,MAAgB,KAAZU,EAAuBX,EACyB,IAAhDI,EAAOU,QAAO,SAAA/J,GAAC,OAAIA,EAAEc,KAAO8I,KAAS7C,QACvC8C,aAAaG,WAAWd,GACjBD,GAGFW,E,oBCpBMK,EAjBA,SAAC,GAAD,IAAGnJ,EAAH,EAAGA,GAAIoJ,EAAP,EAAOA,MAAOC,EAAd,EAAcA,IAAKxG,EAAnB,EAAmBA,KAASyG,EAA5B,kDACb,yBAAKnK,UAAWkK,EAAM,iBAAmB,cACvC,2BAAOE,QAASvJ,EAAIb,UAAU,2BAC3BiK,GAEH,yBAAKjK,UAAU,aACb,0CAAQa,GAAIA,EAAIb,UAAU,gBAAmBmK,GAC1CzG,EAAKqD,KAAI,SAAAsD,GAAC,OACT,4BAAQrD,IAAKqD,EAAExJ,IAAMwJ,EAAGrF,MAAOqF,EAAExJ,IAAMwJ,GACpCA,EAAEzH,MAAQyH,UC8BRvK,kBAjCE,SAAC,GAAW,IAATC,EAAQ,EAARA,EAOlB,OACE,kBAAC,EAAD,CAAOc,GAAG,WAAWG,MAAOjB,EAAE,YAAa2I,OAN3C,4BAAQ1I,UAAU,oBAAoB+I,eAAa,SAChDhJ,EAAE,WAMH,kBAAC,EAAD,CACEc,GAAG,eACHoJ,MAAOlK,EAAE,SACT2D,KAAM0F,EACNc,KAAG,EACHlF,MAAOuE,IACPtE,SAAU,SAAAG,GFVX,IAAqB+D,IEWN/D,EAAEC,OAAOL,MFV7B4E,aAAaU,QAAQrB,EAAaE,GAClCG,OEYI,kBAAC,EAAD,CACEzI,GAAG,kBACHoJ,MAAOlK,EAAE,YACT2D,KAAM6G,IACNL,KAAG,EACHlF,MAAOwF,IAAQC,SACfxF,SAAU,SAAAG,GACRoF,IAAQE,eAAetF,EAAEC,OAAOL,c,SCsC3BlF,kBAlEG,SAAC,GAAW,IAATC,EAAQ,EAARA,EAAQ,EACL4K,mBAAS,IADJ,oBACpB7H,EADoB,KACf8H,EADe,KAGrBC,EAAW,WACH,KAAR/H,IACJgI,EAAUhI,GACV8H,EAAO,MAGHE,EAAY,SAAMhI,GAAN,iBAAAU,EAAAC,OAAA,uDACVsH,EAAiB,CACrBxK,KAAMkB,IAAMuJ,KAAKC,MACjB/G,OAAQnE,EAAE,kBAGN8D,EAAUpC,YAAM1B,EAAE,cANR,oBAAAyD,EAAA,MASRX,EAAQC,IATA,uDAWV,KAAG3B,UAAmC,MAAvB,KAAGA,SAASI,QAC7BE,IAAMwC,OAAOJ,EAASkH,GAZV,yDA2ClB,OACE,kBAAC,EAAD,CAAOlK,GAAG,aAAaG,MAAOjB,EAAE,WAAY2I,OAnB5C,kBAAC,IAAMH,SAAP,KACE,4BACEvI,UAAU,kBACV+I,eAAa,QACb/F,QAAS6H,GAHX,MAOA,4BACE7K,UAAU,oBACVgD,QAAS,kBAAM4H,EAAO,KACtB7B,eAAa,SAHf,YAYA,2BACE/I,UAAU,oBACVO,KAAK,OACL2E,YAAY,MACZF,MAAOlC,EACPmC,SAlCe,SAAC,GAAuB,IAAbiG,EAAY,EAApB7F,OACtBuF,EAAOM,EAAMlG,QAkCTmG,UA/BgB,SAAA/F,GACN,UAAVA,EAAE4B,KAAiB6D,WCCZO,I,MAlCE,SAAC,GAAqB,IAAnBpK,EAAkB,EAAlBA,MAAOiB,EAAW,EAAXA,KAAW,EACA0I,oBAAS,GADT,oBAC7BU,EAD6B,KACjBC,EADiB,KAoBpCvG,qBAdoB,WACd/D,GAEFyD,SAASxC,KAAK2C,MAAM2G,aALtBC,OAAOC,WAAahH,SAASiH,gBAAgBC,YAKc,KACzDlH,SAASxC,KAAKiG,UAAUC,IAAI,kBAC5BmD,GAAc,KAEJtK,GAASqK,IACnB5G,SAASxC,KAAK2C,MAAM2G,aAAe,EACnC9G,SAASxC,KAAKiG,UAAUE,OAAO,kBAC/BkD,GAAc,MAIK,CAACtK,EAAOiB,IAE/B,IAAMkE,EACJ,2BAA6BnF,EAAQ,qBAAuB,IAG9D,OACE,yBAAKhB,UAAWmG,GACd,wBAAInG,UAAU,cAAcgB,GAC5B,wBAAIhB,UAAU,cAAciC,MChB5B2J,I,uNACJlG,MAAQ,CACN5E,aAAc,GACd8E,SAAS,EACTpE,cAAc,EACdqK,SAAU,I,EAkBZC,eAAiB,SAAC9K,GAAsB,IAAfiB,EAAc,uDAAP,GAC9B,EAAKoE,SAAS,CAAEwF,SAAU,CAAE7K,QAAOiB,UACnC8J,YAAW,kBAAM,EAAK1F,SAAS,CAAEwF,SAAU,OAAOG,M,mFAhBlD1F,KAAK2F,uB,+JhBbA1J,EAAKV,IAAIE,I,gBgBkBEjB,E,EAAN4C,KACR4C,KAAKD,SAAS,CAAEvF,eAAc8E,SAAS,EAAOpE,cAAc,I,gDAE5D8E,KAAKD,SAAS,CAAET,SAAS,EAAOpE,cAAc,I,QAGhDuK,YAAW,kBAAM,EAAKE,uBAAsBC,K,yFAQpC,IAAD,EACmD5F,KAAKZ,MAAvDE,EADD,EACCA,QAASpE,EADV,EACUA,aAAcqK,EADxB,EACwBA,SAAU/K,EADlC,EACkCA,aACjCf,EAAMuG,KAAKP,MAAXhG,EAER,OAAIyB,EAAqB,kBAAC,GAAD,CAAUR,MAAOjB,EAAE,kBACxC6F,EAAgB,kBAAC,EAAD,MACf9E,EAAaqL,QAGhB,yBAAKnM,UAAU,OACb,kBAAC,IAAD,MACA,kBAAC,GAAD,CAAUgB,MAAO6K,EAAS7K,MAAOiB,KAAM4J,EAAS5J,OAChD,kBAAC,EAAD,MACA,kBAAC,EAAD,CAAQnB,aAAcA,IAEtB,0BAAMd,UAAU,gBACd,kBAAC,EAAD,CAAQc,aAAcA,IACtB,kBAAC,EAAD,CACEmF,WAAYK,KAAKwF,eACjBhL,aAAcA,IAEhB,kBAAC,GAAD,MACA,kBAAC,GAAD,QAhB4B,kBAAC,GAAD,CAAUE,MAAOjB,EAAE,yB,GAlCvCmH,cAyDHpH,iBAAkB8L,IC7DbQ,QACW,cAA7BZ,OAAOa,SAASC,UAEe,UAA7Bd,OAAOa,SAASC,UAEhBd,OAAOa,SAASC,SAASC,MACvB,2D,sBCTN7K,IAGGN,IAAIoL,MAGJpL,IAAIqL,MAEJrL,IAAIsL,KAGJC,KAAK,CACJC,YAAa,KACbC,MAAO7K,uEAAY8K,qBAEnBC,cAAe,CACbC,aAAa,KAIJtL,EAAf,E,kBCdA4H,IAEA2D,IAAS/I,OAEP,kBAAC,WAAD,CAAUgJ,SAAU,kBAAC,EAAD,OAClB,kBAAC,GAAD,OAEFzI,SAAS0I,eAAe,SF6GpB,kBAAmBC,WACrBA,UAAUC,cAAcC,MAAMC,MAAK,SAAAC,GACjCA,EAAaC,kB","file":"static/js/main.52997fad.chunk.js","sourcesContent":["module.exports = __webpack_public_path__ + \"static/media/logo.91554ce9.svg\";","import React from 'react';\nimport { withTranslation } from 'react-i18next';\nimport './navBar.scss';\nimport logo from '../logo.svg';\n\nconst NavBar = ({ t }) => (\n <nav className=\"navbar navbar-expand-sm navbar-dark bg-primary \">\n <a className=\"navbar-brand\" href=\"/\">\n <img\n src={logo}\n width=\"30\"\n height=\"30\"\n className=\"d-inline-block align-text-bottom mr-2\"\n alt=\"\"\n />\n PiFi Radio\n </a>\n <button\n className=\"navbar-toggler\"\n type=\"button\"\n data-toggle=\"collapse\"\n data-target=\"#navbarNavAltMarkup\"\n aria-controls=\"navbarNavAltMarkup\"\n aria-expanded=\"false\"\n aria-label=\"Toggle navigation\"\n >\n <span className=\"navbar-toggler-icon\"></span>\n </button>\n <div className=\"collapse navbar-collapse\" id=\"navbarNavAltMarkup\">\n <div className=\"navbar-nav pt-1 ml-2\">\n {/* We use buttons and different class names instead of Bootstrap's\n anchor, so we don't get a warning for href=\"#\". */}\n <button\n className=\"btn btn-link nav-link\"\n data-toggle=\"modal\"\n data-target=\"#url-dialog\"\n >\n {t('playURL')}\n </button>\n\n <button\n className=\"btn btn-link nav-link\"\n data-toggle=\"modal\"\n data-target=\"#settings\"\n >\n {t('settings')}\n </button>\n </div>\n </div>\n </nav>\n);\nexport default withTranslation()(NavBar);\n","import React from 'react';\nimport { withTranslation } from 'react-i18next';\n\nconst PlayerStatus = ({ playerStatus, t }) => {\n const { playing, title } = playerStatus;\n\n return (\n <div className=\"text-center\">\n <h5 className=\"small text-uppercase\">\n {playing ? t('playing') : t('stopped')}\n </h5>\n <h3 className=\"ellipsis\">{title}</h3>\n </div>\n );\n};\n\nexport default withTranslation()(PlayerStatus);\n","import React from 'react';\nimport axios from 'axios';\nimport { toast } from 'react-toastify';\nimport { Translation } from 'react-i18next';\n\naxios.interceptors.response.use(null, error => {\n const expectedError =\n error.response &&\n error.response.status >= 400 &&\n error.response.status < 500;\n\n // We already alert about these with a backdrop on App.js\n const networkError = !error.response;\n\n // Ugly, but i18n only worked this way\n if (!expectedError && !networkError) {\n toast.error(\n <Translation>\n {(t, { i18n }) => <p>{t('errorUnexpected')}</p>}\n </Translation>\n );\n }\n // This expected error is universal in our app, so we'll place it here\n if (error.response && error.response.status === 403)\n toast.error(\n <Translation>{(t, { i18n }) => <p>{t('errorForbidden')}</p>}</Translation>\n );\n\n return Promise.reject(error);\n});\n\nexport default {\n get: axios.get,\n post: axios.post\n};\n","import http from './httpService.js';\n\nconst apiEndpoint = `${process.env.REACT_APP_API_URL}/player`;\n\nfunction body(method, params = null) {\n const body = new FormData();\n body.set('method', method);\n if (params) body.set('params', params);\n return body;\n}\n\nexport function getStatus() {\n return http.get(apiEndpoint);\n}\n\nexport function play() {\n return http.post(apiEndpoint, body('play'));\n}\n\nexport function stop() {\n return http.post(apiEndpoint, body('stop'));\n}\n\nexport function changeVol(delta) {\n return http.post(apiEndpoint, body('change_vol', delta));\n}\n\nexport function playRadio(name) {\n return http.post(apiEndpoint, body('play_radios', name));\n}\n\nexport function playURL(url) {\n return http.post(apiEndpoint, body('play_urls', url));\n}\n","import React from 'react';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faStop, faPlay } from '@fortawesome/free-solid-svg-icons';\nimport { play, stop } from '../services/playerService';\n\nconst PlayStopControl = ({ playing }) =>\n playing ? (\n <button className=\"btn btn-danger\" onClick={stop}>\n <FontAwesomeIcon icon={faStop} />\n </button>\n ) : (\n <button className=\"btn btn-dark\" onClick={play}>\n <FontAwesomeIcon icon={faPlay} />\n </button>\n );\n\nexport default PlayStopControl;\n","import React from 'react';\nimport PlayStopControl from './playStopControl';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faVolumeDown, faVolumeUp } from '@fortawesome/free-solid-svg-icons';\nimport { toast } from 'react-toastify';\nimport { withTranslation } from 'react-i18next';\nimport { changeVol } from '../services/playerService';\nimport { volTimeout } from '../config.json';\nimport './playerControls.scss';\n\nconst PlayerControls = ({ playerStatus, t }) => {\n const volDisabled = playerStatus.vol < 0;\n\n const handleVolChange = async delta => {\n const { data: vol } = await changeVol(delta);\n\n const toastId = 'vol';\n const toastMsg = `${t('volume')}: ${vol}%`;\n const toastOpts = { toastId, autoClose: volTimeout };\n if (toast.isActive(toastId)) toast.update(toastId, { render: toastMsg });\n else toast.info(toastMsg, toastOpts);\n };\n\n const renderVolButton = (delta, icon) => (\n <button\n className=\"btn btn-dark p-3\"\n disabled={volDisabled}\n onClick={() => handleVolChange(delta)}\n >\n <FontAwesomeIcon icon={icon} />\n </button>\n );\n\n return (\n <div className=\"player-controls btn-group w-100\">\n {renderVolButton('-5', faVolumeDown)}\n {renderVolButton('+5', faVolumeUp)}\n <PlayStopControl playing={playerStatus.playing} />\n </div>\n );\n};\n\nexport default withTranslation()(PlayerControls);\n","import React, { useEffect } from 'react';\nimport PlayerStatus from './playerStatus';\nimport PlayerControls from './playerControls';\nimport './player.scss';\n\nconst Player = ({ playerStatus }) => {\n useEffect(positionPlayerTop, [playerStatus]);\n\n return (\n <div className=\"player p-2\">\n <PlayerStatus playerStatus={playerStatus} />\n <PlayerControls playerStatus={playerStatus} />\n </div>\n );\n};\n\n// Calculate and set top for Player\n// Needed for desktop view with sticky\nconst positionPlayerTop = () => {\n const navHeight = document.querySelector('nav').clientHeight;\n const topMargin = 24;\n document.querySelector('.player').style.top = navHeight + topMargin + 'px';\n};\n\nexport default Player;\n","import React from 'react';\nimport { withTranslation } from 'react-i18next';\n\nconst SearchBox = ({ value, onChange, t }) => {\n return (\n <input\n className=\"form-control mb-4\"\n type=\"text\"\n id=\"query\"\n placeholder={t('search')}\n autoComplete=\"off\"\n value={value}\n onChange={e => onChange(e.target.value)}\n onFocus={() => onChange('')}\n ></input>\n );\n};\n\nexport default withTranslation()(SearchBox);\n","import React from 'react';\nimport './loader.scss';\n\nconst Loader = () => (\n <div className=\"loader\">\n <div className=\"spinner-border\" role=\"status\">\n <span className=\"sr-only\">Loading...</span>\n </div>\n </div>\n);\n\nexport default Loader;\n","import http from './httpService';\n\nconst apiEndpoint = `${process.env.REACT_APP_API_URL}/streams`;\n\nexport function getStreams() {\n return http.get(apiEndpoint);\n}\n","import React, { Component } from 'react';\nimport SearchBox from './common/searchBox';\nimport Loader from './loader';\nimport { withTranslation } from 'react-i18next';\nimport { toast } from 'react-toastify';\nimport { playRadio } from '../services/playerService';\nimport { getStreams } from '../services/streamsService';\nimport './streams.scss';\n\nclass Streams extends Component {\n state = { streams: {}, loading: true, query: '' };\n\n async componentDidMount() {\n const { data: streams } = await getStreams();\n this.setState({ streams, loading: false });\n }\n\n isPlaying = name => {\n return (\n name === this.props.playerStatus.title && this.props.playerStatus.playing\n );\n };\n\n handleItemClick = async name => {\n const { t, onBackdrop } = this.props;\n\n if (this.isPlaying(name)) return;\n\n onBackdrop(t('tunning'), name);\n try {\n await playRadio(name);\n } catch (ex) {\n if (ex.response && ex.response.status === 400)\n toast.error(t('errorNotFound'));\n }\n };\n\n getItemClasses = name => {\n const classes = 'streams__item ellipsis list-group-item ';\n return this.isPlaying(name)\n ? classes + 'active'\n : classes + 'list-group-item-action';\n };\n\n handleSearch = query => {\n this.setState({ query });\n };\n\n filteredStreams() {\n const { streams, query } = this.state;\n\n if (query === '') return streams;\n\n let filtered = {};\n for (let k in streams) {\n if (k.toLowerCase().includes(query.toLowerCase()) && !streams[k]) {\n filtered[k] = streams[k];\n }\n }\n return filtered;\n }\n\n renderList() {\n const streams = this.filteredStreams();\n\n if (Object.keys(streams).length === 0)\n return <h4 className=\"p-4\">{this.props.t('noStreams')}</h4>;\n\n return (\n <ul className=\"list-group list-group-flush\">\n {Object.keys(streams).map(name =>\n streams[name] ? (\n <li className=\"streams__header ellipsis\" key={name}>\n {name}\n </li>\n ) : (\n <li\n className={this.getItemClasses(name)}\n key={name}\n onClick={() => this.handleItemClick(name)}\n >\n {name}\n </li>\n )\n )}\n </ul>\n );\n }\n\n render() {\n if (this.state.loading) return <Loader />;\n\n return (\n <div className=\"streams p-2\">\n <SearchBox value={this.state.query} onChange={this.handleSearch} />\n {this.renderList()}\n </div>\n );\n }\n}\n\nexport default withTranslation()(Streams);\n","import React from 'react';\nimport PlayStopControl from './playStopControl';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp } from '@fortawesome/free-solid-svg-icons';\nimport './miniPlayer.scss';\n\nconst MiniPlayer = ({ playerStatus }) => {\n const { title, playing } = playerStatus;\n return (\n <div className=\"mini-player p-3\">\n <div className=\"mini-player__left ellipsis pr-3\">\n <FontAwesomeIcon icon={faChevronUp} className=\"drawer__toggler mr-3\" />\n <span>{title}</span>\n </div>\n <PlayStopControl playing={playing} />\n </div>\n );\n};\n\nexport default MiniPlayer;\n","import React, { Component } from 'react';\nimport MiniPlayer from './miniPlayer';\nimport Player from './player';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown } from '@fortawesome/free-solid-svg-icons';\nimport './drawer.scss';\n\nclass Drawer extends Component {\n state = { open: false };\n\n toggle = () => {\n this.setState({ open: !this.state.open });\n };\n\n handleClick = ({ target }) => {\n const blackList =\n (target.tagName === 'BUTTON' || target.tagName === 'I') &&\n !target.className.includes('drawer__toggler');\n if (blackList) return;\n\n this.toggle();\n };\n\n handleTouchStart = e => {\n this.setState({ touchStartY: e.touches[0].clientY });\n };\n\n handleTouchMove = e => {\n const { open, touchStartY } = this.state;\n const touchEndY = e.changedTouches[0].clientY;\n const touchUp = touchEndY < touchStartY;\n const touchDown = touchEndY > touchStartY;\n\n if (touchDown && open) this.toggle();\n else if (touchUp && !open) this.toggle();\n };\n\n render() {\n const { playerStatus } = this.props;\n let classes =\n 'drawer fixed-bottom bg-secondary border-top border-secondary';\n\n if (this.state.open) {\n classes += ' drawer--open';\n document.body.classList.add('body--drawer');\n } else document.body.classList.remove('body--drawer');\n\n return (\n <div\n className={classes}\n onClick={this.handleClick}\n onTouchStart={this.handleTouchStart}\n onTouchMove={this.handleTouchMove}\n >\n {this.state.open ? (\n <React.Fragment>\n <FontAwesomeIcon\n icon={faChevronDown}\n className=\"drawer__toggler fa-lg mb-3\"\n />\n <Player playerStatus={playerStatus} />{' '}\n </React.Fragment>\n ) : (\n <MiniPlayer playerStatus={playerStatus} />\n )}\n </div>\n );\n }\n}\n\nexport default Drawer;\n","import React from 'react';\n\nconst Modal = ({ id, title, footer, children }) => {\n return (\n <div\n className=\"modal fade\"\n id={id}\n tabIndex=\"-1\"\n role=\"dialog\"\n aria-labelledby=\"staticBackdropLabel\"\n aria-hidden=\"true\"\n >\n <div className=\"modal-dialog\" role=\"document\">\n <div className=\"modal-content\">\n <div className=\"modal-header\">\n <h5 className=\"modal-title\" id=\"staticBackdropLabel\">\n {title}\n </h5>\n <button\n type=\"button\"\n className=\"close\"\n data-dismiss=\"modal\"\n aria-label=\"Close\"\n >\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div className=\"modal-body\">{children}</div>\n <div className=\"modal-footer\">{footer}</div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default Modal;\n","const DEFAULT_THEME = 'darkly';\n\nconst STORAGE_KEY = 'theme';\n\nconst getThemePath = themeId =>\n `https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/${themeId}/bootstrap.min.css`;\n\nexport const themes = [\n { id: 'darkly', name: 'Darkly', themeColor: '#375a7f' },\n { id: 'lux', name: 'Lux', themeColor: '#1a1a1a' },\n { id: 'litera', name: 'Litera', themeColor: '#4582ec' }\n];\n\nexport function changeTheme(themeId) {\n localStorage.setItem(STORAGE_KEY, themeId);\n applyTheme();\n}\n\nexport function applyTheme() {\n const themeId = getThemeId();\n const themePath = getThemePath(themeId);\n const themeColor = themes.find(t => t.id === themeId).themeColor;\n\n const linkEl = document.querySelector('link[title=\"theme\"]');\n linkEl.setAttribute('href', themePath);\n\n const metaEl = document.querySelector('meta[name=\"theme-color\"]');\n metaEl.setAttribute('content', themeColor);\n}\n\nexport function getThemeId() {\n const localId = localStorage.getItem(STORAGE_KEY);\n\n if (localId === '') return DEFAULT_THEME;\n if (themes.filter(t => t.id === localId).length === 0) {\n localStorage.removeItem(STORAGE_KEY);\n return DEFAULT_THEME;\n }\n\n return localId;\n}\n","import React from 'react';\n\nconst Select = ({ id, label, row, data, ...rest }) => (\n <div className={row ? 'form-group row' : 'form-group'}>\n <label htmlFor={id} className=\"col-sm-2 col-form-label\">\n {label}\n </label>\n <div className=\"col-sm-10\">\n <select id={id} className=\"form-control\" {...rest}>\n {data.map(d => (\n <option key={d.id || d} value={d.id || d}>\n {d.name || d}\n </option>\n ))}\n </select>\n </div>\n </div>\n);\n\nexport default Select;\n","import React from 'react';\nimport Modal from './common/modal';\nimport { getThemeId, changeTheme, themes } from '../theme';\nimport i18next from 'i18next';\nimport { withTranslation } from 'react-i18next';\nimport { languages } from '../config.json';\nimport Select from './common/select';\n\nconst Settings = ({ t }) => {\n const renderFooter = () => (\n <button className=\"btn btn-secondary\" data-dismiss=\"modal\">\n {t('close')}\n </button>\n );\n\n return (\n <Modal id=\"settings\" title={t('settings')} footer={renderFooter()}>\n <Select\n id=\"theme-select\"\n label={t('theme')}\n data={themes}\n row\n value={getThemeId()}\n onChange={e => {\n changeTheme(e.target.value);\n }}\n />\n <Select\n id=\"language-select\"\n label={t('language')}\n data={languages}\n row\n value={i18next.language}\n onChange={e => {\n i18next.changeLanguage(e.target.value);\n }}\n />\n </Modal>\n );\n};\n\nexport default withTranslation()(Settings);\n","import React, { useState } from 'react';\nimport Modal from './common/modal';\nimport { toast } from 'react-toastify';\nimport { playURL } from '../services/playerService';\nimport { withTranslation } from 'react-i18next';\n\nconst URLDialog = ({ t }) => {\n const [url, setURL] = useState('');\n\n const handleOK = () => {\n if (url === '') return;\n doPlayURL(url);\n setURL('');\n };\n\n const doPlayURL = async url => {\n const errorToastOpts = {\n type: toast.TYPE.ERROR,\n render: t('errorNotFound')\n };\n\n const toastId = toast(t('tryingURL'));\n\n try {\n await playURL(url);\n } catch (ex) {\n if (ex.response && ex.response.status === 400)\n toast.update(toastId, errorToastOpts);\n }\n };\n\n const handleChange = ({ target: input }) => {\n setURL(input.value);\n };\n\n const handleKeyDown = e => {\n if (e.key === 'Enter') handleOK();\n };\n\n const renderFooter = () => (\n <React.Fragment>\n <button\n className=\"btn btn-primary\"\n data-dismiss=\"modal\"\n onClick={handleOK}\n >\n OK\n </button>\n <button\n className=\"btn btn-secondary\"\n onClick={() => setURL('')}\n data-dismiss=\"modal\"\n >\n Cancel\n </button>\n </React.Fragment>\n );\n\n return (\n <Modal id=\"url-dialog\" title={t('playURL')} footer={renderFooter()}>\n <input\n className=\"form-control mb-4\"\n type=\"text\"\n placeholder=\"URL\"\n value={url}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n />\n </Modal>\n );\n};\n\nexport default withTranslation()(URLDialog);\n","import React, { useState, useEffect } from 'react';\nimport './backdrop.scss';\n\nconst Backdrop = ({ title, body }) => {\n const [bodyStyled, setBodyStyled] = useState(false);\n\n const getScrollbarWidth = () =>\n window.innerWidth - document.documentElement.clientWidth;\n\n const doBodyStyle = () => {\n if (title) {\n // Calculate this BEFORE adding the class.\n document.body.style.paddingRight = getScrollbarWidth() + 'px';\n document.body.classList.add('body--backdrop');\n setBodyStyled(true);\n // We need this check because Bootstrap modals also style paddingRight\n } else if (!title && bodyStyled) {\n document.body.style.paddingRight = 0;\n document.body.classList.remove('body--backdrop');\n setBodyStyled(false);\n }\n };\n\n useEffect(doBodyStyle, [title, body]);\n\n const classes =\n 'backdrop p-2 text-white' + (title ? ' backdrop--visible' : '');\n\n // We need text-white on h* tags because some themes override it\n return (\n <div className={classes}>\n <h3 className=\"text-white\">{title}</h3>\n <h5 className=\"text-white\">{body}</h5>\n </div>\n );\n};\n\nexport default Backdrop;\n","import React, { Component } from 'react';\nimport NavBar from './components/navBar';\nimport Player from './components/player';\nimport Streams from './components/streams';\nimport Drawer from './components/drawer';\nimport Settings from './components/settings';\nimport URLDialog from './components/urlDialog';\nimport Loader from './components/loader';\nimport Backdrop from './components/backdrop';\nimport { ToastContainer } from 'react-toastify';\nimport { getStatus } from './services/playerService';\nimport { withTranslation } from 'react-i18next';\nimport { updateInterval, backdropTimeout } from './config.json';\nimport './App.scss';\nimport 'react-toastify/dist/ReactToastify.css';\n\nclass App extends Component {\n state = {\n playerStatus: {},\n loading: true,\n networkError: false,\n backdrop: {}\n };\n\n componentDidMount() {\n this.updatePlayerStatus();\n }\n\n async updatePlayerStatus() {\n try {\n const { data: playerStatus } = await getStatus();\n this.setState({ playerStatus, loading: false, networkError: false });\n } catch (ex) {\n this.setState({ loading: false, networkError: true });\n }\n\n setTimeout(() => this.updatePlayerStatus(), updateInterval);\n }\n\n handleBackdrop = (title, body = '') => {\n this.setState({ backdrop: { title, body } });\n setTimeout(() => this.setState({ backdrop: {} }), backdropTimeout);\n };\n\n render() {\n const { loading, networkError, backdrop, playerStatus } = this.state;\n const { t } = this.props;\n\n if (networkError) return <Backdrop title={t('errorNetwork')} />;\n if (loading) return <Loader />;\n if (!playerStatus.con_mpd) return <Backdrop title={t('disconnectedMPD')} />;\n\n return (\n <div className=\"app\">\n <ToastContainer />\n <Backdrop title={backdrop.title} body={backdrop.body} />\n <NavBar />\n <Drawer playerStatus={playerStatus} />\n\n <main className=\"app-main p-4\">\n <Player playerStatus={playerStatus} />\n <Streams\n onBackdrop={this.handleBackdrop}\n playerStatus={playerStatus}\n />\n <Settings />\n <URLDialog />\n </main>\n </div>\n );\n }\n}\n\nexport default withTranslation()(App);\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' }\n })\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import i18n from 'i18next';\nimport { initReactI18next } from 'react-i18next';\n\nimport Backend from 'i18next-xhr-backend';\nimport LanguageDetector from 'i18next-browser-languagedetector';\n// not like to use this?\n// have a look at the Quick start guide\n// for passing in lng and translations on init\n\ni18n\n // load translation using xhr -> see /public/locales\n // learn more: https://github.com/i18next/i18next-xhr-backend\n .use(Backend)\n // detect user language\n // learn more: https://github.com/i18next/i18next-browser-languageDetector\n .use(LanguageDetector)\n // pass the i18n instance to react-i18next.\n .use(initReactI18next)\n // init i18next\n // for all options read: https://www.i18next.com/overview/configuration-options\n .init({\n fallbackLng: 'en',\n debug: process.env.REACT_APP_I18N_DEBUG,\n\n interpolation: {\n escapeValue: false // not needed for react as it escapes by default\n }\n });\n\nexport default i18n;\n","import React, { Suspense } from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\nimport Loader from './components/loader';\nimport './i18n';\nimport { applyTheme } from './theme';\nimport './index.scss';\n\n// Bootstrap\n//import 'bootstrap/dist/css/bootstrap.css';\n//import 'bootswatch/dist/flatly/bootstrap.min.css'; // This replaces default Bootstrap file\nimport 'jquery/dist/jquery.min.js';\nimport 'bootstrap/dist/js/bootstrap.js';\n\napplyTheme();\n\nReactDOM.render(\n // Suspense needed for i18n\n <Suspense fallback={<Loader />}>\n <App />\n </Suspense>,\n document.getElementById('root')\n);\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"],"sourceRoot":""}
data/lib/pifi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module PiFi
2
- VERSION = "0.3.0".freeze
2
+ VERSION = "0.3.1".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pifi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafael Cavalcanti
@@ -122,7 +122,7 @@ files:
122
122
  - lib/pifi/public/mstile-310x150.png
123
123
  - lib/pifi/public/mstile-310x310.png
124
124
  - lib/pifi/public/mstile-70x70.png
125
- - lib/pifi/public/precache-manifest.c6c8e4145c1ed2fcfcefd9d025331f85.js
125
+ - lib/pifi/public/precache-manifest.2b9510afeb80c583686b4fe78607222c.js
126
126
  - lib/pifi/public/robots.txt
127
127
  - lib/pifi/public/safari-pinned-tab.svg
128
128
  - lib/pifi/public/service-worker.js
@@ -133,8 +133,8 @@ files:
133
133
  - lib/pifi/public/static/js/2.2b057ab5.chunk.js
134
134
  - lib/pifi/public/static/js/2.2b057ab5.chunk.js.LICENSE
135
135
  - lib/pifi/public/static/js/2.2b057ab5.chunk.js.map
136
- - lib/pifi/public/static/js/main.54482466.chunk.js
137
- - lib/pifi/public/static/js/main.54482466.chunk.js.map
136
+ - lib/pifi/public/static/js/main.52997fad.chunk.js
137
+ - lib/pifi/public/static/js/main.52997fad.chunk.js.map
138
138
  - lib/pifi/public/static/js/runtime-main.f04a0f25.js
139
139
  - lib/pifi/public/static/js/runtime-main.f04a0f25.js.map
140
140
  - lib/pifi/public/static/media/logo.91554ce9.svg
@@ -1,2 +0,0 @@
1
- (this.webpackJsonppifi_frontend=this.webpackJsonppifi_frontend||[]).push([[0],{18:function(e){e.exports=JSON.parse('{"b":["en","fr-FR","nl-NL","pt-BR"],"c":1000,"a":2500,"d":3000}')},42:function(e,t,a){e.exports=a.p+"static/media/logo.91554ce9.svg"},46:function(e,t,a){e.exports=a(95)},60:function(e,t,a){},81:function(e,t,a){},82:function(e,t,a){},83:function(e,t,a){},84:function(e,t,a){},85:function(e,t,a){},86:function(e,t,a){},87:function(e,t,a){},88:function(e,t,a){},90:function(e,t,a){},95:function(e,t,a){"use strict";a.r(t);var n=a(0),r=a.n(n),l=a(20),c=a.n(l),o=a(6),s=a.n(o),i=a(3),u=a(4),m=a(8),d=a(10),p=a(11),b=a(2),v=(a(60),a(42)),f=a.n(v),h=Object(b.c)()((function(e){var t=e.t;return r.a.createElement("nav",{className:"navbar navbar-expand-sm navbar-dark bg-primary "},r.a.createElement("a",{className:"navbar-brand",href:"/"},r.a.createElement("img",{src:f.a,width:"30",height:"30",className:"d-inline-block align-text-bottom mr-2",alt:""}),"PiFi Radio"),r.a.createElement("button",{className:"navbar-toggler",type:"button","data-toggle":"collapse","data-target":"#navbarNavAltMarkup","aria-controls":"navbarNavAltMarkup","aria-expanded":"false","aria-label":"Toggle navigation"},r.a.createElement("span",{className:"navbar-toggler-icon"})),r.a.createElement("div",{className:"collapse navbar-collapse",id:"navbarNavAltMarkup"},r.a.createElement("div",{className:"navbar-nav pt-1 ml-2"},r.a.createElement("button",{className:"btn btn-link nav-link","data-toggle":"modal","data-target":"#url-dialog"},t("playURL")),r.a.createElement("button",{className:"btn btn-link nav-link","data-toggle":"modal","data-target":"#settings"},t("settings")))))})),g=Object(b.c)()((function(e){var t=e.playerStatus,a=e.t,n=t.playing,l=t.title;return r.a.createElement("div",{className:"text-center"},r.a.createElement("h5",{className:"small text-uppercase"},a(n?"playing":"stopped")),r.a.createElement("h3",{className:"ellipsis"},l))})),y=a(15),E=a(14),k=a(27),N=a.n(k),w=a(5);N.a.interceptors.response.use(null,(function(e){var t=e.response&&e.response.status>=400&&e.response.status<500,a=!e.response;return t||a||w.b.error(r.a.createElement(b.a,null,(function(e,t){t.i18n;return r.a.createElement("p",null,e("errorUnexpected"))}))),e.response&&403===e.response.status&&w.b.error(r.a.createElement(b.a,null,(function(e,t){t.i18n;return r.a.createElement("p",null,e("errorForbidden"))}))),Promise.reject(e)}));var S={get:N.a.get,post:N.a.post},O="".concat("/api","/player");function j(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,a=new FormData;return a.set("method",e),t&&a.set("params",t),a}function C(){return S.post(O,j("play"))}function x(){return S.post(O,j("stop"))}function _(e){return S.post(O,j("change_vol",e))}function L(e){return S.post(O,j("play_radios",e))}function P(e){return S.post(O,j("play_urls",e))}var R=function(e){return e.playing?r.a.createElement("button",{className:"btn btn-danger",onClick:x},r.a.createElement(y.a,{icon:E.d})):r.a.createElement("button",{className:"btn btn-dark",onClick:C},r.a.createElement(y.a,{icon:E.c}))},T=a(18),A=(a(81),Object(b.c)()((function(e){var t=e.playerStatus,a=e.t,n=t.vol<0,l=function(e,t){return r.a.createElement("button",{className:"btn btn-dark p-3",disabled:n,onClick:function(){return function(e){var t,n,r,l;return s.a.async((function(c){for(;;)switch(c.prev=c.next){case 0:return c.next=2,s.a.awrap(_(e));case 2:t=c.sent,n=t.data,"vol",r="".concat(a("volume"),": ").concat(n,"%"),l={toastId:"vol",autoClose:T.d},w.b.isActive("vol")?w.b.update("vol",{render:r}):w.b.info(r,l);case 8:case"end":return c.stop()}}))}(e)}},r.a.createElement(y.a,{icon:t}))};return r.a.createElement("div",{className:"player-controls btn-group w-100"},l("-5",E.e),l("+5",E.f),r.a.createElement(R,{playing:t.playing}))}))),I=(a(82),function(){var e=document.querySelector("nav").clientHeight;document.querySelector(".player").style.top=e+24+"px"}),B=function(e){var t=e.playerStatus;return Object(n.useEffect)(I,[t]),r.a.createElement("div",{className:"player p-2"},r.a.createElement(g,{playerStatus:t}),r.a.createElement(A,{playerStatus:t}))},F=Object(b.c)()((function(e){var t=e.value,a=e.onChange,n=e.t;return r.a.createElement("input",{className:"form-control mb-4",type:"text",id:"query",placeholder:n("search"),autoComplete:"off",value:t,onChange:function(e){return a(e.target.value)},onFocus:function(){return a("")}})})),U=(a(83),function(){return r.a.createElement("div",{className:"loader"},r.a.createElement("div",{className:"spinner-border",role:"status"},r.a.createElement("span",{className:"sr-only"},"Loading...")))}),q="".concat("/api","/streams");a(84);var M=function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={streams:{},loading:!0,query:""},a.isPlaying=function(e){return e===a.props.playerStatus.title&&a.props.playerStatus.playing},a.handleItemClick=function(e){var t,n,r;return s.a.async((function(l){for(;;)switch(l.prev=l.next){case 0:if(t=a.props,n=t.t,r=t.onBackdrop,!a.isPlaying(e)){l.next=3;break}return l.abrupt("return");case 3:return r(n("tunning"),e),l.prev=4,l.next=7,s.a.awrap(L(e));case 7:l.next=12;break;case 9:l.prev=9,l.t0=l.catch(4),l.t0.response&&400===l.t0.response.status&&w.b.error(n("errorNotFound"));case 12:case"end":return l.stop()}}),null,null,[[4,9]])},a.getItemClasses=function(e){var t="streams__item ellipsis list-group-item ";return a.isPlaying(e)?t+"active":t+"list-group-item-action"},a.handleSearch=function(e){a.setState({query:e})},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"componentDidMount",value:function(){var e,t;return s.a.async((function(a){for(;;)switch(a.prev=a.next){case 0:return a.next=2,s.a.awrap(S.get(q));case 2:e=a.sent,t=e.data,this.setState({streams:t,loading:!1});case 5:case"end":return a.stop()}}),null,this)}},{key:"filteredStreams",value:function(){var e=this.state,t=e.streams,a=e.query;if(""===a)return t;var n={};for(var r in t)r.toLowerCase().includes(a.toLowerCase())&&""!==t[r]&&(n[r]=t[r]);return n}},{key:"renderList",value:function(){var e=this,t=this.filteredStreams();return 0===Object.keys(t).length?r.a.createElement("h4",{className:"p-4"},this.props.t("noStreams")):r.a.createElement("ul",{className:"list-group list-group-flush"},Object.keys(t).map((function(a){return t[a]?r.a.createElement("li",{className:"streams__header ellipsis",key:a},a):r.a.createElement("li",{className:e.getItemClasses(a),key:a,onClick:function(){return e.handleItemClick(a)}},a)})))}},{key:"render",value:function(){return this.state.loading?r.a.createElement(U,null):r.a.createElement("div",{className:"streams p-2"},r.a.createElement(F,{value:this.state.query,onChange:this.handleSearch}),this.renderList())}}]),t}(n.Component),D=Object(b.c)()(M),Y=(a(85),function(e){var t=e.playerStatus,a=t.title,n=t.playing;return r.a.createElement("div",{className:"mini-player p-3"},r.a.createElement("div",{className:"mini-player__left ellipsis pr-3"},r.a.createElement(y.a,{icon:E.b,className:"drawer__toggler mr-3"}),r.a.createElement("span",null,a)),r.a.createElement(R,{playing:n}))}),W=(a(86),function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={open:!1},a.toggle=function(){a.setState({open:!a.state.open})},a.handleClick=function(e){var t=e.target;("BUTTON"===t.tagName||"I"===t.tagName)&&!t.className.includes("drawer__toggler")||a.toggle()},a.handleTouchStart=function(e){a.setState({touchStartY:e.touches[0].clientY})},a.handleTouchMove=function(e){var t=a.state,n=t.open,r=t.touchStartY,l=e.changedTouches[0].clientY,c=l<r;l>r&&n?a.toggle():c&&!n&&a.toggle()},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"render",value:function(){var e=this.props.playerStatus,t="drawer fixed-bottom bg-secondary border-top border-secondary";return this.state.open?(t+=" drawer--open",document.body.classList.add("body--drawer")):document.body.classList.remove("body--drawer"),r.a.createElement("div",{className:t,onClick:this.handleClick,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove},this.state.open?r.a.createElement(r.a.Fragment,null,r.a.createElement(y.a,{icon:E.a,className:"drawer__toggler fa-lg mb-3"}),r.a.createElement(B,{playerStatus:e})," "):r.a.createElement(Y,{playerStatus:e}))}}]),t}(n.Component)),J=function(e){var t=e.id,a=e.title,n=e.footer,l=e.children;return r.a.createElement("div",{className:"modal fade",id:t,tabIndex:"-1",role:"dialog","aria-labelledby":"staticBackdropLabel","aria-hidden":"true"},r.a.createElement("div",{className:"modal-dialog",role:"document"},r.a.createElement("div",{className:"modal-content"},r.a.createElement("div",{className:"modal-header"},r.a.createElement("h5",{className:"modal-title",id:"staticBackdropLabel"},a),r.a.createElement("button",{type:"button",className:"close","data-dismiss":"modal","aria-label":"Close"},r.a.createElement("span",{"aria-hidden":"true"},"\xd7"))),r.a.createElement("div",{className:"modal-body"},l),r.a.createElement("div",{className:"modal-footer"},n))))},K="darkly",V="theme",G=function(e){return"https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/".concat(e,"/bootstrap.min.css")},H=[{id:"darkly",name:"Darkly",themeColor:"#375a7f"},{id:"lux",name:"Lux",themeColor:"#1a1a1a"},{id:"litera",name:"Litera",themeColor:"#4582ec"}];function $(){var e=z(),t=G(e),a=H.find((function(t){return t.id===e})).themeColor;document.querySelector('link[title="theme"]').setAttribute("href",t),document.querySelector('meta[name="theme-color"]').setAttribute("content",a)}function z(){var e=localStorage.getItem(V);return""===e?K:0===H.filter((function(t){return t.id===e})).length?(localStorage.removeItem(V),K):e}var Q=a(22),X=a(43),Z=function(e){var t=e.id,a=e.label,n=e.row,l=e.data,c=Object(X.a)(e,["id","label","row","data"]);return r.a.createElement("div",{className:n?"form-group row":"form-group"},r.a.createElement("label",{htmlFor:t,className:"col-sm-2 col-form-label"},a),r.a.createElement("div",{className:"col-sm-10"},r.a.createElement("select",Object.assign({id:t,className:"form-control"},c),l.map((function(e){return r.a.createElement("option",{key:e.id||e,value:e.id||e},e.name||e)})))))},ee=Object(b.c)()((function(e){var t=e.t;return r.a.createElement(J,{id:"settings",title:t("settings"),footer:r.a.createElement("button",{className:"btn btn-secondary","data-dismiss":"modal"},t("close"))},r.a.createElement(Z,{id:"theme-select",label:t("theme"),data:H,row:!0,value:z(),onChange:function(e){var t;t=e.target.value,localStorage.setItem(V,t),$()}}),r.a.createElement(Z,{id:"language-select",label:t("language"),data:T.b,row:!0,value:Q.a.language,onChange:function(e){Q.a.changeLanguage(e.target.value)}}))})),te=a(19),ae=Object(b.c)()((function(e){var t=e.t,a=Object(n.useState)(""),l=Object(te.a)(a,2),c=l[0],o=l[1],i=function(){""!==c&&(u(c),o(""))},u=function(e){var a,n;return s.a.async((function(r){for(;;)switch(r.prev=r.next){case 0:return a={type:w.b.TYPE.ERROR,render:t("errorNotFound")},n=Object(w.b)(t("tryingURL")),r.prev=2,r.next=5,s.a.awrap(P(e));case 5:r.next=10;break;case 7:r.prev=7,r.t0=r.catch(2),r.t0.response&&400===r.t0.response.status&&w.b.update(n,a);case 10:case"end":return r.stop()}}),null,null,[[2,7]])};return r.a.createElement(J,{id:"url-dialog",title:t("playURL"),footer:r.a.createElement(r.a.Fragment,null,r.a.createElement("button",{className:"btn btn-primary","data-dismiss":"modal",onClick:i},"OK"),r.a.createElement("button",{className:"btn btn-secondary",onClick:function(){return o("")},"data-dismiss":"modal"},"Cancel"))},r.a.createElement("input",{className:"form-control mb-4",type:"text",placeholder:"URL",value:c,onChange:function(e){var t=e.target;o(t.value)},onKeyDown:function(e){"Enter"===e.key&&i()}}))})),ne=(a(87),function(e){var t=e.title,a=e.body,l=Object(n.useState)(!1),c=Object(te.a)(l,2),o=c[0],s=c[1];Object(n.useEffect)((function(){t?(document.body.style.paddingRight=window.innerWidth-document.documentElement.clientWidth+"px",document.body.classList.add("body--backdrop"),s(!0)):!t&&o&&(document.body.style.paddingRight=0,document.body.classList.remove("body--backdrop"),s(!1))}),[t,a]);var i="backdrop p-2 text-white"+(t?" backdrop--visible":"");return r.a.createElement("div",{className:i},r.a.createElement("h3",{className:"text-white"},t),r.a.createElement("h5",{className:"text-white"},a))}),re=(a(88),a(89),function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={playerStatus:{},loading:!0,networkError:!1,backdrop:{}},a.handleBackdrop=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";a.setState({backdrop:{title:e,body:t}}),setTimeout((function(){return a.setState({backdrop:{}})}),T.a)},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"componentDidMount",value:function(){this.updatePlayerStatus()}},{key:"updatePlayerStatus",value:function(){var e,t,a=this;return s.a.async((function(n){for(;;)switch(n.prev=n.next){case 0:return n.prev=0,n.next=3,s.a.awrap(S.get(O));case 3:e=n.sent,t=e.data,this.setState({playerStatus:t,loading:!1,networkError:!1}),n.next=11;break;case 8:n.prev=8,n.t0=n.catch(0),this.setState({loading:!1,networkError:!0});case 11:setTimeout((function(){return a.updatePlayerStatus()}),T.c);case 12:case"end":return n.stop()}}),null,this,[[0,8]])}},{key:"render",value:function(){var e=this.state,t=e.loading,a=e.networkError,n=e.backdrop,l=e.playerStatus,c=this.props.t;return a?r.a.createElement(ne,{title:c("errorNetwork")}):t?r.a.createElement(U,null):l.con_mpd?r.a.createElement("div",{className:"app"},r.a.createElement(w.a,null),r.a.createElement(ne,{title:n.title,body:n.body}),r.a.createElement(h,null),r.a.createElement(W,{playerStatus:l}),r.a.createElement("main",{className:"app-main p-4"},r.a.createElement(B,{playerStatus:l}),r.a.createElement(D,{onBackdrop:this.handleBackdrop,playerStatus:l}),r.a.createElement(ee,null),r.a.createElement(ae,null))):r.a.createElement(ne,{title:c("disconnectedMPD")})}}]),t}(n.Component)),le=Object(b.c)()(re);Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));var ce=a(44),oe=a(45);Q.a.use(ce.a).use(oe.a).use(b.b).init({fallbackLng:"en",debug:Object({NODE_ENV:"production",PUBLIC_URL:"",REACT_APP_API_URL:"/api"}).REACT_APP_I18N_DEBUG,interpolation:{escapeValue:!1}});Q.a,a(90),a(91),a(92);$(),c.a.render(r.a.createElement(n.Suspense,{fallback:r.a.createElement(U,null)},r.a.createElement(le,null)),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then((function(e){e.unregister()}))}},[[46,1,2]]]);
2
- //# sourceMappingURL=main.54482466.chunk.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["logo.svg","components/navBar.jsx","components/playerStatus.jsx","services/httpService.js","services/playerService.js","components/playStopControl.jsx","components/playerControls.jsx","components/player.jsx","components/common/searchBox.jsx","components/loader.jsx","services/streamsService.js","components/streams.jsx","components/miniPlayer.jsx","components/drawer.jsx","components/common/modal.jsx","theme.js","components/common/select.jsx","components/settings.jsx","components/urlDialog.jsx","components/backdrop.jsx","App.js","serviceWorker.js","i18n.js","index.js"],"names":["module","exports","withTranslation","t","className","href","src","logo","width","height","alt","type","data-toggle","data-target","aria-controls","aria-expanded","aria-label","id","playerStatus","playing","title","axios","interceptors","response","use","error","expectedError","status","networkError","toast","i18n","Promise","reject","get","post","apiEndpoint","process","body","method","params","FormData","set","play","http","stop","changeVol","delta","playRadio","name","playURL","url","PlayStopControl","onClick","icon","faStop","faPlay","volDisabled","vol","renderVolButton","disabled","a","async","data","toastMsg","toastOpts","toastId","autoClose","volTimeout","isActive","update","render","info","handleVolChange","faVolumeDown","faVolumeUp","positionPlayerTop","navHeight","document","querySelector","clientHeight","style","top","Player","useEffect","value","onChange","placeholder","autoComplete","e","target","onFocus","Loader","role","Streams","state","streams","loading","query","isPlaying","props","handleItemClick","onBackdrop","getItemClasses","classes","handleSearch","setState","this","filtered","k","toLowerCase","includes","filteredStreams","Object","keys","length","map","key","renderList","Component","MiniPlayer","faChevronUp","Drawer","open","toggle","handleClick","tagName","handleTouchStart","touchStartY","touches","clientY","handleTouchMove","touchEndY","changedTouches","touchUp","classList","add","remove","onTouchStart","onTouchMove","Fragment","faChevronDown","Modal","footer","children","tabIndex","aria-labelledby","aria-hidden","data-dismiss","DEFAULT_THEME","STORAGE_KEY","getThemePath","themeId","themes","themeColor","applyTheme","getThemeId","themePath","find","setAttribute","localId","localStorage","getItem","filter","removeItem","Select","label","row","rest","htmlFor","d","setItem","languages","i18next","language","changeLanguage","useState","setURL","handleOK","doPlayURL","errorToastOpts","TYPE","ERROR","input","onKeyDown","Backdrop","bodyStyled","setBodyStyled","paddingRight","window","innerWidth","documentElement","clientWidth","App","backdrop","handleBackdrop","setTimeout","backdropTimeout","updatePlayerStatus","updateInterval","con_mpd","Boolean","location","hostname","match","Backend","LanguageDetector","initReactI18next","init","fallbackLng","debug","REACT_APP_I18N_DEBUG","interpolation","escapeValue","ReactDOM","fallback","getElementById","navigator","serviceWorker","ready","then","registration","unregister"],"mappings":"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","file":"static/js/main.54482466.chunk.js","sourcesContent":["module.exports = __webpack_public_path__ + \"static/media/logo.91554ce9.svg\";","import React from 'react';\nimport { withTranslation } from 'react-i18next';\nimport './navBar.scss';\nimport logo from '../logo.svg';\n\nconst NavBar = ({ t }) => (\n <nav className=\"navbar navbar-expand-sm navbar-dark bg-primary \">\n <a className=\"navbar-brand\" href=\"/\">\n <img\n src={logo}\n width=\"30\"\n height=\"30\"\n className=\"d-inline-block align-text-bottom mr-2\"\n alt=\"\"\n />\n PiFi Radio\n </a>\n <button\n className=\"navbar-toggler\"\n type=\"button\"\n data-toggle=\"collapse\"\n data-target=\"#navbarNavAltMarkup\"\n aria-controls=\"navbarNavAltMarkup\"\n aria-expanded=\"false\"\n aria-label=\"Toggle navigation\"\n >\n <span className=\"navbar-toggler-icon\"></span>\n </button>\n <div className=\"collapse navbar-collapse\" id=\"navbarNavAltMarkup\">\n <div className=\"navbar-nav pt-1 ml-2\">\n {/* We use buttons and different class names instead of Bootstrap's\n anchor, so we don't get a warning for href=\"#\". */}\n <button\n className=\"btn btn-link nav-link\"\n data-toggle=\"modal\"\n data-target=\"#url-dialog\"\n >\n {t('playURL')}\n </button>\n\n <button\n className=\"btn btn-link nav-link\"\n data-toggle=\"modal\"\n data-target=\"#settings\"\n >\n {t('settings')}\n </button>\n </div>\n </div>\n </nav>\n);\nexport default withTranslation()(NavBar);\n","import React from 'react';\nimport { withTranslation } from 'react-i18next';\n\nconst PlayerStatus = ({ playerStatus, t }) => {\n const { playing, title } = playerStatus;\n\n return (\n <div className=\"text-center\">\n <h5 className=\"small text-uppercase\">\n {playing ? t('playing') : t('stopped')}\n </h5>\n <h3 className=\"ellipsis\">{title}</h3>\n </div>\n );\n};\n\nexport default withTranslation()(PlayerStatus);\n","import React from 'react';\nimport axios from 'axios';\nimport { toast } from 'react-toastify';\nimport { Translation } from 'react-i18next';\n\naxios.interceptors.response.use(null, error => {\n const expectedError =\n error.response &&\n error.response.status >= 400 &&\n error.response.status < 500;\n\n // We already alert about these with a backdrop on App.js\n const networkError = !error.response;\n\n // Ugly, but i18n only worked this way\n if (!expectedError && !networkError) {\n toast.error(\n <Translation>\n {(t, { i18n }) => <p>{t('errorUnexpected')}</p>}\n </Translation>\n );\n }\n // This expected error is universal in our app, so we'll place it here\n if (error.response && error.response.status === 403)\n toast.error(\n <Translation>{(t, { i18n }) => <p>{t('errorForbidden')}</p>}</Translation>\n );\n\n return Promise.reject(error);\n});\n\nexport default {\n get: axios.get,\n post: axios.post\n};\n","import http from './httpService.js';\n\nconst apiEndpoint = `${process.env.REACT_APP_API_URL}/player`;\n\nfunction body(method, params = null) {\n const body = new FormData();\n body.set('method', method);\n if (params) body.set('params', params);\n return body;\n}\n\nexport function getStatus() {\n return http.get(apiEndpoint);\n}\n\nexport function play() {\n return http.post(apiEndpoint, body('play'));\n}\n\nexport function stop() {\n return http.post(apiEndpoint, body('stop'));\n}\n\nexport function changeVol(delta) {\n return http.post(apiEndpoint, body('change_vol', delta));\n}\n\nexport function playRadio(name) {\n return http.post(apiEndpoint, body('play_radios', name));\n}\n\nexport function playURL(url) {\n return http.post(apiEndpoint, body('play_urls', url));\n}\n","import React from 'react';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faStop, faPlay } from '@fortawesome/free-solid-svg-icons';\nimport { play, stop } from '../services/playerService';\n\nconst PlayStopControl = ({ playing }) =>\n playing ? (\n <button className=\"btn btn-danger\" onClick={stop}>\n <FontAwesomeIcon icon={faStop} />\n </button>\n ) : (\n <button className=\"btn btn-dark\" onClick={play}>\n <FontAwesomeIcon icon={faPlay} />\n </button>\n );\n\nexport default PlayStopControl;\n","import React from 'react';\nimport PlayStopControl from './playStopControl';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faVolumeDown, faVolumeUp } from '@fortawesome/free-solid-svg-icons';\nimport { toast } from 'react-toastify';\nimport { withTranslation } from 'react-i18next';\nimport { changeVol } from '../services/playerService';\nimport { volTimeout } from '../config.json';\nimport './playerControls.scss';\n\nconst PlayerControls = ({ playerStatus, t }) => {\n const volDisabled = playerStatus.vol < 0;\n\n const handleVolChange = async delta => {\n const { data: vol } = await changeVol(delta);\n\n const toastId = 'vol';\n const toastMsg = `${t('volume')}: ${vol}%`;\n const toastOpts = { toastId, autoClose: volTimeout };\n if (toast.isActive(toastId)) toast.update(toastId, { render: toastMsg });\n else toast.info(toastMsg, toastOpts);\n };\n\n const renderVolButton = (delta, icon) => (\n <button\n className=\"btn btn-dark p-3\"\n disabled={volDisabled}\n onClick={() => handleVolChange(delta)}\n >\n <FontAwesomeIcon icon={icon} />\n </button>\n );\n\n return (\n <div className=\"player-controls btn-group w-100\">\n {renderVolButton('-5', faVolumeDown)}\n {renderVolButton('+5', faVolumeUp)}\n <PlayStopControl playing={playerStatus.playing} />\n </div>\n );\n};\n\nexport default withTranslation()(PlayerControls);\n","import React, { useEffect } from 'react';\nimport PlayerStatus from './playerStatus';\nimport PlayerControls from './playerControls';\nimport './player.scss';\n\nconst Player = ({ playerStatus }) => {\n useEffect(positionPlayerTop, [playerStatus]);\n\n return (\n <div className=\"player p-2\">\n <PlayerStatus playerStatus={playerStatus} />\n <PlayerControls playerStatus={playerStatus} />\n </div>\n );\n};\n\n// Calculate and set top for Player\n// Needed for desktop view with sticky\nconst positionPlayerTop = () => {\n const navHeight = document.querySelector('nav').clientHeight;\n const topMargin = 24;\n document.querySelector('.player').style.top = navHeight + topMargin + 'px';\n};\n\nexport default Player;\n","import React from 'react';\nimport { withTranslation } from 'react-i18next';\n\nconst SearchBox = ({ value, onChange, t }) => {\n return (\n <input\n className=\"form-control mb-4\"\n type=\"text\"\n id=\"query\"\n placeholder={t('search')}\n autoComplete=\"off\"\n value={value}\n onChange={e => onChange(e.target.value)}\n onFocus={() => onChange('')}\n ></input>\n );\n};\n\nexport default withTranslation()(SearchBox);\n","import React from 'react';\nimport './loader.scss';\n\nconst Loader = () => (\n <div className=\"loader\">\n <div className=\"spinner-border\" role=\"status\">\n <span className=\"sr-only\">Loading...</span>\n </div>\n </div>\n);\n\nexport default Loader;\n","import http from './httpService';\n\nconst apiEndpoint = `${process.env.REACT_APP_API_URL}/streams`;\n\nexport function getStreams() {\n return http.get(apiEndpoint);\n}\n","import React, { Component } from 'react';\nimport SearchBox from './common/searchBox';\nimport Loader from './loader';\nimport { withTranslation } from 'react-i18next';\nimport { toast } from 'react-toastify';\nimport { playRadio } from '../services/playerService';\nimport { getStreams } from '../services/streamsService';\nimport './streams.scss';\n\nclass Streams extends Component {\n state = { streams: {}, loading: true, query: '' };\n\n async componentDidMount() {\n const { data: streams } = await getStreams();\n this.setState({ streams, loading: false });\n }\n\n isPlaying = name => {\n return (\n name === this.props.playerStatus.title && this.props.playerStatus.playing\n );\n };\n\n handleItemClick = async name => {\n const { t, onBackdrop } = this.props;\n\n if (this.isPlaying(name)) return;\n\n onBackdrop(t('tunning'), name);\n try {\n await playRadio(name);\n } catch (ex) {\n if (ex.response && ex.response.status === 400)\n toast.error(t('errorNotFound'));\n }\n };\n\n getItemClasses = name => {\n const classes = 'streams__item ellipsis list-group-item ';\n return this.isPlaying(name)\n ? classes + 'active'\n : classes + 'list-group-item-action';\n };\n\n handleSearch = query => {\n this.setState({ query });\n };\n\n filteredStreams() {\n const { streams, query } = this.state;\n\n if (query === '') return streams;\n\n let filtered = {};\n for (let k in streams) {\n if (k.toLowerCase().includes(query.toLowerCase()) && streams[k] !== '') {\n filtered[k] = streams[k];\n }\n }\n return filtered;\n }\n\n renderList() {\n const streams = this.filteredStreams();\n\n if (Object.keys(streams).length === 0)\n return <h4 className=\"p-4\">{this.props.t('noStreams')}</h4>;\n\n return (\n <ul className=\"list-group list-group-flush\">\n {Object.keys(streams).map(name =>\n streams[name] ? (\n <li className=\"streams__header ellipsis\" key={name}>\n {name}\n </li>\n ) : (\n <li\n className={this.getItemClasses(name)}\n key={name}\n onClick={() => this.handleItemClick(name)}\n >\n {name}\n </li>\n )\n )}\n </ul>\n );\n }\n\n render() {\n if (this.state.loading) return <Loader />;\n\n return (\n <div className=\"streams p-2\">\n <SearchBox value={this.state.query} onChange={this.handleSearch} />\n {this.renderList()}\n </div>\n );\n }\n}\n\nexport default withTranslation()(Streams);\n","import React from 'react';\nimport PlayStopControl from './playStopControl';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp } from '@fortawesome/free-solid-svg-icons';\nimport './miniPlayer.scss';\n\nconst MiniPlayer = ({ playerStatus }) => {\n const { title, playing } = playerStatus;\n return (\n <div className=\"mini-player p-3\">\n <div className=\"mini-player__left ellipsis pr-3\">\n <FontAwesomeIcon icon={faChevronUp} className=\"drawer__toggler mr-3\" />\n <span>{title}</span>\n </div>\n <PlayStopControl playing={playing} />\n </div>\n );\n};\n\nexport default MiniPlayer;\n","import React, { Component } from 'react';\nimport MiniPlayer from './miniPlayer';\nimport Player from './player';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown } from '@fortawesome/free-solid-svg-icons';\nimport './drawer.scss';\n\nclass Drawer extends Component {\n state = { open: false };\n\n toggle = () => {\n this.setState({ open: !this.state.open });\n };\n\n handleClick = ({ target }) => {\n const blackList =\n (target.tagName === 'BUTTON' || target.tagName === 'I') &&\n !target.className.includes('drawer__toggler');\n if (blackList) return;\n\n this.toggle();\n };\n\n handleTouchStart = e => {\n this.setState({ touchStartY: e.touches[0].clientY });\n };\n\n handleTouchMove = e => {\n const { open, touchStartY } = this.state;\n const touchEndY = e.changedTouches[0].clientY;\n const touchUp = touchEndY < touchStartY;\n const touchDown = touchEndY > touchStartY;\n\n if (touchDown && open) this.toggle();\n else if (touchUp && !open) this.toggle();\n };\n\n render() {\n const { playerStatus } = this.props;\n let classes =\n 'drawer fixed-bottom bg-secondary border-top border-secondary';\n\n if (this.state.open) {\n classes += ' drawer--open';\n document.body.classList.add('body--drawer');\n } else document.body.classList.remove('body--drawer');\n\n return (\n <div\n className={classes}\n onClick={this.handleClick}\n onTouchStart={this.handleTouchStart}\n onTouchMove={this.handleTouchMove}\n >\n {this.state.open ? (\n <React.Fragment>\n <FontAwesomeIcon\n icon={faChevronDown}\n className=\"drawer__toggler fa-lg mb-3\"\n />\n <Player playerStatus={playerStatus} />{' '}\n </React.Fragment>\n ) : (\n <MiniPlayer playerStatus={playerStatus} />\n )}\n </div>\n );\n }\n}\n\nexport default Drawer;\n","import React from 'react';\n\nconst Modal = ({ id, title, footer, children }) => {\n return (\n <div\n className=\"modal fade\"\n id={id}\n tabIndex=\"-1\"\n role=\"dialog\"\n aria-labelledby=\"staticBackdropLabel\"\n aria-hidden=\"true\"\n >\n <div className=\"modal-dialog\" role=\"document\">\n <div className=\"modal-content\">\n <div className=\"modal-header\">\n <h5 className=\"modal-title\" id=\"staticBackdropLabel\">\n {title}\n </h5>\n <button\n type=\"button\"\n className=\"close\"\n data-dismiss=\"modal\"\n aria-label=\"Close\"\n >\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div className=\"modal-body\">{children}</div>\n <div className=\"modal-footer\">{footer}</div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default Modal;\n","const DEFAULT_THEME = 'darkly';\n\nconst STORAGE_KEY = 'theme';\n\nconst getThemePath = themeId =>\n `https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/${themeId}/bootstrap.min.css`;\n\nexport const themes = [\n { id: 'darkly', name: 'Darkly', themeColor: '#375a7f' },\n { id: 'lux', name: 'Lux', themeColor: '#1a1a1a' },\n { id: 'litera', name: 'Litera', themeColor: '#4582ec' }\n];\n\nexport function changeTheme(themeId) {\n localStorage.setItem(STORAGE_KEY, themeId);\n applyTheme();\n}\n\nexport function applyTheme() {\n const themeId = getThemeId();\n const themePath = getThemePath(themeId);\n const themeColor = themes.find(t => t.id === themeId).themeColor;\n\n const linkEl = document.querySelector('link[title=\"theme\"]');\n linkEl.setAttribute('href', themePath);\n\n const metaEl = document.querySelector('meta[name=\"theme-color\"]');\n metaEl.setAttribute('content', themeColor);\n}\n\nexport function getThemeId() {\n const localId = localStorage.getItem(STORAGE_KEY);\n\n if (localId === '') return DEFAULT_THEME;\n if (themes.filter(t => t.id === localId).length === 0) {\n localStorage.removeItem(STORAGE_KEY);\n return DEFAULT_THEME;\n }\n\n return localId;\n}\n","import React from 'react';\n\nconst Select = ({ id, label, row, data, ...rest }) => (\n <div className={row ? 'form-group row' : 'form-group'}>\n <label htmlFor={id} className=\"col-sm-2 col-form-label\">\n {label}\n </label>\n <div className=\"col-sm-10\">\n <select id={id} className=\"form-control\" {...rest}>\n {data.map(d => (\n <option key={d.id || d} value={d.id || d}>\n {d.name || d}\n </option>\n ))}\n </select>\n </div>\n </div>\n);\n\nexport default Select;\n","import React from 'react';\nimport Modal from './common/modal';\nimport { getThemeId, changeTheme, themes } from '../theme';\nimport i18next from 'i18next';\nimport { withTranslation } from 'react-i18next';\nimport { languages } from '../config.json';\nimport Select from './common/select';\n\nconst Settings = ({ t }) => {\n const renderFooter = () => (\n <button className=\"btn btn-secondary\" data-dismiss=\"modal\">\n {t('close')}\n </button>\n );\n\n return (\n <Modal id=\"settings\" title={t('settings')} footer={renderFooter()}>\n <Select\n id=\"theme-select\"\n label={t('theme')}\n data={themes}\n row\n value={getThemeId()}\n onChange={e => {\n changeTheme(e.target.value);\n }}\n />\n <Select\n id=\"language-select\"\n label={t('language')}\n data={languages}\n row\n value={i18next.language}\n onChange={e => {\n i18next.changeLanguage(e.target.value);\n }}\n />\n </Modal>\n );\n};\n\nexport default withTranslation()(Settings);\n","import React, { useState } from 'react';\nimport Modal from './common/modal';\nimport { toast } from 'react-toastify';\nimport { playURL } from '../services/playerService';\nimport { withTranslation } from 'react-i18next';\n\nconst URLDialog = ({ t }) => {\n const [url, setURL] = useState('');\n\n const handleOK = () => {\n if (url === '') return;\n doPlayURL(url);\n setURL('');\n };\n\n const doPlayURL = async url => {\n const errorToastOpts = {\n type: toast.TYPE.ERROR,\n render: t('errorNotFound')\n };\n\n const toastId = toast(t('tryingURL'));\n\n try {\n await playURL(url);\n } catch (ex) {\n if (ex.response && ex.response.status === 400)\n toast.update(toastId, errorToastOpts);\n }\n };\n\n const handleChange = ({ target: input }) => {\n setURL(input.value);\n };\n\n const handleKeyDown = e => {\n if (e.key === 'Enter') handleOK();\n };\n\n const renderFooter = () => (\n <React.Fragment>\n <button\n className=\"btn btn-primary\"\n data-dismiss=\"modal\"\n onClick={handleOK}\n >\n OK\n </button>\n <button\n className=\"btn btn-secondary\"\n onClick={() => setURL('')}\n data-dismiss=\"modal\"\n >\n Cancel\n </button>\n </React.Fragment>\n );\n\n return (\n <Modal id=\"url-dialog\" title={t('playURL')} footer={renderFooter()}>\n <input\n className=\"form-control mb-4\"\n type=\"text\"\n placeholder=\"URL\"\n value={url}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n />\n </Modal>\n );\n};\n\nexport default withTranslation()(URLDialog);\n","import React, { useState, useEffect } from 'react';\nimport './backdrop.scss';\n\nconst Backdrop = ({ title, body }) => {\n const [bodyStyled, setBodyStyled] = useState(false);\n\n const getScrollbarWidth = () =>\n window.innerWidth - document.documentElement.clientWidth;\n\n const doBodyStyle = () => {\n if (title) {\n // Calculate this BEFORE adding the class.\n document.body.style.paddingRight = getScrollbarWidth() + 'px';\n document.body.classList.add('body--backdrop');\n setBodyStyled(true);\n // We need this check because Bootstrap modals also style paddingRight\n } else if (!title && bodyStyled) {\n document.body.style.paddingRight = 0;\n document.body.classList.remove('body--backdrop');\n setBodyStyled(false);\n }\n };\n\n useEffect(doBodyStyle, [title, body]);\n\n const classes =\n 'backdrop p-2 text-white' + (title ? ' backdrop--visible' : '');\n\n // We need text-white on h* tags because some themes override it\n return (\n <div className={classes}>\n <h3 className=\"text-white\">{title}</h3>\n <h5 className=\"text-white\">{body}</h5>\n </div>\n );\n};\n\nexport default Backdrop;\n","import React, { Component } from 'react';\nimport NavBar from './components/navBar';\nimport Player from './components/player';\nimport Streams from './components/streams';\nimport Drawer from './components/drawer';\nimport Settings from './components/settings';\nimport URLDialog from './components/urlDialog';\nimport Loader from './components/loader';\nimport Backdrop from './components/backdrop';\nimport { ToastContainer } from 'react-toastify';\nimport { getStatus } from './services/playerService';\nimport { withTranslation } from 'react-i18next';\nimport { updateInterval, backdropTimeout } from './config.json';\nimport './App.scss';\nimport 'react-toastify/dist/ReactToastify.css';\n\nclass App extends Component {\n state = {\n playerStatus: {},\n loading: true,\n networkError: false,\n backdrop: {}\n };\n\n componentDidMount() {\n this.updatePlayerStatus();\n }\n\n async updatePlayerStatus() {\n try {\n const { data: playerStatus } = await getStatus();\n this.setState({ playerStatus, loading: false, networkError: false });\n } catch (ex) {\n this.setState({ loading: false, networkError: true });\n }\n\n setTimeout(() => this.updatePlayerStatus(), updateInterval);\n }\n\n handleBackdrop = (title, body = '') => {\n this.setState({ backdrop: { title, body } });\n setTimeout(() => this.setState({ backdrop: {} }), backdropTimeout);\n };\n\n render() {\n const { loading, networkError, backdrop, playerStatus } = this.state;\n const { t } = this.props;\n\n if (networkError) return <Backdrop title={t('errorNetwork')} />;\n if (loading) return <Loader />;\n if (!playerStatus.con_mpd) return <Backdrop title={t('disconnectedMPD')} />;\n\n return (\n <div className=\"app\">\n <ToastContainer />\n <Backdrop title={backdrop.title} body={backdrop.body} />\n <NavBar />\n <Drawer playerStatus={playerStatus} />\n\n <main className=\"app-main p-4\">\n <Player playerStatus={playerStatus} />\n <Streams\n onBackdrop={this.handleBackdrop}\n playerStatus={playerStatus}\n />\n <Settings />\n <URLDialog />\n </main>\n </div>\n );\n }\n}\n\nexport default withTranslation()(App);\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' }\n })\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import i18n from 'i18next';\nimport { initReactI18next } from 'react-i18next';\n\nimport Backend from 'i18next-xhr-backend';\nimport LanguageDetector from 'i18next-browser-languagedetector';\n// not like to use this?\n// have a look at the Quick start guide\n// for passing in lng and translations on init\n\ni18n\n // load translation using xhr -> see /public/locales\n // learn more: https://github.com/i18next/i18next-xhr-backend\n .use(Backend)\n // detect user language\n // learn more: https://github.com/i18next/i18next-browser-languageDetector\n .use(LanguageDetector)\n // pass the i18n instance to react-i18next.\n .use(initReactI18next)\n // init i18next\n // for all options read: https://www.i18next.com/overview/configuration-options\n .init({\n fallbackLng: 'en',\n debug: process.env.REACT_APP_I18N_DEBUG,\n\n interpolation: {\n escapeValue: false // not needed for react as it escapes by default\n }\n });\n\nexport default i18n;\n","import React, { Suspense } from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\nimport Loader from './components/loader';\nimport './i18n';\nimport { applyTheme } from './theme';\nimport './index.scss';\n\n// Bootstrap\n//import 'bootstrap/dist/css/bootstrap.css';\n//import 'bootswatch/dist/flatly/bootstrap.min.css'; // This replaces default Bootstrap file\nimport 'jquery/dist/jquery.min.js';\nimport 'bootstrap/dist/js/bootstrap.js';\n\napplyTheme();\n\nReactDOM.render(\n // Suspense needed for i18n\n <Suspense fallback={<Loader />}>\n <App />\n </Suspense>,\n document.getElementById('root')\n);\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"],"sourceRoot":""}