- Use >= instead of == for stop_failures threshold (prevents skipping
the exact failure count if incremented past it)
- Add retry with sleep on stream start failure before goto label76
- Replace shell_exec('kill -9 `ps ...`') with safer xargs pipeline
that handles empty PID lists gracefully (xargs -r)
All 6 mass stream operations (start/stop/restart for live/movie)
used the default 5s timeout, causing failures on panels with many streams.
Increased to 120s to allow bulk operations to complete.
Player redirects to /login ignored the access code prefix,
causing 404 on multi-code installations. Now reads $_SERVER['XC_CODE']
and builds the correct redirect URL with the code prefix.
WebPlayer login was broken — DB connection, settings, and destroySession()
were unavailable because includes/admin.php was not loaded.
Added require_once for the missing bootstrap dependency.
- FileLogger::log(): capture caller file via debug_backtrace()
- FileLogger: add 'file' field to log entries
- Database: pass exception line number to FileLogger::log()
- ErrorsCronJob: support both legacy (log_message/log_extra) and current
(message/extra) log formats for backward compatibility
- ErrorsCronJob: extract log fields into typed local variables
- ModuleInterface: replace registerCrons() with registerCommands(CommandRegistry)
- ModuleInterface: add install() and uninstall() lifecycle methods
- ModuleLoader: auto-discover modules from modules/*/module.json
- ModuleLoader: config/modules.php now holds only overrides (enabled => false)
- ModuleLoader: remove constructor dependency on ServiceContainer/Router
- ModuleLoader: add bootAll() for web context, registerAllCommands() for CLI
- ModuleLoader: resolve class name by convention (kebab-case → PascalCase + Module)
- Remove checkDependencies() — modules must not depend on each other
functions.php and session.php are included from post.php inside footer,
after HTML output has started. setcookie() and header() calls without
headers_sent() guard produced 'Cannot modify header information' warnings.
- Add _SETUP to globals import list in renderUnifiedLayoutHeader/Footer
- Pass _SETUP via $vars in setup.php render calls
- Set $GLOBALS['_SETUP'] explicitly in setup.php
- Make header.php theme check null-safe (defensive against missing
$rUserInfo/$rThemes on setup page)
On fresh install cache files (blocked_ua, blocked_isp, blocked_ips,
blocked_servers, allowed_ips, proxy_servers) do not exist yet.
Added is_file() guard before file_get_contents() to prevent warnings.
status, tools, crons/epg.php used dirname($argv[0]) which resolves
relative to CWD. When called from a different directory (e.g. by the
installer), './console.php' was not found. __DIR__ is always absolute.
- Add tools/php_syntax_check.sh (supports full scan + single-file mode)
- CI workflow now calls the shared script
- All 6 agents updated to reference the script
- CONTRIBUTING.md: add Pre-Commit Checks section
- Exclude src/bin/* (third-party stubs) from lint
- EnigmaService.php: add 5 missing closing braces in massEdit foreach
- MagService.php: same fix — 5 missing closing braces in massEdit foreach
- review.php: replace short open tag <? with <?php (short_open_tag=Off)
New GitHub Actions workflow that runs on push/PR to main and weekly:
1. PHP Syntax Check: validates all src/*.php files with php -l
2. Semgrep Security Scan: runs php, security-audit, command-injection,
sql-injection, and xss rule packs against src/
SARIF results are uploaded to GitHub Code Scanning.
delete_files_list:
- Add LAST_TAG empty guard to prevent git diff against empty ref
- Use --diff-filter=DR to catch both deletions and renames
- Add sort -u to deduplicate entries
New target lb_delete_files_list:
- Filters deleted files to only those relevant to LB builds
- Uses awk to match against LB_DIRS and LB_ROOT_FILES
- lb_update now uses lb_delete_files_list instead of delete_files_list
Redis::get() returns false when a key does not exist.
igbinary_unserialize(false) produces a PHP warning and returns false,
which can cause unexpected behavior or fatal errors on the streaming
hot path.
All 5 call sites now check for false before deserializing:
- ConnectionTracker::getConnection()
- ConnectionTracker::closeConnection() (internal lookup)
- ConnectionLimiter::closeConnection()
- reseller_api_actions.php (line_activity kill)
- UsersCronJob (mGet bulk deserialize via array_map)
Covers: how the autoloader works, adding new classes, cache
invalidation, manual registration (addClass), adding new directories,
naming rules, duplicate resolution, and debugging.
Sidebar links added to both language versions of the Docsify site.
The tokenizer-based autoloader requires unique class names across the
entire project. These controllers had naming conflicts with module
classes (e.g. PlexController in both Admin and modules/plex/).
Renames:
- Admin/PlexController -> AdminPlexController
- Admin/WatchController -> AdminWatchController
- Admin/LogoutController -> AdminLogoutController
- Player/LogoutController -> PlayerLogoutController
Routes updated accordingly in admin.php and player.php.
Replace the 220-line manual buildClassMap() with automatic class
discovery using token_get_all(). The autoloader now:
- Scans registered directories recursively via warmCache()
- Extracts class/interface/trait/enum declarations from PHP files
- Persists the class map to disk using igbinary serialization
- Loads cached map on subsequent requests for O(1) resolution
- Uses negative cache ($notFound) for instant rejection of missing classes
- Falls back to warmCache() rebuild instead of per-request recursive scan
- Removes findRecursive() — no recursive traversal during autoload
- Adds infrastructure/ to registered directories
- Enables file cache by default at bootstrap
All comments and PHPDoc rewritten in English with production-quality
@param/@return documentation.
tmdb.php defines the TMDB class. Multiple code paths can include it
(cron jobs, episode import, watch folder). Using require instead of
require_once causes "Cannot declare class TMDB" fatal errors when
the file is loaded more than once in the same process.
DaemonTrait: added @ to shell_exec calls that may fail on restricted
systems (e.g. when /proc is unavailable).
ProcessManager::isNginxRunning(): suppressed exec warning when pgrep
is not available.
Database::query(): guarded explode on debugString output to prevent
undefined offset when Sent SQL section is missing.
UpdateCronJob: added null check for $gitRelease to prevent fatal error
when GitHubReleases service fails to initialize.
stream.php view: replaced || with && in the AJAX detection condition
to prevent accessing an undefined index when the header is absent.
StreamController: moved $rStream = null above the conditional block
so the variable is always defined before the template reads it.
ShutdownHandler::handle() depends on classes and globals initialized in
init.php. Registering it before require_once "init.php" caused a fatal
error (class not found) when the shutdown handler fired.
Affected endpoints: live.php, vod.php, timeshift.php
CLI consolidation:
- Delete 13 legacy CLI scripts from includes/cli/ (ondemand, proxy, queue, record, scanner, signals, startup, thumbnail, tools, update, watchdog, plex_item, watch_item)
- Convert status and tools entry points to thin proxies that delegate to console.php
- Update all shell_exec() calls across admin controllers, views, and API layer to use console.php command syntax instead of direct CLI file paths
- Update src/service to launch daemons via console.php (signals, watchdog, queue, cache_handler, startup)
- Update Python src/update script to call console.php update instead of includes/cli/update.php
- Update test_installer to use console.php startup
Streaming deduplication:
- Extract StreamAuthMiddleware — common response headers and token decryption shared by live/vod/timeshift
- Extract ShutdownHandler — unified shutdown logic replacing 3 duplicate function shutdown() blocks
- Refactor live.php, vod.php, timeshift.php to use new middleware classes
- Add streaming micro-router to www/stream/index.php as fallback entry point
Routing fixes:
- Fix admin index.php redirect to use relative path (supports access code prefixes)
- Add access code root redirect in public/index.php to prevent broken CSS/JS asset resolution
- Fix init.php for CLI compatibility: guard $_SERVER access, define PHP_ERRORS safely
Migrations:
- 001_update_crontab_filenames.sql — strip .php suffix from crontab filenames
- Fix cache.php view query to match new filename format (cache_engine instead of cache_engine.php)
Steps 11.1–11.6: migrated all standalone API entry points to controller classes
dispatched via the Front Controller, then deleted the legacy PHP files.
Controllers created:
- PlayerApiController (player_api.php, 12 actions + numeric aliases)
- Enigma2ApiController (enigma2.php + xplugin.php)
- PlaylistApiController (playlist.php)
- EpgApiController (epg.php)
- InternalApiController (api.php, ~40 server-to-server actions)
Nginx changes (MAIN + LB configs):
- Add location block for streaming API endpoints → FC with XC_SCOPE=api
- Add location block for internal API (/api.php) → FC with XC_API=internal
- Change 6 rewrite rules from break→last for deleted file URLs
- Remove allow/deny from /api.php location — auth handled by PHP
(password + IP whitelist + brute-force guard)
Front Controller (public/index.php):
- Add section 3a: REST API dispatch (XC_SCOPE=includes/api/*)
- Add section 3b: Streaming API dispatch (XC_SCOPE=api, XC_API=*)
- Move autoloader require to top (section 1b) for all code paths
- Controller map: endpoint name → controller class → init → dispatch
Bootstrap changes:
- www/init.php, www/stream/init.php: replace FC_API_NAME constant with
direct $rFilename variable (set by FC before require, checked via isset)
Bug fixes:
- PlayerApiController: fix 3 PHP 8 warnings ($rBouquets undefined,
missing 'rating'/'subtitle' array keys in get_vod_info)
- Fix server-to-server API calls blocked by nginx deny all (api_url_ip
uses external server IP, not 127.0.0.1)
Deleted files (8):
- www/player_api.php, www/enigma2.php, www/xplugin.php
- www/epg.php, www/playlist.php, www/api.php
- includes/api/admin/index.php, includes/api/reseller/index.php
New files:
- tools/test_player_api.sh — comprehensive Player API test suite (13 sections)