Files
newznab-tmux/app/Services/Tmux/TmuxMonitorService.php
T
2025-11-01 16:26:59 +01:00

420 lines
13 KiB
PHP

<?php
namespace App\Services\Tmux;
use App\Models\Category;
use App\Models\Collection;
use App\Models\Release;
use App\Models\Settings;
use Blacklight\Tmux;
use Illuminate\Support\Facades\DB;
/**
* Service for monitoring tmux operations and collecting statistics
*/
class TmuxMonitorService
{
protected Tmux $tmux;
protected array $runVar = [];
protected int $iterations = 1;
protected bool $shouldContinue = true;
public function __construct()
{
$this->tmux = new Tmux;
}
/**
* Initialize monitor with default values
*/
public function initializeMonitor(): array
{
$this->runVar['paths']['misc'] = base_path().'/misc/';
$this->runVar['paths']['cli'] = base_path().'/cli/';
$this->runVar['paths']['scraper'] = base_path().'/misc/IRCScraper/scrape.php';
$this->runVar['constants'] = $this->tmux->getConstantSettings();
$this->runVar['settings'] = $this->tmux->getMonitorSettings();
$this->runVar['connections'] = $this->tmux->getConnectionsInfo($this->runVar['constants']);
// Initialize timers
$this->runVar['timers'] = $this->initializeTimers();
// Initialize counts
$this->runVar['counts'] = [
'iterations' => 1,
'now' => [],
'start' => [],
'diff' => [],
'percent' => [],
];
return $this->runVar;
}
/**
* Initialize all timers
*/
protected function initializeTimers(): array
{
$now = time();
return [
'timer1' => $now,
'timer2' => $now,
'timer3' => $now,
'timer4' => $now,
'timer5' => $now,
'query' => [
'tmux_time' => 0,
'split_time' => 0,
'init_time' => 0,
'proc1_time' => 0,
'proc2_time' => 0,
'proc3_time' => 0,
'split1_time' => 0,
'init1_time' => 0,
'proc11_time' => 0,
'proc21_time' => 0,
'proc31_time' => 0,
'tpg_time' => 0,
'tpg1_time' => 0,
],
'newOld' => [
'newestrelname' => '',
'oldestcollection' => 0,
'newestpre' => 0,
'newestrelease' => 0,
],
];
}
/**
* Collect current statistics
*/
public function collectStatistics(): array
{
// Refresh settings periodically
$monitorDelay = (int) ($this->runVar['settings']['monitor'] ?? 60);
$timeSinceLastRefresh = time() - ($this->runVar['timers']['timer2'] ?? 0);
if ($this->iterations === 1 || $timeSinceLastRefresh >= $monitorDelay) {
$this->refreshStatistics();
$this->runVar['timers']['timer2'] = time();
}
// Update connection counts
$this->updateConnectionCounts();
// Set killswitches
$this->setKillswitches();
return $this->runVar;
}
/**
* Refresh all statistics from database
*/
protected function refreshStatistics(): void
{
$timer = time();
// Refresh settings
$this->runVar['settings'] = $this->tmux->getMonitorSettings();
$this->runVar['timers']['query']['tmux_time'] = time() - $timer;
// Get category counts
$this->getCategoryCounts();
// Get process counts
$this->getProcessCounts();
// Get table counts
$this->getTableCounts();
// Calculate diffs and percentages
$this->calculateStatistics();
}
/**
* Get counts by category
*/
protected function getCategoryCounts(): void
{
$timer = time();
$this->runVar['counts']['now']['tv'] = Release::query()
->whereBetween('categories_id', [Category::TV_ROOT, Category::TV_OTHER])
->count('id');
$this->runVar['counts']['now']['movies'] = Release::query()
->whereBetween('categories_id', [Category::MOVIE_ROOT, Category::MOVIE_OTHER])
->count('id');
$this->runVar['counts']['now']['audio'] = Release::query()
->whereBetween('categories_id', [Category::MUSIC_ROOT, Category::MUSIC_OTHER])
->count('id');
$this->runVar['counts']['now']['books'] = Release::query()
->whereBetween('categories_id', [Category::BOOKS_ROOT, Category::BOOKS_UNKNOWN])
->count('id');
$this->runVar['counts']['now']['console'] = Release::query()
->whereBetween('categories_id', [Category::GAME_ROOT, Category::GAME_OTHER])
->count('id');
$this->runVar['counts']['now']['pc'] = Release::query()
->whereBetween('categories_id', [Category::PC_ROOT, Category::PC_PHONE_ANDROID])
->count('id');
$this->runVar['counts']['now']['xxx'] = Release::query()
->whereBetween('categories_id', [Category::XXX_ROOT, Category::XXX_OTHER])
->count('id');
$this->runVar['counts']['now']['misc'] = Release::query()
->whereBetween('categories_id', [Category::OTHER_ROOT, Category::OTHER_HASHED])
->count('id');
$this->runVar['timers']['query']['init_time'] = time() - $timer;
}
/**
* Get process-related counts
*/
protected function getProcessCounts(): void
{
$timer = time();
try {
$bookReqIds = $this->runVar['settings']['book_reqids'] ?? Category::BOOKS_ROOT;
$dbName = config('nntmux.db_name');
$proc1Query = $this->tmux->proc_query(1, $bookReqIds, $dbName);
$proc1Result = DB::selectOne($proc1Query);
if ($proc1Result) {
foreach ((array) $proc1Result as $key => $value) {
$this->runVar['counts']['now'][$key] = $value;
}
}
$this->runVar['timers']['query']['proc1_time'] = time() - $timer;
// Process 2
$timer2 = time();
$maxSize = $this->runVar['settings']['maxsize_pp'] ?? '';
$minSize = $this->runVar['settings']['minsize_pp'] ?? '';
$proc2Query = $this->tmux->proc_query(2, $bookReqIds, $dbName, $maxSize, $minSize);
$proc2Result = DB::selectOne($proc2Query);
if ($proc2Result) {
foreach ((array) $proc2Result as $key => $value) {
$this->runVar['counts']['now'][$key] = $value;
}
}
$this->runVar['timers']['query']['proc2_time'] = time() - $timer2;
} catch (\Exception $e) {
logger()->error('Error collecting process counts: '.$e->getMessage());
}
}
/**
* Get table row counts
*/
protected function getTableCounts(): void
{
$timer = time();
try {
$this->runVar['counts']['now']['collections_table'] = Collection::query()->count();
// Get binaries/parts counts from information_schema or use approximation
$dbName = config('nntmux.db_name');
$tables = $this->tmux->cbpmTableQuery();
$this->runVar['counts']['now']['binaries_table'] = 0;
$this->runVar['counts']['now']['parts_table'] = 0;
$this->runVar['counts']['now']['missed_parts_table'] = 0;
foreach ($tables as $table) {
$tableName = $table->name;
$count = $this->getTableRowCount($tableName);
if (str_contains($tableName, 'binaries')) {
$this->runVar['counts']['now']['binaries_table'] += $count;
} elseif (str_contains($tableName, 'missed_parts')) {
$this->runVar['counts']['now']['missed_parts_table'] += $count;
} elseif (str_contains($tableName, 'parts')) {
$this->runVar['counts']['now']['parts_table'] += $count;
}
}
$this->runVar['timers']['query']['tpg_time'] = time() - $timer;
// Get additional table counts (query 4)
$timer4 = time();
$bookReqIds = $this->runVar['settings']['book_reqids'] ?? Category::BOOKS_ROOT;
$proc4Query = $this->tmux->proc_query(4, $bookReqIds, $dbName);
$proc4Result = DB::selectOne($proc4Query);
if ($proc4Result) {
foreach ((array) $proc4Result as $key => $value) {
$this->runVar['counts']['now'][$key] = $value;
}
}
// Get newest/oldest data (query 6)
$timer6 = time();
$proc6Query = $this->tmux->proc_query(6, $bookReqIds, $dbName);
$proc6Result = DB::selectOne($proc6Query);
if ($proc6Result) {
foreach ((array) $proc6Result as $key => $value) {
$this->runVar['timers']['newOld'][$key] = $value;
}
}
} catch (\Exception $e) {
logger()->error('Error collecting table counts: '.$e->getMessage());
}
}
/**
* Get row count for a table
*/
protected function getTableRowCount(string $tableName): int
{
try {
$result = DB::selectOne(
'SELECT TABLE_ROWS AS count FROM information_schema.TABLES
WHERE TABLE_NAME = ? AND TABLE_SCHEMA = DATABASE()',
[$tableName]
);
return (int) ($result->count ?? 0);
} catch (\Exception $e) {
return 0;
}
}
/**
* Calculate statistics (diffs, percentages, totals)
*/
protected function calculateStatistics(): void
{
// Calculate total work
$this->runVar['counts']['now']['total_work'] = 0;
foreach ($this->runVar['counts']['now'] as $key => $value) {
if (str_starts_with($key, 'process')) {
$this->runVar['counts']['now']['total_work'] += $value;
}
}
// Set initial start values on first iteration
if ($this->iterations === 1) {
$this->runVar['counts']['start'] = $this->runVar['counts']['now'];
}
// Calculate diffs and percentages
$totalReleases = $this->runVar['counts']['now']['releases'] ?? 1;
foreach ($this->runVar['counts']['now'] as $key => $value) {
$startValue = $this->runVar['counts']['start'][$key] ?? 0;
$this->runVar['counts']['diff'][$key] = number_format($value - $startValue);
$this->runVar['counts']['percent'][$key] = $totalReleases > 0
? sprintf('%02d', floor(($value / $totalReleases) * 100))
: 0;
}
}
/**
* Update connection counts
*/
protected function updateConnectionCounts(): void
{
$this->runVar['conncounts'] = $this->tmux->getUSPConnections(
'primary',
$this->runVar['connections']
);
if ((int) ($this->runVar['constants']['alternate_nntp'] ?? 0) === 1) {
$alternateConns = $this->tmux->getUSPConnections(
'alternate',
$this->runVar['connections']
);
$this->runVar['conncounts'] = array_merge($this->runVar['conncounts'], $alternateConns);
}
}
/**
* Set killswitches based on limits
*/
protected function setKillswitches(): void
{
$ppKillLimit = (int) ($this->runVar['settings']['postprocess_kill'] ?? 0);
$collKillLimit = (int) ($this->runVar['settings']['collections_kill'] ?? 0);
$totalWork = (int) ($this->runVar['counts']['now']['total_work'] ?? 0);
$collections = (int) ($this->runVar['counts']['now']['collections_table'] ?? 0);
$this->runVar['killswitch']['pp'] = ($ppKillLimit > 0 && $ppKillLimit < $totalWork);
$this->runVar['killswitch']['coll'] = ($collKillLimit > 0 && $collKillLimit < $collections);
}
/**
* Update the monitor display
*/
public function updateDisplay(): void
{
// This would output to terminal - implementation depends on TmuxOutput
// For now, we'll just log basic info
if ($this->iterations % 10 === 0) {
logger()->info('Tmux Monitor', [
'iteration' => $this->iterations,
'total_work' => $this->runVar['counts']['now']['total_work'] ?? 0,
'collections' => $this->runVar['counts']['now']['collections_table'] ?? 0,
]);
}
}
/**
* Increment iteration counter
*/
public function incrementIteration(): void
{
$this->iterations++;
$this->runVar['counts']['iterations'] = $this->iterations;
}
/**
* Check if monitoring should continue
*/
public function shouldContinue(): bool
{
$exitFlag = (int) Settings::settingValue('exit');
if ($exitFlag === 0) {
return true;
}
$this->shouldContinue = false;
return false;
}
/**
* Get current run variables
*/
public function getRunVar(): array
{
return $this->runVar;
}
}