### 2026.10.18.1 (beta) - Unraid 7.x preflight; observer mode by default, demo and explicit advanced opt-in. - Select a configured Unraid pool for observation; legacy script editor remains cache-only. - Persist plugin mode/pool independently; never start Mover from the setup page. ### 2026.10.17.1 - Hungarian, US English and German interfaces with a persistent language selector. - Correct unavailable/full cache readings, mounted-cache checks and refresh pause state. - Reject stale checksum manifests during package validation. ### 2026.10.17 - First self-contained public package; Hungarian UI, cache pool required. - Optional legacy threshold settings, retained only on systems with the existing script. - Installer validates PHP before activation and preserves local backup files. 🌐 "; $fmLanguageSelector=str_replace(['HU_SELECTED','EN_SELECTED','DE_SELECTED'],[$fmLang==='hu'?'selected':'',$fmLang==='en'?'selected':'',$fmLang==='de'?'selected':''],$fmLanguageSelector); if($fmLang==='en'){ ?> 1&&preg_match('~(?:^|[ /])mover(?: |$)~',$fmdCmd); $fmdTotal=$fmdMounted?@disk_total_space($fmc['path']):false;$fmdFree=$fmdMounted?@disk_free_space($fmc['path']):false;$fmdUsed=$fmdTotal&&$fmdFree!==false?round(100*(1-$fmdFree/$fmdTotal),1):0; $fmdDisplay=$fmdTotal&&$fmdFree!==false?$fmdUsed.'%':'—'; $fmdFreeText=$fmdFree!==false?number_format($fmdFree/1073741824,1,'.',','):'—'; $fmdStatus=$fmdRun?'● Mover running':($fmdRaw?'⚠ Unverified status':'● Idle');$fmdColor=$fmdRun?'#f2b84b':($fmdRaw?'#ef967a':'#64b99b'); }else{ $fmdRun=false;$fmdRaw='';$fmdUsed=65;$fmdDisplay='65%';$fmdFreeText='350';$fmdStatus='DEMO';$fmdColor='#ef7d00'; } $fmcBanner=$fmc['banner']; $mytiles['fejes.mover.monitor']['column2']=<<

MOVER MONITOR

{$fmLanguageSelector}
{$fmcBanner}
{$fmdDisplay}
{$fmdStatus}

Cache free: {$fmdFreeText} GiB

Cache / SSD → Mover → Array

Files and detailed status →

Data loaded · refresh every 10 sec
HTML; ?> 1&&preg_match('~(?:^|[ /])mover(?: |$)~',$fmdCmd); $fmdTotal=$fmdMounted?@disk_total_space($fmc['path']):false;$fmdFree=$fmdMounted?@disk_free_space($fmc['path']):false;$fmdUsed=$fmdTotal&&$fmdFree!==false?round(100*(1-$fmdFree/$fmdTotal),1):0; $fmdDisplay=$fmdTotal&&$fmdFree!==false?$fmdUsed.'%':'—'; $fmdFreeText=$fmdFree!==false?number_format($fmdFree/1073741824,1,',',' '):'—'; $fmdStatus=$fmdRun?'● Mover läuft':($fmdRaw?'⚠ Status nicht verifiziert':'● Leerlauf');$fmdColor=$fmdRun?'#f2b84b':($fmdRaw?'#ef967a':'#64b99b'); }else{ $fmdRun=false;$fmdRaw='';$fmdUsed=65;$fmdDisplay='65%';$fmdFreeText='350';$fmdStatus='DEMO';$fmdColor='#ef7d00'; } $fmcBanner=$fmc['banner']; $mytiles['fejes.mover.monitor']['column2']=<<

MOVER-MONITOR

{$fmLanguageSelector}
{$fmcBanner}
{$fmdDisplay}
{$fmdStatus}

Cache frei: {$fmdFreeText} GiB

Cache / SSD → Mover → Array

Dateien und detaillierter Status →

Daten geladen · Aktualisierung alle 10 Sek.
HTML; ?> 1&&preg_match('~(?:^|[ /])mover(?: |$)~',$fmdCmd); $fmdTotal=$fmdMounted?@disk_total_space($fmc['path']):false;$fmdFree=$fmdMounted?@disk_free_space($fmc['path']):false;$fmdUsed=$fmdTotal&&$fmdFree!==false?round(100*(1-$fmdFree/$fmdTotal),1):0; $fmdDisplay=$fmdTotal&&$fmdFree!==false?$fmdUsed.'%':'—'; $fmdFreeText=$fmdFree!==false?number_format($fmdFree/1073741824,1,',',' '):'—'; $fmdStatus=$fmdRun?'● Mover fut':($fmdRaw?'⚠ Nem ellenőrizhető':'● Várakozik');$fmdColor=$fmdRun?'#f2b84b':($fmdRaw?'#ef967a':'#64b99b'); }else{ $fmdRun=false;$fmdRaw='';$fmdUsed=65;$fmdDisplay='65%';$fmdFreeText='350';$fmdStatus='DEMO';$fmdColor='#ef7d00'; } $fmcBanner=$fmc['banner']; $mytiles['fejes.mover.monitor']['column2']=<<

MOVER FIGYELŐ

{$fmLanguageSelector}
{$fmcBanner}
{$fmdDisplay}
{$fmdStatus}

Cache szabad: {$fmdFreeText} GiB

Cache / SSD → Mover → Lemeztömb

Fájlok és részletes állapot →

Adatok betöltve · frissítés 10 mp-enként
HTML; ?> ]]>
🌐 "; $fmLanguageSelector=str_replace(['HU_SELECTED','EN_SELECTED','DE_SELECTED'],[$fmLang==='hu'?'selected':'',$fmLang==='en'?'selected':'',$fmLang==='de'?'selected':''],$fmLanguageSelector); if($fmLang==='en'){ ?> @file_get_contents($p)?:''; $fmEsc=static fn($s)=>htmlspecialchars((string)$s,ENT_QUOTES|ENT_SUBSTITUTE,'UTF-8'); $fmRaw=trim($fmRead('/var/run/mover.pid'));$fmPid=ctype_digit($fmRaw)?(int)$fmRaw:0; $fmCmd=$fmPid?str_replace(chr(0),' ',$fmRead("/proc/$fmPid/cmdline")):''; $fmRun=$fmPid>1&&preg_match('~(?:^|[ /])mover(?: |$)~',$fmCmd); $fmIds=[];$fmFiles=[]; if($fmRun){ $fmIds[$fmPid]=true;$fmParents=[]; foreach(glob('/proc/[0-9]*/status')?:[] as $p){$s=$fmRead($p);if(preg_match('/^Pid:\s+(\d+)/m',$s,$a)&&preg_match('/^PPid:\s+(\d+)/m',$s,$b))$fmParents[(int)$a[1]]=(int)$b[1];} do{$add=false;foreach($fmParents as $p=>$parent)if(isset($fmIds[$parent])&&!isset($fmIds[$p])){$fmIds[$p]=true;$add=true;}}while($add); foreach($fmIds as $p=>$_)foreach(glob("/proc/$p/fd/*")?:[] as $fd){ $path=@readlink($fd);if(!$path||!str_starts_with($path,'/mnt/')||str_ends_with($path,' (deleted)'))continue; $st=@stat($fd);if(!$st||(($st['mode']&0170000)!==0100000))continue; $info=$fmRead("/proc/$p/fdinfo/".basename($fd));preg_match('/^pos:\s+(\d+)/m',$info,$pos);preg_match('/^flags:\s+([0-7]+)/m',$info,$fl); $fmFiles[]=['path'=>$path,'pos'=>(int)($pos[1]??0),'size'=>$st['size'],'mode'=>isset($fl[1])?(octdec($fl[1])&3):-1];if(count($fmFiles)>=40)break 2; }} $fmTotal=$fmMounted?@disk_total_space($fmc['path']):false;$fmFree=$fmMounted?@disk_free_space($fmc['path']):false; $fmUsed=$fmTotal&&$fmFree!==false?round(100*(1-$fmFree/$fmTotal),1):null; $fmScript=$fmc['pool']==='cache'?$fmRead('/boot/config/plugins/user.scripts/scripts/Filmek_Mover_Threshold/script'):'';preg_match('/^KUSZOB_GIB=(\d+)/m',$fmScript,$fmMatch);$fmThreshold=$fmMatch[1]??null; $fmGiB=static fn($n)=>number_format($n/1073741824,1,'.',','); $fmTail=static function($path,$limit){$f=@fopen($path,'rb');if(!$f)return '';$sz=fstat($f)['size'];$offset=max(0,$sz-$limit);fseek($f,$offset);$s=stream_get_contents($f);fclose($f);if($offset){$nl=strpos($s,"\n");$s=$nl===false?'':substr($s,$nl+1);}return $s;}; $fmLines=preg_split('/\r?\n/',$fmTail('/var/log/syslog',262144));$fmLines=array_filter($fmLines,static fn($s)=>preg_match('/\bmover(?:\[|:| )|Filmek Mover/i',$s));$fmLog=implode("\n",array_slice($fmLines,-30)); $fmScriptLog=$fmTail('/tmp/user.scripts/tmpScripts/Filmek_Mover_Threshold/log.txt',8192); }else{ $fmRaw='';$fmPid=0;$fmRun=false;$fmIds=[];$fmFiles=[];$fmTotal=1073741824000;$fmFree=375809638400;$fmUsed=65;$fmThreshold=null;$fmGiB=static fn($n)=>number_format($n/1073741824,1,'.',',');$fmLog='DEMO · No live logs';$fmScriptLog=''; } ?>

Mover Monitor

FejesNAS · Live status · Monitoring only

⚙ Settings Data timestamp:
▣ CACHE / SSD➜⇄ MOVER➜▤ ARRAY

Cache / array illustration; actual direction may vary by share ·

MOVER STATUS

CACHE USAGE

/ GiB
Free: GiB

EXISTING SCRIPT START THRESHOLD

=$fmThreshold?'Free space is currently above the threshold.':'Free space is below the threshold. The script checks additional conditions.'):'The threshold cannot be verified.'?>

Currently open files

File / locationOperationFile position / size
/ GiB

Snapshot of files opened by Mover and its child processes. File position does not confirm transfer completion. Remaining data, speed and completion time cannot be reliably determined from this source.

Recent Mover log entries

Filmek Mover · latest script log

A figyelő a folyamatokat, a cache szabad helyét és a meglévő naplókat olvassa. A mozgatást a meglévő Unraid-beállítások és esetleges saját scripted vezérlik. Monitors the selected Unraid pool. Frissítés csak a lap megnyitása közben történik.

@file_get_contents($p)?:''; $fmEsc=static fn($s)=>htmlspecialchars((string)$s,ENT_QUOTES|ENT_SUBSTITUTE,'UTF-8'); $fmRaw=trim($fmRead('/var/run/mover.pid'));$fmPid=ctype_digit($fmRaw)?(int)$fmRaw:0; $fmCmd=$fmPid?str_replace(chr(0),' ',$fmRead("/proc/$fmPid/cmdline")):''; $fmRun=$fmPid>1&&preg_match('~(?:^|[ /])mover(?: |$)~',$fmCmd); $fmIds=[];$fmFiles=[]; if($fmRun){ $fmIds[$fmPid]=true;$fmParents=[]; foreach(glob('/proc/[0-9]*/status')?:[] as $p){$s=$fmRead($p);if(preg_match('/^Pid:\s+(\d+)/m',$s,$a)&&preg_match('/^PPid:\s+(\d+)/m',$s,$b))$fmParents[(int)$a[1]]=(int)$b[1];} do{$add=false;foreach($fmParents as $p=>$parent)if(isset($fmIds[$parent])&&!isset($fmIds[$p])){$fmIds[$p]=true;$add=true;}}while($add); foreach($fmIds as $p=>$_)foreach(glob("/proc/$p/fd/*")?:[] as $fd){ $path=@readlink($fd);if(!$path||!str_starts_with($path,'/mnt/')||str_ends_with($path,' (deleted)'))continue; $st=@stat($fd);if(!$st||(($st['mode']&0170000)!==0100000))continue; $info=$fmRead("/proc/$p/fdinfo/".basename($fd));preg_match('/^pos:\s+(\d+)/m',$info,$pos);preg_match('/^flags:\s+([0-7]+)/m',$info,$fl); $fmFiles[]=['path'=>$path,'pos'=>(int)($pos[1]??0),'size'=>$st['size'],'mode'=>isset($fl[1])?(octdec($fl[1])&3):-1];if(count($fmFiles)>=40)break 2; }} $fmTotal=$fmMounted?@disk_total_space($fmc['path']):false;$fmFree=$fmMounted?@disk_free_space($fmc['path']):false; $fmUsed=$fmTotal&&$fmFree!==false?round(100*(1-$fmFree/$fmTotal),1):null; $fmScript=$fmc['pool']==='cache'?$fmRead('/boot/config/plugins/user.scripts/scripts/Filmek_Mover_Threshold/script'):'';preg_match('/^KUSZOB_GIB=(\d+)/m',$fmScript,$fmMatch);$fmThreshold=$fmMatch[1]??null; $fmGiB=static fn($n)=>number_format($n/1073741824,1,',',' '); $fmTail=static function($path,$limit){$f=@fopen($path,'rb');if(!$f)return '';$sz=fstat($f)['size'];$offset=max(0,$sz-$limit);fseek($f,$offset);$s=stream_get_contents($f);fclose($f);if($offset){$nl=strpos($s,"\n");$s=$nl===false?'':substr($s,$nl+1);}return $s;}; $fmLines=preg_split('/\r?\n/',$fmTail('/var/log/syslog',262144));$fmLines=array_filter($fmLines,static fn($s)=>preg_match('/\bmover(?:\[|:| )|Filmek Mover/i',$s));$fmLog=implode("\n",array_slice($fmLines,-30)); $fmScriptLog=$fmTail('/tmp/user.scripts/tmpScripts/Filmek_Mover_Threshold/log.txt',8192); }else{ $fmRaw='';$fmPid=0;$fmRun=false;$fmIds=[];$fmFiles=[];$fmTotal=1073741824000;$fmFree=375809638400;$fmUsed=65;$fmThreshold=null;$fmGiB=static fn($n)=>number_format($n/1073741824,1,',',' ');$fmLog='DEMO · No live logs';$fmScriptLog=''; } ?>

Mover-Monitor

FejesNAS · Live-Status · Nur Überwachung

⚙ Einstellungen Datenstand:
▣ CACHE / SSD➜⇄ MOVER➜▤ ARRAY

Cache-/Array-Darstellung; die tatsächliche Richtung kann je Freigabe abweichen ·

MOVER-STATUS

CACHE-BELEGUNG

/ GiB
Frei: GiB

STARTSCHWELLE DES VORHANDENEN SKRIPTS

=$fmThreshold?'Der freie Speicher liegt derzeit über dem Schwellenwert.':'Der freie Speicher liegt unter dem Schwellenwert. Das Skript prüft weitere Bedingungen.'):'Der Schwellenwert kann nicht geprüft werden.'?>

Aktuell geöffnete Dateien

Datei / SpeicherortVorgangDateiposition / Größe
/ GiB

Momentaufnahme der von Mover und seinen Kindprozessen geöffneten Dateien. Die Dateiposition bestätigt keinen abgeschlossenen Transfer. Restmenge, Geschwindigkeit und Endzeit lassen sich aus dieser Quelle nicht zuverlässig bestimmen.

Letzte Mover-Protokolleinträge

Filmek Mover · letztes Skriptprotokoll

A figyelő a folyamatokat, a cache szabad helyét és a meglévő naplókat olvassa. A mozgatást a meglévő Unraid-beállítások és esetleges saját scripted vezérlik. Überwacht den ausgewählten Unraid-Pool. Frissítés csak a lap megnyitása közben történik.

@file_get_contents($p)?:''; $fmEsc=static fn($s)=>htmlspecialchars((string)$s,ENT_QUOTES|ENT_SUBSTITUTE,'UTF-8'); $fmRaw=trim($fmRead('/var/run/mover.pid'));$fmPid=ctype_digit($fmRaw)?(int)$fmRaw:0; $fmCmd=$fmPid?str_replace(chr(0),' ',$fmRead("/proc/$fmPid/cmdline")):''; $fmRun=$fmPid>1&&preg_match('~(?:^|[ /])mover(?: |$)~',$fmCmd); $fmIds=[];$fmFiles=[]; if($fmRun){ $fmIds[$fmPid]=true;$fmParents=[]; foreach(glob('/proc/[0-9]*/status')?:[] as $p){$s=$fmRead($p);if(preg_match('/^Pid:\s+(\d+)/m',$s,$a)&&preg_match('/^PPid:\s+(\d+)/m',$s,$b))$fmParents[(int)$a[1]]=(int)$b[1];} do{$add=false;foreach($fmParents as $p=>$parent)if(isset($fmIds[$parent])&&!isset($fmIds[$p])){$fmIds[$p]=true;$add=true;}}while($add); foreach($fmIds as $p=>$_)foreach(glob("/proc/$p/fd/*")?:[] as $fd){ $path=@readlink($fd);if(!$path||!str_starts_with($path,'/mnt/')||str_ends_with($path,' (deleted)'))continue; $st=@stat($fd);if(!$st||(($st['mode']&0170000)!==0100000))continue; $info=$fmRead("/proc/$p/fdinfo/".basename($fd));preg_match('/^pos:\s+(\d+)/m',$info,$pos);preg_match('/^flags:\s+([0-7]+)/m',$info,$fl); $fmFiles[]=['path'=>$path,'pos'=>(int)($pos[1]??0),'size'=>$st['size'],'mode'=>isset($fl[1])?(octdec($fl[1])&3):-1];if(count($fmFiles)>=40)break 2; }} $fmTotal=$fmMounted?@disk_total_space($fmc['path']):false;$fmFree=$fmMounted?@disk_free_space($fmc['path']):false; $fmUsed=$fmTotal&&$fmFree!==false?round(100*(1-$fmFree/$fmTotal),1):null; $fmScript=$fmc['pool']==='cache'?$fmRead('/boot/config/plugins/user.scripts/scripts/Filmek_Mover_Threshold/script'):'';preg_match('/^KUSZOB_GIB=(\d+)/m',$fmScript,$fmMatch);$fmThreshold=$fmMatch[1]??null; $fmGiB=static fn($n)=>number_format($n/1073741824,1,',',' '); $fmTail=static function($path,$limit){$f=@fopen($path,'rb');if(!$f)return '';$sz=fstat($f)['size'];$offset=max(0,$sz-$limit);fseek($f,$offset);$s=stream_get_contents($f);fclose($f);if($offset){$nl=strpos($s,"\n");$s=$nl===false?'':substr($s,$nl+1);}return $s;}; $fmLines=preg_split('/\r?\n/',$fmTail('/var/log/syslog',262144));$fmLines=array_filter($fmLines,static fn($s)=>preg_match('/\bmover(?:\[|:| )|Filmek Mover/i',$s));$fmLog=implode("\n",array_slice($fmLines,-30)); $fmScriptLog=$fmTail('/tmp/user.scripts/tmpScripts/Filmek_Mover_Threshold/log.txt',8192); }else{ $fmRaw='';$fmPid=0;$fmRun=false;$fmIds=[];$fmFiles=[];$fmTotal=1073741824000;$fmFree=375809638400;$fmUsed=65;$fmThreshold=null;$fmGiB=static fn($n)=>number_format($n/1073741824,1,',',' ');$fmLog='DEMO · No live logs';$fmScriptLog=''; } ?>

Mover figyelő

FejesNAS · Élő állapot · Csak megfigyelés

⚙ Beállítások Adatok ideje:
▣ CACHE / SSD➜⇄ MOVER➜▤ LEMEZTÖMB

Cache / lemeztömb szemléltetés; a tényleges irány megosztásonként eltérhet ·

MOVER ÁLLAPOTA

CACHE FOGLALTSÁGA

/ GiB
Szabad: GiB

MEGLÉVŐ SCRIPT INDÍTÁSI KÜSZÖBE

=$fmThreshold?'A szabad hely most a küszöb felett van.':'A szabad hely a küszöb alatt van. A script további feltételeket is ellenőriz.'):'A küszöb nem ellenőrizhető.'?>

Éppen megnyitott fájlok

Fájl / helyMűveletFájlpozíció / méret
/ GiB

A mover és gyermekfolyamatai nyitott fájljainak pillanatképe. A fájlpozíció nem igazolja a mozgatás befejezését. A teljes hátralévő mennyiség, sebesség és befejezési idő ebből az adatforrásból nem állapítható meg megbízhatóan.

Legutóbbi mover-naplóbejegyzések

Filmek Mover · legutóbbi scriptnapló

A figyelő a folyamatokat, a cache szabad helyét és a meglévő naplókat olvassa. A mozgatást a meglévő Unraid-beállítások és esetleges saját scripted vezérlik. A kiválasztott Unraid poolt figyeli. Frissítés csak a lap megnyitása közben történik.

]]>
['hu'=>$hu,'en'=>$en,'de'=>$de][$language]; $esc = static fn($v) => htmlspecialchars((string)$v, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8'); $configPath = '/boot/config/plugins/fejes.mover.monitor/settings.json'; $error = false; $lock = null; if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') { $lock = @fopen('/var/run/fejes-mover-config.lock', 'c'); if (!$lock || !flock($lock, ($configWrite ? LOCK_EX : LOCK_SH)|LOCK_NB)) $error = true; } $lockError = $error; $raw = @file_get_contents($configPath); $readError = $raw === false && file_exists($configPath); if ($readError) $error = true; $config = $raw === false ? [] : json_decode($raw, true); $validName = static fn($name) => is_string($name) && preg_match('/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/D', $name); $valid = is_array($config) && ($raw === false || ( ($config['version'] ?? null) === 1 && in_array($config['mode'] ?? null, ['demo','observer','advanced'], true) && $validName($config['pool'] ?? null) )); if (!$valid) $error = true; $mode = !$error && isset($config['mode']) ? $config['mode'] : 'observer'; $pool = !$error && isset($config['pool']) ? $config['pool'] : 'cache'; $mounts = []; foreach (explode("\n", @file_get_contents('/proc/self/mountinfo') ?: '') as $line) { $fields = explode(' ', $line); $mount = $fields[4] ?? ''; if ($mount !== '') $mounts[$mount] = true; } $pools = []; // Use Unraid pool definitions; never accept a path supplied by the browser. foreach (glob('/boot/config/pools/*.cfg') ?: [] as $file) { $name = basename($file, '.cfg'); if ($validName($name)) $pools[$name] = isset($mounts['/mnt/'.$name]); } if (!$pools && isset($mounts['/mnt/cache'])) $pools['cache'] = true; ksort($pools); $mounted = isset($pools[$pool]) && $pools[$pool]; $modeText = [ 'demo'=>$t('Demó','Demo','Demo'), 'observer'=>$t('Megfigyelő','Observer','Beobachter'), 'advanced'=>$t('Haladó','Advanced','Erweitert'), ][$mode]; $banner = '
'; $banner .= ''.$esc($t('Mód: ','Mode: ','Modus: ').$modeText).' · '; $banner .= $mode === 'demo' ? $esc($t('Szimulált adatok. Nincs élő megfigyelés vagy scriptmentés.','Simulated data. No live monitoring or script saves.','Simulierte Daten. Keine Live-Überwachung oder Skriptspeicherung.')) : $esc($t('Figyelt pool: ','Monitored pool: ','Überwachter Pool: ').$pool); if ($error) $banner .= '

'.$esc($t('A konfiguráció nem olvasható biztonságosan. A szerkesztés tiltva.','Configuration cannot be safely read. Editing is disabled.','Konfiguration kann nicht sicher gelesen werden. Bearbeitung gesperrt.')).'

'; $banner .= ' · '.$esc($t('Mód és pool beállítása','Configure mode and pool','Modus und Pool einstellen')).'
'; $token = @parse_ini_file('/var/local/emhttp/var.ini'); return ['mode'=>$mode,'pool'=>$pool,'path'=>'/mnt/'.$pool,'mounted'=>$mounted,'pools'=>$pools, 'error'=>$error,'lockError'=>$lockError,'readError'=>$readError,'configInvalid'=>!$valid, 'pathConfig'=>$configPath,'raw'=>$raw === false ? '' : $raw, 'hash'=>hash('sha256',$raw === false ? '' : $raw),'lock'=>$lock, 'token'=>is_array($token) ? ($token['csrf_token'] ?? '') : '', 't'=>$t,'esc'=>$esc,'banner'=>$banner]; })(!empty($fmcConfigWrite)); ]]>
🌐 "; $fmLanguageSelector=str_replace(['HU_SELECTED','EN_SELECTED','DE_SELECTED'],[$fmLang==='hu'?'selected':'',$fmLang==='en'?'selected':'',$fmLang==='de'?'selected':''],$fmLanguageSelector); if($fmLang==='en'){ ?>

Mover Settings

Script editing is disabled in this mode. Select Advanced mode to enable it.

Configure mode and pool'; return; } $msMounted=$fmc['mounted']; $msToken=(parse_ini_file('/var/local/emhttp/var.ini')['csrf_token']??''); $msPath='/boot/config/plugins/user.scripts/scripts/Filmek_Mover_Threshold/script'; $msEsc=fn($v)=>htmlspecialchars((string)$v,ENT_QUOTES,'UTF-8'); $msSource=@file_get_contents($msPath); if($msSource===false){ echo '

Mover Settings

This optional page manages the existing Filmek_Mover_Threshold User Scripts automation. The script is not installed on this system.

Mover Monitor still works independently. This plugin does not install or schedule automatic transfers.

Back to Mover Monitor
'; return; } $msParse=function($s){$r=[];foreach(['KUSZOB_GIB','UJRA_PROBALKOZAS_MP'] as $k){if(preg_match_all('/^'.preg_quote($k,'/').'=(\d+)[ \t]*$/m',(string)$s,$m)!==1)throw new RuntimeException('The script parameters are ambiguous.');$r[$k]=(int)$m[1][0];}return $r;}; $msTotal=$msMounted?@disk_total_space('/mnt/cache'):false;$msFree=$msMounted?@disk_free_space('/mnt/cache'):false;$msMax=$msTotal?max(1,(int)floor($msTotal/1073741824)-1):0; $msMessage='';$msError='';$msValues=['KUSZOB_GIB'=>300,'UJRA_PROBALKOZAS_MP'=>360];$msLock=null;$msTmp=null; try{ $msValues=$msParse($msSource); if(($_SERVER['REQUEST_METHOD']??'GET')==='POST'){ if(!is_string($_POST['ms_csrf']??null)||empty($msToken)||!hash_equals($msToken,$_POST['ms_csrf']))throw new RuntimeException('Session expired. Reload the page.'); $msNew=[];foreach(['KUSZOB_GIB'=>$msMax,'UJRA_PROBALKOZAS_MP'=>10080] as $k=>$max){$v=$_POST[$k]??null;if(!is_string($v)||!preg_match('/^[1-9][0-9]{0,4}$/D',$v)||(int)$v>$max)throw new RuntimeException('Invalid value or cache unavailable.');$msNew[$k]=(int)$v;} $msLock=fopen('/var/run/fejes-mover-settings.lock','c');if(!$msLock||!flock($msLock,LOCK_EX|LOCK_NB))throw new RuntimeException('Another save is in progress.'); $msSource=file_get_contents($msPath);$msValues=$msParse($msSource); if(!is_string($_POST['source_hash']??null)||!hash_equals(hash('sha256',$msSource),$_POST['source_hash']))throw new RuntimeException('The script changed in the meantime. Reload the page.'); if($msNew===$msValues){$msMessage='Verified: these values are already saved. Nothing changed.';} else{ if(file_exists('/var/run/mover.pid'))throw new RuntimeException('Settings cannot be changed while Mover is running.'); $msChanged=$msSource;foreach($msNew as $k=>$v)$msChanged=preg_replace('/^'.preg_quote($k,'/').'=[0-9]+[ \t]*$/m',$k.'='.$v,$msChanged); $msDir='/boot/config/plugins/fejes.mover.monitor/backups';if(!is_dir($msDir)&&!mkdir($msDir,0700,true))throw new RuntimeException('Unable to create the backup directory.'); $msBackup=$msDir.'/threshold-'.date('Ymd-His').'-'.bin2hex(random_bytes(3)).'.sh'; if(file_put_contents($msBackup,$msSource,LOCK_EX)!==strlen($msSource)||file_get_contents($msBackup)!==$msSource)throw new RuntimeException('Backup verification failed.'); $msTmp=tempnam(dirname($msPath),'.mover-');if(!$msTmp||file_put_contents($msTmp,$msChanged)!==strlen($msChanged))throw new RuntimeException('Unable to prepare the save.'); chmod($msTmp,fileperms($msPath)&0777);exec('/bin/bash -n '.escapeshellarg($msTmp).' 2>&1',$msOut,$msCode);if($msCode!==0)throw new RuntimeException('Script validation failed.'); if(file_get_contents($msPath)!==$msSource)throw new RuntimeException('The script changed in the meantime.'); if(!rename($msTmp,$msPath))throw new RuntimeException('Script replacement failed.');$msTmp=null; if(file_get_contents($msPath)!==$msChanged)throw new RuntimeException('Read-back verification failed. Backup: '.$msBackup); $msSource=$msChanged;$msValues=$msNew;$msMessage='Saved and verified. The next script run will use these values. Backup: '.basename($msBackup); } } }catch(Throwable $e){$msError=$e->getMessage();} finally{if($msTmp&&is_file($msTmp))unlink($msTmp);if(is_resource($msLock)){flock($msLock,LOCK_UN);fclose($msLock);}} ?>
FEJESNAS / MOVER SETUP

Adjust your settings

Filmek automation · cache → array

← Mover Monitor

01 · Free-space threshold

Below this amount of free cache space, the script attempts to start the built-in Mover.


Higher value: may start sooner. Lower value: files stay in cache longer.

02 · Retry interval

Minimum time between two start attempts.


The script check schedule separately determines when the next attempt occurs.

Live settings preview

Currently free: GiB · Cache capacity: GiB

Fill: current usage · Vertical marker: configured start threshold

Existing automation checks

Your own script implements backup and parity guards; this settings page cannot verify them. Saving checks the Mover PID file.

Saving does not start Mover. The next script run uses the new values; a backup is created before each change.

Change the schedule on the existing User Scripts page.

Mover-Einstellungen

Skriptbearbeitung ist in diesem Modus gesperrt. Zum Aktivieren den erweiterten Modus wählen.

Modus und Pool einstellen'; return; } $msMounted=$fmc['mounted']; $msToken=(parse_ini_file('/var/local/emhttp/var.ini')['csrf_token']??''); $msPath='/boot/config/plugins/user.scripts/scripts/Filmek_Mover_Threshold/script'; $msEsc=fn($v)=>htmlspecialchars((string)$v,ENT_QUOTES,'UTF-8'); $msSource=@file_get_contents($msPath); if($msSource===false){ echo '

Mover-Einstellungen

Diese optionale Seite verwaltet die vorhandene Filmek_Mover_Threshold-Automatisierung in User Scripts. Das Skript ist auf diesem System nicht installiert.

Der Mover-Monitor funktioniert unabhängig davon. Dieses Plugin installiert oder plant keine automatischen Übertragungen.

Zurück zum Mover-Monitor
'; return; } $msParse=function($s){$r=[];foreach(['KUSZOB_GIB','UJRA_PROBALKOZAS_MP'] as $k){if(preg_match_all('/^'.preg_quote($k,'/').'=(\d+)[ \t]*$/m',(string)$s,$m)!==1)throw new RuntimeException('Die Skriptparameter sind nicht eindeutig.');$r[$k]=(int)$m[1][0];}return $r;}; $msTotal=$msMounted?@disk_total_space('/mnt/cache'):false;$msFree=$msMounted?@disk_free_space('/mnt/cache'):false;$msMax=$msTotal?max(1,(int)floor($msTotal/1073741824)-1):0; $msMessage='';$msError='';$msValues=['KUSZOB_GIB'=>300,'UJRA_PROBALKOZAS_MP'=>360];$msLock=null;$msTmp=null; try{ $msValues=$msParse($msSource); if(($_SERVER['REQUEST_METHOD']??'GET')==='POST'){ if(!is_string($_POST['ms_csrf']??null)||empty($msToken)||!hash_equals($msToken,$_POST['ms_csrf']))throw new RuntimeException('Sitzung abgelaufen. Seite neu laden.'); $msNew=[];foreach(['KUSZOB_GIB'=>$msMax,'UJRA_PROBALKOZAS_MP'=>10080] as $k=>$max){$v=$_POST[$k]??null;if(!is_string($v)||!preg_match('/^[1-9][0-9]{0,4}$/D',$v)||(int)$v>$max)throw new RuntimeException('Ungültiger Wert oder Cache nicht verfügbar.');$msNew[$k]=(int)$v;} $msLock=fopen('/var/run/fejes-mover-settings.lock','c');if(!$msLock||!flock($msLock,LOCK_EX|LOCK_NB))throw new RuntimeException('Ein anderer Speichervorgang läuft.'); $msSource=file_get_contents($msPath);$msValues=$msParse($msSource); if(!is_string($_POST['source_hash']??null)||!hash_equals(hash('sha256',$msSource),$_POST['source_hash']))throw new RuntimeException('Das Skript wurde zwischenzeitlich geändert. Seite neu laden.'); if($msNew===$msValues){$msMessage='Geprüft: Diese Werte sind bereits gespeichert. Keine Änderung vorgenommen.';} else{ if(file_exists('/var/run/mover.pid'))throw new RuntimeException('Während Mover läuft, können die Werte nicht geändert werden.'); $msChanged=$msSource;foreach($msNew as $k=>$v)$msChanged=preg_replace('/^'.preg_quote($k,'/').'=[0-9]+[ \t]*$/m',$k.'='.$v,$msChanged); $msDir='/boot/config/plugins/fejes.mover.monitor/backups';if(!is_dir($msDir)&&!mkdir($msDir,0700,true))throw new RuntimeException('Der Sicherungsordner kann nicht erstellt werden.'); $msBackup=$msDir.'/threshold-'.date('Ymd-His').'-'.bin2hex(random_bytes(3)).'.sh'; if(file_put_contents($msBackup,$msSource,LOCK_EX)!==strlen($msSource)||file_get_contents($msBackup)!==$msSource)throw new RuntimeException('Prüfung der Sicherung fehlgeschlagen.'); $msTmp=tempnam(dirname($msPath),'.mover-');if(!$msTmp||file_put_contents($msTmp,$msChanged)!==strlen($msChanged))throw new RuntimeException('Speichern konnte nicht vorbereitet werden.'); chmod($msTmp,fileperms($msPath)&0777);exec('/bin/bash -n '.escapeshellarg($msTmp).' 2>&1',$msOut,$msCode);if($msCode!==0)throw new RuntimeException('Skriptprüfung fehlgeschlagen.'); if(file_get_contents($msPath)!==$msSource)throw new RuntimeException('Das Skript wurde zwischenzeitlich geändert.'); if(!rename($msTmp,$msPath))throw new RuntimeException('Ersetzen des Skripts fehlgeschlagen.');$msTmp=null; if(file_get_contents($msPath)!==$msChanged)throw new RuntimeException('Prüfung durch erneutes Lesen fehlgeschlagen. Sicherung: '.$msBackup); $msSource=$msChanged;$msValues=$msNew;$msMessage='Gespeichert und geprüft. Der nächste Skriptlauf verwendet diese Werte. Sicherung: '.basename($msBackup); } } }catch(Throwable $e){$msError=$e->getMessage();} finally{if($msTmp&&is_file($msTmp))unlink($msTmp);if(is_resource($msLock)){flock($msLock,LOCK_UN);fclose($msLock);}} ?>
FEJESNAS / MOVER SETUP

Einstellungen anpassen

Filmek-Automatisierung · Cache → Array

← Mover-Monitor

01 · Schwellenwert für freien Speicher

Unterhalb dieses freien Cache-Speichers versucht das Skript, den integrierten Mover zu starten.


Höherer Wert: früherer Start möglich. Niedrigerer Wert: Dateien bleiben länger im Cache.

02 · Wiederholungsintervall

Mindestzeit zwischen zwei Startversuchen.


Der Prüfzeitplan des Skripts bestimmt unabhängig davon den nächsten Versuch.

Live-Vorschau der Einstellungen

Derzeit frei: GiB · Cache-Kapazität: GiB

Füllung: aktuelle Belegung · Senkrechte Markierung: eingestellter Startschwellenwert

Prüfungen der vorhandenen Automatisierung

Dein eigenes Skript setzt Sicherungs- und Paritätsschutz um; diese Einstellungsseite kann sie nicht verifizieren. Beim Speichern wird die Mover-PID-Datei geprüft.

Speichern startet Mover nicht. Der nächste Skriptlauf verwendet die neuen Werte; vor jeder Änderung wird eine Sicherung erstellt.

Den Zeitplan auf der vorhandenen User-Scripts-Seite ändern.

Mover beállítások

A scriptszerkesztés ebben a módban tiltva. A bekapcsoláshoz válaszd a Haladó módot.

Mód és pool beállítása'; return; } $msMounted=$fmc['mounted']; $msToken=(parse_ini_file('/var/local/emhttp/var.ini')['csrf_token']??''); $msPath='/boot/config/plugins/user.scripts/scripts/Filmek_Mover_Threshold/script'; $msEsc=fn($v)=>htmlspecialchars((string)$v,ENT_QUOTES,'UTF-8'); $msSource=@file_get_contents($msPath); if($msSource===false){ echo '

Mover beállítások

Ez az opcionális oldal a meglévő Filmek_Mover_Threshold User Scripts automatika paramétereit kezeli. A script nincs telepítve ezen a rendszeren.

A Mover figyelő ettől függetlenül használható. Ez a bővítmény nem telepít és nem ütemez automatikus mozgatást.

Vissza a Mover figyelőhöz
'; return; } $msParse=function($s){$r=[];foreach(['KUSZOB_GIB','UJRA_PROBALKOZAS_MP'] as $k){if(preg_match_all('/^'.preg_quote($k,'/').'=(\d+)[ \t]*$/m',(string)$s,$m)!==1)throw new RuntimeException('A script paraméterei nem egyértelműek.');$r[$k]=(int)$m[1][0];}return $r;}; $msTotal=$msMounted?@disk_total_space('/mnt/cache'):false;$msFree=$msMounted?@disk_free_space('/mnt/cache'):false;$msMax=$msTotal?max(1,(int)floor($msTotal/1073741824)-1):0; $msMessage='';$msError='';$msValues=['KUSZOB_GIB'=>300,'UJRA_PROBALKOZAS_MP'=>360];$msLock=null;$msTmp=null; try{ $msValues=$msParse($msSource); if(($_SERVER['REQUEST_METHOD']??'GET')==='POST'){ if(!is_string($_POST['ms_csrf']??null)||empty($msToken)||!hash_equals($msToken,$_POST['ms_csrf']))throw new RuntimeException('Lejárt munkamenet. Töltsd újra az oldalt.'); $msNew=[];foreach(['KUSZOB_GIB'=>$msMax,'UJRA_PROBALKOZAS_MP'=>10080] as $k=>$max){$v=$_POST[$k]??null;if(!is_string($v)||!preg_match('/^[1-9][0-9]{0,4}$/D',$v)||(int)$v>$max)throw new RuntimeException('Érvénytelen érték vagy nem elérhető cache.');$msNew[$k]=(int)$v;} $msLock=fopen('/var/run/fejes-mover-settings.lock','c');if(!$msLock||!flock($msLock,LOCK_EX|LOCK_NB))throw new RuntimeException('Másik mentés folyamatban.'); $msSource=file_get_contents($msPath);$msValues=$msParse($msSource); if(!is_string($_POST['source_hash']??null)||!hash_equals(hash('sha256',$msSource),$_POST['source_hash']))throw new RuntimeException('A script közben megváltozott. Töltsd újra az oldalt.'); if($msNew===$msValues){$msMessage='Ellenőrzés sikeres: az értékek már el vannak mentve. Nem történt módosítás.';} else{ if(file_exists('/var/run/mover.pid'))throw new RuntimeException('Futó Mover mellett nem módosíthatók az értékek.'); $msChanged=$msSource;foreach($msNew as $k=>$v)$msChanged=preg_replace('/^'.preg_quote($k,'/').'=[0-9]+[ \t]*$/m',$k.'='.$v,$msChanged); $msDir='/boot/config/plugins/fejes.mover.monitor/backups';if(!is_dir($msDir)&&!mkdir($msDir,0700,true))throw new RuntimeException('Nem hozható létre a mentési mappa.'); $msBackup=$msDir.'/threshold-'.date('Ymd-His').'-'.bin2hex(random_bytes(3)).'.sh'; if(file_put_contents($msBackup,$msSource,LOCK_EX)!==strlen($msSource)||file_get_contents($msBackup)!==$msSource)throw new RuntimeException('A biztonsági másolat ellenőrzése sikertelen.'); $msTmp=tempnam(dirname($msPath),'.mover-');if(!$msTmp||file_put_contents($msTmp,$msChanged)!==strlen($msChanged))throw new RuntimeException('A mentés előkészítése sikertelen.'); chmod($msTmp,fileperms($msPath)&0777);exec('/bin/bash -n '.escapeshellarg($msTmp).' 2>&1',$msOut,$msCode);if($msCode!==0)throw new RuntimeException('A script ellenőrzése sikertelen.'); if(file_get_contents($msPath)!==$msSource)throw new RuntimeException('A script közben megváltozott.'); if(!rename($msTmp,$msPath))throw new RuntimeException('A script cseréje sikertelen.');$msTmp=null; if(file_get_contents($msPath)!==$msChanged)throw new RuntimeException('A visszaolvasás sikertelen. Biztonsági másolat: '.$msBackup); $msSource=$msChanged;$msValues=$msNew;$msMessage='Sikeresen mentve és visszaellenőrizve. A következő scriptfutás ezeket használja. Biztonsági másolat: '.basename($msBackup); } } }catch(Throwable $e){$msError=$e->getMessage();} finally{if($msTmp&&is_file($msTmp))unlink($msTmp);if(is_resource($msLock)){flock($msLock,LOCK_UN);fclose($msLock);}} ?>
FEJESNAS / MOVER SETUP

Hangold a működését

Filmek automatika · cache → lemeztömb

← Mover figyelő

01 · Szabadhely-küszöb

Ennyi szabad cache-hely alatt próbálja elindítani a beépített Movert.


Nagyobb érték: hamarabb indulhat. Kisebb érték: tovább maradnak a fájlok a cache-en.

02 · Újrapróbálkozási idő

Legalább ennyi idő teljen el két indítási próbálkozás között.


A script ellenőrzési ütemezése külön kezeli, mikor kerül sor a következő próbára.

Élő beállítási előnézet

Most szabad: GiB · Cache kapacitás: GiB

Kitöltés: jelenlegi foglaltság · Függőleges jel: a beállított indítási küszöb

A meglévő automatika ellenőrzései

A backup- és paritásvédelmet a saját scripted valósítja meg; ezt a beállítóoldal nem tudja igazolni. Mentéskor a Mover PID-fájlját ellenőrzi.

A mentés nem indít Movert. Az új értékeket a következő scriptfutás használja; minden módosítás előtt biztonsági másolat készül.

Ütemezés módosítása: a meglévő User Scripts oldalon.

]]>
🌐 "; $fmLanguageSelector=str_replace(['HU_SELECTED','EN_SELECTED','DE_SELECTED'],[$fmLang==='hu'?'selected':'',$fmLang==='en'?'selected':'',$fmLang==='de'?'selected':''],$fmLanguageSelector); if($fmLang==='en'){ ?> 1,'mode'=>$newMode,'pool'=>$newPool]; $data=json_encode($saved,JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR)."\n"; if($data!==$fmc['raw']){ $dir=dirname($fmc['pathConfig']); if(!is_dir($dir)&&!@mkdir($dir,0700,true))throw new RuntimeException('Failed to save mode configuration.'); $tmp=tempnam($dir,'.settings-'); if(!$tmp)throw new RuntimeException('Failed to save mode configuration.'); try{ if(!chmod($tmp,0600)||file_put_contents($tmp,$data)!==strlen($data)||file_get_contents($tmp)!==$data||!rename($tmp,$fmc['pathConfig']))throw new RuntimeException('Failed to save mode configuration.'); }finally{if(is_file($tmp))unlink($tmp);} if(file_get_contents($fmc['pathConfig'])!==$data)throw new RuntimeException('Mode configuration verification failed.'); } $fmcMessage='Mode and monitored pool saved. This does not start Mover.'; $fmc['mode']=$newMode;$fmc['pool']=$newPool;$fmc['hash']=hash('sha256',$data); }catch(Throwable $e){$fmcError=$e->getMessage();} } ?>

Operating mode and monitored pool

Unraid 7.x · Default installation: Observer mode. Changing mode never starts a transfer.

Operating mode
Monitored pool

The list comes from Unraid pool configuration. Unmounted pool capacity remains unavailable.

Advanced mode acknowledgment

Mode and pool selection only save plugin configuration. Automation changes require a separate save with a backup.

Back to Mover Monitor
1,'mode'=>$newMode,'pool'=>$newPool]; $data=json_encode($saved,JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR)."\n"; if($data!==$fmc['raw']){ $dir=dirname($fmc['pathConfig']); if(!is_dir($dir)&&!@mkdir($dir,0700,true))throw new RuntimeException('Moduskonfiguration konnte nicht gespeichert werden.'); $tmp=tempnam($dir,'.settings-'); if(!$tmp)throw new RuntimeException('Moduskonfiguration konnte nicht gespeichert werden.'); try{ if(!chmod($tmp,0600)||file_put_contents($tmp,$data)!==strlen($data)||file_get_contents($tmp)!==$data||!rename($tmp,$fmc['pathConfig']))throw new RuntimeException('Moduskonfiguration konnte nicht gespeichert werden.'); }finally{if(is_file($tmp))unlink($tmp);} if(file_get_contents($fmc['pathConfig'])!==$data)throw new RuntimeException('Prüfung der Moduskonfiguration fehlgeschlagen.'); } $fmcMessage='Modus und überwachter Pool gespeichert. Mover wird nicht gestartet.'; $fmc['mode']=$newMode;$fmc['pool']=$newPool;$fmc['hash']=hash('sha256',$data); }catch(Throwable $e){$fmcError=$e->getMessage();} } ?>

Betriebsmodus und überwachter Pool

Unraid 7.x · Standard: Beobachtermodus. Ein Moduswechsel startet keine Übertragung.

Betriebsmodus
Überwachter Pool

Die Liste stammt aus der Unraid-Poolkonfiguration. Die Kapazität nicht eingehängter Pools bleibt unbekannt.

Bestätigung des erweiterten Modus

Modus- und Poolauswahl speichern nur die Plugin-Konfiguration. Änderungen an der Automatisierung erfordern separates Speichern mit Sicherung.

Zurück zum Mover-Monitor
1,'mode'=>$newMode,'pool'=>$newPool]; $data=json_encode($saved,JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR)."\n"; if($data!==$fmc['raw']){ $dir=dirname($fmc['pathConfig']); if(!is_dir($dir)&&!@mkdir($dir,0700,true))throw new RuntimeException('A módbeállítás mentése sikertelen.'); $tmp=tempnam($dir,'.settings-'); if(!$tmp)throw new RuntimeException('A módbeállítás mentése sikertelen.'); try{ if(!chmod($tmp,0600)||file_put_contents($tmp,$data)!==strlen($data)||file_get_contents($tmp)!==$data||!rename($tmp,$fmc['pathConfig']))throw new RuntimeException('A módbeállítás mentése sikertelen.'); }finally{if(is_file($tmp))unlink($tmp);} if(file_get_contents($fmc['pathConfig'])!==$data)throw new RuntimeException('A módbeállítás visszaellenőrzése sikertelen.'); } $fmcMessage='A mód és a figyelt pool elmentve. Ez nem indít Movert.'; $fmc['mode']=$newMode;$fmc['pool']=$newPool;$fmc['hash']=hash('sha256',$data); }catch(Throwable $e){$fmcError=$e->getMessage();} } ?>

Működési mód és figyelt pool

Unraid 7.x · A telepítés alapértelmezése: Megfigyelő mód. A módváltás nem indít mozgatást.

Működési mód
Figyelt pool

A lista az Unraid poolbeállításaiból származik. A nem csatolt pool kapacitása ismeretlen marad.

Haladó mód jóváhagyása

A mód- és poolválasztás kizárólag a bővítmény saját beállítását menti. Az automatika csak külön mentéskor változik, biztonsági másolattal.

Vissza a Mover figyelőhöz
]]>
&2; exit 1; } php -r '$v=parse_ini_file("/etc/unraid-version")["version"]??""; exit(PHP_VERSION_ID>=80000 && preg_match("/^7[.]/",$v)?0:1);' || { echo 'Unsupported Unraid/PHP version (requires Unraid 7.x, PHP 8+).' >&2; exit 1; } for name in FejesMover.Dashboard.page FejesMover.page FejesMoverRuntime.php FejesMoverSettings.page FejesMoverSetup.page; do php -l "$src/$name" >/dev/null done install -d -m 0755 "$dst" for name in FejesMover.Dashboard.page FejesMover.page FejesMoverRuntime.php FejesMoverSettings.page FejesMoverSetup.page; do install -m 0644 "$src/$name" "$dst/$name" done printf '%s\n' 'Fejes Mover Monitor installed. Open Tools / Mover figyelo.' ]]> /dev/null || true printf '%s\n' 'Fejes Mover Monitor removed; local configuration and backups preserved.' ]]>