Commit Graph
1145 Commits
Author SHA1 Message Date
Divarion_D 5e061dd0b6 ci: move referenced issues to "Review & Deploy" instead of closing
Add a push-to-main workflow that parses issue references in the
pushed commit messages and moves each referenced issue's card on the
org Projects v2 board (Vateron-Media/projects/5) to the
"Review & Deploy" status column, without closing the issue.

Reuses the existing ADD_TO_PROJECT_PAT secret.
2026-08-15 17:31:06 +03:00
Divarion_D 3b163e3d47 fix(install): add libogg0 to debian13/ubuntu20/ubuntu24 package lists
The bundled ffmpeg/ffprobe 8.0 has a hard NEEDED dependency on
libogg.so.0. The debian13, ubuntu20 and ubuntu24 package lists (in
both the MAIN installer and the LB install flow) omitted libogg0,
so ffmpeg would fail to start with "error while loading shared
libraries: libogg.so.0" on those distributions.

Add libogg0 next to libnuma1 in all three lists, in both
install (PACKAGES) and LbInstallFlow::getPackages(), keeping MAIN
and LB in sync. redhat already ships the equivalent libogg RPM.

Refs: #152
2026-08-15 17:31:05 +03:00
Divarion_D 98fd38c928 fix(admin-api): dispatch table list actions in-process instead of dead table.php proxy
AdminAPIWrapper::TableAPI proxied over HTTP to `<code>/table.php` on the
broadcast port to reuse TableController's DataTables logic. That round-trip is
dead under the Front Controller, so every list action (get_lines, get_users,
get_streams, get_mags, …) returned literal `null`:

- the URL was built from `dirname($_SERVER['PHP_SELF'])`, which now resolves to
  `/public` (nginx sets SCRIPT_NAME=/public/index.php), not the access code;
- nginx.conf 404s every `*.php` (`location ~ \.php$ { return 404; }`);
- the api-scope access code routes every path back to AdminApiController, never
  TableController — so even a corrected URL just loops to "Invalid action".

`json_decode(curl_exec(...))` therefore always produced null.

Dispatch the handler in-process instead: seed RequestManager with the request
params and call TableController::index(), which authenticates via api_key and
echoes the JSON directly. Safe because XC_Bootstrap::boot() is idempotent and
TableController has no constructor deps; all TableAPI call sites are passthrough
`echo json_encode(...)`. Validated live against get_lines/users/streams/mags/
movies/stations plus show_columns filtering.
2026-08-14 20:51:33 +03:00
DanilandGitHub c67b16b4d2 Merge pull request #151 from Vateron-Media/chore/ministra-portal-5.6.10
feat(ministra): update portal to Ministra 5.6.10
2026-08-13 21:52:10 +03:00
Divarion_D 04a63dc858 docs(ministra): remove completed working-notes guides
Drop the two ministra restoration working-notes now that the work is
done: ministra-unused-modules.md (dead-module audit complete) and
ministra-5.6.10-lost-customizations.md (all items resolved). Also drop
the now-dangling doc reference from the get_modules debug comment.

The ministra-browser-emulation.md integration guide is kept.
2026-08-13 21:46:17 +03:00
Divarion_D 04b25e4e54 docs(ministra): resolve player/account/xpcom restoration items
Review of the remaining 5.6.10 lost-customization items:

- player.js: multi-audio (titles/infoCurtitle) kept and improved by
  5.6.10; xc_vm PVR branch intact; parental flow lives in tv.js, not
  here. No changes needed.
- account.js: keep the full 5.6.10 account screen (decision) instead of
  restoring the minimal Phone+message fork screen.
- xpcom.common.js: allowed_stb_types/aurahd richer in 5.6.10;
  outdated_firmware (player<1382) only gates non-whitelisted types and
  is more permissive, not a regression; check_image_version is a no-op
  (backend sends no autoupdate); load_channels parental left to 5.6.10 +
  tv.js guards to avoid a double password prompt.
2026-08-13 21:38:50 +03:00
Divarion_D ba4ed765dd fix(ministra): restore channel logos + xc_vm search button
Re-apply two xc_vm customizations that the 5.6.10 client drop wiped out:

- Channel logos (tv.js handling_block + player.js preview): on xc_vm the
  backend sends a ready-to-use logo URL, but 5.6.10 rebuilt a classic
  stalker misc/logos/<size>/ path, breaking the image. Use the URL
  directly; keep the 5.6.10 timeshift_mode class on the player logo.
- TV color buttons (tv.js): in the xc_vm theme the 4th button is channel
  search (search_menu_switcher) instead of the fav-manage "move" mode.
  Hybrid with the new 5.6.10 quality-filter button — quality wins when
  the profile enables tv_quality_filter, otherwise search.

Also drop the obsolete snumber/sortIDs note: the backend now numbers
channels sequentially, so 5.6.10 sortBy("number") is correct.
2026-08-13 21:30:52 +03:00
Divarion_D 8033b17bde docs(ministra): 5.6.10 restoration checklist + browser-emulation notes
- Add ministra-5.6.10-lost-customizations.md: what our fork's clean
  5.6.10 drop wiped out and how to restore it (get_types_list, tv.js
  parental control, channel logos, account screen, DVR .ts format, ...).
- Update ministra-browser-emulation.md for the ?debug_key gate.
2026-08-13 21:20:18 +03:00
Divarion_D e7a504243b feat(ministra): port client scripts to 5.6.10, restore xc_vm customizations
Bring xpcom.common.js, player.js, account.js, time_shift.js, version.js
and tv.js up to Ministra 5.6.10 while re-applying our fork's changes
that the clean upstream drop wiped out:

- xpcom.common.js: portal.php API endpoint + portal_path regex,
  get_types_list wiring, browser debug/emulation gate (?debug_key),
  layer.sclub_info in base_modules.
- tv.js: restore parental-control guards (genre.alias != "for adults")
  in the EPG, full-screen and preview paths. 5.6.10 switched the guard
  to genre.censored and added a home-genre lookup defaulting to
  censored=1, which prompted for a password on every channel open.
- time_shift.js: keep .ts segment format (flussonic-style DVR).
- account.js: keep the minimal account screen.

Remaining restoration items tracked in
docs/ru/guides/ministra-5.6.10-lost-customizations.md.
2026-08-13 21:20:09 +03:00
Divarion_D 386dcc567e feat(ministra): update portal backend for 5.6.10 client
- Add stb/get_types_list action: the 5.6.10 client fetches the allowed
  STB-type whitelist via a dedicated call and stores it in
  stb.allowed_stb_types (the field the client-side stb_type check reads,
  separate from the profile copy). Without it the response was empty and
  every device read as "not supported".
- Filter the get_preload_images list by is_file() so themes on sprites
  no longer 404 on removed per-module icons.
- Drop server handlers for deleted modules (weatherco, course,
  get_demo_video_parts) and their orphaned language keys / demo_video
  profile field in portal.php.
- Guard MAGSCAN serial/device checks behind !$rDebug so browser
  emulation (debug_key) can reach the portal.
2026-08-13 21:19:57 +03:00
Divarion_D f53b2372d0 chore(ministra): remove dead/unused portal modules
Drop portal JS modules that the loader never pulls in (not in
base_modules/all_modules, no dynamic loader, no live references) and
their theme assets:

- infoportal branch: infoportal, anecdote, cityinfo, horoscope,
  weather.current, weather.day, course.cbr, course.nbu, game.mastermind
  (root module dead + crashes main menu when force-enabled)
- magiccast (leaked proxy creds to an external Infomir service)
- youtube (web wrapper dir absent from repo)
- demo, service_management (stub features, no server handlers)
- karaoke (no backend; "radio only")
- pvr.js (legacy Pvr, never instantiated; live local PVR is
  pvr_local/records)
- JsHttpRequest-debug.js (core loads JsHttpRequest.js)

Removes 15 per-theme CSS variants + menu icons per module. The dead-code
audit is tracked in docs/ru/guides/ministra-unused-modules.md.
2026-08-13 21:19:43 +03:00
Divarion_D 139d1a0ad0 feat(ministra): update settings iframe to Ministra 5.6.10
Update external/settings (the STB device-settings iframe) to 5.6.10:
refreshed the 31 HTML pages, 6 CSS files and language files. This adds
support for newer STB models (MAG520/522/524/526/540, im44xx) and 4K
video output modes, plus the "576-50" -> "576p-50" mode-label fix and new
"Time format" strings. The main.js/pres.js engine already matched 5.6.10.

Rewrite the 5.6.10 script paths ../../c/*.js -> ../../*.js to match our
flattened src/Ministra layout (no c/ subdir). Note: this replaces our
previous custom settings skin with the 5.6.10 stock styling; verify the
settings screens on a device.
2026-08-12 20:58:54 +03:00
Divarion_D 30f128adf9 fix(ministra): guard modal hide in tv.js (cherry-pick from 5.6.10)
Port the 5.6.10 null-safety in the TV layer's hide(): guard
password_input before dereferencing .on (avoids a TypeError if it is
not yet created) and also close parent_password_promt on hide, so the
parental-control prompt is dismissed together with the TV layer.

Other 5.6.10 additions to the kept-ours core files were evaluated and
skipped as non-applicable or too risky: multi-audio/title support and
the STB model list already exist here, timezones are server-driven
(we don't use the client timezone_list), and the #33139 seek fix
would require editing the deeply-diverged player.js at 3 call sites.
2026-08-12 20:44:02 +03:00
Divarion_D 1f3e037753 feat(ministra): update stock themes to Ministra 5.6.10
Overlay the 5.6.10 cappuccino/default/digital/emerald/ocean_blue themes
(413 CSS reformatted to house style, 6 updated sprites/previews, plus
course.widget CSS). template/xc_vm (our own design) is left untouched.

Rename password_continer -> password_container in password_input.js and the
xc_vm theme CSS so our client pairs with 5.6.10's fixed stock CSS class.

The 5.6.10 RTL .otf fonts + fonts.css are intentionally omitted: our
index.html does not load the fonts module, so the client already renders
with fallback fonts (as it always has); shipping them would be ~14 MB of
dead weight.
2026-08-12 20:37:07 +03:00
Divarion_D 36aadfb427 feat(ministra): update client scripts to Ministra 5.6.10
Rebase our xc_vm customizations onto the 5.6.10 client scripts. Of the 72
common files, 39 already matched 5.6.10; 14 adopted verbatim (incl.
vclub.js movie-lock/password + HD filters and a settings.js query-string
fix); 4 merged per-hunk keeping our xc_vm design branches (global,
main_menu, layer.list, layer.base).

5 deeply-diverged core files (xpcom.common, player, tv, account,
time_shift) and index.html are intentionally left as-ours to preserve the
debug-mode (?mac=), parental control, .ts DVR and the custom account
screen; a cherry-pick list of the forgone 5.6.10 features is tracked
separately.
2026-08-12 20:29:44 +03:00
Divarion_D 3905ede5d1 fix(migrations): strip -- comments before splitting on ;
MigrationRunner split each .sql file into statements on `;` BEFORE
removing full-line `--` comments, so a semicolon inside a comment (e.g.
010_maxmind's "no GeoIP data; on the old schedule…") broke parsing:
the trailing comment fragment leaked onto the next statement, producing
invalid SQL that failed forever ([FAIL] not recorded — will retry).

Strip comment lines from the whole file first, then split on `;`.
2026-08-12 19:33:25 +03:00
Divarion_D 7659058382 fix(geoip): self-heal missing GeoLite2 DB instead of waiting for Tuesday
The installer downloads the GeoLite2 databases by running
`console.php cron:maxmind --force`, but that step is non-fatal and its exit
code is unchecked, so a transient network/GitHub failure at install time
leaves the panel with no GeoIP data and no error. Recovery then waited for the
next Tuesday because both the crontab schedule (0 4 * * 2) and the command's
internal gate restrict it to Tuesdays — meanwhile ministra/portal.php fatals on
the absent GeoLite2-Country.mmdb.

- MaxMindCronJob: when GeoLite2-Country.mmdb is missing, run regardless of the
  Tuesday gate. The download steps already fetch only the files that are
  actually absent, so a present database is still skipped.
- migration 010: move the `maxmind` crontab entry from Tuesday-only to daily so
  the self-heal can trigger within a day of a failed install download. The real
  weekly refresh still only happens on Tuesdays via the command's own gate. The
  UPDATE is guarded to the old default so a customised schedule is preserved.
2026-08-12 18:51:57 +03:00
Divarion_D 89dcefc7f5 style(ministra): reformat src/Ministra with prettier (house style baseline)
Run prettier over the whole src/Ministra tree (js/css/html/php) to normalise
to the project house style (tabs, LF, double quotes, printWidth 100). Pure
formatting, no behavioural change — this is the clean baseline so the upcoming
Ministra 5.6.10 client update diffs by content only, not whitespace.
2026-08-12 12:36:42 +03:00
DanilandGitHub 399997a3d0 Merge pull request #149 from Vateron-Media/refactor/ministra-to-core
refactor(ministra): move Stalker portal from module into core (src/Ministra)
2026-08-11 21:49:06 +03:00
Divarion_D 9ffbe1a34b build: add on-demand phpstan-deadcode audit (unused-public)
Adds a `make phpstan-deadcode` target and build/phpstan-deadcode.neon that
layer tomasvotruba/unused-public on top of the main PHPStan setup to report
unused PUBLIC methods/properties/constants. It is an on-demand audit, NOT a
CI gate (expect false positives for dynamically-invoked code — routes,
#[ListensTo] subscribers, CLI handlers, view templates).

unused-public is a require-dev package (installed by `make dev-tools`); the
committed vendor/ stays production-only.
2026-08-11 21:45:32 +03:00
Divarion_D 7b870b2ea7 docs(faq): MAGSCAN serial/device_id ban + how to clear blocked IPs
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.
2026-08-11 21:41:30 +03:00
Divarion_D fda7f41258 refactor(ministra): move Stalker portal from module into core (src/Ministra)
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.
2026-08-11 21:41:11 +03:00
Divarion_D 9c7231c6ca refactor(core): remove unused BoundaryInterface marker
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).
2026-08-11 19:22:09 +03:00
Divarion_D 11d3a83d99 chore(db): drop redundant require_once and dead Database imports
DatabaseHandler no longer manually require_once's Database.php — PSR-4
autoloading resolves the base class (verified on-server: both classes
class_exists() without the require). Also removes the unused
ServiceContainer import (docblock-only reference).

Strips 12 dead `use XcVm\Core\Database\Database;` imports that only
appeared in comments/docblocks; the 3 files that reference the base
type (bootstrap, ScannerCommand, admin/api view) keep theirs.

No behavioural change: Database and DatabaseHandler stay split; the
hot query() path is untouched. Full class merge intentionally deferred.

Verified: php -l, make gates (check-procedural-use), make phpstan
(No errors); console status green on 45.90.13.217 (bundled php/xcvm_core).
2026-08-11 18:48:06 +03:00
Divarion_D 33faee10ea Prepare release 2.3.9 2.3.9 2026-08-10 19:55:15 +03:00
Divarion_D 036457dbb3 chore(php): set xcvm_core.server_url in bundled php.ini 2026-08-10 19:37:10 +03:00
Divarion_D dd993da279 fix(db): don't let reconnect/watchdog fall back to xc_vm_migrat 2026-08-10 19:12:40 +03:00
Divarion_D f20cf039f7 Prepare release 2.3.8 2.3.8 2026-08-09 20:07:38 +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 bf7da54961 fix(geoip): make cron:maxmind --force actually re-download
--force only overrode the Tuesday-only gate; the per-database download decision
still skipped whatever was already up to date, so `cron:maxmind --force` just
printed "[SKIP] already up to date" and changed nothing.

--force now also bypasses the freshness check in both code paths:
- GitHub GeoLite2 fallback: re-download regardless of the md5 match.
- MaxMind API: drop the If-Modified-Since header so the server returns the
  database (200) instead of 304 Not Modified.

Verified on a live box: `cron:maxmind --force` now reports "[OK] updated" for
all three GeoLite2 databases and their mtimes advance (previously [SKIP]).
2026-08-09 19:20:08 +03:00
Divarion_D 4022993857 refactor(stream): StreamProcess P2/P3 tidy — dead/stale docs, dedupe update signal
- Remove an orphaned docblock (it described createChannelItem but sat above
  buildSubtitleImport, left behind by an earlier extraction).
- Refresh buildLive()'s docblock: it is now the sole command builder, not a
  shadow — the "run in shadow before it replaces the inline assembly" note was
  stale after the flip (0bd6a9dc).
- deleteCache(): if(!empty){…}else{return} -> early-return guard clause.
- updateStream()/updateStreams(): extract the identical check-then-insert of the
  cache-invalidation signal into insertCacheSignalOnce(); also drops the
  redundant double return in updateStreams and the unconditional getMainID()
  when caching is off.

Documentation/cosmetic + a behaviour-preserving dedupe. Refs: #148

Verified: php -l, make phpstan (No errors), make gates. Serve check on the hot
updateStream path (warm 567 -> m3u8 200/#EXTM3U).
2026-08-09 18:56:00 +03:00
Divarion_D 0bd6a9dc8d refactor(stream): flip startStream to buildLive(), remove inline assembly + shadow
Completes the in-progress FFmpeg-command migration. startStream's ~180-line
inline command assembly (and the shadow buildLive() diff-check that ran beside
it) are gone; the command is now built solely by the pure buildLive(), fed the
raw stream row. The delay-playlist bookkeeping (segment start + sleep window)
is hoisted just above the call since buildLive() consumes segmentStart /
delayActive. Net -171 lines.

buildLive() already did all transcode-attribute resolution and {TEMPLATE}
substitution internally, so feeding it the raw row (instead of the
post-mutation row the shadow used) removes a latent double-mutation in the
custom-transcode branch (transcode_profile_id == -1): json_decode((string)
$array) on an already-decoded value would have yielded [] there. Only the
inline path ever ran, so this was never a live bug — but it made the shadow an
invalid equivalence probe for that branch, which is why the flip is verified
directly rather than via the shadow log.

Equivalence verified two ways:
- Structural: the 129 command-building statements of buildLive() are
  line-for-line identical to the inline block (the sole non-command delta was a
  dead `$rFFPROBE = FFPROBE_BIN_40` assignment that never entered the string).
- Empirical: on a live box, the generated _.ffmpeg is byte-identical
  (md5-equal) before vs after the flip for BOTH a plain stream and a
  custom-transcode stream (enable_transcode=1, profile_id=-1, custom attrs),
  and the flipped stream serves (m3u8 200/#EXTM3U, ts 200 ~2.8MB).

Refs: #148

Verified: php -l, make phpstan (No errors), make gates.
2026-08-09 18:46:43 +03:00
Divarion_D 4644a0780c refactor(stream): dedupe stop-path PID lookup + row reset helpers
- pidFromFileOrColumn() replaces stopStream's two identical "read the PID from
  the sidecar file, else fall back to the streams_servers column" blocks (the
  monitor_pid and pid lookups). stopStream no longer needs its own $db handle.
- resetStreamServerRow() replaces the 11/12-column NULL-reset UPDATE duplicated
  in stopStream (with monitor_pid) and stopMovie (without) — the difference is
  now a $rWithMonitor flag.

Pure mechanical extraction, behaviour preserved. Refs: #148

Verified: php -l, make phpstan (No errors), make gates. Canaried live: warmed
stream 567 (pid+monitor running, sidecars present) then StreamProcess::stopStream
(567, true) -> row reset to pid/monitor NULL + status 0 and all _* sidecars
removed.
2026-08-09 18:30:57 +03:00
Divarion_D fbc5a1de1c refactor(stream): dedupe launcher key/iv + pid-segment cleanup helpers
Two byte-identical blocks were copied across the stream launchers:

- writeStreamKeyIv()       — the AES-128-CBC _.key/_.iv sidecar generation,
                             repeated verbatim in startStream, startLoopback and
                             startLLOD (3x)
- clearStreamPidSegments() — the "rm _*.ts + unlink stale _.pid" preamble,
                             repeated in startLoopback and startLLOD (2x)

Pure mechanical extraction, no behaviour change. Refs: #148

Verified: php -l, make phpstan (No errors), make gates. Canaried live via a
warmed stream (567): startStream produces a 16-byte _.key and 16-byte _.iv and
the m3u8 serves (HTTP 200, #EXTM3U).
2026-08-09 18:27:47 +03:00
Divarion_D 1fb7eea35c refactor(ondemand): dedupe the redis/DB stat-collection branches
The two arms building $rRows ran byte-identical queries — the active
on-demand stream list and the attached-restreamer counts — differing only in
where viewer counts came from (Redis getStreamConnections vs a lines_live
COUNT). Collapsed to one path: fetch the stream IDs and attached counts via the
new ConnectionTracker helpers once, then branch solely on the viewer-count
source. ~58 lines to ~27, and the DB arm's non-$r-prefixed locals
($online/$attached/$stream_id) are gone.

The empty-list usleep+continue guard, previously only on the DB arm, now covers
both (same timing, just skips the redundant work under Redis too). Behaviour
otherwise identical: same idle test (0 viewers, 0 attached, age>=30) and kill
path. Refs: #148

Verified: php -l, make phpstan (No errors), make gates. Not live-canaried on
purpose — this daemon kills streams; validation is static + logic-preservation.
2026-08-09 17:53:43 +03:00
Divarion_D 7774d6a735 refactor(stream): add on-demand-killer query helpers to ConnectionTracker
Three DatabaseAware helpers the OndemandCommand daemon can share instead of
inlining the same SQL twice (once per redis/DB branch):

- activeOnDemandStreamIDs()  — streams this server serves on-demand (pid set)
- attachedRestreamCounts()   — child restreamers per stream (parent_id, live
                               feed + monitor); a stream with any must not die
- onlineClientCounts()       — DB fallback for live viewers per stream when the
                               Redis store is off (Redis path uses
                               getStreamConnections)

Pure extraction, no behaviour change; wired up in the next commit. Refs: #148
2026-08-09 17:53:43 +03:00
Divarion_D acebb9f16f build(deps): sync committed production vendor to composer.lock
Commit a6b6bc92 ("deps: bump the composer group") bumped composer.json /
composer.lock but never re-committed a matching production vendor, so the
shipped src/vendor drifted from the lock: every `composer install --no-dev`
re-materialised the newer packages and left an un-cleanable diff (this is why
`make dev-clean` could not restore a pristine tree).

Regenerated with `composer install --no-dev` and committed the result:
- geoip2/geoip2 v3.3.0 -> v3.4.0 (adds Record/AnonymizerFeed, drops upstream
  CLAUDE.md, ships the package's docs/ + mise/lychee tooling as extracted —
  matching how other vendored packages are committed, no trimming policy)
- maxmind-db/reader 1.5.12 -> 1.5.13 (installed.php/json metadata; shipped PHP
  sources are byte-identical between the two patch releases)
- composer/ca-bundle cacert.pem refresh

The root-package self-reference in installed.php/json is kept at the committed
placeholder (per the dev-clean convention) to avoid per-commit HEAD-sha churn.
Verified: make gates (procedural-use, LB-archive, vendor-prod-only all OK),
php -l on the new/changed geoip2 sources. Refs: #148
2026-08-09 16:37:10 +03:00
Divarion_D ed39fd3b12 build(phpstan): regenerate baseline against the pinned 2.2.8
CI runs PHPStan from composer.lock (2.2.8), but the baseline had last been
frozen with a stale local dev-tools install (2.1.17). 2.2.8 reports several
identifiers with different wording (e.g. variable.undefined now emits
"Undefined variable: $x" instead of "Variable $x might not be defined"), so
ten baseline entries no longer matched and those pre-existing errors surfaced
in CI while staying hidden locally. reportUnmatchedIgnoredErrors:false hid the
reverse (stale entries silently dropped).

Regenerated with 2.2.8 after syncing dev-tools to the lock. Net effect is a
large shrink (479 lines removed, 47 added): 2.2.8 resolves much of the noise
2.1.17 false-flagged. No source change — these are all pre-existing entries,
now frozen under the tool version CI actually uses. Refs: #148
2026-08-09 16:25:00 +03:00
Divarion_D 3614f71f32 refactor(stream): dedupe live.php off-air + ip-match helpers; fix undefined $rServerID
- OffAirHandler::showNotOnAir() wraps the show_not_on_air_video / path call that
  live.php repeated 7 times with an identical 9-arg list.
- NetworkUtils::ipMatches($subnet, $target, $client) replaces the exact / same-/24
  ternary that appeared 3 times.
- Initialise $rServerID = SERVER_ID at the top: it was only assigned inside the
  `if ($rChannelInfo)` block, so the final off-air call (the channel-less path)
  read an undefined $rServerID.

Behaviour preserved. Validated live: ts serves (2.5MB by t=5s); an HLS token
requested twice returns #EXTM3U on both create and update (the update branch runs
the ip-match). Refs: #148

Verified: php -l, phpstan (No errors), make gates.
2026-08-09 16:15:46 +03:00
DanilandGitHub b85cb3bccd Merge pull request #146 from Vateron-Media/dependabot/composer/src/composer-dd2fcaaae8
deps: bump the composer group in /src with 3 updates
2026-08-09 16:07:57 +03:00
DanilandGitHub 8238ecf393 Merge branch 'main' into dependabot/composer/src/composer-dd2fcaaae8 2026-08-09 16:05:09 +03:00
DanilandGitHub 246a5b0f13 Merge pull request #145 from Vateron-Media/dependabot/github_actions/actions/checkout-7
ci: bump actions/checkout from 4 to 7
2026-08-09 16:04:35 +03:00
DanilandGitHub a34361c2ee Merge branch 'main' into dependabot/github_actions/actions/checkout-7 2026-08-09 16:02:50 +03:00
DanilandGitHub 1f18eb52c9 Merge pull request #144 from Vateron-Media/dependabot/github_actions/actions/upload-pages-artifact-5
ci: bump actions/upload-pages-artifact from 3 to 5
2026-08-09 16:02:31 +03:00
dependabot[bot]andGitHub c0e5cde892 ci: bump actions/upload-pages-artifact from 3 to 5
Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 5.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](https://github.com/actions/upload-pages-artifact/compare/v3...v5)

---
updated-dependencies:
- dependency-name: actions/upload-pages-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-09 12:59:54 +00:00
dependabot[bot]andGitHub e1040752dc ci: bump actions/checkout from 4 to 7
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-09 12:59:48 +00:00
DanilandGitHub e0607d70f4 Merge branch 'main' into dependabot/composer/src/composer-dd2fcaaae8 2026-08-09 15:59:33 +03:00
DanilandGitHub 8b27372c0c Merge pull request #143 from Vateron-Media/dependabot/github_actions/actions/configure-pages-6
ci: bump actions/configure-pages from 5 to 6
2026-08-09 15:57:44 +03:00
DanilandGitHub cbe47b1c0b Merge branch 'main' into dependabot/github_actions/actions/configure-pages-6 2026-08-09 15:55:59 +03:00