From e4a4b232bebd69b5e75de5d921cf937ba840ce90 Mon Sep 17 00:00:00 2001 From: KIMB-technologies Date: Thu, 14 Mar 2024 22:50:35 +0100 Subject: [PATCH 1/3] Resize huge logos for display --- php/classes/RadioLogo.php | 53 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/php/classes/RadioLogo.php b/php/classes/RadioLogo.php index f985d94..507c87d 100644 --- a/php/classes/RadioLogo.php +++ b/php/classes/RadioLogo.php @@ -83,11 +83,12 @@ private function fetchLogo(string $logo) : bool { return false; } + $tmpName = tempnam(sys_get_temp_dir(), 'conv'); if($mimetype == 'image/svg+xml'){ if( - self::svg2png($filename, $filename . '.cv') + self::svg2png($filename, $tmpName) && - @rename($filename . '.cv', $filename) + @rename($tmpName, $filename) ){ return true; } @@ -97,6 +98,11 @@ private function fetchLogo(string $logo) : bool { } } else { + + // resize file (radio does not like huge images) + self::resize($filename, $mimetype, $tmpName); + @rename($tmpName, $filename); + return true; } } @@ -107,6 +113,7 @@ private static function svg2png(string $inputSVG, string $outputPNG) : bool { '--width', '256', '--height', '256', '--keep-aspect-ratio', + '--background-color', 'white', '--format', 'png', '-o', '"'.$outputPNG.'"', '"'.$inputSVG.'"' @@ -114,6 +121,48 @@ private static function svg2png(string $inputSVG, string $outputPNG) : bool { return exec(implode(' ', $command)) !== false; } + private static function imageDimensions(string $file) : array { + $info = exec('file --brief "'.$file.'"'); + if(preg_match(',(\d+)x(\d+),', str_replace(' ', '', $info), $matches) === 1){ + $width = intval($matches[1]); + $height = intval($matches[2]); + + return [$width, $height]; + } + else{ + return [0, 0]; + } + } + + private static function resize(string $inputFile, string $inputMime, string $outputPNG) : bool { + // determine image dimensions + list($width, $height) = self::imageDimensions($inputFile); + + // error + if($width == 0 || $height == 0){ + return false; + } + + // do not resize if smaller than 256 px + if( $width <= 256 && $height <= 256 ){ + return true; + } + + // create an svg with image + $svgFile = " + + "; + + // write to tmp + $inputSVG = tempnam(sys_get_temp_dir(), 'svg'); + file_put_contents($inputSVG, $svgFile); + + // create small png + return self::svg2png($inputSVG, $outputPNG); + } + } ?> \ No newline at end of file From 88a152d49b2bdc17aa2c47adb07043add7a62b54 Mon Sep 17 00:00:00 2001 From: KIMB-technologies Date: Fri, 15 Mar 2024 20:58:06 +0100 Subject: [PATCH 2/3] Optim. Image processing, NC Logos --- php/classes/PodcastLoader.php | 35 ++++++++++++++++++++++++++--------- php/classes/RadioLogo.php | 31 ++++++++++++++++++------------- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/php/classes/PodcastLoader.php b/php/classes/PodcastLoader.php index 6f6d959..354d5f2 100644 --- a/php/classes/PodcastLoader.php +++ b/php/classes/PodcastLoader.php @@ -42,6 +42,7 @@ private static function loadFromNextcloud( string $url ) : array { $useindex = !empty($matches[2]); // used index.php to access files? $share = $matches[3]; // the token/ name of share + // do webdav request $cont = stream_context_create( array( "http" => array( 'method' => "PROPFIND", @@ -49,8 +50,12 @@ private static function loadFromNextcloud( string $url ) : array { ) )); $data = file_get_contents( $server . '/public.php/webdav/', false, $cont ); - $data = explode( '', $data ); - + + // parse webdav XML + $data = json_decode(json_encode( + simplexml_load_string( $data, 'SimpleXMLElement', 0, "d", true) + ), true ); + $poddata = array( 'title' => '', 'logo' => '', @@ -58,19 +63,31 @@ private static function loadFromNextcloud( string $url ) : array { ); $eid = 1; - foreach($data as $d){ - if( strpos( $d, 'audio' ) !== false ){ - $fina = substr( $d, 0, strpos( $d, '') ); - $fina = substr( $fina, strrpos( $fina, '/' ) + 1 ); - $fina = urldecode( $fina ); + // iterate files + foreach($data["response"] as $r){ + + // get data from xml + $mime = $r["propstat"]["prop"]["getcontenttype"] ?? ''; + $href = $r["href"] ?? ''; + // get filename + $filename = urldecode(substr( $href, strrpos( $href, '/' ) + 1 )); + // get streaming/ web url + $streamurl = $server . ($useindex ? '/index.php' : '') . '/s/'. $share .'/download?path=%2F&files=' . rawurlencode( $filename ); + + // is this an audio file? + if( str_starts_with($mime, 'audio/') ){ $poddata['episodes'][$eid] = array( - 'title' => $fina, + 'title' => $filename, 'desc' => '', - 'url' => $server . ($useindex ? '/index.php' : '') . '/s/'. $share .'/download?path=%2F&files=' . rawurlencode( $fina ) + 'url' => $streamurl ); $eid++; } + // it this a logo? + else if(str_starts_with($mime, 'image/') && str_starts_with($filename, "logo") ){ + $poddata['logo'] = $streamurl; + } } return $poddata; diff --git a/php/classes/RadioLogo.php b/php/classes/RadioLogo.php index 507c87d..e6aa000 100644 --- a/php/classes/RadioLogo.php +++ b/php/classes/RadioLogo.php @@ -85,12 +85,8 @@ private function fetchLogo(string $logo) : bool { $tmpName = tempnam(sys_get_temp_dir(), 'conv'); if($mimetype == 'image/svg+xml'){ - if( - self::svg2png($filename, $tmpName) - && - @rename($tmpName, $filename) - ){ - return true; + if( self::svg2png($filename, $tmpName) ){ + return rename($tmpName, $filename); } else { unlink($filename); @@ -99,9 +95,11 @@ private function fetchLogo(string $logo) : bool { } else { - // resize file (radio does not like huge images) - self::resize($filename, $mimetype, $tmpName); - @rename($tmpName, $filename); + // resize file + // will only return true if resize done (false on error or if already small enough) + if(self::resize($filename, $mimetype, $tmpName)){ + rename($tmpName, $filename); + } return true; } @@ -118,12 +116,17 @@ private static function svg2png(string $inputSVG, string $outputPNG) : bool { '-o', '"'.$outputPNG.'"', '"'.$inputSVG.'"' ); - return exec(implode(' ', $command)) !== false; + exec(implode(' ', $command), result_code:$rs); + return $rs === 0; } private static function imageDimensions(string $file) : array { - $info = exec('file --brief "'.$file.'"'); - if(preg_match(',(\d+)x(\d+),', str_replace(' ', '', $info), $matches) === 1){ + $finfo = finfo_open(FILEINFO_CONTINUE); + $info = finfo_file($finfo, $file); + finfo_close($finfo); + + // file info (including dimensions as ", 000 x 000,") + if(preg_match('/,(\d+)x(\d+),/', str_replace(' ', '', $info), $matches) === 1){ $width = intval($matches[1]); $height = intval($matches[2]); @@ -140,12 +143,14 @@ private static function resize(string $inputFile, string $inputMime, string $out // error if($width == 0 || $height == 0){ + // no resize return false; } // do not resize if smaller than 256 px if( $width <= 256 && $height <= 256 ){ - return true; + // resize not necessary + return false; } // create an svg with image From f7c7bc9b2f847c36deb8e33f9c549aa71286da77 Mon Sep 17 00:00:00 2001 From: KIMB-technologies Date: Fri, 15 Mar 2024 21:48:17 +0100 Subject: [PATCH 3/3] Clear logo cache, Own streams logo --- Setup.md | 7 +++++-- VERSION | 2 +- php/classes/Config.php | 2 +- php/classes/Inner.php | 12 ++++++++++++ php/classes/OwnStreams.php | 9 +++++++-- php/classes/RadioLogo.php | 13 +++++++++++++ php/classes/templates/list.json | 1 + php/classes/templates/list_de.html | 4 ++++ php/classes/templates/list_en.html | 4 ++++ php/gui/index.php | 1 + 10 files changed, 49 insertions(+), 6 deletions(-) diff --git a/Setup.md b/Setup.md index 59f1b51..c9f3338 100644 --- a/Setup.md +++ b/Setup.md @@ -210,13 +210,15 @@ The JSON resource at `CONF_STREAM_JSON` should look like this: "name": "Name A", "url": "https://stream.example.com/file.mp3", "live": false, - "proxy": true + "proxy": true, + "logo" : "https://stream.example.com/logo.png" }, { "name": "Name B", "url": "http://stream.example.com/live.m3u", "live": true, - "proxy": false + "proxy": false, + "logo" : "http://stream.example.com/live.png" } ] ``` @@ -226,3 +228,4 @@ JSON list of objects with the following keys each: - `url` Contains the url of the stream (either a file, .e.g., mp3, or a streamable ressource, e.g., m3u). - `live` (optional, default `true`) Live streams can not be paused or fast forwarded, for non live streams the entire file needs to be available from the start. - `proxy` (optional, default `false`) Use the internal proxy to allow https urls. +- `logo` (optional) Url to an image to display as logo in the radio's display. diff --git a/VERSION b/VERSION index 1b505bc..8b5e524 100644 --- a/VERSION +++ b/VERSION @@ -1,3 +1,3 @@ -2.9.0 +2.9.1 2.9 2 diff --git a/php/classes/Config.php b/php/classes/Config.php index fe2674a..1785080 100644 --- a/php/classes/Config.php +++ b/php/classes/Config.php @@ -85,7 +85,7 @@ class Config { /** * The system's version. */ - const VERSION = 'v2.9.0'; + const VERSION = 'v2.9.1'; /** * The real domain which should be used. diff --git a/php/classes/Inner.php b/php/classes/Inner.php index eba72d7..8fe36fc 100644 --- a/php/classes/Inner.php +++ b/php/classes/Inner.php @@ -26,6 +26,18 @@ public function __construct(int $id, Template $template){ $this->template = $template; } + public function clearCache() : void { + if(Config::USE_LOGO_CACHE){ + $this->template->setContent('CLEAR_CACHE', ''); + + if(isset($_GET['clear-logo-cache'])){ + $this->html[] = 'Cleared logo cache!' : 'red;">Error clearing logo cache!' + ) .''; + } + } + } + public function checkPost() : void { if(isset( $_GET['radios'] ) && isset( $_POST['name'] )){ $this->html[] = 'Changed Radio stations!'; diff --git a/php/classes/OwnStreams.php b/php/classes/OwnStreams.php index a2d920c..7ca3821 100644 --- a/php/classes/OwnStreams.php +++ b/php/classes/OwnStreams.php @@ -43,16 +43,21 @@ public function getStreams() : array { if( !empty( $list ) ){ foreach( $list as $item ){ - $urlOK = isset( $item['url'] ) && filter_var($item['url'], FILTER_VALIDATE_URL) !== false; - $streams[] = array( + + $stream = array( "name" => (!$urlOK ? "(url invalid!)" : '') . (isset( $item['name'] ) && is_string($item['name']) ? $item["name"] : 'Invalid Name'), "url" => $urlOK ? $item['url'] : '', "live" => isset($item['live']) && is_bool($item['live']) ? $item['live'] : true, "proxy" => isset($item['proxy']) && is_bool($item['proxy']) ? $item['proxy'] : false ); + // add logo if in json + if(isset($item['logo']) && filter_var($item['logo'], FILTER_VALIDATE_URL) !== false){ + $stream['logo'] = $item['logo']; + } + $streams[] = $stream; } $this->redis->arraySet( 'list', $list, Config::CACHE_EXPIRE ); } diff --git a/php/classes/RadioLogo.php b/php/classes/RadioLogo.php index e6aa000..ffd93b8 100644 --- a/php/classes/RadioLogo.php +++ b/php/classes/RadioLogo.php @@ -22,6 +22,19 @@ public function __construct(){ // check if image cache active and if cache folder writable $this->useImageCache = Config::USE_LOGO_CACHE && is_writable(self::BASE_DIR); } + + public function clearCache() : bool { + if($this->useImageCache){ + $ok = true; + foreach(scandir(self::BASE_DIR) as $d){ + if(preg_match('/^[a-f0-9]{40}\.(image|error)$/', $d) === 1){ + $ok &= unlink(self::BASE_DIR . '/' . $d); + } + } + return $ok; + } + return false; + } public function logoUrl(string $logo) : string { // empty or no url diff --git a/php/classes/templates/list.json b/php/classes/templates/list.json index e4b5937..a6b5ede 100644 --- a/php/classes/templates/list.json +++ b/php/classes/templates/list.json @@ -1,5 +1,6 @@ { "%%ADD_HTML%%" : "", + "%%CLEAR_CACHE%%" : " display: none; ", "%%RADIO_MAC%%" : "", "%%RADIO_DOMAIN%%" : "", "%%LOGIN_CODE%%" : "", diff --git a/php/classes/templates/list_de.html b/php/classes/templates/list_de.html index bc4ff85..23ffc8d 100644 --- a/php/classes/templates/list_de.html +++ b/php/classes/templates/list_de.html @@ -327,6 +327,10 @@

Podcasts

Vorschau

+
+ Logo-Cache leeren +
+
diff --git a/php/classes/templates/list_en.html b/php/classes/templates/list_en.html index 592c5ea..8d68656 100644 --- a/php/classes/templates/list_en.html +++ b/php/classes/templates/list_en.html @@ -335,6 +335,10 @@

Podcasts

Preview

+
+ Clear logo cache +
+
diff --git a/php/gui/index.php b/php/gui/index.php index a39e5fc..b8697d9 100644 --- a/php/gui/index.php +++ b/php/gui/index.php @@ -62,6 +62,7 @@ $inner = new Inner($login->getId(), $listTemplate); $inner->checkPost(); + $inner->clearCache(); $inner->radioForm(); $inner->podcastForm();