mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-07 20:17:04 +03:00
Merge pull request #90 from michaelherger/firmware-proxy
Firmware proxy
This commit is contained in:
@@ -1 +1 @@
|
||||
[{"C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\test.js":"1","C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\custom.js":"2"},{"size":4775,"mtime":1608244817341,"results":"3","hashOfConfig":"4"},{"size":61704,"mtime":1618438544167,"results":"5","hashOfConfig":"4"},{"filePath":"6","messages":"7","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"8"},"1275pne",{"filePath":"9","messages":"10","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\test.js",[],[],"C:\\Users\\sle11\\Documents\\VSCode\\squeezelite-esp32\\components\\wifi-manager\\webapp\\src\\js\\custom.js",[]]
|
||||
[{"/Users/mh/SynologyDrive/git/squeezelite-esp32/components/wifi-manager/webapp/src/js/custom.js":"1"},{"size":59815,"mtime":1618633783112,"results":"2","hashOfConfig":"3"},{"filePath":"4","messages":"5","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"15w6qa4","/Users/mh/SynologyDrive/git/squeezelite-esp32/components/wifi-manager/webapp/src/js/custom.js",[]]
|
||||
@@ -1496,7 +1496,8 @@ function checkStatus() {
|
||||
const baseUrl = 'http://' + data.lms_ip + ':' + data.lms_port;
|
||||
prevLMSIP=data.lms_ip;
|
||||
$.ajax({
|
||||
url: baseUrl + '/plugins/SqueezeESP32/firmware/-99',
|
||||
url: baseUrl + '/plugins/SqueezeESP32/firmware/-check.bin',
|
||||
type: 'HEAD',
|
||||
dataType: 'text',
|
||||
cache: false,
|
||||
error: function() {
|
||||
|
||||
@@ -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/index.html.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/index.18c3b7.bundle.js.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/node-modules.18c3b7.bundle.js.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/runtime.18c3b7.bundle.js.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/index.df6830.bundle.js.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/node-modules.df6830.bundle.js.gz BINARY)
|
||||
target_add_binary_data( __idf_wifi-manager ./webapp/webpack/dist/js/runtime.df6830.bundle.js.gz BINARY)
|
||||
|
||||
@@ -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 _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_18c3b7_bundle_js_gz_start[] asm("_binary_index_18c3b7_bundle_js_gz_start");
|
||||
extern const uint8_t _index_18c3b7_bundle_js_gz_end[] asm("_binary_index_18c3b7_bundle_js_gz_end");
|
||||
extern const uint8_t _node_modules_18c3b7_bundle_js_gz_start[] asm("_binary_node_modules_18c3b7_bundle_js_gz_start");
|
||||
extern const uint8_t _node_modules_18c3b7_bundle_js_gz_end[] asm("_binary_node_modules_18c3b7_bundle_js_gz_end");
|
||||
extern const uint8_t _runtime_18c3b7_bundle_js_gz_start[] asm("_binary_runtime_18c3b7_bundle_js_gz_start");
|
||||
extern const uint8_t _runtime_18c3b7_bundle_js_gz_end[] asm("_binary_runtime_18c3b7_bundle_js_gz_end");
|
||||
extern const uint8_t _index_df6830_bundle_js_gz_start[] asm("_binary_index_df6830_bundle_js_gz_start");
|
||||
extern const uint8_t _index_df6830_bundle_js_gz_end[] asm("_binary_index_df6830_bundle_js_gz_end");
|
||||
extern const uint8_t _node_modules_df6830_bundle_js_gz_start[] asm("_binary_node_modules_df6830_bundle_js_gz_start");
|
||||
extern const uint8_t _node_modules_df6830_bundle_js_gz_end[] asm("_binary_node_modules_df6830_bundle_js_gz_end");
|
||||
extern const uint8_t _runtime_df6830_bundle_js_gz_start[] asm("_binary_runtime_df6830_bundle_js_gz_start");
|
||||
extern const uint8_t _runtime_df6830_bundle_js_gz_end[] asm("_binary_runtime_df6830_bundle_js_gz_end");
|
||||
const char * resource_lookups[] = {
|
||||
"/favicon-32x32.png",
|
||||
"/index.html.gz",
|
||||
"/js/index.18c3b7.bundle.js.gz",
|
||||
"/js/node-modules.18c3b7.bundle.js.gz",
|
||||
"/js/runtime.18c3b7.bundle.js.gz",
|
||||
"/js/index.df6830.bundle.js.gz",
|
||||
"/js/node-modules.df6830.bundle.js.gz",
|
||||
"/js/runtime.df6830.bundle.js.gz",
|
||||
""
|
||||
};
|
||||
const uint8_t * resource_map_start[] = {
|
||||
_favicon_32x32_png_start,
|
||||
_index_html_gz_start,
|
||||
_index_18c3b7_bundle_js_gz_start,
|
||||
_node_modules_18c3b7_bundle_js_gz_start,
|
||||
_runtime_18c3b7_bundle_js_gz_start
|
||||
_index_df6830_bundle_js_gz_start,
|
||||
_node_modules_df6830_bundle_js_gz_start,
|
||||
_runtime_df6830_bundle_js_gz_start
|
||||
};
|
||||
const uint8_t * resource_map_end[] = {
|
||||
_favicon_32x32_png_end,
|
||||
_index_html_gz_end,
|
||||
_index_18c3b7_bundle_js_gz_end,
|
||||
_node_modules_18c3b7_bundle_js_gz_end,
|
||||
_runtime_18c3b7_bundle_js_gz_end
|
||||
_index_df6830_bundle_js_gz_end,
|
||||
_node_modules_df6830_bundle_js_gz_end,
|
||||
_runtime_df6830_bundle_js_gz_end
|
||||
};
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
/***********************************
|
||||
webpack_headers
|
||||
Hash: 18c3b78fe9dd6db2c31d
|
||||
Hash: df683065b9a62ef5a0ce
|
||||
Version: webpack 4.46.0
|
||||
Time: 8782ms
|
||||
Built at: 2021-04-21 12 h 01 min 40 s
|
||||
Time: 2739ms
|
||||
Built at: 26.04.2021 07:00:49
|
||||
Asset Size Chunks Chunk Names
|
||||
./js/index.18c3b7.bundle.js 232 KiB 0 [emitted] [immutable] index
|
||||
./js/index.18c3b7.bundle.js.br 32.5 KiB [emitted]
|
||||
./js/index.18c3b7.bundle.js.gz 41.9 KiB [emitted]
|
||||
./js/node-modules.18c3b7.bundle.js 266 KiB 1 [emitted] [immutable] [big] node-modules
|
||||
./js/node-modules.18c3b7.bundle.js.br 76.3 KiB [emitted]
|
||||
./js/node-modules.18c3b7.bundle.js.gz 88.7 KiB [emitted]
|
||||
./js/runtime.18c3b7.bundle.js 1.46 KiB 2 [emitted] [immutable] runtime
|
||||
./js/runtime.18c3b7.bundle.js.br 644 bytes [emitted]
|
||||
./js/runtime.18c3b7.bundle.js.gz 722 bytes [emitted]
|
||||
./js/index.df6830.bundle.js 232 KiB 0 [emitted] [immutable] index
|
||||
./js/index.df6830.bundle.js.br 32.5 KiB [emitted]
|
||||
./js/index.df6830.bundle.js.gz 41.9 KiB [emitted]
|
||||
./js/node-modules.df6830.bundle.js 266 KiB 1 [emitted] [immutable] [big] node-modules
|
||||
./js/node-modules.df6830.bundle.js.br 76.3 KiB [emitted]
|
||||
./js/node-modules.df6830.bundle.js.gz 88.7 KiB [emitted]
|
||||
./js/runtime.df6830.bundle.js 1.46 KiB 2 [emitted] [immutable] runtime
|
||||
./js/runtime.df6830.bundle.js.br 644 bytes [emitted]
|
||||
./js/runtime.df6830.bundle.js.gz 722 bytes [emitted]
|
||||
favicon-32x32.png 634 bytes [emitted]
|
||||
index.html 21.7 KiB [emitted]
|
||||
index.html.br 4.74 KiB [emitted]
|
||||
index.html.gz 5.75 KiB [emitted]
|
||||
sprite.svg 4.4 KiB [emitted]
|
||||
sprite.svg.br 898 bytes [emitted]
|
||||
Entrypoint index [big] = ./js/runtime.18c3b7.bundle.js ./js/node-modules.18c3b7.bundle.js ./js/index.18c3b7.bundle.js
|
||||
Entrypoint index [big] = ./js/runtime.df6830.bundle.js ./js/node-modules.df6830.bundle.js ./js/index.df6830.bundle.js
|
||||
[6] ./node_modules/bootstrap/dist/js/bootstrap-exposed.js 437 bytes {1} [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]
|
||||
[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]
|
||||
[38] ./src/index.ts + 1 modules 62.5 KiB {0} [built]
|
||||
| ./src/index.ts 1.4 KiB [built]
|
||||
| ./src/js/custom.js 61 KiB [built]
|
||||
@@ -43,14 +43,14 @@ Entrypoint index [big] = ./js/runtime.18c3b7.bundle.js ./js/node-modules.18c3b7.
|
||||
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
|
||||
This can impact web performance.
|
||||
Assets:
|
||||
./js/node-modules.18c3b7.bundle.js (266 KiB)
|
||||
./js/node-modules.df6830.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.
|
||||
Entrypoints:
|
||||
index (499 KiB)
|
||||
./js/runtime.18c3b7.bundle.js
|
||||
./js/node-modules.18c3b7.bundle.js
|
||||
./js/index.18c3b7.bundle.js
|
||||
./js/runtime.df6830.bundle.js
|
||||
./js/node-modules.df6830.bundle.js
|
||||
./js/index.df6830.bundle.js
|
||||
|
||||
|
||||
WARNING in webpack performance recommendations:
|
||||
@@ -58,9 +58,9 @@ You can limit the size of your bundles by using import() or require.ensure to la
|
||||
For more info visit https://webpack.js.org/guides/code-splitting/
|
||||
Child html-webpack-plugin for "index.html":
|
||||
Asset Size Chunks Chunk Names
|
||||
index.html 560 KiB 0
|
||||
index.html 559 KiB 0
|
||||
Entrypoint undefined = index.html
|
||||
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./src/index.ejs 23.9 KiB {0} [built]
|
||||
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./src/index.ejs 22.9 KiB {0} [built]
|
||||
[1] ./node_modules/lodash/lodash.js 531 KiB {0} [built]
|
||||
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
|
||||
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
components/wifi-manager/webapp/webpack/dist/js/index.df6830.bundle.js.br
vendored
Normal file
BIN
components/wifi-manager/webapp/webpack/dist/js/index.df6830.bundle.js.br
vendored
Normal file
Binary file not shown.
BIN
components/wifi-manager/webapp/webpack/dist/js/index.df6830.bundle.js.gz
vendored
Normal file
BIN
components/wifi-manager/webapp/webpack/dist/js/index.df6830.bundle.js.gz
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -13,25 +13,74 @@ use constant FIRMWARE_POLL_INTERVAL => 3600 * (5 + rand());
|
||||
use constant GITHUB_RELEASES_URI => "https://api.github.com/repos/sle118/squeezelite-esp32/releases";
|
||||
use constant GITHUB_ASSET_URI => GITHUB_RELEASES_URI . "/assets/";
|
||||
use constant GITHUB_DOWNLOAD_URI => "https://github.com/sle118/squeezelite-esp32/releases/download/";
|
||||
my $FW_DOWNLOAD_ID_REGEX = qr|plugins/SqueezeESP32/firmware/(-?\d+)|;
|
||||
use constant ESP32_STATUS_URI => "http://%s/status.json";
|
||||
|
||||
my $FW_DOWNLOAD_REGEX = qr|plugins/SqueezeESP32/firmware/([-a-z0-9-/.]+\.bin)$|i;
|
||||
my $FW_CUSTOM_REGEX = qr/^((?:squeezelite-esp32-)?custom\.bin)$/;
|
||||
my $FW_FILENAME_REGEX = qr/^squeezelite-esp32-.*\.bin(\.tmp)?$/;
|
||||
my $FW_TAG_REGEX = qr/\/(ESP32-A1S|SqueezeAmp|I2S-4MFlash)\.(16|32)\.(\d+)\.(.*)\//;
|
||||
my $FW_TAG_REGEX = qr/\b(ESP32-A1S|SqueezeAmp|I2S-4MFlash)\.(16|32)\.(\d+)\.([-a-zA-Z0-9]+)\b/;
|
||||
|
||||
use constant MAX_FW_IMAGE_SIZE => 10 * 1024 * 1024;
|
||||
|
||||
my $prefs = preferences('plugin.squeezeesp32');
|
||||
my $log = logger('plugin.squeezeesp32');
|
||||
|
||||
my $initialized;
|
||||
|
||||
sub init {
|
||||
Slim::Web::Pages->addRawFunction($FW_DOWNLOAD_ID_REGEX, \&handleFirmwareDownload);
|
||||
Slim::Web::Pages->addRawFunction($FW_DOWNLOAD_REGEX, \&handleFirmwareDownloadDirect);
|
||||
my ($client) = @_;
|
||||
|
||||
if (!$initialized) {
|
||||
$initialized = 1;
|
||||
Slim::Web::Pages->addRawFunction($FW_DOWNLOAD_REGEX, \&handleFirmwareDownload);
|
||||
Slim::Web::Pages->addRawFunction('plugins/SqueezeESP32/firmware/upload', \&handleFirmwareUpload);
|
||||
}
|
||||
|
||||
# start checking for firmware updates
|
||||
Slim::Utils::Timers::setTimer(undef, Time::HiRes::time() + 30 + rand(30), \&prefetchFirmware);
|
||||
Slim::Utils::Timers::setTimer($client, Time::HiRes::time() + 3.0 + rand(3.0), \&initFirmwareDownload);
|
||||
}
|
||||
|
||||
sub initFirmwareDownload {
|
||||
my ($client, $cb) = @_;
|
||||
|
||||
Slim::Utils::Timers::killTimers($client, \&initFirmwareDownload);
|
||||
|
||||
return unless preferences('server')->get('checkVersion') || $cb;
|
||||
|
||||
Slim::Networking::SimpleAsyncHTTP->new(
|
||||
sub {
|
||||
my $http = shift;
|
||||
my $content = eval { from_json( $http->content ) };
|
||||
|
||||
if ($content && ref $content) {
|
||||
my $releaseInfo = getFirmwareTag($content->{version});
|
||||
|
||||
if ($releaseInfo && ref $releaseInfo) {
|
||||
prefetchFirmware($releaseInfo, $cb);
|
||||
}
|
||||
else {
|
||||
$cb->() if $cb;
|
||||
}
|
||||
}
|
||||
},
|
||||
sub {
|
||||
my ($http, $error) = @_;
|
||||
$log->error("Failed to get releases from Github: $error");
|
||||
|
||||
$cb->() if $cb;
|
||||
},
|
||||
{
|
||||
timeout => 10
|
||||
}
|
||||
)->get(sprintf(ESP32_STATUS_URI, $client->ip));
|
||||
|
||||
Slim::Utils::Timers::setTimer($client, Time::HiRes::time() + FIRMWARE_POLL_INTERVAL, \&initFirmwareDownload);
|
||||
}
|
||||
|
||||
sub prefetchFirmware {
|
||||
Slim::Utils::Timers::killTimers(undef, \&prefetchFirmware);
|
||||
my $releaseInfo = $prefs->get('lastReleaseTagUsed');
|
||||
my ($releaseInfo, $cb) = @_;
|
||||
|
||||
return unless $releaseInfo;
|
||||
|
||||
Slim::Networking::SimpleAsyncHTTP->new(
|
||||
sub {
|
||||
@@ -54,6 +103,9 @@ sub prefetchFirmware {
|
||||
}
|
||||
}
|
||||
|
||||
my $customFwUrl = _urlFromPath('custom.bin') if $cb && -f _customFirmwareFile();
|
||||
|
||||
if ( ($url && $url =~ /^https?/) || $customFwUrl ) {
|
||||
downloadFirmwareFile(sub {
|
||||
main::INFOLOG && $log->is_info && $log->info("Pre-cached firmware file: " . $_[0]);
|
||||
}, sub {
|
||||
@@ -62,8 +114,10 @@ sub prefetchFirmware {
|
||||
$url ||= ($http && $http->url) || 'no URL';
|
||||
|
||||
$log->error(sprintf("Failed to get firmware image from Github: %s (%s)", $error || $http->error, $url));
|
||||
}, $url) if $url && $url =~ /^https?/;
|
||||
}, $url) if $url;
|
||||
|
||||
$cb->($releaseInfo, _gh2lmsUrl($url), $customFwUrl) if $cb;
|
||||
}
|
||||
},
|
||||
sub {
|
||||
my ($http, $error) = @_;
|
||||
@@ -74,9 +128,23 @@ sub prefetchFirmware {
|
||||
cache => 1,
|
||||
expires => 3600
|
||||
}
|
||||
)->get(GITHUB_RELEASES_URI) if $releaseInfo;
|
||||
)->get(GITHUB_RELEASES_URI);
|
||||
}
|
||||
|
||||
Slim::Utils::Timers::setTimer(undef, Time::HiRes::time() + FIRMWARE_POLL_INTERVAL, \&prefetchFirmware);
|
||||
sub _gh2lmsUrl {
|
||||
my ($url) = @_;
|
||||
my $ghPrefix = GITHUB_DOWNLOAD_URI;
|
||||
my $baseUrl = Slim::Utils::Network::serverURL();
|
||||
$url =~ s/$ghPrefix/$baseUrl\/plugins\/SqueezeESP32\/firmware\//;
|
||||
return $url;
|
||||
}
|
||||
|
||||
sub _urlFromPath {
|
||||
return sprintf('%s/plugins/SqueezeESP32/firmware/%s', Slim::Utils::Network::serverURL(), basename(shift));
|
||||
}
|
||||
|
||||
sub _customFirmwareFile {
|
||||
return catfile(scalar Slim::Utils::OSDetect::dirsFor('updates'), 'squeezelite-esp32-custom.bin');
|
||||
}
|
||||
|
||||
sub handleFirmwareDownload {
|
||||
@@ -88,13 +156,13 @@ sub handleFirmwareDownload {
|
||||
_errorDownloading($httpClient, $response, @_);
|
||||
};
|
||||
|
||||
my $id;
|
||||
if (!defined $request || !(($id) = $request->uri =~ $FW_DOWNLOAD_ID_REGEX)) {
|
||||
my $path;
|
||||
if (!defined $request || !(($path) = $request->uri =~ $FW_DOWNLOAD_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) {
|
||||
if ($path eq '-check.bin' && $request->method eq 'HEAD') {
|
||||
$response->code(204);
|
||||
$response->header('Access-Control-Allow-Origin' => '*');
|
||||
|
||||
@@ -102,48 +170,20 @@ sub handleFirmwareDownload {
|
||||
return Slim::Web::HTTP::closeHTTPSocket($httpClient);
|
||||
}
|
||||
|
||||
Slim::Networking::SimpleAsyncHTTP->new(
|
||||
sub {
|
||||
my $http = shift;
|
||||
my $content = eval { from_json( $http->content ) };
|
||||
if ($path =~ $FW_CUSTOM_REGEX) {
|
||||
my $firmwareFile = _customFirmwareFile();
|
||||
|
||||
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');
|
||||
if (! -f $firmwareFile) {
|
||||
main::INFOLOG && $log->is_info && $log->info("Failed to find custom firmware build: $firmwareFile");
|
||||
$response->code(404);
|
||||
$httpClient->send_response($response);
|
||||
return Slim::Web::HTTP::closeHTTPSocket($httpClient);
|
||||
}
|
||||
|
||||
downloadFirmwareFile(sub {
|
||||
my $firmwareFile = shift;
|
||||
main::INFOLOG && $log->is_info && $log->info("Getting custom firmware build");
|
||||
|
||||
$response->code(200);
|
||||
Slim::Web::HTTP::sendStreamingFile($httpClient, $response, 'application/octet-stream', $firmwareFile, undef, 1);
|
||||
}, $_errorDownloading, $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);
|
||||
return Slim::Web::HTTP::sendStreamingFile($httpClient, $response, 'application/octet-stream', $firmwareFile, undef, 1);
|
||||
}
|
||||
|
||||
main::INFOLOG && $log->is_info && $log->info("Requesting firmware from: $path");
|
||||
@@ -159,7 +199,7 @@ sub downloadFirmwareFile {
|
||||
my ($cb, $ecb, $url, $name) = @_;
|
||||
|
||||
# keep track of the last firmware we requested, to prefetch it in the future
|
||||
_getFirmwareTag($url);
|
||||
my $releaseInfo = getFirmwareTag($url);
|
||||
|
||||
$name ||= basename($url);
|
||||
|
||||
@@ -167,9 +207,21 @@ sub downloadFirmwareFile {
|
||||
return $ecb->(undef, 'Unexpected firmware image name: ' . $name, $url, 400);
|
||||
}
|
||||
|
||||
my $updatesDir = Slim::Utils::OSDetect::dirsFor('updates');
|
||||
my $updatesDir = _getTempDir();
|
||||
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 uploaded firmware file $name");
|
||||
return $cb->($firmwareFile);
|
||||
}
|
||||
|
||||
$updatesDir = Slim::Utils::OSDetect::dirsFor('updates');
|
||||
$firmwareFile = catfile($updatesDir, $name);
|
||||
|
||||
if ($releaseInfo) {
|
||||
my $fileMatchRegex = join('-', '', $releaseInfo->{branch}, $releaseInfo->{model}, $releaseInfo->{res});
|
||||
Slim::Utils::Misc::deleteFiles($updatesDir, $fileMatchRegex, $firmwareFile);
|
||||
}
|
||||
|
||||
if (-f $firmwareFile) {
|
||||
main::INFOLOG && $log->is_info && $log->info("Found cached firmware file");
|
||||
@@ -188,7 +240,11 @@ sub downloadFirmwareFile {
|
||||
|
||||
return $cb->($firmwareFile);
|
||||
},
|
||||
$ecb,
|
||||
sub {
|
||||
my ($http, $error) = @_;
|
||||
$http->code(404) if $error =~ /\b404\b/;
|
||||
$ecb->(@_);
|
||||
},
|
||||
{
|
||||
saveAs => "$firmwareFile.tmp",
|
||||
}
|
||||
@@ -197,10 +253,10 @@ sub downloadFirmwareFile {
|
||||
return;
|
||||
}
|
||||
|
||||
sub _getFirmwareTag {
|
||||
my ($url) = @_;
|
||||
sub getFirmwareTag {
|
||||
my ($info) = @_;
|
||||
|
||||
if (my ($model, $resolution, $version, $branch) = $url =~ $FW_TAG_REGEX) {
|
||||
if (my ($model, $resolution, $version, $branch) = $info =~ $FW_TAG_REGEX) {
|
||||
my $releaseInfo = {
|
||||
model => $model,
|
||||
res => $resolution,
|
||||
@@ -208,8 +264,6 @@ sub _getFirmwareTag {
|
||||
branch => $branch
|
||||
};
|
||||
|
||||
$prefs->set('lastReleaseTagUsed', $releaseInfo);
|
||||
|
||||
return $releaseInfo;
|
||||
}
|
||||
}
|
||||
@@ -233,5 +287,123 @@ sub _errorDownloading {
|
||||
Slim::Web::HTTP::closeHTTPSocket($httpClient);
|
||||
};
|
||||
|
||||
sub handleFirmwareUpload {
|
||||
my ($httpClient, $response) = @_;
|
||||
|
||||
my $request = $response->request;
|
||||
my $result = {};
|
||||
|
||||
my $t = Time::HiRes::time();
|
||||
|
||||
main::INFOLOG && $log->is_info && $log->info("New firmware image to upload. Size: " . formatMB($request->content_length));
|
||||
|
||||
if ( $request->method !~ /HEAD|OPTIONS|POST/ ) {
|
||||
$log->error("Invalid HTTP verb: " . $request->method);
|
||||
$result = {
|
||||
error => 'Invalid request.',
|
||||
code => 400,
|
||||
};
|
||||
}
|
||||
elsif ( $request->content_length > MAX_FW_IMAGE_SIZE ) {
|
||||
$log->error("Upload data is too large: " . $request->content_length);
|
||||
$result = {
|
||||
error => string('PLUGIN_DNDPLAY_FILE_TOO_LARGE', formatMB($request->content_length), formatMB(MAX_FW_IMAGE_SIZE)),
|
||||
code => 413,
|
||||
};
|
||||
}
|
||||
else {
|
||||
my $ct = $request->header('Content-Type');
|
||||
my ($boundary) = $ct =~ /boundary=(.*)/;
|
||||
|
||||
my ($uploadedFwFh, $filename, $inUpload, $buf);
|
||||
|
||||
# open a pseudo-filehandle to the uploaded data ref for further processing
|
||||
open TEMP, '<', $request->content_ref;
|
||||
|
||||
while (<TEMP>) {
|
||||
if ( Time::HiRes::time - $t > 0.2 ) {
|
||||
main::idleStreams();
|
||||
$t = Time::HiRes::time();
|
||||
}
|
||||
|
||||
# a new part starts - reset some variables
|
||||
if ( /--\Q$boundary\E/i ) {
|
||||
$filename = '';
|
||||
|
||||
if ($buf) {
|
||||
$buf =~ s/\r\n$//;
|
||||
print $uploadedFwFh $buf if $uploadedFwFh;
|
||||
}
|
||||
|
||||
close $uploadedFwFh if $uploadedFwFh;
|
||||
$inUpload = undef;
|
||||
}
|
||||
|
||||
# write data to file handle
|
||||
elsif ( $inUpload && $uploadedFwFh ) {
|
||||
print $uploadedFwFh $buf if defined $buf;
|
||||
$buf = $_;
|
||||
}
|
||||
|
||||
# we got an uploaded file name
|
||||
elsif ( /filename="(.+?)"/i ) {
|
||||
$filename = $1;
|
||||
main::INFOLOG && $log->is_info && $log->info("New file to upload: $filename")
|
||||
}
|
||||
|
||||
# we got the separator after the upload file name: file data comes next. Open a file handle to write the data to.
|
||||
elsif ( $filename && /^\s*$/ ) {
|
||||
$inUpload = 1;
|
||||
|
||||
$uploadedFwFh = File::Temp->new(
|
||||
DIR => _getTempDir(),
|
||||
SUFFIX => '.bin',
|
||||
TEMPLATE => 'squeezelite-esp32-upload-XXXXXX',
|
||||
UNLINK => 0,
|
||||
) or $log->warn("Failed to open file: $@");
|
||||
|
||||
binmode $uploadedFwFh;
|
||||
|
||||
# remove file after a few minutes
|
||||
Slim::Utils::Timers::setTimer($uploadedFwFh->filename, Time::HiRes::time() + 15 * 60, sub { unlink shift });
|
||||
}
|
||||
}
|
||||
|
||||
close TEMP;
|
||||
close $uploadedFwFh if $uploadedFwFh;
|
||||
|
||||
main::idleStreams();
|
||||
|
||||
if (!$result->{error}) {
|
||||
$result->{url} = _urlFromPath($uploadedFwFh->filename);
|
||||
$result->{size} = -s $uploadedFwFh->filename;
|
||||
}
|
||||
}
|
||||
|
||||
$log->error($result->{error}) if $result->{error};
|
||||
|
||||
my $content = to_json($result);
|
||||
$response->header( 'Content-Length' => length($content) );
|
||||
$response->code($result->{code} || 200);
|
||||
$response->header('Connection' => 'close');
|
||||
$response->content_type('application/json');
|
||||
|
||||
Slim::Web::HTTP::addHTTPResponse( $httpClient, $response, \$content );
|
||||
}
|
||||
|
||||
my $tempDir;
|
||||
sub _getTempDir {
|
||||
return $tempDir if $tempDir;
|
||||
|
||||
eval { $tempDir = Slim::Utils::Misc::getTempDir() }; # LMS 8.2+ only
|
||||
$tempDir ||= File::Temp::tempdir(CLEANUP => 1, DIR => preferences('server')->get('cachedir'));
|
||||
|
||||
return $tempDir;
|
||||
}
|
||||
|
||||
sub formatMB {
|
||||
return Slim::Utils::Misc::delimitThousands(int($_[0] / 1024 / 1024)) . 'MB';
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
@@ -1,5 +1,21 @@
|
||||
[% PROCESS settings/header.html %]
|
||||
|
||||
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_FIRMWARE" desc="" %]
|
||||
<div><a href="http://[% player_ip %]" target="_blank">[% "PLUGIN_SQUEEZEESP32_PLAYERSETTINGS" | string %] ([% player_ip %])</a></div>
|
||||
[% IF fwUpdateAvailable %]
|
||||
<div>
|
||||
<input type="submit" name="installUpdate" class="stdclick" value="[% "CONTROLPANEL_INSTALL_UPDATE" | string %]"/>
|
||||
[% fwUpdateAvailable %]
|
||||
</div>
|
||||
[% END %]
|
||||
[% IF fwCustomUpdateAvailable %]
|
||||
<div>
|
||||
<input type="submit" name="installCustomUpdate" class="stdclick" value="[% "CONTROLPANEL_INSTALL_UPDATE" | string %]"/>
|
||||
[% fwCustomUpdateAvailable | string %]
|
||||
</div>
|
||||
[% END %]
|
||||
[% END %]
|
||||
|
||||
[% IF prefs.pref_width %]
|
||||
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_WIDTH" desc="PLUGIN_SQUEEZEESP32_WIDTH_DESC" %]
|
||||
<!--<input type="text" readonly class="stdedit" name="pref_width" id="width" value="[% prefs.pref_width %]" size="3">-->
|
||||
|
||||
@@ -9,6 +9,8 @@ use List::Util qw(min);
|
||||
use Slim::Utils::Log;
|
||||
use Slim::Utils::Prefs;
|
||||
|
||||
use Plugins::SqueezeESP32::FirmwareHelper;
|
||||
|
||||
my $sprefs = preferences('server');
|
||||
my $prefs = preferences('plugin.squeezeesp32');
|
||||
my $log = logger('plugin.squeezeesp32');
|
||||
@@ -95,6 +97,8 @@ sub init {
|
||||
}
|
||||
|
||||
$client->SUPER::init(@_);
|
||||
Plugins::SqueezeESP32::FirmwareHelper::init($client);
|
||||
|
||||
main::INFOLOG && $log->is_info && $log->info("SqueezeESP player connected: " . $client->id);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package Plugins::SqueezeESP32::PlayerSettings;
|
||||
|
||||
use strict;
|
||||
use base qw(Slim::Web::Settings);
|
||||
use JSON::XS::VersionOneAndTwo;
|
||||
use List::Util qw(first);
|
||||
|
||||
use Slim::Utils::Log;
|
||||
@@ -36,7 +37,7 @@ sub prefs {
|
||||
}
|
||||
|
||||
sub handler {
|
||||
my ($class, $client, $paramRef) = @_;
|
||||
my ($class, $client, $paramRef, $callback, @args) = @_;
|
||||
|
||||
my ($cprefs, @prefs) = $class->prefs($client);
|
||||
|
||||
@@ -76,7 +77,7 @@ sub handler {
|
||||
|
||||
}
|
||||
|
||||
if ($client->depth == 16) {
|
||||
if ($client->can('depth') && $client->depth == 16) {
|
||||
my $equalizer = $cprefs->get('equalizer');
|
||||
for my $i (0 .. $#{$equalizer}) {
|
||||
$equalizer->[$i] = $paramRef->{"pref_equalizer.$i"} || 0;
|
||||
@@ -93,9 +94,46 @@ sub handler {
|
||||
$paramRef->{'pref_artwork'} = $cprefs->get('artwork');
|
||||
}
|
||||
|
||||
$paramRef->{'pref_equalizer'} = $cprefs->get('equalizer') if $client->depth == 16;
|
||||
$paramRef->{'pref_equalizer'} = $cprefs->get('equalizer') if $client->can('depth') && $client->depth == 16;
|
||||
$paramRef->{'player_ip'} = $client->ip;
|
||||
|
||||
return $class->SUPER::handler($client, $paramRef);
|
||||
Plugins::SqueezeESP32::FirmwareHelper::initFirmwareDownload($client, sub {
|
||||
my ($currentFWInfo, $newFWUrl, $customFwUrl) = @_;
|
||||
|
||||
$currentFWInfo ||= {};
|
||||
my $newFWInfo = Plugins::SqueezeESP32::FirmwareHelper::getFirmwareTag($newFWUrl) || {};
|
||||
|
||||
if ($paramRef->{installUpdate} || $paramRef->{installCustomUpdate}) {
|
||||
my $http = Slim::Networking::SimpleAsyncHTTP->new(sub {
|
||||
main::INFOLOG && $log->is_info && $log->info("Firmware update triggered");
|
||||
}, sub {
|
||||
main::INFOLOG && $log->is_info && $log->info("Failed to trigger firmware update");
|
||||
main::DEBUGLOG && $log->is_debug && $log->debug(Data::Dump::dump(@_));
|
||||
})->post(sprintf('http://%s/config.json', $client->ip), to_json({
|
||||
timestamp => int(Time::HiRes::time() * 1000) * 1,
|
||||
config => {
|
||||
fwurl => {
|
||||
value => $paramRef->{installCustomUpdate} ? $customFwUrl : $newFWUrl,
|
||||
type => 33
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
else {
|
||||
if ($currentFWInfo->{version} && $newFWInfo->{version} && $currentFWInfo->{version} > $newFWInfo->{version}) {
|
||||
main::INFOLOG && $log->is_info && $log->info("There's an update for your SqueezeESP32 player: $newFWUrl");
|
||||
$paramRef->{fwUpdateAvailable} = sprintf($client->string('PLUGIN_SQUEEZEESP32_FIRMWARE_AVAILABLE'), $newFWInfo->{version}, $currentFWInfo->{version});
|
||||
}
|
||||
if ($customFwUrl) {
|
||||
main::INFOLOG && $log->is_info && $log->info("There's a custom firmware for your SqueezeESP32 player: $customFwUrl");
|
||||
$paramRef->{fwCustomUpdateAvailable} = 'PLUGIN_SQUEEZEESP32_CUSTOM_FIRMWARE_AVAILABLE';
|
||||
}
|
||||
}
|
||||
|
||||
$callback->( $client, $paramRef, $class->SUPER::handler($client, $paramRef), @args );
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -8,8 +8,6 @@ use Slim::Utils::Prefs;
|
||||
use Slim::Utils::Log;
|
||||
use Slim::Web::ImageProxy;
|
||||
|
||||
use Plugins::SqueezeESP32::FirmwareHelper;
|
||||
|
||||
my $prefs = preferences('plugin.squeezeesp32');
|
||||
|
||||
my $log = Slim::Utils::Log->addLogCategory({
|
||||
@@ -39,12 +37,13 @@ $prefs->setChange(sub {
|
||||
sub initPlugin {
|
||||
my $class = shift;
|
||||
|
||||
# enable the following to test the firmware downloading code without a SqueezeliteESP32 player
|
||||
# require Plugins::SqueezeESP32::FirmwareHelper;
|
||||
# Plugins::SqueezeESP32::FirmwareHelper::init();
|
||||
|
||||
if ( main::WEBUI ) {
|
||||
require Plugins::SqueezeESP32::PlayerSettings;
|
||||
Plugins::SqueezeESP32::PlayerSettings->new;
|
||||
|
||||
# require Plugins::SqueezeESP32::Settings;
|
||||
# Plugins::SqueezeESP32::Settings->new;
|
||||
}
|
||||
|
||||
$class->SUPER::initPlugin(@_);
|
||||
@@ -60,8 +59,6 @@ sub initPlugin {
|
||||
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['newmetadata'] ] );
|
||||
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['playlist'], ['open', 'newsong'] ]);
|
||||
Slim::Control::Request::subscribe( \&onStopClear, [ ['playlist'], ['stop', 'clear'] ]);
|
||||
|
||||
Plugins::SqueezeESP32::FirmwareHelper->init();
|
||||
}
|
||||
|
||||
sub onStopClear {
|
||||
|
||||
@@ -21,6 +21,17 @@ PLUGIN_SQUEEZEESP32_PLAYERSETTINGS
|
||||
DE ESP32 Einstellungen
|
||||
EN ESP32 settings
|
||||
|
||||
PLUGIN_SQUEEZEESP32_FIRMWARE
|
||||
EN Firmware
|
||||
|
||||
PLUGIN_SQUEEZEESP32_FIRMWARE_AVAILABLE
|
||||
DE Es steht eine neue Firmware Version v%s zur Verfügung (aktuell installiert: v%s).
|
||||
EN A new firmware version v%s is available (currently installed: v%s).
|
||||
|
||||
PLUGIN_SQUEEZEESP32_CUSTOM_FIRMWARE_AVAILABLE
|
||||
DE Es steht eine benutzerdefinierte Firmware Version zur Verfügung.
|
||||
EN A custom firmware image is available for installation.
|
||||
|
||||
PLUGIN_SQUEEZEESP32_WIDTH
|
||||
DE Displaybreite
|
||||
EN Screen width
|
||||
|
||||
Reference in New Issue
Block a user