diff --git a/css/audioplayer.css b/css/audioplayer.css
deleted file mode 100644
index e69de29..0000000
diff --git a/css/libremedia.css b/css/libremedia.css
index b4b6d42..fd5365e 100644
--- a/css/libremedia.css
+++ b/css/libremedia.css
@@ -310,4 +310,4 @@ button {
button:focus {
outline: none;
-}
\ No newline at end of file
+}
diff --git a/go.mod b/go.mod
index d59db46..f28c7a2 100644
--- a/go.mod
+++ b/go.mod
@@ -12,17 +12,24 @@ require (
)
require (
+ github.com/CAFxX/httpcompression v0.0.8 // indirect
+ github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/PuerkitoBio/goquery v1.8.1 // indirect
+ github.com/andybalholm/brotli v1.0.4 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/badfortrains/mdns v0.0.0-20160325001438-447166384f51 // indirect
github.com/brynbellomy/klog v0.0.0-20200414031930-87fbf2e555ae // indirect
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
github.com/eolso/threadsafe v0.0.0-20230304165831-d28da4e4d0d3 // indirect
github.com/go-errors/errors v1.4.2 // indirect
+ github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1 // indirect
+ github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27 // indirect
+ github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/gosimple/slug v1.13.1 // indirect
github.com/gosimple/unidecode v1.0.1 // indirect
github.com/jfbus/httprs v1.0.1 // indirect
+ github.com/klauspost/compress v1.14.1 // indirect
github.com/miekg/dns v1.1.55 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
diff --git a/go.sum b/go.sum
index 944a8df..2f93342 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,15 @@
+github.com/CAFxX/httpcompression v0.0.8 h1:UBWojERnpCS6X7whJkGGZeCC3ruZBRwkwkcnfGfb0ko=
+github.com/CAFxX/httpcompression v0.0.8/go.mod h1:bVd1taHK1vYb5SWe9lwNDCqrfj2ka+C1Zx7JHzxuHnU=
github.com/JoshuaDoes/json v0.0.0-20200726213358-ec3860544ac0 h1:315Zb0n+8KwZyUiIKbDGvfrQ003c0XfHYNy0M4vv5cA=
github.com/JoshuaDoes/json v0.0.0-20200726213358-ec3860544ac0/go.mod h1:vsCdx75bni6k6GIQPPima8KiM7ZjNHeBJ8keBaJOADA=
+github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
+github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/PuerkitoBio/goquery v1.4.1 h1:smcIRGdYm/w7JSbcdeLHEMzxmsBQvl8lhf0dSw2nzMI=
github.com/PuerkitoBio/goquery v1.4.1/go.mod h1:T9ezsOHcCrDCgA8aF1Cqr3sSYbO/xgdy8/R/XiIMAhA=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
+github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
+github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
@@ -37,12 +43,19 @@ github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
+github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1 h1:zga7zaRE8HCbWjcXMDlfvmQtH0/kMVLo7cQ48dy6kWg=
+github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1/go.mod h1:PumS+5d59wmAGsZo6IfRpVNaJUq+6xjC4Utt/k8GO6Q=
+github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27 h1:O6yi4xa9b2DMosGsXzlMe2E9qXgXCVkRLCoRX+5amxI=
+github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27/go.mod h1:AYvN8omj7nKLmbcXS2dyABYU6JB1Lz1bHmkkq1kf4I4=
+github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno=
+github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw=
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/brotli/go/cbrotli v0.0.0-20210623081221-ce222e317e36/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -56,6 +69,9 @@ github.com/jfbus/httprs v1.0.1 h1:kIf3dk5QlEiBDPnY88BRHI6iQ1HepvYD8Fb8zVmiNDU=
github.com/jfbus/httprs v1.0.1/go.mod h1:M9fpbEbf1Ns5RSaTkvnykqBCdJkwNtYAoAC73Ie9bEs=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/klauspost/compress v1.14.1 h1:hLQYb23E8/fO+1u53d02A97a8UnsddcvYzq4ERRU4ds=
+github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/librespot-org/librespot-golang v0.0.0-20220325184705-31669e5a889f h1:tTMPsyVClxxV+CnlLqzgxTTTFM6J7i//rSejN1Md0b0=
github.com/librespot-org/librespot-golang v0.0.0-20220325184705-31669e5a889f/go.mod h1:LeHPXRci2Bg6RBmw8BDEFH5nfb8oGU6mll+qTH8UIzw=
github.com/miekg/dns v1.1.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ -67,6 +83,7 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/pierrec/lz4/v4 v4.1.12/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -85,6 +102,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/valyala/gozstd v1.11.0/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
github.com/xlab/portaudio-go v0.0.0-20170905165025-132d041879db h1:sSIQlvfIWUHLDhEWUL2K2CeYv9CDksC00VxuxPUe4lw=
github.com/xlab/portaudio-go v0.0.0-20170905165025-132d041879db/go.mod h1:r57mRacDQMS6Fz8ubv1nE8zZ0DbQ/sY0NkCmdxeUXmY=
github.com/xlab/vorbis-go v0.0.0-20190125051917-087364aef51d/go.mod h1:AMqfx3jFwPqem3u8mF2lsRodZs30jG/Mag5HZ3mB3sA=
diff --git a/index.html b/index.html
index c8860b3..c2a3238 100644
--- a/index.html
+++ b/index.html
@@ -1,19 +1,21 @@
+
libremedia
-
-
+
+
-
-
-
+
+
+
+
@@ -28,7 +30,6 @@
-
@@ -38,7 +39,7 @@
-
+
diff --git a/js/libremedia/libremedia-artwork.js b/js/libremedia/libremedia-artwork.js
index 77e2537..06b7349 100644
--- a/js/libremedia/libremedia-artwork.js
+++ b/js/libremedia/libremedia-artwork.js
@@ -8,11 +8,9 @@ function setBgImg(url) {
newbg = "";
}
- if (document.body.style.backgroundImage !== newbg) {
- //console.log("Setting background " + url + " using " + newbg + " to replace " + document.body.style.backgroundImage);
- document.body.style.backgroundImage = newbg;
- bgImg = url;
- }
+ //console.log("Setting background " + url + " using " + newbg + " to replace " + document.body.style.backgroundImage);
+ document.body.style.backgroundImage = newbg;
+ bgImg = url;
}
function setBgStream(stream) {
diff --git a/js/libremedia/libremedia-downloader.js b/js/libremedia/libremedia-downloader.js
index b8922bc..dabac51 100644
--- a/js/libremedia/libremedia-downloader.js
+++ b/js/libremedia/libremedia-downloader.js
@@ -1,17 +1,27 @@
-function downloadStream(match) {
+async function downloadStream(match) {
if (match.params == null) {
pagePotato(match);
return;
}
var uri = match.params.uri;
+
+ var stream = (await v1GetObject(uri)).object;
+ if (stream == null) {
+ displayNotification("Download not ready!", 3000);
+ return;
+ }
+
var hostname = window.location.hostname;
- var urlpath = "https://" + hostname + "/v1/download/" + uri;
+ var port = window.location.port;
+ if (port != "")
+ port = ":" + port;
+ var protocol = location.protocol;
+ var urlpath = protocol + "//" + hostname + port + "/v1/download/" + uri;
window.open(urlpath, "_blank");
pagePotato(match);
- var stream = v1GetObject(uri).object;
const creator = '
';
- const albumObj = v1GetObject(stream.album.object.uri).object;
+ const albumObj = (await v1GetObject(stream.album.object.uri)).object;
const album = '
' + albumObj.name + '';
const name = '
';
@@ -29,4 +39,4 @@ function downloadAlbum(albumURI) {
function downloadDiscography(creatorURI) {
//console.log("Not implemented yet! TODO: Download " + creatorURI + " as ZIP");
-}
\ No newline at end of file
+}
diff --git a/js/libremedia/libremedia-navigation.js b/js/libremedia/libremedia-navigation.js
index a613eb2..967f84d 100644
--- a/js/libremedia/libremedia-navigation.js
+++ b/js/libremedia/libremedia-navigation.js
@@ -56,6 +56,7 @@ function navigoResolve() {
.on((match) => {
//console.log("Nothing to do!");
render(match, "");
+ setBgImg("https://files.joshuadoes.com/randombackground_redirect.php");
})
.resolve();
}
diff --git a/js/navigo-8.11.1.min.js b/js/navigo-8.11.1.min.js
new file mode 100644
index 0000000..95bea73
--- /dev/null
+++ b/js/navigo-8.11.1.min.js
@@ -0,0 +1,2 @@
+!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define("Navigo",[],n):"object"==typeof exports?exports.Navigo=n():t.Navigo=n()}("undefined"!=typeof self?self:this,(function(){return function(){"use strict";var t={407:function(t,n,e){e.d(n,{default:function(){return N}});var o=/([:*])(\w+)/g,r=/\*/g,i=/\/\?/g;function a(t){return void 0===t&&(t="/"),v()?location.pathname+location.search+location.hash:t}function s(t){return t.replace(/\/+$/,"").replace(/^\/+/,"")}function c(t){return"string"==typeof t}function u(t){return t&&t.indexOf("#")>=0&&t.split("#").pop()||""}function h(t){var n=s(t).split(/\?(.*)?$/);return[s(n[0]),n.slice(1).join("")]}function f(t){for(var n={},e=t.split("&"),o=0;o
0?1===t.matches.length?t.matches[0]:t.matches:void 0)}})).concat([function(){return o()}])):o()}else o()}})),{},(function(){return n()})):n()}function P(t,n){d(t.navigateOptions,"updateState")&&t.instance._setCurrent(t.matches),n()}var R=[function(t,n){var e=t.instance.lastResolved();if(e&&e[0]&&e[0].route===t.match.route&&e[0].url===t.match.url&&e[0].queryString===t.match.queryString)return e.forEach((function(n){n.route.hooks&&n.route.hooks.already&&d(t.navigateOptions,"callHooks")&&n.route.hooks.already.forEach((function(n){return n(t.match)}))})),void n(!1);n()},function(t,n){t.match.route.hooks&&t.match.route.hooks.before&&d(t.navigateOptions,"callHooks")?m(t.match.route.hooks.before.map((function(n){return function(e,o){return n((function(n){!1===n?t.instance.__markAsClean(t):o()}),t.match)}})).concat([function(){return n()}])):n()},function(t,n){d(t.navigateOptions,"callHandler")&&t.match.route.handler(t.match),t.instance.updatePageLinks(),n()},function(t,n){t.match.route.hooks&&t.match.route.hooks.after&&d(t.navigateOptions,"callHooks")&&t.match.route.hooks.after.forEach((function(n){return n(t.match)})),n()}],S=[A,function(t,n){var e=t.instance._notFoundRoute;if(e){t.notFoundHandled=!0;var o=h(t.currentLocationPath),r=o[0],i=o[1],a=u(t.to);e.path=s(r);var c={url:e.path,queryString:i,hashString:a,data:null,route:e,params:""!==i?f(i):null};t.matches=[c],t.match=c}n()},m.if((function(t){return t.notFoundHandled}),R.concat([P]),[function(t,n){t.resolveOptions&&!1!==t.resolveOptions.noMatchWarning&&void 0!==t.resolveOptions.noMatchWarning||console.warn('Navigo: "'+t.currentLocationPath+"\" didn't match any of the registered routes."),n()},function(t,n){t.instance._setCurrent(null),n()}])];function E(){return(E=Object.assign||function(t){for(var n=1;n=0&&(t=!0===o.hash?t.split("#")[1]||"/":t.split("#")[0]),t}function E(t){return s(i+"/"+s(t))}function N(t,n,e,o){return t=c(t)?E(t):t,{name:o||s(String(t)),path:t,handler:n,hooks:g(e)}}function U(t,n){if(!r.__dirty){r.__dirty=!0,t=t?s(i)+"/"+s(t):void 0;var e={instance:r,to:t,currentLocationPath:t,navigateOptions:{},resolveOptions:j({},o,n)};return m([y,_,m.if((function(t){var n=t.matches;return n&&n.length>0}),x,S)],e,H),!!e.matches&&e.matches}r.__waiting.push((function(){return r.resolve(t,n)}))}function q(t,n){if(r.__dirty)r.__waiting.push((function(){return r.navigate(t,n)}));else{r.__dirty=!0,t=s(i)+"/"+s(t);var e={instance:r,to:t,navigateOptions:n||{},resolveOptions:n&&n.resolveOptions?n.resolveOptions:o,currentLocationPath:R(t)};m([k,O,_,m.if((function(t){var n=t.matches;return n&&n.length>0}),x,S),b,H],e,H)}}function F(){if(P)return(P?[].slice.call(document.querySelectorAll(o.linksSelector||C)):[]).forEach((function(t){"false"!==t.getAttribute("data-navigo")&&"_blank"!==t.getAttribute("target")?t.hasListenerAttached||(t.hasListenerAttached=!0,t.navigoHandler=function(n){if((n.ctrlKey||n.metaKey)&&"a"===n.target.tagName.toLowerCase())return!1;var e=t.getAttribute("href");if(null==e)return!1;if(e.match(/^(http|https)/)&&"undefined"!=typeof URL)try{var o=new URL(e);e=o.pathname+o.search}catch(t){}var i=function(t){if(!t)return{};var n,e=t.split(","),o={};return e.forEach((function(t){var e=t.split(":").map((function(t){return t.replace(/(^ +| +$)/g,"")}));switch(e[0]){case"historyAPIMethod":o.historyAPIMethod=e[1];break;case"resolveOptionsStrategy":n||(n={}),n.strategy=e[1];break;case"resolveOptionsHash":n||(n={}),n.hash="true"===e[1];break;case"updateBrowserURL":case"callHandler":case"updateState":case"force":o[e[0]]="true"===e[1]}})),n&&(o.resolveOptions=n),o}(t.getAttribute("data-navigo-options"));L||(n.preventDefault(),n.stopPropagation(),r.navigate(s(e),i))},t.addEventListener("click",t.navigoHandler)):t.hasListenerAttached&&t.removeEventListener("click",t.navigoHandler)})),r}function I(t,n,e){var o=w.find((function(n){return n.name===t})),r=null;if(o){if(r=o.path,n)for(var a in n)r=r.replace(":"+a,n[a]);r=r.match(/^\//)?r:"/"+r}return r&&e&&!e.includeRoot&&(r=r.replace(new RegExp("^/"+i),"")),r}function M(t){var n=h(s(t)),o=n[0],r=n[1],i=""===r?null:f(r);return{url:o,queryString:r,hashString:u(t),route:N(o,(function(){}),[e],o),data:null,params:i}}function T(t,n,e){return"string"==typeof n&&(n=z(n)),n?(n.hooks[t]||(n.hooks[t]=[]),n.hooks[t].push(e),function(){n.hooks[t]=n.hooks[t].filter((function(t){return t!==e}))}):(console.warn("Route doesn't exists: "+n),function(){})}function z(t){return"string"==typeof t?w.find((function(n){return n.name===E(t)})):w.find((function(n){return n.handler===t}))}t?i=s(t):console.warn('Navigo requires a root path in its constructor. If not provided will use "/" as default.'),this.root=i,this.routes=w,this.destroyed=L,this.current=d,this.__freezeListening=!1,this.__waiting=[],this.__dirty=!1,this.__markAsClean=function(t){t.instance.__dirty=!1,t.instance.__waiting.length>0&&t.instance.__waiting.shift()()},this.on=function(t,n,o){var r=this;return"object"!=typeof t||t instanceof RegExp?("function"==typeof t&&(o=n,n=t,t=i),w.push(N(t,n,[e,o])),this):(Object.keys(t).forEach((function(n){if("function"==typeof t[n])r.on(n,t[n]);else{var o=t[n],i=o.uses,a=o.as,s=o.hooks;w.push(N(n,i,[e,s],a))}})),this)},this.off=function(t){return this.routes=w=w.filter((function(n){return c(t)?s(n.path)!==s(t):"function"==typeof t?t!==n.handler:String(n.path)!==String(t)})),this},this.resolve=U,this.navigate=q,this.navigateByName=function(t,n,e){var o=I(t,n);return null!==o&&(q(o.replace(new RegExp("^/?"+i),""),e),!0)},this.destroy=function(){this.routes=w=[],A&&window.removeEventListener("popstate",this.__popstateListener),this.destroyed=L=!0},this.notFound=function(t,n){return r._notFoundRoute=N("*",t,[e,n],"__NOT_FOUND__"),this},this.updatePageLinks=F,this.link=function(t){return"/"+i+"/"+s(t)},this.hooks=function(t){return e=t,this},this.extractGETParameters=function(t){return h(R(t))},this.lastResolved=function(){return d},this.generate=I,this.getLinkPath=function(t){return t.getAttribute("href")},this.match=function(t){var n={instance:r,currentLocationPath:t,to:t,navigateOptions:{},resolveOptions:o};return _(n,(function(){})),!!n.matches&&n.matches},this.matchLocation=function(t,n,e){void 0===n||void 0!==e&&!e||(n=E(n));var o={instance:r,to:n,currentLocationPath:n};return y(o,(function(){})),"string"==typeof t&&(t=void 0===e||e?E(t):t),l(o,{name:String(t),path:t,handler:function(){},hooks:{}})||!1},this.getCurrentLocation=function(){return M(s(a(i)).replace(new RegExp("^"+i),""))},this.addBeforeHook=T.bind(this,"before"),this.addAfterHook=T.bind(this,"after"),this.addAlreadyHook=T.bind(this,"already"),this.addLeaveHook=T.bind(this,"leave"),this.getRoute=z,this._pathToMatchObject=M,this._clean=s,this._checkForAHash=R,this._setCurrent=function(t){return d=r.current=t},function(){A&&(this.__popstateListener=function(){r.__freezeListening||U()},window.addEventListener("popstate",this.__popstateListener))}.call(this),F.call(this)}}},n={};function e(o){if(n[o])return n[o].exports;var r=n[o]={exports:{}};return t[o](r,r.exports,e),r.exports}return e.d=function(t,n){for(var o in n)e.o(n,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:n[o]})},e.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},e(407)}().default}));
+//# sourceMappingURL=navigo.min.js.map
\ No newline at end of file
diff --git a/main.go b/main.go
index ee3f8ce..eba6769 100644
--- a/main.go
+++ b/main.go
@@ -21,7 +21,9 @@ import (
"strings"
"time"
+ "github.com/CAFxX/httpcompression"
"github.com/eolso/librespot-golang/librespot/utils"
+ "github.com/go-http-utils/etag"
)
type exporterr struct {
@@ -96,17 +98,20 @@ func main() {
}
//libremedia API v1
- http.HandleFunc("/v1/", v1Handler)
- http.HandleFunc("/v1/stream/", v1StreamHandler)
- http.HandleFunc("/v1/download/", v1DownloadHandler)
+ mux := http.NewServeMux()
+ mux.HandleFunc("/v1/", v1Handler)
+ mux.HandleFunc("/v1/stream/", v1StreamHandler)
+ mux.HandleFunc("/v1/download/", v1DownloadHandler)
//Built-in utilities that may not be recreatable in some circumstances
- http.HandleFunc("/util/gid2id/", gid2id)
+ mux.HandleFunc("/util/gid2id/", gid2id)
//Web interfaces
- http.HandleFunc("/", webHandler)
+ mux.HandleFunc("/", webHandler)
- Warning.Fatal(http.ListenAndServe(service.HostAddr, nil))
+ //Compression
+ compress, _ := httpcompression.DefaultAdapter()
+ Warning.Fatal(http.ListenAndServe(service.HostAddr, etag.Handler(compress(mux), false)))
}
func v1Handler(w http.ResponseWriter, r *http.Request) {
@@ -116,13 +121,15 @@ func v1Handler(w http.ResponseWriter, r *http.Request) {
jsonWriteErrorf(w, 404, "no matching object")
return
}
- if !obj.Expanded && !obj.Expanding {
- switch obj.Type {
+ if !obj.Expanded {
+ /*switch obj.Type {
case "album", "creator", "stream":
obj.Expand()
default:
go obj.Expand()
- }
+ }*/
+ go obj.Expand()
+ time.Sleep(time.Second * 1)
}
jsonWrite(w, obj)
}
diff --git a/objects.go b/objects.go
index 181ea90..667c3a8 100644
--- a/objects.go
+++ b/objects.go
@@ -20,11 +20,20 @@ type Object struct {
Expanded bool `json:"expanded,omitempty"` //Whether or not this object has been expanded internally
}
+var (
+ expandQueue map[string]*Object //Holds the active expanding queue to check for dead objects
+)
+
+func init() {
+ expandQueue = make(map[string]*Object)
+}
+
// JSON returns this object as serialized JSON
func (obj *Object) JSON() ([]byte, error) {
return json.Marshal(obj)
}
+// SearchResults returns this object as *ObjectSearchResults
func (obj *Object) SearchResults() *ObjectSearchResults {
if obj.Object == nil {
return nil
@@ -41,6 +50,7 @@ func (obj *Object) SearchResults() *ObjectSearchResults {
return nil
}
+// Creator returns this object as *ObjectCreator
func (obj *Object) Creator() *ObjectCreator {
if obj.Object == nil {
return nil
@@ -57,6 +67,7 @@ func (obj *Object) Creator() *ObjectCreator {
return nil
}
+// Album returns this object as *ObjectAlbum
func (obj *Object) Album() *ObjectAlbum {
if obj.Object == nil {
return nil
@@ -73,6 +84,7 @@ func (obj *Object) Album() *ObjectAlbum {
return nil
}
+// Stream returns this object as *ObjectStream
func (obj *Object) Stream() *ObjectStream {
if obj.Object == nil {
return nil
@@ -138,28 +150,28 @@ func (obj *Object) Sync() {
}
// Expand fills in all top-level object arrays with completed objects
-func (src *Object) Expand() {
- if src.URI == "" {
+func (obj *Object) Expand() {
+ if obj.URI == "" {
return
}
//Check if object is being expanded right now
- if src.Expanding {
- //Sleep and try again
+ if _, exists := expandQueue[obj.URI]; exists {
return
}
- src.Expanding = true
- src.Expanded = false
- src.Sync()
- Trace.Println("Expanding " + src.URI)
- switch src.Type {
+ obj.Expanding = true
+ obj.Expanded = false
+ obj.Sync()
+ expandQueue[obj.URI] = obj
+ Trace.Println("Expanding " + obj.URI)
+ switch obj.Type {
case "search":
- if search := src.SearchResults(); search != nil {
+ if search := obj.SearchResults(); search != nil {
syncSearch := func() {
searchJSON, err := json.Marshal(search)
if err == nil {
- src.Object = &json.RawMessage{}
- src.Object.UnmarshalJSON(searchJSON)
- src.Sync()
+ obj.Object = &json.RawMessage{}
+ obj.Object.UnmarshalJSON(searchJSON)
+ obj.Sync()
}
}
for i := 0; i < len(search.Streams); i++ {
@@ -167,126 +179,126 @@ func (src *Object) Expand() {
continue
}
search.Streams[i] = GetObject(search.Streams[i].URI)
- syncSearch()
}
+ syncSearch()
for i := 0; i < len(search.Creators); i++ {
if search.Creators[i].URI == "" {
continue
}
search.Creators[i] = GetObject(search.Creators[i].URI)
- syncSearch()
}
+ syncSearch()
for i := 0; i < len(search.Albums); i++ {
if search.Albums[i].URI == "" {
continue
}
search.Albums[i] = GetObject(search.Albums[i].URI)
- syncSearch()
}
+ syncSearch()
}
case "artist", "creator", "user", "channel", "chan", "streamer":
- if creator := src.Creator(); creator != nil {
+ if creator := obj.Creator(); creator != nil {
syncCreator := func() {
creatorJSON, err := json.Marshal(creator)
if err == nil {
- src.Object = &json.RawMessage{}
- src.Object.UnmarshalJSON(creatorJSON)
- src.Sync()
+ obj.Object = &json.RawMessage{}
+ obj.Object.UnmarshalJSON(creatorJSON)
+ obj.Sync()
}
}
- for i := 0; i < len(creator.TopStreams); i++ {
+ /*for i := 0; i < len(creator.TopStreams); i++ {
if creator.TopStreams[i].URI == "" {
continue
}
creator.TopStreams[i] = GetObject(creator.TopStreams[i].URI)
- syncCreator()
}
+ syncCreator()
for i := 0; i < len(creator.Albums); i++ {
if creator.Albums[i].URI == "" {
continue
}
creator.Albums[i] = GetObject(creator.Albums[i].URI)
- syncCreator()
}
+ syncCreator()
for i := 0; i < len(creator.Appearances); i++ {
if creator.Appearances[i].URI == "" {
continue
}
creator.Appearances[i] = GetObject(creator.Appearances[i].URI)
- syncCreator()
}
+ syncCreator()
for i := 0; i < len(creator.Singles); i++ {
if creator.Singles[i].URI == "" {
continue
}
creator.Singles[i] = GetObject(creator.Singles[i].URI)
- syncCreator()
}
+ syncCreator()
for i := 0; i < len(creator.Related); i++ {
if creator.Related[i].URI == "" {
continue
}
creator.Related[i] = GetObject(creator.Related[i].URI)
- syncCreator()
- }
+ }*/
+ syncCreator()
}
case "album":
- if album := src.Album(); album != nil {
+ if album := obj.Album(); album != nil {
syncAlbum := func() {
albumJSON, err := json.Marshal(album)
if err == nil {
- src.Object = &json.RawMessage{}
- src.Object.UnmarshalJSON(albumJSON)
- src.Sync()
+ obj.Object = &json.RawMessage{}
+ obj.Object.UnmarshalJSON(albumJSON)
+ obj.Sync()
}
}
- for i := 0; i < len(album.Creators); i++ {
+ /*for i := 0; i < len(album.Creators); i++ {
if album.Creators[i].URI == "" {
continue
}
album.Creators[i] = GetObject(album.Creators[i].URI)
syncAlbum()
- }
+ }*/
for i := 0; i < len(album.Discs); i++ {
for j := 0; j < len(album.Discs[i].Streams); j++ {
if album.Discs[i].Streams[j].URI == "" {
continue
}
album.Discs[i].Streams[j] = GetObject(album.Discs[i].Streams[j].URI)
- syncAlbum()
}
}
+ syncAlbum()
}
case "track", "song", "video", "audio", "stream":
- if stream := src.Stream(); stream != nil {
+ if stream := obj.Stream(); stream != nil {
syncStream := func() {
streamJSON, err := json.Marshal(stream)
if err == nil {
- src.Object = &json.RawMessage{}
- src.Object.UnmarshalJSON(streamJSON)
- src.Sync()
+ obj.Object = &json.RawMessage{}
+ obj.Object.UnmarshalJSON(streamJSON)
+ obj.Sync()
}
}
stream.Album = GetObject(stream.Album.URI)
- syncStream()
- for i := 0; i < len(stream.Creators); i++ {
+ /*for i := 0; i < len(stream.Creators); i++ {
if stream.Creators[i].URI == "" {
continue
}
stream.Creators[i] = GetObject(stream.Creators[i].URI)
syncStream()
- }
+ }*/
+ syncStream()
}
}
- if src.Object != nil {
- src.Expanding = false
- src.Expanded = true
- Trace.Println("Finished expanding " + src.URI)
+ obj.Expanding = false
+ if obj.Object != nil {
+ obj.Expanded = true
+ Trace.Println("Finished expanding " + obj.URI)
} else {
- src.Expanding = false
- Trace.Println("Failed to expand " + src.URI)
+ Trace.Println("Failed to expand " + obj.URI)
}
- src.Sync()
+ obj.Sync()
+ delete(expandQueue, obj.URI)
}
// GetObjectCached returns a new object from the cache that links to a given URI
@@ -344,9 +356,9 @@ func GetObjectCached(uri string) (obj *Object) {
// NewObjError returns an error object
func NewObjError(msg string) (obj *Object) {
obj = &Object{
- Type: "error",
+ Type: "error",
Provider: "libremedia",
- Object: &json.RawMessage{},
+ Object: &json.RawMessage{},
}
errJSON, err := json.Marshal(&exporterr{Error: msg})
if err == nil {
diff --git a/service.go b/service.go
index cc118b1..be75f45 100644
--- a/service.go
+++ b/service.go
@@ -7,6 +7,7 @@ import (
)
var (
+ //TODO: Order handler priority via configuration
handlers = map[string]Handler{
"tidal": &TidalClient{},
"spotify": &SpotifyClient{},
@@ -14,6 +15,7 @@ var (
providers = make([]string, 0)
)
+// Handler is an interface to satisfy a libremedia service handler (a media or metadata provider, respond to something)
type Handler interface {
Provider() string //Used for service identification
SetService(*Service) //Provides the handler access to the libremedia service
@@ -28,6 +30,7 @@ type Handler interface {
ReplaceURI(text string) string //Replaces all instances of a URI with a libremedia-acceptable URI, for dynamic hyperlinking
}
+// HandlerConfig defines a handler's login credentials and/or active status
type HandlerConfig struct {
Active bool `json:"active"`
Username string `json:"username"`
@@ -36,6 +39,7 @@ type HandlerConfig struct {
BlobPath string `json:"blobPath"`
}
+// Service hosts a libremedia service instance's sessions and configuration
type Service struct {
AccessKeys []string `json:"accessKeys"`
BaseURL string `json:"baseURL"`
@@ -45,6 +49,7 @@ type Service struct {
Grants map[string]*ServiceUser `json:"-"`
}
+// Login logs into all of the active providers, blocking execution if any require action
func (s *Service) Login() error {
if s.BaseURL[len(s.BaseURL)-1] != '/' {
s.BaseURL += "/"
@@ -54,14 +59,14 @@ func (s *Service) Login() error {
if config.Active {
newHandler, err := handler.Authenticate(config)
if err != nil {
- Error.Println("Failed to authenticate " + provider + ": ", err)
+ Error.Println("Failed to authenticate "+provider+": ", err)
return err
}
newHandler.SetService(s)
handlers[provider] = newHandler
providers = append(providers, provider)
} else {
- Trace.Println("Skipping authenticating " + provider)
+ Trace.Println("Skipping authenticating inactive provider " + provider)
delete(handlers, provider)
}
}
@@ -69,6 +74,7 @@ func (s *Service) Login() error {
return nil
}
+// Auth checks the authentication key
func (s *Service) Auth(accessKey string) (*ServiceUser, error) {
allow := false
for i := 0; i < len(s.AccessKeys); i++ {
@@ -83,6 +89,7 @@ func (s *Service) Auth(accessKey string) (*ServiceUser, error) {
return nil, nil
}
+// Stream transports a media stream for the specified format
func (s *Service) Stream(w http.ResponseWriter, r *http.Request, stream *ObjectStream, format int) error {
if stream == nil {
return fmt.Errorf("stream is nil")
@@ -90,23 +97,25 @@ func (s *Service) Stream(w http.ResponseWriter, r *http.Request, stream *ObjectS
if stream.Provider == "" {
return fmt.Errorf("provider not specified")
}
-/* if stream.Formats == nil || len(stream.Formats) <= format {
- objStream := GetObject(stream.URI, false)
- if objStream != nil {
- stream = objStream.Stream()
- if stream.Formats == nil || len(stream.Formats) <= format {
- return fmt.Errorf("format not available to stream")
+ /* if stream.Formats == nil || len(stream.Formats) <= format {
+ objStream := GetObject(stream.URI, false)
+ if objStream != nil {
+ stream = objStream.Stream()
+ if stream.Formats == nil || len(stream.Formats) <= format {
+ return fmt.Errorf("format not available to stream")
+ }
+ } else {
+ return fmt.Errorf("stream not available right now")
}
- } else {
- return fmt.Errorf("stream not available right now")
}
- }
-*/ if handler, exists := handlers[stream.Provider]; exists {
+ */
+ if handler, exists := handlers[stream.Provider]; exists {
return handler.StreamFormat(w, r, stream, format)
}
return fmt.Errorf("no handler for provider " + stream.Provider)
}
+// Download transports a media download for the specified format
func (s *Service) Download(w http.ResponseWriter, r *http.Request, stream *ObjectStream, format int) error {
if stream.Provider == "" {
return fmt.Errorf("provider not specified")
@@ -118,6 +127,7 @@ func (s *Service) Download(w http.ResponseWriter, r *http.Request, stream *Objec
return fmt.Errorf("no handler for provider " + stream.Provider)
}
+// ServiceUser hosts a user's live session
type ServiceUser struct {
- Expires time.Time
+ LastPing time.Time
}
diff --git a/tidal.go b/tidal.go
index 6e8ee5c..d5e6892 100644
--- a/tidal.go
+++ b/tidal.go
@@ -69,6 +69,7 @@ func (terr *TidalError) Error() error {
// TidalClient holds a Tidal client
type TidalClient struct {
sync.Mutex
+ Ratelimiter int `json:"ratelimiter"` //Increased by one each time the ratelimit is encountered, spreads out requests
ClientID string `json:"clientID"`
ClientSecret string `json:"clientSecret"`
@@ -163,8 +164,16 @@ func (t *TidalClient) GetJSON(endpoint string, query url.Values, target interfac
if resp.StatusCode != 200 {
return fmt.Errorf("%s: %s", resp.Status, string(body))
}
- if target != nil {
- return json.Unmarshal(body, target)
+ switch resp.StatusCode {
+ case 200: //OK
+ if target != nil {
+ return json.Unmarshal(body, target)
+ }
+ case 429: //Too Many Requests
+ t.Ratelimiter++
+ fmt.Printf("Sleeping for %d seconds\n", t.Ratelimiter)
+ time.Sleep(time.Duration(t.Ratelimiter) * time.Second)
+ return t.GetJSON(endpoint, query, target)
}
return nil
}