stream_queue_check.py: batch --playlist mode, per-stream JSON via --out-dir, TTY-aware summary vs JSON, HLS health judged by rebuffers (TS by stall), 120s default duration, --tolerance/--stall-timeout. New stream_graph.py renders the checker JSON as dependency-free SVG charts (per-stream + --combined comparison, unique colour per stream). Both moved under tools/stream-check/ with a README; tools/README.md and docs/en updated.
PHP-CS-Fixer's `no_unused_imports` is conservative — it treats a class name that
merely appears in a PHPDoc *description* as "used", so genuinely-dead imports
(e.g. `use ...Request;` next to a "Request IP" doc description) were never
flagged. Slevomat's UnusedUses is precise: it parses annotation *types*
(@param/@return/@var), so it keeps docblock-typed imports but removes truly
unused ones — matching what Intelephense (P1003) reports.
- Swap require-dev: friendsofphp/php-cs-fixer -> squizlabs/php_codesniffer +
slevomat/coding-standard (+ phpcodesniffer-composer-installer, allow-listed).
- New narrow ruleset build/phpcs.xml.dist (import/namespace hygiene only, NOT
full PSR-12): UnusedUses (searchAnnotations=true), UseFromSameNamespace,
UseDoesNotStartWithBackslash, AlphabeticallySortedUses, UseSpacing,
NamespaceSpacing. View templates stay excluded.
- Makefile: `make cs` -> phpcs, `make cs-fix` -> phpcbf (same target names).
- CI code-style job, CLAUDE.md, CONTRIBUTING.md, docs, .gitignore updated;
build/.php-cs-fixer.dist.php removed. Committed vendor stays production-only.
Move the generated site out of the repo root: site_dir -> build/site, with the
Pages upload path and .gitignore updated to match. build/ already holds tooling
config, so the build output no longer clutters the top-level listing.
CI translation was slow, so move it out of GitHub Actions: docs/ru is now a
committed, generated tree refreshed LOCALLY before a release; CI only builds it.
- pages.yml: drop the setup-python/cache/translate steps — the workflow now just
installs the build toolchain and runs `mkdocs build --strict` on the committed
en+ru trees.
- .gitignore: stop ignoring docs/ru (now committed); keep site/ + .docs-cache/.
- Split deps: docs/requirements.txt = build only (mkdocs-material, static-i18n,
used by CI); tools/docs/requirements.txt = translators (local-only).
- Makefile: docs-venv installs both; docs-build/docs-serve no longer translate
(translation is the deliberate `make docs-translate` release step).
- updates_checklist.md: new "Regenerate translated documentation" step
(make docs-translate + docs-build, commit docs/ru with the release commit).
- Commit the generated docs/ru (37 files, translators/yandex).
Rule: edit ONLY docs/en; docs/ru is generated — never hand-edit it.
Enrich the English source (ru regenerates automatically) with subsystem
behaviour that was missing or stale:
- streaming-subsystem: live client delivery is now daemon-only via xc_fanout
(X-Accel handoff, fan-out over a unix socket, control-socket off-air/telemetry,
fanout_sync reconciliation); on-disk HLS kept only for timeshift/thumbnail/
analyse. Documented the admin "Send Message" drawtext overlay via
POST /signal/<uuid>. Replaced the stale generateHLS/chase-read delivery step.
- caching-and-redis: how long-lived daemon connections survive a server idle
`timeout` close (phpredis silent reconnect without AUTH → non-PONG guard +
re-authenticated reconnect; getCapacity multi() guard), and cold-cache
fail-closed defaults in LegacyInitializer::initStreaming().
Replace the hand-maintained Docsify site (parallel docs/en + docs/ru trees
that had already drifted) with a MkDocs Material build where English is the
single source of truth and Russian is generated at build time.
Engine & structure
- mkdocs.yml: Material theme, site_url for the /XC_VM/ Pages subpath, and a
two-tab information architecture — User Guide (administration, API/Swagger,
UI translations, diagnostics, info/FAQ) vs Developer Guide (architecture,
workflow, security, integrations, build). Files are NOT moved — the split is
nav-only, so URLs and cross-links stay stable.
- mkdocs-static-i18n (folder mode): English at root, Russian under /ru/, with a
language switcher. `mkdocs build --strict` validates every link/anchor.
Translation pipeline (tools/docs/translate.py)
- Engine-agnostic via DOCS_TRANSLATE_PROVIDER: translators (free, no API key —
default), anthropic, deepl, or noop. Per-file sha256 cache so only changed
English files are re-translated. Markdown-safe: code, URLs, HTML tags and
glossary terms (XC_VM, FFmpeg, HLS, ...) are masked and never translated.
A file whose translation fails falls back to English so the build never breaks.
- docs/ru is generated and gitignored — never committed. It is produced locally
(`make docs-serve` / `docs-build`) and in CI.
CI & tooling
- pages.yml: build-then-upload (setup-python -> install -> restore .docs-cache
with restore-keys -> translate -> mkdocs build --strict -> deploy), replacing
the verbatim docs/ upload.
- Makefile: docs-venv / docs-translate / docs-build / docs-serve.
- docs/requirements.txt; .gitignore for docs/ru, site/, .docs-cache.
Migration details
- Removed Docsify control files (index.html, _navbar.md, _sidebar.md, .nojekyll)
and de-Docsify-ed body links in 6 English files (en-us/ aliases -> relative,
swagger _media paths, stripped ':ignore' link syntax).
- Preserved the two Russian-only planning docs (no English source) by moving
them into docs/adr/ as *.ru.md (repo-internal, excluded from the site).
The PHP SignalSender byte-path overlay class had 0 callers after E3 moved
the admin "send message" feature into xc_fanout (drawtext on the viewer's
HLS segment / TS window via FanoutClient::sendSignal -> POST /signal/<uuid>).
Delete the class and scrub its now-dangling mentions:
- src/Streaming/Delivery/SignalSender.php: removed (git rm)
- FanoutClient.php: comment reworded (legacy PHP byte-path, not the class)
- docs/{en,ru}/development/streaming-subsystem.md: dropped the tree line
- docs/adr/0003: overlay is e2e-proven on the LB; note the drawtext-ffmpeg
selection gotcha (bundled 8.0/7.1 lack the filter)
E2 deleted live.php's non-proxy chase-read — the only user of SegmentReader
(playlist segment extraction). Remove the now-dead class, its unit test, and the
doc references. SignalSender (still used by segment.php) and CacheReader (used
widely) are kept.
Add an entry (RU + EN) explaining why a MAG/STB box gets its IP blocked
after a factory reset / firmware change: the portal's MAGSCAN anti-clone
check compares the posted serial against the stored mag_devices.sn (and
device_id/device_id2/hw_version when lock_device is on) and bans the IP on
mismatch (blocked_ips -> iptables). Documents the fix — reset the device's
stored sn/device_id in the panel — and how to unblock: the web panel
(Tools -> IP Management, /<admin-code>/ips), console.php tools flush, or
manual iptables + flood-marker removal.
Ministra stops being a module — the whole Stalker portal (portal.php,
MinistraBootstrap, PortalHandler/PortalHelpers and the STB front-end) now
lives in src/Ministra/ under the XcVm\Ministra namespace, served at
/home/xc_vm/Ministra via the nginx alias.
- src/ministra/* and Modules/ministra_85a7d/{PortalHandler,PortalHelpers}
→ src/Ministra/; MinistraModule.php + module.json removed. Ministra was
the only committed module, so src/Modules/ keeps a .gitkeep.
- portal.php resolves PortalHandler as a sibling and derives MAIN_HOME from
its new location (glob crutch gone).
- nginx alias + AuthRepository $rAlias switched to /home/xc_vm/Ministra
(PascalCase); ministra entry dropped from bundled_modules.php.
- Makefile: Modules/ removed from LB_DIRS — all modules are MAIN-only, so
the ~50 MB of portal assets no longer ship to LB nodes.
- ArchitectureTest: zero committed modules is now a valid state.
- PHPStan: analyse src/Ministra, exclude the procedural portal.php entry,
repath the ministra baseline entries.
- Docs (architecture, ministra-browser-emulation, extraction plan) updated
to the new layout; the "extract to a separate repo" plan is cancelled.
Verified: php -l, make gates, make phpstan (No errors), full unit suite
(432 tests). On-server smoke: handshake + get_profile work end-to-end with
a registered MAC after deploy.
BoundaryInterface was a marker interface with no runtime consumer —
nothing read getEntryPoint()/isIsolated() and, being static-less
metadata, it enforced nothing. Its only implementor was MinistraModule.
Removes the interface, drops `implements BoundaryInterface` plus the two
orphaned methods from MinistraModule (getName/getVersion stay — they come
from BaseModule/ModuleInterface), and deletes the now-empty Core/Boundary/.
Test contract (InterfaceContractTest) loses the three BoundaryInterface
assertions; docs (en/ru architecture + modules, .github instructions) now
describe isolated subsystems like Ministra as a convention — own entry
point + bootstrap — rather than a marker interface.
Verified: php -l, make gates, make phpstan (No errors);
InterfaceContractTest + ArchitectureTest green (33 tests, 96 assertions).
The script was never wired up (no `make module-hashes` target, no CI/Makefile
caller). Module hash_id generation now lives in the standalone Module_Template
kit; for existing modules, generate inline with
`php -r 'echo bin2hex(random_bytes(16));'`.
Updated the module-dev docs (en + ru) that referenced the removed script /
non-existent `make module-hashes` target to use the one-liner.
Standalone Python (stdlib-only) tool that verifies a stream delivers
segments correctly and its delivery queue does not break:
- HLS: EXT-X-MEDIA-SEQUENCE contiguity, no dropped/rewound segments, no
EXT-X-DISCONTINUITY, every newly appearing segment downloadable.
- MPEG-TS: per-PID continuity_counter, sync-byte loss, TEI, delivery stalls,
with a --tolerance for rare source glitches relayed by -c copy.
- --live: colored TUI dashboard modelling a virtual player — received
timeline from PCR (TS) / EXTINF (HLS), playhead, and buffered cache
seconds graphed over time.
Documented in docs/{en,ru}/development/streaming-subsystem.md.
The heartbeat is written by the watchdog daemon (which now waits out DB
outages), not by cron:servers; the babysitter section documents the
crontab/cron-service/hung-lock sub-checks, and the cheat-sheet covers
the 'all nodes drop at the same moment' scenario. cli-tools blurbs
updated to match (en/ru).
update-system.md and faq.md (ru+en) linked to <lang>/development/cli-tools.md,
but the file lives at <lang>/guides/cli-tools.md (as the sidebar already points) —
the old path rendered an empty docsify page. Also repoint two non-existent anchors
(#миграции-базы-данных, #database-migrations) to the real section
(#обновление-бд-после-обновления-версии / #database-updates-after-version-upgrade).
The panel is deeply coupled to TMDb (VOD import, player metadata, admin
search, two crons), so shipping it as an uninstallable module only added
failure modes: after the move to hash-suffixed dirs (tmdb_f4e6e) every
hardcoded `Modules/tmdb/lib/...` require broke, and 2.3.3 crons died with
"Failed opening required TmdbClient.php".
tmdb -> core:
- Vendored \TMDB client -> src/Infrastructure/Tmdb/lib/; the only loader
is TmdbApiService::requireLibrary() (now public, also loads Release.php).
- TmdbApiService -> XcVm\Infrastructure\Tmdb — composer-autoloaded in every
bootstrap context, no module boot required (player scope never booted
modules, so module-namespace classes were unreachable there).
- TmdbCron / TmdbPopularCron -> XcVm\Domain\Vod; cron jobs -> Cli/CronJobs
(picked up by the console.php scan; command names cron:tmdb and
cron:tmdb_popular are unchanged).
- TmdbController -> Public/Controllers/Admin; tmdb_search / tmdb api
actions registered in routes/admin.php (same dispatchApi fallback).
- Domain/Vod services and player_functions.php load the lib through
TMDbService::requireLibrary() instead of hardcoded module paths.
- tmdb removed from config/bundled_modules.php. ModuleLoader gains
CORE_PROVIDED_MODULES: released watch/plex archives still declare
"dependencies": ["tmdb"] — such deps are stripped during manifest
normalization and in ModuleManager::listModules().
- syncBundledModules() purges stale on-disk tmdb module dirs and their
config/modules.php state on upgraded panels, so the old copy cannot boot
alongside the core implementation and collide on command names.
Standard-set provisioning fix (root cause of the "Undefined variable $db"
errors from watch/plex settings views on 2.3.3):
- Production still ran watch_e6c86/plex_20cd9-less legacy copies migrated
from 2.3.2 with generated hash_ids; provisionStandardSet() treated any
same-name directory as "already on disk" and never fetched the pinned
1.0.2/1.0.1 releases that contain the fix. A same-name directory whose
identity does not match the pinned hash_id is now considered stale: it
is deleted and the pinned release is installed in its place.
- installModuleFromSource(): when the module is already recorded as
installed (files re-provisioned over a stale copy), run updateModule()
(incremental from->to migrations) instead of re-running the initial
install.
Document the directory naming convention, the mandatory/auto-generated hash_id,
runtime generation + auto-migration of legacy bare dirs, and the update-source
manifest block, in both EN and RU module guides.
Bundle interactive OpenAPI 3.0 documentation for all XC_VM APIs into the
docsify site and align the docs visual with the Swagger UI page.
API reference
- Add self-contained Swagger UI host page (_media/swagger-ui.html) with a
tab per specification (Admin / System / Player / Playlist) plus nested
Documentation and Swagger views; deep-linkable via ?spec=<key>.
- Add OpenAPI 3.0 specs: admin-api (renamed from openapi.yaml, 104 ops) and
new system-api (31 actions), player-api (XtreamCodes) and playlist-api,
generated from the existing prose guides and controllers.
- Add EN/RU hub page (api/swagger.md) and wire it into the sidebars.
- Remove now-obsolete prose guides (system_api.md, xtreamcodes_api.md,
playlist.md) after verifying full coverage in the specs.
- Rebrand XUI.ONE -> XC_VM across the spec and host page; point links to
github.com/Vateron-Media/XC_VM.
Theming
- Add shared design tokens (_media/theme-tokens.css) consumed by both the
docs and the Swagger page as a single source of truth.
- Switch docsify to docsify-themeable (Simple / Simple Dark) and drop ~150
lines of hand-written CSS.
- Add a light/dark toggle synced across both pages via localStorage['theme'];
default to dark.
`php -l` syntax checking is redundant with the real linters/validators (PHPStan
parses the code, PHP-CS-Fixer and the PHPUnit bootstrap also fail on parse
errors). Remove the script and every reference to it:
- Makefile: drop the `syntax_check` target and its .PHONY entry.
- CI (ci.yml): drop the dedicated `lint` (PHP Syntax Check) job.
- Release workflows (build-release, build_pre-release): drop the "Check syntax"
step from the Quality Gate (PHPUnit remains).
- CONTRIBUTING.md: replace the syntax-check pre-commit guidance with the real
checks (make dev-tools / phpstan / cs / gates / phpunit).
- updates_checklist (en/ru): replace `make syntax_check` with the quality-check
suite and drop the stale "Security scan" snippet that referenced the removed
script and a non-existent tools/run_scan.sh (Semgrep runs automatically in CI).
Overhaul the Docsify documentation (English + Russian) so it matches the current
codebase and follows one consistent pattern.
Content accuracy (post-migration):
- Rewrite development/autoloader.md to PSR-4 / Composer (the old XC_Autoloader
scanner, igbinary tmp/cache/autoload_map and registerDirectories are gone).
- PascalCase every source path (src/core -> src/Core, domain/Stream, cli/Commands,
public/Controllers, Infrastructure/Redis, ...) across all docs.
- Replace the removed autoload.php references with vendor/autoload.php
(build_system, bootstrap-contexts, error-handling, modules).
- ssl-generation: note that the installer now auto-generates a unique self-signed
certificate before Nginx starts.
Common pattern (Clean & uniform):
- Strip emoji from headings; remove the in-page Navigation blocks (the Docsify
sidebar already provides navigation).
- One H1 + intro per doc; uniform "Related files" / "Связанные файлы" section,
added to the code-centric docs that lacked it.
Structure:
- Remove the empty stray docs/api/; move updates_checklist.md into builds/;
link the previously-orphaned ucs-integration.md.
- Regroup the sidebars (split the oversized guides group into Developer Guides /
Security & Access / Integrations; fold builds into Build & Release).
Augment:
- dev-workflow: Local Setup (make dev-tools) + Quality Checks (phpstan, cs, gates).
- build_system: Composer Dependencies section (committed prod-only vendor,
committed lock, dev tools via composer install, no build-time vendor step).
en/ru parity:
- Apply the same structure, fixes and pattern to docs/ru/ (translated), including
a new Russian ucs-integration.md. The en and ru file sets are now identical.
ARCHITECTURE.md and ARCHITECTURE_RU.md (620 lines each) were a parallel
documentation source that drifted from the code and contained outdated
references (CONTEXT_* string constants, global $db, MIGRATION.md which
does not exist).
All relevant content is now covered by specialized pages in docs/:
- Module system → development/modules.md
- Bootstrap contexts → development/bootstrap-contexts.md
- Event system → development/event-system.md
- Build variants → builds/build_system.md
docs/{en,ru}/development/architecture.md rewritten as a clean, self-contained
overview: source tree table, runtime flow diagram, extension points table,
contributor rules. Broken links to ARCHITECTURE.md and MIGRATION.md removed.
Replaced the incorrect BoundaryInterface API (boot/getExportedServices)
with the real contract (getName/getEntryPoint/isIsolated). Updated all
examples to use extends BaseModule instead of implements ModuleInterface.
Both English and Russian guides are in sync.
- Removed ConfigLoader.php and transitioned to using ConfigReader for configuration management.
- Updated DatabaseHandler instantiation to no longer rely on global $_INFO, instead using default parameters.
- Enhanced Database and MigrationRunner classes to improve error handling and connection management.
- Simplified RedisManager connection logic by utilizing XC_VM::redis_connect().
- Adjusted various controllers and services to align with the new configuration and database connection methods.
- Removed unnecessary global variables and improved code readability across multiple files.
- Updated comments and documentation to reflect changes in configuration handling and database connections.
- Implemented `ServerInstallCommand` for installing and configuring servers via SSH.
- Created `ProxyInstallFlow` class to handle proxy-specific installation tasks.
- Updated `ServerService` to use the new command structure for server installations.
- Modified API endpoint to initiate server installations using the new command format.
- Enhanced error handling and logging during installation processes.
Co-authored-by: Copilot <copilot@github.com>
- Added new fields to module.json: environment, dependencies, has_navbar, and has_settings.
- Updated ModuleLoader to support environment filtering and topological sorting of modules based on dependencies.
- Implemented error handling for missing and cyclic dependencies during module loading.
- Enhanced documentation for module.json structure and ModuleLoader functionality.
- Introduced unit tests for ModuleLoader to validate loading behavior and dependency resolution.
- Updated existing modules' manifest files to include new fields.
Co-authored-by: Copilot <copilot@github.com>
- Added checks for file existence before unlinking files in CacheHandlerCommand and CacheEngineCronJob.
- Suppressed errors for exec and shell_exec calls in WatchdogCommand, DaemonTrait, and SystemInfo.
- Enhanced bouquet map retrieval in BouquetService and CacheEngineCronJob to handle potential unserialization issues.
- Improved error handling in RootMysqlCronJob for MySQLD log parsing.
- Updated UserRepository to safely retrieve user IDs and user info from files, ensuring file existence checks.
- Refactored EpisodeService to handle stream sources and subtitles more robustly, including fallback mechanisms.
- Adjusted API responses in PlayerApiController and admin views to ensure consistent data handling.
- Enhanced server and line management views to prevent potential errors with undefined variables and arrays.
- General code cleanup and consistency improvements across various files.
- Remove DEVELOPMENT constant from AppConfig.php; introduce DB_ACCESS_ENABLED
(controls phpMiniAdmin access in admin panel only, not core DB connections)
- Detach bootstrap.php from www/constants.php: load core/Config/* and
core/Logging/Logger directly; define PHP_ERRORS fallback
- Switch Logger::init() to PHP_ERRORS in bootstrap.php, stream/init.php,
RequestGuard.php; remove leftover TODO comment
- Logger: always set error_reporting(E_ALL); UI visibility controlled
separately via display_errors; rename $development -> $showErrors
- MigrationRunner: track applied/failed counts separately; failed migrations
are not marked as applied
- Replace DEVELOPMENT with DB_ACCESS_ENABLED in admin settings.php and
database.php (phpMiniAdmin gate)
- Replace DEVELOPMENT with PHP_ERRORS in CertbotCronJob
- Docs (en/ru): add DB_ACCESS_ENABLED flag description; update feature-flags,
updates_checklist, http-request-handling; remove DEVELOPMENT references
- MIGRATION.md: mark L-1 as done, remove from backlog and wave A;
unblock L-2; renumber steps