Browse Source

Add support for a firmware download proxy (#85)

* Add support for a firmware download proxy. This should help in situations where the player's firmware can't handle https correctly.

Two possibilities:
* full path to image: http://yourlms:9000/plugins/SqueezeESP32/firmware/ESP32-A1S.32.634.master-cmake/squeezelite-esp32-master-cmake-ESP32-A1S-32-V0.634.bin
* use Github's asset ID: http://yourlms:9000/plugins/SqueezeESP32/firmware/34298863

The former is more prone to issues related to the path. A change in the schema could break the matching regex.
The latter is simpler to use if you know the ID. But the ID is not easily available to the user. And it requires one more lookup in the plugin to get from the ID to the download path.

* Add support for proxying firmware downloads through LMS

* add magic asset ID -99 to allow the front-end to check whether the plugin does support download proxying
* web manager is expecting `lms_port` and `lms_ip` in `status.json`. If that's available, check whether plugin does support firmware downloading. If that's the case, download firmwares through LMS
* plugin would cache firmware images. In case of multiple images the file would be served directly from LMS.

Co-authored-by: Michael Herger <michael@herger.net>
Michael Herger 4 years ago
parent
commit
bc0d104290
26 changed files with 235 additions and 66 deletions
  1. 4 1
      components/wifi-manager/webapp/mock/status.json
  2. 23 2
      components/wifi-manager/webapp/src/js/custom.js
  3. 3 3
      components/wifi-manager/webapp/webapp.cmake
  4. 15 15
      components/wifi-manager/webapp/webpack.c
  5. 36 36
      components/wifi-manager/webapp/webpack.h
  6. BIN
      components/wifi-manager/webapp/webpack/dist/favicon-32x32.png
  7. 0 0
      components/wifi-manager/webapp/webpack/dist/index.html
  8. BIN
      components/wifi-manager/webapp/webpack/dist/index.html.br
  9. BIN
      components/wifi-manager/webapp/webpack/dist/index.html.gz
  10. 0 0
      components/wifi-manager/webapp/webpack/dist/js/index.0b6890.bundle.js
  11. BIN
      components/wifi-manager/webapp/webpack/dist/js/index.0b6890.bundle.js.br
  12. BIN
      components/wifi-manager/webapp/webpack/dist/js/index.0b6890.bundle.js.gz
  13. 0 0
      components/wifi-manager/webapp/webpack/dist/js/index.e644c0.bundle.js
  14. BIN
      components/wifi-manager/webapp/webpack/dist/js/index.e644c0.bundle.js.br
  15. BIN
      components/wifi-manager/webapp/webpack/dist/js/index.e644c0.bundle.js.gz
  16. 7 0
      components/wifi-manager/webapp/webpack/dist/js/node-modules.0b6890.bundle.js
  17. BIN
      components/wifi-manager/webapp/webpack/dist/js/node-modules.0b6890.bundle.js.br
  18. BIN
      components/wifi-manager/webapp/webpack/dist/js/node-modules.0b6890.bundle.js.gz
  19. 0 7
      components/wifi-manager/webapp/webpack/dist/js/node-modules.e644c0.bundle.js
  20. BIN
      components/wifi-manager/webapp/webpack/dist/js/node-modules.e644c0.bundle.js.br
  21. BIN
      components/wifi-manager/webapp/webpack/dist/js/node-modules.e644c0.bundle.js.gz
  22. 0 0
      components/wifi-manager/webapp/webpack/dist/js/runtime.0b6890.bundle.js
  23. 0 0
      components/wifi-manager/webapp/webpack/dist/js/runtime.0b6890.bundle.js.br
  24. BIN
      components/wifi-manager/webapp/webpack/dist/js/runtime.0b6890.bundle.js.gz
  25. 1 1
      components/wifi-manager/webapp/webpack/webpack.dev.js
  26. 146 1
      plugin/SqueezeESP32/Plugin.pm

+ 4 - 1
components/wifi-manager/webapp/mock/status.json

@@ -14,5 +14,8 @@
 	"ssid": "MyTestSSID",
 	"ssid": "MyTestSSID",
 	"ip": "192.168.10.225",
 	"ip": "192.168.10.225",
 	"netmask": "255.255.255.0",
 	"netmask": "255.255.255.0",
-	"gw": "192.168.10.1"
+	"gw": "192.168.10.1",
+	"lms_cport": 9090,
+	"lms_port": 9000,
+	"lms_ip": "127.0.0.1"
 }
 }

+ 23 - 2
components/wifi-manager/webapp/src/js/custom.js

@@ -228,6 +228,7 @@ let versionName='SqueezeESP32';
 let appTitle=versionName;
 let appTitle=versionName;
 let ConnectedToSSID={};
 let ConnectedToSSID={};
 let ConnectingToSSID={};
 let ConnectingToSSID={};
+let lmsBaseUrl;
 const ConnectingToActions = {
 const ConnectingToActions = {
   'CONN' : 0,'MAN' : 1,'STS' : 2,
   'CONN' : 0,'MAN' : 1,'STS' : 2,
 }
 }
@@ -895,8 +896,7 @@ $(document).ready(function() {
 
 
 // eslint-disable-next-line no-unused-vars
 // eslint-disable-next-line no-unused-vars
 window.setURL = function(button) {
 window.setURL = function(button) {
-  const url = button.dataset.url;
-  $('#fwurl').val(url);
+  let url = button.dataset.url;
 
 
   $('[data-url^="http"]')
   $('[data-url^="http"]')
     .addClass('btn-success')
     .addClass('btn-success')
@@ -904,6 +904,13 @@ window.setURL = function(button) {
   $('[data-url="' + url + '"]')
   $('[data-url="' + url + '"]')
     .addClass('btn-danger')
     .addClass('btn-danger')
     .removeClass('btn-success');
     .removeClass('btn-success');
+
+  // if user can proxy download through LMS, modify the URL
+  if (lmsBaseUrl) {
+    url = url.replace(/.*\/download\//, lmsBaseUrl + '/plugins/SqueezeESP32/firmware/');
+  }
+
+  $('#fwurl').val(url);
 }
 }
 
 
 // function performConnect(conntype) {
 // function performConnect(conntype) {
@@ -1327,6 +1334,20 @@ function checkStatus() {
     } else {
     } else {
       $('#battery').hide();
       $('#battery').hide();
     }
     }
+
+    if (typeof lmsBaseUrl == "undefined" && data.lms_ip && data.lms_port) {
+      const baseUrl = 'http://' + data.lms_ip + ':' + data.lms_port;
+      $.ajax({
+        url: baseUrl + '/plugins/SqueezeESP32/firmware/-99', 
+        error: function() {
+          // define the value, so we don't check it any more.
+          lmsBaseUrl = '';
+        },
+        success: function() {
+          lmsBaseUrl = baseUrl;
+        }
+      });
+    }
     
     
     $('#o_jack').attr('display', Number(data.Jack) ? 'inline' : 'none');
     $('#o_jack').attr('display', Number(data.Jack) ? 'inline' : 'none');
     blockAjax = false;
     blockAjax = false;

+ 3 - 3
components/wifi-manager/webapp/webapp.cmake

@@ -1,5 +1,5 @@
 target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/favicon-32x32.png BINARY)
 target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/favicon-32x32.png BINARY)
 target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/index.html.gz BINARY)
 target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/index.html.gz BINARY)
-target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/index.e644c0.bundle.js.gz BINARY)
-target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/node-modules.e644c0.bundle.js.gz BINARY)
-target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/runtime.e644c0.bundle.js.gz BINARY)
+target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/index.0b6890.bundle.js.gz BINARY)
+target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/node-modules.0b6890.bundle.js.gz BINARY)
+target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/runtime.0b6890.bundle.js.gz BINARY)

+ 15 - 15
components/wifi-manager/webapp/webpack.c

@@ -4,31 +4,31 @@ extern const uint8_t _favicon_32x32_png_start[] asm("_binary_favicon_32x32_png_s
 extern const uint8_t _favicon_32x32_png_end[] asm("_binary_favicon_32x32_png_end");
 extern const uint8_t _favicon_32x32_png_end[] asm("_binary_favicon_32x32_png_end");
 extern const uint8_t _index_html_gz_start[] asm("_binary_index_html_gz_start");
 extern const uint8_t _index_html_gz_start[] asm("_binary_index_html_gz_start");
 extern const uint8_t _index_html_gz_end[] asm("_binary_index_html_gz_end");
 extern const uint8_t _index_html_gz_end[] asm("_binary_index_html_gz_end");
-extern const uint8_t _index_e644c0_bundle_js_gz_start[] asm("_binary_index_e644c0_bundle_js_gz_start");
-extern const uint8_t _index_e644c0_bundle_js_gz_end[] asm("_binary_index_e644c0_bundle_js_gz_end");
-extern const uint8_t _node_modules_e644c0_bundle_js_gz_start[] asm("_binary_node_modules_e644c0_bundle_js_gz_start");
-extern const uint8_t _node_modules_e644c0_bundle_js_gz_end[] asm("_binary_node_modules_e644c0_bundle_js_gz_end");
-extern const uint8_t _runtime_e644c0_bundle_js_gz_start[] asm("_binary_runtime_e644c0_bundle_js_gz_start");
-extern const uint8_t _runtime_e644c0_bundle_js_gz_end[] asm("_binary_runtime_e644c0_bundle_js_gz_end");
+extern const uint8_t _index_0b6890_bundle_js_gz_start[] asm("_binary_index_0b6890_bundle_js_gz_start");
+extern const uint8_t _index_0b6890_bundle_js_gz_end[] asm("_binary_index_0b6890_bundle_js_gz_end");
+extern const uint8_t _node_modules_0b6890_bundle_js_gz_start[] asm("_binary_node_modules_0b6890_bundle_js_gz_start");
+extern const uint8_t _node_modules_0b6890_bundle_js_gz_end[] asm("_binary_node_modules_0b6890_bundle_js_gz_end");
+extern const uint8_t _runtime_0b6890_bundle_js_gz_start[] asm("_binary_runtime_0b6890_bundle_js_gz_start");
+extern const uint8_t _runtime_0b6890_bundle_js_gz_end[] asm("_binary_runtime_0b6890_bundle_js_gz_end");
 const char * resource_lookups[] = {
 const char * resource_lookups[] = {
 	"/dist/favicon-32x32.png",
 	"/dist/favicon-32x32.png",
 	"/dist/index.html.gz",
 	"/dist/index.html.gz",
-	"/js/index.e644c0.bundle.js.gz",
-	"/js/node-modules.e644c0.bundle.js.gz",
-	"/js/runtime.e644c0.bundle.js.gz",
+	"/js/index.0b6890.bundle.js.gz",
+	"/js/node-modules.0b6890.bundle.js.gz",
+	"/js/runtime.0b6890.bundle.js.gz",
 ""
 ""
 };
 };
 const uint8_t * resource_map_start[] = {
 const uint8_t * resource_map_start[] = {
 	_favicon_32x32_png_start,
 	_favicon_32x32_png_start,
 	_index_html_gz_start,
 	_index_html_gz_start,
-	_index_e644c0_bundle_js_gz_start,
-	_node_modules_e644c0_bundle_js_gz_start,
-	_runtime_e644c0_bundle_js_gz_start
+	_index_0b6890_bundle_js_gz_start,
+	_node_modules_0b6890_bundle_js_gz_start,
+	_runtime_0b6890_bundle_js_gz_start
 };
 };
 const uint8_t * resource_map_end[] = {
 const uint8_t * resource_map_end[] = {
 	_favicon_32x32_png_end,
 	_favicon_32x32_png_end,
 	_index_html_gz_end,
 	_index_html_gz_end,
-	_index_e644c0_bundle_js_gz_end,
-	_node_modules_e644c0_bundle_js_gz_end,
-	_runtime_e644c0_bundle_js_gz_end
+	_index_0b6890_bundle_js_gz_end,
+	_node_modules_0b6890_bundle_js_gz_end,
+	_runtime_0b6890_bundle_js_gz_end
 };
 };

+ 36 - 36
components/wifi-manager/webapp/webpack.h

@@ -1,56 +1,56 @@
 /***********************************
 /***********************************
 webpack_headers
 webpack_headers
-Hash: e644c04d107606ae748d
-Version: webpack 4.44.2
-Time: 6142ms
-Built at: 2020-12-21 12 h 10 min 00 s
+Hash: 0b6890f4337e767921f7
+Version: webpack 4.46.0
+Time: 273269ms
+Built at: 2021-04-03 1:28:56
                                 Asset       Size  Chunks                                Chunk Names
                                 Asset       Size  Chunks                                Chunk Names
-          ./js/index.e644c0.bundle.js    230 KiB       0  [emitted] [immutable]         index
-       ./js/index.e644c0.bundle.js.br   31.3 KiB          [emitted]                     
-       ./js/index.e644c0.bundle.js.gz   40.9 KiB          [emitted]                     
-   ./js/node-modules.e644c0.bundle.js    265 KiB       1  [emitted] [immutable]  [big]  node-modules
-./js/node-modules.e644c0.bundle.js.br   76.2 KiB          [emitted]                     
-./js/node-modules.e644c0.bundle.js.gz   88.6 KiB          [emitted]                     
-        ./js/runtime.e644c0.bundle.js   1.46 KiB       2  [emitted] [immutable]         runtime
-     ./js/runtime.e644c0.bundle.js.br  644 bytes          [emitted]                     
-     ./js/runtime.e644c0.bundle.js.gz  722 bytes          [emitted]                     
-                    favicon-32x32.png  578 bytes          [emitted]                     
+          ./js/index.0b6890.bundle.js    231 KiB       0  [emitted] [immutable]         index
+       ./js/index.0b6890.bundle.js.br   31.5 KiB          [emitted]                     
+       ./js/index.0b6890.bundle.js.gz   41.1 KiB          [emitted]                     
+   ./js/node-modules.0b6890.bundle.js    266 KiB       1  [emitted] [immutable]  [big]  node-modules
+./js/node-modules.0b6890.bundle.js.br   76.3 KiB          [emitted]                     
+./js/node-modules.0b6890.bundle.js.gz   88.7 KiB          [emitted]                     
+        ./js/runtime.0b6890.bundle.js   1.46 KiB       2  [emitted] [immutable]         runtime
+     ./js/runtime.0b6890.bundle.js.br  644 bytes          [emitted]                     
+     ./js/runtime.0b6890.bundle.js.gz  722 bytes          [emitted]                     
+                    favicon-32x32.png  634 bytes          [emitted]                     
                            index.html   19.5 KiB          [emitted]                     
                            index.html   19.5 KiB          [emitted]                     
                         index.html.br   4.48 KiB          [emitted]                     
                         index.html.br   4.48 KiB          [emitted]                     
                         index.html.gz   5.46 KiB          [emitted]                     
                         index.html.gz   5.46 KiB          [emitted]                     
                            sprite.svg    4.4 KiB          [emitted]                     
                            sprite.svg    4.4 KiB          [emitted]                     
                         sprite.svg.br  912 bytes          [emitted]                     
                         sprite.svg.br  912 bytes          [emitted]                     
-Entrypoint index [big] = ./js/runtime.e644c0.bundle.js ./js/node-modules.e644c0.bundle.js ./js/index.e644c0.bundle.js
+Entrypoint index [big] = ./js/runtime.0b6890.bundle.js ./js/node-modules.0b6890.bundle.js ./js/index.0b6890.bundle.js
  [6] ./node_modules/bootstrap/dist/js/bootstrap-exposed.js 437 bytes {1} [built]
  [6] ./node_modules/bootstrap/dist/js/bootstrap-exposed.js 437 bytes {1} [built]
 [11] ./src/sass/main.scss 1.55 KiB {0} [built]
 [11] ./src/sass/main.scss 1.55 KiB {0} [built]
-[16] ./node_modules/remixicon/icons/Device/signal-wifi-fill.svg 340 bytes {1} [built]
-[17] ./node_modules/remixicon/icons/Device/signal-wifi-3-fill.svg 344 bytes {1} [built]
-[18] ./node_modules/remixicon/icons/Device/signal-wifi-2-fill.svg 344 bytes {1} [built]
-[19] ./node_modules/remixicon/icons/Device/signal-wifi-1-fill.svg 344 bytes {1} [built]
-[20] ./node_modules/remixicon/icons/Device/signal-wifi-line.svg 340 bytes {1} [built]
-[21] ./node_modules/remixicon/icons/Device/battery-line.svg 332 bytes {1} [built]
-[22] ./node_modules/remixicon/icons/Device/battery-low-line.svg 340 bytes {1} [built]
-[23] ./node_modules/remixicon/icons/Device/battery-fill.svg 332 bytes {1} [built]
-[24] ./node_modules/remixicon/icons/Media/headphone-fill.svg 335 bytes {1} [built]
-[25] ./node_modules/remixicon/icons/Device/device-recover-fill.svg 346 bytes {1} [built]
-[26] ./node_modules/remixicon/icons/Device/bluetooth-fill.svg 336 bytes {1} [built]
-[27] ./node_modules/remixicon/icons/Device/bluetooth-connect-fill.svg 352 bytes {1} [built]
-[37] ./src/index.ts + 1 modules 52.6 KiB {0} [built]
+[16] ./node_modules/remixicon/icons/Device/signal-wifi-fill.svg 323 bytes {1} [built]
+[17] ./node_modules/remixicon/icons/Device/signal-wifi-3-fill.svg 327 bytes {1} [built]
+[18] ./node_modules/remixicon/icons/Device/signal-wifi-2-fill.svg 327 bytes {1} [built]
+[19] ./node_modules/remixicon/icons/Device/signal-wifi-1-fill.svg 327 bytes {1} [built]
+[20] ./node_modules/remixicon/icons/Device/signal-wifi-line.svg 323 bytes {1} [built]
+[21] ./node_modules/remixicon/icons/Device/battery-line.svg 315 bytes {1} [built]
+[22] ./node_modules/remixicon/icons/Device/battery-low-line.svg 323 bytes {1} [built]
+[23] ./node_modules/remixicon/icons/Device/battery-fill.svg 315 bytes {1} [built]
+[24] ./node_modules/remixicon/icons/Media/headphone-fill.svg 318 bytes {1} [built]
+[25] ./node_modules/remixicon/icons/Device/device-recover-fill.svg 329 bytes {1} [built]
+[26] ./node_modules/remixicon/icons/Device/bluetooth-fill.svg 319 bytes {1} [built]
+[27] ./node_modules/remixicon/icons/Device/bluetooth-connect-fill.svg 335 bytes {1} [built]
+[37] ./src/index.ts + 1 modules 53.3 KiB {0} [built]
      | ./src/index.ts 1.36 KiB [built]
      | ./src/index.ts 1.36 KiB [built]
-     | ./src/js/custom.js 51.2 KiB [built]
+     | ./src/js/custom.js 51.8 KiB [built]
     + 23 hidden modules
     + 23 hidden modules
 
 
 WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
 WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
 This can impact web performance.
 This can impact web performance.
 Assets: 
 Assets: 
-  ./js/node-modules.e644c0.bundle.js (265 KiB)
+  ./js/node-modules.0b6890.bundle.js (266 KiB)
 
 
 WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
 WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
 Entrypoints:
 Entrypoints:
-  index (497 KiB)
-      ./js/runtime.e644c0.bundle.js
-      ./js/node-modules.e644c0.bundle.js
-      ./js/index.e644c0.bundle.js
+  index (499 KiB)
+      ./js/runtime.0b6890.bundle.js
+      ./js/node-modules.0b6890.bundle.js
+      ./js/index.0b6890.bundle.js
 
 
 
 
 WARNING in webpack performance recommendations: 
 WARNING in webpack performance recommendations: 
@@ -60,8 +60,8 @@ Child html-webpack-plugin for "index.html":
          Asset     Size  Chunks  Chunk Names
          Asset     Size  Chunks  Chunk Names
     index.html  556 KiB       0  
     index.html  556 KiB       0  
     Entrypoint undefined = index.html
     Entrypoint undefined = index.html
-    [0] ./node_modules/html-webpack-plugin/lib/loader.js!./src/index.ejs 21.1 KiB {0} [built]
-    [1] ./node_modules/lodash/lodash.js 530 KiB {0} [built]
+    [0] ./node_modules/html-webpack-plugin/lib/loader.js!./src/index.ejs 20.3 KiB {0} [built]
+    [1] ./node_modules/lodash/lodash.js 531 KiB {0} [built]
     [2] (webpack)/buildin/global.js 472 bytes {0} [built]
     [2] (webpack)/buildin/global.js 472 bytes {0} [built]
     [3] (webpack)/buildin/module.js 497 bytes {0} [built]
     [3] (webpack)/buildin/module.js 497 bytes {0} [built]
 ***********************************/
 ***********************************/

BIN
components/wifi-manager/webapp/webpack/dist/favicon-32x32.png


File diff suppressed because it is too large
+ 0 - 0
components/wifi-manager/webapp/webpack/dist/index.html


BIN
components/wifi-manager/webapp/webpack/dist/index.html.br


BIN
components/wifi-manager/webapp/webpack/dist/index.html.gz


File diff suppressed because it is too large
+ 0 - 0
components/wifi-manager/webapp/webpack/dist/js/index.0b6890.bundle.js


BIN
components/wifi-manager/webapp/webpack/dist/js/index.0b6890.bundle.js.br


BIN
components/wifi-manager/webapp/webpack/dist/js/index.0b6890.bundle.js.gz


File diff suppressed because it is too large
+ 0 - 0
components/wifi-manager/webapp/webpack/dist/js/index.e644c0.bundle.js


BIN
components/wifi-manager/webapp/webpack/dist/js/index.e644c0.bundle.js.br


BIN
components/wifi-manager/webapp/webpack/dist/js/index.e644c0.bundle.js.gz


File diff suppressed because it is too large
+ 7 - 0
components/wifi-manager/webapp/webpack/dist/js/node-modules.0b6890.bundle.js


BIN
components/wifi-manager/webapp/webpack/dist/js/node-modules.0b6890.bundle.js.br


BIN
components/wifi-manager/webapp/webpack/dist/js/node-modules.0b6890.bundle.js.gz


File diff suppressed because it is too large
+ 0 - 7
components/wifi-manager/webapp/webpack/dist/js/node-modules.e644c0.bundle.js


BIN
components/wifi-manager/webapp/webpack/dist/js/node-modules.e644c0.bundle.js.br


BIN
components/wifi-manager/webapp/webpack/dist/js/node-modules.e644c0.bundle.js.gz


+ 0 - 0
components/wifi-manager/webapp/webpack/dist/js/runtime.e644c0.bundle.js → components/wifi-manager/webapp/webpack/dist/js/runtime.0b6890.bundle.js


+ 0 - 0
components/wifi-manager/webapp/webpack/dist/js/runtime.e644c0.bundle.js.br → components/wifi-manager/webapp/webpack/dist/js/runtime.0b6890.bundle.js.br


BIN
components/wifi-manager/webapp/webpack/dist/js/runtime.e644c0.bundle.js.gz → components/wifi-manager/webapp/webpack/dist/js/runtime.0b6890.bundle.js.gz


+ 1 - 1
components/wifi-manager/webapp/webpack/webpack.dev.js

@@ -105,7 +105,7 @@ module.exports = merge(common, {
         contentBase: path.join(__dirname, 'dist'),
         contentBase: path.join(__dirname, 'dist'),
         publicPath: '/',
         publicPath: '/',
         port: 9100,
         port: 9100,
-        host: 'desktop-n8u8515',//your ip address
+        host: '127.0.0.1',//your ip address
         disableHostCheck: true,
         disableHostCheck: true,
         overlay: true,
         overlay: true,
 
 

+ 146 - 1
plugin/SqueezeESP32/Plugin.pm

@@ -3,6 +3,9 @@ package Plugins::SqueezeESP32::Plugin;
 use strict;
 use strict;
 
 
 use base qw(Slim::Plugin::Base);
 use base qw(Slim::Plugin::Base);
+use File::Basename qw(basename);
+use File::Spec::Functions qw(catfile);
+use JSON::XS::VersionOneAndTwo;
 
 
 use Slim::Utils::Prefs;
 use Slim::Utils::Prefs;
 use Slim::Utils::Log;
 use Slim::Utils::Log;
@@ -16,6 +19,12 @@ my $log = Slim::Utils::Log->addLogCategory({
 	'description'  => 'PLUGIN_SQUEEZEESP32',
 	'description'  => 'PLUGIN_SQUEEZEESP32',
 });
 });
 
 
+use constant GITHUB_ASSET_URI => "https://api.github.com/repos/sle118/squeezelite-esp32/releases/assets/";
+use constant GITHUB_DOWNLOAD_URI => "https://github.com/sle118/squeezelite-esp32/releases/download/";
+my $FW_DOWNLOAD_ID_REGEX = qr|plugins/SqueezeESP32/firmware/(-?\d+)|;
+my $FW_DOWNLOAD_REGEX = qr|plugins/SqueezeESP32/firmware/([-a-z0-9-/.]+\.bin)$|i;
+my $FW_FILENAME_REGEX = qr/^squeezelite-esp32-.*\.bin(\.tmp)?$/;
+
 # migrate 'eq' pref, as that's a reserved word and could cause problems in the future
 # migrate 'eq' pref, as that's a reserved word and could cause problems in the future
 $prefs->migrateClient(1, sub {
 $prefs->migrateClient(1, sub {
 	my ($cprefs, $client) = @_;
 	my ($cprefs, $client) = @_;
@@ -48,7 +57,7 @@ sub initPlugin {
 	$class->SUPER::initPlugin(@_);
 	$class->SUPER::initPlugin(@_);
 	# no name can be a subset of others due to a bug in addPlayerClass
 	# no name can be a subset of others due to a bug in addPlayerClass
 	Slim::Networking::Slimproto::addPlayerClass($class, 100, 'squeezeesp32-basic', { client => 'Plugins::SqueezeESP32::Player', display => 'Plugins::SqueezeESP32::Graphics' });
 	Slim::Networking::Slimproto::addPlayerClass($class, 100, 'squeezeesp32-basic', { client => 'Plugins::SqueezeESP32::Player', display => 'Plugins::SqueezeESP32::Graphics' });
-	Slim::Networking::Slimproto::addPlayerClass($class, 101, 'squeezeesp32-graphic', { client => 'Plugins::SqueezeESP32::Player', display => 'Slim::Display::NoDisplay' });		
+	Slim::Networking::Slimproto::addPlayerClass($class, 101, 'squeezeesp32-graphic', { client => 'Plugins::SqueezeESP32::Player', display => 'Slim::Display::NoDisplay' });
 	main::INFOLOG && $log->is_info && $log->info("Added class 100 and 101 for SqueezeESP32");
 	main::INFOLOG && $log->is_info && $log->info("Added class 100 and 101 for SqueezeESP32");
 
 
 	# register a command to set the EQ - without saving the values! Send params as single comma separated list of values
 	# register a command to set the EQ - without saving the values! Send params as single comma separated list of values
@@ -58,6 +67,9 @@ sub initPlugin {
 	Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['newmetadata'] ] );
 	Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['newmetadata'] ] );
 	Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['playlist'], ['open', 'newsong'] ]);
 	Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['playlist'], ['open', 'newsong'] ]);
 	Slim::Control::Request::subscribe( \&onStopClear, [ ['playlist'], ['stop', 'clear'] ]);
 	Slim::Control::Request::subscribe( \&onStopClear, [ ['playlist'], ['stop', 'clear'] ]);
+
+	Slim::Web::Pages->addRawFunction($FW_DOWNLOAD_ID_REGEX, \&handleFirmwareDownload);
+	Slim::Web::Pages->addRawFunction($FW_DOWNLOAD_REGEX, \&handleFirmwareDownloadDirect);
 }
 }
 
 
 sub onStopClear {
 sub onStopClear {
@@ -99,4 +111,137 @@ sub setEQ {
 	$client->send_equalizer(\@eqParams);
 	$client->send_equalizer(\@eqParams);
 }
 }
 
 
+sub handleFirmwareDownload {
+	my ($httpClient, $response) = @_;
+
+	my $request = $response->request;
+
+	my $_errorDownloading = sub {
+		_errorDownloading($httpClient, $response, @_);
+	};
+
+	my $id;
+	if (!defined $request || !(($id) = $request->uri =~ $FW_DOWNLOAD_ID_REGEX)) {
+		return $_errorDownloading->(undef, 'Invalid request', $request->uri, 400);
+	}
+
+	# this is the magic number used on the client to figure out whether the plugin does support download proxying
+	if ($id == -99) {
+		$response->code(204);
+		$response->header('Access-Control-Allow-Origin' => '*');
+
+		$httpClient->send_response($response);
+		return Slim::Web::HTTP::closeHTTPSocket($httpClient);
+	}
+
+	Slim::Networking::SimpleAsyncHTTP->new(
+		sub {
+			my $http = shift;
+			my $content = eval { from_json( $http->content ) };
+
+			if (!$content || !ref $content) {
+				$@ && $log->error("Failed to parse response: $@");
+				return $_errorDownloading->($http);
+			}
+			elsif (!$content->{browser_download_url} || !$content->{name}) {
+				return $_errorDownloading->($http, 'No download URL found');
+			}
+
+			downloadAndStreamFirmware($httpClient, $response, $content->{browser_download_url}, $content->{name});
+		},
+		$_errorDownloading,
+		{
+			timeout => 10,
+			cache => 1,
+			expires => 86400
+		}
+	)->get(GITHUB_ASSET_URI . $id);
+
+	return;
+}
+
+sub handleFirmwareDownloadDirect {
+	my ($httpClient, $response) = @_;
+
+	my $request = $response->request;
+
+	my $_errorDownloading = sub {
+		_errorDownloading($httpClient, $response, @_);
+	};
+
+	my $path;
+	if (!defined $request || !(($path) = $request->uri =~ $FW_DOWNLOAD_REGEX)) {
+		return $_errorDownloading->(undef, 'Invalid request', $request->uri, 400);
+	}
+
+	main::INFOLOG && $log->is_info && $log->info("Requesting firmware from: $path");
+
+	downloadAndStreamFirmware($httpClient, $response, GITHUB_DOWNLOAD_URI . $path);
+}
+
+sub downloadAndStreamFirmware {
+	my ($httpClient, $response, $url, $name) = @_;
+
+	my $_errorDownloading = sub {
+		_errorDownloading($httpClient, $response, @_);
+	};
+
+	$name ||= basename($url);
+
+	if ($name !~ $FW_FILENAME_REGEX) {
+		return $_errorDownloading->(undef, 'Unexpected firmware image name: ' . $name, $url, 400);
+	}
+
+	my $updatesDir = Slim::Utils::OSDetect::dirsFor('updates');
+	my $firmwareFile = catfile($updatesDir, $name);
+	Slim::Utils::Misc::deleteFiles($updatesDir, $FW_FILENAME_REGEX, $firmwareFile);
+
+	if (-f $firmwareFile) {
+		main::INFOLOG && $log->is_info && $log->info("Found cached firmware version");
+		$response->code(200);
+		return Slim::Web::HTTP::sendStreamingFile($httpClient, $response, 'application/octet-stream', $firmwareFile, undef, 1);
+	}
+
+	Slim::Networking::SimpleAsyncHTTP->new(
+		sub {
+			my $http = shift;
+
+			if ($http->code != 200 || !-e "$firmwareFile.tmp") {
+				return $_errorDownloading->($http, $http->mess);
+			}
+
+			rename "$firmwareFile.tmp", $firmwareFile or return $_errorDownloading->($http, "Unable to rename temporary $firmwareFile file" );
+
+			$response->code(200);
+			Slim::Web::HTTP::sendStreamingFile($httpClient, $response, 'application/octet-stream', $firmwareFile, undef, 1);
+		},
+		$_errorDownloading,
+		{
+			saveAs => "$firmwareFile.tmp",
+		}
+	)->get($url);
+
+	return;
+}
+
+sub _errorDownloading {
+	my ($httpClient, $response, $http, $error, $url, $code) = @_;
+
+	$error ||= ($http && $http->error) || 'unknown error';
+	$url   ||= ($http && $http->url) || 'no URL';
+	$code  ||= ($http && $http->code) || 500;
+
+	$log->error(sprintf("Failed to get data from Github: %s (%s)", $error || $http->error, $url));
+
+	$response->headers->remove_content_headers;
+	$response->code($code);
+	$response->content_type('text/plain');
+	$response->header('Connection' => 'close');
+	$response->content('');
+
+	$httpClient->send_response($response);
+	Slim::Web::HTTP::closeHTTPSocket($httpClient);
+};
+
+
 1;
 1;

Some files were not shown because too many files changed in this diff