Commit Graph
67 Commits
Author SHA1 Message Date
Divarion_D 320edb819c refactor(bootstrap): convert procedural prelude to BC shims
Point the five procedural prelude files at the new source-of-truth classes,
keeping them on disk as thin shims because the boot paths require_once them by
name and legacy code reads their globals/constants directly.

- Paths.php / AppConfig.php / Binaries.php -> ConstantsInitializer::init()
- ErrorCodes.php -> bridges ErrorResponder::codes() into $GLOBALS['rErrorCodes']
- ErrorHandler.php -> generateError()/generate404() shims (function_exists
  guarded) delegating to ErrorResponder; production still exit()s, so the ~139
  call sites are untouched.

Register ErrorHandler.php in composer autoload.files so the functions exist even
on paths that do not require the prelude; regenerate the production-only vendor
autoload accordingly.

The path/app-config constants are no longer literal define()s PHPStan can scan,
so add the four it previously learned from them but that were missing from the
stub (GIT_REPO_FANOUT, FANOUT_{RUN_PATH,CTL_SOCK,HTTP_SOCK}).

Debug/404 output stays byte-identical to the legacy functions (golden tests);
full suite, PHPStan, phpcs and make gates all green.
2026-09-16 16:34:16 +03:00
Divarion_D 734f4751c9 chore(tests): move phpunit.phar into tests/ and repoint references
The committed PHPUnit runner lived at tools/.bin/phpunit.phar, away from
the suite it runs. Move it next to the tests it drives —
tests/phpunit.phar — and update every invocation to
`php tests/phpunit.phar -c tests/phpunit.xml.dist`:

- CI workflows (ci, build-release, build_pre-release) + the ci.yml header,
- CLAUDE.md, CONTRIBUTING.md, tools/README.md, the qa-lead-reviewer agent,
- docs/en (dev-workflow, updates_checklist, phpunit-phar, refactoring).

docs/ru is generated from docs/en (make docs-translate) and is left for
the next regeneration, per the docs workflow.
2026-09-13 22:04:10 +03:00
Divarion_D b461e71cc1 feat(test-stream-generator): shared TS encoder, keyframe-aligned joins, launcher
Serve /stream.ts from one always-running encoder fanned out to every client
(TsBroadcaster) instead of spawning a fresh ffmpeg per connection: the process
count stays fixed at two (one HLS, one TS) no matter how many clients or
repeated on-demand pulls connect, and a joiner attaches to the live byte stream
with a keyframe-aligned prebuffer, so opening the channel is instant.

Emit TS keyframes every 0.4s so a consumer that joins mid-GOP (e.g. the panel's
LLOD probe with analyzeduration=0.5s) finds an IDR + SPS/PPS within its analysis
window — otherwise it reports "dimensions not set" and never starts.

Add run-background.sh (start/stop/status/restart, PID + log, auto-picks the
bundled XC_VM ffmpeg build that actually runs) and an M3u/ sample playlist;
refresh README to match.
2026-09-11 15:46:56 +03:00
Divarion_D 4556c4d54c docs: drop dts-audio-test refs, refresh test-generator blurb, reformat ffmpeg ADR
- CONTRIBUTING.md / tools/README.md: remove the deleted dts-audio-test row
  and update the test-stream-generator description (now generates a
  synthetic pattern, no input file).
- docs/adr/plan-ffmpeg-per-distro-distribution.ru.md: table/list whitespace
  reformat only.
2026-09-09 21:34:20 +03:00
Divarion_D ee3707f79a feat(tools): synthetic test pattern in test-stream-generator (no input file)
Drop the sample.mp4 dependency: generate the source live with ffmpeg
`testsrc2` plus overlays — a big running stopwatch (elapsed), the real
wall-clock (handy to eyeball end-to-end latency), a frame counter and two
sweeping boxes; 1 kHz test tone for audio. Falls back to `testsrc` (v1,
built-in timestamp) when no TrueType font is found.

New flags: --size, --fps, --font (autodetected). Removed -i/--input and
--encode (the raw synthetic source is always encoded).

Long-run hardening so it can sit generating for days:
- --max-clients caps concurrent /stream.ts pulls (each is an ffmpeg);
  extra connections get 503 instead of piling up processes/FDs.
- Per-client socket write timeout + TCP keepalive drops a stalled or
  half-open peer instead of pinning its ffmpeg; hard-kill if terminate()
  does not reap it.
- Per-request access logging is off by default (an HLS player polls every
  few seconds and would flood the log over days); --verbose restores it.
2026-09-09 21:33:06 +03:00
Divarion_D 347e257561 chore(tools): remove dts-audio-test helper scripts
Drop the one-off tools/dts-audio-test/ helpers (README + compare-audio.sh
+ gen-problem-stream.sh); no longer needed.
2026-09-09 21:08:57 +03:00
DanilandGitHub fde35439e3 Merge pull request #181 from Vateron-Media/feat/new-adminUI
Feat/new admin UI
2026-09-08 20:39:11 +03:00
Divarion_D 5b23a4807b docs(contributing): rewrite contributor guide 2026-09-07 21:00:01 +03:00
Divarion_D 6f8f091f1d build(admin-ui): add iconify subset generator script
tools/generate-iconify-subset.py scans the PHP/JS/CSS source for literal
tabler-* tokens and regenerates the used-only iconify-icons.min.css from the
full iconify-icons.css. Run it after adding a new icon. Also regenerates the
subset header banner to point at the script.
2026-09-06 22:48:40 +03:00
Divarion_D 175198a902 refactor(stream-check): merge checker and grapher into one stream_check.py
Consolidate the two stream-check tools into a single master script with
three subcommands:

- check <url>          verify one stream (or --live dashboard)
- playlist <path|url>  batch an .m3u list -> aggregate JSON (+ per-stream files)
- graph <inputs...>    render the JSON as SVG charts

Shared helpers (HTTP, slugify, m3u/JSON handling) are now defined once.
The old --playlist flag becomes the `playlist` subcommand and the bare-URL
form becomes `check <url>`; the streamtest harness is updated accordingly.

Also fix the per-stream SVG rendering black in viewers that do not support
8-digit #rrggbbaa hex: the bitrate area fill and not-PLAYING bands now use
6-digit hex plus a separate fill-opacity attribute.

Docs (English + tools READMEs + STREAMTEST) updated to the new invocation.
Removes stream_queue_check.py and stream_graph.py.
2026-09-06 11:11:10 +03:00
Divarion_D d4da90f37b fix(docs): translate bold spans atomically and auto-prune orphaned ru pages
The line-by-line web translator reordered words inside `**bold**` spans and
misplaced/dropped the markers, producing `**LB` or `****` (empty bold). Mask
each `**...**` as ONE atomic sentinel: translate the inner text on its own,
then store the whole balanced `**inner**` — the engine never sees the markers
and cannot reorder or collapse them. Also harden the anthropic prompt to keep
emphasis balanced.

Auto-prune: after translating, delete generated docs/ru files whose docs/en
source no longer exists (renamed/removed) and drop now-empty dirs, so the tree
mirrors docs/en 1:1 (removes the stale development/modules.md and
guides/geoip-and-device-detection.md).

Bump PROMPT_VERSION to 6 to invalidate the contaminated cache and regenerate
docs/ru (0 broken bold spans remaining, aside from pre-existing multi-line
bold that spans a soft line break).
2026-08-27 18:07:43 +03:00
Divarion_D 72eb0fbc58 refactor(bootstrap): replace per-scope include pairs with ScopeBootstrap classes
The front controller selected a pair of procedural files per scope
(<scope>_session.php + <scope>_functions.php) from a hardcoded map and
require'd them at global scope. Consolidate into a typed class hierarchy:

- ScopeBootstrap (interface) + ScopeBootstrapFactory::create($scope)
- Admin/Reseller/PlayerScopeBootstrap, each porting its former session +
  functions logic 1:1 into boot()/bootSession()/bootFunctions().

index.php now calls ScopeBootstrapFactory::create($scope)->boot() instead
of building file paths and require'ing. Unknown scopes still fall back to
admin.

Behaviour is unchanged: the view-facing globals ($rUserInfo, $rPermissions,
$_STATUS, $customScript, $_PAGE, server/health flags) are still injected via
explicit `global` declarations in the boot methods, so the ~140 procedural
view templates read them from scope exactly as before. player_utility_-
functions.php is unchanged and still loaded by PlayerScopeBootstrap.

Also fix the PHPStan stub: SERVER_ID is an int (getMainID(): ?int, a
server_id PK), not a string. Moving its define() into a class method meant
PHPStan no longer inferred the type from a top-level define and fell back to
the stub, which wrongly typed it `(string) mt_rand()` — surfacing 20
false argument.type errors at int-param call sites. Stub now uses mt_rand().

Removes: admin_session_fc.php, admin_functions_fc.php, reseller_session.php,
reseller_functions.php, player_session.php, player_functions.php.

make phpstan / cs / gates all green.
2026-08-27 15:57:35 +03:00
Divarion_D 1007b07cd6 feat(tools): add sync-dev.sh incremental dev-server deploy
Deploys the files changed by a git commit range from src/ to a running XC_VM box (src/ maps 1:1 to /home/xc_vm/): full-file tar-over-ssh, status-aware copy/delete/rename, ownership fixup, optional settings-cache rebuild and panel restart. All SSH multiplexes over one ControlMaster socket to stay fail2ban-safe. Developer convenience only — no versions, DB migrations, binaries, or per-server config; use the release archive for real upgrades. Ships a full man-style --help. Ignore the local .dev-sync-state watermark.
2026-08-25 18:40:58 +03:00
Divarion_D 48a69def2b build(docs): unbuffered translate output, cache/venv under build/
Run tools/docs/translate.py with python -u and print per-file progress so 'make docs-translate' shows live output instead of going silent. Relocate the translation cache and venv under build/ (build/docs-cache, build/docs-venv).
2026-08-23 13:49:58 +03:00
Divarion_D 505f876cde fix(test-stream-generator): serve /stream.ts as a live channel, not from t=0
The /stream.ts endpoint spawned a fresh per-client ffmpeg reading the file from the start, so the panel re-opening a channel always rewound to the beginning. Each client's ffmpeg now seeks to the current live position of the loop (offset = (now - start) %% duration); -ss lands on the nearest keyframe for a fast LLOD start, and clients connecting at the same wall-clock get the same offset (in sync).
2026-08-23 13:49:48 +03:00
Divarion_D 169e2cca2a feat(test-install): e2e streaming test through the panel (streamtest)
New streamtest-gen / -gen-stop / -backup / streamtest commands: configure the panel once by hand, snapshot the DB, then restore + run repeatably. The container runner rebuilds caches, launches the test-stream-generator, fetches the panel's output m3u for the test line, and runs the checker (aggregate JSON + per-stream logs copied out). STREAMTEST.md documents the flow; docker-compose mounts tools/ and exposes port 8088; ignore out/, fixtures/, graphs/.
2026-08-23 13:49:37 +03:00
Divarion_D a530a0599b feat(tools): stream-check — m3u playlist, JSON logs, SVG graphs
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.
2026-08-23 13:49:27 +03:00
Divarion_D dbf535bcc5 docs(translate): robust {N} sentinels + possessive handling; regenerate ru
Fix Markdown-mangling artifacts the free web engine (yandex) produced in the
committed docs/ru, and regenerate the whole tree cleanly (0 fallbacks):

- Sentinel format @@N@@ -> {N}. MT engines are trained to preserve curly
  format-string placeholders, so {N} survives code-heavy lines where @@N@@ (and
  ZZZ…ZZZ, which also duplicated its Z) were split/moved — e.g. the stray
  "@0@@" in the FAQ and "load balancerZ" in the README are gone.
- Possessive: a trailing English `'s` is consumed INTO the masked span and
  dropped on restore. Every sentinel format breaks when a bare `'s` sits right
  after it, and Russian has no possessive `'s`.
- Validate + retry: after restore, any leftover brace fragment triggers a retry
  (the engine is non-deterministic); after a few failures the line stays English
  so a broken token is never emitted.
- Glossary += KeyDB, yt-dlp, Ubuntu, iptables, MAGSCAN.

Regenerated docs/ru (37 files, translators/yandex): no residual sentinels,
mkdocs build --strict clean.
2026-08-21 16:31:01 +03:00
Divarion_D bdbcde97c7 docs: commit generated ru, translate locally before release (not in CI)
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.
2026-08-20 22:37:44 +03:00
Divarion_D dcd1d8c860 docs: migrate to MkDocs Material with auto-translated ru from English
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).
2026-08-20 22:09:34 +03:00
Divarion_D b14ba6ff96 docs(tools): document the full CLI in stream_queue_check.py header
The header's Usage block only listed --duration/--json/--ua; expand it to the
complete run command, every argparse option (--stall-timeout, --tolerance,
--live, --prebuffer, --buffer-target, --no-color) with defaults, and a couple
more examples. Matches `--help` 1:1. No code change.
2026-08-09 20:04:06 +03:00
Divarion_D 7f3812e165 chore(build): move phpstan / php-cs-fixer config out of the repo root
Relocate the dev-tooling config into build/ so the project root only holds
source and first-class project files:
- phpstan.dist.neon        -> build/phpstan.dist.neon
- phpstan-baseline.neon    -> build/phpstan-baseline.neon
- .php-cs-fixer.dist.php    -> build/.php-cs-fixer.dist.php
- .php-cs-fixer.cache       -> build/ (regenerated there; gitignored)

Because neon/CS-Fixer resolve relative paths against the config file's own
directory, the internal references are re-anchored one level up (src/ ->
../src/, tools/ -> ../tools/, Finder in(__DIR__.'/../src'); the baseline's
path: entries likewise). The Makefile now points PHPStan at the config with
-c build/phpstan.dist.neon (it previously relied on root auto-discovery),
generates the baseline into build/, and passes --config=build/... to CS-Fixer;
the CS-Fixer cache is pinned to build/ via setCacheFile and re-gitignored.

No behaviour change. Verified: make phpstan (No errors), make cs (0 fixable),
make gates. CI runs through these make targets, so it is covered.
2026-08-09 19:37:11 +03:00
Divarion_D 8147c2c0f6 docs(tools): add tools/README index documenting every utility
Documents each tool in tools/ and its purpose, grouped by role: CI gates,
PHPStan support, the PHPUnit runner, manual panel test/QA utilities
(test_player_api.sh, stream_queue_check.py, test-stream-generator,
dts-audio-test, test-install), and repo maintenance
(update_top_contributors.py). Makes the previously-unreferenced manual test
utilities discoverable.
2026-08-07 21:08:15 +03:00
Divarion_D 11a72afdd2 docs(tools): fix stale autoload.php ref and drop real IP from example
- tools/test-install/README.md listed /home/xc_vm/autoload.php among the
  post-install sanity files, but src/autoload.php was removed at the Composer
  migration; the shipped autoloader is vendor/autoload.php.
- tools/stream_queue_check.py had a real server IP (45.90.13.217) in a usage
  example; replaced with a "host" placeholder.
2026-08-07 21:03:57 +03:00
Divarion_D cc212c051b fix(ci): correct check_procedural_use skip path to Infrastructure/Tmdb/lib
The skip list keyed on /Modules/tmdb/lib/, which matches nothing: there is no
src/Modules/tmdb* dir, and the vendored non-namespaced TMDB library lives at
src/Infrastructure/Tmdb/lib/. So the intended skip was dead and that library
was being scanned. Point the skip at the real path. Gate still passes
(411 migrated classes, no violations).
2026-08-07 21:03:57 +03:00
Divarion_D cb056cdcf5 fix(phpstan): wire constants-stub regen into make, refresh drifted stub
gen-constants-stub.php had no Makefile/CI target, so tools/phpstan/
constants.stub.php (a bootstrapFile in phpstan.dist.neon) silently drifted:
regenerating it added DB_ACCESS_PWD and GIT_REPO_PROXY (real runtime define()s
in src/Core/Config/AppConfig.php that PHPStan could not see). Added `make
phpstan-stub` so the stub is regenerable/discoverable, and refreshed it
(129 -> 131 constants).
2026-08-07 21:03:42 +03:00
Divarion_D 24736873f6 chore(phpstan): correct stale "no Composer/PSR-4" bootstrap comments
phpstan-bootstrap.php and phpstan.dist.neon claimed the project "has no
Composer and no PSR-4 namespaces" and referenced the removed src/autoload.php
— both false since the Composer PSR-4 migration (and self-contradicted by the
neon's own excludePaths note about src/vendor/autoload.php). Comment-only:
they now state the bootstrap defines constants and deliberately wires no
project autoloader (PHPStan discovers symbols via paths + scanDirectories).
No behavior change.
2026-08-07 20:51:02 +03:00
Divarion_D 2466ddfc3e chore(tools): remove orphaned gen-module-hashes.php
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.
2026-08-07 20:49:36 +03:00
Divarion_D 6d6aa6652e feat(tools): add stream_queue_check.py queue-integrity monitor
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.
2026-08-06 21:22:33 +03:00
Divarion-D 29c014102b feat(dts-audio-test): add scripts for testing DTS/AC3 audio and generating problematic streams 2026-07-10 13:14:04 +03:00
Divarion-D 6de8ab7d9f feat(modules): add permanent hash_id generator script
tools/gen-module-hashes.php ensures every module.json carries a stable,
immutable hash_id (random 32-hex, generated once). Idempotent: modules that
already have a hash_id are untouched; new ones get it inserted after "name",
preserving file formatting. Run directly (php tools/gen-module-hashes.php).
2026-07-03 20:10:33 +03:00
Divarion-D 159ae3e9c8 feat(stream_server): add QuietThreadingHTTPServer to handle client disconnects gracefully 2026-06-28 14:26:02 +03:00
Divarion-D bd0c492867 feat(tests): add test stream generator for MP4 to HTTP-served streams 2026-06-28 12:04:39 +03:00
Divarion-D f88be578e9 chore(tools): remove unused test_redis.php
Standalone Redis diagnostic script with no references anywhere in the repo
  (run only ad-hoc on a server). No longer needed; removed.
2026-06-26 19:05:20 +03:00
Divarion-D bdad6e5406 chore(tools): remove unused install_xdebug.sh
Standalone xdebug installer helper that nothing references (not wired into
  Makefile, CI, docs or any script) and is no longer needed. No other changes —
  it had zero references in the repo.
2026-06-26 19:02:26 +03:00
Divarion-D 07d536ed10 chore(tools): remove php_syntax_check.sh — covered by PHPStan/CS/PHPUnit
`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).
2026-06-26 18:59:38 +03:00
Divarion-D aa7befa375 build: gitignore dev packages under vendor + harden the prod-only gate
Belt-and-suspenders so dev packages can never land in git:

- .gitignore: whitelist the committed production set under src/vendor/ and ignore
  everything else (src/vendor/* + !autoload.php/!chrisyue/!composer/!gemorroj,
  then re-ignore composer/{pcre,semver,xdebug-handler,autoload_files.php}). A
  local `composer install` (for PHPStan/PHP-CS-Fixer) now writes dev packages
  into ignored paths, so `git add` can never stage them. gitignore never
  untracks, so the committed prod files stay tracked and updatable; a NEW prod
  dep just needs a `!` whitelist line.
- check-vendor-prod-only.sh: add a second check on the committed
  vendor/composer/installed.json — it must list no dev package. This covers the
  one thing .gitignore cannot: installed.json is a tracked file that
  `composer install` rewrites with dev entries. Both checks read the git INDEX,
  so a local dev install does not trip the gate, but a committed dev artifact does.

Verified: clean tree passes; a dev `composer install` in the working tree leaves
the gate green (index-based); a fabricated dev installed.json is detected; no dev
dir is stageable after `composer install`.
2026-06-25 21:59:46 +03:00
Divarion-D baf6c8e231 build: ship a production-only vendor; install dev tools via Composer
Switch from "commit vendor with dev deps + strip at release" to the standard
application model: the committed src/vendor/ is PRODUCTION-ONLY, and dev tooling
(PHPStan, PHP-CS-Fixer + ~37 transitive deps) is installed on demand with
"composer install".

- Regenerate the committed src/vendor/ via "composer install --no-dev"
  (34 MB -> ~0.5 MB; only the Composer autoloader + gemorroj/m3u-parser +
  chrisyue/php-m3u8 remain). This also stops PHPStan\PharAutoloader registering
  in production.
- Commit src/composer.lock (un-ignored) — this is an application, so the lock is
  committed to make "composer install" reproducible across dev/CI.
- Revert the release-time strip step (Makefile hooks + tools/build/
  strip-dev-vendor.sh) — no longer needed; the archive ships the prod vendor as-is.
- CI: the phpstan and code-style jobs now run "composer install --working-dir=src"
  (with tools: composer) to obtain the dev tools before running.
- New gate tools/ci/check-vendor-prod-only.sh (+ make check-vendor-prod-only,
  wired into "make gates"): asserts no require-dev package from composer.lock is
  committed under src/vendor/ — guards against accidentally committing a
  dev-bloated vendor. Inspects git-tracked files, so it is correct even in a CI
  job that already ran "composer install".
- Fix verify-lb-archive.sh: LB legitimately ships most of Cli/Commands and
  Cli/CronJobs (edge commands + certbot/cache/cleanup crons), so flag only the
  genuinely privileged dirs + the specific install/root files, not the whole dirs.
- .gitignore / composer.json notes updated.

Verified before pruning: PHPStan no errors, PHPUnit 303, cs + gates green. After
pruning: PHPUnit 303 (prod-only vendor), all three gates green. Local dev tools
restored afterwards with "composer install" (not committed).
2026-06-25 21:51:13 +03:00
Divarion-D 0165b595d5 build: strip Composer dev dependencies from release archives
The committed src/vendor/ carries require-dev packages (PHPStan, PHP-CS-Fixer +
their transitive symfony/react/psr deps, ~33 MB) so they are available for local
dev and CI. They must not ship to production — besides the weight, PHPStan's
files-autoload registers PHPStan\PharAutoloader at runtime on every request.

Add tools/build/strip-dev-vendor.sh and hook it into both `make main` and
`make lb` right after the file-copy step (before archiving). It runs against the
staged TEMP_DIR, not the repo:
- removes every dev package dir listed in vendor/composer/installed.json
  (dev-package-names) and their bin shims;
- regenerates the autoloader with `composer dump-autoload --no-dev` (drops the
  dev files-autoload, incl. the PharAutoloader);
- prunes the emptied vendor namespace dirs.
LB does not stage composer.json (not in LB_ROOT_FILES); the script copies it in
so dump-autoload can run (harmless metadata in the LB archive).

Result: shipped vendor/ goes 34 MB -> ~0.8 MB, contains only the Composer
autoloader + the two prod packages (gemorroj/m3u-parser, chrisyue/php-m3u8);
M3uParser/Chrisyue still autoload and no PharAutoloader is registered. The
committed src/vendor/ is left untouched (dev tooling stays for local use).
2026-06-25 21:34:32 +03:00
Divarion-D e9bc5de0e4 test/ci(psr4): add the missing migration regression gates
Adds the automated gates the PSR-4 plan specified but that were verified only
manually per phase:

PHPUnit (run by the existing test job):
- AutoloadOrderTest      — Composer autoloader registered; the retired
                           XC_Autoloader scanner is NOT in the SPL stack; init()
                           is a no-op; no igbinary class-map cache is written.
- BootstrapPathsTest     — no live require/include points at a lowercase renamed
                           dir (the Фаза-1 grep-gate, as a runtime guard).
- ConsoleDiscoveryTest   — console.php FQCN discovery resolves every Cli command
                           file and the concrete command surface stays stable.

Shell gates (new 'PSR-4 Regression Gates' CI job + 'make gates'):
- tools/ci/check_procedural_use.php — procedural/view files must import every
  migrated class they use, with the `use` ABOVE the usage (PHP imports are
  positional). Runs with short_open_tag=1 so short-tag templates are analysed.
- tools/ci/verify-lb-archive.sh — reproduces the Makefile LB file selection from
  the real LB_* vars and asserts no privileged tree (Admin/Reseller/Player
  controllers, Domain/User|Device, Cli/CronJobs|Commands) ships to an LB node
  (security blocker 1).

Makefile: cs/cs-fix now force short_open_tag=1; new print-%, check-procedural-use,
verify-lb-archive and aggregate `gates` targets.
2026-06-25 20:57:53 +03:00
Divarion-D 66ecbc8cbc fix(phpstan): fix real bugs (return contracts, arg/byref, stub types) 599→585
- constants stub: use mt_rand()-based exprs so PHPStan infers GENERAL types,
  not literal 0/'' — fixes false division-by-zero (PACKET_SIZE) and
  foreach-over-false (str_split with len 0). Load stub via bootstrapFiles so
  result-cache invalidates on change.
- return contracts: explicit returns where a path fell through to null and
  violated the declared type:
  - StreamRepository::getById/getWatchFolder, GroupService::getById,
    getStream() → return false (declared array|false).
  - ServerRepository::getPublicURL → return '' when server missing (array→string).
  - MagService::resetSTB → return query() result (declared bool).
  - StreamUtils::getPlaylistSegments → explicit return null.
  - NetworkUtils::stopDownload → @return null corrected to @return void.
- PlexController: getPlexToken() called with 5 args but accepts 4 — dropped
  the dead 5th argument.
- DropboxClient::getMetaFromHeaders: array_shift() on an array_filter()
  expression (not a variable, by-ref error) — assign to a var first.
- WatchdogCommand: wrap numeric-string subtractions (nginx/proc-stat values)
  in floatval()/intval().
2026-06-23 20:02:46 +03:00
Divarion-D b9e911e7e8 fix(phpstan): resolve false positives and real bugs (988 → 622)
Systemic fixes (cascade across modules):
- Type `$db` params as \DatabaseHandler in 38 files (was `object`), so
  PHPStan resolves get_row()/query() and stops inferring closed array
  shapes — clears offsetAccess.notFound clusters.
- Database/DatabaseHandler: correct get_row()/clean_row() PHPDoc to
  array<string,mixed>, and query() $buffered param to mixed (it is the
  first positional bind value via func_get_args, not a bool flag).
- constants stub generator: explicit type overrides (HOST/PAGE_NAME →
  string, PHP_ERRORS → bool) to avoid null/mixed false positives.
- phpstan.dist.neon: scanFiles autoload.php/bootstrap.php; ignore
  unactionable FPs (dynamic require paths, proprietary XC_VM ioncube
  class, MaxMind vendor lib, ext-inotify stub gap).

Real bugs fixed:
- streaming/ConnectionLimiter: appended the whole accumulated UUID array
  instead of a single uuid, breaking per-stream temp-file cleanup.
- streaming/TimeshiftClient: dead `|| $x == 3` branch (always redundant).
- streaming/AsyncFileOperations: redundant is_array after === false.
- core/Storage/DropboxClient: @param callback → callable|null.

streaming module is clean (0 errors); infrastructure 39 → 27.
2026-06-23 19:46:33 +03:00
Divarion-D 69669d8e1c feat(phpstan): add PHPStan static analysis setup and constants stub generation 2026-06-23 19:28:45 +03:00
Divarion-D 4a0501324c feat(modules): evolve module system to extensible platform architecture
TASK-000: Remove legacy core patching system
- Delete src/core/Module/CoreCodePatcher.php
- Delete src/core/Module/CoreCodePatchableModuleInterface.php

TASK-001: Split ModuleInterface into specialized provider interfaces
- Add ServiceProviderInterface (boot, getEventSubscribers)
- Add RouteProviderInterface (registerRoutes)
- Add CommandProviderInterface (registerCommands)
- Add NavbarProviderInterface (registerNavbar)
- Add StreamMiddlewareProviderInterface
- ModuleLoader::bootAll() detects supported providers via instanceof

TASK-002: Refactor EventDispatcher
- Add ListenerProvider with priority support
- Add AbstractEvent base class
- Add StoppableEventInterface support
- Preserve legacy subscribe/publish API compatibility

TASK-003: Extend ServiceContainer
- Add ServiceContainer::decorate()
- Support priority-based decorator chains
- Add protected service registry:
  - db
  - settings
  - config
  - auth

TASK-004: Add core event catalog
- ModuleLoadedEvent
- ModuleBootedEvent
- PackageInstalledEvent
- UserAuthenticatedEvent
- UserLoggedOutEvent
- StreamStartingEvent
- StreamStartedEvent
- StreamStoppedEvent
- SettingsChangedEvent

TASK-005: Formalize subsystem boundaries
- Add BoundaryInterface
- Add MinistraBootstrap implementation
- Define exported service contracts

TASK-006: Add stream middleware pipeline
- Add StreamContext
- Add StreamMiddlewareInterface
- Add StreamPipeline
- Support middleware priority ordering

TASK-007: Add module hot reload
- Dispatch PackageInstalledEvent after installation
- Add ModuleManager::hotReload()
- Load and boot newly installed modules without service restart

TASK-008: Standardize container contracts
- Add container interfaces:
  - ContainerInterface
  - ContainerExceptionInterface
  - NotFoundExceptionInterface
- Add NotFoundException implementation
- ServiceContainer::get() throws NotFoundException

TASK-009: Refactor navbar registration
- CoreNavbarProvider implements NavbarProviderInterface
- Add registerNavbar() instance method
- ModuleLoader uses provider-based registration

TASK-010: Add module priority support
- Add priority field to module.json
- Default value: 0
- Higher priority modules load earlier
- Resolve load order by:
  1. Priority DESC
  2. Module name ASC

TASK-011: Add optional dependencies
- Add optional_dependencies field to module.json
- Missing optional dependencies do not block module loading
- Process optional dependencies after required dependencies

Tests
- Add ServiceContainer tests
- Add EventDispatcher tests
- Add StreamPipeline tests
- Add ModuleLoader priority tests

Documentation
- Rewrite module architecture documentation
- Update examples and extension development guides
- Document lifecycle, events, services, middleware, and dependencies
2026-06-14 14:25:19 +03:00
Divarion-D 4d0e256b0c fix: improve URL construction in ImageResizeService and refactor syntax check script 2026-04-28 19:56:39 +03:00
Divarion-D 235547168a feat: enhance installation process with dynamic port configuration and database updates 2026-04-21 21:48:10 +03:00
Divarion-D 17c7a16f78 refactor: streamline Docker installation process and update documentation 2026-04-18 18:09:12 +03:00
Divarion-D a81ada07bd chore(test-install): update test container to Ubuntu 24.04
- Base image: ubuntu:22.04 → ubuntu:24.04
- Remove python3-pip (not needed)
- Update README
2026-04-14 18:14:02 +03:00
Divarion-D d22b47bb65 feat(contributors): update the contributors table with new data and improve the update script 2026-04-11 22:08:14 +03:00
Divarion-D fb2bd8129c feat(tools): add Redis diagnostics tool 2026-04-11 22:06:04 +03:00