From 69d5cfaa0851dfb1d0852288d91dc34dfdd7c3e9 Mon Sep 17 00:00:00 2001 From: Divarion-D Date: Sat, 21 Feb 2026 19:59:51 +0300 Subject: [PATCH] Phase 3 is complete. --- ARCHITECTURE.md | 371 +- src/admin/table.php | 12 +- src/autoload.php | 38 + src/crons/errors.php | 9 +- src/domain/Auth/CodeRepository.php | 73 + src/domain/Auth/CodeService.php | 70 + src/domain/Auth/HMACRepository.php | 23 + src/domain/Auth/HMACService.php | 54 + src/domain/Auth/HMACValidator.php | 32 + src/domain/Bouquet/BouquetRepository.php | 30 + src/domain/Bouquet/BouquetService.php | 169 + src/domain/Device/DeviceSync.php | 36 + src/domain/Device/EnigmaService.php | 23 + src/domain/Device/MagService.php | 23 + src/domain/Epg/EpgRepository.php | 80 + src/domain/Epg/EpgService.php | 44 + src/domain/Line/LineRepository.php | 33 + src/domain/Line/LineService.php | 55 + src/domain/Line/PackageRepository.php | 16 + src/domain/Line/PackageService.php | 52 + src/domain/Security/BlocklistRepository.php | 132 + src/domain/Security/BlocklistService.php | 149 + src/domain/Server/ServerRepository.php | 154 + src/domain/Server/ServerService.php | 279 ++ src/domain/Settings/SettingsService.php | 129 + src/domain/Stream/CategoryService.php | 42 + src/domain/Stream/ChannelService.php | 444 +++ src/domain/Stream/CronGenerator.php | 28 + src/domain/Stream/M3UParser.php | 15 + src/domain/Stream/PlaylistGenerator.php | 395 +++ src/domain/Stream/StreamProcess.php | 1000 ++++++ src/domain/Stream/StreamRepository.php | 153 + src/domain/Stream/StreamService.php | 818 +++++ src/domain/Ticket/TicketService.php | 44 + src/domain/User/GroupRepository.php | 52 + src/domain/User/GroupService.php | 7 + src/domain/User/ProfileService.php | 27 + src/domain/User/UserRepository.php | 284 ++ src/domain/User/UserService.php | 23 + src/domain/Vod/EpisodeService.php | 387 +++ src/domain/Vod/MovieRepository.php | 32 + src/domain/Vod/MovieService.php | 239 ++ src/domain/Vod/SeriesRepository.php | 110 + src/domain/Vod/SeriesService.php | 121 + src/includes/CoreUtilities.php | 1533 +------- src/includes/StreamingUtilities.php | 220 +- src/includes/admin.php | 873 +---- src/includes/admin_api.php | 3468 ++----------------- 48 files changed, 6698 insertions(+), 5703 deletions(-) create mode 100644 src/domain/Auth/CodeRepository.php create mode 100644 src/domain/Auth/CodeService.php create mode 100644 src/domain/Auth/HMACRepository.php create mode 100644 src/domain/Auth/HMACService.php create mode 100644 src/domain/Auth/HMACValidator.php create mode 100644 src/domain/Bouquet/BouquetService.php create mode 100644 src/domain/Device/DeviceSync.php create mode 100644 src/domain/Device/EnigmaService.php create mode 100644 src/domain/Device/MagService.php create mode 100644 src/domain/Epg/EpgRepository.php create mode 100644 src/domain/Epg/EpgService.php create mode 100644 src/domain/Line/LineRepository.php create mode 100644 src/domain/Line/LineService.php create mode 100644 src/domain/Line/PackageRepository.php create mode 100644 src/domain/Line/PackageService.php create mode 100644 src/domain/Security/BlocklistRepository.php create mode 100644 src/domain/Security/BlocklistService.php create mode 100644 src/domain/Server/ServerService.php create mode 100644 src/domain/Settings/SettingsService.php create mode 100644 src/domain/Stream/CategoryService.php create mode 100644 src/domain/Stream/ChannelService.php create mode 100644 src/domain/Stream/CronGenerator.php create mode 100644 src/domain/Stream/M3UParser.php create mode 100644 src/domain/Stream/PlaylistGenerator.php create mode 100644 src/domain/Stream/StreamProcess.php create mode 100644 src/domain/Stream/StreamRepository.php create mode 100644 src/domain/Stream/StreamService.php create mode 100644 src/domain/Ticket/TicketService.php create mode 100644 src/domain/User/GroupRepository.php create mode 100644 src/domain/User/GroupService.php create mode 100644 src/domain/User/ProfileService.php create mode 100644 src/domain/User/UserRepository.php create mode 100644 src/domain/User/UserService.php create mode 100644 src/domain/Vod/EpisodeService.php create mode 100644 src/domain/Vod/MovieRepository.php create mode 100644 src/domain/Vod/MovieService.php create mode 100644 src/domain/Vod/SeriesRepository.php create mode 100644 src/domain/Vod/SeriesService.php diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 1882853d..41c791f0 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -9,7 +9,8 @@ 5. [Карта миграции: откуда → куда](#5-карта-миграции-откуда--куда) 6. [Система модулей](#6-система-модулей) 7. [Границы ядра и модулей](#7-границы-ядра-и-модулей) -8. [Порядок миграции](#8-порядок-миграции) +8. [Варианты сборки: MAIN vs LoadBalancer](#8-варианты-сборки-main-vs-loadbalancer) +9. [Порядок миграции](#9-порядок-миграции) --- @@ -796,7 +797,193 @@ modules/ --- -## 8. Порядок миграции +## 8. Варианты сборки: MAIN vs LoadBalancer + +### 8.1. Два артефакта из одной кодовой базы + +Система собирается в два варианта из одного `src/`: + +| Артефакт | Назначение | Что включает | Что исключает | +|----------|-----------|--------------|---------------| +| **MAIN** | Основной сервер (admin + streaming) | Всё содержимое `src/` | Ничего | +| **LoadBalancer (LB)** | Стриминг-сервер без управления | Только стриминг + инфраструктура | Админ-панель, реселлер, player, ministra, admin-only модули | + +**Ключевой принцип:** LB — это подмножество MAIN. Код не форкается, а фильтруется при сборке. + +### 8.2. Текущая проблема (до миграции Makefile) + +`Makefile` собирает LB из фиксированного списка директорий (`LB_FILES`): + +```makefile +# ТЕКУЩИЙ (устаревший) список: +LB_FILES := bin config content crons includes signals tmp www status update service +``` + +**Эти новые директории НЕ входят в LB_FILES и НЕ копируются в LB:** + +| Директория / файл | Нужен LB? | Статус | +|----|---|---| +| `autoload.php` | ✅ ДА — без него новые классы не загружаются | ⚠ Отсутствует в LB | +| `bootstrap.php` | ✅ ДА — единая точка инициализации (CONTEXT_STREAM) | ⚠ Отсутствует в LB | +| `core/` | ✅ ДА — Database, Cache, Auth, Config, Process, Http, Logging, Container, Util | ⚠ Отсутствует в LB | +| `domain/` | ⚠ ЧАСТИЧНО — Stream/, Server/, Bouquet/ нужны; User/, Ticket/, Device/ — нет | ⚠ Отсутствует в LB | +| `streaming/` | ✅ ДА — весь стриминг-движок | ⚠ Отсутствует в LB | +| `infrastructure/` | ✅ ДА — redis/, nginx/ | ⚠ Отсутствует в LB | +| `resources/` | ⚠ ЧАСТИЧНО — data/error_codes.php нужен; langs/ — нет | ⚠ Отсутствует в LB | +| `data/` | ⚠ ЧАСТИЧНО — runtime-данные создаются динамически | ⚠ Отсутствует в LB | +| `interfaces/` | ⚠ ЧАСТИЧНО — Cli/ частично нужен | ⚠ Отсутствует в LB | +| `modules/` | ❌ НЕТ — все текущие модули admin-only | ⚠ Отсутствует в LB | +| `admin/` | ❌ НЕТ | ✅ Корректно отсутствует | +| `ministra/` | ❌ НЕТ | ✅ Корректно отсутствует | +| `player/` | ❌ НЕТ | ✅ Корректно отсутствует | +| `reseller/` | ❌ НЕТ | ✅ Корректно отсутствует | + +**Последствие:** Proxy-методы в `CoreUtilities.php` (который IS копируется в LB через `includes/`) вызывают классы из `core/` и `domain/`, но эти директории отсутствуют → **LB-сборка ломается при вызове мигрированного кода**. + +### 8.3. Целевая конфигурация Makefile + +#### Обновлённый `LB_FILES` + +```makefile +# ЦЕЛЕВОЙ (обновлённый) список: +LB_FILES := autoload.php bootstrap.php \ + bin config content core crons data domain includes infrastructure \ + interfaces resources signals streaming tmp www status update service +``` + +#### Обновлённый `LB_DIRS_TO_REMOVE` + +```makefile +LB_DIRS_TO_REMOVE := \ + # Существующие исключения: + bin/install \ + bin/redis \ + includes/langs \ + includes/api \ + includes/libs/resources \ + bin/nginx/conf/codes \ + # Новые исключения для целевой архитектуры: + admin \ + ministra \ + player \ + reseller \ + domain/User \ + domain/Ticket \ + domain/Device \ + interfaces/Http/Controllers/Admin \ + interfaces/Http/Controllers/Reseller \ + interfaces/Http/Views \ + interfaces/Player \ + modules/fingerprint \ + modules/magscan \ + modules/ministra \ + modules/plex \ + modules/theft-detection \ + modules/tmdb \ + modules/watch \ + resources/langs +``` + +#### Обновлённый `LB_FILES_TO_REMOVE` + +```makefile +LB_FILES_TO_REMOVE := \ + # Существующие (пока admin.php/admin_api.php живут в includes/): + includes/admin_api.php \ + includes/admin.php \ + includes/reseller_api.php \ + # ... остальные существующие исключения ... + # Новые: + includes/cli/migrate.php \ + includes/cli/cache_handler.php \ + includes/cli/balancer.php \ + crons/backups.php \ + crons/cache_engine.php \ + crons/epg.php \ + crons/update.php \ + crons/providers.php \ + crons/root_mysql.php \ + crons/series.php \ + crons/tmdb.php \ + crons/tmdb_popular.php +``` + +### 8.4. Что LB использует — карта зависимостей + +``` +www/stream/*.php ──→ bootstrap.php (CONTEXT_STREAM) + │ + ├──→ autoload.php (class map) + ├──→ core/Config/ (пути, конфигурация) + ├──→ core/Database/ (PDO-обёртка) + ├──→ core/Cache/ (Redis + File кэш) + ├──→ core/Auth/ (BruteforceGuard) + ├──→ core/Http/ (Request, RequestGuard) + ├──→ core/Process/ (ProcessManager) + ├──→ core/Logging/ (FileLogger, DatabaseLogger) + ├──→ core/Util/ (GeoIP, NetworkUtils, Encryption) + ├──→ core/Error/ (ErrorHandler, ErrorCodes) + │ + ├──→ streaming/Auth/ (TokenAuth, StreamAuth, DeviceLock) + ├──→ streaming/Delivery/ (LiveDelivery, VodDelivery, SegmentReader) + ├──→ streaming/Balancer/ (LoadBalancer, RedirectStrategy) + ├──→ streaming/Protection/(RestreamDetector, ConnectionLimiter, GeoBlock) + ├──→ streaming/Codec/ (FFmpegCommand, TsParser) + │ + ├──→ domain/Stream/ (StreamProcess, ConnectionTracker, StreamSorter) + ├──→ domain/Server/ (ServerRepository) + ├──→ domain/Bouquet/ (BouquetMapper — для плейлистов) + ├──→ domain/Vod/ (для VOD-доставки) + │ + ├──→ infrastructure/redis/(RedisManager) + └──→ resources/data/ (error_codes.php) + +crons/*.php (на LB) ──→ bootstrap.php (CONTEXT_CLI) + └──→ те же зависимости + domain/Stream/* +``` + +### 8.5. Правила для разработки с учётом LB + +1. **Proxy-методы в `CoreUtilities`/`StreamingUtilities`** должны быть безопасны для LB. Пока `autoload.php` и `core/`/`domain/` не добавлены в LB-сборку, proxy-вызовы сломают LB. + +2. **Makefile должен обновляться синхронно с миграцией**, конкретно при добавлении новых `LB_FILES` каждый раз, когда код из `includes/` мигрирует в новые директории. + +3. **Тестирование LB-сборки** — после каждой фазы миграции нужно проверять: + ```bash + make new && make lb + # Убедиться, что все нужные файлы присутствуют + # Убедиться, что admin-only файлы отсутствуют + ``` + +4. **domain/ частично нужен LB** — нельзя целиком исключать `domain/`. Исключаются только admin-specific поддомены (`User/`, `Ticket/`, `Device/`). + +5. **modules/ полностью исключается из LB** — все текущие модули (ministra, plex, tmdb, watch, fingerprint, theft-detection, magscan) — это admin-функциональность. В будущем, если появится LB-specific модуль, его нужно будет явно добавить. + +### 8.6. Переходный период (сейчас) + +Пока миграция идёт итеративно (фазы 1-3 в процессе), Makefile нужно обновить **немедленно**: + +**Минимально необходимое изменение (`LB_FILES`):** +```makefile +LB_FILES := bin config content core crons domain includes \ + infrastructure resources signals streaming tmp www status update service +``` + +**Плюс два root-файла** — `autoload.php` и `bootstrap.php` — нужно копировать отдельно, т.к. `LB_FILES` работает только с директориями: +```makefile +lb_copy_files: + # ... существующий код ... + @echo "==> [LB] Copying root files" + @for root_file in autoload.php bootstrap.php; do \ + if [ -f "$(MAIN_DIR)/$$root_file" ]; then \ + cp "$(MAIN_DIR)/$$root_file" "$(TEMP_DIR)/$$root_file"; \ + fi; \ + done +``` + +--- + +## 9. Порядок миграции ### Принцип: извлечение → делегирование → замена @@ -1099,6 +1286,24 @@ API::moveStreams() (стр. 5998–6045) → StreamService::m API::replaceDNS() (стр. 6046–6059) → StreamService::replaceDNS() API::massDeleteStreams() (стр. 1479–1496) → StreamService::massDelete() ``` +- ✅ `API::processChannel()` вынесен в `domain/Stream/ChannelService.php` (proxy в `admin_api.php`) +- ✅ `API::processStream()` вынесен в `domain/Stream/StreamService.php` (proxy в `admin_api.php`) +- ✅ `API::massEditStreams()` вынесен в `domain/Stream/StreamService::massEdit()` (proxy в `admin_api.php`) +- ✅ `API::massEditChannels()` вынесен в `domain/Stream/ChannelService::massEdit()` (proxy в `admin_api.php`) +- ✅ `API::processCategory()` и `API::orderCategories()` вынесены в `domain/Stream/CategoryService.php` (proxy в `admin_api.php`) +- ✅ `API::moveStreams()` и `API::replaceDNS()` вынесены в `domain/Stream/StreamService.php` (proxy в `admin_api.php`) +- ✅ `API::setChannelOrder()` вынесен в `domain/Stream/ChannelService::setOrder()` (proxy в `admin_api.php`) +- ✅ `API::massDeleteStreams()` вынесен в `domain/Stream/StreamService::massDelete()` (proxy в `admin_api.php`) +- ✅ Из `admin.php` вынесены `getStream()/getStreamStats()/getStreamErrors()/getStreamPIDs()/getStreamOptions()/getStreamSys()/getNextOrder()` в `domain/Stream/StreamRepository.php` +- ✅ Из `admin.php` `parseM3U()` вынесен в `domain/Stream/M3UParser.php` +- ✅ Из `admin.php` `getCategories()` переключён на `domain/Stream/CategoryRepository.php` +- ✅ Из `CoreUtilities` вынесены в `domain/Stream/StreamProcess.php`: `deleteCache()`, `queueChannel()`, `createChannel()`, `startMonitor()`, `startProxy()`, `startThumbnail()`, `updateStream()`, `updateStreams()` (proxy в `CoreUtilities`) +- ✅ Из `CoreUtilities` вынесены в `domain/Stream/StreamProcess.php`: `createChannelItem()`, `stopStream()` (proxy в `CoreUtilities`) +- ✅ Дополнительно из `CoreUtilities` вынесены в `domain/Stream/StreamProcess.php`: `startMovie()`, `stopMovie()` (proxy в `CoreUtilities`) +- ✅ Дополнительно из `CoreUtilities` вынесен в `domain/Stream/StreamProcess.php`: `startLoopback()` (proxy в `CoreUtilities`) +- ✅ Дополнительно из `CoreUtilities` вынесены в `domain/Stream/StreamProcess.php`: `queueMovie()`, `queueMovies()`, `refreshMovies()` (proxy в `CoreUtilities`) +- ✅ Дополнительно из `CoreUtilities` вынесен в `domain/Stream/StreamProcess.php`: `startStream()` (proxy в `CoreUtilities`) +- ✅ Дополнительно из `CoreUtilities` вынесен в `domain/Stream/StreamProcess.php`: `startLLOD()` (proxy в `CoreUtilities`) **Из `admin.php`:** ``` @@ -1147,6 +1352,17 @@ API::processEpisode() (стр. 821–1074, 254 строки) → domain/Vod/E API::massEditEpisodes() (стр. 1075–1259, 185 строк) → EpisodeService::massEdit() API::massDeleteEpisodes()(стр. 1623–1642) → EpisodeService::massDelete() ``` +- ✅ `API::massDeleteMovies()` вынесен в `domain/Vod/MovieService::massDelete()` (proxy в `admin_api.php`) +- ✅ `API::massDeleteSeries()` вынесен в `domain/Vod/SeriesService::massDelete()` (proxy в `admin_api.php`) +- ✅ `API::massDeleteEpisodes()` вынесен в `domain/Vod/EpisodeService::massDelete()` (proxy в `admin_api.php`) +- ✅ `API::massEditMovies()` вынесен в `domain/Vod/MovieService::massEdit()` (proxy в `admin_api.php`) +- ✅ `API::massEditSeries()` вынесен в `domain/Vod/SeriesService::massEdit()` (proxy в `admin_api.php`) +- ✅ `API::massEditEpisodes()` вынесен в `domain/Vod/EpisodeService::massEdit()` (proxy в `admin_api.php`) +- ✅ `API::processEpisode()` вынесен в `domain/Vod/EpisodeService::process()` (proxy в `admin_api.php`) +- ✅ `API::processMovie()` вынесен в `domain/Vod/MovieService::process()` (proxy в `admin_api.php`) +- ✅ `API::processSeries()` вынесен в `domain/Vod/SeriesService::process()` (proxy в `admin_api.php`) +- ✅ `API::importMovies()` вынесен в `domain/Vod/MovieService::import()` (proxy в `admin_api.php`) +- ✅ `API::importSeries()` вынесен в `domain/Vod/SeriesService::import()` (proxy в `admin_api.php`) **Из `admin.php`:** ``` @@ -1155,9 +1371,16 @@ updateSeries() (стр. 164) → updateSeriesAsync() (стр. 207) → getSimilarMovies() (стр. 4104) → domain/Vod/MovieRepository.php getSimilarSeries() (стр. 4116) → domain/Vod/SeriesRepository.php -deleteMovieFile() (стр. 3717) → MovieService::deleteFile() -generateSeriesPlaylist() (стр. 4367) → SeriesService::generatePlaylist() +deleteMovieFile() (стр. 3717) → MovieRepository::deleteFile() +generateSeriesPlaylist() (стр. 4367) → SeriesRepository::generatePlaylist() ``` +- ✅ `getSeriesList()` вынесен в `domain/Vod/SeriesRepository::getList()` (proxy в `admin.php`) +- ✅ `updateSeries()` вынесен в `domain/Vod/SeriesRepository::updateFromTMDB()` (proxy в `admin.php`) +- ✅ `updateSeriesAsync()` вынесен в `domain/Vod/SeriesRepository::queueRefresh()` (proxy в `admin.php`) +- ✅ `getSimilarMovies()` вынесен в `domain/Vod/MovieRepository::getSimilar()` (proxy в `admin.php`) +- ✅ `getSimilarSeries()` вынесен в `domain/Vod/SeriesRepository::getSimilar()` (proxy в `admin.php`) +- ✅ `deleteMovieFile()` вынесен в `domain/Vod/MovieRepository::deleteFile()` (proxy в `admin.php`) +- ✅ `generateSeriesPlaylist()` вынесен в `domain/Vod/SeriesRepository::generatePlaylist()` (proxy в `admin.php`) **Из `CoreUtilities`:** ``` @@ -1178,6 +1401,11 @@ CoreUtilities::deleteLines() (стр. 3858) → CoreUtilities::updateLine() (стр. 3861) → LineService::update() CoreUtilities::updateLines() (стр. 3872) → ``` +- ✅ `API::massDeleteLines()` вынесен в `domain/Line/LineService::massDelete()` (proxy в `admin_api.php`) +- ✅ `API::massEditLines()` вынесен в `domain/Line/LineService::massEdit()` (proxy в `admin_api.php`) +- ✅ `API::processLine()` вынесен в `domain/Line/LineService::process()` (proxy в `admin_api.php`) +- ✅ `admin.php::deleteLines()` вынесен в `domain/Line/LineRepository::deleteMany()` (proxy в `admin.php`) +- ✅ `CoreUtilities::deleteLine()/deleteLines()/updateLine()/updateLines()` вынесены в `domain/Line/LineService` (proxy в `CoreUtilities`) #### Шаг 3.4 — domain/User/ (пользователи, группы, реселлеры) ``` @@ -1200,6 +1428,22 @@ admin.php::deleteGroup() (стр. 3548) → StreamingUtilities::getUserInfo() (стр. 970) → domain/User/UserRepository (streaming-вариант) ``` +- ✅ `API::massDeleteUsers()` вынесен в `domain/User/UserService::massDelete()` (proxy в `admin_api.php`) +- ✅ `API::massEditUsers()` вынесен в `domain/User/UserService::massEdit()` (proxy в `admin_api.php`) +- ✅ `API::processUser()` вынесен в `domain/User/UserService::process()` (proxy в `admin_api.php`) +- ✅ `API::processGroup()` вынесен в `domain/User/GroupService::process()` (proxy в `admin_api.php`) +- ✅ `admin.php::getUserInfo()` вынесен в `domain/User/UserRepository::getAuthUserByCredentials()` (proxy в `admin.php`) +- ✅ `admin.php::getUser()` вынесен в `domain/User/UserRepository::getLineById()` (proxy в `admin.php`) +- ✅ `admin.php::getRegisteredUser()` вынесен в `domain/User/UserRepository::getRegisteredUserById()` (proxy в `admin.php`) +- ✅ `admin.php::getRegisteredUsers()` вынесен в `domain/User/UserRepository::getRegisteredUsers()` (proxy в `admin.php`) +- ✅ `admin.php::getResellers()` вынесен в `domain/User/UserRepository::getResellers()` (proxy в `admin.php`) +- ✅ `admin.php::getDirectReports()` вынесен в `domain/User/UserRepository::getDirectReports()` (proxy в `admin.php`) +- ✅ `admin.php::getSubUsers()` вынесен в `domain/User/UserRepository::getSubUsers()` (proxy в `admin.php`) +- ✅ `admin.php::getParent()` вынесен в `domain/User/UserRepository::getParent()` (proxy в `admin.php`) +- ✅ `admin.php::getMemberGroups()` вынесен в `domain/User/GroupRepository::getAll()` (proxy в `admin.php`) +- ✅ `admin.php::getMemberGroup()` вынесен в `domain/User/GroupRepository::getById()` (proxy в `admin.php`) +- ✅ `admin.php::deleteGroup()` вынесен в `domain/User/GroupRepository::deleteById()` (proxy в `admin.php`) +- ✅ `StreamingUtilities::getUserInfo()` вынесен в `domain/User/UserRepository::getStreamingUserInfo()` (proxy в `StreamingUtilities`) #### Шаг 3.5 — domain/Device/ (MAG + Enigma) ``` @@ -1213,6 +1457,13 @@ API::massDeleteEnigmas() (стр. 1587–1604) → EnigmaService::massDelete() admin.php::syncDevices() (стр. 371) → domain/Device/DeviceSync.php ``` +- ✅ `API::massDeleteMags()` вынесен в `domain/Device/MagService::massDelete()` (proxy в `admin_api.php`) +- ✅ `API::massDeleteEnigmas()` вынесен в `domain/Device/EnigmaService::massDelete()` (proxy в `admin_api.php`) +- ✅ `API::massEditMags()` вынесен в `domain/Device/MagService::massEdit()` (proxy в `admin_api.php`) +- ✅ `API::massEditEnigmas()` вынесен в `domain/Device/EnigmaService::massEdit()` (proxy в `admin_api.php`) +- ✅ `API::processMAG()` вынесен в `domain/Device/MagService::process()` (proxy в `admin_api.php`) +- ✅ `API::processEnigma()` вынесен в `domain/Device/EnigmaService::process()` (proxy в `admin_api.php`) +- ✅ `admin.php::syncDevices()` вынесен в `domain/Device/DeviceSync::syncLineDevices()` (proxy в `admin.php`) #### Шаг 3.6 — domain/Server/ ``` @@ -1236,83 +1487,101 @@ admin.php::freeTemp() (стр. 4173) → admin.php::freeStreams() (стр. 4177) → ``` +- ✅ `API::processServer()` вынесен в `domain/Server/ServerService::process()` (proxy в `admin_api.php`) +- ✅ `API::processProxy()` вынесен в `domain/Server/ServerService::processProxy()` (proxy в `admin_api.php`) +- ✅ `API::installServer()` вынесен в `domain/Server/ServerService::install()` (proxy в `admin_api.php`) +- ✅ `API::orderServers()` вынесен в `domain/Server/ServerService::reorder()` (proxy в `admin_api.php`) +- ✅ `admin.php::getAllServers()` вынесен в `domain/Server/ServerRepository::getAllSimple()` (proxy в `admin.php`) +- ✅ `admin.php::getStreamingServers()` вынесен в `domain/Server/ServerRepository::getStreamingSimple()` (proxy в `admin.php`) +- ✅ `admin.php::getProxyServers()` вынесен в `domain/Server/ServerRepository::getProxySimple()` (proxy в `admin.php`) +- ✅ `admin.php::getFreeSpace()` вынесен в `domain/Server/ServerRepository::getFreeSpace()` (proxy в `admin.php`) +- ✅ `admin.php::getStreamsRamdisk()` вынесен в `domain/Server/ServerRepository::getStreamsRamdisk()` (proxy в `admin.php`) +- ✅ `admin.php::killPID()` вынесен в `domain/Server/ServerRepository::killPID()` (proxy в `admin.php`) +- ✅ `admin.php::getRTMPStats()` вынесен в `domain/Server/ServerRepository::getRTMPStats()` (proxy в `admin.php`) +- ✅ `admin.php::deleteServer()` вынесен в `domain/Server/ServerRepository::deleteById()` (proxy в `admin.php`) +- ✅ `admin.php::probeSource()` вынесен в `domain/Server/ServerRepository::probeSource()` (proxy в `admin.php`) +- ✅ `admin.php::getSSLLog()` вынесен в `domain/Server/ServerRepository::getSSLLog()` (proxy в `admin.php`) +- ✅ `admin.php::checksource()` вынесен в `domain/Server/ServerRepository::checkSource()` (proxy в `admin.php`) +- ✅ `admin.php::freeTemp()` вынесен в `domain/Server/ServerRepository::freeTemp()` (proxy в `admin.php`) +- ✅ `admin.php::freeStreams()` вынесен в `domain/Server/ServerRepository::freeStreams()` (proxy в `admin.php`) + #### Шаг 3.7 — domain/Bouquet/ ``` -API::processBouquet() (стр. 156–244) → domain/Bouquet/BouquetService.php -API::reorderBouquet() (стр. 379–395) → BouquetService::reorder() -API::sortBouquets() (стр. 456–498) → BouquetService::sort() +- ✅ `API::processBouquet()` вынесен в `domain/Bouquet/BouquetService::process()` (proxy в `admin_api.php`) +- ✅ `API::reorderBouquet()` вынесен в `domain/Bouquet/BouquetService::reorder()` (proxy в `admin_api.php`) +- ✅ `API::sortBouquets()` вынесен в `domain/Bouquet/BouquetService::sort()` (proxy в `admin_api.php`) -admin.php::getBouquets() (стр. 3954) → domain/Bouquet/BouquetRepository.php -admin.php::getBouquetOrder() (стр. 3969) → -admin.php::getUserBouquets() (стр. 3939) → -admin.php::scanBouquets() (стр. 4269) → BouquetService::scan() -admin.php::scanBouquet() (стр. 4279) → BouquetService::scanOne() +- ✅ `admin.php::getBouquets()` вынесен в `domain/Bouquet/BouquetRepository::getAllSimple()` (proxy в `admin.php`) +- ✅ `admin.php::getBouquetOrder()` вынесен в `domain/Bouquet/BouquetRepository::getOrder()` (proxy в `admin.php`) +- ✅ `admin.php::getUserBouquets()` вынесен в `domain/Bouquet/BouquetRepository::getUserBouquets()` (proxy в `admin.php`) +- ✅ `admin.php::scanBouquets()` вынесен в `domain/Bouquet/BouquetService::scan()` (proxy в `admin.php`) +- ✅ `admin.php::scanBouquet()` вынесен в `domain/Bouquet/BouquetService::scanOne()` (proxy в `admin.php`) ``` #### Шаг 3.8 — domain/Epg/ ``` -API::processEPG() (стр. 731–764) → domain/Epg/EpgService.php +- ✅ `API::processEPG()` вынесен в `domain/Epg/EpgService::process()` (proxy в `admin_api.php`) -admin.php::getchannelepg() (стр. 4185) → domain/Epg/EpgRepository.php -admin.php::getEPG() (стр. 4200) → -admin.php::findEPG() (стр. 3525) → +- ✅ `admin.php::getchannelepg()` вынесен в `domain/Epg/EpgService::getChannelEpg()` (proxy в `admin.php`) +- ✅ `admin.php::getEPG()` вынесен в `domain/Epg/EpgRepository::getById()` (proxy в `admin.php`) +- ✅ `admin.php::findEPG()` вынесен в `domain/Epg/EpgRepository::findByName()` (proxy в `admin.php`) -CoreUtilities::getEPG() (стр. 4502) → -CoreUtilities::getEPGs() (стр. 4517) → -CoreUtilities::getProgramme()(стр. 4524) → -CoreUtilities::searchEPG() (стр. 672) → +- ✅ `CoreUtilities::getEPG()` вынесен в `domain/Epg/EpgRepository::getStreamEpg()` (proxy в `CoreUtilities.php`) +- ✅ `CoreUtilities::getEPGs()` вынесен в `domain/Epg/EpgRepository::getStreamsEpg()` (proxy в `CoreUtilities.php`) +- ✅ `CoreUtilities::getProgramme()` вынесен в `domain/Epg/EpgRepository::getProgramme()` (proxy в `CoreUtilities.php`) +- ✅ `CoreUtilities::searchEPG()` вынесен в `domain/Epg/EpgRepository::search()` (proxy в `CoreUtilities.php`) ``` #### Шаг 3.9 — domain/Settings/ + domain/Ticket/ ``` -API::editSettings() (стр. 4684) → domain/Settings/SettingsService.php -API::editBackupSettings() (стр. 4754) → -API::editCacheCron() (стр. 4798) → -API::editAdminProfile() (стр. 396) → domain/User/ProfileService.php +- ✅ `API::editSettings()` вынесен в `domain/Settings/SettingsService::edit()` (proxy в `admin_api.php`) +- ✅ `API::editBackupSettings()` вынесен в `domain/Settings/SettingsService::editBackup()` (proxy в `admin_api.php`) +- ✅ `API::editCacheCron()` вынесен в `domain/Settings/SettingsService::editCacheCron()` (proxy в `admin_api.php`) +- ✅ `API::editAdminProfile()` вынесен в `domain/User/ProfileService::editAdminProfile()` (proxy в `admin_api.php`) -API::submitTicket() (стр. 6060) → domain/Ticket/TicketService.php +- ✅ `API::submitTicket()` вынесен в `domain/Ticket/TicketService::submit()` (proxy в `admin_api.php`) ``` #### Шаг 3.10 — domain/Security/ (IP, ISP, UA, RTMP) ``` -API::processISP() (стр. 1351) → domain/Security/BlocklistService.php -API::processUA() (стр. 6110) → -API::blockIP() (стр. 428) → -API::processRTMPIP() (стр. 3670) → +- ✅ `API::processISP()` вынесен в `domain/Security/BlocklistService::processISP()` (proxy в `admin_api.php`) +- ✅ `API::processUA()` вынесен в `domain/Security/BlocklistService::processUA()` (proxy в `admin_api.php`) +- ✅ `API::blockIP()` вынесен в `domain/Security/BlocklistService::blockIP()` (proxy в `admin_api.php`) +- ✅ `API::processRTMPIP()` вынесен в `domain/Security/BlocklistService::processRTMPIP()` (proxy в `admin_api.php`) -admin.php::getBlockedIPs() (стр. 3984) → domain/Security/BlocklistRepository.php -admin.php::getRTMPIPs() (стр. 3999) → +- ✅ `admin.php::getBlockedIPs()` вынесен в `domain/Security/BlocklistRepository::getBlockedIPsSimple()` (proxy в `admin.php`) +- ✅ `admin.php::getRTMPIPs()` вынесен в `domain/Security/BlocklistRepository::getRTMPIPsSimple()` (proxy в `admin.php`) -StreamingUtilities::checkBlockedUAs() (стр. 1178) → -StreamingUtilities::checkISP() (стр. 1327) → -StreamingUtilities::checkServer() (стр. 1336) → -CoreUtilities::getBlockedUA() (стр. 180) → -CoreUtilities::getBlockedIPs() (стр. 194) → -CoreUtilities::getBlockedISP() (стр. 211) → -CoreUtilities::getBlockedServers() (стр. 225) → -CoreUtilities::getProxyIPs() (стр. 155) → +- ✅ `StreamingUtilities::checkBlockedUAs()` вынесен в `domain/Security/BlocklistService::checkBlockedUAs()` (proxy в `StreamingUtilities.php`) +- ✅ `StreamingUtilities::checkISP()` вынесен в `domain/Security/BlocklistService::checkISP()` (proxy в `StreamingUtilities.php`) +- ✅ `StreamingUtilities::checkServer()` вынесен в `domain/Security/BlocklistService::checkServer()` (proxy в `StreamingUtilities.php`) +- ✅ `CoreUtilities::getBlockedUA()` вынесен в `domain/Security/BlocklistRepository::getBlockedUA()` (proxy в `CoreUtilities.php`) +- ✅ `CoreUtilities::getBlockedIPs()` вынесен в `domain/Security/BlocklistRepository::getBlockedIPs()` (proxy в `CoreUtilities.php`) +- ✅ `CoreUtilities::getBlockedISP()` вынесен в `domain/Security/BlocklistRepository::getBlockedISP()` (proxy в `CoreUtilities.php`) +- ✅ `CoreUtilities::getBlockedServers()` вынесен в `domain/Security/BlocklistRepository::getBlockedServers()` (proxy в `CoreUtilities.php`) +- ✅ `CoreUtilities::getProxyIPs()` вынесен в `domain/Security/BlocklistRepository::getProxyIPs()` (proxy в `CoreUtilities.php`) ``` #### Шаг 3.11 — domain/Auth/ (коды, HMAC, пакеты) ``` -API::processCode() (стр. 245–322) → domain/Auth/CodeService.php -API::processHMAC() (стр. 323–378) → domain/Auth/HMACService.php -API::processPackage()(стр. 2370–2432) → domain/Line/PackageService.php +- ✅ `API::processCode()` вынесен в `domain/Auth/CodeService::process()` (proxy в `admin_api.php`) +- ✅ `API::processHMAC()` вынесен в `domain/Auth/HMACService::process()` (proxy в `admin_api.php`) +- ✅ `API::processPackage()` вынесен в `domain/Line/PackageService::process()` (proxy в `admin_api.php`) -admin.php::getActiveCodes() (стр. 663) → domain/Auth/CodeRepository.php -admin.php::updateCodes() (стр. 680) → -admin.php::getCurrentCode() (стр. 725) → -admin.php::getHMACTokens() (стр. 635) → domain/Auth/HMACRepository.php -admin.php::getHMACToken() (стр. 650) → -admin.php::deletePackage() (стр. 3577) → domain/Line/PackageRepository.php +- ✅ `admin.php::getActiveCodes()` вынесен в `domain/Auth/CodeRepository::getActiveCodes()` (proxy в `admin.php`) +- ✅ `admin.php::updateCodes()` вынесен в `domain/Auth/CodeRepository::updateCodes()` (proxy в `admin.php`) +- ✅ `admin.php::getCurrentCode()` вынесен в `domain/Auth/CodeRepository::getCurrentCode()` (proxy в `admin.php`) +- ✅ `admin.php::getHMACTokens()` вынесен в `domain/Auth/HMACRepository::getAll()` (proxy в `admin.php`) +- ✅ `admin.php::getHMACToken()` вынесен в `domain/Auth/HMACRepository::getById()` (proxy в `admin.php`) +- ✅ `admin.php::deletePackage()` вынесен в `domain/Line/PackageRepository::deleteById()` (proxy в `admin.php`) -StreamingUtilities::validateHMAC() (стр. 1143) → domain/Auth/HMACValidator.php +- ✅ `StreamingUtilities::validateHMAC()` вынесен в `domain/Auth/HMACValidator::validate()` (proxy в `StreamingUtilities.php`) ``` #### Шаг 3.12 — Playlist-генератор (большой метод) ``` -CoreUtilities::generatePlaylist() (стр. 892–1314, 423 строки!) → domain/Stream/PlaylistGenerator.php -CoreUtilities::generateCron() (стр. 1315–1337) → domain/Stream/CronGenerator.php +- ✅ `CoreUtilities::generatePlaylist()` вынесен в `domain/Stream/PlaylistGenerator::generate()` (proxy в `CoreUtilities.php`) +- ✅ `CoreUtilities::generateCron()` вынесен в `domain/Stream/CronGenerator::generate()` (proxy в `CoreUtilities.php`) ``` - `generatePlaylist()` — самый длинный метод в CoreUtilities - Дробится внутри на: live playlist, vod playlist, series playlist, radio playlist diff --git a/src/admin/table.php b/src/admin/table.php index 497ec15e..93001d5f 100644 --- a/src/admin/table.php +++ b/src/admin/table.php @@ -1375,7 +1375,17 @@ if ($rType == "lines") { $rRow["bitrate"] = "?"; } if (isset($rProgressInfo["speed"])) { - $rSpeed = floor($rProgressInfo["speed"] * 100) / 100 . "x"; + $rSpeedValue = null; + if (is_numeric($rProgressInfo["speed"])) { + $rSpeedValue = (float) $rProgressInfo["speed"]; + } elseif (is_string($rProgressInfo["speed"]) && preg_match('/([0-9]+(?:\.[0-9]+)?)/', $rProgressInfo["speed"], $rSpeedMatch)) { + $rSpeedValue = (float) $rSpeedMatch[1]; + } + if ($rSpeedValue !== null) { + $rSpeed = floor($rSpeedValue * 100) / 100 . "x"; + } else { + $rSpeed = "1x"; + } } else { $rSpeed = "1x"; } diff --git a/src/autoload.php b/src/autoload.php index ea616ad7..b0de6396 100644 --- a/src/autoload.php +++ b/src/autoload.php @@ -299,10 +299,48 @@ class XC_Autoloader { 'ImageUtils' => $base . 'core/Util/ImageUtils.php', 'ConnectionTracker' => $base . 'domain/Stream/ConnectionTracker.php', 'StreamSorter' => $base . 'domain/Stream/StreamSorter.php', + 'ChannelService' => $base . 'domain/Stream/ChannelService.php', + 'StreamService' => $base . 'domain/Stream/StreamService.php', + 'StreamProcess' => $base . 'domain/Stream/StreamProcess.php', + 'PlaylistGenerator' => $base . 'domain/Stream/PlaylistGenerator.php', + 'CronGenerator' => $base . 'domain/Stream/CronGenerator.php', + 'CategoryService' => $base . 'domain/Stream/CategoryService.php', + 'StreamRepository' => $base . 'domain/Stream/StreamRepository.php', + 'M3UParser' => $base . 'domain/Stream/M3UParser.php', 'CategoryRepository' => $base . 'domain/Stream/CategoryRepository.php', + 'MovieService' => $base . 'domain/Vod/MovieService.php', + 'MovieRepository' => $base . 'domain/Vod/MovieRepository.php', + 'SeriesService' => $base . 'domain/Vod/SeriesService.php', + 'SeriesRepository' => $base . 'domain/Vod/SeriesRepository.php', + 'EpisodeService' => $base . 'domain/Vod/EpisodeService.php', + 'LineService' => $base . 'domain/Line/LineService.php', + 'LineRepository' => $base . 'domain/Line/LineRepository.php', + 'PackageService' => $base . 'domain/Line/PackageService.php', + 'PackageRepository' => $base . 'domain/Line/PackageRepository.php', + 'UserService' => $base . 'domain/User/UserService.php', + 'ProfileService' => $base . 'domain/User/ProfileService.php', + 'GroupService' => $base . 'domain/User/GroupService.php', + 'UserRepository' => $base . 'domain/User/UserRepository.php', + 'GroupRepository' => $base . 'domain/User/GroupRepository.php', + 'EpgService' => $base . 'domain/Epg/EpgService.php', + 'EpgRepository' => $base . 'domain/Epg/EpgRepository.php', + 'SettingsService' => $base . 'domain/Settings/SettingsService.php', + 'BlocklistService' => $base . 'domain/Security/BlocklistService.php', + 'BlocklistRepository' => $base . 'domain/Security/BlocklistRepository.php', + 'ServerService' => $base . 'domain/Server/ServerService.php', + 'MagService' => $base . 'domain/Device/MagService.php', + 'EnigmaService' => $base . 'domain/Device/EnigmaService.php', + 'DeviceSync' => $base . 'domain/Device/DeviceSync.php', + 'BouquetService' => $base . 'domain/Bouquet/BouquetService.php', 'BouquetMapper' => $base . 'domain/Bouquet/BouquetMapper.php', 'BouquetRepository' => $base . 'domain/Bouquet/BouquetRepository.php', 'ServerRepository' => $base . 'domain/Server/ServerRepository.php', + 'TicketService' => $base . 'domain/Ticket/TicketService.php', + 'CodeService' => $base . 'domain/Auth/CodeService.php', + 'HMACService' => $base . 'domain/Auth/HMACService.php', + 'CodeRepository' => $base . 'domain/Auth/CodeRepository.php', + 'HMACRepository' => $base . 'domain/Auth/HMACRepository.php', + 'HMACValidator' => $base . 'domain/Auth/HMACValidator.php', 'SystemInfo' => $base . 'core/Util/SystemInfo.php', 'EventInterface' => $base . 'core/Events/EventInterface.php', 'EventDispatcher' => $base . 'core/Events/EventDispatcher.php', diff --git a/src/crons/errors.php b/src/crons/errors.php index c221578b..3e86bddb 100644 --- a/src/crons/errors.php +++ b/src/crons/errors.php @@ -116,10 +116,13 @@ function loadCron() { while (false !== ($fileEntry = readdir($rHandle))) { if ($fileEntry != '.' && $fileEntry != '..' && is_file($rPath . $fileEntry)) { $rFile = $rPath . $fileEntry; - list($rStreamID, $rExtension) = explode('.', $fileEntry); - if ($rExtension == 'errors') { - $rErrors = array_values(array_unique(array_map('trim', explode("\n", file_get_contents($rFile))))); + $rPathInfo = pathinfo($fileEntry); + $rStreamID = (int) ($rPathInfo['filename'] ?? 0); + $rExtension = $rPathInfo['extension'] ?? ''; + if ($rExtension == 'errors' && 0 < $rStreamID) { + $rErrors = preg_split('/\r\n|\r|\n/', (string) file_get_contents($rFile)); foreach ($rErrors as $rError) { + $rError = trim((string) $rError); if (!(empty($rError) || inArray($rIgnoreErrors, $rError))) { if (CoreUtilities::$rSettings['stream_logs_save']) { $rQuery .= '(' . $rStreamID . ',' . SERVER_ID . ',' . time() . ',' . $db->escape($rError) . '),'; diff --git a/src/domain/Auth/CodeRepository.php b/src/domain/Auth/CodeRepository.php new file mode 100644 index 00000000..8b7b6d1d --- /dev/null +++ b/src/domain/Auth/CodeRepository.php @@ -0,0 +1,73 @@ + 0) { + $rWhitelist[] = 'deny all;'; + } + + $rType = array('admin', 'reseller', 'ministra', 'includes/api/admin', 'includes/api/reseller', 'ministra/new', 'player')[$rCode['type']]; + $rBurst = array(500, 50, 50, 1000, 1000, 50, 500)[$rCode['type']]; + + if (strlen($rCode['code']) >= 4) { + file_put_contents($rMainHome . 'bin/nginx/conf/codes/' . $rCode['code'] . '.conf', str_replace(array('#WHITELIST#', '#CODE#', '#TYPE#', '#BURST#'), array(implode(' ', $rWhitelist), $rCode['code'], $rType, $rBurst), $rTemplate)); + } else { + file_put_contents($rMainHome . 'bin/nginx/conf/codes/' . $rCode['code'] . '.conf', str_replace(array('#WHITELIST#', '#CODE#', '#TYPE#', '#BURST#'), array(implode(' ', $rWhitelist), $rCode['code'] . '/', $rType . '/', $rBurst), $rTemplate)); + } + } + } + + if (count(self::getActiveCodes($rMainHome)) == 0) { + if (!file_exists($rMainHome . 'bin/nginx/conf/codes/default.conf')) { + file_put_contents($rMainHome . 'bin/nginx/conf/codes/default.conf', str_replace(array('alias ', '#WHITELIST#', '#CODE#', '#TYPE#'), array('root ', '', '', 'admin'), $rTemplate)); + } + } else { + if (file_exists($rMainHome . 'bin/nginx/conf/codes/default.conf')) { + unlink($rMainHome . 'bin/nginx/conf/codes/default.conf'); + } + } + + call_user_func($rReloadNginxCallback, $rServerId); + } + + public static function getCurrentCode($db, $rInfo = false) { + if ($rInfo) { + $db->query('SELECT * FROM `access_codes` WHERE `code` = ?;', basename(dirname($_SERVER['PHP_SELF']))); + if ($db->num_rows() == 1) { + return $db->get_row(); + } + return null; + } + + return basename(dirname($_SERVER['PHP_SELF'])); + } +} diff --git a/src/domain/Auth/CodeService.php b/src/domain/Auth/CodeService.php new file mode 100644 index 00000000..d22cd949 --- /dev/null +++ b/src/domain/Auth/CodeService.php @@ -0,0 +1,70 @@ + STATUS_CODE_LENGTH, 'data' => $rData); + } + + if ($rData['type'] == 2 && empty($rData['code'])) { + return array('status' => STATUS_INVALID_CODE, 'data' => $rData); + } + + if (in_array($rData['code'], array('admin', 'stream', 'images', 'player_api', 'player', 'playlist', 'epg', 'live', 'movie', 'series', 'status', 'nginx_status', 'get', 'panel_api', 'xmltv', 'probe', 'thumb', 'timeshift', 'auth', 'vauth', 'tsauth', 'hls', 'play', 'key', 'api', 'c'))) { + return array('status' => STATUS_RESERVED_CODE, 'data' => $rData); + } + + if (isset($rData['edit'])) { + $db->query('SELECT `id` FROM `access_codes` WHERE `code` = ? AND `id` <> ?;', $rData['code'], $rData['edit']); + } else { + $db->query('SELECT `id` FROM `access_codes` WHERE `code` = ?;', $rData['code']); + } + + if (0 < $db->num_rows()) { + return array('status' => STATUS_EXISTS_CODE, 'data' => $rData); + } + + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `access_codes`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + call_user_func($rUpdateCodesCallback); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID, 'orig_code' => $rOrigCode, 'new_code' => $rData['code'])); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } +} diff --git a/src/domain/Auth/HMACRepository.php b/src/domain/Auth/HMACRepository.php new file mode 100644 index 00000000..ca57d7cb --- /dev/null +++ b/src/domain/Auth/HMACRepository.php @@ -0,0 +1,23 @@ +query('SELECT * FROM `hmac_keys` ORDER BY `id` ASC;'); + + if ($db->num_rows() > 0) { + foreach ($db->get_rows() as $rRow) { + $rReturn[intval($rRow['id'])] = $rRow; + } + } + + return $rReturn; + } + + public static function getById($db, $rID) { + $db->query('SELECT * FROM `hmac_keys` WHERE `id` = ?;', $rID); + if ($db->num_rows() == 1) { + return $db->get_row(); + } + } +} diff --git a/src/domain/Auth/HMACService.php b/src/domain/Auth/HMACService.php new file mode 100644 index 00000000..88d8e92c --- /dev/null +++ b/src/domain/Auth/HMACService.php @@ -0,0 +1,54 @@ + STATUS_NO_KEY, 'data' => $rData); + } + + if (strlen($rData['notes']) == 0) { + return array('status' => STATUS_NO_DESCRIPTION, 'data' => $rData); + } + + if (isset($rData['edit'])) { + if ($rData['keygen'] != 'HMAC KEY HIDDEN') { + $db->query('SELECT `id` FROM `hmac_keys` WHERE `key` = ? AND `id` <> ?;', CoreUtilities::encryptData($rData['keygen'], $rSettings['live_streaming_pass'], OPENSSL_EXTRA), $rData['edit']); + if (0 < $db->num_rows()) { + return array('status' => STATUS_EXISTS_HMAC, 'data' => $rData); + } + } + } else { + $db->query('SELECT `id` FROM `hmac_keys` WHERE `key` = ?;', CoreUtilities::encryptData($rData['keygen'], $rSettings['live_streaming_pass'], OPENSSL_EXTRA)); + if (0 < $db->num_rows()) { + return array('status' => STATUS_EXISTS_HMAC, 'data' => $rData); + } + } + + if ($rData['keygen'] != 'HMAC KEY HIDDEN') { + $rArray['key'] = CoreUtilities::encryptData($rData['keygen'], $rSettings['live_streaming_pass'], OPENSSL_EXTRA); + } + + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `hmac_keys`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } +} diff --git a/src/domain/Auth/HMACValidator.php b/src/domain/Auth/HMACValidator.php new file mode 100644 index 00000000..8c49d4ab --- /dev/null +++ b/src/domain/Auth/HMACValidator.php @@ -0,0 +1,32 @@ +query('SELECT `id`, `key` FROM `hmac_keys` WHERE `enabled` = 1;'); + foreach ($db->get_rows() as $rKey) { + $rKeys[] = $rKey; + } + } + + foreach ($rKeys as $rKey) { + $rSecret = call_user_func($rDecryptCallback, $rKey['key'], $rSettings['live_streaming_pass'], OPENSSL_EXTRA); + $rResult = hash_hmac('sha256', (string) $rStreamID . '##' . $rExtension . '##' . $rExpiry . '##' . $rMACIP . '##' . $rIdentifier . '##' . $rMaxConnections, $rSecret); + + if (md5($rResult) == md5($rHMAC)) { + $rKeyID = $rKey['id']; + break; + } + } + + return $rKeyID; + } +} diff --git a/src/domain/Bouquet/BouquetRepository.php b/src/domain/Bouquet/BouquetRepository.php index b346a359..8de81bf9 100644 --- a/src/domain/Bouquet/BouquetRepository.php +++ b/src/domain/Bouquet/BouquetRepository.php @@ -25,4 +25,34 @@ class BouquetRepository { return $rOutput; } + + public static function getUserBouquets($db) { + $rReturn = array(); + $db->query('SELECT `id`, `bouquet` FROM `lines` ORDER BY `id` ASC;'); + + if ($db->num_rows() > 0) { + foreach ($db->get_rows() as $rRow) { + $rReturn[intval($rRow['id'])] = $rRow; + } + } + + return $rReturn; + } + + public static function getAllSimple($db) { + $rReturn = array(); + $db->query('SELECT * FROM `bouquets` ORDER BY `bouquet_order` ASC;'); + + if ($db->num_rows() > 0) { + foreach ($db->get_rows() as $rRow) { + $rReturn[intval($rRow['id'])] = $rRow; + } + } + + return $rReturn; + } + + public static function getOrder($db) { + return self::getAllSimple($db); + } } diff --git a/src/domain/Bouquet/BouquetService.php b/src/domain/Bouquet/BouquetService.php new file mode 100644 index 00000000..7c2ed4ac --- /dev/null +++ b/src/domain/Bouquet/BouquetService.php @@ -0,0 +1,169 @@ + 0) { + $db->query('SELECT `id`, `type` FROM `streams` WHERE `id` IN (' . implode(',', $rRequiredIDs) . ');'); + + foreach ($db->get_rows() as $rRow) { + if (intval($rRow['type']) == 3) { + $rRow['type'] = 1; + } + + $rStreams[intval($rRow['type'])][] = intval($rRow['id']); + } + } + + if (count($rBouquetSeries) > 0) { + $db->query('SELECT `id` FROM `streams_series` WHERE `id` IN (' . implode(',', $rBouquetSeries) . ');'); + + foreach ($db->get_rows() as $rRow) { + $rStreams[5][] = intval($rRow['id']); + } + } + + $rArray['bouquet_channels'] = array_intersect(array_map('intval', array_values($rBouquetStreams)), $rStreams[1] ?? []); + $rArray['bouquet_movies'] = array_intersect(array_map('intval', array_values($rBouquetMovies)), $rStreams[2] ?? []); + $rArray['bouquet_radios'] = array_intersect(array_map('intval', array_values($rBouquetRadios)), $rStreams[4] ?? []); + $rArray['bouquet_series'] = array_intersect(array_map('intval', array_values($rBouquetSeries)), $rStreams[5] ?? []); + } else if (isset($rData['edit'])) { + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + if (!isset($rData['edit'])) { + $db->query('SELECT MAX(`bouquet_order`) AS `max` FROM `bouquets`;'); + $rArray['bouquet_order'] = intval($db->get_row()['max']) + 1; + } + + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `bouquets`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + call_user_func($rScanBouquetCallback, $rInsertID); + + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + public static function reorder($rData, $db) { + $rOrder = json_decode($rData['stream_order_array'], true); + $rOrder['stream'] = confirmIDs($rOrder['stream']); + $rOrder['series'] = confirmIDs($rOrder['series']); + $rOrder['movie'] = confirmIDs($rOrder['movie']); + $rOrder['radio'] = confirmIDs($rOrder['radio']); + $db->query('UPDATE `bouquets` SET `bouquet_channels` = ?, `bouquet_series` = ?, `bouquet_movies` = ?, `bouquet_radios` = ? WHERE `id` = ?;', '[' . implode(',', array_map('intval', $rOrder['stream'])) . ']', '[' . implode(',', array_map('intval', $rOrder['series'])) . ']', '[' . implode(',', array_map('intval', $rOrder['movie'])) . ']', '[' . implode(',', array_map('intval', $rOrder['radio'])) . ']', $rData['reorder']); + + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rData['reorder'])); + } + + public static function sort($rData, $db, $rGetUserBouquetsCallback, $rGetPackagesCallback, $rSortArrayByArrayCallback, $rUpdateLineCallback) { + set_time_limit(0); + ini_set('mysql.connect_timeout', 0); + ini_set('max_execution_time', 0); + ini_set('default_socket_timeout', 0); + $rOrder = json_decode($rData['bouquet_order_array'], true); + $rSort = 1; + + foreach ($rOrder as $rBouquetID) { + $db->query('UPDATE `bouquets` SET `bouquet_order` = ? WHERE `id` = ?;', $rSort, $rBouquetID); + $rSort++; + } + + if (isset($rData['confirmReplace'])) { + $rUsers = call_user_func($rGetUserBouquetsCallback); + + foreach ($rUsers as $rUser) { + $rBouquet = json_decode($rUser['bouquet'], true); + $rBouquet = array_map('intval', call_user_func($rSortArrayByArrayCallback, $rBouquet, $rOrder)); + $db->query('UPDATE `lines` SET `bouquet` = ? WHERE `id` = ?;', '[' . implode(',', $rBouquet) . ']', $rUser['id']); + call_user_func($rUpdateLineCallback, $rUser['id']); + } + + $rPackages = call_user_func($rGetPackagesCallback); + foreach ($rPackages as $rPackage) { + $rBouquet = json_decode($rPackage['bouquets'], true); + $rBouquet = array_map('intval', call_user_func($rSortArrayByArrayCallback, $rBouquet, $rOrder)); + $db->query('UPDATE `users_packages` SET `bouquets` = ? WHERE `id` = ?;', '[' . implode(',', $rBouquet) . ']', $rPackage['id']); + } + + return array('status' => STATUS_SUCCESS_REPLACE); + } + + return array('status' => STATUS_SUCCESS); + } + + public static function scan() { + shell_exec(PHP_BIN . ' ' . CLI_PATH . 'tools.php "bouquets" > /dev/null 2>/dev/null &'); + } + + public static function scanOne($db, $rID, $rGetBouquetCallback, $rFilterIDsCallback) { + $rBouquet = call_user_func($rGetBouquetCallback, $rID); + if (!$rBouquet) { + return; + } + + $availableStreams = []; + $db->query('SELECT `id` FROM `streams`;'); + if ($db->num_rows() > 0) { + foreach ($db->get_rows() as $rRow) { + $availableStreams[] = (int)$rRow['id']; + } + } + + $availableSeries = []; + $db->query('SELECT `id` FROM `streams_series`;'); + if ($db->num_rows() > 0) { + foreach ($db->get_rows() as $rRow) { + $availableSeries[] = (int)$rRow['id']; + } + } + + $updateData = [ + 'channels' => call_user_func($rFilterIDsCallback, json_decode($rBouquet['bouquet_channels'] ?? '[]', true), $availableStreams, true), + 'movies' => call_user_func($rFilterIDsCallback, json_decode($rBouquet['bouquet_movies'] ?? '[]', true), $availableStreams, true), + 'radios' => call_user_func($rFilterIDsCallback, json_decode($rBouquet['bouquet_radios'] ?? '[]', true), $availableStreams, true), + 'series' => call_user_func($rFilterIDsCallback, json_decode($rBouquet['bouquet_series'] ?? '[]', true), $availableSeries, false) + ]; + + $db->query( + "UPDATE `bouquets` SET + `bouquet_channels` = ?, + `bouquet_movies` = ?, + `bouquet_radios` = ?, + `bouquet_series` = ? + WHERE `id` = ?", + json_encode($updateData['channels']), + json_encode($updateData['movies']), + json_encode($updateData['radios']), + json_encode($updateData['series']), + $rBouquet['id'] + ); + } +} diff --git a/src/domain/Device/DeviceSync.php b/src/domain/Device/DeviceSync.php new file mode 100644 index 00000000..a457f5c0 --- /dev/null +++ b/src/domain/Device/DeviceSync.php @@ -0,0 +1,36 @@ +query('SELECT * FROM `lines` WHERE `id` = (SELECT `user_id` FROM `mag_devices` WHERE `mag_id` = ?);', $rDeviceID); + } else { + $db->query('SELECT * FROM `lines` WHERE `pair_id` = ?;', $rUserID); + } + + foreach ($db->get_rows() as $rDevice) { + $rUpdateDevice = $rUser; + $rUpdateDevice['pair_id'] = intval($rUserID); + $rUpdateDevice['play_token'] = ''; + + foreach (array('id', 'is_mag', 'is_e2', 'is_restreamer', 'max_connections', 'created_at', 'username', 'password', 'admin_notes', 'reseller_notes') as $rKey) { + $rUpdateDevice[$rKey] = $rDevice[$rKey]; + } + + if (!isset($rUpdateDevice['id'])) { + } else { + $rPrepare = prepareArray($rUpdateDevice); + $rQuery = 'REPLACE INTO `lines`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + $db->query($rQuery, ...$rPrepare['data']); + CoreUtilities::updateLine($rUpdateDevice['id']); + } + } + } + } +} diff --git a/src/domain/Device/EnigmaService.php b/src/domain/Device/EnigmaService.php new file mode 100644 index 00000000..933dc4e3 --- /dev/null +++ b/src/domain/Device/EnigmaService.php @@ -0,0 +1,23 @@ + STATUS_SUCCESS); + } + + public static function massEdit($rData) { + return API::massEditEnigmasLegacy($rData); + } + + public static function process($rData) { + return API::processEnigmaLegacy($rData); + } +} diff --git a/src/domain/Device/MagService.php b/src/domain/Device/MagService.php new file mode 100644 index 00000000..4a168de5 --- /dev/null +++ b/src/domain/Device/MagService.php @@ -0,0 +1,23 @@ + STATUS_SUCCESS); + } + + public static function massEdit($rData) { + return API::massEditMagsLegacy($rData); + } + + public static function process($rData) { + return API::processMAGLegacy($rData); + } +} diff --git a/src/domain/Epg/EpgRepository.php b/src/domain/Epg/EpgRepository.php new file mode 100644 index 00000000..97dafb8f --- /dev/null +++ b/src/domain/Epg/EpgRepository.php @@ -0,0 +1,80 @@ +query('SELECT `id`, `data` FROM `epg`;'); + + if ($db->num_rows() > 0) { + foreach ($db->get_rows() as $rRow) { + foreach (json_decode($rRow['data'], true) as $rChannelID => $rChannelData) { + if ($rChannelID == $rEPGName) { + if (count($rChannelData['langs']) > 0) { + $rEPGLang = $rChannelData['langs'][0]; + } else { + $rEPGLang = ''; + } + + return array('channel_id' => $rChannelID, 'epg_lang' => $rEPGLang, 'epg_id' => intval($rRow['id'])); + } + } + } + } + } + + public static function getById($db, $rID) { + $db->query('SELECT * FROM `epg` WHERE `id` = ?;', $rID); + + if ($db->num_rows() == 1) { + return $db->get_row(); + } + } + + public static function search($rArray, $rKey, $rValue) { + $rResults = array(); + self::searchRecursive($rArray, $rKey, $rValue, $rResults); + return $rResults; + } + + private static function searchRecursive($rArray, $rKey, $rValue, &$rResults) { + if (is_array($rArray)) { + if (isset($rArray[$rKey]) && $rArray[$rKey] == $rValue) { + $rResults[] = $rArray; + } + foreach ($rArray as $subarray) { + self::searchRecursive($subarray, $rKey, $rValue, $rResults); + } + } + } + + public static function getStreamEpg($rStreamID, $rStartDate = null, $rFinishDate = null, $rByID = false) { + $rReturn = array(); + $rData = (file_exists(EPG_PATH . 'stream_' . $rStreamID) ? igbinary_unserialize(file_get_contents(EPG_PATH . 'stream_' . $rStreamID)) : array()); + + foreach ($rData as $rItem) { + if (!$rStartDate || ($rStartDate < $rItem['end'] && $rItem['start'] < $rFinishDate)) { + if ($rByID) { + $rReturn[$rItem['id']] = $rItem; + } else { + $rReturn[] = $rItem; + } + } + } + + return $rReturn; + } + + public static function getStreamsEpg($rStreamIDs, $rStartDate = null, $rFinishDate = null) { + $rReturn = array(); + foreach ($rStreamIDs as $rStreamID) { + $rReturn[$rStreamID] = self::getStreamEpg($rStreamID, $rStartDate, $rFinishDate); + } + return $rReturn; + } + + public static function getProgramme($rStreamID, $rProgrammeID) { + $rData = self::getStreamEpg($rStreamID, null, null, true); + if (isset($rData[$rProgrammeID])) { + return $rData[$rProgrammeID]; + } + } +} diff --git a/src/domain/Epg/EpgService.php b/src/domain/Epg/EpgService.php new file mode 100644 index 00000000..dbd6a028 --- /dev/null +++ b/src/domain/Epg/EpgService.php @@ -0,0 +1,44 @@ +query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + public static function getChannelEpg($rStream, $rArchive = false, $rGetEpgCallback = null) { + if (!$rStream || !$rStream['channel_id']) { + return array(); + } + + if (!$rGetEpgCallback) { + $rGetEpgCallback = array('CoreUtilities', 'getEPG'); + } + + if ($rArchive) { + return call_user_func($rGetEpgCallback, $rStream['id'], time() - $rStream['tv_archive_duration'] * 86400, time()); + } + + return call_user_func($rGetEpgCallback, $rStream['id'], time(), time() + 1209600); + } +} diff --git a/src/domain/Line/LineRepository.php b/src/domain/Line/LineRepository.php new file mode 100644 index 00000000..09791297 --- /dev/null +++ b/src/domain/Line/LineRepository.php @@ -0,0 +1,33 @@ += count($rIDs)) { + return false; + } + + CoreUtilities::deleteLines($rIDs); + $db->query('DELETE FROM `lines` WHERE `id` IN (' . implode(',', $rIDs) . ');'); + $db->query('DELETE FROM `lines_logs` WHERE `user_id` IN (' . implode(',', $rIDs) . ');'); + $db->query('UPDATE `lines_activity` SET `user_id` = 0 WHERE `user_id` IN (' . implode(',', $rIDs) . ');'); + $rPairIDs = array(); + $db->query('SELECT `id` FROM `lines` WHERE `pair_id` IN (' . implode(',', $rIDs) . ');'); + + foreach ($db->get_rows() as $rRow) { + if (0 >= $rRow['id'] || in_array($rRow['id'], $rPairIDs)) { + } else { + $rPairIDs[] = $rRow['id']; + } + } + + if (0 >= count($rPairIDs)) { + } else { + $db->query('UPDATE `lines` SET `pair_id` = null WHERE `id` = (' . implode(',', $rPairIDs) . ');'); + CoreUtilities::updateLines($rPairIDs); + } + + return true; + } +} diff --git a/src/domain/Line/LineService.php b/src/domain/Line/LineService.php new file mode 100644 index 00000000..04d77d3e --- /dev/null +++ b/src/domain/Line/LineService.php @@ -0,0 +1,55 @@ + STATUS_SUCCESS); + } + + public static function massEdit($rData) { + return API::massEditLinesLegacy($rData); + } + + public static function process($rData) { + return API::processLineLegacy($rData); + } + + public static function deleteLineSignal($db, $rCached, $rMainID, $rUserID, $rForce = false) { + self::updateLineSignal($db, $rCached, $rMainID, $rUserID, $rForce); + } + + public static function deleteLinesSignal($db, $rCached, $rMainID, $rUserIDs, $rForce = false) { + self::updateLinesSignal($db, $rCached, $rMainID, $rUserIDs); + } + + public static function updateLineSignal($db, $rCached, $rMainID, $rUserID, $rForce = false) { + if ($rCached) { + $db->query('SELECT COUNT(*) AS `count` FROM `signals` WHERE `server_id` = ? AND `cache` = 1 AND `custom_data` = ?;', $rMainID, json_encode(array('type' => 'update_line', 'id' => $rUserID))); + if ($db->get_row()['count'] != 0) { + } else { + $db->query('INSERT INTO `signals`(`server_id`, `cache`, `time`, `custom_data`) VALUES(?, 1, ?, ?);', $rMainID, time(), json_encode(array('type' => 'update_line', 'id' => $rUserID))); + } + return true; + } + return false; + } + + public static function updateLinesSignal($db, $rCached, $rMainID, $rUserIDs) { + if ($rCached) { + $db->query('SELECT COUNT(*) AS `count` FROM `signals` WHERE `server_id` = ? AND `cache` = 1 AND `custom_data` = ?;', $rMainID, json_encode(array('type' => 'update_lines', 'id' => $rUserIDs))); + if ($db->get_row()['count'] != 0) { + } else { + $db->query('INSERT INTO `signals`(`server_id`, `cache`, `time`, `custom_data`) VALUES(?, 1, ?, ?);', $rMainID, time(), json_encode(array('type' => 'update_lines', 'id' => $rUserIDs))); + } + return true; + } + return false; + } +} diff --git a/src/domain/Line/PackageRepository.php b/src/domain/Line/PackageRepository.php new file mode 100644 index 00000000..5eec8c3a --- /dev/null +++ b/src/domain/Line/PackageRepository.php @@ -0,0 +1,16 @@ +query('UPDATE `lines` SET `package_id` = null WHERE `package_id` = ?;', $rID); + $db->query('DELETE FROM `users_packages` WHERE `id` = ?;', $rID); + + return true; + } +} diff --git a/src/domain/Line/PackageService.php b/src/domain/Line/PackageService.php new file mode 100644 index 00000000..9b355700 --- /dev/null +++ b/src/domain/Line/PackageService.php @@ -0,0 +1,52 @@ + STATUS_INVALID_NAME, 'data' => $rData); + } + + foreach (array('is_trial', 'is_official', 'is_mag', 'is_e2', 'is_line', 'lock_device', 'is_restreamer', 'is_isplock', 'check_compatible') as $rSelection) { + if (isset($rData[$rSelection])) { + $rArray[$rSelection] = 1; + } else { + $rArray[$rSelection] = 0; + } + } + + $rArray['groups'] = '[' . implode(',', array_map('intval', json_decode($rData['groups_selected'], true))) . ']'; + $rArray['bouquets'] = call_user_func($rSortArrayByArrayCallback, array_values(json_decode($rData['bouquets_selected'], true)), array_keys(call_user_func($rGetBouquetOrderCallback))); + $rArray['bouquets'] = '[' . implode(',', array_map('intval', $rArray['bouquets'])) . ']'; + + if (isset($rData['output_formats'])) { + $rArray['output_formats'] = array(); + foreach ($rData['output_formats'] as $rOutput) { + $rArray['output_formats'][] = $rOutput; + } + $rArray['output_formats'] = '[' . implode(',', array_map('intval', $rArray['output_formats'])) . ']'; + } + + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `users_packages`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } +} diff --git a/src/domain/Security/BlocklistRepository.php b/src/domain/Security/BlocklistRepository.php new file mode 100644 index 00000000..b43ba7fc --- /dev/null +++ b/src/domain/Security/BlocklistRepository.php @@ -0,0 +1,132 @@ +query('SELECT id,exact_match,LOWER(user_agent) as blocked_ua FROM `blocked_uas`'); + $rOutput = $db->get_rows(true, 'id'); + + if (is_callable($rSetCacheCallback)) { + call_user_func($rSetCacheCallback, 'blocked_ua', $rOutput); + } + + return $rOutput; + } + + public static function getBlockedIPs($db, $rGetCacheCallback, $rSetCacheCallback, $rForce = false) { + if (!$rForce && is_callable($rGetCacheCallback)) { + $rCache = call_user_func($rGetCacheCallback, 'blocked_ips', 20); + if ($rCache !== false) { + return $rCache; + } + } + + $rOutput = array(); + $db->query('SELECT `ip` FROM `blocked_ips`'); + foreach ($db->get_rows() as $rRow) { + $rOutput[] = $rRow['ip']; + } + + if (is_callable($rSetCacheCallback)) { + call_user_func($rSetCacheCallback, 'blocked_ips', $rOutput); + } + + return $rOutput; + } + + public static function getBlockedISP($db, $rGetCacheCallback, $rSetCacheCallback, $rForce = false) { + if (!$rForce && is_callable($rGetCacheCallback)) { + $rCache = call_user_func($rGetCacheCallback, 'blocked_isp', 20); + if ($rCache !== false) { + return $rCache; + } + } + + $db->query('SELECT id,isp,blocked FROM `blocked_isps`'); + $rOutput = $db->get_rows(); + + if (is_callable($rSetCacheCallback)) { + call_user_func($rSetCacheCallback, 'blocked_isp', $rOutput); + } + + return $rOutput; + } + + public static function getBlockedServers($db, $rGetCacheCallback, $rSetCacheCallback, $rForce = false) { + if (!$rForce && is_callable($rGetCacheCallback)) { + $rCache = call_user_func($rGetCacheCallback, 'blocked_servers', 20); + if ($rCache !== false) { + return $rCache; + } + } + + $rOutput = array(); + $db->query('SELECT `asn` FROM `blocked_asns` WHERE `blocked` = 1;'); + foreach ($db->get_rows() as $rRow) { + $rOutput[] = $rRow['asn']; + } + + if (is_callable($rSetCacheCallback)) { + call_user_func($rSetCacheCallback, 'blocked_servers', $rOutput); + } + + return $rOutput; + } + + public static function getBlockedIPsSimple($db) { + $rReturn = array(); + $db->query('SELECT * FROM `blocked_ips` ORDER BY `id` ASC;'); + + if ($db->num_rows() > 0) { + foreach ($db->get_rows() as $rRow) { + $rReturn[] = $rRow; + } + } + + return $rReturn; + } + + public static function getRTMPIPsSimple($db) { + $rReturn = array(); + $db->query('SELECT * FROM `rtmp_ips` ORDER BY `id` ASC;'); + + if ($db->num_rows() > 0) { + foreach ($db->get_rows() as $rRow) { + $rReturn[] = $rRow; + } + } + + return $rReturn; + } +} diff --git a/src/domain/Security/BlocklistService.php b/src/domain/Security/BlocklistService.php new file mode 100644 index 00000000..182e1393 --- /dev/null +++ b/src/domain/Security/BlocklistService.php @@ -0,0 +1,149 @@ + STATUS_INVALID_IP, 'data' => $rData); + } + + $rArray = array('ip' => $rData['ip'], 'notes' => $rData['notes'], 'date' => time()); + touch(FLOOD_TMP_PATH . 'block_' . $rData['ip']); + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `blocked_ips`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + public static function processISP($db, $rData, $rGetISPCallback) { + if (isset($rData['edit'])) { + if (!hasPermissions('adv', 'block_isps')) { + exit(); + } + $rArray = overwriteData(call_user_func($rGetISPCallback, $rData['edit']), $rData); + } else { + if (!hasPermissions('adv', 'block_isps')) { + exit(); + } + $rArray = verifyPostTable('blocked_isps', $rData); + unset($rArray['id']); + } + + if (isset($rData['blocked'])) { + $rArray['blocked'] = 1; + } else { + $rArray['blocked'] = 0; + } + + if (strlen($rArray['isp']) == 0) { + return array('status' => STATUS_INVALID_NAME, 'data' => $rData); + } + + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `blocked_isps`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + public static function processRTMPIP($db, $rData, $rGetRTMPIPCallback) { + if (isset($rData['edit'])) { + $rArray = overwriteData(call_user_func($rGetRTMPIPCallback, $rData['edit']), $rData); + } else { + $rArray = verifyPostTable('rtmp_ips', $rData); + unset($rArray['id']); + } + + foreach (array('push', 'pull') as $rSelection) { + if (isset($rData[$rSelection])) { + $rArray[$rSelection] = 1; + } else { + $rArray[$rSelection] = 0; + } + } + + if (!filter_var($rData['ip'], FILTER_VALIDATE_IP)) { + return array('status' => STATUS_INVALID_IP, 'data' => $rData); + } + + if (checkExists('rtmp_ips', 'ip', $rData['ip'], 'id', $rArray['id'])) { + return array('status' => STATUS_EXISTS_IP, 'data' => $rData); + } + + if (strlen($rData['password']) == 0) { + $rArray['password'] = generateString(16); + } + + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `rtmp_ips`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + public static function processUA($db, $rData, $rGetUserAgentCallback) { + if (isset($rData['edit'])) { + $rArray = overwriteData(call_user_func($rGetUserAgentCallback, $rData['edit']), $rData); + } else { + $rArray = verifyPostTable('blocked_uas', $rData); + unset($rArray['id']); + } + + if (isset($rData['exact_match'])) { + $rArray['exact_match'] = true; + } else { + $rArray['exact_match'] = false; + } + + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `blocked_uas`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + public static function checkBlockedUAs($rBlockedUA, $rUserAgent, $rReturn = false) { + $rUserAgent = strtolower($rUserAgent); + foreach ($rBlockedUA as $rBlocked) { + if ($rBlocked['exact_match'] == 1) { + if ($rBlocked['blocked_ua'] == $rUserAgent) { + return true; + } + } else { + if (stristr($rUserAgent, $rBlocked['blocked_ua'])) { + return true; + } + } + } + return false; + } + + public static function checkISP($rBlockedISP, $rConISP) { + foreach ($rBlockedISP as $rISP) { + if (strtolower($rConISP) == strtolower($rISP['isp'])) { + return intval($rISP['blocked']); + } + } + return 0; + } + + public static function checkServer($rBlockedServers, $rASN) { + return in_array($rASN, $rBlockedServers); + } +} diff --git a/src/domain/Server/ServerRepository.php b/src/domain/Server/ServerRepository.php index 9039e430..f07a630d 100644 --- a/src/domain/Server/ServerRepository.php +++ b/src/domain/Server/ServerRepository.php @@ -80,4 +80,158 @@ class ServerRepository { return $rServers; } + + public static function getAllSimple($db) { + $rReturn = array(); + $db->query('SELECT * FROM `servers` ORDER BY `id` ASC;'); + + if ($db->num_rows() > 0) { + foreach ($db->get_rows() as $rRow) { + $rRow['server_online'] = in_array($rRow['status'], array(1, 3)) && time() - $rRow['last_check_ago'] <= 90 || $rRow['is_main']; + $rReturn[$rRow['id']] = $rRow; + } + } + + return $rReturn; + } + + public static function getStreamingSimple($db, $rPermissions, $type = 'online') { + $rReturn = array(); + $db->query('SELECT * FROM `servers` WHERE `server_type` = 0 ORDER BY `id` ASC;'); + + if ($db->num_rows() > 0) { + foreach ($db->get_rows() as $rRow) { + if (isset($rPermissions['is_reseller']) && $rPermissions['is_reseller']) { + $rRow['server_name'] = 'Server #' . ($rRow['id'] ?? 'unknown'); + } + + $rRow['server_online'] = in_array($rRow['status'], array(1, 3)) && time() - $rRow['last_check_ago'] <= 90 || $rRow['is_main']; + if (!isset($rRow['order'])) { + $rRow['order'] = 0; + } + if ($rRow['server_online'] || $type == 'all') { + $rReturn[$rRow['id']] = $rRow; + } + } + } + + return $rReturn; + } + + public static function getProxySimple($db, $rPermissions, $rOnline = false) { + $rReturn = array(); + $db->query('SELECT * FROM `servers` WHERE `server_type` = 1 ORDER BY `id` ASC;'); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + if ($rPermissions['is_reseller']) { + $rRow['server_name'] = 'Proxy #' . $rRow['id']; + } + + $rRow['server_online'] = in_array($rRow['status'], array(1, 3)) && time() - $rRow['last_check_ago'] <= 90 || $rRow['is_main']; + if (!($rRow['server_online'] == 0 && $rOnline)) { + $rReturn[$rRow['id']] = $rRow; + } + } + } + + return $rReturn; + } + + public static function getFreeSpace($rSystemApiRequest, $rServerID) { + $rReturn = array(); + $rLines = json_decode(call_user_func($rSystemApiRequest, $rServerID, array('action' => 'get_free_space')), true); + + if (!empty($rLines)) { + array_shift($rLines); + } + + foreach ($rLines as $rLine) { + $rSplit = explode(' ', preg_replace('!\s+!', ' ', trim($rLine))); + if (0 < strlen($rSplit[0]) && strpos($rSplit[5], 'xc_vm') !== false || $rSplit[5] == '/') { + $rReturn[] = array('filesystem' => $rSplit[0], 'size' => $rSplit[1], 'used' => $rSplit[2], 'avail' => $rSplit[3], 'percentage' => $rSplit[4], 'mount' => implode(' ', array_slice($rSplit, 5, count($rSplit) - 5))); + } + } + + return $rReturn; + } + + public static function getStreamsRamdisk($rSystemApiRequest, $rServerID) { + $response = call_user_func($rSystemApiRequest, $rServerID, array('action' => 'streams_ramdisk')); + $rReturn = json_decode($response, true); + + if (!is_array($rReturn)) { + return array(); + } + + if (empty($rReturn['result'])) { + return array(); + } + + return ($rReturn['streams'] ?? array()); + } + + public static function killPID($rSystemApiRequest, $rServerID, $rPID) { + call_user_func($rSystemApiRequest, $rServerID, array('action' => 'kill_pid', 'pid' => $rPID)); + } + + public static function getRTMPStats($rSystemApiRequest, $rServerID) { + return json_decode(call_user_func($rSystemApiRequest, $rServerID, array('action' => 'rtmp_stats')), true); + } + + public static function checkSource($rServers, $rFFProbe, $rServerID, $rFilename) { + $rAPI = $rServers[intval($rServerID)]['api_url_ip'] . '&action=getFile&filename=' . urlencode($rFilename); + $rCommand = 'timeout 10 ' . $rFFProbe . ' -user_agent "Mozilla/5.0" -show_streams -v quiet "' . $rAPI . '" -of json'; + return json_decode(shell_exec($rCommand), true); + } + + public static function getSSLLog($rServers, $rServerID) { + $rAPI = $rServers[intval($rServerID)]['api_url_ip'] . '&action=getFile&filename=' . urlencode(BIN_PATH . 'certbot/logs/xc_vm.log'); + return json_decode(file_get_contents($rAPI), true); + } + + public static function freeTemp($rSystemApiRequest, $rServerID) { + call_user_func($rSystemApiRequest, $rServerID, array('action' => 'free_temp')); + } + + public static function freeStreams($rSystemApiRequest, $rServerID) { + call_user_func($rSystemApiRequest, $rServerID, array('action' => 'free_streams')); + } + + public static function probeSource($rSystemApiRequest, $rServerID, $rURL, $rUserAgent = null, $rProxy = null, $rCookies = null, $rHeaders = null) { + return json_decode(call_user_func($rSystemApiRequest, $rServerID, array('action' => 'probe', 'url' => $rURL, 'user_agent' => $rUserAgent, 'http_proxy' => $rProxy, 'cookies' => $rCookies, 'headers' => $rHeaders), 30), true); + } + + public static function deleteById($db, $rSettings, $rGetServerById, $rID, $rReplaceWith = null) { + $rServer = call_user_func($rGetServerById, $rID); + + if (!$rServer || $rServer['is_main']) { + return false; + } + + if ($rReplaceWith) { + $db->query('UPDATE `streams_servers` SET `server_id` = ? WHERE `server_id` = ?;', $rReplaceWith, $rID); + if (!$rSettings['redis_handler']) { + $db->query('UPDATE `lines_live` SET `server_id` = ? WHERE `server_id` = ?;', $rReplaceWith, $rID); + } + $db->query('UPDATE `lines_activity` SET `server_id` = ? WHERE `server_id` = ?;', $rReplaceWith, $rID); + } else { + $db->query('DELETE FROM `streams_servers` WHERE `server_id` = ?;', $rID); + if (!$rSettings['redis_handler']) { + $db->query('DELETE FROM `lines_live` WHERE `server_id` = ?;', $rID); + } + $db->query('UPDATE `lines_activity` SET `server_id` = 0 WHERE `server_id` = ?;', $rID); + } + + $db->query('UPDATE `servers` SET `parent_id` = NULL, `enabled` = 0 WHERE `server_type` = 1 AND `parent_id` = ?;', $rID); + $db->query('DELETE FROM `servers_stats` WHERE `server_id` = ?;', $rID); + $db->query('DELETE FROM `servers` WHERE `id` = ?;', $rID); + + if ($rServer['server_type'] == 0) { + CoreUtilities::revokePrivileges($rServer['server_ip']); + } + + return true; + } } diff --git a/src/domain/Server/ServerService.php b/src/domain/Server/ServerService.php new file mode 100644 index 00000000..a5158448 --- /dev/null +++ b/src/domain/Server/ServerService.php @@ -0,0 +1,279 @@ + STATUS_INVALID_INPUT, 'data' => $rData); + } + + $rArray = verifyPostTable('servers', $rData, true); + $rPorts = array('http' => array(), 'https' => array()); + + foreach ($rData['http_broadcast_ports'] as $rPort) { + if (is_numeric($rPort) && 80 <= $rPort && $rPort <= 65535 && !in_array($rPort, ($rPorts['http'] ?: array())) && $rPort != $rData['rtmp_port']) { + $rPorts['http'][] = $rPort; + } + } + $rPorts['http'] = array_unique($rPorts['http']); + unset($rData['http_broadcast_ports']); + + foreach ($rData['https_broadcast_ports'] as $rPort) { + if (is_numeric($rPort) && 80 <= $rPort && $rPort <= 65535 && !in_array($rPort, ($rPorts['http'] ?: array())) && !in_array($rPort, ($rPorts['https'] ?: array())) && $rPort != $rData['rtmp_port']) { + $rPorts['https'][] = $rPort; + } + } + $rPorts['https'] = array_unique($rPorts['https']); + unset($rData['https_broadcast_ports']); + $rArray['http_broadcast_port'] = null; + $rArray['http_ports_add'] = null; + + if (count($rPorts['http']) > 0) { + $rArray['http_broadcast_port'] = $rPorts['http'][0]; + if (1 < count($rPorts['http'])) { + $rArray['http_ports_add'] = implode(',', array_slice($rPorts['http'], 1, count($rPorts['http']) - 1)); + } + } + + $rArray['https_broadcast_port'] = null; + $rArray['https_ports_add'] = null; + if (count($rPorts['https']) > 0) { + $rArray['https_broadcast_port'] = $rPorts['https'][0]; + if (1 < count($rPorts['https'])) { + $rArray['https_ports_add'] = implode(',', array_slice($rPorts['https'], 1, count($rPorts['https']) - 1)); + } + } + + foreach (array('enable_gzip', 'timeshift_only', 'enable_https', 'random_ip', 'enable_geoip', 'enable_isp', 'enabled', 'enable_proxy') as $rKey) { + $rArray[$rKey] = isset($rData[$rKey]) ? 1 : 0; + } + + if ($rServer['is_main']) { + $rArray['enabled'] = 1; + } + + if (isset($rData['geoip_countries'])) { + $rArray['geoip_countries'] = array(); + foreach ($rData['geoip_countries'] as $rCountry) { + $rArray['geoip_countries'][] = $rCountry; + } + } else { + $rArray['geoip_countries'] = array(); + } + + if (isset($rData['isp_names'])) { + $rArray['isp_names'] = array(); + foreach ($rData['isp_names'] as $rISP) { + $rArray['isp_names'][] = strtolower(trim(preg_replace('/[^A-Za-z0-9 ]/', '', $rISP))); + } + } else { + $rArray['isp_names'] = array(); + } + + if (isset($rData['domain_name'])) { + $rArray['domain_name'] = implode(',', $rData['domain_name']); + } else { + $rArray['domain_name'] = ''; + } + + if (strlen($rData['server_ip']) == 0 || !filter_var($rData['server_ip'], FILTER_VALIDATE_IP)) { + return array('status' => STATUS_INVALID_IP, 'data' => $rData); + } + if (0 < strlen($rData['private_ip']) && !filter_var($rData['private_ip'], FILTER_VALIDATE_IP)) { + return array('status' => STATUS_INVALID_IP, 'data' => $rData); + } + + $rArray['total_services'] = $rData['total_services']; + $rPrepare = prepareArray($rArray); + $rPrepare['data'][] = $rData['edit']; + $rQuery = 'UPDATE `servers` SET ' . $rPrepare['update'] . ' WHERE `id` = ?;'; + + if (!$db->query($rQuery, ...$rPrepare['data'])) { + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + $rInsertID = $rData['edit']; + $rPorts = array('http' => array(), 'https' => array()); + foreach (array_merge(array(intval($rArray['http_broadcast_port'])), explode(',', $rArray['http_ports_add'])) as $rPort) { + if (is_numeric($rPort) && 0 < $rPort && $rPort <= 65535) { + $rPorts['http'][] = intval($rPort); + } + } + foreach (array_merge(array(intval($rArray['https_broadcast_port'])), explode(',', $rArray['https_ports_add'])) as $rPort) { + if (is_numeric($rPort) && 0 < $rPort && $rPort <= 65535) { + $rPorts['https'][] = intval($rPort); + } + } + changePort($rInsertID, 0, $rPorts['http'], false); + changePort($rInsertID, 1, $rPorts['https'], false); + changePort($rInsertID, 2, array($rArray['rtmp_port']), false); + setServices($rInsertID, intval($rArray['total_services']), true); + + if (!empty($rArray['governor'])) { + setGovernor($rInsertID, $rArray['governor']); + } + if (!empty($rArray['sysctl'])) { + setSysctl($rInsertID, $rArray['sysctl']); + } + if (file_exists(CACHE_TMP_PATH . 'servers')) { + unlink(CACHE_TMP_PATH . 'servers'); + } + + $rFS = getFreeSpace($rInsertID); + $rMounted = false; + foreach ($rFS as $rMount) { + if ($rMount['mount'] == rtrim(STREAMS_PATH, '/')) { + $rMounted = true; + break; + } + } + + if ($rData['disable_ramdisk'] && $rMounted) { + $db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`) VALUES(?, ?, ?);', $rInsertID, time(), json_encode(array('action' => 'disable_ramdisk'))); + } else if (!$rData['disable_ramdisk'] && !$rMounted) { + $db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`) VALUES(?, ?, ?);', $rInsertID, time(), json_encode(array('action' => 'enable_ramdisk'))); + } + + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + public static function processProxy($rData, $db) { + if (!hasPermissions('adv', 'edit_server')) { + exit(); + } + + $rArray = overwriteData(getStreamingServersByID($rData['edit']), $rData); + foreach (array('enable_https', 'random_ip', 'enable_geoip', 'enabled') as $rKey) { + $rArray[$rKey] = isset($rData[$rKey]); + } + + if (isset($rData['geoip_countries'])) { + $rArray['geoip_countries'] = array(); + foreach ($rData['geoip_countries'] as $rCountry) { + $rArray['geoip_countries'][] = $rCountry; + } + } else { + $rArray['geoip_countries'] = array(); + } + + if (isset($rData['domain_name'])) { + $rArray['domain_name'] = implode(',', $rData['domain_name']); + } else { + $rArray['domain_name'] = ''; + } + + if (strlen($rData['server_ip']) == 0 || !filter_var($rData['server_ip'], FILTER_VALIDATE_IP)) { + return array('status' => STATUS_INVALID_IP, 'data' => $rData); + } + if (checkExists('servers', 'server_ip', $rData['server_ip'], 'id', $rArray['id'])) { + return array('status' => STATUS_EXISTS_IP, 'data' => $rData); + } + + $rArray['server_type'] = 1; + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `servers`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + if (file_exists(CACHE_TMP_PATH . 'servers')) { + unlink(CACHE_TMP_PATH . 'servers'); + } + if (file_exists(CACHE_TMP_PATH . 'proxy_servers')) { + unlink(CACHE_TMP_PATH . 'proxy_servers'); + } + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + public static function install($rData, $db, $rServers, $rProxyServers) { + if (!hasPermissions('adv', 'add_server')) { + exit(); + } + + $rParentIDs = array(); + $rUpdateSysctl = isset($rData['update_sysctl']) ? 1 : 0; + $rPrivateIP = isset($rData['use_private_ip']) ? 1 : 0; + + if ($rData['type'] == 1) { + foreach (json_decode($rData['parent_id'], true) as $rServerID) { + if ($rServers[$rServerID]['server_type'] == 0) { + $rParentIDs[] = intval($rServerID); + } + } + } + + if (isset($rData['edit'])) { + if ($rData['type'] == 1) { + $rServer = $rProxyServers[$rData['edit']]; + } else { + $rServer = $rServers[$rData['edit']]; + } + if (!$rServer) { + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + $db->query('UPDATE `servers` SET `status` = 3, `parent_id` = ? WHERE `id` = ?;', '[' . implode(',', $rParentIDs) . ']', $rServer['id']); + if ($rData['type'] == 1) { + $rCommand = PHP_BIN . ' ' . CLI_PATH . 'balancer.php ' . intval($rData['type']) . ' ' . intval($rServer['id']) . ' ' . intval($rData['ssh_port']) . ' ' . escapeshellarg($rData['root_username']) . ' ' . escapeshellarg($rData['root_password']) . ' ' . intval($rData['http_broadcast_port']) . ' ' . intval($rData['https_broadcast_port']) . ' ' . intval($rUpdateSysctl) . ' ' . intval($rPrivateIP) . ' "' . json_encode($rParentIDs) . '" > "' . BIN_PATH . 'install/' . intval($rServer['id']) . '.install" 2>/dev/null &'; + } else { + $rCommand = PHP_BIN . ' ' . CLI_PATH . 'balancer.php ' . intval($rData['type']) . ' ' . intval($rServer['id']) . ' ' . intval($rData['ssh_port']) . ' ' . escapeshellarg($rData['root_username']) . ' ' . escapeshellarg($rData['root_password']) . ' 80 443 ' . intval($rUpdateSysctl) . ' > "' . BIN_PATH . 'install/' . intval($rServer['id']) . '.install" 2>/dev/null &'; + } + shell_exec($rCommand); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rServer['id'])); + } + + $rArray = verifyPostTable('servers', $rData); + $rArray['status'] = 3; + unset($rArray['id']); + + if (strlen($rArray['server_ip']) == 0 || !filter_var($rArray['server_ip'], FILTER_VALIDATE_IP)) { + return array('status' => STATUS_INVALID_IP, 'data' => $rData); + } + + if ($rData['type'] == 1) { + $rArray['server_type'] = 1; + $rArray['parent_id'] = '[' . implode(',', $rParentIDs) . ']'; + } else { + $rArray['server_type'] = 0; + } + + $rArray['network_interface'] = 'auto'; + $rPrepare = prepareArray($rArray); + $rQuery = 'INSERT INTO `servers`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if (!$db->query($rQuery, ...$rPrepare['data'])) { + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + $rInsertID = $db->last_insert_id(); + if ($rArray['server_type'] == 0) { + CoreUtilities::grantPrivileges($rArray['server_ip']); + } + + if ($rData['type'] == 1) { + $rCommand = PHP_BIN . ' ' . CLI_PATH . 'balancer.php ' . intval($rData['type']) . ' ' . intval($rInsertID) . ' ' . intval($rData['ssh_port']) . ' ' . escapeshellarg($rData['root_username']) . ' ' . escapeshellarg($rData['root_password']) . ' ' . intval($rData['http_broadcast_port']) . ' ' . intval($rData['https_broadcast_port']) . ' ' . intval($rUpdateSysctl) . ' ' . intval($rPrivateIP) . ' "' . json_encode($rParentIDs) . '" > "' . BIN_PATH . 'install/' . intval($rInsertID) . '.install" 2>/dev/null &'; + } else { + $rCommand = PHP_BIN . ' ' . CLI_PATH . 'balancer.php ' . intval($rData['type']) . ' ' . intval($rInsertID) . ' ' . intval($rData['ssh_port']) . ' ' . escapeshellarg($rData['root_username']) . ' ' . escapeshellarg($rData['root_password']) . ' 80 443 ' . intval($rUpdateSysctl) . ' > "' . BIN_PATH . 'install/' . intval($rInsertID) . '.install" 2>/dev/null &'; + } + + shell_exec($rCommand); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + public static function reorder($rData, $db) { + $rPostServers = json_decode($rData['server_order'], true); + if (count($rPostServers) > 0) { + foreach ($rPostServers as $rOrder => $rPostServer) { + $db->query('UPDATE `servers` SET `order` = ? WHERE `id` = ?;', intval($rOrder) + 1, $rPostServer['id']); + } + } + + return array('status' => STATUS_SUCCESS); + } +} diff --git a/src/domain/Settings/SettingsService.php b/src/domain/Settings/SettingsService.php new file mode 100644 index 00000000..c519b63d --- /dev/null +++ b/src/domain/Settings/SettingsService.php @@ -0,0 +1,129 @@ +query('UPDATE `streams_arguments` SET `argument_default_value` = ? WHERE `argument_key` = ?;', ($rData[$rKey] ?: null), $rKey); + unset($rData[$rKey]); + } + + $rArray = verifyPostTable('settings', $rData, true); + + foreach (array('php_loopback', 'restreamer_bypass_proxy', 'request_prebuffer', 'modal_edit', 'group_buttons', 'enable_search', 'on_demand_checker', 'ondemand_balance_equal', 'disable_mag_token', 'allow_cdn_access', 'dts_legacy_ffmpeg', 'mag_load_all_channels', 'disable_xmltv_restreamer', 'disable_playlist_restreamer', 'ffmpeg_warnings', 'reseller_ssl_domain', 'extract_subtitles', 'show_category_duplicates', 'vod_sort_newest', 'header_stats', 'mag_keep_extension', 'keep_protocol', 'read_native_hls', 'player_allow_playlist', 'player_allow_bouquet', 'player_hide_incompatible', 'player_allow_hevc', 'force_epg_timezone', 'check_vod', 'ignore_keyframes', 'save_login_logs', 'save_restart_logs', 'mag_legacy_redirect', 'restrict_playlists', 'monitor_connection_status', 'kill_rogue_ffmpeg', 'show_images', 'on_demand_instant_off', 'on_demand_failure_exit', 'playlist_from_mysql', 'ignore_invalid_users', 'legacy_mag_auth', 'ministra_allow_blank', 'block_proxies', 'block_streaming_servers', 'ip_subnet_match', 'debug_show_errors', 'enable_debug_stalker', 'restart_php_fpm', 'restream_deny_unauthorised', 'api_probe', 'legacy_panel_api', 'hide_failures', 'verify_host', 'encrypt_playlist', 'encrypt_playlist_restreamer', 'mag_disable_ssl', 'legacy_get', 'legacy_xmltv', 'save_closed_connection', 'show_tickets', 'stream_logs_save', 'client_logs_save', 'streams_grouped', 'cloudflare', 'cleanup', 'dashboard_stats', 'dashboard_status', 'dashboard_map', 'dashboard_display_alt', 'recaptcha_enable', 'ip_logout', 'disable_player_api', 'disable_playlist', 'disable_xmltv', 'disable_enigma2', 'disable_ministra', 'enable_isp_lock', 'block_svp', 'disable_ts', 'disable_ts_allow_restream', 'disable_hls', 'disable_hls_allow_restream', 'disable_rtmp', 'disable_rtmp_allow_restream', 'case_sensitive_line', 'county_override_1st', 'disallow_2nd_ip_con', 'use_mdomain_in_lists', 'encrypt_hls', 'disallow_empty_user_agents', 'detect_restream_block_user', 'download_images', 'api_redirect', 'use_buffer', 'audio_restart_loss', 'show_isps', 'priority_backup', 'rtmp_random', 'show_connected_video', 'show_not_on_air_video', 'show_banned_video', 'show_expired_video', 'show_expiring_video', 'show_all_category_mag', 'always_enabled_subtitles', 'enable_connection_problem_indication', 'show_tv_channel_logo', 'show_channel_logo_in_preview', 'disable_trial', 'restrict_same_ip', 'js_navigate') as $rSetting) { + if (isset($rData[$rSetting])) { + $rArray[$rSetting] = 1; + } else { + $rArray[$rSetting] = 0; + } + } + + if (!isset($rData['allowed_stb_types_for_local_recording'])) { + $rArray['allowed_stb_types_for_local_recording'] = array(); + } + + if (!isset($rData['allowed_stb_types'])) { + $rArray['allowed_stb_types'] = array(); + } + + if (!isset($rData['allow_countries'])) { + $rArray['allow_countries'] = array('ALL'); + } + + if ($rArray['mag_legacy_redirect']) { + if (!file_exists(MAIN_HOME . 'www/c/')) { + $db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`) VALUES(?, ?, ?);', SERVER_ID, time(), json_encode(array('action' => 'enable_ministra'))); + } + } else { + if (file_exists(MAIN_HOME . 'www/c/')) { + $db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`) VALUES(?, ?, ?);', SERVER_ID, time(), json_encode(array('action' => 'disable_ministra'))); + } + } + + if (100 < $rArray['search_items']) { + $rArray['search_items'] = 100; + } + + if ($rArray['search_items'] <= 0) { + $rArray['search_items'] = 1; + } + + $rPrepare = prepareArray($rArray); + if (count($rPrepare['data']) <= 0) { + return array('status' => STATUS_FAILURE); + } + + $rQuery = 'UPDATE `settings` SET ' . $rPrepare['update'] . ';'; + if ($db->query($rQuery, ...$rPrepare['data'])) { + call_user_func($rClearSettingsCacheCallback); + return array('status' => STATUS_SUCCESS); + } + + return array('status' => STATUS_FAILURE); + } + + public static function editBackup($db, $rData, $rClearSettingsCacheCallback) { + $rArray = verifyPostTable('settings', $rData, true); + + foreach (array('dropbox_remote') as $rSetting) { + if (isset($rData[$rSetting])) { + $rArray[$rSetting] = 1; + } else { + $rArray[$rSetting] = 0; + } + } + + if (!isset($rData['allowed_stb_types_for_local_recording'])) { + $rArray['allowed_stb_types_for_local_recording'] = array(); + } + + if (!isset($rData['allowed_stb_types'])) { + $rArray['allowed_stb_types'] = array(); + } + + $rPrepare = prepareArray($rArray); + if (count($rPrepare['data']) <= 0) { + return array('status' => STATUS_FAILURE); + } + + $rQuery = 'UPDATE `settings` SET ' . $rPrepare['update'] . ';'; + if ($db->query($rQuery, ...$rPrepare['data'])) { + call_user_func($rClearSettingsCacheCallback); + return array('status' => STATUS_SUCCESS); + } + + return array('status' => STATUS_FAILURE); + } + + public static function editCacheCron($db, $rData, $rClearSettingsCacheCallback) { + $rCheck = array(false, false); + $rCron = array('*', '*', '*', '*', '*'); + $rPattern = '/^[0-9\/*,-]+$/'; + $rCron[0] = $rData['minute']; + preg_match($rPattern, $rCron[0], $rMatches); + $rCheck[0] = 0 < count($rMatches); + $rCron[1] = $rData['hour']; + preg_match($rPattern, $rCron[1], $rMatches); + $rCheck[1] = 0 < count($rMatches); + $rCronOutput = implode(' ', $rCron); + + if (isset($rData['cache_changes'])) { + $rCacheChanges = true; + } else { + $rCacheChanges = false; + } + + if ($rCheck[0] && $rCheck[1]) { + $db->query("UPDATE `crontab` SET `time` = ? WHERE `filename` = 'cache_engine.php';", $rCronOutput); + $db->query('UPDATE `settings` SET `cache_thread_count` = ?, `cache_changes` = ?;', $rData['cache_thread_count'], $rCacheChanges); + + if (file_exists(TMP_PATH . 'crontab')) { + unlink(TMP_PATH . 'crontab'); + } + + call_user_func($rClearSettingsCacheCallback); + return array('status' => STATUS_SUCCESS); + } + + return array('status' => STATUS_FAILURE); + } +} diff --git a/src/domain/Stream/CategoryService.php b/src/domain/Stream/CategoryService.php new file mode 100644 index 00000000..4f26e4da --- /dev/null +++ b/src/domain/Stream/CategoryService.php @@ -0,0 +1,42 @@ += count($rPostCategories)) { + } else { + foreach ($rPostCategories as $rOrder => $rPostCategory) { + $db->query('UPDATE `streams_categories` SET `cat_order` = ?, `parent_id` = 0 WHERE `id` = ?;', intval($rOrder) + 1, $rPostCategory['id']); + } + } + + return array('status' => STATUS_SUCCESS); + } + + public static function process($rData, $db) { + if (isset($rData['edit'])) { + $rArray = overwriteData(getCategory($rData['edit']), $rData); + } else { + $rArray = verifyPostTable('streams_categories', $rData); + $rArray['cat_order'] = 99; + unset($rArray['id']); + } + + if (isset($rData['is_adult'])) { + $rArray['is_adult'] = 1; + } else { + $rArray['is_adult'] = 0; + } + + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `streams_categories`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } +} diff --git a/src/domain/Stream/ChannelService.php b/src/domain/Stream/ChannelService.php new file mode 100644 index 00000000..8c51ae4a --- /dev/null +++ b/src/domain/Stream/ChannelService.php @@ -0,0 +1,444 @@ + intval($rData['channel_type'])); + + if (intval($rData['channel_type']) == 0) { + $rPlaylist = generateSeriesPlaylist($rData['series_no']); + $rArray['stream_source'] = $rPlaylist; + $rArray['series_no'] = intval($rData['series_no']); + } else { + $rVideoFiles = $rData['video_files']; + if (is_string($rVideoFiles)) { + $rVideoFiles = json_decode($rVideoFiles, true); + } + $rArray['stream_source'] = is_array($rVideoFiles) ? $rVideoFiles : array(); + $rArray['series_no'] = 0; + } + + if ($rData['transcode_profile_id'] == -1) { + $rArray['movie_symlink'] = 1; + } else { + $rArray['movie_symlink'] = 0; + } + + if (0 < count($rArray['stream_source'])) { + $rBouquetCreate = array(); + + foreach (json_decode($rData['bouquet_create_list'], true) as $rBouquet) { + $rPrepare = prepareArray(array('bouquet_name' => $rBouquet, 'bouquet_channels' => array(), 'bouquet_movies' => array(), 'bouquet_series' => array(), 'bouquet_radios' => array())); + $rQuery = 'INSERT INTO `bouquets`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if (!$db->query($rQuery, ...$rPrepare['data'])) { + } else { + $rBouquetID = $db->last_insert_id(); + $rBouquetCreate[$rBouquet] = $rBouquetID; + } + } + $rCategoryCreate = array(); + + foreach (json_decode($rData['category_create_list'], true) as $rCategory) { + $rPrepare = prepareArray(array('category_type' => 'live', 'category_name' => $rCategory, 'parent_id' => 0, 'cat_order' => 99, 'is_adult' => 0)); + $rQuery = 'INSERT INTO `streams_categories`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if (!$db->query($rQuery, ...$rPrepare['data'])) { + } else { + $rCategoryID = $db->last_insert_id(); + $rCategoryCreate[$rCategory] = $rCategoryID; + } + } + $rBouquets = array(); + + foreach ($rData['bouquets'] as $rBouquet) { + if (isset($rBouquetCreate[$rBouquet])) { + $rBouquets[] = $rBouquetCreate[$rBouquet]; + } else { + if (!is_numeric($rBouquet)) { + } else { + $rBouquets[] = intval($rBouquet); + } + } + } + $rCategories = array(); + + foreach ($rData['category_id'] as $rCategory) { + if (isset($rCategoryCreate[$rCategory])) { + $rCategories[] = $rCategoryCreate[$rCategory]; + } else { + if (is_numeric($rCategory)) { + $rCategories[] = intval($rCategory); + } + } + } + $rArray['category_id'] = '[' . implode(',', array_map('intval', $rCategories)) . ']'; + + if (!$rSettings['download_images']) { + } else { + $rArray['stream_icon'] = CoreUtilities::downloadImage($rArray['stream_icon'], 3); + } + + if (isset($rData['edit'])) { + } else { + $rArray['order'] = getNextOrder(); + } + + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `streams`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + $rStreamExists = array(); + + if (!isset($rData['edit'])) { + } else { + $db->query('SELECT `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` = ?;', $rInsertID); + + foreach ($db->get_rows() as $rRow) { + $rStreamExists[intval($rRow['server_id'])] = intval($rRow['server_stream_id']); + } + } + + $rStreamsAdded = array(); + $rServerTree = json_decode($rData['server_tree_data'], true); + + foreach ($rServerTree as $rServer) { + if ($rServer['parent'] == '#') { + } else { + $rServerID = intval($rServer['id']); + $rStreamsAdded[] = $rServerID; + $rOD = intval(in_array($rServerID, ($rData['on_demand'] ?: array()))); + + if ($rServer['parent'] == 'source') { + $rParent = null; + } else { + $rParent = intval($rServer['parent']); + } + + if (isset($rStreamExists[$rServerID])) { + $db->query('UPDATE `streams_servers` SET `parent_id` = ?, `on_demand` = ? WHERE `server_stream_id` = ?;', $rParent, $rOD, $rStreamExists[$rServerID]); + } else { + $db->query("INSERT INTO `streams_servers`(`stream_id`, `server_id`, `parent_id`, `on_demand`, `pids_create_channel`, `cchannel_rsources`) VALUES(?, ?, ?, ?, '[]', '[]');", $rInsertID, $rServerID, $rParent, $rOD); + } + } + } + + foreach ($rStreamExists as $rServerID => $rDBID) { + if (in_array($rServerID, $rStreamsAdded)) { + } else { + deleteStream($rInsertID, $rServerID, false, false); + } + } + + if ($rReencode) { + APIRequest(array('action' => 'stream', 'sub' => 'stop', 'stream_ids' => array($rInsertID))); + $db->query("UPDATE `streams_servers` SET `pids_create_channel` = '[]', `cchannel_rsources` = '[]' WHERE `stream_id` = ?;", $rInsertID); + CoreUtilities::queueChannel($rInsertID); + } + + if ($rRestart) { + APIRequest(array('action' => 'stream', 'sub' => 'start', 'stream_ids' => array($rInsertID))); + } + + foreach ($rBouquets as $rBouquet) { + addToBouquet('stream', $rBouquet, $rInsertID); + } + + if (!isset($rData['edit'])) { + } else { + foreach (getBouquets() as $rBouquet) { + if (in_array($rBouquet['id'], $rBouquets)) { + } else { + removeFromBouquet('stream', $rBouquet['id'], $rInsertID); + } + } + } + + CoreUtilities::updateStream($rInsertID); + + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } else { + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + } else { + return array('status' => STATUS_NO_SOURCES, 'data' => $rData); + } + } + + public static function massEdit($rData, $db) { + set_time_limit(0); + ini_set('mysql.connect_timeout', 0); + ini_set('max_execution_time', 0); + ini_set('default_socket_timeout', 0); + + $rArray = array(); + + foreach (array('allow_record', 'rtmp_output') as $rKey) { + if (!isset($rData['c_' . $rKey])) { + } else { + if (isset($rData[$rKey])) { + $rArray[$rKey] = 1; + } else { + $rArray[$rKey] = 0; + } + } + } + + if (!isset($rData['c_transcode_profile_id'])) { + } else { + $rArray['transcode_profile_id'] = $rData['transcode_profile_id']; + + if (0 < $rArray['transcode_profile_id']) { + $rArray['enable_transcode'] = 1; + } else { + $rArray['enable_transcode'] = 0; + } + } + + $rStreamIDs = json_decode($rData['streams'], true); + + if (0 >= count($rStreamIDs)) { + } else { + $rCategoryMap = array(); + + if (!(isset($rData['c_category_id']) && in_array($rData['category_id_type'], array('ADD', 'DEL')))) { + } else { + $db->query('SELECT `id`, `category_id` FROM `streams` WHERE `id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); + + foreach ($db->get_rows() as $rRow) { + $rCategoryMap[$rRow['id']] = (json_decode($rRow['category_id'], true) ?: array()); + } + } + + $rDeleteServers = $rProcessServers = $rStreamExists = array(); + $db->query('SELECT `stream_id`, `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); + + foreach ($db->get_rows() as $rRow) { + $rStreamExists[intval($rRow['stream_id'])][intval($rRow['server_id'])] = intval($rRow['server_stream_id']); + $rProcessServers[intval($rRow['stream_id'])][] = intval($rRow['server_id']); + } + $rBouquets = getBouquets(); + $rDelOptions = $rAddBouquet = $rDelBouquet = array(); + $rEncQuery = $rAddQuery = ''; + + foreach ($rStreamIDs as $rStreamID) { + if (!isset($rData['c_category_id'])) { + } else { + $rCategories = array_map('intval', $rData['category_id']); + + if ($rData['category_id_type'] == 'ADD') { + foreach (($rCategoryMap[$rStreamID] ?: array()) as $rCategoryID) { + if (in_array($rCategoryID, $rCategories)) { + } else { + $rCategories[] = $rCategoryID; + } + } + } else { + if ($rData['category_id_type'] != 'DEL') { + } else { + $rNewCategories = $rCategoryMap[$rStreamID]; + + foreach ($rCategories as $rCategoryID) { + if (($rKey = array_search($rCategoryID, $rNewCategories)) === false) { + } else { + unset($rNewCategories[$rKey]); + } + } + $rCategories = $rNewCategories; + } + } + + $rArray['category_id'] = '[' . implode(',', $rCategories) . ']'; + } + + $rPrepare = prepareArray($rArray); + + if (0 >= count($rPrepare['data'])) { + } else { + $rPrepare['data'][] = $rStreamID; + $rQuery = 'UPDATE `streams` SET ' . $rPrepare['update'] . ' WHERE `id` = ?;'; + $db->query($rQuery, ...$rPrepare['data']); + } + + if (!isset($rData['c_server_tree'])) { + } else { + $rStreamsAdded = array(); + $rServerTree = json_decode($rData['server_tree_data'], true); + + foreach ($rServerTree as $rServer) { + if ($rServer['parent'] == '#') { + } else { + $rServerID = intval($rServer['id']); + + if (in_array($rData['server_type'], array('ADD', 'SET'))) { + $rStreamsAdded[] = $rServerID; + $rOD = intval(in_array($rServerID, ($rData['on_demand'] ?: array()))); + + if ($rServer['parent'] == 'source') { + $rParent = null; + } else { + $rParent = intval($rServer['parent']); + } + + if (isset($rStreamExists[$rServerID])) { + $db->query('UPDATE `streams_servers` SET `parent_id` = ?, `on_demand` = ? WHERE `server_stream_id` = ?;', $rParent, $rOD, $rStreamExists[$rServerID]); + } else { + $rAddQuery .= '(' . intval($rStreamID) . ', ' . intval($rServerID) . ', ' . (($rParent ?: 'NULL')) . ', ' . $rOD . '),'; + } + + $rProcessServers[$rStreamID][] = $rServerID; + } else { + if (!isset($rStreamExists[$rStreamID][$rServerID])) { + } else { + $rDeleteServers[$rServerID][] = $rStreamID; + } + } + } + } + + if ($rData['server_type'] != 'SET') { + } else { + foreach ($rStreamExists as $rServerID => $rDBID) { + if (in_array($rServerID, $rStreamsAdded)) { + } else { + $rDeleteServers[$rServerID][] = $rStreamID; + + if (($rKey = array_search($rServerID, $rProcessServers[$rStreamID])) === false) { + } else { + unset($rProcessServers[$rStreamID][$rKey]); + } + } + } + } + } + + if (!isset($rData['c_bouquets'])) { + } else { + if ($rData['bouquets_type'] == 'SET') { + foreach ($rData['bouquets'] as $rBouquet) { + $rAddBouquet[$rBouquet][] = $rStreamID; + } + + foreach ($rBouquets as $rBouquet) { + if (in_array($rBouquet['id'], $rData['bouquets'])) { + } else { + $rDelBouquet[$rBouquet['id']][] = $rStreamID; + } + } + } else { + if ($rData['bouquets_type'] == 'ADD') { + foreach ($rData['bouquets'] as $rBouquet) { + $rAddBouquet[$rBouquet][] = $rStreamID; + } + } else { + if ($rData['bouquets_type'] != 'DEL') { + } else { + foreach ($rData['bouquets'] as $rBouquet) { + $rDelBouquet[$rBouquet][] = $rStreamID; + } + } + } + } + } + + if (!isset($rData['reencode_on_edit'])) { + } else { + foreach ($rProcessServers[$rStreamID] as $rServerID) { + $rEncQuery .= "('channel', " . intval($rStreamID) . ', ' . intval($rServerID) . ', ' . time() . '),'; + } + } + } + + foreach ($rDeleteServers as $rServerID => $rDeleteIDs) { + deleteStreamsByServer($rDeleteIDs, $rServerID, false); + } + + foreach ($rAddBouquet as $rBouquetID => $rAddIDs) { + addToBouquet('stream', $rBouquetID, $rAddIDs); + } + + foreach ($rDelBouquet as $rBouquetID => $rRemIDs) { + removeFromBouquet('stream', $rBouquetID, $rRemIDs); + } + + if (empty($rAddQuery)) { + } else { + $rAddQuery = rtrim($rAddQuery, ','); + $db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`, `parent_id`, `on_demand`) VALUES ' . $rAddQuery . ';'); + } + + CoreUtilities::updateStreams($rStreamIDs); + + if (isset($rData['reencode_on_edit'])) { + $db->query("UPDATE `streams_servers` SET `pids_create_channel` = '[]', `cchannel_rsources` = '[]' WHERE `stream_id` IN (" . implode(',', array_map('intval', $rStreamIDs)) . ');'); + + if (empty($rEncQuery)) { + } else { + $rEncQuery = rtrim($rEncQuery, ','); + $db->query('INSERT INTO `queue`(`type`, `stream_id`, `server_id`, `added`) VALUES ' . $rEncQuery . ';'); + } + + APIRequest(array('action' => 'stream', 'sub' => 'stop', 'stream_ids' => array_values($rStreamIDs))); + } else { + if (!isset($rData['restart_on_edit'])) { + } else { + APIRequest(array('action' => 'stream', 'sub' => 'start', 'stream_ids' => array_values($rStreamIDs))); + } + } + } + + return array('status' => STATUS_SUCCESS); + } + + public static function setOrder($rData, $db) { + set_time_limit(0); + ini_set('mysql.connect_timeout', 0); + ini_set('max_execution_time', 0); + ini_set('default_socket_timeout', 0); + $rOrder = json_decode($rData['stream_order_array'], true); + $rSort = 0; + + foreach ($rOrder as $rStream) { + $db->query('UPDATE `streams` SET `order` = ? WHERE `id` = ?;', $rSort, $rStream); + $rSort++; + } + + return array('status' => STATUS_SUCCESS); + } +} diff --git a/src/domain/Stream/CronGenerator.php b/src/domain/Stream/CronGenerator.php new file mode 100644 index 00000000..9ba4a77c --- /dev/null +++ b/src/domain/Stream/CronGenerator.php @@ -0,0 +1,28 @@ +query('SELECT * FROM `crontab` WHERE `enabled` = 1;'); + foreach ($db->get_rows() as $rRow) { + $rFullPath = CRON_PATH . $rRow['filename']; + if (pathinfo($rFullPath, PATHINFO_EXTENSION) == 'php' && file_exists($rFullPath)) { + $rJobs[] = $rRow['time'] . ' ' . PHP_BIN . ' ' . $rFullPath . ' # XC_VM'; + } + } + + shell_exec('crontab -r'); + $rTempName = tempnam('/tmp', 'crontab'); + $rHandle = fopen($rTempName, 'w'); + fwrite($rHandle, implode("\n", $rJobs) . "\n"); + fclose($rHandle); + shell_exec('crontab -u xc_vm ' . $rTempName); + @unlink($rTempName); + file_put_contents(TMP_PATH . 'crontab', 1); + return true; + } +} diff --git a/src/domain/Stream/M3UParser.php b/src/domain/Stream/M3UParser.php new file mode 100644 index 00000000..f9be7c8a --- /dev/null +++ b/src/domain/Stream/M3UParser.php @@ -0,0 +1,15 @@ +addDefaultTags(); + + if ($rFile) { + return $rParser->parseFile($rData); + } + + return $rParser->parse($rData); + } +} diff --git a/src/domain/Stream/PlaylistGenerator.php b/src/domain/Stream/PlaylistGenerator.php new file mode 100644 index 00000000..cf8ce115 --- /dev/null +++ b/src/domain/Stream/PlaylistGenerator.php @@ -0,0 +1,395 @@ +query('SELECT t1.output_ext FROM `output_formats` t1 INNER JOIN `output_devices` t2 ON t2.default_output = t1.access_output_id AND `device_key` = ?', $rDeviceKey); + } else { + $db->query('SELECT t1.output_ext FROM `output_formats` t1 WHERE `output_key` = ?', $rOutputKey); + } + + if ($db->num_rows() <= 0) { + return false; + } + + $rCacheName = $rUserInfo['id'] . '_' . $rDeviceKey . '_' . $rOutputKey . '_' . implode('_', ($rTypeKey ?: array())); + $rOutputExt = $db->get_col(); + $rEncryptPlaylist = ($rUserInfo['is_restreamer'] ? $rSettings['encrypt_playlist_restreamer'] : $rSettings['encrypt_playlist']); + if ($rUserInfo['is_stalker']) { + $rEncryptPlaylist = false; + } + + $rDomainName = CoreUtilities::getDomainName(); + if (!$rDomainName) { + exit(); + } + + if (!$rProxy) { + $rRTMPRows = array(); + if ($rOutputKey == 'rtmp') { + $db->query('SELECT t1.id,t2.server_id FROM `streams` t1 INNER JOIN `streams_servers` t2 ON t2.stream_id = t1.id WHERE t1.rtmp_output = 1'); + $rRTMPRows = $db->get_rows(true, 'id', false, 'server_id'); + } + } else { + if ($rOutputKey == 'rtmp') { + $rOutputKey = 'ts'; + } + $rRTMPRows = array(); + } + + if (empty($rOutputExt)) { + $rOutputExt = 'ts'; + } + + $db->query('SELECT t1.*,t2.* FROM `output_devices` t1 LEFT JOIN `output_formats` t2 ON t2.access_output_id = t1.default_output WHERE t1.device_key = ? LIMIT 1', $rDeviceKey); + if ($db->num_rows() <= 0) { + return false; + } + $rDeviceInfo = $db->get_row(); + if (strlen($rUserInfo['access_token']) == 32) { + $rFilename = str_replace('{USERNAME}', $rUserInfo['access_token'], $rDeviceInfo['device_filename']); + } else { + $rFilename = str_replace('{USERNAME}', $rUserInfo['username'], $rDeviceInfo['device_filename']); + } + + if (0 < $rSettings['cache_playlists'] && !$rNoCache && file_exists(PLAYLIST_PATH . md5($rCacheName))) { + header('Content-Description: File Transfer'); + header('Content-Type: audio/mpegurl'); + header('Expires: 0'); + header('Cache-Control: must-revalidate'); + header('Pragma: public'); + header('Content-Disposition: attachment; filename="' . $rFilename . '"'); + header('Content-Length: ' . filesize(PLAYLIST_PATH . md5($rCacheName))); + readfile(PLAYLIST_PATH . md5($rCacheName)); + exit(); + } + + $rData = ''; + $rSeriesAllocation = $rSeriesEpisodes = $rSeriesInfo = array(); + $rUserInfo['episode_ids'] = array(); + if (count($rUserInfo['series_ids']) > 0) { + if ($rCached) { + foreach ($rUserInfo['series_ids'] as $rSeriesID) { + $rSeriesInfo[$rSeriesID] = igbinary_unserialize(file_get_contents(SERIES_TMP_PATH . 'series_' . intval($rSeriesID))); + $rSeriesData = igbinary_unserialize(file_get_contents(SERIES_TMP_PATH . 'episodes_' . intval($rSeriesID))); + foreach ($rSeriesData as $rSeasonID => $rEpisodes) { + foreach ($rEpisodes as $rEpisode) { + $rSeriesEpisodes[$rEpisode['stream_id']] = array($rSeasonID, $rEpisode['episode_num']); + $rSeriesAllocation[$rEpisode['stream_id']] = $rSeriesID; + $rUserInfo['episode_ids'][] = $rEpisode['stream_id']; + } + } + } + } else { + $db->query('SELECT * FROM `streams_series` WHERE `id` IN (' . implode(',', $rUserInfo['series_ids']) . ')'); + $rSeriesInfo = $db->get_rows(true, 'id'); + if (count($rUserInfo['series_ids']) > 0) { + $db->query('SELECT stream_id, series_id, season_num, episode_num FROM `streams_episodes` WHERE series_id IN (' . implode(',', $rUserInfo['series_ids']) . ') ORDER BY FIELD(series_id,' . implode(',', $rUserInfo['series_ids']) . '), season_num ASC, episode_num ASC'); + foreach ($db->get_rows(true, 'series_id', false) as $rSeriesID => $rEpisodes) { + foreach ($rEpisodes as $rEpisode) { + $rSeriesEpisodes[$rEpisode['stream_id']] = array($rEpisode['season_num'], $rEpisode['episode_num']); + $rSeriesAllocation[$rEpisode['stream_id']] = $rSeriesID; + $rUserInfo['episode_ids'][] = $rEpisode['stream_id']; + } + } + } + } + } + + if (count($rUserInfo['episode_ids']) > 0) { + $rUserInfo['channel_ids'] = array_merge($rUserInfo['channel_ids'], $rUserInfo['episode_ids']); + } + + $rChannelIDs = array(); + $rAdded = false; + if ($rTypeKey) { + foreach ($rTypeKey as $rType) { + switch ($rType) { + case 'live': + case 'created_live': + if (!$rAdded) { + $rChannelIDs = array_merge($rChannelIDs, $rUserInfo['live_ids']); + $rAdded = true; + } + break; + case 'movie': + $rChannelIDs = array_merge($rChannelIDs, $rUserInfo['vod_ids']); + break; + case 'radio_streams': + $rChannelIDs = array_merge($rChannelIDs, $rUserInfo['radio_ids']); + break; + case 'series': + $rChannelIDs = array_merge($rChannelIDs, $rUserInfo['episode_ids']); + break; + } + } + } else { + $rChannelIDs = $rUserInfo['channel_ids']; + } + + if (in_array($rSettings['channel_number_type'], array('bouquet_new', 'manual'))) { + $rChannelIDs = CoreUtilities::sortChannels($rChannelIDs); + } + + unset($rUserInfo['live_ids'], $rUserInfo['vod_ids'], $rUserInfo['radio_ids'], $rUserInfo['episode_ids'], $rUserInfo['channel_ids']); + + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header('Expires: 0'); + header('Cache-Control: must-revalidate'); + header('Pragma: public'); + if (strlen($rUserInfo['access_token']) == 32) { + header('Content-Disposition: attachment; filename="' . str_replace('{USERNAME}', $rUserInfo['access_token'], $rDeviceInfo['device_filename']) . '"'); + } else { + header('Content-Disposition: attachment; filename="' . str_replace('{USERNAME}', $rUserInfo['username'], $rDeviceInfo['device_filename']) . '"'); + } + + $rOutputFile = null; + if ($rSettings['cache_playlists'] == 1) { + $rOutputPath = PLAYLIST_PATH . md5($rCacheName) . '.write'; + $rOutputFile = fopen($rOutputPath, 'w'); + } + + if ($rDeviceKey == 'starlivev5') { + $rOutput = array(); + $rOutput['iptvstreams_list'] = array('@version' => 1, 'group' => array('name' => 'IPTV', 'channel' => array())); + foreach (array_chunk($rChannelIDs, 1000) as $rBlockIDs) { + if ($rSettings['playlist_from_mysql'] || !$rCached) { + $rOrder = 'FIELD(`t1`.`id`,' . implode(',', $rBlockIDs) . ')'; + $db->query('SELECT t1.id,t1.channel_id,t1.year,t1.movie_properties,t1.stream_icon,t1.custom_sid,t1.category_id,t1.stream_display_name,t2.type_output,t2.type_key,t1.target_container,t2.live FROM `streams` t1 INNER JOIN `streams_types` t2 ON t2.type_id = t1.type WHERE `t1`.`id` IN (' . implode(',', array_map('intval', $rBlockIDs)) . ') ORDER BY ' . $rOrder . ';'); + $rRows = $db->get_rows(); + } else { + $rRows = array(); + foreach ($rBlockIDs as $rID) { + $rRows[] = igbinary_unserialize(file_get_contents(STREAMS_TMP_PATH . 'stream_' . intval($rID)))['info']; + } + } + foreach ($rRows as $rChannelInfo) { + if (!$rTypeKey || in_array($rChannelInfo['type_output'], $rTypeKey)) { + if (!$rChannelInfo['target_container']) { + $rChannelInfo['target_container'] = 'mp4'; + } + $rProperties = (!is_array($rChannelInfo['movie_properties']) ? json_decode($rChannelInfo['movie_properties'], true) : $rChannelInfo['movie_properties']); + if ($rChannelInfo['type_key'] == 'series') { + $rSeriesID = $rSeriesAllocation[$rChannelInfo['id']]; + $rChannelInfo['live'] = 0; + $rChannelInfo['stream_display_name'] = $rSeriesInfo[$rSeriesID]['title'] . ' S' . sprintf('%02d', $rSeriesEpisodes[$rChannelInfo['id']][0]) . 'E' . sprintf('%02d', $rSeriesEpisodes[$rChannelInfo['id']][1]); + $rChannelInfo['movie_properties'] = array('movie_image' => (!empty($rProperties['movie_image']) ? $rProperties['movie_image'] : $rSeriesInfo['cover'])); + $rChannelInfo['type_output'] = 'series'; + $rChannelInfo['category_id'] = $rSeriesInfo[$rSeriesID]['category_id']; + } else { + $rChannelInfo['stream_display_name'] = CoreUtilities::formatTitle($rChannelInfo['stream_display_name'], $rChannelInfo['year']); + } + if (strlen($rUserInfo['access_token']) == 32) { + $rURL = $rDomainName . $rChannelInfo['type_output'] . '/' . $rUserInfo['access_token'] . '/'; + if ($rChannelInfo['live'] == 0) { + $rURL .= $rChannelInfo['id'] . '.' . $rChannelInfo['target_container']; + } else { + $rURL .= ($rSettings['cloudflare'] && $rOutputExt == 'ts') ? $rChannelInfo['id'] : ($rChannelInfo['id'] . '.' . $rOutputExt); + } + } else { + if ($rEncryptPlaylist) { + $rEncData = $rChannelInfo['type_output'] . '/' . $rUserInfo['username'] . '/' . $rUserInfo['password'] . '/'; + if ($rChannelInfo['live'] == 0) { + $rEncData .= $rChannelInfo['id'] . '/' . $rChannelInfo['target_container']; + } else { + $rEncData .= ($rSettings['cloudflare'] && $rOutputExt == 'ts') ? $rChannelInfo['id'] : ($rChannelInfo['id'] . '/' . $rOutputExt); + } + $rToken = CoreUtilities::encryptData($rEncData, $rSettings['live_streaming_pass'], OPENSSL_EXTRA); + $rURL = $rDomainName . 'play/' . $rToken; + if ($rChannelInfo['live'] == 0) { + $rURL .= '#.' . $rChannelInfo['target_container']; + } + } else { + $rURL = $rDomainName . $rChannelInfo['type_output'] . '/' . $rUserInfo['username'] . '/' . $rUserInfo['password'] . '/'; + if ($rChannelInfo['live'] == 0) { + $rURL .= $rChannelInfo['id'] . '.' . $rChannelInfo['target_container']; + } else { + $rURL .= ($rSettings['cloudflare'] && $rOutputExt == 'ts') ? $rChannelInfo['id'] : ($rChannelInfo['id'] . '.' . $rOutputExt); + } + } + } + $rIcon = ($rChannelInfo['live'] == 0 ? (!empty($rProperties['movie_image']) ? $rProperties['movie_image'] : null) : $rChannelInfo['stream_icon']); + $rOutput['iptvstreams_list']['group']['channel'][] = array('name' => $rChannelInfo['stream_display_name'], 'icon' => CoreUtilities::validateImage($rIcon), 'stream_url' => $rURL, 'stream_type' => 0); + } + } + } + $rData = json_encode((object) $rOutput); + if ($rOutputFile) { + fwrite($rOutputFile, $rData); + } + echo $rData; + } else { + if (!empty($rDeviceInfo['device_header'])) { + $epgUrl = $rDomainName . 'epg/' . $rUserInfo['username'] . '/' . $rUserInfo['password']; + $isM3UFormat = (strpos($rDeviceInfo['device_header'], '#EXTM3U') !== false); + if ($isM3UFormat && strpos($rDeviceInfo['device_header'], 'x-tvg-url') === false) { + $rDeviceInfo['device_header'] = str_replace('#EXTM3U', '#EXTM3U x-tvg-url="' . $epgUrl . '"', $rDeviceInfo['device_header']); + } + $rAppend = ($isM3UFormat ? "\n" . '#EXT-X-SESSION-DATA:DATA-ID="com.xc_vm.' . str_replace('.', '_', XC_VM_VERSION) . '"' : ''); + $rData = str_replace(array('<', '>'), array('<', '>'), str_replace(array('{BOUQUET_NAME}', '{USERNAME}', '{PASSWORD}', '{SERVER_URL}', '{OUTPUT_KEY}'), array($rSettings['server_name'], $rUserInfo['username'], $rUserInfo['password'], $rDomainName, $rOutputKey), $rDeviceInfo['device_header'] . $rAppend)) . "\n"; + if ($rOutputFile) { + fwrite($rOutputFile, $rData); + } + echo $rData; + } + + if (!empty($rDeviceInfo['device_conf'])) { + if (preg_match('/\{URL\#(.*?)\}/', $rDeviceInfo['device_conf'], $rMatches)) { + $rCharts = str_split($rMatches[1]); + $rPattern = $rMatches[0]; + } else { + $rCharts = array(); + $rPattern = '{URL}'; + } + + foreach (array_chunk($rChannelIDs, 1000) as $rBlockIDs) { + if ($rSettings['playlist_from_mysql'] || !$rCached) { + $rOrder = 'FIELD(`t1`.`id`,' . implode(',', $rBlockIDs) . ')'; + $db->query('SELECT t1.id,t1.channel_id,t1.year,t1.movie_properties,t1.stream_icon,t1.custom_sid,t1.category_id,t1.stream_display_name,t2.type_output,t2.type_key,t1.target_container,t2.live,t1.tv_archive_duration,t1.tv_archive_server_id FROM `streams` t1 INNER JOIN `streams_types` t2 ON t2.type_id = t1.type WHERE `t1`.`id` IN (' . implode(',', array_map('intval', $rBlockIDs)) . ') ORDER BY ' . $rOrder . ';'); + $rRows = $db->get_rows(); + } else { + $rRows = array(); + foreach ($rBlockIDs as $rID) { + $rRows[] = igbinary_unserialize(file_get_contents(STREAMS_TMP_PATH . 'stream_' . intval($rID)))['info']; + } + } + + foreach ($rRows as $rChannel) { + if ($rTypeKey && !in_array($rChannel['type_output'], $rTypeKey)) { + continue; + } + if (!$rChannel['target_container']) { + $rChannel['target_container'] = 'mp4'; + } + $rConfig = $rDeviceInfo['device_conf']; + if ($rDeviceInfo['device_key'] == 'm3u_plus') { + if (!$rChannel['live']) { + $rConfig = str_replace('tvg-id="{CHANNEL_ID}" ', '', $rConfig); + } + if (!$rEncryptPlaylist) { + $rConfig = str_replace('xc_vm-id="{XC_VM_ID}" ', '', $rConfig); + } + if (0 < $rChannel['tv_archive_server_id'] && 0 < $rChannel['tv_archive_duration']) { + $rConfig = str_replace('#EXTINF:-1 ', '#EXTINF:-1 timeshift="' . intval($rChannel['tv_archive_duration']) . '" ', $rConfig); + } + } + + $rProperties = (!is_array($rChannel['movie_properties']) ? json_decode($rChannel['movie_properties'], true) : $rChannel['movie_properties']); + if ($rChannel['type_key'] == 'series') { + $rSeriesID = $rSeriesAllocation[$rChannel['id']]; + $rChannel['live'] = 0; + $rChannel['stream_display_name'] = $rSeriesInfo[$rSeriesID]['title'] . ' S' . sprintf('%02d', $rSeriesEpisodes[$rChannel['id']][0]) . 'E' . sprintf('%02d', $rSeriesEpisodes[$rChannel['id']][1]); + $rChannel['movie_properties'] = array('movie_image' => (!empty($rProperties['movie_image']) ? $rProperties['movie_image'] : $rSeriesInfo['cover'])); + $rChannel['type_output'] = 'series'; + $rChannel['category_id'] = $rSeriesInfo[$rSeriesID]['category_id']; + } else { + $rChannel['stream_display_name'] = CoreUtilities::formatTitle($rChannel['stream_display_name'], $rChannel['year']); + } + + if ($rChannel['live'] == 0) { + if (strlen($rUserInfo['access_token']) == 32) { + $rURL = $rDomainName . $rChannel['type_output'] . '/' . $rUserInfo['access_token'] . '/' . $rChannel['id'] . '.' . $rChannel['target_container']; + } else if ($rEncryptPlaylist) { + $rEncData = $rChannel['type_output'] . '/' . $rUserInfo['username'] . '/' . $rUserInfo['password'] . '/' . $rChannel['id'] . '/' . $rChannel['target_container']; + $rToken = CoreUtilities::encryptData($rEncData, $rSettings['live_streaming_pass'], OPENSSL_EXTRA); + $rURL = $rDomainName . 'play/' . $rToken . '#.' . $rChannel['target_container']; + } else { + $rURL = $rDomainName . $rChannel['type_output'] . '/' . $rUserInfo['username'] . '/' . $rUserInfo['password'] . '/' . $rChannel['id'] . '.' . $rChannel['target_container']; + } + if (!empty($rProperties['movie_image'])) { + $rIcon = $rProperties['movie_image']; + } + } else { + if ($rOutputKey != 'rtmp' || !array_key_exists($rChannel['id'], $rRTMPRows)) { + if (strlen($rUserInfo['access_token']) == 32) { + if ($rSettings['cloudflare'] && $rOutputExt == 'ts') { + $rURL = $rDomainName . $rChannel['type_output'] . '/' . $rUserInfo['access_token'] . '/' . $rChannel['id']; + } else { + $rURL = $rDomainName . $rChannel['type_output'] . '/' . $rUserInfo['access_token'] . '/' . $rChannel['id'] . '.' . $rOutputExt; + } + } else if ($rEncryptPlaylist) { + $rEncData = $rChannel['type_output'] . '/' . $rUserInfo['username'] . '/' . $rUserInfo['password'] . '/' . $rChannel['id']; + $rToken = CoreUtilities::encryptData($rEncData, $rSettings['live_streaming_pass'], OPENSSL_EXTRA); + if ($rSettings['cloudflare'] && $rOutputExt == 'ts') { + $rURL = $rDomainName . 'play/' . $rToken; + } else { + $rURL = $rDomainName . 'play/' . $rToken . '/' . $rOutputExt; + } + } else { + if ($rSettings['cloudflare'] && $rOutputExt == 'ts') { + $rURL = $rDomainName . $rUserInfo['username'] . '/' . $rUserInfo['password'] . '/' . $rChannel['id']; + } else { + $rURL = $rDomainName . $rUserInfo['username'] . '/' . $rUserInfo['password'] . '/' . $rChannel['id'] . '.' . $rOutputExt; + } + } + } else { + $rAvailableServers = array_values(array_keys($rRTMPRows[$rChannel['id']])); + if (in_array($rUserInfo['force_server_id'], $rAvailableServers)) { + $rServerID = $rUserInfo['force_server_id']; + } else { + $rServerID = ($rSettings['rtmp_random'] == 1 ? $rAvailableServers[array_rand($rAvailableServers, 1)] : $rAvailableServers[0]); + } + if (strlen($rUserInfo['access_token']) == 32) { + $rURL = $rServers[$rServerID]['rtmp_server'] . $rChannel['id'] . '?token=' . $rUserInfo['access_token']; + } else if ($rEncryptPlaylist) { + $rEncData = $rUserInfo['username'] . '/' . $rUserInfo['password']; + $rToken = CoreUtilities::encryptData($rEncData, $rSettings['live_streaming_pass'], OPENSSL_EXTRA); + $rURL = $rServers[$rServerID]['rtmp_server'] . $rChannel['id'] . '?token=' . $rToken; + } else { + $rURL = $rServers[$rServerID]['rtmp_server'] . $rChannel['id'] . '?username=' . $rUserInfo['username'] . '&password=' . $rUserInfo['password']; + } + } + $rIcon = $rChannel['stream_icon']; + } + + $rESRID = ($rChannel['live'] == 1 ? 1 : 4097); + $rSID = (!empty($rChannel['custom_sid']) ? $rChannel['custom_sid'] : ':0:1:0:0:0:0:0:0:0:'); + $rCategoryIDs = json_decode($rChannel['category_id'], true); + if (empty($rCategoryIDs)) { + $rCategoryIDs = [0]; + } + foreach ($rCategoryIDs as $rCategoryID) { + if (isset($rCategories[$rCategoryID])) { + $rData = str_replace(array('<', '>'), array('<', '>'), str_replace(array($rPattern, '{ESR_ID}', '{SID}', '{CHANNEL_NAME}', '{CHANNEL_ID}', '{XC_VM_ID}', '{CATEGORY}', '{CHANNEL_ICON}'), array(str_replace($rCharts, array_map('urlencode', $rCharts), $rURL), $rESRID, $rSID, $rChannel['stream_display_name'], $rChannel['channel_id'], $rChannel['id'], $rCategories[$rCategoryID]['category_name'], CoreUtilities::validateImage($rIcon)), $rConfig)) . "\r\n"; + } else { + $rData = str_replace(array('<', '>'), array('<', '>'), str_replace(array($rPattern, '{ESR_ID}', '{SID}', '{CHANNEL_NAME}', '{CHANNEL_ID}', '{XC_VM_ID}', '{CHANNEL_ICON}'), array(str_replace($rCharts, array_map('urlencode', $rCharts), $rURL), $rESRID, $rSID, $rChannel['stream_display_name'], $rChannel['channel_id'], $rChannel['id'], $rIcon), $rConfig)) . "\r\n"; + $rData = str_replace(' group-title="{CATEGORY}"', '', $rData); + } + if ($rOutputFile) { + fwrite($rOutputFile, $rData); + } + echo $rData; + if (stripos($rDeviceInfo['device_conf'], '{CATEGORY}') === false) { + break; + } + } + } + } + + $rData = trim(str_replace(array('<', '>'), array('<', '>'), $rDeviceInfo['device_footer'])); + if ($rOutputFile) { + fwrite($rOutputFile, $rData); + } + echo $rData; + } + } + + if ($rOutputFile) { + fclose($rOutputFile); + rename(PLAYLIST_PATH . md5($rCacheName) . '.write', PLAYLIST_PATH . md5($rCacheName)); + } + exit(); + } +} diff --git a/src/domain/Stream/StreamProcess.php b/src/domain/Stream/StreamProcess.php new file mode 100644 index 00000000..fbaa7cbf --- /dev/null +++ b/src/domain/Stream/StreamProcess.php @@ -0,0 +1,1000 @@ +query('SELECT `id` FROM `queue` WHERE `stream_id` = ? AND `server_id` = ?;', $rStreamID, $rServerID); + if ($db->num_rows() != 0) { + } else { + $db->query("INSERT INTO `queue`(`type`, `stream_id`, `server_id`, `added`) VALUES('channel', ?, ?, ?);", $rStreamID, $rServerID, time()); + } + } + + public static function createChannel($rStreamID) { + shell_exec(PHP_BIN . ' ' . CLI_PATH . 'created.php ' . intval($rStreamID) . ' >/dev/null 2>/dev/null &'); + return true; + } + + public static function startMonitor($rStreamID, $rRestart = 0) { + shell_exec(PHP_BIN . ' ' . CLI_PATH . 'monitor.php ' . intval($rStreamID) . ' ' . intval($rRestart) . ' >/dev/null 2>/dev/null &'); + return true; + } + + public static function startProxy($rStreamID) { + shell_exec(PHP_BIN . ' ' . CLI_PATH . 'proxy.php ' . intval($rStreamID) . ' >/dev/null 2>/dev/null &'); + return true; + } + + public static function startThumbnail($rStreamID) { + shell_exec(PHP_BIN . ' ' . CLI_PATH . 'thumbnail.php ' . intval($rStreamID) . ' >/dev/null 2>/dev/null &'); + return true; + } + + public static function updateStream($db, $rCached, $rMainID, $rStreamID, $rForce = false) { + if ($rCached) { + $db->query('SELECT COUNT(*) AS `count` FROM `signals` WHERE `server_id` = ? AND `cache` = 1 AND `custom_data` = ?;', $rMainID, json_encode(array('type' => 'update_stream', 'id' => $rStreamID))); + if ($db->get_row()['count'] != 0) { + } else { + $db->query('INSERT INTO `signals`(`server_id`, `cache`, `time`, `custom_data`) VALUES(?, 1, ?, ?);', $rMainID, time(), json_encode(array('type' => 'update_stream', 'id' => $rStreamID))); + } + return true; + } + return false; + } + + public static function updateStreams($db, $rCached, $rMainID, $rStreamIDs) { + if ($rCached) { + $db->query('SELECT COUNT(*) AS `count` FROM `signals` WHERE `server_id` = ? AND `cache` = 1 AND `custom_data` = ?;', $rMainID, json_encode(array('type' => 'update_streams', 'id' => $rStreamIDs))); + if ($db->get_row()['count'] != 0) { + } else { + $db->query('INSERT INTO `signals`(`server_id`, `cache`, `time`, `custom_data`) VALUES(?, 1, ?, ?);', $rMainID, time(), json_encode(array('type' => 'update_streams', 'id' => $rStreamIDs))); + } + return true; + } + return false; + } + + public static function createChannelItem($db, $rSettings, $rServers, $rFFMPEGCPU, $rFFMPEGGPU, $rStreamID, $rSource) { + $rStream = array(); + $rLoopback = false; + $db->query('SELECT * FROM `streams` t1 INNER JOIN `streams_types` t2 ON t2.type_id = t1.type AND t1.type = 3 LEFT JOIN `profiles` t4 ON t1.transcode_profile_id = t4.profile_id WHERE t1.direct_source = 0 AND t1.id = ?', $rStreamID); + if ($db->num_rows() > 0) { + $rStream['stream_info'] = $db->get_row(); + $db->query('SELECT * FROM `streams_servers` WHERE stream_id = ? AND `server_id` = ?', $rStreamID, SERVER_ID); + if ($db->num_rows() > 0) { + $rStream['server_info'] = $db->get_row(); + $rMD5 = md5($rSource); + if (substr($rSource, 0, 2) == 's:') { + $rSplit = explode(':', $rSource, 3); + $rServerID = intval($rSplit[1]); + $rSourcePath = $rSplit[2]; + if ($rServerID != SERVER_ID) { + if (is_array($rServers) && isset($rServers[$rServerID])) { + $rSourcePath = $rServers[$rServerID]['api_url'] . '&action=getFile&filename=' . urlencode($rSplit[2]); + } else { + $rSourcePath = $rSplit[2]; + } + } + } else { + $rServerID = SERVER_ID; + $rSourcePath = $rSource; + } + + if ($rServerID == SERVER_ID && intval($rStream['stream_info']['movie_symlink']) == 1) { + $rExtension = pathinfo($rSource)['extension']; + if (strlen($rExtension) == 0) { + $rExtension = 'mp4'; + } + $rCommand = 'ln -sfn ' . escapeshellarg($rSourcePath) . ' "' . CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.' . escapeshellcmd($rExtension) . '" >/dev/null 2>/dev/null & echo $! > "' . CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.pid"'; + } else { + $rStream['stream_info']['transcode_attributes'] = json_decode($rStream['stream_info']['profile_options'], true); + if (!is_array($rStream['stream_info']['transcode_attributes'])) { + $rStream['stream_info']['transcode_attributes'] = array(); + } + + $rLogoOptions = ''; + if (isset($rStream['stream_info']['transcode_attributes'][16]) && !$rLoopback) { + $rAttr = $rStream['stream_info']['transcode_attributes']; + $rLogoPath = $rAttr[16]['val']; + $rPos = (isset($rAttr[16]['pos']) && $rAttr[16]['pos'] !== '10:10') ? $rAttr[16]['pos'] : '10:main_h-overlay_h-10'; + + $rChain = array(); + $rBase = '[0:v]'; + $rVideoFilters = array(); + if (isset($rAttr[17])) { + $rVideoFilters[] = 'yadif'; + } + if (isset($rAttr[9]['val']) && strlen($rAttr[9]['val']) > 0) { + $rVideoFilters[] = 'scale=' . $rAttr[9]['val']; + } + + if (!empty($rVideoFilters)) { + $rChain[] = $rBase . implode(',', $rVideoFilters) . '[bg]'; + $rBase = '[bg]'; + } + + $rChain[] = '[1:v]scale=250:-1[logo]'; + $rChain[] = $rBase . '[logo]overlay=' . $rPos; + + $rLogoOptions = '-i ' . escapeshellarg($rLogoPath) . ' -filter_complex "' . implode('; ', $rChain) . '"'; + unset($rStream['stream_info']['transcode_attributes'][16]); + } + + $rGPUOptions = (isset($rStream['stream_info']['transcode_attributes']['gpu']) ? $rStream['stream_info']['transcode_attributes']['gpu']['cmd'] : ''); + $rInputCodec = ''; + if (!empty($rGPUOptions)) { + $rFFProbeOutput = CoreUtilities::probeStream($rSourcePath); + if (in_array($rFFProbeOutput['codecs']['video']['codec_name'], array('h264', 'hevc', 'mjpeg', 'mpeg1', 'mpeg2', 'mpeg4', 'vc1', 'vp8', 'vp9'))) { + $rInputCodec = '-c:v ' . $rFFProbeOutput['codecs']['video']['codec_name'] . '_cuvid'; + } + } + + $rCommand = ((isset($rStream['stream_info']['transcode_attributes']['gpu']) ? $rFFMPEGGPU : $rFFMPEGCPU)) . ' -y -nostdin -hide_banner -loglevel ' . (($rSettings['ffmpeg_warnings'] ? 'warning' : 'error')) . ' -err_detect ignore_err {GPU} -fflags +genpts -async 1 -i {STREAM_SOURCE} {LOGO} '; + + if (!array_key_exists('-acodec', $rStream['stream_info']['transcode_attributes'])) { + $rStream['stream_info']['transcode_attributes']['-acodec'] = 'copy'; + } + if (!array_key_exists('-vcodec', $rStream['stream_info']['transcode_attributes'])) { + $rStream['stream_info']['transcode_attributes']['-vcodec'] = 'copy'; + } + if (isset($rStream['stream_info']['transcode_attributes']['gpu'])) { + $rCommand .= '-gpu ' . intval($rStream['stream_info']['transcode_attributes']['gpu']['device']) . ' '; + } + $rCommand .= implode(' ', CoreUtilities::parseTranscode($rStream['stream_info']['transcode_attributes'])) . ' '; + $rCommand .= '-strict -2 -mpegts_flags +initial_discontinuity -f mpegts "' . CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.ts"'; + $rCommand .= ' >/dev/null 2>"' . CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.errors" & echo $! > "' . CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.pid"'; + $rCommand = str_replace(array('{GPU}', '{INPUT_CODEC}', '{LOGO}', '{STREAM_SOURCE}'), array($rGPUOptions, $rInputCodec, $rLogoOptions, escapeshellarg($rSourcePath)), $rCommand); + } + + shell_exec($rCommand); + return intval(file_get_contents(CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.pid')); + } + return false; + } + return false; + } + + public static function stopStream($db, $rStreamID, $rStop = false) { + if (file_exists(STREAMS_PATH . $rStreamID . '_.monitor')) { + $rMonitor = intval(file_get_contents(STREAMS_PATH . $rStreamID . '_.monitor')); + } else { + $db->query('SELECT `monitor_pid` FROM `streams_servers` WHERE `server_id` = ? AND `stream_id` = ? LIMIT 1;', SERVER_ID, $rStreamID); + $rMonitor = intval($db->get_row()['monitor_pid']); + } + + if (0 < $rMonitor && CoreUtilities::checkPID($rMonitor, array('XC_VM[' . $rStreamID . ']', 'XC_VMProxy[' . $rStreamID . ']')) && is_numeric($rMonitor)) { + posix_kill($rMonitor, 9); + } + + if (file_exists(STREAMS_PATH . $rStreamID . '_.pid')) { + $rPID = intval(file_get_contents(STREAMS_PATH . $rStreamID . '_.pid')); + } else { + $db->query('SELECT `pid` FROM `streams_servers` WHERE `server_id` = ? AND `stream_id` = ? LIMIT 1;', SERVER_ID, $rStreamID); + $rPID = intval($db->get_row()['pid']); + } + + if (0 < $rPID && CoreUtilities::checkPID($rPID, array($rStreamID . '_.m3u8', $rStreamID . '_%d.ts', 'LLOD[' . $rStreamID . ']', 'XC_VMProxy[' . $rStreamID . ']', 'Loopback[' . $rStreamID . ']')) && is_numeric($rPID)) { + posix_kill($rPID, 9); + } + + if (file_exists(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID))) { + unlink(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID)); + } + + CoreUtilities::streamLog($rStreamID, SERVER_ID, 'STREAM_STOP'); + shell_exec('rm -f ' . STREAMS_PATH . intval($rStreamID) . '_*'); + + if ($rStop) { + shell_exec('rm -f ' . DELAY_PATH . intval($rStreamID) . '_*'); + $db->query('UPDATE `streams_servers` SET `bitrate` = NULL,`current_source` = NULL,`to_analyze` = 0,`pid` = NULL,`stream_started` = NULL,`stream_info` = NULL,`audio_codec` = NULL,`video_codec` = NULL,`resolution` = NULL,`compatible` = 0,`stream_status` = 0,`monitor_pid` = NULL WHERE `stream_id` = ? AND `server_id` = ?', $rStreamID, SERVER_ID); + CoreUtilities::updateStream($rStreamID); + } + } + + public static function stopMovie($db, $rStreamID, $rForce = false) { + shell_exec("kill -9 `ps -ef | grep '/" . intval($rStreamID) . ".' | grep -v grep | awk '{print \$2}'`;" ); + if ($rForce) { + exec('rm ' . MAIN_HOME . 'content/vod/' . intval($rStreamID) . '.*'); + } else { + $db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`, `cache`) VALUES(?, ?, ?, 1);', SERVER_ID, time(), json_encode(array('type' => 'delete_vod', 'id' => $rStreamID))); + } + $db->query('UPDATE `streams_servers` SET `bitrate` = NULL,`current_source` = NULL,`to_analyze` = 0,`pid` = NULL,`stream_started` = NULL,`stream_info` = NULL,`audio_codec` = NULL,`video_codec` = NULL,`resolution` = NULL,`compatible` = 0,`stream_status` = 0 WHERE `stream_id` = ? AND `server_id` = ?', $rStreamID, SERVER_ID); + CoreUtilities::updateStream($rStreamID); + } + + public static function queueMovie($db, $rStreamID, $rServerID = null) { + if ($rServerID) { + } else { + $rServerID = SERVER_ID; + } + $db->query('DELETE FROM `queue` WHERE `stream_id` = ? AND `server_id` = ?;', $rStreamID, $rServerID); + $db->query("INSERT INTO `queue`(`type`, `stream_id`, `server_id`, `added`) VALUES('movie', ?, ?, ?);", $rStreamID, $rServerID, time()); + } + + public static function queueMovies($db, $rStreamIDs, $rServerID = null) { + if ($rServerID) { + } else { + $rServerID = SERVER_ID; + } + if (0 >= count($rStreamIDs)) { + } else { + $db->query('DELETE FROM `queue` WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ') AND `server_id` = ?;', $rServerID); + $rQuery = ''; + foreach ($rStreamIDs as $rStreamID) { + if (0 >= $rStreamID) { + } else { + $rQuery .= "('movie', " . intval($rStreamID) . ', ' . intval($rServerID) . ', ' . time() . '),'; + } + } + if (empty($rQuery)) { + } else { + $rQuery = rtrim($rQuery, ','); + $db->query('INSERT INTO `queue`(`type`, `stream_id`, `server_id`, `added`) VALUES ' . $rQuery . ';'); + } + } + } + + public static function refreshMovies($db, $rIDs, $rType = 1) { + if (0 >= count($rIDs)) { + } else { + $db->query('DELETE FROM `watch_refresh` WHERE `type` = ? AND `stream_id` IN (' . implode(',', array_map('intval', $rIDs)) . ');', $rType); + $rQuery = ''; + foreach ($rIDs as $rID) { + if (0 >= $rID) { + } else { + $rQuery .= '(' . intval($rType) . ', ' . intval($rID) . ', 0),'; + } + } + if (empty($rQuery)) { + } else { + $rQuery = rtrim($rQuery, ','); + $db->query('INSERT INTO `watch_refresh`(`type`, `stream_id`, `status`) VALUES ' . $rQuery . ';'); + } + } + } + + public static function startMovie($db, $rSettings, $rServers, $rFFMPEGCPU, $rFFMPEGGPU, $rStreamID) { + $rStream = array(); + $rLoopback = false; + $db->query('SELECT * FROM `streams` t1 INNER JOIN `streams_types` t2 ON t2.type_id = t1.type AND t2.live = 0 LEFT JOIN `profiles` t4 ON t1.transcode_profile_id = t4.profile_id WHERE t1.direct_source = 0 AND t1.id = ?', $rStreamID); + if ($db->num_rows() > 0) { + $rStream['stream_info'] = $db->get_row(); + $db->query('SELECT * FROM `streams_servers` WHERE stream_id = ? AND `server_id` = ?', $rStreamID, SERVER_ID); + if ($db->num_rows() > 0) { + $rStream['server_info'] = $db->get_row(); + $db->query('SELECT t1.*, t2.* FROM `streams_options` t1, `streams_arguments` t2 WHERE t1.stream_id = ? AND t1.argument_id = t2.id', $rStreamID); + $rStream['stream_arguments'] = $db->get_rows(); + + list($rStreamSource) = json_decode($rStream['stream_info']['stream_source'], true); + if (substr($rStreamSource, 0, 2) == 's:') { + $rMovieSource = explode(':', $rStreamSource, 3); + $rMovieServerID = $rMovieSource[1]; + if ($rMovieServerID != SERVER_ID) { + $rMoviePath = $rServers[$rMovieServerID]['api_url'] . '&action=getFile&filename=' . urlencode($rMovieSource[2]); + } else { + $rMoviePath = $rMovieSource[2]; + } + $rProtocol = null; + } else { + if (substr($rStreamSource, 0, 1) == '/') { + $rMovieServerID = SERVER_ID; + $rMoviePath = $rStreamSource; + $rProtocol = null; + } else { + $rProtocol = substr($rStreamSource, 0, strpos($rStreamSource, '://')); + $rMoviePath = str_replace(' ', '%20', $rStreamSource); + $rFetchOptions = implode(' ', CoreUtilities::getArguments($rStream['stream_arguments'], $rProtocol, 'fetch')); + } + } + + if ((isset($rMovieServerID) && $rMovieServerID == SERVER_ID || file_exists($rMoviePath)) && $rStream['stream_info']['movie_symlink'] == 1) { + $rFFMPEG = 'ln -sfn ' . escapeshellarg($rMoviePath) . ' ' . VOD_PATH . intval($rStreamID) . '.' . escapeshellcmd(pathinfo($rMoviePath)['extension']) . ' >/dev/null 2>/dev/null & echo $! > ' . VOD_PATH . intval($rStreamID) . '_.pid'; + } else { + $rSubtitles = json_decode($rStream['stream_info']['movie_subtitles'], true); + $rSubtitlesImport = ''; + $rSubtitlesMetadata = ''; + if (!empty($rSubtitles) && !empty($rSubtitles['files']) && is_array($rSubtitles['files'])) { + for ($i = 0; $i < count($rSubtitles['files']); $i++) { + $rSubtitleFile = escapeshellarg($rSubtitles['files'][$i]); + $rInputCharset = escapeshellarg($rSubtitles['charset'][$i]); + if ($rSubtitles['location'] == SERVER_ID) { + $rSubtitlesImport .= '-sub_charenc ' . $rInputCharset . ' -i ' . $rSubtitleFile . ' '; + } else { + $rSubtitlesImport .= '-sub_charenc ' . $rInputCharset . ' -i "' . $rServers[$rSubtitles['location']]['api_url'] . '&action=getFile&filename=' . urlencode($rSubtitleFile) . '" '; + } + for ($i = 0; $i < count($rSubtitles['files']); $i++) { + $rSubtitlesMetadata .= '-map ' . ($i + 1) . ' -metadata:s:s:' . $i . ' title=' . escapeshellcmd($rSubtitles['names'][$i]) . ' -metadata:s:s:' . $i . ' language=' . escapeshellcmd($rSubtitles['names'][$i]) . ' '; + } + } + } + + $rReadNative = ($rStream['stream_info']['read_native'] == 1 ? '-re' : ''); + if ($rStream['stream_info']['enable_transcode'] == 1) { + if ($rStream['stream_info']['transcode_profile_id'] == -1) { + $rDecoded = json_decode($rStream['stream_info']['transcode_attributes'], true); + $rStream['stream_info']['transcode_attributes'] = array_merge(CoreUtilities::getArguments($rStream['stream_arguments'], $rProtocol, 'transcode'), (is_array($rDecoded) ? $rDecoded : array())); + } else { + $rDecoded = json_decode($rStream['stream_info']['profile_options'], true); + $rStream['stream_info']['transcode_attributes'] = (is_array($rDecoded) ? $rDecoded : array()); + } + } else { + $rStream['stream_info']['transcode_attributes'] = array(); + } + + $rLogoOptions = ''; + if (isset($rStream['stream_info']['transcode_attributes'][16]) && !$rLoopback) { + $rAttr = $rStream['stream_info']['transcode_attributes']; + $rLogoPath = $rAttr[16]['val']; + $rPos = (isset($rAttr[16]['pos']) && $rAttr[16]['pos'] !== '10:10') ? $rAttr[16]['pos'] : '10:main_h-overlay_h-10'; + + $rChain = array(); + $rBase = '[0:v]'; + $rVideoFilters = array(); + if (isset($rAttr[17])) { + $rVideoFilters[] = 'yadif'; + } + if (isset($rAttr[9]['val']) && strlen($rAttr[9]['val']) > 0) { + $rVideoFilters[] = 'scale=' . $rAttr[9]['val']; + } + + if (!empty($rVideoFilters)) { + $rChain[] = $rBase . implode(',', $rVideoFilters) . '[bg]'; + $rBase = '[bg]'; + } + + $rChain[] = '[1:v]scale=250:-1[logo]'; + $rChain[] = $rBase . '[logo]overlay=' . $rPos; + + $rLogoOptions = '-i ' . escapeshellarg($rLogoPath) . ' -filter_complex "' . implode('; ', $rChain) . '"'; + unset($rStream['stream_info']['transcode_attributes'][16]); + } + $rGPUOptions = (isset($rStream['stream_info']['transcode_attributes']['gpu']) ? $rStream['stream_info']['transcode_attributes']['gpu']['cmd'] : ''); + $rInputCodec = ''; + if (!empty($rGPUOptions)) { + $rFFProbeOutput = CoreUtilities::probeStream($rMoviePath); + if (in_array($rFFProbeOutput['codecs']['video']['codec_name'], array('h264', 'hevc', 'mjpeg', 'mpeg1', 'mpeg2', 'mpeg4', 'vc1', 'vp8', 'vp9'))) { + $rInputCodec = '-c:v ' . $rFFProbeOutput['codecs']['video']['codec_name'] . '_cuvid'; + } + } + $rFFMPEG = ((isset($rStream['stream_info']['transcode_attributes']['gpu']) ? $rFFMPEGGPU : $rFFMPEGCPU)) . ' -y -nostdin -hide_banner -loglevel ' . (($rSettings['ffmpeg_warnings'] ? 'warning' : 'error')) . ' -err_detect ignore_err {GPU} {FETCH_OPTIONS} -fflags +genpts -async 1 {READ_NATIVE} -i {STREAM_SOURCE} {LOGO} ' . $rSubtitlesImport; + $rMap = '-map 0 -copy_unknown '; + if (!empty($rStream['stream_info']['custom_map'])) { + $rMap = escapeshellcmd($rStream['stream_info']['custom_map']) . ' -copy_unknown '; + } else { + if ($rStream['stream_info']['remove_subtitles'] == 1) { + $rMap = '-map 0:a -map 0:v'; + } + } + if (!array_key_exists('-acodec', $rStream['stream_info']['transcode_attributes'])) { + $rStream['stream_info']['transcode_attributes']['-acodec'] = 'copy'; + } + if (!array_key_exists('-vcodec', $rStream['stream_info']['transcode_attributes'])) { + $rStream['stream_info']['transcode_attributes']['-vcodec'] = 'copy'; + } + if ($rStream['stream_info']['target_container'] == 'mp4') { + $rStream['stream_info']['transcode_attributes']['-scodec'] = 'mov_text'; + } elseif ($rStream['stream_info']['target_container'] == 'mkv') { + $rStream['stream_info']['transcode_attributes']['-scodec'] = 'srt'; + } else { + $rStream['stream_info']['transcode_attributes']['-scodec'] = 'copy'; + } + $rOutputs = array(); + $rOutputs[$rStream['stream_info']['target_container']] = '-movflags +faststart -dn ' . $rMap . ' -ignore_unknown ' . $rSubtitlesMetadata . ' ' . VOD_PATH . intval($rStreamID) . '.' . escapeshellcmd($rStream['stream_info']['target_container']); + foreach ($rOutputs as $rOutputCommand) { + $rFFMPEG .= implode(' ', CoreUtilities::parseTranscode($rStream['stream_info']['transcode_attributes'])) . ' '; + $rFFMPEG .= $rOutputCommand; + } + $rFFMPEG .= ' >/dev/null 2>' . VOD_PATH . intval($rStreamID) . '.errors & echo $! > ' . VOD_PATH . intval($rStreamID) . '_.pid'; + $rFFMPEG = str_replace(array('{GPU}', '{INPUT_CODEC}', '{LOGO}', '{FETCH_OPTIONS}', '{STREAM_SOURCE}', '{READ_NATIVE}'), array($rGPUOptions, $rInputCodec, $rLogoOptions, (empty($rFetchOptions) ? '' : $rFetchOptions), escapeshellarg($rMoviePath), (empty($rStream['stream_info']['custom_ffmpeg']) ? $rReadNative : '')), $rFFMPEG); + } + + shell_exec($rFFMPEG); + file_put_contents(VOD_PATH . $rStreamID . '_.ffmpeg', $rFFMPEG); + $rPID = intval(file_get_contents(VOD_PATH . $rStreamID . '_.pid')); + $db->query('UPDATE `streams_servers` SET `to_analyze` = 1,`stream_started` = ?,`stream_status` = 0,`pid` = ? WHERE `stream_id` = ? AND `server_id` = ?', time(), $rPID, $rStreamID, SERVER_ID); + CoreUtilities::updateStream($rStreamID); + return $rPID; + } + return false; + } + return false; + } + + public static function startLoopback($db, $rSettings, $rServers, $rStreamID) { + shell_exec('rm -f ' . STREAMS_PATH . intval($rStreamID) . '_*.ts'); + if (!file_exists(STREAMS_PATH . $rStreamID . '_.pid')) { + } else { + unlink(STREAMS_PATH . $rStreamID . '_.pid'); + } + $rStream = array(); + $db->query('SELECT * FROM `streams` WHERE direct_source = 0 AND id = ?', $rStreamID); + if ($db->num_rows() > 0) { + $rStream['stream_info'] = $db->get_row(); + $db->query('SELECT * FROM `streams_servers` WHERE stream_id = ? AND `server_id` = ?', $rStreamID, SERVER_ID); + if ($db->num_rows() > 0) { + $rStream['server_info'] = $db->get_row(); + if ($rStream['server_info']['parent_id'] != 0) { + shell_exec(PHP_BIN . ' ' . CLI_PATH . 'loopback.php ' . intval($rStreamID) . ' ' . intval($rStream['server_info']['parent_id']) . ' >/dev/null 2>/dev/null & echo $! > ' . STREAMS_PATH . intval($rStreamID) . '_.pid'); + $rPID = intval(file_get_contents(STREAMS_PATH . $rStreamID . '_.pid')); + $rLoopURL = (!is_null($rServers[SERVER_ID]['private_url_ip']) && !is_null($rServers[$rStream['server_info']['parent_id']]['private_url_ip']) ? $rServers[$rStream['server_info']['parent_id']]['private_url_ip'] : $rServers[$rStream['server_info']['parent_id']]['public_url_ip']); + $rCurrentSource = $rLoopURL . 'admin/live?stream=' . intval($rStreamID) . '&password=' . urlencode($rSettings['live_streaming_pass']) . '&extension=ts'; + $rKey = openssl_random_pseudo_bytes(16); + file_put_contents(STREAMS_PATH . $rStreamID . '_.key', $rKey); + $rIVSize = openssl_cipher_iv_length('AES-128-CBC'); + $rIV = openssl_random_pseudo_bytes($rIVSize); + file_put_contents(STREAMS_PATH . $rStreamID . '_.iv', $rIV); + $db->query('UPDATE `streams_servers` SET `delay_available_at` = ?,`to_analyze` = 0,`stream_started` = ?,`stream_info` = ?,`stream_status` = 2,`pid` = ?,`progress_info` = ?,`current_source` = ? WHERE `stream_id` = ? AND `server_id` = ?', null, time(), null, $rPID, json_encode(array()), $rCurrentSource, $rStreamID, SERVER_ID); + CoreUtilities::updateStream($rStreamID); + return array('main_pid' => $rPID, 'stream_source' => $rLoopURL . 'admin/live?stream=' . intval($rStreamID) . '&password=' . urlencode($rSettings['live_streaming_pass']) . '&extension=ts', 'delay_enabled' => false, 'parent_id' => 0, 'delay_start_at' => null, 'playlist' => STREAMS_PATH . $rStreamID . '_.m3u8', 'transcode' => false, 'offset' => 0); + } + return 0; + } + return false; + } + return false; + } + + public static function startLLOD($db, $rStreamID, $rStreamInfo, $rStreamArguments, $rForceSource = null) { + shell_exec('rm -f ' . STREAMS_PATH . intval($rStreamID) . '_*.ts'); + if (file_exists(STREAMS_PATH . $rStreamID . '_.pid')) { + unlink(STREAMS_PATH . $rStreamID . '_.pid'); + } + $rSources = ($rForceSource ? array($rForceSource) : json_decode($rStreamInfo['stream_source'], true)); + $rArgumentMap = array(); + foreach ($rStreamArguments as $rStreamArgument) { + $rArgumentMap[$rStreamArgument['argument_key']] = array('value' => $rStreamArgument['value'], 'argument_default_value' => $rStreamArgument['argument_default_value']); + } + shell_exec(PHP_BIN . ' ' . CLI_PATH . 'llod.php ' . intval($rStreamID) . ' "' . base64_encode(json_encode($rSources)) . '" "' . base64_encode(json_encode($rArgumentMap)) . '" >/dev/null 2>/dev/null & echo $! > ' . STREAMS_PATH . intval($rStreamID) . '_.pid'); + $rPID = intval(file_get_contents(STREAMS_PATH . $rStreamID . '_.pid')); + $rKey = openssl_random_pseudo_bytes(16); + file_put_contents(STREAMS_PATH . $rStreamID . '_.key', $rKey); + $rIVSize = openssl_cipher_iv_length('AES-128-CBC'); + $rIV = openssl_random_pseudo_bytes($rIVSize); + file_put_contents(STREAMS_PATH . $rStreamID . '_.iv', $rIV); + $db->query('UPDATE `streams_servers` SET `delay_available_at` = ?,`to_analyze` = 0,`stream_started` = ?,`stream_info` = ?,`stream_status` = 2,`pid` = ?,`progress_info` = ?,`current_source` = ? WHERE `stream_id` = ? AND `server_id` = ?', null, time(), null, $rPID, json_encode(array()), $rSources[0], $rStreamID, SERVER_ID); + CoreUtilities::updateStream($rStreamID); + return array('main_pid' => $rPID, 'stream_source' => $rSources[0], 'delay_enabled' => false, 'parent_id' => 0, 'delay_start_at' => null, 'playlist' => STREAMS_PATH . $rStreamID . '_.m3u8', 'transcode' => false, 'offset' => 0); + } + + public static function startStream($db, $rSettings, $rServers, $rSegmentSettings, $rFFMPEGCPU, $rFFMPEGGPU, $rFFPROBE, $rStreamID, $rFromCache = false, $rForceSource = null, $rLLOD = false, $rStartPos = 0) { + if (file_exists(STREAMS_PATH . $rStreamID . '_.pid')) { + unlink(STREAMS_PATH . $rStreamID . '_.pid'); + } + + $rStream = array(); + $db->query('SELECT * FROM `streams` t1 INNER JOIN `streams_types` t2 ON t2.type_id = t1.type AND t2.live = 1 LEFT JOIN `profiles` t4 ON t1.transcode_profile_id = t4.profile_id WHERE t1.direct_source = 0 AND t1.id = ?', $rStreamID); + + if ($db->num_rows() > 0) { + $rStream['stream_info'] = $db->get_row(); + $db->query('SELECT * FROM `streams_servers` WHERE stream_id = ? AND `server_id` = ?', $rStreamID, SERVER_ID); + + if ($db->num_rows() > 0) { + $rStream['server_info'] = $db->get_row(); + $db->query('SELECT t1.*, t2.* FROM `streams_options` t1, `streams_arguments` t2 WHERE t1.stream_id = ? AND t1.argument_id = t2.id', $rStreamID); + $rStream['stream_arguments'] = $db->get_rows(); + + if ($rStream['server_info']['on_demand'] == 1) { + $rProbesize = intval($rStream['stream_info']['probesize_ondemand']); + $rAnalyseDuration = '10000000'; + } else { + $rAnalyseDuration = abs(intval($rSettings['stream_max_analyze'])); + $rProbesize = abs(intval($rSettings['probesize'])); + } + + $rTimeout = intval($rAnalyseDuration / 1000000) + $rSettings['probe_extra_wait']; + $rFFProbee = 'timeout ' . $rTimeout . ' ' . $rFFPROBE . ' {FETCH_OPTIONS} -probesize ' . $rProbesize . ' -analyzeduration ' . $rAnalyseDuration . ' {CONCAT} -i {STREAM_SOURCE} -v quiet -print_format json -show_streams -show_format'; + $rFetchOptions = array(); + $rLoopback = false; + $rOffset = 0; + + if (!$rStream['server_info']['parent_id']) { + if ($rStream['stream_info']['type_key'] == 'created_live') { + $rSources = array(CREATED_PATH . $rStreamID . '_.list'); + + if ($rStartPos > 0) { + $rCCOutput = array(); + $rCCDuration = array(); + $rCCInfo = json_decode($rStream['server_info']['cc_info'], true); + + foreach ($rCCInfo as $rItem) { + $rCCDuration[$rItem['path']] = intval(explode('.', $rItem['seconds'])[0]); + } + $rTimer = 0; + $rValid = true; + + foreach (explode("\n", file_get_contents(CREATED_PATH . $rStreamID . '_.list')) as $rItem) { + list($rPath) = explode("'", explode("file '", $rItem)[1]); + + if ($rPath) { + if ($rCCDuration[$rPath]) { + $rDuration = $rCCDuration[$rPath]; + + if ($rTimer <= $rStartPos && $rStartPos < $rTimer + $rDuration) { + $rOffset = $rTimer; + $rCCOutput[] = $rPath; + } else { + if ($rStartPos < $rTimer + $rDuration) { + $rCCOutput[] = $rPath; + } + } + + $rTimer += $rDuration; + } else { + $rValid = false; + } + } + } + + if ($rValid) { + $rSources = array(CREATED_PATH . $rStreamID . '_.tlist'); + $rTList = ''; + + foreach ($rCCOutput as $rItem) { + $rTList .= "file '" . $rItem . "'" . "\n"; + } + file_put_contents(CREATED_PATH . $rStreamID . '_.tlist', $rTList); + } + } + } else { + $rSources = json_decode($rStream['stream_info']['stream_source'], true); + } + + if (count($rSources) > 0) { + if (!empty($rForceSource)) { + $rSources = array($rForceSource); + } else { + if ($rSettings['priority_backup'] != 1) { + if (!empty($rStream['server_info']['current_source'])) { + $k = array_search($rStream['server_info']['current_source'], $rSources); + + if ($k !== false) { + $i = 0; + + while ($i <= $k) { + $rTemp = $rSources[$i]; + unset($rSources[$i]); + array_push($rSources, $rTemp); + $i++; + } + $rSources = array_values($rSources); + } + } + } + } + } + } else { + $rLoopback = true; + + if ($rStream['server_info']['on_demand']) { + $rLLOD = true; + } + + $rLoopURL = (!is_null($rServers[SERVER_ID]['private_url_ip']) && !is_null($rServers[$rStream['server_info']['parent_id']]['private_url_ip']) ? $rServers[$rStream['server_info']['parent_id']]['private_url_ip'] : $rServers[$rStream['server_info']['parent_id']]['public_url_ip']); + $rSources = array($rLoopURL . 'admin/live?stream=' . intval($rStreamID) . '&password=' . urlencode($rSettings['live_streaming_pass']) . '&extension=ts'); + } + + if ($rStream['stream_info']['type_key'] == 'created_live' && file_exists(CREATED_PATH . $rStreamID . '_.info')) { + $db->query('UPDATE `streams_servers` SET `cc_info` = ? WHERE `server_id` = ? AND `stream_id` = ?;', file_get_contents(CREATED_PATH . $rStreamID . '_.info'), SERVER_ID, $rStreamID); + } + + if (!$rFromCache) { + self::deleteCache($rSources); + } + + foreach ($rSources as $rSource) { + $rProcessed = false; + $rRealSource = $rSource; + $rStreamSource = CoreUtilities::parseStreamURL($rSource); + echo 'Checking source: ' . $rSource . "\n"; + $rURLInfo = parse_url($rStreamSource); + $rIsXC_VM = ($rLoopback ? true : CoreUtilities::detectXC_VM($rStreamSource)); + + if ($rIsXC_VM && !$rLoopback && $rSettings['send_xc_vm_header']) { + foreach (array_keys($rStream['stream_arguments']) as $rID) { + if ($rStream['stream_arguments'][$rID]['argument_key'] == 'headers') { + $rStream['stream_arguments'][$rID]['value'] .= "\r\n" . 'X-XC_VM-Detect:1'; + $rProcessed = true; + } + } + + if (!$rProcessed) { + $rStream['stream_arguments'][] = array('value' => 'X-XC_VM-Detect:1', 'argument_key' => 'headers', 'argument_cat' => 'fetch', 'argument_wprotocol' => 'http', 'argument_type' => 'text', 'argument_cmd' => "-headers '%s" . "\r\n" . "'"); + } + } + + $rProbeArguments = $rStream['stream_arguments']; + + if ($rIsXC_VM && $rStream['server_info']['on_demand'] == 1 && $rSettings['request_prebuffer'] == 1) { + foreach (array_keys($rStream['stream_arguments']) as $rID) { + if ($rStream['stream_arguments'][$rID]['argument_key'] == 'headers') { + $rStream['stream_arguments'][$rID]['value'] .= "\r\n" . 'X-XC_VM-Prebuffer:1'; + $rProcessed = true; + } + } + + if (!$rProcessed) { + $rStream['stream_arguments'][] = array('value' => 'X-XC_VM-Prebuffer:1', 'argument_key' => 'headers', 'argument_cat' => 'fetch', 'argument_wprotocol' => 'http', 'argument_type' => 'text', 'argument_cmd' => "-headers '%s" . "\r\n" . "'"); + } + } + + foreach (array_keys($rProbeArguments) as $rID) { + if ($rProbeArguments[$rID]['argument_key'] == 'headers') { + $rProbeArguments[$rID]['value'] .= "\r\n" . 'X-XC_VM-Prebuffer:1'; + $rProcessed = true; + } + } + + if (!$rProcessed) { + $rProbeArguments[] = array('value' => 'X-XC_VM-Prebuffer:1', 'argument_key' => 'headers', 'argument_cat' => 'fetch', 'argument_wprotocol' => 'http', 'argument_type' => 'text', 'argument_cmd' => "-headers '%s" . "\r\n" . "'"); + } + + $rProtocol = strtolower(substr($rStreamSource, 0, strpos($rStreamSource, '://'))); + $rProbeOptions = implode(' ', CoreUtilities::getArguments($rProbeArguments, $rProtocol, 'fetch')); + $rFetchOptions = implode(' ', CoreUtilities::getArguments($rStream['stream_arguments'], $rProtocol, 'fetch')); + + $rSkipFFProbe = false; + foreach ($rStream['stream_arguments'] as $rArg) { + if ($rArg['argument_key'] == 'skip_ffprobe' && $rArg['value'] == 1) { + $rSkipFFProbe = true; + break; + } + } + + if ($rSkipFFProbe) { + $rFFProbeOutput = array( + 'codecs' => array( + 'video' => array('codec_name' => 'h264', 'codec_type' => 'video', 'height' => 1080), + 'audio' => array('codec_name' => 'aac', 'codec_type' => 'audio') + ), + 'container' => 'mpegts' + ); + error_log('[XC_VM] Stream ' . $rStreamID . ': FFProbe skipped'); + echo 'Got stream information via skip_ffprobe (assumed h264/aac)' . "\n"; + + if (empty($rSource)) { + $rSource = is_array($rSources) && count($rSources) > 0 ? $rSources[0] : $rStreamSource; + } + break; + } + + if ($rFromCache && file_exists(CACHE_TMP_PATH . md5($rSource)) && time() - filemtime(CACHE_TMP_PATH . md5($rSource)) <= 300) { + $rFFProbeOutput = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . md5($rStreamSource))); + + if ($rFFProbeOutput && (isset($rFFProbeOutput['streams']) || isset($rFFProbeOutput['codecs']))) { + echo 'Got stream information via cache' . "\n"; + + break; + } + } else { + if ($rFromCache && file_exists(CACHE_TMP_PATH . md5($rSource))) { + $rFromCache = false; + } + } + + if (!($rStream['server_info']['on_demand'] && $rLLOD)) { + if ($rIsXC_VM && $rSettings['api_probe']) { + $rProbeURL = $rURLInfo['scheme'] . '://' . $rURLInfo['host'] . ':' . $rURLInfo['port'] . '/probe/' . base64_encode($rURLInfo['path']); + $rFFProbeOutput = json_decode(CoreUtilities::getURL($rProbeURL), true); + + if ($rFFProbeOutput && isset($rFFProbeOutput['codecs'])) { + echo 'Got stream information via API' . "\n"; + + break; + } + } + + $rProbeCmd = str_replace(array('{FETCH_OPTIONS}', '{CONCAT}', '{STREAM_SOURCE}'), array($rProbeOptions, ($rStream['stream_info']['type_key'] == 'created_live' && !$rStream['server_info']['parent_id'] ? '-safe 0 -f concat' : ''), escapeshellarg($rStreamSource)), $rFFProbee); + $rFFProbeOutput = json_decode(shell_exec($rProbeCmd), true); + + if ($rFFProbeOutput && isset($rFFProbeOutput['streams'])) { + echo 'Got stream information via ffprobe' . "\n"; + + break; + } + } + } + if (!($rStream['server_info']['on_demand'] && $rLLOD)) { + if (!isset($rFFProbeOutput['codecs'])) { + $rFFProbeOutput = CoreUtilities::parseFFProbe($rFFProbeOutput); + } + + if (empty($rFFProbeOutput)) { + $db->query("UPDATE `streams_servers` SET `progress_info` = '',`to_analyze` = 0,`pid` = -1,`stream_status` = 1 WHERE `server_id` = ? AND `stream_id` = ?", SERVER_ID, $rStreamID); + + return 0; + } + + if (!$rFromCache) { + file_put_contents(CACHE_TMP_PATH . md5($rSource), igbinary_serialize($rFFProbeOutput)); + } + } + + $externalPushJson = $rStream['stream_info']['external_push'] ?? '[]'; + $rExternalPush = json_decode($externalPushJson, true); + $rProgressURL = 'http://127.0.0.1:' . intval($rServers[SERVER_ID]['http_broadcast_port']) . '/progress?stream_id=' . intval($rStreamID); + + if (empty($rStream['stream_info']['custom_ffmpeg'])) { + if ($rLoopback) { + $rOptions = '{FETCH_OPTIONS}'; + } else { + $rOptions = '{GPU} {FETCH_OPTIONS}'; + } + + if ($rStream['stream_info']['stream_all'] == 1) { + $rMap = '-map 0 -copy_unknown '; + } else { + if (!empty($rStream['stream_info']['custom_map'])) { + $rMap = escapeshellcmd($rStream['stream_info']['custom_map']) . ' -copy_unknown '; + } else { + if ($rStream['stream_info']['type_key'] == 'radio_streams') { + $rMap = '-map 0:a? '; + } else { + $rMap = ''; + } + } + } + + if (($rStream['stream_info']['gen_timestamps'] == 1 || empty($rProtocol)) && $rStream['stream_info']['type_key'] != 'created_live') { + $rGenPTS = '-fflags +genpts -async 1'; + } else { + if (is_array($rFFProbeOutput) && isset($rFFProbeOutput['codecs']['audio']['codec_name']) && in_array($rFFProbeOutput['codecs']['audio']['codec_name'], array('ac3', 'eac3')) && $rSettings['dts_legacy_ffmpeg']) { + $rFFMPEGCPU = FFMPEG_BIN_40; + $rFFPROBE = FFPROBE_BIN_40; + } + + $rNoFix = ($rFFMPEGCPU == FFMPEG_BIN_40 ? '-nofix_dts' : ''); + $rGenPTS = $rNoFix . ' -start_at_zero -copyts -vsync 0 -correct_ts_overflow 0 -avoid_negative_ts disabled -max_interleave_delta 0'; + } + + $container = (isset($rFFProbeOutput) && is_array($rFFProbeOutput)) ? ($rFFProbeOutput['container'] ?? null) : null; + if (empty($rStream['server_info']['parent_id']) && (($rStream['stream_info']['read_native'] == 1) || ($container && stristr($container, 'hls') && $rSettings['read_native_hls']) || empty($rProtocol) || ($container && stristr($container, 'mp4')) || ($container && stristr($container, 'matroska')))) { + $rReadNative = '-re'; + } else { + $rReadNative = ''; + } + + if (!$rStream['server_info']['parent_id'] && $rStream['stream_info']['enable_transcode'] == 1 && $rStream['stream_info']['type_key'] != 'created_live') { + if ($rStream['stream_info']['transcode_profile_id'] == -1) { + $rStream['stream_info']['transcode_attributes'] = array_merge(CoreUtilities::getArguments($rStream['stream_arguments'], $rProtocol, 'transcode'), json_decode($rStream['stream_info']['transcode_attributes'], true)); + } else { + $rStream['stream_info']['transcode_attributes'] = json_decode($rStream['stream_info']['profile_options'], true); + } + } else { + $rStream['stream_info']['transcode_attributes'] = array(); + } + + $rFFMPEG = ((isset($rStream['stream_info']['transcode_attributes']['gpu']) ? $rFFMPEGGPU : $rFFMPEGCPU)) . ' -y -nostdin -hide_banner -loglevel ' . (($rSettings['ffmpeg_warnings'] ? 'warning' : 'error')) . ' -err_detect ignore_err ' . $rOptions . ' {GEN_PTS} {READ_NATIVE} -probesize ' . $rProbesize . ' -analyzeduration ' . $rAnalyseDuration . ' -progress "' . $rProgressURL . '" {CONCAT} -i {STREAM_SOURCE} {LOGO} '; + + if (!array_key_exists('-acodec', $rStream['stream_info']['transcode_attributes'])) { + $rStream['stream_info']['transcode_attributes']['-acodec'] = 'copy'; + } + + if (!array_key_exists('-vcodec', $rStream['stream_info']['transcode_attributes'])) { + $rStream['stream_info']['transcode_attributes']['-vcodec'] = 'copy'; + } + + if (!array_key_exists('-scodec', $rStream['stream_info']['transcode_attributes'])) { + $rStream['stream_info']['transcode_attributes']['-sn'] = ''; + } + } else { + $rStream['stream_info']['transcode_attributes'] = array(); + $rFFMPEG = ((stripos($rStream['stream_info']['custom_ffmpeg'], 'nvenc') !== false ? $rFFMPEGGPU : $rFFMPEGCPU)) . ' -y -nostdin -hide_banner -loglevel ' . (($rSettings['ffmpeg_warnings'] ? 'warning' : 'error')) . ' -progress "' . $rProgressURL . '" ' . $rStream['stream_info']['custom_ffmpeg']; + } + + $rLLODOptions = ($rLLOD && !$rLoopback ? '-fflags nobuffer -flags low_delay -strict experimental' : ''); + $rOutputs = array(); + + if ($rLoopback) { + $rOptions = '{MAP}'; + $rFLVOptions = '{MAP}'; + $rMap = '-map 0 -copy_unknown '; + } else { + $rOptions = '{MAP} {LLOD}'; + $rFLVOptions = '{MAP} {AAC_FILTER}'; + } + + $rKeyFrames = ($rSettings['ignore_keyframes'] ? '+split_by_time' : ''); + $rOutputs['mpegts'][] = $rOptions . ' -individual_header_trailer 0 -f hls -hls_time ' . intval($rSegmentSettings['seg_time']) . ' -hls_list_size ' . intval($rSegmentSettings['seg_list_size']) . ' -hls_delete_threshold ' . intval($rSegmentSettings['seg_delete_threshold']) . ' -hls_flags delete_segments+discont_start+omit_endlist' . $rKeyFrames . ' -hls_segment_type mpegts -hls_segment_filename "' . STREAMS_PATH . intval($rStreamID) . '_%d.ts" "' . STREAMS_PATH . intval($rStreamID) . '_.m3u8" '; + + if ($rStream['stream_info']['rtmp_output'] == 1) { + $rOutputs['flv'][] = $rFLVOptions . ' -f flv -flvflags no_duration_filesize rtmp://127.0.0.1:' . intval($rServers[$rStream['server_info']['server_id']]['rtmp_port']) . '/live/' . intval($rStreamID) . '?password=' . urlencode($rSettings['live_streaming_pass']) . ' '; + } + + if (!empty($rExternalPush[SERVER_ID])) { + foreach ($rExternalPush[SERVER_ID] as $rPushURL) { + $rOutputs['flv'][] = $rFLVOptions . ' -f flv -flvflags no_duration_filesize ' . escapeshellarg($rPushURL) . ' '; + } + } + + $rLogoOptions = ''; + if (isset($rStream['stream_info']['transcode_attributes'][16]) && !$rLoopback) { + $rAttr = $rStream['stream_info']['transcode_attributes']; + $rLogoPath = $rAttr[16]['val']; + $rPos = (isset($rAttr[16]['pos']) && $rAttr[16]['pos'] !== '10:10') ? $rAttr[16]['pos'] : '10:main_h-overlay_h-10'; + + $rChain = array(); + $rBase = '[0:v]'; + $rVideoFilters = array(); + if (isset($rAttr[17])) { + $rVideoFilters[] = 'yadif'; + } + if (isset($rAttr[9]['val']) && strlen($rAttr[9]['val']) > 0) { + $rVideoFilters[] = 'scale=' . $rAttr[9]['val']; + } + + if (!empty($rVideoFilters)) { + $rChain[] = $rBase . implode(',', $rVideoFilters) . '[bg]'; + $rBase = '[bg]'; + } + + $rChain[] = '[1:v]scale=250:-1[logo]'; + $rChain[] = $rBase . '[logo]overlay=' . $rPos; + + $rLogoOptions = '-i ' . escapeshellarg($rLogoPath) . ' -filter_complex "' . implode('; ', $rChain) . '"'; + unset($rStream['stream_info']['transcode_attributes'][16]); + } + + $rGPUOptions = (isset($rStream['stream_info']['transcode_attributes']['gpu']) ? $rStream['stream_info']['transcode_attributes']['gpu']['cmd'] : ''); + $rInputCodec = ''; + + $supportedCodecs = ['h264', 'hevc', 'mjpeg', 'mpeg1', 'mpeg2', 'mpeg4', 'vc1', 'vp8', 'vp9']; + $videoCodec = null; + if (isset($rFFProbeOutput) && is_array($rFFProbeOutput)) { + $videoCodec = $rFFProbeOutput['codecs']['video']['codec_name'] ?? null; + } + + if (!empty($rGPUOptions) && in_array($videoCodec, $supportedCodecs)) { + $rInputCodec = '-c:v ' . $rFFProbeOutput['codecs']['video']['codec_name'] . '_cuvid'; + } + + if (0 >= $rStream['stream_info']['delay_minutes'] || $rStream['server_info']['parent_id']) { + foreach ($rOutputs as $rOutputCommands) { + foreach ($rOutputCommands as $rOutputCommand) { + if (isset($rStream['stream_info']['transcode_attributes']['gpu'])) { + $rFFMPEG .= '-gpu ' . intval($rStream['stream_info']['transcode_attributes']['gpu']['device']) . ' '; + } + + $rFFMPEG .= implode(' ', CoreUtilities::parseTranscode($rStream['stream_info']['transcode_attributes'])) . ' '; + $rFFMPEG .= $rOutputCommand; + } + } + } else { + $rSegmentStart = 0; + $m3u8File = DELAY_PATH . $rStreamID . '_.m3u8'; + $oldM3u8File = DELAY_PATH . intval($rStreamID) . '_.m3u8_old'; + + if (file_exists($m3u8File)) { + $rFile = file($m3u8File, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + + if (!is_array($rFile) || count($rFile) < 2) { + return; + } + + $lastLine = $rFile[count($rFile) - 1]; + $prevLine = $rFile[count($rFile) - 2]; + + if (stristr($lastLine, $rStreamID . '_')) { + if (preg_match('/_(.*?)\.ts/', $lastLine, $rMatches)) { + $rSegmentStart = intval($rMatches[1]) + 1; + } + } else { + if (preg_match('/_(.*?)\.ts/', $prevLine, $rMatches)) { + $rSegmentStart = intval($rMatches[1]) + 1; + } + } + + if (file_exists($oldM3u8File)) { + file_put_contents($oldM3u8File, file_get_contents($oldM3u8File) . file_get_contents($m3u8File)); + shell_exec("sed -i '/EXTINF\\|.ts/!d' " . escapeshellarg($oldM3u8File)); + } else { + copy($m3u8File, $oldM3u8File); + } + } + + $rFFMPEG .= implode(' ', CoreUtilities::parseTranscode($rStream['stream_info']['transcode_attributes'])) . ' '; + $rFFMPEG .= '{MAP} -individual_header_trailer 0 -f hls -hls_time ' . intval($rSegmentSettings['seg_time']) . ' -hls_list_size ' . intval($rStream['stream_info']['delay_minutes']) * 6 . ' -hls_delete_threshold 4 -start_number ' . $rSegmentStart . ' -hls_flags delete_segments+discont_start+omit_endlist -hls_segment_type mpegts -hls_segment_filename "' . DELAY_PATH . intval($rStreamID) . '_%d.ts" "' . DELAY_PATH . intval($rStreamID) . '_.m3u8" '; + + $rSleepTime = $rStream['stream_info']['delay_minutes'] * 60; + + if ($rSegmentStart > 0) { + $rSleepTime -= ($rSegmentStart - 1) * 10; + + if ($rSleepTime > 0) { + } else { + $rSleepTime = 0; + } + } + } + + $rFFMPEG .= ' >/dev/null 2>>' . STREAMS_PATH . intval($rStreamID) . '.errors & echo $! > ' . STREAMS_PATH . intval($rStreamID) . '_.pid'; + + $ffprobeContainer = (isset($rFFProbeOutput['container']) && is_string($rFFProbeOutput['container'])) ? $rFFProbeOutput['container'] : ''; + + $audioCodec = (isset($rFFProbeOutput['codecs']['audio']['codec_name']) && is_array($rFFProbeOutput['codecs']['audio'])) ? $rFFProbeOutput['codecs']['audio']['codec_name'] : ''; + + $rFFMPEG = str_replace( + ['{FETCH_OPTIONS}', '{GEN_PTS}', '{STREAM_SOURCE}', '{MAP}', '{READ_NATIVE}', '{CONCAT}', '{AAC_FILTER}', '{GPU}', '{INPUT_CODEC}', '{LOGO}', '{LLOD}'], + [ + empty($rStream['stream_info']['custom_ffmpeg']) ? $rFetchOptions : '', + empty($rStream['stream_info']['custom_ffmpeg']) ? $rGenPTS : '', + escapeshellarg($rStreamSource), + empty($rStream['stream_info']['custom_ffmpeg']) ? $rMap : '', + empty($rStream['stream_info']['custom_ffmpeg']) ? $rReadNative : '', + ($rStream['stream_info']['type_key'] == 'created_live' && empty($rStream['server_info']['parent_id']) ? '-safe 0 -f concat' : ''), + (!stristr($ffprobeContainer, 'flv') && $audioCodec === 'aac' && ($rStream['stream_info']['transcode_attributes']['-acodec'] ?? '') === 'copy' ? '-bsf:a aac_adtstoasc' : ''), + $rGPUOptions, + $rInputCodec, + $rLogoOptions, + $rLLODOptions + ], + $rFFMPEG + ); + + shell_exec($rFFMPEG); + file_put_contents(STREAMS_PATH . $rStreamID . '_.ffmpeg', $rFFMPEG); + $rKey = openssl_random_pseudo_bytes(16); + file_put_contents(STREAMS_PATH . $rStreamID . '_.key', $rKey); + $rIVSize = openssl_cipher_iv_length('AES-128-CBC'); + $rIV = openssl_random_pseudo_bytes($rIVSize); + file_put_contents(STREAMS_PATH . $rStreamID . '_.iv', $rIV); + $rPID = intval(file_get_contents(STREAMS_PATH . $rStreamID . '_.pid')); + + if ($rStream['stream_info']['tv_archive_server_id'] == SERVER_ID) { + shell_exec(PHP_BIN . ' ' . CLI_PATH . 'archive.php ' . intval($rStreamID) . ' >/dev/null 2>/dev/null & echo $!'); + } + + if ($rStream['stream_info']['vframes_server_id'] == SERVER_ID) { + self::startThumbnail($rStreamID); + } + + $rDelayEnabled = 0 < $rStream['stream_info']['delay_minutes'] && !$rStream['server_info']['parent_id']; + $rDelayStartAt = ($rDelayEnabled ? time() + $rSleepTime : 0); + + if ($rStream['stream_info']['enable_transcode']) { + $rFFProbeOutput = array(); + } + + $rCompatible = 0; + $rAudioCodec = $rVideoCodec = $rResolution = null; + + if (isset($rFFProbeOutput) && is_array($rFFProbeOutput) && isset($rFFProbeOutput['codecs']) && is_array($rFFProbeOutput['codecs'])) { + $rCompatible = intval(CoreUtilities::checkCompatibility($rFFProbeOutput)); + $rAudioCodec = ($rFFProbeOutput['codecs']['audio']['codec_name'] ?: null); + $rVideoCodec = ($rFFProbeOutput['codecs']['video']['codec_name'] ?: null); + $rResolution = ($rFFProbeOutput['codecs']['video']['height'] ?: null); + + if ($rResolution) { + $rResolution = CoreUtilities::getNearest(array(240, 360, 480, 576, 720, 1080, 1440, 2160), $rResolution); + } + } + + $rFFProbeOutputSafe = isset($rFFProbeOutput) && is_array($rFFProbeOutput) ? $rFFProbeOutput : []; + $db->query('UPDATE `streams_servers` SET `delay_available_at` = ?,`to_analyze` = 0,`stream_started` = ?,`stream_info` = ?,`audio_codec` = ?, `video_codec` = ?, `resolution` = ?,`compatible` = ?,`stream_status` = 2,`pid` = ?,`progress_info` = ?,`current_source` = ? WHERE `stream_id` = ? AND `server_id` = ?', $rDelayStartAt, time(), json_encode($rFFProbeOutputSafe), $rAudioCodec, $rVideoCodec, $rResolution, $rCompatible, $rPID, json_encode(array()), $rSource, $rStreamID, SERVER_ID); + CoreUtilities::updateStream($rStreamID); + $rPlaylist = (!$rDelayEnabled ? STREAMS_PATH . $rStreamID . '_.m3u8' : DELAY_PATH . $rStreamID . '_.m3u8'); + + return array('main_pid' => $rPID, 'stream_source' => $rRealSource, 'delay_enabled' => $rDelayEnabled, 'parent_id' => $rStream['server_info']['parent_id'], 'delay_start_at' => $rDelayStartAt, 'playlist' => $rPlaylist, 'transcode' => $rStream['stream_info']['enable_transcode'], 'offset' => $rOffset); + } else { + return false; + } + } else { + return false; + } + } +} diff --git a/src/domain/Stream/StreamRepository.php b/src/domain/Stream/StreamRepository.php new file mode 100644 index 00000000..361c24e3 --- /dev/null +++ b/src/domain/Stream/StreamRepository.php @@ -0,0 +1,153 @@ +query('SELECT * FROM (SELECT MAX(`date`) AS `date`, `error` FROM `streams_errors` WHERE `stream_id` = ? GROUP BY `error`) AS `output` ORDER BY `date` DESC LIMIT ' . intval($rAmount) . ';', $rStreamID); + + foreach ($db->get_rows() as $rRow) { + $rReturn[] = $rRow; + } + + return $rReturn; + } + + public static function getById($db, $rID) { + $db->query('SELECT * FROM `streams` WHERE `id` = ?;', $rID); + + if ($db->num_rows() != 1) { + } else { + return $db->get_row(); + } + } + + public static function getStats($db, $rStreamID) { + $rReturn = array(); + $db->query('SELECT * FROM `streams_stats` WHERE `stream_id` = ?;', $rStreamID); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + $rReturn[$rRow['type']] = $rRow; + } + } + + foreach (array('today', 'week', 'month', 'all') as $rType) { + if (isset($rReturn[$rType])) { + } else { + $rReturn[$rType] = array('rank' => 0, 'users' => 0, 'connections' => 0, 'time' => 0); + } + } + + return $rReturn; + } + + public static function getPIDs($db, $rServerID, $rSettings) { + $rReturn = array(); + $db->query('SELECT `streams`.`id`, `streams`.`stream_display_name`, `streams`.`type`, `streams_servers`.`pid`, `streams_servers`.`monitor_pid`, `streams_servers`.`delay_pid` FROM `streams_servers` LEFT JOIN `streams` ON `streams`.`id` = `streams_servers`.`stream_id` WHERE `streams_servers`.`server_id` = ?;', $rServerID); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + foreach (array('pid', 'monitor_pid', 'delay_pid') as $rPIDType) { + if (!$rRow[$rPIDType]) { + } else { + $rReturn[$rRow[$rPIDType]] = array('id' => $rRow['id'], 'title' => $rRow['stream_display_name'], 'type' => $rRow['type'], 'pid_type' => $rPIDType); + } + } + } + } + + $db->query('SELECT `id`, `stream_display_name`, `type`, `tv_archive_pid` FROM `streams` WHERE `tv_archive_server_id` = ?;', $rServerID); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + $rReturn[$rRow['tv_archive_pid']] = array('id' => $rRow['id'], 'title' => $rRow['stream_display_name'], 'type' => $rRow['type'], 'pid_type' => 'timeshift'); + } + } + + $db->query('SELECT `id`, `stream_display_name`, `type`, `vframes_pid` FROM `streams` WHERE `vframes_server_id` = ?;', $rServerID); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + $rReturn[$rRow['vframes_pid']] = array('id' => $rRow['id'], 'title' => $rRow['stream_display_name'], 'type' => $rRow['type'], 'pid_type' => 'vframes'); + } + } + + if ($rSettings['redis_handler']) { + $rStreamIDs = $rStreamMap = array(); + $rConnections = CoreUtilities::getRedisConnections(null, $rServerID, null, true, false, false); + + foreach ($rConnections as $rConnection) { + if (in_array($rConnection['stream_id'], $rStreamIDs)) { + } else { + $rStreamIDs[] = intval($rConnection['stream_id']); + } + } + + if (0 >= count($rStreamIDs)) { + } else { + $db->query('SELECT `id`, `type`, `stream_display_name` FROM `streams` WHERE `id` IN (' . implode(',', $rStreamIDs) . ');'); + + foreach ($db->get_rows() as $rRow) { + $rStreamMap[$rRow['id']] = array($rRow['stream_display_name'], $rRow['type']); + } + } + + foreach ($rConnections as $rRow) { + $rReturn[$rRow['pid']] = array('id' => $rRow['stream_id'], 'title' => $rStreamMap[$rRow['stream_id']][0], 'type' => $rStreamMap[$rRow['stream_id']][1], 'pid_type' => 'activity'); + } + } else { + $db->query('SELECT `streams`.`id`, `streams`.`stream_display_name`, `streams`.`type`, `lines_live`.`pid` FROM `lines_live` LEFT JOIN `streams` ON `streams`.`id` = `lines_live`.`stream_id` WHERE `lines_live`.`server_id` = ?;', $rServerID); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + $rReturn[$rRow['pid']] = array('id' => $rRow['id'], 'title' => $rRow['stream_display_name'], 'type' => $rRow['type'], 'pid_type' => 'activity'); + } + } + } + + return $rReturn; + } + + public static function getOptions($db, $rID) { + $rReturn = array(); + $db->query('SELECT * FROM `streams_options` WHERE `stream_id` = ?;', $rID); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + $rReturn[intval($rRow['argument_id'])] = $rRow; + } + } + + return $rReturn; + } + + public static function getSystemRows($db, $rID) { + $rReturn = array(); + $db->query('SELECT * FROM `streams_servers` WHERE `stream_id` = ?;', $rID); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + $rReturn[intval($rRow['server_id'])] = $rRow; + } + } + + return $rReturn; + } + + public static function getNextOrder($db) { + $db->query('SELECT MAX(`order`) AS `order` FROM `streams`;'); + + if ($db->num_rows() != 1) { + return 0; + } + + return intval($db->get_row()['order']) + 1; + } +} diff --git a/src/domain/Stream/StreamService.php b/src/domain/Stream/StreamService.php new file mode 100644 index 00000000..c34e1b4d --- /dev/null +++ b/src/domain/Stream/StreamService.php @@ -0,0 +1,818 @@ + array(), 'at' => $rData['time_to_restart']); + + foreach ($rData['days_to_restart'] as $rID => $rDay) { + $rTimeArray['days'][] = $rDay; + } + $rArray['auto_restart'] = $rTimeArray; + } else { + $rArray['auto_restart'] = ''; + } + + foreach (array('fps_restart', 'gen_timestamps', 'allow_record', 'rtmp_output', 'stream_all', 'direct_source', 'direct_proxy', 'read_native') as $rKey) { + if (isset($rData[$rKey])) { + $rArray[$rKey] = 1; + } else { + $rArray[$rKey] = 0; + } + } + + if (!$rArray['transcode_profile_id']) { + $rArray['transcode_profile_id'] = 0; + } + + if ($rArray['transcode_profile_id'] > 0) { + $rArray['enable_transcode'] = 1; + } + + if (isset($rData['restart_on_edit'])) { + $rRestart = true; + } else { + $rRestart = false; + } + + $rReview = false; + $rImportStreams = array(); + + if (isset($rData['review'])) { + $rReview = true; + + foreach ($rData['review'] as $rImportStream) { + if ($rImportStream['channel_id'] || !$rImportStream['tvg_id']) { + } else { + $rEPG = findEPG($rImportStream['tvg_id']); + + if (!isset($rEPG)) { + } else { + $rImportStream['epg_id'] = $rEPG['epg_id']; + $rImportStream['channel_id'] = $rEPG['channel_id']; + + if (empty($rEPG['epg_lang'])) { + } else { + $rImportStream['epg_lang'] = $rEPG['epg_lang']; + } + } + } + + $rImportStreams[] = $rImportStream; + } + } else { + if (isset($_FILES['m3u_file'])) { + if (hasPermissions('adv', 'import_streams')) { + if (!(empty($_FILES['m3u_file']['tmp_name']) || strtolower(pathinfo(explode('?', $_FILES['m3u_file']['name'])[0], PATHINFO_EXTENSION)) != 'm3u')) { + $rResults = parseM3U($_FILES['m3u_file']['tmp_name']); + + if (0 >= count($rResults)) { + } else { + $rEPGDatabase = $rSourceDatabase = $rStreamDatabase = array(); + $db->query('SELECT `id`, `stream_display_name`, `stream_source`, `channel_id` FROM `streams` WHERE `type` = 1;'); + + foreach ($db->get_rows() as $rRow) { + $rName = preg_replace('/[^A-Za-z0-9 ]/', '', strtolower($rRow['stream_display_name'])); + + if (empty($rName)) { + } else { + $rStreamDatabase[$rName] = $rRow['id']; + } + + $rEPGDatabase[$rRow['channel_id']] = $rRow['id']; + + foreach (json_decode($rRow['stream_source'], true) as $rSource) { + if (empty($rSource)) { + } else { + $rSourceDatabase[md5(preg_replace('(^https?://)', '', str_replace(' ', '%20', $rSource)))] = $rRow['id']; + } + } + } + $rEPGMatch = $rEPGScan = array(); + $i = 0; + + foreach ($rResults as $rResult) { + list($rTag) = $rResult->getExtTags(); + + if (!$rTag) { + } else { + if (!$rTag->getAttribute('tvg-id')) { + } else { + $rID = $rTag->getAttribute('tvg-id'); + $rEPGScan[$rID][] = $i; + } + } + + $i++; + } + + if (0 >= count($rEPGScan)) { + } else { + $db->query('SELECT `id`, `data` FROM `epg`;'); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + foreach (json_decode($rRow['data'], true) as $rChannelID => $rChannelData) { + if (!isset($rEPGScan[$rChannelID])) { + } else { + if (0 < count($rChannelData['langs'])) { + $rEPGLang = $rChannelData['langs'][0]; + } else { + $rEPGLang = ''; + } + + foreach ($rEPGScan[$rChannelID] as $i) { + $rEPGMatch[$i] = array('channel_id' => $rChannelID, 'epg_lang' => $rEPGLang, 'epg_id' => intval($rRow['id'])); + } + } + } + } + } + } + + $i = 0; + + foreach ($rResults as $rResult) { + list($rTag) = $rResult->getExtTags(); + + if (!$rTag) { + } else { + $rURL = $rResult->getPath(); + $rImportArray = array('stream_source' => array($rURL), 'stream_icon' => ($rTag->getAttribute('tvg-logo') ?: ''), 'stream_display_name' => ($rTag->getTitle() ?: ''), 'epg_id' => null, 'epg_lang' => null, 'channel_id' => null); + + if (!$rTag->getAttribute('tvg-id')) { + } else { + $rEPG = ($rEPGMatch[$i] ?: null); + + if (!isset($rEPG)) { + } else { + $rImportArray['epg_id'] = $rEPG['epg_id']; + $rImportArray['channel_id'] = $rEPG['channel_id']; + + if (empty($rEPG['epg_lang'])) { + } else { + $rImportArray['epg_lang'] = $rEPG['epg_lang']; + } + } + } + + $rBackupID = $rExistsID = null; + $rSourceID = md5(preg_replace('(^https?://)', '', str_replace(' ', '%20', $rURL))); + + if (!isset($rSourceDatabase[$rSourceID])) { + } else { + $rExistsID = $rSourceDatabase[$rSourceID]; + } + + $rName = preg_replace('/[^A-Za-z0-9 ]/', '', strtolower($rTag->getTitle())); + + if (!empty($rName) && isset($rStreamDatabase[$rName])) { + $rBackupID = $rStreamDatabase[$rName]; + } else { + if (empty($rImportArray['channel_id']) || !isset($rEPGDatabase[$rImportArray['channel_id']])) { + } else { + $rBackupID = $rEPGDatabase[$rImportArray['channel_id']]; + } + } + + if ($rBackupID && !$rExistsID && isset($rData['add_source_as_backup'])) { + $db->query('SELECT `stream_source` FROM `streams` WHERE `id` = ?;', $rBackupID); + + if (0 >= $db->num_rows()) { + } else { + $rSources = (json_decode($db->get_row()['stream_source'], true) ?: array()); + $rSources[] = $rURL; + $db->query('UPDATE `streams` SET `stream_source` = ? WHERE `id` = ?;', json_encode($rSources), $rBackupID); + $rImportStreams[] = array('update' => true, 'id' => $rBackupID); + } + } else { + if ($rExistsID && isset($rData['update_existing'])) { + $rImportArray['id'] = $rExistsID; + $rImportStreams[] = $rImportArray; + } else { + if ($rExistsID) { + } else { + $rImportStreams[] = $rImportArray; + } + } + } + } + + $i++; + } + } + } else { + return array('status' => STATUS_INVALID_FILE, 'data' => $rData); + } + } else { + exit(); + } + } else { + $rImportArray = array('stream_source' => array(), 'stream_icon' => $rArray['stream_icon'], 'stream_display_name' => $rArray['stream_display_name'], 'epg_id' => $rArray['epg_id'], 'epg_lang' => $rArray['epg_lang'], 'channel_id' => $rArray['channel_id']); + + if (isset($rData['stream_source'])) { + foreach ($rData['stream_source'] as $rID => $rURL) { + if (0 >= strlen($rURL)) { + } else { + $rImportArray['stream_source'][] = $rURL; + } + } + } + + $rImportStreams[] = $rImportArray; + } + } + + if (0 < count($rImportStreams)) { + $rBouquetCreate = array(); + $rCategoryCreate = array(); + + if ($rReview) { + } else { + foreach (json_decode($rData['bouquet_create_list'], true) as $rBouquet) { + $rPrepare = prepareArray(array('bouquet_name' => $rBouquet, 'bouquet_channels' => array(), 'bouquet_movies' => array(), 'bouquet_series' => array(), 'bouquet_radios' => array())); + $rQuery = 'INSERT INTO `bouquets`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if (!$db->query($rQuery, ...$rPrepare['data'])) { + } else { + $rBouquetID = $db->last_insert_id(); + $rBouquetCreate[$rBouquet] = $rBouquetID; + } + } + + foreach (json_decode($rData['category_create_list'], true) as $rCategory) { + $rPrepare = prepareArray(array('category_type' => 'live', 'category_name' => $rCategory, 'parent_id' => 0, 'cat_order' => 99, 'is_adult' => 0)); + $rQuery = 'INSERT INTO `streams_categories`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if (!$db->query($rQuery, ...$rPrepare['data'])) { + } else { + $rCategoryID = $db->last_insert_id(); + $rCategoryCreate[$rCategory] = $rCategoryID; + } + } + } + + foreach ($rImportStreams as $rImportStream) { + if (!$rImportStream['update']) { + $rImportArray = $rArray; + + if (!$rSettings['download_images']) { + } else { + $rImportStream['stream_icon'] = CoreUtilities::downloadImage($rImportStream['stream_icon'], 1); + } + + if ($rReview) { + $rImportArray['category_id'] = '[' . implode(',', array_map('intval', $rImportStream['category_id'])) . ']'; + $rBouquets = array_map('intval', $rImportStream['bouquets']); + unset($rImportStream['bouquets']); + } else { + $rBouquets = array(); + + foreach ($rData['bouquets'] as $rBouquet) { + if (isset($rBouquetCreate[$rBouquet])) { + $rBouquets[] = $rBouquetCreate[$rBouquet]; + } else { + if (!is_numeric($rBouquet)) { + } else { + $rBouquets[] = intval($rBouquet); + } + } + } + $rCategories = array(); + + foreach ($rData['category_id'] as $rCategory) { + if (isset($rCategoryCreate[$rCategory])) { + $rCategories[] = $rCategoryCreate[$rCategory]; + } else { + if (!is_numeric($rCategory)) { + } else { + $rCategories[] = intval($rCategory); + } + } + } + $rImportArray['category_id'] = '[' . implode(',', array_map('intval', $rCategories)) . ']'; + + if (isset($rData['adaptive_link']) && 0 < count($rData['adaptive_link'])) { + $rImportArray['adaptive_link'] = '[' . implode(',', array_map('intval', $rData['adaptive_link'])) . ']'; + } else { + $rImportArray['adaptive_link'] = null; + } + } + + foreach (array_keys($rImportStream) as $rKey) { + $rImportArray[$rKey] = $rImportStream[$rKey]; + } + + if (isset($rData['edit']) || isset($rImportStream['id'])) { + } else { + $rImportArray['order'] = getNextOrder(); + } + + $rImportArray['title_sync'] = ($rData['title_sync'] ?: null); + + if (!$rImportArray['title_sync']) { + } else { + list($rSyncID, $rSyncStream) = array_map('intval', explode('_', $rImportArray['title_sync'])); + $db->query('SELECT `stream_display_name` FROM `providers_streams` WHERE `provider_id` = ? AND `stream_id` = ?;', $rSyncID, $rSyncStream); + + if ($db->num_rows() != 1) { + } else { + $rImportArray['stream_display_name'] = $db->get_row()['stream_display_name']; + } + } + + $rPrepare = prepareArray($rImportArray); + $rQuery = 'REPLACE INTO `streams`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + $rStreamExists = array(); + + if (!(isset($rData['edit']) || isset($rImportStream['id']))) { + } else { + $db->query('SELECT `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` = ?;', $rInsertID); + + foreach ($db->get_rows() as $rRow) { + $rStreamExists[intval($rRow['server_id'])] = intval($rRow['server_stream_id']); + } + } + + $rStreamsAdded = array(); + $rServerTree = json_decode($rData['server_tree_data'], true); + + foreach ($rServerTree as $rServer) { + if ($rServer['parent'] != '#') { + $rServerID = intval($rServer['id']); + $rStreamsAdded[] = $rServerID; + $rOD = intval(in_array($rServerID, ($rData['on_demand'] ?: array()))); + + if ($rServer['parent'] == 'source') { + $rParent = null; + } else { + $rParent = intval($rServer['parent']); + } + + if (isset($rStreamExists[$rServerID])) { + $db->query('UPDATE `streams_servers` SET `parent_id` = ?, `on_demand` = ? WHERE `server_stream_id` = ?;', $rParent, $rOD, $rStreamExists[$rServerID]); + } else { + $db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`, `parent_id`, `on_demand`) VALUES(?, ?, ?, ?);', $rInsertID, $rServerID, $rParent, $rOD); + } + } + } + + foreach ($rStreamExists as $rServerID => $rDBID) { + if (in_array($rServerID, $rStreamsAdded)) { + } else { + deleteStream($rInsertID, $rServerID, false, false); + } + } + $db->query('DELETE FROM `streams_options` WHERE `stream_id` = ?;', $rInsertID); + + if (!(isset($rData['user_agent']) && 0 < strlen($rData['user_agent']))) { + } else { + $db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 1, ?);', $rInsertID, $rData['user_agent']); + } + + if (!(isset($rData['http_proxy']) && 0 < strlen($rData['http_proxy']))) { + } else { + $db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 2, ?);', $rInsertID, $rData['http_proxy']); + } + + if (!(isset($rData['cookie']) && 0 < strlen($rData['cookie']))) { + } else { + $db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 17, ?);', $rInsertID, $rData['cookie']); + } + + if (!(isset($rData['headers']) && 0 < strlen($rData['headers']))) { + } else { + $db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 19, ?);', $rInsertID, $rData['headers']); + } + + if (isset($rData['skip_ffprobe']) && ($rData['skip_ffprobe'] == 'on' || $rData['skip_ffprobe'] == 1)) { + $db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 21, ?);', $rInsertID, '1'); + } + + if (isset($rData['force_input_acodec']) && strlen(trim($rData['force_input_acodec'])) > 0) { + $db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 20, ?);', $rInsertID, trim($rData['force_input_acodec'])); + } + + if (!$rRestart) { + } else { + APIRequest(array('action' => 'stream', 'sub' => 'start', 'stream_ids' => array($rInsertID))); + } + + foreach ($rBouquets as $rBouquet) { + addToBouquet('stream', $rBouquet, $rInsertID); + } + + if (!(isset($rData['edit']) || isset($rImportStream['id']))) { + } else { + foreach (getBouquets() as $rBouquet) { + if (in_array($rBouquet['id'], $rBouquets)) { + } else { + removeFromBouquet('stream', $rBouquet['id'], $rInsertID); + } + } + } + + CoreUtilities::updateStream($rInsertID); + } else { + foreach ($rBouquetCreate as $rBouquet => $rID) { + $db->query('DELETE FROM `bouquets` WHERE `id` = ?;', $rID); + } + + foreach ($rCategoryCreate as $rCategory => $rID) { + $db->query('DELETE FROM `streams_categories` WHERE `id` = ?;', $rID); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + } + } + + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } else { + return array('status' => STATUS_NO_SOURCES, 'data' => $rData); + } + } + + public static function massEdit($rData, $db) { + set_time_limit(0); + ini_set('mysql.connect_timeout', 0); + ini_set('max_execution_time', 0); + ini_set('default_socket_timeout', 0); + + $rArray = array(); + + if (!isset($rData['c_days_to_restart'])) { + } else { + if (isset($rData['days_to_restart']) && preg_match('/^(?:2[0-3]|[01][0-9]):[0-5][0-9]$/', $rData['time_to_restart'])) { + $rTimeArray = array('days' => array(), 'at' => $rData['time_to_restart']); + + foreach ($rData['days_to_restart'] as $rID => $rDay) { + $rTimeArray['days'][] = $rDay; + } + $rArray['auto_restart'] = json_encode($rTimeArray); + } else { + $rArray['auto_restart'] = ''; + } + } + + foreach (array('gen_timestamps', 'allow_record', 'rtmp_output', 'fps_restart', 'stream_all', 'read_native') as $rKey) { + if (!isset($rData['c_' . $rKey])) { + } else { + if (isset($rData[$rKey])) { + $rArray[$rKey] = 1; + } else { + $rArray[$rKey] = 0; + } + } + } + + if (!isset($rData['c_direct_source'])) { + } else { + if (isset($rData['direct_source'])) { + $rArray['direct_source'] = 1; + } else { + $rArray['direct_source'] = 0; + $rArray['direct_proxy'] = 0; + } + } + + if (!isset($rData['c_direct_proxy'])) { + } else { + if (isset($rData['direct_proxy'])) { + $rArray['direct_proxy'] = 1; + $rArray['direct_source'] = 1; + } else { + $rArray['direct_proxy'] = 0; + } + } + + foreach (array('tv_archive_server_id', 'vframes_server_id', 'tv_archive_duration', 'delay_minutes', 'probesize_ondemand', 'fps_threshold', 'llod') as $rKey) { + if (!isset($rData['c_' . $rKey])) { + } else { + $rArray[$rKey] = intval($rData[$rKey]); + } + } + + if (!isset($rData['c_custom_sid'])) { + } else { + $rArray['custom_sid'] = $rData['custom_sid']; + } + + if (!isset($rData['c_transcode_profile_id'])) { + } else { + $rArray['transcode_profile_id'] = $rData['transcode_profile_id']; + + if (0 < $rArray['transcode_profile_id']) { + $rArray['enable_transcode'] = 1; + } else { + $rArray['enable_transcode'] = 0; + } + } + + $rStreamIDs = json_decode($rData['streams'], true); + + if (0 >= count($rStreamIDs)) { + } else { + $rCategoryMap = array(); + + if (!(isset($rData['c_category_id']) && in_array($rData['category_id_type'], array('ADD', 'DEL')))) { + } else { + $db->query('SELECT `id`, `category_id` FROM `streams` WHERE `id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); + + foreach ($db->get_rows() as $rRow) { + $rCategoryMap[$rRow['id']] = (json_decode($rRow['category_id'], true) ?: array()); + } + } + + $rDeleteServers = $rStreamExists = array(); + $db->query('SELECT `stream_id`, `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); + + foreach ($db->get_rows() as $rRow) { + $rStreamExists[intval($rRow['stream_id'])][intval($rRow['server_id'])] = intval($rRow['server_stream_id']); + } + $rBouquets = getBouquets(); + $rDelOptions = $rAddBouquet = $rDelBouquet = array(); + $rOptQuery = $rAddQuery = ''; + + foreach ($rStreamIDs as $rStreamID) { + if (!isset($rData['c_category_id'])) { + } else { + $rCategories = array_map('intval', $rData['category_id']); + + if ($rData['category_id_type'] == 'ADD') { + foreach (($rCategoryMap[$rStreamID] ?: array()) as $rCategoryID) { + if (in_array($rCategoryID, $rCategories)) { + } else { + $rCategories[] = $rCategoryID; + } + } + } else { + if ($rData['category_id_type'] != 'DEL') { + } else { + $rNewCategories = $rCategoryMap[$rStreamID]; + + foreach ($rCategories as $rCategoryID) { + if (($rKey = array_search($rCategoryID, $rNewCategories)) === false) { + } else { + unset($rNewCategories[$rKey]); + } + } + $rCategories = $rNewCategories; + } + } + + $rArray['category_id'] = '[' . implode(',', $rCategories) . ']'; + } + + $rPrepare = prepareArray($rArray); + + if (0 >= count($rPrepare['data'])) { + } else { + $rPrepare['data'][] = $rStreamID; + $rQuery = 'UPDATE `streams` SET ' . $rPrepare['update'] . ' WHERE `id` = ?;'; + $db->query($rQuery, ...$rPrepare['data']); + } + + if (!isset($rData['c_server_tree'])) { + } else { + $rStreamsAdded = array(); + $rServerTree = json_decode($rData['server_tree_data'], true); + + foreach ($rServerTree as $rServer) { + if ($rServer['parent'] == '#') { + } else { + $rServerID = intval($rServer['id']); + + if (in_array($rData['server_type'], array('ADD', 'SET'))) { + $rOD = intval(in_array($rServerID, ($rData['on_demand'] ?: array()))); + + if ($rServer['parent'] == 'source') { + $rParent = null; + } else { + $rParent = intval($rServer['parent']); + } + + $rStreamsAdded[] = $rServerID; + + if (isset($rStreamExists[$rStreamID][$rServerID])) { + $db->query('UPDATE `streams_servers` SET `parent_id` = ?, `on_demand` = ? WHERE `server_stream_id` = ?;', $rParent, $rOD, $rStreamExists[$rStreamID][$rServerID]); + } else { + $rAddQuery .= '(' . intval($rStreamID) . ', ' . intval($rServerID) . ', ' . (($rParent ?: 'NULL')) . ', ' . $rOD . '),'; + } + } else { + if (!isset($rStreamExists[$rStreamID][$rServerID])) { + } else { + $rDeleteServers[$rServerID][] = $rStreamID; + } + } + } + } + + if ($rData['server_type'] != 'SET') { + } else { + foreach ($rStreamExists[$rStreamID] as $rServerID => $rDBID) { + if (in_array($rServerID, $rStreamsAdded)) { + } else { + $rDeleteServers[$rServerID][] = $rStreamID; + } + } + } + } + + if (!isset($rData['c_user_agent'])) { + } else { + if (!(isset($rData['user_agent']) && 0 < strlen($rData['user_agent']))) { + } else { + $rDelOptions[1][] = $rStreamID; + $rOptQuery .= '(' . intval($rStreamID) . ', 1, ' . $db->escape($rData['user_agent']) . '),'; + } + } + + if (!isset($rData['c_http_proxy'])) { + } else { + if (!(isset($rData['http_proxy']) && 0 < strlen($rData['http_proxy']))) { + } else { + $rDelOptions[2][] = $rStreamID; + $rOptQuery .= '(' . intval($rStreamID) . ', 2, ' . $db->escape($rData['http_proxy']) . '),'; + } + } + + if (!isset($rData['c_cookie'])) { + } else { + if (!(isset($rData['cookie']) && 0 < strlen($rData['cookie']))) { + } else { + $rDelOptions[17][] = $rStreamID; + $rOptQuery .= '(' . intval($rStreamID) . ', 17, ' . $db->escape($rData['cookie']) . '),'; + } + } + + if (!isset($rData['c_headers'])) { + } else { + if (!(isset($rData['headers']) && 0 < strlen($rData['headers']))) { + } else { + $rDelOptions[19][] = $rStreamID; + $rOptQuery .= '(' . intval($rStreamID) . ', 19, ' . $db->escape($rData['headers']) . '),'; + } + } + + if (!isset($rData['c_bouquets'])) { + } else { + if ($rData['bouquets_type'] == 'SET') { + foreach ($rData['bouquets'] as $rBouquet) { + $rAddBouquet[$rBouquet][] = $rStreamID; + } + + foreach ($rBouquets as $rBouquet) { + if (in_array($rBouquet['id'], $rData['bouquets'])) { + } else { + $rDelBouquet[$rBouquet['id']][] = $rStreamID; + } + } + } else { + if ($rData['bouquets_type'] == 'ADD') { + foreach ($rData['bouquets'] as $rBouquet) { + $rAddBouquet[$rBouquet][] = $rStreamID; + } + } else { + if ($rData['bouquets_type'] != 'DEL') { + } else { + foreach ($rData['bouquets'] as $rBouquet) { + $rDelBouquet[$rBouquet][] = $rStreamID; + } + } + } + } + } + } + + foreach ($rDeleteServers as $rServerID => $rDeleteIDs) { + deleteStreamsByServer($rDeleteIDs, $rServerID, false); + } + + foreach ($rDelOptions as $rOptionID => $rDelIDs) { + $rDelIDs = array_unique(array_map('intval', $rDelIDs)); + if (0 >= count($rDelIDs)) { + } else { + $db->query('DELETE FROM `streams_options` WHERE `stream_id` IN (' . implode(',', $rDelIDs) . ') AND `argument_id` = ?;', intval($rOptionID)); + } + } + + if (empty($rOptQuery)) { + } else { + $rOptQuery = rtrim($rOptQuery, ','); + $db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES ' . $rOptQuery . ';'); + } + + foreach ($rAddBouquet as $rBouquetID => $rAddIDs) { + addToBouquet('stream', $rBouquetID, $rAddIDs); + } + + foreach ($rDelBouquet as $rBouquetID => $rRemIDs) { + removeFromBouquet('stream', $rBouquetID, $rRemIDs); + } + + if (empty($rAddQuery)) { + } else { + $rAddQuery = rtrim($rAddQuery, ','); + $db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`, `parent_id`, `on_demand`) VALUES ' . $rAddQuery . ';'); + } + + CoreUtilities::updateStreams($rStreamIDs); + + if (!isset($rData['restart_on_edit'])) { + } else { + APIRequest(array('action' => 'stream', 'sub' => 'start', 'stream_ids' => array_values($rStreamIDs))); + } + } + + return array('status' => STATUS_SUCCESS); + } + + public static function move($rData, $db) { + $rType = intval($rData['content_type']); + $rSource = intval($rData['source_server']); + $rReplacement = intval($rData['replacement_server']); + + if (!(0 < $rSource && 0 < $rReplacement && $rSource != $rReplacement)) { + } else { + $rExisting = array(); + + if ($rType == 0) { + $db->query('SELECT `stream_id` FROM `streams_servers` WHERE `server_id` = ?;', $rReplacement); + + foreach ($db->get_rows() as $rRow) { + $rExisting[] = intval($rRow['stream_id']); + } + } else { + $db->query('SELECT `streams_servers`.`stream_id` FROM `streams_servers` LEFT JOIN `streams` ON `streams`.`id` = `streams_servers`.`stream_id` WHERE `streams_servers`.`server_id` = ? AND `streams`.`type` = ?;', $rReplacement, $rType); + + foreach ($db->get_rows() as $rRow) { + $rExisting[] = intval($rRow['stream_id']); + } + } + + $db->query('SELECT `stream_id` FROM `streams_servers` WHERE `server_id` = ?;', $rSource); + + foreach ($db->get_rows() as $rRow) { + if (!in_array(intval($rRow['stream_id']), $rExisting)) { + } else { + $db->query('DELETE FROM `streams_servers` WHERE `stream_id` = ? AND `server_id` = ?;', $rRow['stream_id'], $rSource); + } + } + + if ($rType == 0) { + $db->query('UPDATE `streams_servers` SET `server_id` = ? WHERE `server_id` = ?;', $rReplacement, $rSource); + } else { + $db->query('UPDATE `streams_servers` LEFT JOIN `streams` ON `streams`.`id` = `streams_servers`.`stream_id` SET `streams_servers`.`server_id` = ? WHERE `streams_servers`.`server_id` = ? AND `streams`.`type` = ?;', $rReplacement, $rSource, $rType); + } + } + + return array('status' => STATUS_SUCCESS); + } + + public static function replaceDNS($rData, $db) { + $rOldDNS = str_replace('/', '\\/', $rData['old_dns']); + $rNewDNS = str_replace('/', '\\/', $rData['new_dns']); + $db->query('UPDATE `streams` SET `stream_source` = REPLACE(`stream_source`, ?, ?);', $rOldDNS, $rNewDNS); + + return array('status' => STATUS_SUCCESS); + } + + public static function massDelete($rData) { + set_time_limit(0); + ini_set('mysql.connect_timeout', 0); + ini_set('max_execution_time', 0); + ini_set('default_socket_timeout', 0); + + $rStreams = json_decode($rData['streams'], true); + deleteStreams($rStreams, false); + + return array('status' => STATUS_SUCCESS); + } +} diff --git a/src/domain/Ticket/TicketService.php b/src/domain/Ticket/TicketService.php new file mode 100644 index 00000000..39286b1b --- /dev/null +++ b/src/domain/Ticket/TicketService.php @@ -0,0 +1,44 @@ + STATUS_INVALID_DATA, 'data' => $rData); + } + + if (!isset($rData['respond'])) { + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `tickets`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + $db->query('INSERT INTO `tickets_replies`(`ticket_id`, `admin_reply`, `message`, `date`) VALUES(?, 0, ?, ?);', $rInsertID, $rData['message'], time()); + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); + } + + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + $rTicket = call_user_func($rGetTicketCallback, $rData['respond']); + if (!$rTicket) { + return array('status' => STATUS_FAILURE, 'data' => $rData); + } + + if (intval($rUserInfo['id']) == intval($rTicket['member_id'])) { + $db->query('UPDATE `tickets` SET `admin_read` = 0, `user_read` = 1 WHERE `id` = ?;', $rData['respond']); + $db->query('INSERT INTO `tickets_replies`(`ticket_id`, `admin_reply`, `message`, `date`) VALUES(?, 0, ?, ?);', $rData['respond'], $rData['message'], time()); + } else { + $db->query('UPDATE `tickets` SET `admin_read` = 0, `user_read` = 0 WHERE `id` = ?;', $rData['respond']); + $db->query('INSERT INTO `tickets_replies`(`ticket_id`, `admin_reply`, `message`, `date`) VALUES(?, 1, ?, ?);', $rData['respond'], $rData['message'], time()); + } + + return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rData['respond'])); + } +} diff --git a/src/domain/User/GroupRepository.php b/src/domain/User/GroupRepository.php new file mode 100644 index 00000000..94afbd85 --- /dev/null +++ b/src/domain/User/GroupRepository.php @@ -0,0 +1,52 @@ +query('SELECT * FROM `users_groups` ORDER BY `group_id` ASC;'); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + $rReturn[intval($rRow['group_id'])] = $rRow; + } + } + + return $rReturn; + } + + public static function getById($db, $rID) { + $db->query('SELECT * FROM `users_groups` WHERE `group_id` = ?;', $rID); + + if ($db->num_rows() != 1) { + } else { + return $db->get_row(); + } + } + + public static function deleteById($db, $rID) { + $rGroup = getMemberGroup($rID); + + if (!($rGroup && $rGroup['can_delete'])) { + return false; + } + + $db->query("SELECT `id`, `groups` FROM `users_packages` WHERE JSON_CONTAINS(`groups`, ?, '\$');", $rID); + + foreach ($db->get_rows() as $rRow) { + $rRow['groups'] = json_decode($rRow['groups'], true); + + if ($rKey = array_search($rID, $rRow['groups']) !== false) { + unset($rRow['groups'][$rKey]); + } + + $groups = array_map('intval', $rRow['groups']); + + $db->query("UPDATE `users_packages` SET `groups` = '[" . implode(',', $groups) . "]' WHERE `id` = ?;", $rRow['id']); + } + $db->query('UPDATE `users` SET `member_group_id` = 0 WHERE `member_group_id` = ?;', $rID); + $db->query('DELETE FROM `users_groups` WHERE `group_id` = ?;', $rID); + + return true; + } +} diff --git a/src/domain/User/GroupService.php b/src/domain/User/GroupService.php new file mode 100644 index 00000000..788a7603 --- /dev/null +++ b/src/domain/User/GroupService.php @@ -0,0 +1,7 @@ += strlen($rData['email']) || filter_var($rData['email'], FILTER_VALIDATE_EMAIL))) { + return array('status' => STATUS_INVALID_EMAIL); + } + + if (0 < strlen($rData['password'])) { + $rPassword = cryptPassword($rData['password']); + } else { + $rPassword = $rUserInfo['password']; + } + + if (!(ctype_xdigit($rData['api_key']) && strlen($rData['api_key']) == 32)) { + $rData['api_key'] = ''; + } + + if (!in_array($rData['lang'], $allowedLangs)) { + $rData['lang'] = 'en'; + } + + $db->query('UPDATE `users` SET `password` = ?, `email` = ?, `theme` = ?, `hue` = ?, `timezone` = ?, `api_key` = ?, `lang` = ? WHERE `id` = ?;', $rPassword, $rData['email'], $rData['theme'], $rData['hue'], $rData['timezone'], $rData['api_key'], $rData['lang'], $rUserInfo['id']); + + return array('status' => STATUS_SUCCESS); + } +} diff --git a/src/domain/User/UserRepository.php b/src/domain/User/UserRepository.php new file mode 100644 index 00000000..5256f940 --- /dev/null +++ b/src/domain/User/UserRepository.php @@ -0,0 +1,284 @@ +query('SELECT `id`, `username`, `password`, `member_group_id`, `status` FROM `users` WHERE `username` = ? LIMIT 1;', $rUsername); + + if ($db->num_rows() == 1) { + $rRow = $db->get_row(); + + if (cryptPassword($rPassword, $rRow['password']) == $rRow['password']) { + return $rRow; + } + } + } + + public static function getResellers($db, $rOwner, $rIncludeSelf = true) { + if ($rIncludeSelf) { + $db->query('SELECT `id`, `username` FROM `users` WHERE `owner_id` = ? OR `id` = ? ORDER BY `username` ASC;', $rOwner, $rOwner); + } else { + $db->query('SELECT `id`, `username` FROM `users` WHERE `owner_id` = ? ORDER BY `username` ASC;', $rOwner); + } + + return $db->get_rows(true, 'id'); + } + + public static function getDirectReports($db, $rPermissions, $rUserInfo, $rIncludeSelf = true) { + $rUserIDs = $rPermissions['direct_reports']; + + if (!$rIncludeSelf) { + } else { + $rUserIDs[] = $rUserInfo['id']; + } + + $rReturn = array(); + + if (0 >= count($rUserIDs)) { + } else { + $db->query('SELECT * FROM `users` WHERE `owner_id` IN (' . implode(',', array_map('intval', $rUserIDs)) . ') ORDER BY `username` ASC;'); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + $rReturn[intval($rRow['id'])] = $rRow; + } + } + } + + return $rReturn; + } + + public static function getParent($rPermissions, $rUserInfo, $rID) { + if (!isset($rPermissions['users'][$rID]['parent']) || $rPermissions['users'][$rID]['parent'] == 0 || $rPermissions['users'][$rID]['parent'] == $rUserInfo['id']) { + return $rID; + } + + return self::getParent($rPermissions, $rUserInfo, $rPermissions['users'][$rID]['parent']); + } + + public static function getSubUsers($db, $rUser) { + $rReturn = array(); + $db->query('SELECT `id`, `username` FROM `users` WHERE `owner_id` = ?;', $rUser); + + foreach ($db->get_rows() as $rRow) { + $rReturn[$rRow['id']] = array('username' => $rRow['username'], 'parent' => $rUser); + + foreach (self::getSubUsers($db, $rRow['id']) as $rUserID => $rUserData) { + $rReturn[$rUserID] = $rUserData; + } + } + + return $rReturn; + } + + public static function getLineById($db, $rID) { + $db->query('SELECT * FROM `lines` WHERE `id` = ?;', $rID); + + if ($db->num_rows() != 1) { + } else { + return $db->get_row(); + } + } + + public static function getRegisteredUserById($db, $rID) { + $db->query('SELECT * FROM `users` WHERE `id` = ?;', $rID); + + if ($db->num_rows() != 1) { + } else { + return $db->get_row(); + } + } + + public static function getRegisteredUsers($db, $rOwner = null, $rIncludeSelf = true) { + $rReturn = array(); + $db->query('SELECT * FROM `users` ORDER BY `username` ASC;'); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + if (!(!$rOwner || $rRow['owner_id'] == $rOwner || $rRow['id'] == $rOwner && $rIncludeSelf)) { + } else { + $rReturn[intval($rRow['id'])] = $rRow; + } + } + } + + if (count($rReturn) == 0) { + $rReturn[-1] = array(); + } + + return $rReturn; + } + + public static function getStreamingUserInfo($db, $rSettings, $rCached, $rBouquets, $rUserID = null, $rUsername = null, $rPassword = null, $rGetChannelIDs = false, $rGetConnections = false, $rIP = '', $rCallbacks = array()) { + $rUserInfo = null; + + if ($rCached) { + if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { + if ($rSettings['case_sensitive_line']) { + $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_t_' . $rUsername)); + } else { + $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_t_' . strtolower($rUsername))); + } + } else { + if (!empty($rUsername) && !empty($rPassword)) { + if ($rSettings['case_sensitive_line']) { + $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_c_' . $rUsername . '_' . $rPassword)); + } else { + $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_c_' . strtolower($rUsername) . '_' . strtolower($rPassword))); + } + } else { + if (empty($rUserID)) { + return false; + } + } + } + + if ($rUserID) { + $rUserInfo = igbinary_unserialize(file_get_contents(LINES_TMP_PATH . 'line_i_' . $rUserID)); + } + } else { + if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { + $db->query('SELECT * FROM `lines` WHERE `is_mag` = 0 AND `is_e2` = 0 AND `access_token` = ? AND LENGTH(`access_token`) = 32', $rUsername); + } else { + if (!empty($rUsername) && !empty($rPassword)) { + $db->query('SELECT `lines`.*, `mag_devices`.`token` AS `mag_token` FROM `lines` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines`.`id` WHERE `username` = ? AND `password` = ? LIMIT 1', $rUsername, $rPassword); + } else { + if (!empty($rUserID)) { + $db->query('SELECT `lines`.*, `mag_devices`.`token` AS `mag_token` FROM `lines` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines`.`id` WHERE `id` = ?', $rUserID); + } else { + return false; + } + } + } + + if (0 < $db->num_rows()) { + $rUserInfo = $db->get_row(); + } + } + + if (!$rUserInfo) { + return false; + } + + if ($rCached) { + if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { + if ($rUsername != $rUserInfo['access_token']) { + return false; + } + } else { + if (!empty($rUsername) && !empty($rPassword)) { + if ($rUsername != $rUserInfo['username'] || $rPassword != $rUserInfo['password']) { + return false; + } + } + } + } + + if ($rSettings['county_override_1st'] == 1 && empty($rUserInfo['forced_country']) && !empty($rIP) && $rUserInfo['max_connections'] == 1) { + $rUserInfo['forced_country'] = call_user_func($rCallbacks['getIPInfo'], $rIP)['registered_country']['iso_code']; + + if ($rCached) { + call_user_func($rCallbacks['setSignal'], 'forced_country/' . $rUserInfo['id'], $rUserInfo['forced_country']); + } else { + $db->query('UPDATE `lines` SET `forced_country` = ? WHERE `id` = ?', $rUserInfo['forced_country'], $rUserInfo['id']); + } + } + + $allowedIPS = json_decode($rUserInfo['allowed_ips'], true); + $allowedUa = json_decode($rUserInfo['allowed_ua'], true); + $rUserInfo['bouquet'] = json_decode($rUserInfo['bouquet'], true); + $rUserInfo['allowed_ips'] = array_filter(array_map('trim', is_array($allowedIPS) ? $allowedIPS : [])); + $rUserInfo['allowed_ua'] = array_filter(array_map('trim', is_array($allowedUa) ? $allowedUa : [])); + $rUserInfo['allowed_outputs'] = array_map('intval', json_decode($rUserInfo['allowed_outputs'], true)); + $rUserInfo['output_formats'] = array(); + + if ($rCached) { + foreach (igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'output_formats')) as $rRow) { + if (in_array(intval($rRow['access_output_id']), $rUserInfo['allowed_outputs'])) { + $rUserInfo['output_formats'][] = $rRow['output_key']; + } + } + } else { + $db->query('SELECT `access_output_id`, `output_key` FROM `output_formats`;'); + + foreach ($db->get_rows() as $rRow) { + if (in_array(intval($rRow['access_output_id']), $rUserInfo['allowed_outputs'])) { + $rUserInfo['output_formats'][] = $rRow['output_key']; + } + } + } + + $rUserInfo['con_isp_name'] = null; + $rUserInfo['isp_violate'] = 0; + $rUserInfo['isp_is_server'] = 0; + + if ($rSettings['show_isps'] == 1 && !empty($rIP)) { + $rISPLock = call_user_func($rCallbacks['getISP'], $rIP); + + if (is_array($rISPLock) && !empty($rISPLock['isp'])) { + $rUserInfo['con_isp_name'] = $rISPLock['isp']; + $rUserInfo['isp_asn'] = $rISPLock['autonomous_system_number']; + $rUserInfo['isp_violate'] = call_user_func($rCallbacks['checkISP'], $rUserInfo['con_isp_name']); + + if ($rSettings['block_svp'] == 1) { + $rUserInfo['isp_is_server'] = intval(call_user_func($rCallbacks['checkServer'], $rUserInfo['isp_asn'])); + } + } + + if (!empty($rUserInfo['con_isp_name']) && $rSettings['enable_isp_lock'] == 1 && $rUserInfo['is_stalker'] == 0 && $rUserInfo['is_isplock'] == 1 && !empty($rUserInfo['isp_desc']) && strtolower($rUserInfo['con_isp_name']) != strtolower($rUserInfo['isp_desc'])) { + $rUserInfo['isp_violate'] = 1; + } + + if ($rUserInfo['isp_violate'] == 0 && strtolower($rUserInfo['con_isp_name']) != strtolower($rUserInfo['isp_desc'])) { + if ($rCached) { + call_user_func($rCallbacks['setSignal'], 'isp/' . $rUserInfo['id'], json_encode(array($rUserInfo['con_isp_name'], $rUserInfo['isp_asn']))); + } else { + $db->query('UPDATE `lines` SET `isp_desc` = ?, `as_number` = ? WHERE `id` = ?', $rUserInfo['con_isp_name'], $rUserInfo['isp_asn'], $rUserInfo['id']); + } + } + } + + if ($rGetChannelIDs) { + $rLiveIDs = $rVODIDs = $rRadioIDs = $rCategoryIDs = $rChannelIDs = $rSeriesIDs = array(); + + foreach ($rUserInfo['bouquet'] as $rID) { + if (isset($rBouquets[$rID]['streams'])) { + $rChannelIDs = array_merge($rChannelIDs, $rBouquets[$rID]['streams']); + } + + if (isset($rBouquets[$rID]['series'])) { + $rSeriesIDs = array_merge($rSeriesIDs, $rBouquets[$rID]['series']); + } + + if (isset($rBouquets[$rID]['channels'])) { + $rLiveIDs = array_merge($rLiveIDs, $rBouquets[$rID]['channels']); + } + + if (isset($rBouquets[$rID]['movies'])) { + $rVODIDs = array_merge($rVODIDs, $rBouquets[$rID]['movies']); + } + + if (isset($rBouquets[$rID]['radios'])) { + $rRadioIDs = array_merge($rRadioIDs, $rBouquets[$rID]['radios']); + } + } + + $rUserInfo['channel_ids'] = array_map('intval', array_unique($rChannelIDs)); + $rUserInfo['series_ids'] = array_map('intval', array_unique($rSeriesIDs)); + $rUserInfo['vod_ids'] = array_map('intval', array_unique($rVODIDs)); + $rUserInfo['live_ids'] = array_map('intval', array_unique($rLiveIDs)); + $rUserInfo['radio_ids'] = array_map('intval', array_unique($rRadioIDs)); + } + + $rAllowedCategories = array(); + $rCategoryMap = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'category_map')); + + foreach ($rUserInfo['bouquet'] as $rID) { + $rAllowedCategories = array_merge($rAllowedCategories, ($rCategoryMap[$rID] ?: array())); + } + + $rUserInfo['category_ids'] = array_values(array_unique($rAllowedCategories)); + return $rUserInfo; + } +} diff --git a/src/domain/User/UserService.php b/src/domain/User/UserService.php new file mode 100644 index 00000000..49f31cef --- /dev/null +++ b/src/domain/User/UserService.php @@ -0,0 +1,23 @@ + STATUS_SUCCESS); + } + + public static function massEdit($rData) { + return API::massEditUsersLegacy($rData); + } + + public static function process($rData, $rBypassAuth = false) { + return API::processUserLegacy($rData, $rBypassAuth); + } +} diff --git a/src/domain/Vod/EpisodeService.php b/src/domain/Vod/EpisodeService.php new file mode 100644 index 00000000..6897a11c --- /dev/null +++ b/src/domain/Vod/EpisodeService.php @@ -0,0 +1,387 @@ + array($rSplit[2]), 'names' => array('Subtitles'), 'charset' => array('UTF-8'), 'location' => intval($rSplit[1])); + } else { + $rArray['movie_subtitles'] = null; + } + + if (0 < $rArray['transcode_profile_id']) { + $rArray['enable_transcode'] = 1; + } + + foreach (array('read_native', 'movie_symlink', 'direct_source', 'direct_proxy', 'remove_subtitles') as $rKey) { + if (isset($rData[$rKey])) { + $rArray[$rKey] = 1; + } else { + $rArray[$rKey] = 0; + } + } + + if (isset($rData['restart_on_edit'])) { + $rRestart = true; + } else { + $rRestart = false; + } + + $rProcessArray = array(); + + if (isset($rData['multi'])) { + if (hasPermissions('adv', 'import_episodes')) { + set_time_limit(0); + include INCLUDES_PATH . 'libs/tmdb.php'; + $rSeries = getSerie(intval($rData['series'])); + + if (0 < strlen($rSettings['tmdb_language'])) { + $rTMDB = new TMDB($rSettings['tmdb_api_key'], $rSettings['tmdb_language']); + } else { + $rTMDB = new TMDB($rSettings['tmdb_api_key']); + } + + $rJSON = json_decode($rTMDB->getSeason($rData['tmdb_id'], intval($rData['season_num']))->getJSON(), true); + + foreach ($rData as $rKey => $rFilename) { + $rSplit = explode('_', $rKey); + + if ($rSplit[0] == 'episode' && $rSplit[2] == 'name') { + if (0 < strlen($rData['episode_' . $rSplit[1] . '_num'])) { + $rImportArray = array('filename' => '', 'properties' => array(), 'name' => '', 'episode' => 0, 'target_container' => ''); + $rEpisodeNum = intval($rData['episode_' . $rSplit[1] . '_num']); + $rImportArray['filename'] = 's:' . $rData['server'] . ':' . $rData['season_folder'] . $rFilename; + $rImage = ''; + + if (isset($rData['addName1']) && isset($rData['addName2'])) { + $rImportArray['name'] = $rSeries['title'] . ' - S' . sprintf('%02d', intval($rData['season_num'])) . 'E' . sprintf('%02d', $rEpisodeNum) . ' - '; + } elseif (isset($rData['addName1'])) { + $rImportArray['name'] = $rSeries['title'] . ' - '; + } elseif (isset($rData['addName2'])) { + $rImportArray['name'] = 'S' . sprintf('%02d', intval($rData['season_num'])) . 'E' . sprintf('%02d', $rEpisodeNum) . ' - '; + } + + $rImportArray['episode'] = $rEpisodeNum; + + foreach ($rJSON['episodes'] as $rEpisode) { + if (intval($rEpisode['episode_number']) == $rEpisodeNum) { + if (0 < strlen($rEpisode['still_path'])) { + $rImage = 'https://image.tmdb.org/t/p/w600_and_h900_bestv2' . $rEpisode['still_path']; + + if ($rSettings['download_images']) { + $rImage = CoreUtilities::downloadImage($rImage, 5); + } + } + + $rImportArray['name'] .= $rEpisode['name']; + $rSeconds = intval($rSeries['episode_run_time']) * 60; + $rImportArray['properties'] = array('tmdb_id' => $rEpisode['id'], 'release_date' => $rEpisode['air_date'], 'plot' => $rEpisode['overview'], 'duration_secs' => $rSeconds, 'duration' => sprintf('%02d:%02d:%02d', $rSeconds / 3600, ($rSeconds / 60) % 60, $rSeconds % 60), 'movie_image' => $rImage, 'video' => array(), 'audio' => array(), 'bitrate' => 0, 'rating' => $rEpisode['vote_average'], 'season' => $rData['season_num']); + + if (strlen($rImportArray['properties']['movie_image'][0]) == 0) { + unset($rImportArray['properties']['movie_image']); + } + } + } + + if (strlen($rImportArray['name']) == 0) { + $rImportArray['name'] = 'No Episode Title'; + } + + $rPathInfo = pathinfo(explode('?', $rFilename)[0]); + $rImportArray['target_container'] = $rPathInfo['extension']; + $rProcessArray[] = $rImportArray; + } + } + } + } else { + exit(); + } + } else { + $rImportArray = array('filename' => $rArray['stream_source'][0], 'properties' => array(), 'name' => $rArray['stream_display_name'], 'episode' => $rData['episode'], 'target_container' => $rData['target_container']); + + if ($rSettings['download_images']) { + $rData['movie_image'] = CoreUtilities::downloadImage($rData['movie_image'], 5); + } + + $rSeconds = intval($rData['episode_run_time']) * 60; + $rImportArray['properties'] = array('release_date' => $rData['release_date'], 'plot' => $rData['plot'], 'duration_secs' => $rSeconds, 'duration' => sprintf('%02d:%02d:%02d', $rSeconds / 3600, ($rSeconds / 60) % 60, $rSeconds % 60), 'movie_image' => $rData['movie_image'], 'video' => array(), 'audio' => array(), 'bitrate' => 0, 'rating' => $rData['rating'], 'season' => $rData['season_num'], 'tmdb_id' => $rData['tmdb_id']); + + if (strlen($rImportArray['properties']['movie_image'][0]) == 0) { + unset($rImportArray['properties']['movie_image']); + } + + if ($rData['direct_proxy']) { + $rExtension = pathinfo(explode('?', $rData['stream_source'])[0])['extension']; + + if ($rExtension) { + $rImportArray['target_container'] = $rExtension; + } elseif (!$rImportArray['target_container']) { + $rImportArray['target_container'] = 'mp4'; + } + } + + $rProcessArray[] = $rImportArray; + } + + $rRestartIDs = array(); + + foreach ($rProcessArray as $rImportArray) { + $rArray['stream_source'] = array($rImportArray['filename']); + $rArray['movie_properties'] = $rImportArray['properties']; + $rArray['stream_display_name'] = $rImportArray['name']; + + if (!empty($rImportArray['target_container'])) { + $rArray['target_container'] = $rImportArray['target_container']; + } elseif (empty($rData['target_container'])) { + $rArray['target_container'] = pathinfo(explode('?', $rImportArray['filename'])[0])['extension']; + } else { + $rArray['target_container'] = $rData['target_container']; + } + + $rPrepare = prepareArray($rArray); + $rQuery = 'REPLACE INTO `streams`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; + + if ($db->query($rQuery, ...$rPrepare['data'])) { + $rInsertID = $db->last_insert_id(); + $db->query('DELETE FROM `streams_episodes` WHERE `stream_id` = ?;', $rInsertID); + $db->query('INSERT INTO `streams_episodes`(`season_num`, `series_id`, `stream_id`, `episode_num`) VALUES(?, ?, ?, ?);', $rData['season_num'], $rData['series'], $rInsertID, $rImportArray['episode']); + updateSeriesAsync(intval($rData['series'])); + $rStreamExists = array(); + + if (isset($rData['edit'])) { + $db->query('SELECT `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` = ?;', $rInsertID); + + foreach ($db->get_rows() as $rRow) { + $rStreamExists[intval($rRow['server_id'])] = intval($rRow['server_stream_id']); + } + } + + $rStreamsAdded = array(); + $rServerTree = json_decode($rData['server_tree_data'], true); + + foreach ($rServerTree as $rServer) { + if ($rServer['parent'] != '#') { + $rServerID = intval($rServer['id']); + $rStreamsAdded[] = $rServerID; + + if (!isset($rStreamExists[$rServerID])) { + $db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`, `on_demand`) VALUES(?, ?, 0);', $rInsertID, $rServerID); + } + } + } + + foreach ($rStreamExists as $rServerID => $rDBID) { + if (!in_array($rServerID, $rStreamsAdded)) { + deleteStream($rInsertID, $rServerID, true, false); + } + } + + if ($rRestart) { + $rRestartIDs[] = $rInsertID; + } + + $db->query('UPDATE `streams_series` SET `last_modified` = ? WHERE `id` = ?;', time(), $rData['streams_series']); + CoreUtilities::updateStream($rInsertID); + } else { + return array('status' => STATUS_FAILURE); + } + } + + if ($rRestart) { + APIRequest(array('action' => 'vod', 'sub' => 'start', 'stream_ids' => $rRestartIDs)); + } + + if (isset($rData['multi'])) { + return array('status' => STATUS_SUCCESS_MULTI, 0 => array('series_id' => $rData['series'])); + } + + return array('status' => STATUS_SUCCESS, 'data' => array('series_id' => $rData['series'], 'insert_id' => $rInsertID)); + } + + public static function massDelete($rData) { + set_time_limit(0); + ini_set('mysql.connect_timeout', 0); + ini_set('max_execution_time', 0); + ini_set('default_socket_timeout', 0); + + $rEpisodes = json_decode($rData['episodes'], true); + deleteStreams($rEpisodes, true); + + return array('status' => STATUS_SUCCESS); + } + + public static function massEdit($db, $rData) { + set_time_limit(0); + ini_set('mysql.connect_timeout', 0); + ini_set('max_execution_time', 0); + ini_set('default_socket_timeout', 0); + + $rArray = array(); + + if (isset($rData['c_movie_symlink'])) { + if (isset($rData['movie_symlink'])) { + $rArray['movie_symlink'] = 1; + } else { + $rArray['movie_symlink'] = 0; + } + } + + if (isset($rData['c_direct_source'])) { + if (isset($rData['direct_source'])) { + $rArray['direct_source'] = 1; + } else { + $rArray['direct_source'] = 0; + $rArray['direct_proxy'] = 0; + } + } + + if (isset($rData['c_direct_proxy'])) { + if (isset($rData['direct_proxy'])) { + $rArray['direct_proxy'] = 1; + $rArray['direct_source'] = 1; + } else { + $rArray['direct_proxy'] = 0; + } + } + + if (isset($rData['c_read_native'])) { + if (isset($rData['read_native'])) { + $rArray['read_native'] = 1; + } else { + $rArray['read_native'] = 0; + } + } + + if (isset($rData['c_remove_subtitles'])) { + if (isset($rData['remove_subtitles'])) { + $rArray['remove_subtitles'] = 1; + } else { + $rArray['remove_subtitles'] = 0; + } + } + + if (isset($rData['c_target_container'])) { + $rArray['target_container'] = $rData['target_container']; + } + + if (isset($rData['c_transcode_profile_id'])) { + $rArray['transcode_profile_id'] = $rData['transcode_profile_id']; + + if (0 < $rArray['transcode_profile_id']) { + $rArray['enable_transcode'] = 1; + } else { + $rArray['enable_transcode'] = 0; + } + } + + $rStreamIDs = confirmIDs(json_decode($rData['streams'], true)); + + if (0 < count($rStreamIDs)) { + if (isset($rData['c_serie_name'])) { + $db->query('UPDATE `streams_episodes` SET `series_id` = ? WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');', $rData['serie_name']); + $db->query('UPDATE `streams` SET `series_no` = ? WHERE `id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');', $rData['serie_name']); + } + + $rPrepare = prepareArray($rArray); + + if (0 < count($rPrepare['data'])) { + $rQuery = 'UPDATE `streams` SET ' . $rPrepare['update'] . ' WHERE `id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'; + $db->query($rQuery, ...$rPrepare['data']); + } + + $rDeleteServers = $rQueueMovies = $rProcessServers = $rStreamExists = array(); + $db->query('SELECT `stream_id`, `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); + + foreach ($db->get_rows() as $rRow) { + $rStreamExists[intval($rRow['stream_id'])][intval($rRow['server_id'])] = intval($rRow['server_stream_id']); + $rProcessServers[intval($rRow['stream_id'])][] = intval($rRow['server_id']); + } + $rAddQuery = ''; + + foreach ($rStreamIDs as $rStreamID) { + if (isset($rData['c_server_tree'])) { + $rStreamsAdded = array(); + $rServerTree = json_decode($rData['server_tree_data'], true); + + foreach ($rServerTree as $rServer) { + if ($rServer['parent'] != '#') { + $rServerID = intval($rServer['id']); + + if (in_array($rData['server_type'], array('ADD', 'SET'))) { + $rStreamsAdded[] = $rServerID; + + if (!isset($rStreamExists[$rStreamID][$rServerID])) { + $rAddQuery .= '(' . intval($rStreamID) . ', ' . intval($rServerID) . '),'; + $rProcessServers[$rStreamID][] = $rServerID; + } + } elseif (isset($rStreamExists[$rStreamID][$rServerID])) { + $rDeleteServers[$rServerID][] = $rStreamID; + } + } + } + + if ($rData['server_type'] == 'SET') { + foreach ($rStreamExists[$rStreamID] as $rServerID => $rDBID) { + if (!in_array($rServerID, $rStreamsAdded)) { + $rDeleteServers[$rServerID][] = $rStreamID; + + if (($rKey = array_search($rServerID, $rProcessServers[$rStreamID])) !== false) { + unset($rProcessServers[$rStreamID][$rKey]); + } + } + } + } + } + + if (isset($rData['reencode_on_edit'])) { + foreach ($rProcessServers[$rStreamID] as $rServerID) { + $rQueueMovies[$rServerID][] = $rStreamID; + } + } + } + + foreach ($rDeleteServers as $rServerID => $rDeleteIDs) { + deleteStreamsByServer($rDeleteIDs, $rServerID, true); + } + + if (!empty($rAddQuery)) { + $rAddQuery = rtrim($rAddQuery, ','); + $db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`) VALUES ' . $rAddQuery . ';'); + } + + CoreUtilities::updateStreams($rStreamIDs); + + if (isset($rData['reencode_on_edit'])) { + foreach ($rQueueMovies as $rServerID => $rQueueIDs) { + CoreUtilities::queueMovies($rQueueIDs, $rServerID); + } + } + + if (isset($rData['reprocess_tmdb'])) { + CoreUtilities::refreshMovies($rStreamIDs, 3); + } + } + + return array('status' => STATUS_SUCCESS); + } +} diff --git a/src/domain/Vod/MovieRepository.php b/src/domain/Vod/MovieRepository.php new file mode 100644 index 00000000..c16c9024 --- /dev/null +++ b/src/domain/Vod/MovieRepository.php @@ -0,0 +1,32 @@ +getSimilarMovies($rID, $rPage)), true); + } + + /** + * Send delete signal for movie files on specified servers. + * Extracted from admin.php::deleteMovieFile() + */ + public static function deleteFile($db, $rServerIDs, $rID) { + if (is_array($rServerIDs)) { + } else { + $rServerIDs = array($rServerIDs); + } + + foreach ($rServerIDs as $rServerID) { + $db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`, `cache`) VALUES(?, ?, ?, 1);', $rServerID, time(), json_encode(array('type' => 'delete_vod', 'id' => $rID))); + } + + return true; + } +} diff --git a/src/domain/Vod/MovieService.php b/src/domain/Vod/MovieService.php new file mode 100644 index 00000000..327f61aa --- /dev/null +++ b/src/domain/Vod/MovieService.php @@ -0,0 +1,239 @@ + STATUS_SUCCESS); + } + + public static function massEdit($db, $rData) { + set_time_limit(0); + ini_set('mysql.connect_timeout', 0); + ini_set('max_execution_time', 0); + ini_set('default_socket_timeout', 0); + + $rArray = array(); + + if (isset($rData['c_movie_symlink'])) { + if (isset($rData['movie_symlink'])) { + $rArray['movie_symlink'] = 1; + } else { + $rArray['movie_symlink'] = 0; + } + } + + if (isset($rData['c_direct_source'])) { + if (isset($rData['direct_source'])) { + $rArray['direct_source'] = 1; + } else { + $rArray['direct_source'] = 0; + $rArray['direct_proxy'] = 0; + } + } + + if (isset($rData['c_direct_proxy'])) { + if (isset($rData['direct_proxy'])) { + $rArray['direct_proxy'] = 1; + $rArray['direct_source'] = 1; + } else { + $rArray['direct_proxy'] = 0; + } + } + + if (isset($rData['c_read_native'])) { + if (isset($rData['read_native'])) { + $rArray['read_native'] = 1; + } else { + $rArray['read_native'] = 0; + } + } + + if (isset($rData['c_remove_subtitles'])) { + if (isset($rData['remove_subtitles'])) { + $rArray['remove_subtitles'] = 1; + } else { + $rArray['remove_subtitles'] = 0; + } + } + + if (isset($rData['c_target_container'])) { + $rArray['target_container'] = $rData['target_container']; + } + + if (isset($rData['c_transcode_profile_id'])) { + $rArray['transcode_profile_id'] = $rData['transcode_profile_id']; + + if (0 < $rArray['transcode_profile_id']) { + $rArray['enable_transcode'] = 1; + } else { + $rArray['enable_transcode'] = 0; + } + } + + $rStreamIDs = json_decode($rData['streams'], true); + + if (0 < count($rStreamIDs)) { + $rCategoryMap = array(); + + if (isset($rData['c_category_id']) && in_array($rData['category_id_type'], array('ADD', 'DEL'))) { + $db->query('SELECT `id`, `category_id` FROM `streams` WHERE `id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); + + foreach ($db->get_rows() as $rRow) { + $rCategoryMap[$rRow['id']] = (json_decode($rRow['category_id'], true) ?: array()); + } + } + + $rDeleteServers = $rQueueMovies = $rProcessServers = $rStreamExists = array(); + $db->query('SELECT `stream_id`, `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); + + foreach ($db->get_rows() as $rRow) { + $rStreamExists[intval($rRow['stream_id'])][intval($rRow['server_id'])] = intval($rRow['server_stream_id']); + $rProcessServers[intval($rRow['stream_id'])][] = intval($rRow['server_id']); + } + $rBouquets = getBouquets(); + $rAddBouquet = $rDelBouquet = array(); + $rAddQuery = ''; + + foreach ($rStreamIDs as $rStreamID) { + if (isset($rData['c_category_id'])) { + $rCategories = array_map('intval', $rData['category_id']); + + if ($rData['category_id_type'] == 'ADD') { + foreach (($rCategoryMap[$rStreamID] ?: array()) as $rCategoryID) { + if (!in_array($rCategoryID, $rCategories)) { + $rCategories[] = $rCategoryID; + } + } + } elseif ($rData['category_id_type'] == 'DEL') { + $rNewCategories = $rCategoryMap[$rStreamID]; + + foreach ($rCategories as $rCategoryID) { + if (($rKey = array_search($rCategoryID, $rNewCategories)) !== false) { + unset($rNewCategories[$rKey]); + } + } + $rCategories = $rNewCategories; + } + + $rArray['category_id'] = '[' . implode(',', $rCategories) . ']'; + } + + $rPrepare = prepareArray($rArray); + + if (0 < count($rPrepare['data'])) { + $rPrepare['data'][] = $rStreamID; + $rQuery = 'UPDATE `streams` SET ' . $rPrepare['update'] . ' WHERE `id` = ?;'; + $db->query($rQuery, ...$rPrepare['data']); + } + + if (isset($rData['c_server_tree'])) { + $rStreamsAdded = array(); + $rServerTree = json_decode($rData['server_tree_data'], true); + + foreach ($rServerTree as $rServer) { + if ($rServer['parent'] != '#') { + $rServerID = intval($rServer['id']); + + if (in_array($rData['server_type'], array('ADD', 'SET'))) { + $rStreamsAdded[] = $rServerID; + + if (!isset($rStreamExists[$rStreamID][$rServerID])) { + $rAddQuery .= '(' . intval($rStreamID) . ', ' . intval($rServerID) . '),'; + $rProcessServers[$rStreamID][] = $rServerID; + } + } elseif (isset($rStreamExists[$rStreamID][$rServerID])) { + $rDeleteServers[$rServerID][] = $rStreamID; + } + } + } + + if ($rData['server_type'] == 'SET') { + foreach ($rStreamExists[$rStreamID] as $rServerID => $rDBID) { + if (!in_array($rServerID, $rStreamsAdded)) { + $rDeleteServers[$rServerID][] = $rStreamID; + + if (($rKey = array_search($rServerID, $rProcessServers[$rStreamID])) !== false) { + unset($rProcessServers[$rStreamID][$rKey]); + } + } + } + } + } + + if (isset($rData['c_bouquets'])) { + if ($rData['bouquets_type'] == 'SET') { + foreach ($rData['bouquets'] as $rBouquet) { + $rAddBouquet[$rBouquet][] = $rStreamID; + } + + foreach ($rBouquets as $rBouquet) { + if (!in_array($rBouquet['id'], $rData['bouquets'])) { + $rDelBouquet[$rBouquet['id']][] = $rStreamID; + } + } + } elseif ($rData['bouquets_type'] == 'ADD') { + foreach ($rData['bouquets'] as $rBouquet) { + $rAddBouquet[$rBouquet][] = $rStreamID; + } + } elseif ($rData['bouquets_type'] == 'DEL') { + foreach ($rData['bouquets'] as $rBouquet) { + $rDelBouquet[$rBouquet][] = $rStreamID; + } + } + } + + if (isset($rData['reencode_on_edit'])) { + foreach ($rProcessServers[$rStreamID] as $rServerID) { + $rQueueMovies[$rServerID][] = $rStreamID; + } + } + } + + foreach ($rDeleteServers as $rServerID => $rDeleteIDs) { + deleteStreamsByServer($rDeleteIDs, $rServerID, true); + } + + foreach ($rAddBouquet as $rBouquetID => $rAddIDs) { + addToBouquet('movie', $rBouquetID, $rAddIDs); + } + + foreach ($rDelBouquet as $rBouquetID => $rRemIDs) { + removeFromBouquet('movie', $rBouquetID, $rRemIDs); + } + + if (!empty($rAddQuery)) { + $rAddQuery = rtrim($rAddQuery, ','); + $db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`) VALUES ' . $rAddQuery . ';'); + } + + CoreUtilities::updateStreams($rStreamIDs); + + if (isset($rData['reencode_on_edit'])) { + foreach ($rQueueMovies as $rServerID => $rQueueIDs) { + CoreUtilities::queueMovies($rQueueIDs, $rServerID); + } + } + + if (isset($rData['reprocess_tmdb'])) { + CoreUtilities::refreshMovies($rStreamIDs, 1); + } + } + + return array('status' => STATUS_SUCCESS); + } +} diff --git a/src/domain/Vod/SeriesRepository.php b/src/domain/Vod/SeriesRepository.php new file mode 100644 index 00000000..c45e9929 --- /dev/null +++ b/src/domain/Vod/SeriesRepository.php @@ -0,0 +1,110 @@ +getSimilarSeries($rID, $rPage)), true); + } + + /** + * Get all series as id => row array, ordered by title. + * Extracted from admin.php::getSeriesList() + */ + public static function getList($db) { + $rReturn = array(); + $db->query('SELECT `id`, `title` FROM `streams_series` ORDER BY `title` ASC;'); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + $rReturn[intval($rRow['id'])] = $rRow; + } + } + return $rReturn; + } + + /** + * Update series seasons from TMDB. + * Extracted from admin.php::updateSeries() + */ + public static function updateFromTMDB($db, $rID) { + require_once MAIN_HOME . 'includes/libs/tmdb.php'; + $db->query('SELECT `tmdb_id`, `tmdb_language` FROM `streams_series` WHERE `id` = ?;', $rID); + + if ($db->num_rows() != 1) { + } else { + $rRow = $db->get_row(); + $rTMDBID = $rRow['tmdb_id']; + + if (0 >= strlen($rTMDBID)) { + } else { + if (0 < strlen($rRow['tmdb_language'])) { + $rTMDB = new TMDB(CoreUtilities::$rSettings['tmdb_api_key'], $rRow['tmdb_language']); + } else { + if (0 < strlen(CoreUtilities::$rSettings['tmdb_language'])) { + $rTMDB = new TMDB(CoreUtilities::$rSettings['tmdb_api_key'], CoreUtilities::$rSettings['tmdb_language']); + } else { + $rTMDB = new TMDB(CoreUtilities::$rSettings['tmdb_api_key']); + } + } + + $rReturn = array(); + $rSeasons = json_decode($rTMDB->getTVShow($rTMDBID)->getJSON(), true)['seasons']; + + foreach ($rSeasons as $rSeason) { + $rSeason['cover'] = 'https://image.tmdb.org/t/p/w600_and_h900_bestv2' . $rSeason['poster_path']; + + if (!CoreUtilities::$rSettings['download_images']) { + } else { + $rSeason['cover'] = CoreUtilities::downloadImage($rSeason['cover']); + } + + $rSeason['cover_big'] = $rSeason['cover']; + unset($rSeason['poster_path']); + $rReturn[] = $rSeason; + } + + $db->query('UPDATE `streams_series` SET `seasons` = ? WHERE `id` = ?;', json_encode($rReturn, JSON_UNESCAPED_UNICODE), $rID); + } + } + } + + /** + * Queue async series refresh via watch_refresh table. + * Extracted from admin.php::updateSeriesAsync() + */ + public static function queueRefresh($db, $rID) { + $db->query('INSERT INTO `watch_refresh`(`type`, `stream_id`, `status`) VALUES(4, ?, 0);', $rID); + } + + /** + * Generate playlist of episode sources for a series. + * Extracted from admin.php::generateSeriesPlaylist() + */ + public static function generatePlaylist($db, $rSeriesNo) { + $rReturn = array(); + $db->query('SELECT `stream_id` FROM `streams_episodes` WHERE `series_id` = ? ORDER BY `season_num` ASC, `episode_num` ASC;', $rSeriesNo); + + if (0 >= $db->num_rows()) { + } else { + foreach ($db->get_rows() as $rRow) { + $db->query('SELECT `stream_source` FROM `streams` WHERE `id` = ?;', $rRow['stream_id']); + + if (0 >= $db->num_rows()) { + } else { + list($rSource) = json_decode($db->get_row()['stream_source'], true); + $rReturn[] = $rSource; + } + } + } + + return $rReturn; + } +} diff --git a/src/domain/Vod/SeriesService.php b/src/domain/Vod/SeriesService.php new file mode 100644 index 00000000..10cba7bf --- /dev/null +++ b/src/domain/Vod/SeriesService.php @@ -0,0 +1,121 @@ + STATUS_SUCCESS); + } + + public static function massEdit($db, $rData) { + set_time_limit(0); + ini_set('mysql.connect_timeout', 0); + ini_set('max_execution_time', 0); + ini_set('default_socket_timeout', 0); + + $rArray = array(); + $rSeriesIDs = json_decode($rData['series'], true); + + if (0 < count($rSeriesIDs)) { + $rCategoryMap = array(); + + if (isset($rData['c_category_id']) && in_array($rData['category_id_type'], array('ADD', 'DEL'))) { + $db->query('SELECT `id`, `category_id` FROM `streams_series` WHERE `id` IN (' . implode(',', array_map('intval', $rSeriesIDs)) . ');'); + + foreach ($db->get_rows() as $rRow) { + $rCategoryMap[$rRow['id']] = (json_decode($rRow['category_id'], true) ?: array()); + } + } + + $rBouquets = getBouquets(); + $rAddBouquet = $rDelBouquet = array(); + + foreach ($rSeriesIDs as $rSeriesID) { + if (isset($rData['c_category_id'])) { + $rCategories = array_map('intval', $rData['category_id']); + + if ($rData['category_id_type'] == 'ADD') { + foreach (($rCategoryMap[$rSeriesID] ?: array()) as $rCategoryID) { + if (!in_array($rCategoryID, $rCategories)) { + $rCategories[] = $rCategoryID; + } + } + } elseif ($rData['category_id_type'] == 'DEL') { + $rNewCategories = $rCategoryMap[$rSeriesID]; + + foreach ($rCategories as $rCategoryID) { + if (($rKey = array_search($rCategoryID, $rNewCategories)) !== false) { + unset($rNewCategories[$rKey]); + } + } + $rCategories = $rNewCategories; + } + + $rArray['category_id'] = '[' . implode(',', $rCategories) . ']'; + } + + $rPrepare = prepareArray($rArray); + + if (0 < count($rPrepare['data'])) { + $rPrepare['data'][] = $rSeriesID; + $rQuery = 'UPDATE `streams_series` SET ' . $rPrepare['update'] . ' WHERE `id` = ?;'; + $db->query($rQuery, ...$rPrepare['data']); + } + + if (isset($rData['c_bouquets'])) { + if ($rData['bouquets_type'] == 'SET') { + foreach ($rData['bouquets'] as $rBouquet) { + $rAddBouquet[$rBouquet][] = $rSeriesID; + } + + foreach ($rBouquets as $rBouquet) { + if (!in_array($rBouquet['id'], $rData['bouquets'])) { + $rDelBouquet[$rBouquet['id']][] = $rSeriesID; + } + } + } elseif ($rData['bouquets_type'] == 'ADD') { + foreach ($rData['bouquets'] as $rBouquet) { + $rAddBouquet[$rBouquet][] = $rSeriesID; + } + } elseif ($rData['bouquets_type'] == 'DEL') { + foreach ($rData['bouquets'] as $rBouquet) { + $rDelBouquet[$rBouquet][] = $rSeriesID; + } + } + } + } + + foreach ($rAddBouquet as $rBouquetID => $rAddIDs) { + addToBouquet('series', $rBouquetID, $rAddIDs); + } + + foreach ($rDelBouquet as $rBouquetID => $rRemIDs) { + removeFromBouquet('series', $rBouquetID, $rRemIDs); + } + + if (isset($rData['reprocess_tmdb'])) { + foreach ($rSeriesIDs as $rSeriesID) { + if (0 < intval($rSeriesID)) { + $db->query('INSERT INTO `watch_refresh`(`type`, `stream_id`, `status`) VALUES(2, ?, 0);', $rSeriesID); + } + } + } + } + + return array('status' => STATUS_SUCCESS); + } +} diff --git a/src/includes/CoreUtilities.php b/src/includes/CoreUtilities.php index 9d5c4492..77c2056e 100644 --- a/src/includes/CoreUtilities.php +++ b/src/includes/CoreUtilities.php @@ -20,20 +20,21 @@ class CoreUtilities { public static $rFFMPEG_GPU = null; public static $rFFPROBE = null; public static $rCached = null; + public static function init($rUseCache = false) { LegacyInitializer::initCore($rUseCache); } + public static function getDiffTimezone($rTimezone) { $rServerTZ = new DateTime('UTC', new DateTimeZone(date_default_timezone_get())); $rUserTZ = new DateTime('UTC', new DateTimeZone($rTimezone)); return $rUserTZ->getTimestamp() - $rServerTZ->getTimestamp(); } + public static function getAllowedDomains($rForce = false) { - if ($rForce) { - } else { + if (!$rForce) { $rCache = self::getCache('allowed_domains', 20); - if ($rCache === false) { - } else { + if ($rCache !== false) { return $rCache; } } @@ -43,19 +44,16 @@ class CoreUtilities { foreach (explode(',', $rRow['domain_name']) as $rDomain) { $rDomains[] = $rDomain; } - if (!$rRow['server_ip']) { - } else { + if (!empty($rRow['server_ip'])) { $rDomains[] = $rRow['server_ip']; } - if (!$rRow['private_ip']) { - } else { + if (!empty($rRow['private_ip'])) { $rDomains[] = $rRow['private_ip']; } } self::$db->query('SELECT `reseller_dns` FROM `users` WHERE `status` = 1;'); foreach (self::$db->get_rows() as $rRow) { - if (!$rRow['reseller_dns']) { - } else { + if (!empty($rRow['reseller_dns'])) { $rDomains[] = $rRow['reseller_dns']; } } @@ -63,461 +61,210 @@ class CoreUtilities { self::setCache('allowed_domains', $rDomains); return $rDomains; } + public static function getProxyIPs($rForce = false) { - if (!$rForce) { - $rCache = self::getCache('proxy_servers', 20); - if ($rCache === true) { - return $rCache; - } - } - $rOutput = array(); - foreach (self::$rServers as $rServer) { - if ($rServer['server_type'] == 1) { - $rOutput[$rServer['server_ip']] = $rServer; - if ($rServer['private_ip']) { - $rOutput[$rServer['private_ip']] = $rServer; - } - } - } - self::setCache('proxy_servers', $rOutput); - return $rOutput; + return BlocklistRepository::getProxyIPs(self::$rServers, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rForce); } + public static function isProxy($rIP) { - if (!isset(self::$rProxies[$rIP])) { - } else { + if (isset(self::$rProxies[$rIP])) { return self::$rProxies[$rIP]; } } + public static function getBlockedUA($rForce = false) { - if ($rForce) { - } else { - $rCache = self::getCache('blocked_ua', 20); - if ($rCache === false) { - } else { - return $rCache; - } - } - self::$db->query('SELECT id,exact_match,LOWER(user_agent) as blocked_ua FROM `blocked_uas`'); - $rOutput = self::$db->get_rows(true, 'id'); - self::setCache('blocked_ua', $rOutput); - return $rOutput; + return BlocklistRepository::getBlockedUA(self::$db, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rForce); } + public static function getBlockedIPs($rForce = false) { - if ($rForce) { - } else { - $rCache = self::getCache('blocked_ips', 20); - if ($rCache === false) { - } else { - return $rCache; - } - } - $rOutput = array(); - self::$db->query('SELECT `ip` FROM `blocked_ips`'); - foreach (self::$db->get_rows() as $rRow) { - $rOutput[] = $rRow['ip']; - } - self::setCache('blocked_ips', $rOutput); - return $rOutput; + return BlocklistRepository::getBlockedIPs(self::$db, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rForce); } + public static function getBlockedISP($rForce = false) { - if ($rForce) { - } else { - $rCache = self::getCache('blocked_isp', 20); - if ($rCache === false) { - } else { - return $rCache; - } - } - self::$db->query('SELECT id,isp,blocked FROM `blocked_isps`'); - $rOutput = self::$db->get_rows(); - self::setCache('blocked_isp', $rOutput); - return $rOutput; + return BlocklistRepository::getBlockedISP(self::$db, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rForce); } + public static function getBlockedServers($rForce = false) { - if ($rForce) { - } else { - $rCache = self::getCache('blocked_servers', 20); - if ($rCache === false) { - } else { - return $rCache; - } - } - $rOutput = array(); - self::$db->query('SELECT `asn` FROM `blocked_asns` WHERE `blocked` = 1;'); - foreach (self::$db->get_rows() as $rRow) { - $rOutput[] = $rRow['asn']; - } - self::setCache('blocked_servers', $rOutput); - return $rOutput; + return BlocklistRepository::getBlockedServers(self::$db, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rForce); } + public static function getBouquets($rForce = false) { return BouquetRepository::getAll(self::$db, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rForce); } + public static function getSettings($rForce = false) { return SettingsRepository::getAll(self::$db, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rForce); } + public static function setCache($rCache, $rData) { - $rData = igbinary_serialize($rData); - file_put_contents(CACHE_TMP_PATH . $rCache, $rData, LOCK_EX); + file_put_contents(CACHE_TMP_PATH . $rCache, igbinary_serialize($rData), LOCK_EX); } + public static function getCache($rCache, $rSeconds = null) { if (file_exists(CACHE_TMP_PATH . $rCache)) { - if ($rSeconds && time() - filemtime(CACHE_TMP_PATH . $rCache) >= $rSeconds) { - } else { - $rData = file_get_contents(CACHE_TMP_PATH . $rCache); - return igbinary_unserialize($rData); + if (!$rSeconds || time() - filemtime(CACHE_TMP_PATH . $rCache) < $rSeconds) { + return igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . $rCache)); } } return false; } + public static function getServers($rForce = false) { return ServerRepository::getAll(self::$db, self::$rSettings, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rForce); } + public static function getMultiCURL($rURLs, $callback = null, $rTimeout = 5) { return CurlClient::getMultiCURL(self::$rServers, $rURLs, $callback, $rTimeout); } + public static function cleanGlobals(&$rData, $rIteration = 0) { - if (10 > $rIteration) { - foreach ($rData as $rKey => $rValue) { - if (is_array($rValue)) { - self::cleanGlobals($rData[$rKey], ++$rIteration); - } else { - $rValue = str_replace(chr('0'), '', $rValue); - $rValue = str_replace('', '', $rValue); - $rValue = str_replace('', '', $rValue); - $rValue = str_replace('../', '../', $rValue); - $rValue = str_replace('‮', '', $rValue); - $rData[$rKey] = $rValue; - } + if ($rIteration >= 10) { + return; + } + foreach ($rData as $rKey => $rValue) { + if (is_array($rValue)) { + self::cleanGlobals($rData[$rKey], $rIteration + 1); + } else { + $rValue = str_replace(chr(0), '', $rValue); + $rValue = str_replace('../', '../', $rValue); + $rValue = str_replace('‮', '', $rValue); + $rData[$rKey] = $rValue; } - } else { - return null; } } + public static function parseIncomingRecursively(&$rData, $rInput = array(), $rIteration = 0) { - if (20 > $rIteration) { - if (is_array($rData)) { - foreach ($rData as $rKey => $rValue) { - if (is_array($rValue)) { - $rInput[$rKey] = self::parseIncomingRecursively($rData[$rKey], array(), $rIteration + 1); - } else { - $rKey = self::parseCleanKey($rKey); - $rValue = self::parseCleanValue($rValue); - $rInput[$rKey] = $rValue; - } - } - return $rInput; - } else { - return $rInput; - } - } else { + if ($rIteration >= 20 || !is_array($rData)) { return $rInput; } + foreach ($rData as $rKey => $rValue) { + if (is_array($rValue)) { + $rInput[$rKey] = self::parseIncomingRecursively($rData[$rKey], array(), $rIteration + 1); + } else { + $rInput[self::parseCleanKey($rKey)] = self::parseCleanValue($rValue); + } + } + return $rInput; } + public static function parseCleanKey($rKey) { - if ($rKey !== '') { - $rKey = htmlspecialchars(urldecode($rKey)); - $rKey = str_replace('..', '', $rKey); - $rKey = preg_replace('/\\_\\_(.+?)\\_\\_/', '', $rKey); - return preg_replace('/^([\\w\\.\\-\\_]+)$/', '$1', $rKey); + if ($rKey === '') { + return ''; } - return ''; + $rKey = htmlspecialchars(urldecode($rKey)); + $rKey = str_replace('..', '', $rKey); + $rKey = preg_replace('/\_\_(.+?)\_\_/', '', $rKey); + return preg_replace('/^([\w\.\-\_]+)$/', '$1', $rKey); } + public static function parseCleanValue($rValue) { - if ($rValue != '') { - $rValue = str_replace(' ', ' ', stripslashes($rValue)); - $rValue = str_replace(array("\r\n", "\n\r", "\r"), "\n", $rValue); - $rValue = str_replace('', '-->', $rValue); - $rValue = str_ireplace('', '-->', $rValue); + $rValue = str_ireplace('= $rTime) { - if (!(is_numeric($rPID) && 0 < $rPID)) { - } else { - posix_kill($rPID, 9); - } - } else { - exit('Running...'); - } + if (file_exists('/proc/' . $rPID) && time() - filemtime($rFilename) < $rTime) { + exit('Running...'); + } + if (time() - filemtime($rFilename) >= $rTime && is_numeric($rPID) && 0 < $rPID) { + posix_kill($rPID, 9); } } file_put_contents($rFilename, getmypid()); - return false; } - /** @deprecated Use BruteforceGuard::checkFlood() */ + public static function checkFlood($rIP = null) { return BruteforceGuard::checkFlood($rIP); } - /** @deprecated Use BruteforceGuard::checkBruteforce() */ + public static function checkBruteforce($rIP = null, $rMAC = null, $rUsername = null) { return BruteforceGuard::checkBruteforce($rIP, $rMAC, $rUsername); } - /** @deprecated Use BruteforceGuard::truncateAttempts() */ + public static function truncateAttempts($rAttempts, $rFrequency, $rList = false) { return BruteforceGuard::truncateAttempts($rAttempts, $rFrequency, $rList); } + public static function getCategories($rType = null, $rForce = false) { return CategoryRepository::getFromDatabase(self::$db, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rType, $rForce); } + public static function generateUniqueCode() { return substr(md5(self::$rSettings['live_streaming_pass']), 0, 15); } + public static function unserialize_php($rSessionData) { $rReturn = array(); $rOffset = 0; while ($rOffset < strlen($rSessionData)) { - if (strstr(substr($rSessionData, $rOffset), '|')) { - $rPos = strpos($rSessionData, '|', $rOffset); - $rNum = $rPos - $rOffset; - $rVarName = substr($rSessionData, $rOffset, $rNum); - $rOffset += $rNum + 1; - $rData = igbinary_unserialize(substr($rSessionData, $rOffset)); - $rReturn[$rVarName] = $rData; - $rOffset += strlen(igbinary_serialize($rData)); - } else { + if (!strstr(substr($rSessionData, $rOffset), '|')) { return array(); } + $rPos = strpos($rSessionData, '|', $rOffset); + $rVarName = substr($rSessionData, $rOffset, $rPos - $rOffset); + $rOffset += ($rPos - $rOffset) + 1; + $rData = igbinary_unserialize(substr($rSessionData, $rOffset)); + $rReturn[$rVarName] = $rData; + $rOffset += strlen(igbinary_serialize($rData)); } return $rReturn; } + public static function generatePlaylist($rUserInfo, $rDeviceKey, $rOutputKey = 'ts', $rTypeKey = null, $rNoCache = false, $rProxy = false) { - if (!empty($rDeviceKey)) { - if ($rOutputKey == 'mpegts') { - $rOutputKey = 'ts'; - } - if ($rOutputKey == 'hls') { - $rOutputKey = 'm3u8'; - } - if (empty($rOutputKey)) { - self::$db->query('SELECT t1.output_ext FROM `output_formats` t1 INNER JOIN `output_devices` t2 ON t2.default_output = t1.access_output_id AND `device_key` = ?', $rDeviceKey); - } else { - self::$db->query('SELECT t1.output_ext FROM `output_formats` t1 WHERE `output_key` = ?', $rOutputKey); - } - if (self::$db->num_rows() > 0) { - $rCacheName = $rUserInfo['id'] . '_' . $rDeviceKey . '_' . $rOutputKey . '_' . implode('_', ($rTypeKey ?: array())); - $rOutputExt = self::$db->get_col(); - $rEncryptPlaylist = ($rUserInfo['is_restreamer'] ? self::$rSettings['encrypt_playlist_restreamer'] : self::$rSettings['encrypt_playlist']); - if ($rUserInfo['is_stalker']) { - $rEncryptPlaylist = false; - } - $rDomainName = self::getDomainName(); - if ($rDomainName) { - if (!$rProxy) { - $rRTMPRows = array(); - if ($rOutputKey == 'rtmp') { - self::$db->query('SELECT t1.id,t2.server_id FROM `streams` t1 INNER JOIN `streams_servers` t2 ON t2.stream_id = t1.id WHERE t1.rtmp_output = 1'); - $rRTMPRows = self::$db->get_rows(true, 'id', false, 'server_id'); - } - } else { - if ($rOutputKey == 'rtmp') { - $rOutputKey = 'ts'; - } - } - if (empty($rOutputExt)) { - $rOutputExt = 'ts'; - } - self::$db->query('SELECT t1.*,t2.* FROM `output_devices` t1 LEFT JOIN `output_formats` t2 ON t2.access_output_id = t1.default_output WHERE t1.device_key = ? LIMIT 1', $rDeviceKey); - if (0 >= self::$db->num_rows()) { - return false; - } - $rDeviceInfo = self::$db->get_row(); - if (strlen($rUserInfo['access_token']) == 32) { - $rFilename = str_replace('{USERNAME}', $rUserInfo['access_token'], $rDeviceInfo['device_filename']); - } else { - $rFilename = str_replace('{USERNAME}', $rUserInfo['username'], $rDeviceInfo['device_filename']); - } - if (!(0 < self::$rSettings['cache_playlists'] && !$rNoCache && file_exists(PLAYLIST_PATH . md5($rCacheName)))) { - $rData = ''; - $rSeriesAllocation = $rSeriesEpisodes = $rSeriesInfo = array(); - $rUserInfo['episode_ids'] = array(); - if (0 >= count($rUserInfo['series_ids'])) { - } else { - if (self::$rCached) { - foreach ($rUserInfo['series_ids'] as $rSeriesID) { - $rSeriesInfo[$rSeriesID] = igbinary_unserialize(file_get_contents(SERIES_TMP_PATH . 'series_' . intval($rSeriesID))); - $rSeriesData = igbinary_unserialize(file_get_contents(SERIES_TMP_PATH . 'episodes_' . intval($rSeriesID))); - foreach ($rSeriesData as $rSeasonID => $rEpisodes) { - foreach ($rEpisodes as $rEpisode) { - $rSeriesEpisodes[$rEpisode['stream_id']] = array($rSeasonID, $rEpisode['episode_num']); - $rSeriesAllocation[$rEpisode['stream_id']] = $rSeriesID; - $rUserInfo['episode_ids'][] = $rEpisode['stream_id']; - } - } - } - } else { - self::$db->query('SELECT * FROM `streams_series` WHERE `id` IN (' . implode(',', $rUserInfo['series_ids']) . ')'); - $rSeriesInfo = self::$db->get_rows(true, 'id'); - if (0 >= count($rUserInfo['series_ids'])) { - } else { - self::$db->query('SELECT stream_id, series_id, season_num, episode_num FROM `streams_episodes` WHERE series_id IN (' . implode(',', $rUserInfo['series_ids']) . ') ORDER BY FIELD(series_id,' . implode(',', $rUserInfo['series_ids']) . '), season_num ASC, episode_num ASC'); - foreach (self::$db->get_rows(true, 'series_id', false) as $rSeriesID => $rEpisodes) { - foreach ($rEpisodes as $rEpisode) { - $rSeriesEpisodes[$rEpisode['stream_id']] = array($rEpisode['season_num'], $rEpisode['episode_num']); - $rSeriesAllocation[$rEpisode['stream_id']] = $rSeriesID; - $rUserInfo['episode_ids'][] = $rEpisode['stream_id']; - } - } - } - } - } - if (0 >= count($rUserInfo['episode_ids'])) { - } else { - $rUserInfo['channel_ids'] = array_merge($rUserInfo['channel_ids'], $rUserInfo['episode_ids']); - } - $rChannelIDs = array(); - $rAdded = false; - if ($rTypeKey) { - foreach ($rTypeKey as $rType) { - switch ($rType) { - case 'live': - case 'created_live': - if (!$rAdded) { - $rChannelIDs = array_merge($rChannelIDs, $rUserInfo['live_ids']); - $rAdded = true; - break; - } - break; - case 'movie': - $rChannelIDs = array_merge($rChannelIDs, $rUserInfo['vod_ids']); - break; - case 'radio_streams': - $rChannelIDs = array_merge($rChannelIDs, $rUserInfo['radio_ids']); - break; - case 'series': - $rChannelIDs = array_merge($rChannelIDs, $rUserInfo['episode_ids']); - break; - } - } - } else { - $rChannelIDs = $rUserInfo['channel_ids']; - } - if (in_array(self::$rSettings['channel_number_type'], array('bouquet_new', 'manual'))) { - $rChannelIDs = self::sortChannels($rChannelIDs); - } - unset($rUserInfo['live_ids'], $rUserInfo['vod_ids'], $rUserInfo['radio_ids'], $rUserInfo['episode_ids'], $rUserInfo['channel_ids']); - $rOutputFile = null; - header('Content-Description: File Transfer'); - header('Content-Type: application/octet-stream'); - header('Expires: 0'); - header('Cache-Control: must-revalidate'); - header('Pragma: public'); - if (strlen($rUserInfo['access_token']) == 32) { - header('Content-Disposition: attachment; filename="' . str_replace('{USERNAME}', $rUserInfo['access_token'], $rDeviceInfo['device_filename']) . '"'); - } else { - header('Content-Disposition: attachment; filename="' . str_replace('{USERNAME}', $rUserInfo['username'], $rDeviceInfo['device_filename']) . '"'); - } - if (self::$rSettings['cache_playlists'] == 1) { - $rOutputPath = PLAYLIST_PATH . md5($rCacheName) . '.write'; - $rOutputFile = fopen($rOutputPath, 'w'); - } - if ($rDeviceKey == 'starlivev5') { - $rOutput = array(); - $rOutput['iptvstreams_list'] = array(); - $rOutput['iptvstreams_list']['@version'] = 1; - $rOutput['iptvstreams_list']['group'] = array(); - $rOutput['iptvstreams_list']['group']['name'] = 'IPTV'; - $rOutput['iptvstreams_list']['group']['channel'] = array(); - foreach (array_chunk($rChannelIDs, 1000) as $rBlockIDs) { - if (self::$rSettings['playlist_from_mysql'] || !self::$rCached) { - $rOrder = 'FIELD(`t1`.`id`,' . implode(',', $rBlockIDs) . ')'; - self::$db->query('SELECT t1.id,t1.channel_id,t1.year,t1.movie_properties,t1.stream_icon,t1.custom_sid,t1.category_id,t1.stream_display_name,t2.type_output,t2.type_key,t1.target_container,t2.live FROM `streams` t1 INNER JOIN `streams_types` t2 ON t2.type_id = t1.type WHERE `t1`.`id` IN (' . implode(',', array_map('intval', $rBlockIDs)) . ') ORDER BY ' . $rOrder . ';'); - $rRows = self::$db->get_rows(); - } else { - $rRows = array(); - foreach ($rBlockIDs as $rID) { - $rRows[] = igbinary_unserialize(file_get_contents(STREAMS_TMP_PATH . 'stream_' . intval($rID)))['info']; - } - } - foreach ($rRows as $rChannelInfo) { - if (!$rTypeKey || in_array($rChannelInfo['type_output'], $rTypeKey)) { - if ($rChannelInfo['target_container']) { - } else { - $rChannelInfo['target_container'] = 'mp4'; - } - $rProperties = (!is_array($rChannelInfo['movie_properties']) ? json_decode($rChannelInfo['movie_properties'], true) : $rChannelInfo['movie_properties']); - if ($rChannelInfo['type_key'] == 'series') { - $rSeriesID = $rSeriesAllocation[$rChannelInfo['id']]; - $rChannelInfo['live'] = 0; - $rChannelInfo['stream_display_name'] = $rSeriesInfo[$rSeriesID]['title'] . ' S' . sprintf('%02d', $rSeriesEpisodes[$rChannelInfo['id']][0]) . 'E' . sprintf('%02d', $rSeriesEpisodes[$rChannelInfo['id']][1]); - $rChannelInfo['movie_properties'] = array('movie_image' => (!empty($rProperties['movie_image']) ? $rProperties['movie_image'] : $rSeriesInfo['cover'])); - $rChannelInfo['type_output'] = 'series'; - $rChannelInfo['category_id'] = $rSeriesInfo[$rSeriesID]['category_id']; - } else { - $rChannelInfo['stream_display_name'] = self::formatTitle($rChannelInfo['stream_display_name'], $rChannelInfo['year']); - } - if (strlen($rUserInfo['access_token']) == 32) { - $rURL = $rDomainName . $rChannelInfo['type_output'] . '/' . $rUserInfo['access_token'] . '/'; - if ($rChannelInfo['live'] == 0) { - $rURL .= $rChannelInfo['id'] . '.' . $rChannelInfo['target_container']; - } else { - if (self::$rSettings['cloudflare'] && $rOutputExt == 'ts') { - $rURL .= $rChannelInfo['id']; - } else { - $rURL .= $rChannelInfo['id'] . '.' . $rOutputExt; - } - } - } else { - if ($rEncryptPlaylist) { - $rEncData = $rChannelInfo['type_output'] . '/' . $rUserInfo['username'] . '/' . $rUserInfo['password'] . '/'; - if ($rChannelInfo['live'] == 0) { + return PlaylistGenerator::generate(self::$db, self::$rSettings, self::$rServers, self::$rCategories, self::$rCached, $rUserInfo, $rDeviceKey, $rOutputKey, $rTypeKey, $rNoCache, $rProxy); + } + + public static function generateCron() { + return CronGenerator::generate(self::$db); + } + + /* $rEncData .= $rChannelInfo['id'] . '/' . $rChannelInfo['target_container']; } else { if (self::$rSettings['cloudflare'] && $rOutputExt == 'ts') { @@ -784,6 +531,7 @@ class CoreUtilities { return false; } } + */ public static function secondsToTime($rInputSeconds, $rInclSecs = true) { $rSecondsInAMinute = 60; $rSecondsInAnHour = 60 * $rSecondsInAMinute; @@ -851,137 +599,16 @@ class CoreUtilities { return CurlClient::serverRequest(self::$rServers, $rServerID, $rURL, $rPostData); } public static function deleteCache($rSources) { - if (!empty($rSources)) { - foreach ($rSources as $rSource) { - if (!file_exists(CACHE_TMP_PATH . md5($rSource))) { - } else { - unlink(CACHE_TMP_PATH . md5($rSource)); - } - } - } else { - return null; - } + return StreamProcess::deleteCache($rSources); } public static function queueChannel($rStreamID, $rServerID = null) { - if ($rServerID) { - } else { - $rServerID = SERVER_ID; - } - self::$db->query('SELECT `id` FROM `queue` WHERE `stream_id` = ? AND `server_id` = ?;', $rStreamID, $rServerID); - if (self::$db->num_rows() != 0) { - } else { - self::$db->query("INSERT INTO `queue`(`type`, `stream_id`, `server_id`, `added`) VALUES('channel', ?, ?, ?);", $rStreamID, $rServerID, time()); - } + StreamProcess::queueChannel(self::$db, $rStreamID, $rServerID); } public static function createChannel($rStreamID) { - shell_exec(PHP_BIN . ' ' . CLI_PATH . 'created.php ' . intval($rStreamID) . ' >/dev/null 2>/dev/null &'); - return true; + return StreamProcess::createChannel($rStreamID); } public static function createChannelItem($rStreamID, $rSource) { - $rStream = array(); - $rLoopback = false; - self::$db->query('SELECT * FROM `streams` t1 INNER JOIN `streams_types` t2 ON t2.type_id = t1.type AND t1.type = 3 LEFT JOIN `profiles` t4 ON t1.transcode_profile_id = t4.profile_id WHERE t1.direct_source = 0 AND t1.id = ?', $rStreamID); - if (self::$db->num_rows() > 0) { - $rStream['stream_info'] = self::$db->get_row(); - self::$db->query('SELECT * FROM `streams_servers` WHERE stream_id = ? AND `server_id` = ?', $rStreamID, SERVER_ID); - if (self::$db->num_rows() > 0) { - $rStream['server_info'] = self::$db->get_row(); - $rMD5 = md5($rSource); - if (substr($rSource, 0, 2) == 's:') { - $rSplit = explode(':', $rSource, 3); - $rServerID = intval($rSplit[1]); - $rSourcePath = $rSplit[2]; // File path - if ($rServerID != SERVER_ID) { - // File on another server - needs to be retrieved via API. - if (is_array(self::$rServers) && isset(self::$rServers[$rServerID])) { - $rSourcePath = self::$rServers[$rServerID]['api_url'] . '&action=getFile&filename=' . urlencode($rSplit[2]); - } else { - // Server not found, use local path. - $rSourcePath = $rSplit[2]; - } - } - } else { - $rServerID = SERVER_ID; - $rSourcePath = $rSource; - } - if ($rServerID == SERVER_ID && intval($rStream['stream_info']['movie_symlink']) == 1) { - $rExtension = pathinfo($rSource)['extension']; - if (strlen($rExtension) == 0) { - $rExtension = 'mp4'; - } - // Create symlink - $rCommand = 'ln -sfn ' . escapeshellarg($rSourcePath) . ' "' . CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.' . escapeshellcmd($rExtension) . '" >/dev/null 2>/dev/null & echo $! > "' . CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.pid"'; - } else { - $rStream['stream_info']['transcode_attributes'] = json_decode($rStream['stream_info']['profile_options'], true); - if (!is_array($rStream['stream_info']['transcode_attributes'])) { - $rStream['stream_info']['transcode_attributes'] = array(); - } - // Logo overlay - $rLogoOptions = ''; - if (isset($rStream['stream_info']['transcode_attributes'][16]) && !$rLoopback) { - $rAttr = $rStream['stream_info']['transcode_attributes']; - $rLogoPath = $rAttr[16]['val']; - $rPos = (isset($rAttr[16]['pos']) && $rAttr[16]['pos'] !== '10:10') ? $rAttr[16]['pos'] : '10:main_h-overlay_h-10'; - - // Reconstruct filter chain to ensure fixed logo size - $rChain = array(); - $rBase = '[0:v]'; - - // Handle Yadif (ID 17) and Video Scaling (ID 9) - $rVideoFilters = array(); - if (isset($rAttr[17])) { - $rVideoFilters[] = 'yadif'; - } - if (isset($rAttr[9]['val']) && strlen($rAttr[9]['val']) > 0) { - $rVideoFilters[] = 'scale=' . $rAttr[9]['val']; - } - - if (!empty($rVideoFilters)) { - $rChain[] = $rBase . implode(',', $rVideoFilters) . '[bg]'; - $rBase = '[bg]'; - } - - // Scale logo to fixed width 250px (keep aspect ratio) - $rChain[] = '[1:v]scale=250:-1[logo]'; - - // Overlay - $rChain[] = $rBase . '[logo]overlay=' . $rPos; - - $rLogoOptions = '-i ' . escapeshellarg($rLogoPath) . ' -filter_complex "' . implode('; ', $rChain) . '"'; - unset($rStream['stream_info']['transcode_attributes'][16]); - } - $rGPUOptions = (isset($rStream['stream_info']['transcode_attributes']['gpu']) ? $rStream['stream_info']['transcode_attributes']['gpu']['cmd'] : ''); - $rInputCodec = ''; - if (empty($rGPUOptions)) { - } else { - $rFFProbeOutput = self::probeStream($rSourcePath); - if (!in_array($rFFProbeOutput['codecs']['video']['codec_name'], array('h264', 'hevc', 'mjpeg', 'mpeg1', 'mpeg2', 'mpeg4', 'vc1', 'vp8', 'vp9'))) { - } else { - $rInputCodec = '-c:v ' . $rFFProbeOutput['codecs']['video']['codec_name'] . '_cuvid'; - } - } - $rCommand = ((isset($rStream['stream_info']['transcode_attributes']['gpu']) ? self::$rFFMPEG_GPU : self::$rFFMPEG_CPU)) . ' -y -nostdin -hide_banner -loglevel ' . ((self::$rSettings['ffmpeg_warnings'] ? 'warning' : 'error')) . ' -err_detect ignore_err {GPU} -fflags +genpts -async 1 -i {STREAM_SOURCE} {LOGO} '; - - if (!array_key_exists('-acodec', $rStream['stream_info']['transcode_attributes'])) { - $rStream['stream_info']['transcode_attributes']['-acodec'] = 'copy'; - } - if (!array_key_exists('-vcodec', $rStream['stream_info']['transcode_attributes'])) { - $rStream['stream_info']['transcode_attributes']['-vcodec'] = 'copy'; - } - if (isset($rStream['stream_info']['transcode_attributes']['gpu'])) { - $rCommand .= '-gpu ' . intval($rStream['stream_info']['transcode_attributes']['gpu']['device']) . ' '; - } - $rCommand .= implode(' ', self::parseTranscode($rStream['stream_info']['transcode_attributes'])) . ' '; - $rCommand .= '-strict -2 -mpegts_flags +initial_discontinuity -f mpegts "' . CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.ts"'; - $rCommand .= ' >/dev/null 2>"' . CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.errors" & echo $! > "' . CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.pid"'; - $rCommand = str_replace(array('{GPU}', '{INPUT_CODEC}', '{LOGO}', '{STREAM_SOURCE}'), array($rGPUOptions, $rInputCodec, $rLogoOptions, escapeshellarg($rSourcePath)), $rCommand); - } - shell_exec($rCommand); - return intval(file_get_contents(CREATED_PATH . intval($rStreamID) . '_' . $rMD5 . '.pid')); - } - return false; - } - return false; + return StreamProcess::createChannelItem(self::$db, self::$rSettings, self::$rServers, self::$rFFMPEG_CPU, self::$rFFMPEG_GPU, $rStreamID, $rSource); } public static function extractSubtitle($rStreamID, $rSourceURL, $rIndex) { $rTimeout = 10; @@ -1049,44 +676,7 @@ class CoreUtilities { } } public static function stopStream($rStreamID, $rStop = false) { - if (file_exists(STREAMS_PATH . $rStreamID . '_.monitor')) { - $rMonitor = intval(file_get_contents(STREAMS_PATH . $rStreamID . '_.monitor')); - } else { - self::$db->query('SELECT `monitor_pid` FROM `streams_servers` WHERE `server_id` = ? AND `stream_id` = ? LIMIT 1;', SERVER_ID, $rStreamID); - $rMonitor = intval(self::$db->get_row()['monitor_pid']); - } - if (0 >= $rMonitor) { - } else { - if (!(self::checkPID($rMonitor, array('XC_VM[' . $rStreamID . ']', 'XC_VMProxy[' . $rStreamID . ']')) && is_numeric($rMonitor) && 0 < $rMonitor)) { - } else { - posix_kill($rMonitor, 9); - } - } - if (file_exists(STREAMS_PATH . $rStreamID . '_.pid')) { - $rPID = intval(file_get_contents(STREAMS_PATH . $rStreamID . '_.pid')); - } else { - self::$db->query('SELECT `pid` FROM `streams_servers` WHERE `server_id` = ? AND `stream_id` = ? LIMIT 1;', SERVER_ID, $rStreamID); - $rPID = intval(self::$db->get_row()['pid']); - } - if (0 >= $rPID) { - } else { - if (!(self::checkPID($rPID, array($rStreamID . '_.m3u8', $rStreamID . '_%d.ts', 'LLOD[' . $rStreamID . ']', 'XC_VMProxy[' . $rStreamID . ']', 'Loopback[' . $rStreamID . ']')) && is_numeric($rPID) && 0 < $rPID)) { - } else { - posix_kill($rPID, 9); - } - } - if (!file_exists(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID))) { - } else { - unlink(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID)); - } - self::streamLog($rStreamID, SERVER_ID, 'STREAM_STOP'); - shell_exec('rm -f ' . STREAMS_PATH . intval($rStreamID) . '_*'); - if (!$rStop) { - } else { - shell_exec('rm -f ' . DELAY_PATH . intval($rStreamID) . '_*'); - self::$db->query('UPDATE `streams_servers` SET `bitrate` = NULL,`current_source` = NULL,`to_analyze` = 0,`pid` = NULL,`stream_started` = NULL,`stream_info` = NULL,`audio_codec` = NULL,`video_codec` = NULL,`resolution` = NULL,`compatible` = 0,`stream_status` = 0,`monitor_pid` = NULL WHERE `stream_id` = ? AND `server_id` = ?', $rStreamID, SERVER_ID); - self::updateStream($rStreamID); - } + StreamProcess::stopStream(self::$db, $rStreamID, $rStop); } public static function checkPID($rPID, $rSearch) { if (is_array($rSearch)) { @@ -1106,227 +696,28 @@ class CoreUtilities { return false; } public static function startMonitor($rStreamID, $rRestart = 0) { - shell_exec(PHP_BIN . ' ' . CLI_PATH . 'monitor.php ' . intval($rStreamID) . ' ' . intval($rRestart) . ' >/dev/null 2>/dev/null &'); - return true; + return StreamProcess::startMonitor($rStreamID, $rRestart); } public static function startProxy($rStreamID) { - shell_exec(PHP_BIN . ' ' . CLI_PATH . 'proxy.php ' . intval($rStreamID) . ' >/dev/null 2>/dev/null &'); - return true; + return StreamProcess::startProxy($rStreamID); } public static function startThumbnail($rStreamID) { - shell_exec(PHP_BIN . ' ' . CLI_PATH . 'thumbnail.php ' . intval($rStreamID) . ' >/dev/null 2>/dev/null &'); - return true; + return StreamProcess::startThumbnail($rStreamID); } public static function stopMovie($rStreamID, $rForce = false) { - shell_exec("kill -9 `ps -ef | grep '/" . intval($rStreamID) . ".' | grep -v grep | awk '{print \$2}'`;"); - if ($rForce) { - exec('rm ' . MAIN_HOME . 'content/vod/' . intval($rStreamID) . '.*'); - } else { - self::$db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`, `cache`) VALUES(?, ?, ?, 1);', SERVER_ID, time(), json_encode(array('type' => 'delete_vod', 'id' => $rStreamID))); - } - self::$db->query('UPDATE `streams_servers` SET `bitrate` = NULL,`current_source` = NULL,`to_analyze` = 0,`pid` = NULL,`stream_started` = NULL,`stream_info` = NULL,`audio_codec` = NULL,`video_codec` = NULL,`resolution` = NULL,`compatible` = 0,`stream_status` = 0 WHERE `stream_id` = ? AND `server_id` = ?', $rStreamID, SERVER_ID); - self::updateStream($rStreamID); + StreamProcess::stopMovie(self::$db, $rStreamID, $rForce); } public static function queueMovie($rStreamID, $rServerID = null) { - if ($rServerID) { - } else { - $rServerID = SERVER_ID; - } - self::$db->query('DELETE FROM `queue` WHERE `stream_id` = ? AND `server_id` = ?;', $rStreamID, $rServerID); - self::$db->query("INSERT INTO `queue`(`type`, `stream_id`, `server_id`, `added`) VALUES('movie', ?, ?, ?);", $rStreamID, $rServerID, time()); + StreamProcess::queueMovie(self::$db, $rStreamID, $rServerID); } public static function queueMovies($rStreamIDs, $rServerID = null) { - if ($rServerID) { - } else { - $rServerID = SERVER_ID; - } - if (0 >= count($rStreamIDs)) { - } else { - self::$db->query('DELETE FROM `queue` WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ') AND `server_id` = ?;', $rServerID); - $rQuery = ''; - foreach ($rStreamIDs as $rStreamID) { - if (0 >= $rStreamID) { - } else { - $rQuery .= "('movie', " . intval($rStreamID) . ', ' . intval($rServerID) . ', ' . time() . '),'; - } - } - if (empty($rQuery)) { - } else { - $rQuery = rtrim($rQuery, ','); - self::$db->query('INSERT INTO `queue`(`type`, `stream_id`, `server_id`, `added`) VALUES ' . $rQuery . ';'); - } - } + StreamProcess::queueMovies(self::$db, $rStreamIDs, $rServerID); } public static function refreshMovies($rIDs, $rType = 1) { - if (0 >= count($rIDs)) { - } else { - self::$db->query('DELETE FROM `watch_refresh` WHERE `type` = ? AND `stream_id` IN (' . implode(',', array_map('intval', $rIDs)) . ');', $rType); - $rQuery = ''; - foreach ($rIDs as $rID) { - if (0 >= $rID) { - } else { - $rQuery .= '(' . intval($rType) . ', ' . intval($rID) . ', 0),'; - } - } - if (empty($rQuery)) { - } else { - $rQuery = rtrim($rQuery, ','); - self::$db->query('INSERT INTO `watch_refresh`(`type`, `stream_id`, `status`) VALUES ' . $rQuery . ';'); - } - } + StreamProcess::refreshMovies(self::$db, $rIDs, $rType); } public static function startMovie($rStreamID) { - $rStream = array(); - $rLoopback = false; - self::$db->query('SELECT * FROM `streams` t1 INNER JOIN `streams_types` t2 ON t2.type_id = t1.type AND t2.live = 0 LEFT JOIN `profiles` t4 ON t1.transcode_profile_id = t4.profile_id WHERE t1.direct_source = 0 AND t1.id = ?', $rStreamID); - if (self::$db->num_rows() > 0) { - $rStream['stream_info'] = self::$db->get_row(); - self::$db->query('SELECT * FROM `streams_servers` WHERE stream_id = ? AND `server_id` = ?', $rStreamID, SERVER_ID); - if (self::$db->num_rows() > 0) { - $rStream['server_info'] = self::$db->get_row(); - self::$db->query('SELECT t1.*, t2.* FROM `streams_options` t1, `streams_arguments` t2 WHERE t1.stream_id = ? AND t1.argument_id = t2.id', $rStreamID); - $rStream['stream_arguments'] = self::$db->get_rows(); - - list($rStreamSource) = json_decode($rStream['stream_info']['stream_source'], true); - if (substr($rStreamSource, 0, 2) == 's:') { - $rMovieSource = explode(':', $rStreamSource, 3); - $rMovieServerID = $rMovieSource[1]; - if ($rMovieServerID != SERVER_ID) { - $rMoviePath = self::$rServers[$rMovieServerID]['api_url'] . '&action=getFile&filename=' . urlencode($rMovieSource[2]); - } else { - $rMoviePath = $rMovieSource[2]; - } - $rProtocol = null; - } else { - if (substr($rStreamSource, 0, 1) == '/') { - $rMovieServerID = SERVER_ID; - $rMoviePath = $rStreamSource; - $rProtocol = null; - } else { - $rProtocol = substr($rStreamSource, 0, strpos($rStreamSource, '://')); - $rMoviePath = str_replace(' ', '%20', $rStreamSource); - $rFetchOptions = implode(' ', self::getArguments($rStream['stream_arguments'], $rProtocol, 'fetch')); - } - } - - // If symlink movie - if ((isset($rMovieServerID) && $rMovieServerID == SERVER_ID || file_exists($rMoviePath)) && $rStream['stream_info']['movie_symlink'] == 1) { - $rFFMPEG = 'ln -sfn ' . escapeshellarg($rMoviePath) . ' ' . VOD_PATH . intval($rStreamID) . '.' . escapeshellcmd(pathinfo($rMoviePath)['extension']) . ' >/dev/null 2>/dev/null & echo $! > ' . VOD_PATH . intval($rStreamID) . '_.pid'; - } else { - // subtitle import + metadata (คงไว้) - $rSubtitles = json_decode($rStream['stream_info']['movie_subtitles'], true); - $rSubtitlesImport = ''; - $rSubtitlesMetadata = ''; - if (!empty($rSubtitles) && !empty($rSubtitles['files']) && is_array($rSubtitles['files'])) { - for ($i = 0; $i < count($rSubtitles['files']); $i++) { - $rSubtitleFile = escapeshellarg($rSubtitles['files'][$i]); - $rInputCharset = escapeshellarg($rSubtitles['charset'][$i]); - if ($rSubtitles['location'] == SERVER_ID) { - $rSubtitlesImport .= '-sub_charenc ' . $rInputCharset . ' -i ' . $rSubtitleFile . ' '; - } else { - $rSubtitlesImport .= '-sub_charenc ' . $rInputCharset . ' -i "' . self::$rServers[$rSubtitles['location']]['api_url'] . '&action=getFile&filename=' . urlencode($rSubtitleFile) . '" '; - } - for ($i = 0; $i < count($rSubtitles['files']); $i++) { - $rSubtitlesMetadata .= '-map ' . ($i + 1) . ' -metadata:s:s:' . $i . ' title=' . escapeshellcmd($rSubtitles['names'][$i]) . ' -metadata:s:s:' . $i . ' language=' . escapeshellcmd($rSubtitles['names'][$i]) . ' '; - } - } - } - - $rReadNative = ($rStream['stream_info']['read_native'] == 1 ? '-re' : ''); - if ($rStream['stream_info']['enable_transcode'] == 1) { - if ($rStream['stream_info']['transcode_profile_id'] == -1) { - $rDecoded = json_decode($rStream['stream_info']['transcode_attributes'], true); - $rStream['stream_info']['transcode_attributes'] = array_merge(self::getArguments($rStream['stream_arguments'], $rProtocol, 'transcode'), (is_array($rDecoded) ? $rDecoded : array())); - } else { - $rDecoded = json_decode($rStream['stream_info']['profile_options'], true); - $rStream['stream_info']['transcode_attributes'] = (is_array($rDecoded) ? $rDecoded : array()); - } - } else { - $rStream['stream_info']['transcode_attributes'] = array(); - } - // Logo overlay - $rLogoOptions = ''; - if (isset($rStream['stream_info']['transcode_attributes'][16]) && !$rLoopback) { - $rAttr = $rStream['stream_info']['transcode_attributes']; - $rLogoPath = $rAttr[16]['val']; - $rPos = (isset($rAttr[16]['pos']) && $rAttr[16]['pos'] !== '10:10') ? $rAttr[16]['pos'] : '10:main_h-overlay_h-10'; - - // Reconstruct filter chain to ensure fixed logo size - $rChain = array(); - $rBase = '[0:v]'; - - // Handle Yadif (ID 17) and Video Scaling (ID 9) - $rVideoFilters = array(); - if (isset($rAttr[17])) { - $rVideoFilters[] = 'yadif'; - } - if (isset($rAttr[9]['val']) && strlen($rAttr[9]['val']) > 0) { - $rVideoFilters[] = 'scale=' . $rAttr[9]['val']; - } - - if (!empty($rVideoFilters)) { - $rChain[] = $rBase . implode(',', $rVideoFilters) . '[bg]'; - $rBase = '[bg]'; - } - - // Scale logo to fixed width 250px (keep aspect ratio) - $rChain[] = '[1:v]scale=250:-1[logo]'; - - // Overlay - $rChain[] = $rBase . '[logo]overlay=' . $rPos; - - $rLogoOptions = '-i ' . escapeshellarg($rLogoPath) . ' -filter_complex "' . implode('; ', $rChain) . '"'; - unset($rStream['stream_info']['transcode_attributes'][16]); - } - $rGPUOptions = (isset($rStream['stream_info']['transcode_attributes']['gpu']) ? $rStream['stream_info']['transcode_attributes']['gpu']['cmd'] : ''); - $rInputCodec = ''; - if (!empty($rGPUOptions)) { - $rFFProbeOutput = self::probeStream($rMoviePath); - if (in_array($rFFProbeOutput['codecs']['video']['codec_name'], array('h264', 'hevc', 'mjpeg', 'mpeg1', 'mpeg2', 'mpeg4', 'vc1', 'vp8', 'vp9'))) { - $rInputCodec = '-c:v ' . $rFFProbeOutput['codecs']['video']['codec_name'] . '_cuvid'; - } - } - $rFFMPEG = ((isset($rStream['stream_info']['transcode_attributes']['gpu']) ? self::$rFFMPEG_GPU : self::$rFFMPEG_CPU)) . ' -y -nostdin -hide_banner -loglevel ' . ((self::$rSettings['ffmpeg_warnings'] ? 'warning' : 'error')) . ' -err_detect ignore_err {GPU} {FETCH_OPTIONS} -fflags +genpts -async 1 {READ_NATIVE} -i {STREAM_SOURCE} {LOGO} ' . $rSubtitlesImport; - $rMap = '-map 0 -copy_unknown '; - if (!empty($rStream['stream_info']['custom_map'])) { - $rMap = escapeshellcmd($rStream['stream_info']['custom_map']) . ' -copy_unknown '; - } else { - if ($rStream['stream_info']['remove_subtitles'] == 1) { - $rMap = '-map 0:a -map 0:v'; - } - } - if (!array_key_exists('-acodec', $rStream['stream_info']['transcode_attributes'])) { - $rStream['stream_info']['transcode_attributes']['-acodec'] = 'copy'; - } - if (!array_key_exists('-vcodec', $rStream['stream_info']['transcode_attributes'])) { - $rStream['stream_info']['transcode_attributes']['-vcodec'] = 'copy'; - } - if ($rStream['stream_info']['target_container'] == 'mp4') { - $rStream['stream_info']['transcode_attributes']['-scodec'] = 'mov_text'; - } elseif ($rStream['stream_info']['target_container'] == 'mkv') { - $rStream['stream_info']['transcode_attributes']['-scodec'] = 'srt'; - } else { - $rStream['stream_info']['transcode_attributes']['-scodec'] = 'copy'; - } - $rOutputs = array(); - $rOutputs[$rStream['stream_info']['target_container']] = '-movflags +faststart -dn ' . $rMap . ' -ignore_unknown ' . $rSubtitlesMetadata . ' ' . VOD_PATH . intval($rStreamID) . '.' . escapeshellcmd($rStream['stream_info']['target_container']); - foreach ($rOutputs as $rOutputCommand) { - $rFFMPEG .= implode(' ', self::parseTranscode($rStream['stream_info']['transcode_attributes'])) . ' '; - $rFFMPEG .= $rOutputCommand; - } - $rFFMPEG .= ' >/dev/null 2>' . VOD_PATH . intval($rStreamID) . '.errors & echo $! > ' . VOD_PATH . intval($rStreamID) . '_.pid'; - $rFFMPEG = str_replace(array('{GPU}', '{INPUT_CODEC}', '{LOGO}', '{FETCH_OPTIONS}', '{STREAM_SOURCE}', '{READ_NATIVE}'), array($rGPUOptions, $rInputCodec, $rLogoOptions, (empty($rFetchOptions) ? '' : $rFetchOptions), escapeshellarg($rMoviePath), (empty($rStream['stream_info']['custom_ffmpeg']) ? $rReadNative : '')), $rFFMPEG); - } - shell_exec($rFFMPEG); - file_put_contents(VOD_PATH . $rStreamID . '_.ffmpeg', $rFFMPEG); - $rPID = intval(file_get_contents(VOD_PATH . $rStreamID . '_.pid')); - self::$db->query('UPDATE `streams_servers` SET `to_analyze` = 1,`stream_started` = ?,`stream_status` = 0,`pid` = ? WHERE `stream_id` = ? AND `server_id` = ?', time(), $rPID, $rStreamID, SERVER_ID); - self::updateStream($rStreamID); - return $rPID; - } - return false; - } - return false; + return StreamProcess::startMovie(self::$db, self::$rSettings, self::$rServers, self::$rFFMPEG_CPU, self::$rFFMPEG_GPU, $rStreamID); } public static function fixCookie($rCookie) { $rPath = false; @@ -1358,57 +749,10 @@ class CoreUtilities { return $rCookie; } public static function startLLOD($rStreamID, $rStreamInfo, $rStreamArguments, $rForceSource = null) { - shell_exec('rm -f ' . STREAMS_PATH . intval($rStreamID) . '_*.ts'); - if (file_exists(STREAMS_PATH . $rStreamID . '_.pid')) { - unlink(STREAMS_PATH . $rStreamID . '_.pid'); - } - $rSources = ($rForceSource ? array($rForceSource) : json_decode($rStreamInfo['stream_source'], true)); - $rArgumentMap = array(); - foreach ($rStreamArguments as $rStreamArgument) { - $rArgumentMap[$rStreamArgument['argument_key']] = array('value' => $rStreamArgument['value'], 'argument_default_value' => $rStreamArgument['argument_default_value']); - } - shell_exec(PHP_BIN . ' ' . CLI_PATH . 'llod.php ' . intval($rStreamID) . ' "' . base64_encode(json_encode($rSources)) . '" "' . base64_encode(json_encode($rArgumentMap)) . '" >/dev/null 2>/dev/null & echo $! > ' . STREAMS_PATH . intval($rStreamID) . '_.pid'); - $rPID = intval(file_get_contents(STREAMS_PATH . $rStreamID . '_.pid')); - $rKey = openssl_random_pseudo_bytes(16); - file_put_contents(STREAMS_PATH . $rStreamID . '_.key', $rKey); - $rIVSize = openssl_cipher_iv_length('AES-128-CBC'); - $rIV = openssl_random_pseudo_bytes($rIVSize); - file_put_contents(STREAMS_PATH . $rStreamID . '_.iv', $rIV); - self::$db->query('UPDATE `streams_servers` SET `delay_available_at` = ?,`to_analyze` = 0,`stream_started` = ?,`stream_info` = ?,`stream_status` = 2,`pid` = ?,`progress_info` = ?,`current_source` = ? WHERE `stream_id` = ? AND `server_id` = ?', null, time(), null, $rPID, json_encode(array()), $rSources[0], $rStreamID, SERVER_ID); - self::updateStream($rStreamID); - return array('main_pid' => $rPID, 'stream_source' => $rSources[0], 'delay_enabled' => false, 'parent_id' => 0, 'delay_start_at' => null, 'playlist' => STREAMS_PATH . $rStreamID . '_.m3u8', 'transcode' => false, 'offset' => 0); + return StreamProcess::startLLOD(self::$db, $rStreamID, $rStreamInfo, $rStreamArguments, $rForceSource); } public static function startLoopback($rStreamID) { - shell_exec('rm -f ' . STREAMS_PATH . intval($rStreamID) . '_*.ts'); - if (!file_exists(STREAMS_PATH . $rStreamID . '_.pid')) { - } else { - unlink(STREAMS_PATH . $rStreamID . '_.pid'); - } - $rStream = array(); - self::$db->query('SELECT * FROM `streams` WHERE direct_source = 0 AND id = ?', $rStreamID); - if (self::$db->num_rows() > 0) { - $rStream['stream_info'] = self::$db->get_row(); - self::$db->query('SELECT * FROM `streams_servers` WHERE stream_id = ? AND `server_id` = ?', $rStreamID, SERVER_ID); - if (self::$db->num_rows() > 0) { - $rStream['server_info'] = self::$db->get_row(); - if ($rStream['server_info']['parent_id'] != 0) { - shell_exec(PHP_BIN . ' ' . CLI_PATH . 'loopback.php ' . intval($rStreamID) . ' ' . intval($rStream['server_info']['parent_id']) . ' >/dev/null 2>/dev/null & echo $! > ' . STREAMS_PATH . intval($rStreamID) . '_.pid'); - $rPID = intval(file_get_contents(STREAMS_PATH . $rStreamID . '_.pid')); - $rKey = openssl_random_pseudo_bytes(16); - file_put_contents(STREAMS_PATH . $rStreamID . '_.key', $rKey); - $rIVSize = openssl_cipher_iv_length('AES-128-CBC'); - $rIV = openssl_random_pseudo_bytes($rIVSize); - file_put_contents(STREAMS_PATH . $rStreamID . '_.iv', $rIV); - self::$db->query('UPDATE `streams_servers` SET `delay_available_at` = ?,`to_analyze` = 0,`stream_started` = ?,`stream_info` = ?,`stream_status` = 2,`pid` = ?,`progress_info` = ?,`current_source` = ? WHERE `stream_id` = ? AND `server_id` = ?', null, time(), null, $rPID, json_encode(array()), $rSources[0], $rStreamID, SERVER_ID); - self::updateStream($rStreamID); - $rLoopURL = (!is_null(self::$rServers[SERVER_ID]['private_url_ip']) && !is_null(self::$rServers[$rStream['server_info']['parent_id']]['private_url_ip']) ? self::$rServers[$rStream['server_info']['parent_id']]['private_url_ip'] : self::$rServers[$rStream['server_info']['parent_id']]['public_url_ip']); - return array('main_pid' => $rPID, 'stream_source' => $rLoopURL . 'admin/live?stream=' . intval($rStreamID) . '&password=' . urlencode(self::$rSettings['live_streaming_pass']) . '&extension=ts', 'delay_enabled' => false, 'parent_id' => 0, 'delay_start_at' => null, 'playlist' => STREAMS_PATH . $rStreamID . '_.m3u8', 'transcode' => false, 'offset' => 0); - } - return 0; - } - return false; - } - return false; + return StreamProcess::startLoopback(self::$db, self::$rSettings, self::$rServers, $rStreamID); } /** @@ -1432,548 +776,7 @@ class CoreUtilities { * @return array|false|int Returns array with stream details on success, false on failure, or 0 when stream is empty/invalid */ public static function startStream($rStreamID, $rFromCache = false, $rForceSource = null, $rLLOD = false, $rStartPos = 0) { - if (file_exists(STREAMS_PATH . $rStreamID . '_.pid')) { - unlink(STREAMS_PATH . $rStreamID . '_.pid'); - } - - $rStream = array(); - self::$db->query('SELECT * FROM `streams` t1 INNER JOIN `streams_types` t2 ON t2.type_id = t1.type AND t2.live = 1 LEFT JOIN `profiles` t4 ON t1.transcode_profile_id = t4.profile_id WHERE t1.direct_source = 0 AND t1.id = ?', $rStreamID); - - if (self::$db->num_rows() > 0) { - $rStream['stream_info'] = self::$db->get_row(); - self::$db->query('SELECT * FROM `streams_servers` WHERE stream_id = ? AND `server_id` = ?', $rStreamID, SERVER_ID); - - if (self::$db->num_rows() > 0) { - $rStream['server_info'] = self::$db->get_row(); - self::$db->query('SELECT t1.*, t2.* FROM `streams_options` t1, `streams_arguments` t2 WHERE t1.stream_id = ? AND t1.argument_id = t2.id', $rStreamID); - $rStream['stream_arguments'] = self::$db->get_rows(); - - if ($rStream['server_info']['on_demand'] == 1) { - $rProbesize = intval($rStream['stream_info']['probesize_ondemand']); - $rAnalyseDuration = '10000000'; - } else { - $rAnalyseDuration = abs(intval(self::$rSettings['stream_max_analyze'])); - $rProbesize = abs(intval(self::$rSettings['probesize'])); - } - - $rTimeout = intval($rAnalyseDuration / 1000000) + self::$rSettings['probe_extra_wait']; - $rFFProbee = 'timeout ' . $rTimeout . ' ' . self::$rFFPROBE . ' {FETCH_OPTIONS} -probesize ' . $rProbesize . ' -analyzeduration ' . $rAnalyseDuration . ' {CONCAT} -i {STREAM_SOURCE} -v quiet -print_format json -show_streams -show_format'; - $rFetchOptions = array(); - $rLoopback = false; - $rOffset = 0; - - if (!$rStream['server_info']['parent_id']) { - if ($rStream['stream_info']['type_key'] == 'created_live') { - $rSources = array(CREATED_PATH . $rStreamID . '_.list'); - - if ($rStartPos > 0) { - $rCCOutput = array(); - $rCCDuration = array(); - $rCCInfo = json_decode($rStream['server_info']['cc_info'], true); - - foreach ($rCCInfo as $rItem) { - $rCCDuration[$rItem['path']] = intval(explode('.', $rItem['seconds'])[0]); - } - $rTimer = 0; - $rValid = true; - - foreach (explode("\n", file_get_contents(CREATED_PATH . $rStreamID . '_.list')) as $rItem) { - list($rPath) = explode("'", explode("file '", $rItem)[1]); - - if ($rPath) { - if ($rCCDuration[$rPath]) { - $rDuration = $rCCDuration[$rPath]; - - if ($rTimer <= $rStartPos && $rStartPos < $rTimer + $rDuration) { - $rOffset = $rTimer; - $rCCOutput[] = $rPath; - } else { - if ($rStartPos < $rTimer + $rDuration) { - $rCCOutput[] = $rPath; - } - } - - $rTimer += $rDuration; - } else { - $rValid = false; - } - } - } - - if ($rValid) { - $rSources = array(CREATED_PATH . $rStreamID . '_.tlist'); - $rTList = ''; - - foreach ($rCCOutput as $rItem) { - $rTList .= "file '" . $rItem . "'" . "\n"; - } - file_put_contents(CREATED_PATH . $rStreamID . '_.tlist', $rTList); - } - } - } else { - $rSources = json_decode($rStream['stream_info']['stream_source'], true); - } - - if (count($rSources) > 0) { - if (!empty($rForceSource)) { - $rSources = array($rForceSource); - } else { - if (self::$rSettings['priority_backup'] != 1) { - if (!empty($rStream['server_info']['current_source'])) { - $k = array_search($rStream['server_info']['current_source'], $rSources); - - if ($k !== false) { - $i = 0; - - while ($i <= $k) { - $rTemp = $rSources[$i]; - unset($rSources[$i]); - array_push($rSources, $rTemp); - $i++; - } - $rSources = array_values($rSources); - } - } - } - } - } - } else { - $rLoopback = true; - - if ($rStream['server_info']['on_demand']) { - $rLLOD = true; - } - - $rLoopURL = (!is_null(self::$rServers[SERVER_ID]['private_url_ip']) && !is_null(self::$rServers[$rStream['server_info']['parent_id']]['private_url_ip']) ? self::$rServers[$rStream['server_info']['parent_id']]['private_url_ip'] : self::$rServers[$rStream['server_info']['parent_id']]['public_url_ip']); - $rSources = array($rLoopURL . 'admin/live?stream=' . intval($rStreamID) . '&password=' . urlencode(self::$rSettings['live_streaming_pass']) . '&extension=ts'); - } - - if ($rStream['stream_info']['type_key'] == 'created_live' && file_exists(CREATED_PATH . $rStreamID . '_.info')) { - self::$db->query('UPDATE `streams_servers` SET `cc_info` = ? WHERE `server_id` = ? AND `stream_id` = ?;', file_get_contents(CREATED_PATH . $rStreamID . '_.info'), SERVER_ID, $rStreamID); - } - - if (!$rFromCache) { - self::deleteCache($rSources); - } - - foreach ($rSources as $rSource) { - $rProcessed = false; - $rRealSource = $rSource; - $rStreamSource = self::parseStreamURL($rSource); - echo 'Checking source: ' . $rSource . "\n"; - $rURLInfo = parse_url($rStreamSource); - $rIsXC_VM = ($rLoopback ? true : self::detectXC_VM($rStreamSource)); - - if ($rIsXC_VM && !$rLoopback && self::$rSettings['send_xc_vm_header']) { - foreach (array_keys($rStream['stream_arguments']) as $rID) { - if ($rStream['stream_arguments'][$rID]['argument_key'] == 'headers') { - $rStream['stream_arguments'][$rID]['value'] .= "\r\n" . 'X-XC_VM-Detect:1'; - $rProcessed = true; - } - } - - if (!$rProcessed) { - $rStream['stream_arguments'][] = array('value' => 'X-XC_VM-Detect:1', 'argument_key' => 'headers', 'argument_cat' => 'fetch', 'argument_wprotocol' => 'http', 'argument_type' => 'text', 'argument_cmd' => "-headers '%s" . "\r\n" . "'"); - } - } - - $rProbeArguments = $rStream['stream_arguments']; - - if ($rIsXC_VM && $rStream['server_info']['on_demand'] == 1 && self::$rSettings['request_prebuffer'] == 1) { - foreach (array_keys($rStream['stream_arguments']) as $rID) { - if ($rStream['stream_arguments'][$rID]['argument_key'] == 'headers') { - $rStream['stream_arguments'][$rID]['value'] .= "\r\n" . 'X-XC_VM-Prebuffer:1'; - $rProcessed = true; - } - } - - if (!$rProcessed) { - $rStream['stream_arguments'][] = array('value' => 'X-XC_VM-Prebuffer:1', 'argument_key' => 'headers', 'argument_cat' => 'fetch', 'argument_wprotocol' => 'http', 'argument_type' => 'text', 'argument_cmd' => "-headers '%s" . "\r\n" . "'"); - } - } - - foreach (array_keys($rProbeArguments) as $rID) { - if ($rProbeArguments[$rID]['argument_key'] == 'headers') { - $rProbeArguments[$rID]['value'] .= "\r\n" . 'X-XC_VM-Prebuffer:1'; - $rProcessed = true; - } - } - - if (!$rProcessed) { - $rProbeArguments[] = array('value' => 'X-XC_VM-Prebuffer:1', 'argument_key' => 'headers', 'argument_cat' => 'fetch', 'argument_wprotocol' => 'http', 'argument_type' => 'text', 'argument_cmd' => "-headers '%s" . "\r\n" . "'"); - } - - $rProtocol = strtolower(substr($rStreamSource, 0, strpos($rStreamSource, '://'))); - $rProbeOptions = implode(' ', self::getArguments($rProbeArguments, $rProtocol, 'fetch')); - $rFetchOptions = implode(' ', self::getArguments($rStream['stream_arguments'], $rProtocol, 'fetch')); - - // === SKIP FFPROBE FEATURE === - // Feature for streams with corrupted PMT where ffprobe reports incorrect codecs - // (e.g., AC3 with sample_rate=0, channels=0 when actual audio is AAC ADTS) - $rSkipFFProbe = false; - foreach ($rStream['stream_arguments'] as $rArg) { - if ($rArg['argument_key'] == 'skip_ffprobe' && $rArg['value'] == 1) { - $rSkipFFProbe = true; - break; - } - } - - if ($rSkipFFProbe) { - $rFFProbeOutput = array( - 'codecs' => array( - 'video' => array('codec_name' => 'h264', 'codec_type' => 'video', 'height' => 1080), - 'audio' => array('codec_name' => 'aac', 'codec_type' => 'audio') - ), - 'container' => 'mpegts' - ); - error_log('[XC_VM] Stream ' . $rStreamID . ': FFProbe skipped'); - echo 'Got stream information via skip_ffprobe (assumed h264/aac)' . "\n"; - - // Ensure $rSource is defined for cache after the loop - if (empty($rSource)) { - $rSource = is_array($rSources) && count($rSources) > 0 ? $rSources[0] : $rStreamSource; - } - break; - } - // === END SKIP FFPROBE FEATURE === - - if ($rFromCache && file_exists(CACHE_TMP_PATH . md5($rSource)) && time() - filemtime(CACHE_TMP_PATH . md5($rSource)) <= 300) { - $rFFProbeOutput = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . md5($rStreamSource))); - - if ($rFFProbeOutput && (isset($rFFProbeOutput['streams']) || isset($rFFProbeOutput['codecs']))) { - echo 'Got stream information via cache' . "\n"; - - break; - } - } else { - if ($rFromCache && file_exists(CACHE_TMP_PATH . md5($rSource))) { - $rFromCache = false; - } - } - - if (!($rStream['server_info']['on_demand'] && $rLLOD)) { - if ($rIsXC_VM && self::$rSettings['api_probe']) { - $rProbeURL = $rURLInfo['scheme'] . '://' . $rURLInfo['host'] . ':' . $rURLInfo['port'] . '/probe/' . base64_encode($rURLInfo['path']); - $rFFProbeOutput = json_decode(self::getURL($rProbeURL), true); - - if ($rFFProbeOutput && isset($rFFProbeOutput['codecs'])) { - echo 'Got stream information via API' . "\n"; - - break; - } - } - - $rProbeCmd = str_replace(array('{FETCH_OPTIONS}', '{CONCAT}', '{STREAM_SOURCE}'), array($rProbeOptions, ($rStream['stream_info']['type_key'] == 'created_live' && !$rStream['server_info']['parent_id'] ? '-safe 0 -f concat' : ''), escapeshellarg($rStreamSource)), $rFFProbee); - $rFFProbeOutput = json_decode(shell_exec($rProbeCmd), true); - - if ($rFFProbeOutput && isset($rFFProbeOutput['streams'])) { - echo 'Got stream information via ffprobe' . "\n"; - - break; - } - } - } - if (!($rStream['server_info']['on_demand'] && $rLLOD)) { - if (!isset($rFFProbeOutput['codecs'])) { - $rFFProbeOutput = self::parseFFProbe($rFFProbeOutput); - } - - if (empty($rFFProbeOutput)) { - self::$db->query("UPDATE `streams_servers` SET `progress_info` = '',`to_analyze` = 0,`pid` = -1,`stream_status` = 1 WHERE `server_id` = ? AND `stream_id` = ?", SERVER_ID, $rStreamID); - - return 0; - } - - if (!$rFromCache) { - file_put_contents(CACHE_TMP_PATH . md5($rSource), igbinary_serialize($rFFProbeOutput)); - } - } - - $externalPushJson = $rStream['stream_info']['external_push'] ?? '[]'; - $rExternalPush = json_decode($externalPushJson, true); - $rProgressURL = 'http://127.0.0.1:' . intval(self::$rServers[SERVER_ID]['http_broadcast_port']) . '/progress?stream_id=' . intval($rStreamID); - - if (empty($rStream['stream_info']['custom_ffmpeg'])) { - if ($rLoopback) { - $rOptions = '{FETCH_OPTIONS}'; - } else { - $rOptions = '{GPU} {FETCH_OPTIONS}'; - } - - if ($rStream['stream_info']['stream_all'] == 1) { - $rMap = '-map 0 -copy_unknown '; - } else { - if (!empty($rStream['stream_info']['custom_map'])) { - $rMap = escapeshellcmd($rStream['stream_info']['custom_map']) . ' -copy_unknown '; - } else { - if ($rStream['stream_info']['type_key'] == 'radio_streams') { - $rMap = '-map 0:a? '; - } else { - $rMap = ''; - } - } - } - - if (($rStream['stream_info']['gen_timestamps'] == 1 || empty($rProtocol)) && $rStream['stream_info']['type_key'] != 'created_live') { - $rGenPTS = '-fflags +genpts -async 1'; - } else { - if (is_array($rFFProbeOutput) && isset($rFFProbeOutput['codecs']['audio']['codec_name']) && in_array($rFFProbeOutput['codecs']['audio']['codec_name'], array('ac3', 'eac3')) && self::$rSettings['dts_legacy_ffmpeg']) { - self::$rFFMPEG_CPU = FFMPEG_BIN_40; - self::$rFFPROBE = FFPROBE_BIN_40; - } - - // Use -nofix_dts only for FFmpeg 4.0, newer versions don't support it - $rNoFix = (self::$rFFMPEG_CPU == FFMPEG_BIN_40 ? '-nofix_dts' : ''); - // For newer FFmpeg versions, use equivalent timestamp handling - $rGenPTS = $rNoFix . ' -start_at_zero -copyts -vsync 0 -correct_ts_overflow 0 -avoid_negative_ts disabled -max_interleave_delta 0'; - } - - $container = (isset($rFFProbeOutput) && is_array($rFFProbeOutput)) ? ($rFFProbeOutput['container'] ?? null) : null; - if (empty($rStream['server_info']['parent_id']) && (($rStream['stream_info']['read_native'] == 1) || ($container && stristr($container, 'hls') && self::$rSettings['read_native_hls']) || empty($rProtocol) || ($container && stristr($container, 'mp4')) || ($container && stristr($container, 'matroska')))) { - $rReadNative = '-re'; - } else { - $rReadNative = ''; - } - - - if (!$rStream['server_info']['parent_id'] && $rStream['stream_info']['enable_transcode'] == 1 && $rStream['stream_info']['type_key'] != 'created_live') { - if ($rStream['stream_info']['transcode_profile_id'] == -1) { - $rStream['stream_info']['transcode_attributes'] = array_merge(self::getArguments($rStream['stream_arguments'], $rProtocol, 'transcode'), json_decode($rStream['stream_info']['transcode_attributes'], true)); - } else { - $rStream['stream_info']['transcode_attributes'] = json_decode($rStream['stream_info']['profile_options'], true); - } - } else { - $rStream['stream_info']['transcode_attributes'] = array(); - } - - $rFFMPEG = ((isset($rStream['stream_info']['transcode_attributes']['gpu']) ? self::$rFFMPEG_GPU : self::$rFFMPEG_CPU)) . ' -y -nostdin -hide_banner -loglevel ' . ((self::$rSettings['ffmpeg_warnings'] ? 'warning' : 'error')) . ' -err_detect ignore_err ' . $rOptions . ' {GEN_PTS} {READ_NATIVE} -probesize ' . $rProbesize . ' -analyzeduration ' . $rAnalyseDuration . ' -progress "' . $rProgressURL . '" {CONCAT} -i {STREAM_SOURCE} {LOGO} '; - - if (!array_key_exists('-acodec', $rStream['stream_info']['transcode_attributes'])) { - $rStream['stream_info']['transcode_attributes']['-acodec'] = 'copy'; - } - - if (!array_key_exists('-vcodec', $rStream['stream_info']['transcode_attributes'])) { - $rStream['stream_info']['transcode_attributes']['-vcodec'] = 'copy'; - } - - if (!array_key_exists('-scodec', $rStream['stream_info']['transcode_attributes'])) { - $rStream['stream_info']['transcode_attributes']['-sn'] = ''; - } - } else { - $rStream['stream_info']['transcode_attributes'] = array(); - $rFFMPEG = ((stripos($rStream['stream_info']['custom_ffmpeg'], 'nvenc') !== false ? self::$rFFMPEG_GPU : self::$rFFMPEG_CPU)) . ' -y -nostdin -hide_banner -loglevel ' . ((self::$rSettings['ffmpeg_warnings'] ? 'warning' : 'error')) . ' -progress "' . $rProgressURL . '" ' . $rStream['stream_info']['custom_ffmpeg']; - } - - $rLLODOptions = ($rLLOD && !$rLoopback ? '-fflags nobuffer -flags low_delay -strict experimental' : ''); - $rOutputs = array(); - - if ($rLoopback) { - $rOptions = '{MAP}'; - $rFLVOptions = '{MAP}'; - $rMap = '-map 0 -copy_unknown '; - } else { - $rOptions = '{MAP} {LLOD}'; - $rFLVOptions = '{MAP} {AAC_FILTER}'; - } - - $rKeyFrames = (self::$rSettings['ignore_keyframes'] ? '+split_by_time' : ''); - $rOutputs['mpegts'][] = $rOptions . ' -individual_header_trailer 0 -f hls -hls_time ' . intval(self::$rSegmentSettings['seg_time']) . ' -hls_list_size ' . intval(self::$rSegmentSettings['seg_list_size']) . ' -hls_delete_threshold ' . intval(self::$rSegmentSettings['seg_delete_threshold']) . ' -hls_flags delete_segments+discont_start+omit_endlist' . $rKeyFrames . ' -hls_segment_type mpegts -hls_segment_filename "' . STREAMS_PATH . intval($rStreamID) . '_%d.ts" "' . STREAMS_PATH . intval($rStreamID) . '_.m3u8" '; - - if ($rStream['stream_info']['rtmp_output'] == 1) { - $rOutputs['flv'][] = $rFLVOptions . ' -f flv -flvflags no_duration_filesize rtmp://127.0.0.1:' . intval(self::$rServers[$rStream['server_info']['server_id']]['rtmp_port']) . '/live/' . intval($rStreamID) . '?password=' . urlencode(self::$rSettings['live_streaming_pass']) . ' '; - } - - if (!empty($rExternalPush[SERVER_ID])) { - foreach ($rExternalPush[SERVER_ID] as $rPushURL) { - $rOutputs['flv'][] = $rFLVOptions . ' -f flv -flvflags no_duration_filesize ' . escapeshellarg($rPushURL) . ' '; - } - } - - // Logo overlay - $rLogoOptions = ''; - if (isset($rStream['stream_info']['transcode_attributes'][16]) && !$rLoopback) { - $rAttr = $rStream['stream_info']['transcode_attributes']; - $rLogoPath = $rAttr[16]['val']; - $rPos = (isset($rAttr[16]['pos']) && $rAttr[16]['pos'] !== '10:10') ? $rAttr[16]['pos'] : '10:main_h-overlay_h-10'; - - // Reconstruct filter chain to ensure fixed logo size - $rChain = array(); - $rBase = '[0:v]'; - - // Handle Yadif (ID 17) and Video Scaling (ID 9) - $rVideoFilters = array(); - if (isset($rAttr[17])) { - $rVideoFilters[] = 'yadif'; - } - if (isset($rAttr[9]['val']) && strlen($rAttr[9]['val']) > 0) { - $rVideoFilters[] = 'scale=' . $rAttr[9]['val']; - } - - if (!empty($rVideoFilters)) { - $rChain[] = $rBase . implode(',', $rVideoFilters) . '[bg]'; - $rBase = '[bg]'; - } - - // Scale logo to fixed width 250px (keep aspect ratio) - $rChain[] = '[1:v]scale=250:-1[logo]'; - - // Overlay - $rChain[] = $rBase . '[logo]overlay=' . $rPos; - - $rLogoOptions = '-i ' . escapeshellarg($rLogoPath) . ' -filter_complex "' . implode('; ', $rChain) . '"'; - unset($rStream['stream_info']['transcode_attributes'][16]); - } - - $rGPUOptions = (isset($rStream['stream_info']['transcode_attributes']['gpu']) ? $rStream['stream_info']['transcode_attributes']['gpu']['cmd'] : ''); - $rInputCodec = ''; - - $supportedCodecs = ['h264', 'hevc', 'mjpeg', 'mpeg1', 'mpeg2', 'mpeg4', 'vc1', 'vp8', 'vp9']; - $videoCodec = null; - if (isset($rFFProbeOutput) && is_array($rFFProbeOutput)) { - $videoCodec = $rFFProbeOutput['codecs']['video']['codec_name'] ?? null; - } - - if (!empty($rGPUOptions) && in_array($videoCodec, $supportedCodecs)) { - $rInputCodec = '-c:v ' . $rFFProbeOutput['codecs']['video']['codec_name'] . '_cuvid'; - } - - if (0 >= $rStream['stream_info']['delay_minutes'] || $rStream['server_info']['parent_id']) { - foreach ($rOutputs as $rOutputCommands) { - foreach ($rOutputCommands as $rOutputCommand) { - if (isset($rStream['stream_info']['transcode_attributes']['gpu'])) { - $rFFMPEG .= '-gpu ' . intval($rStream['stream_info']['transcode_attributes']['gpu']['device']) . ' '; - } - - $rFFMPEG .= implode(' ', self::parseTranscode($rStream['stream_info']['transcode_attributes'])) . ' '; - $rFFMPEG .= $rOutputCommand; - } - } - } else { - $rSegmentStart = 0; - $m3u8File = DELAY_PATH . $rStreamID . '_.m3u8'; - $oldM3u8File = DELAY_PATH . intval($rStreamID) . '_.m3u8_old'; - - - if (file_exists($m3u8File)) { - $rFile = file($m3u8File, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - - if (!is_array($rFile) || count($rFile) < 2) { - // The file is empty or unreadable — safe exit - return; - } - - $lastLine = $rFile[count($rFile) - 1]; - $prevLine = $rFile[count($rFile) - 2]; - - if (stristr($lastLine, $rStreamID . '_')) { - if (preg_match('/_(.*?)\.ts/', $lastLine, $rMatches)) { - $rSegmentStart = intval($rMatches[1]) + 1; - } - } else { - if (preg_match('/_(.*?)\.ts/', $prevLine, $rMatches)) { - $rSegmentStart = intval($rMatches[1]) + 1; - } - } - - - if (file_exists($oldM3u8File)) { - file_put_contents($oldM3u8File, file_get_contents($oldM3u8File) . file_get_contents($m3u8File)); - shell_exec("sed -i '/EXTINF\\|.ts/!d' " . escapeshellarg($oldM3u8File)); - } else { - copy($m3u8File, $oldM3u8File); - } - } - - $rFFMPEG .= implode(' ', self::parseTranscode($rStream['stream_info']['transcode_attributes'])) . ' '; - $rFFMPEG .= '{MAP} -individual_header_trailer 0 -f hls -hls_time ' . intval(self::$rSegmentSettings['seg_time']) . ' -hls_list_size ' . intval($rStream['stream_info']['delay_minutes']) * 6 . ' -hls_delete_threshold 4 -start_number ' . $rSegmentStart . ' -hls_flags delete_segments+discont_start+omit_endlist -hls_segment_type mpegts -hls_segment_filename "' . DELAY_PATH . intval($rStreamID) . '_%d.ts" "' . DELAY_PATH . intval($rStreamID) . '_.m3u8" '; - - $rSleepTime = $rStream['stream_info']['delay_minutes'] * 60; - - if ($rSegmentStart > 0) { - $rSleepTime -= ($rSegmentStart - 1) * 10; - - if ($rSleepTime > 0) { - } else { - $rSleepTime = 0; - } - } - } - - $rFFMPEG .= ' >/dev/null 2>>' . STREAMS_PATH . intval($rStreamID) . '.errors & echo $! > ' . STREAMS_PATH . intval($rStreamID) . '_.pid'; - - $ffprobeContainer = (isset($rFFProbeOutput['container']) && is_string($rFFProbeOutput['container'])) ? $rFFProbeOutput['container'] : ''; - - $audioCodec = (isset($rFFProbeOutput['codecs']['audio']['codec_name']) && is_array($rFFProbeOutput['codecs']['audio'])) ? $rFFProbeOutput['codecs']['audio']['codec_name'] : ''; - - $rFFMPEG = str_replace( - ['{FETCH_OPTIONS}', '{GEN_PTS}', '{STREAM_SOURCE}', '{MAP}', '{READ_NATIVE}', '{CONCAT}', '{AAC_FILTER}', '{GPU}', '{INPUT_CODEC}', '{LOGO}', '{LLOD}'], - [ - empty($rStream['stream_info']['custom_ffmpeg']) ? $rFetchOptions : '', - empty($rStream['stream_info']['custom_ffmpeg']) ? $rGenPTS : '', - escapeshellarg($rStreamSource), - empty($rStream['stream_info']['custom_ffmpeg']) ? $rMap : '', - empty($rStream['stream_info']['custom_ffmpeg']) ? $rReadNative : '', - ($rStream['stream_info']['type_key'] == 'created_live' && empty($rStream['server_info']['parent_id']) ? '-safe 0 -f concat' : ''), - (!stristr($ffprobeContainer, 'flv') && $audioCodec === 'aac' && ($rStream['stream_info']['transcode_attributes']['-acodec'] ?? '') === 'copy' ? '-bsf:a aac_adtstoasc' : ''), - $rGPUOptions, - $rInputCodec, - $rLogoOptions, - $rLLODOptions - ], - $rFFMPEG - ); - - - shell_exec($rFFMPEG); - file_put_contents(STREAMS_PATH . $rStreamID . '_.ffmpeg', $rFFMPEG); - $rKey = openssl_random_pseudo_bytes(16); - file_put_contents(STREAMS_PATH . $rStreamID . '_.key', $rKey); - $rIVSize = openssl_cipher_iv_length('AES-128-CBC'); - $rIV = openssl_random_pseudo_bytes($rIVSize); - file_put_contents(STREAMS_PATH . $rStreamID . '_.iv', $rIV); - $rPID = intval(file_get_contents(STREAMS_PATH . $rStreamID . '_.pid')); - - if ($rStream['stream_info']['tv_archive_server_id'] == SERVER_ID) { - shell_exec(PHP_BIN . ' ' . CLI_PATH . 'archive.php ' . intval($rStreamID) . ' >/dev/null 2>/dev/null & echo $!'); - } - - if ($rStream['stream_info']['vframes_server_id'] == SERVER_ID) { - self::startThumbnail($rStreamID); - } - - $rDelayEnabled = 0 < $rStream['stream_info']['delay_minutes'] && !$rStream['server_info']['parent_id']; - $rDelayStartAt = ($rDelayEnabled ? time() + $rSleepTime : 0); - - if ($rStream['stream_info']['enable_transcode']) { - $rFFProbeOutput = array(); - } - - $rCompatible = 0; - $rAudioCodec = $rVideoCodec = $rResolution = null; - - if (isset($rFFProbeOutput) && is_array($rFFProbeOutput) && isset($rFFProbeOutput['codecs']) && is_array($rFFProbeOutput['codecs'])) { - $rCompatible = intval(self::checkCompatibility($rFFProbeOutput)); - $rAudioCodec = ($rFFProbeOutput['codecs']['audio']['codec_name'] ?: null); - $rVideoCodec = ($rFFProbeOutput['codecs']['video']['codec_name'] ?: null); - $rResolution = ($rFFProbeOutput['codecs']['video']['height'] ?: null); - - if ($rResolution) { - $rResolution = self::getNearest(array(240, 360, 480, 576, 720, 1080, 1440, 2160), $rResolution); - } - } - - $rFFProbeOutputSafe = isset($rFFProbeOutput) && is_array($rFFProbeOutput) ? $rFFProbeOutput : []; - self::$db->query('UPDATE `streams_servers` SET `delay_available_at` = ?,`to_analyze` = 0,`stream_started` = ?,`stream_info` = ?,`audio_codec` = ?, `video_codec` = ?, `resolution` = ?,`compatible` = ?,`stream_status` = 2,`pid` = ?,`progress_info` = ?,`current_source` = ? WHERE `stream_id` = ? AND `server_id` = ?', $rDelayStartAt, time(), json_encode($rFFProbeOutputSafe), $rAudioCodec, $rVideoCodec, $rResolution, $rCompatible, $rPID, json_encode(array()), $rSource, $rStreamID, SERVER_ID); - self::updateStream($rStreamID); - $rPlaylist = (!$rDelayEnabled ? STREAMS_PATH . $rStreamID . '_.m3u8' : DELAY_PATH . $rStreamID . '_.m3u8'); - - return array('main_pid' => $rPID, 'stream_source' => $rRealSource, 'delay_enabled' => $rDelayEnabled, 'parent_id' => $rStream['server_info']['parent_id'], 'delay_start_at' => $rDelayStartAt, 'playlist' => $rPlaylist, 'transcode' => $rStream['stream_info']['enable_transcode'], 'offset' => $rOffset); - } else { - return false; - } - } else { - return false; - } + return StreamProcess::startStream(self::$db, self::$rSettings, self::$rServers, self::$rSegmentSettings, self::$rFFMPEG_CPU, self::$rFFMPEG_GPU, self::$rFFPROBE, $rStreamID, $rFromCache, $rForceSource, $rLLOD, $rStartPos); } public static function getArguments($rArguments, $rProtocol, $rType) { @@ -3041,54 +1844,22 @@ class CoreUtilities { return BouquetMapper::getMapEntry($rStreamID); } public static function updateStream($rStreamID, $rForce = false) { - if (self::$rCached) { - self::$db->query('SELECT COUNT(*) AS `count` FROM `signals` WHERE `server_id` = ? AND `cache` = 1 AND `custom_data` = ?;', self::getMainID(), json_encode(array('type' => 'update_stream', 'id' => $rStreamID))); - if (self::$db->get_row()['count'] != 0) { - } else { - self::$db->query('INSERT INTO `signals`(`server_id`, `cache`, `time`, `custom_data`) VALUES(?, 1, ?, ?);', self::getMainID(), time(), json_encode(array('type' => 'update_stream', 'id' => $rStreamID))); - } - return true; - } - return false; + return StreamProcess::updateStream(self::$db, self::$rCached, self::getMainID(), $rStreamID, $rForce); } public static function updateStreams($rStreamIDs) { - if (self::$rCached) { - self::$db->query('SELECT COUNT(*) AS `count` FROM `signals` WHERE `server_id` = ? AND `cache` = 1 AND `custom_data` = ?;', self::getMainID(), json_encode(array('type' => 'update_streams', 'id' => $rStreamIDs))); - if (self::$db->get_row()['count'] != 0) { - } else { - self::$db->query('INSERT INTO `signals`(`server_id`, `cache`, `time`, `custom_data`) VALUES(?, 1, ?, ?);', self::getMainID(), time(), json_encode(array('type' => 'update_streams', 'id' => $rStreamIDs))); - } - return true; - } - return false; + return StreamProcess::updateStreams(self::$db, self::$rCached, self::getMainID(), $rStreamIDs); } public static function deleteLine($rUserID, $rForce = false) { - self::updateLine($rUserID, $rForce); + LineService::deleteLineSignal(self::$db, self::$rCached, self::getMainID(), $rUserID, $rForce); } public static function deleteLines($rUserIDs, $rForce = false) { - self::updateLines($rUserIDs); + LineService::deleteLinesSignal(self::$db, self::$rCached, self::getMainID(), $rUserIDs, $rForce); } public static function updateLine($rUserID, $rForce = false) { - if (self::$rCached) { - self::$db->query('SELECT COUNT(*) AS `count` FROM `signals` WHERE `server_id` = ? AND `cache` = 1 AND `custom_data` = ?;', self::getMainID(), json_encode(array('type' => 'update_line', 'id' => $rUserID))); - if (self::$db->get_row()['count'] != 0) { - } else { - self::$db->query('INSERT INTO `signals`(`server_id`, `cache`, `time`, `custom_data`) VALUES(?, 1, ?, ?);', self::getMainID(), time(), json_encode(array('type' => 'update_line', 'id' => $rUserID))); - } - return true; - } - return false; + return LineService::updateLineSignal(self::$db, self::$rCached, self::getMainID(), $rUserID, $rForce); } public static function updateLines($rUserIDs) { - if (self::$rCached) { - self::$db->query('SELECT COUNT(*) AS `count` FROM `signals` WHERE `server_id` = ? AND `cache` = 1 AND `custom_data` = ?;', self::getMainID(), json_encode(array('type' => 'update_lines', 'id' => $rUserIDs))); - if (self::$db->get_row()['count'] != 0) { - } else { - self::$db->query('INSERT INTO `signals`(`server_id`, `cache`, `time`, `custom_data`) VALUES(?, 1, ?, ?);', self::getMainID(), time(), json_encode(array('type' => 'update_lines', 'id' => $rUserIDs))); - } - return true; - } - return false; + return LineService::updateLinesSignal(self::$db, self::$rCached, self::getMainID(), $rUserIDs); } public static function getMainID() { return ConnectionTracker::getMainID(self::$rServers); @@ -3387,33 +2158,13 @@ class CoreUtilities { return json_decode(shell_exec(BIN_PATH . 'tsinfo ' . escapeshellarg($rFilename)), true); } public static function getEPG($rStreamID, $rStartDate = null, $rFinishDate = null, $rByID = false) { - $rReturn = array(); - $rData = (file_exists(EPG_PATH . 'stream_' . $rStreamID) ? igbinary_unserialize(file_get_contents(EPG_PATH . 'stream_' . $rStreamID)) : array()); - foreach ($rData as $rItem) { - if ($rStartDate && !($rStartDate < $rItem['end'] && $rItem['start'] < $rFinishDate)) { - } else { - if ($rByID) { - $rReturn[$rItem['id']] = $rItem; - } else { - $rReturn[] = $rItem; - } - } - } - return $rReturn; + return EpgRepository::getStreamEpg($rStreamID, $rStartDate, $rFinishDate, $rByID); } public static function getEPGs($rStreamIDs, $rStartDate = null, $rFinishDate = null) { - $rReturn = array(); - foreach ($rStreamIDs as $rStreamID) { - $rReturn[$rStreamID] = self::getEPG($rStreamID, $rStartDate, $rFinishDate); - } - return $rReturn; + return EpgRepository::getStreamsEpg($rStreamIDs, $rStartDate, $rFinishDate); } public static function getProgramme($rStreamID, $rProgrammeID) { - $rData = self::getEPG($rStreamID, null, null, true); - if (!isset($rData[$rProgrammeID])) { - } else { - return $rData[$rProgrammeID]; - } + return EpgRepository::getProgramme($rStreamID, $rProgrammeID); } public static function getProxies($rServerID, $rOnline = true) { return ConnectionTracker::getProxies(self::$rServers, $rServerID, $rOnline); diff --git a/src/includes/StreamingUtilities.php b/src/includes/StreamingUtilities.php index b54bff15..b5cac2d6 100644 --- a/src/includes/StreamingUtilities.php +++ b/src/includes/StreamingUtilities.php @@ -735,220 +735,16 @@ class StreamingUtilities { return false; } public static function getUserInfo($rUserID = null, $rUsername = null, $rPassword = null, $rGetChannelIDs = false, $rGetConnections = false, $rIP = '') { - $rUserInfo = null; - if (self::$rCached) { - if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { - if (self::$rSettings['case_sensitive_line']) { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_t_' . $rUsername)); - } else { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_t_' . strtolower($rUsername))); - } - } else { - if (!empty($rUsername) && !empty($rPassword)) { - if (self::$rSettings['case_sensitive_line']) { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_c_' . $rUsername . '_' . $rPassword)); - } else { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_c_' . strtolower($rUsername) . '_' . strtolower($rPassword))); - } - } else { - if (!empty($rUserID)) { - } else { - return false; - } - } - } - if (!$rUserID) { - } else { - $rUserInfo = igbinary_unserialize(file_get_contents(LINES_TMP_PATH . 'line_i_' . $rUserID)); - } - } else { - if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { - self::$db->query('SELECT * FROM `lines` WHERE `is_mag` = 0 AND `is_e2` = 0 AND `access_token` = ? AND LENGTH(`access_token`) = 32', $rUsername); - } else { - if (!empty($rUsername) && !empty($rPassword)) { - self::$db->query('SELECT `lines`.*, `mag_devices`.`token` AS `mag_token` FROM `lines` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines`.`id` WHERE `username` = ? AND `password` = ? LIMIT 1', $rUsername, $rPassword); - } else { - if (!empty($rUserID)) { - self::$db->query('SELECT `lines`.*, `mag_devices`.`token` AS `mag_token` FROM `lines` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines`.`id` WHERE `id` = ?', $rUserID); - } else { - return false; - } - } - } - if (0 >= self::$db->num_rows()) { - } else { - $rUserInfo = self::$db->get_row(); - } - } - if (!$rUserInfo) { - return false; - } - if (!self::$rCached) { - } else { - if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { - if ($rUsername == $rUserInfo['access_token']) { - } else { - return false; - } - } else { - if (empty($rUsername) || empty($rPassword)) { - } else { - if (!($rUsername != $rUserInfo['username'] || $rPassword != $rUserInfo['password'])) { - } else { - return false; - } - } - } - } - if (!(self::$rSettings['county_override_1st'] == 1 && empty($rUserInfo['forced_country']) && !empty($rIP) && $rUserInfo['max_connections'] == 1)) { - } else { - $rUserInfo['forced_country'] = self::getIPInfo($rIP)['registered_country']['iso_code']; - if (self::$rCached) { - self::setSignal('forced_country/' . $rUserInfo['id'], $rUserInfo['forced_country']); - } else { - self::$db->query('UPDATE `lines` SET `forced_country` = ? WHERE `id` = ?', $rUserInfo['forced_country'], $rUserInfo['id']); - } - } - - $allowedIPS = json_decode($rUserInfo['allowed_ips'], true); - $allowedUa = json_decode($rUserInfo['allowed_ua'], true); - $rUserInfo['bouquet'] = json_decode($rUserInfo['bouquet'], true); - $rUserInfo['allowed_ips'] = array_filter(array_map('trim', is_array($allowedIPS) ? $allowedIPS : [])); - $rUserInfo['allowed_ua'] = array_filter(array_map('trim', is_array($allowedUa) ? $allowedUa : [])); - $rUserInfo['allowed_outputs'] = array_map('intval', json_decode($rUserInfo['allowed_outputs'], true)); - $rUserInfo['output_formats'] = array(); - if (self::$rCached) { - foreach (igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'output_formats')) as $rRow) { - if (!in_array(intval($rRow['access_output_id']), $rUserInfo['allowed_outputs'])) { - } else { - $rUserInfo['output_formats'][] = $rRow['output_key']; - } - } - } else { - self::$db->query('SELECT `access_output_id`, `output_key` FROM `output_formats`;'); - foreach (self::$db->get_rows() as $rRow) { - if (!in_array(intval($rRow['access_output_id']), $rUserInfo['allowed_outputs'])) { - } else { - $rUserInfo['output_formats'][] = $rRow['output_key']; - } - } - } - $rUserInfo['con_isp_name'] = null; - $rUserInfo['isp_violate'] = 0; - $rUserInfo['isp_is_server'] = 0; - if (self::$rSettings['show_isps'] != 1 || empty($rIP)) { - } else { - $rISPLock = self::getISP($rIP); - if (!is_array($rISPLock)) { - } else { - if (empty($rISPLock['isp'])) { - } else { - $rUserInfo['con_isp_name'] = $rISPLock['isp']; - $rUserInfo['isp_asn'] = $rISPLock['autonomous_system_number']; - $rUserInfo['isp_violate'] = self::checkISP($rUserInfo['con_isp_name']); - if (self::$rSettings['block_svp'] != 1) { - } else { - $rUserInfo['isp_is_server'] = intval(self::checkServer($rUserInfo['isp_asn'])); - } - } - } - if (!(!empty($rUserInfo['con_isp_name']) && self::$rSettings['enable_isp_lock'] == 1 && $rUserInfo['is_stalker'] == 0 && $rUserInfo['is_isplock'] == 1 && !empty($rUserInfo['isp_desc']) && strtolower($rUserInfo['con_isp_name']) != strtolower($rUserInfo['isp_desc']))) { - } else { - $rUserInfo['isp_violate'] = 1; - } - if (!($rUserInfo['isp_violate'] == 0 && strtolower($rUserInfo['con_isp_name']) != strtolower($rUserInfo['isp_desc']))) { - } else { - if (self::$rCached) { - self::setSignal('isp/' . $rUserInfo['id'], json_encode(array($rUserInfo['con_isp_name'], $rUserInfo['isp_asn']))); - } else { - self::$db->query('UPDATE `lines` SET `isp_desc` = ?, `as_number` = ? WHERE `id` = ?', $rUserInfo['con_isp_name'], $rUserInfo['isp_asn'], $rUserInfo['id']); - } - } - } - if (!$rGetChannelIDs) { - } else { - $rLiveIDs = $rVODIDs = $rRadioIDs = $rCategoryIDs = $rChannelIDs = $rSeriesIDs = array(); - foreach ($rUserInfo['bouquet'] as $rID) { - if (!isset(self::$rBouquets[$rID]['streams'])) { - } else { - $rChannelIDs = array_merge($rChannelIDs, self::$rBouquets[$rID]['streams']); - } - if (!isset(self::$rBouquets[$rID]['series'])) { - } else { - $rSeriesIDs = array_merge($rSeriesIDs, self::$rBouquets[$rID]['series']); - } - if (!isset(self::$rBouquets[$rID]['channels'])) { - } else { - $rLiveIDs = array_merge($rLiveIDs, self::$rBouquets[$rID]['channels']); - } - if (!isset(self::$rBouquets[$rID]['movies'])) { - } else { - $rVODIDs = array_merge($rVODIDs, self::$rBouquets[$rID]['movies']); - } - if (!isset(self::$rBouquets[$rID]['radios'])) { - } else { - $rRadioIDs = array_merge($rRadioIDs, self::$rBouquets[$rID]['radios']); - } - } - $rUserInfo['channel_ids'] = array_map('intval', array_unique($rChannelIDs)); - $rUserInfo['series_ids'] = array_map('intval', array_unique($rSeriesIDs)); - $rUserInfo['vod_ids'] = array_map('intval', array_unique($rVODIDs)); - $rUserInfo['live_ids'] = array_map('intval', array_unique($rLiveIDs)); - $rUserInfo['radio_ids'] = array_map('intval', array_unique($rRadioIDs)); - } - $rAllowedCategories = array(); - $rCategoryMap = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'category_map')); - foreach ($rUserInfo['bouquet'] as $rID) { - $rAllowedCategories = array_merge($rAllowedCategories, ($rCategoryMap[$rID] ?: array())); - } - $rUserInfo['category_ids'] = array_values(array_unique($rAllowedCategories)); - return $rUserInfo; + return UserRepository::getStreamingUserInfo(self::$db, self::$rSettings, self::$rCached, self::$rBouquets, $rUserID, $rUsername, $rPassword, $rGetChannelIDs, $rGetConnections, $rIP, array('getIPInfo' => array(self::class, 'getIPInfo'), 'setSignal' => array(self::class, 'setSignal'), 'getISP' => array(self::class, 'getISP'), 'checkISP' => array(self::class, 'checkISP'), 'checkServer' => array(self::class, 'checkServer'))); } public static function setSignal($rKey, $rData) { file_put_contents(SIGNALS_TMP_PATH . 'cache_' . md5($rKey), json_encode(array($rKey, $rData))); } public static function validateHMAC($rHMAC, $rExpiry, $rStreamID, $rExtension, $rIP = '', $rMACIP = '', $rIdentifier = '', $rMaxConnections = 0) { - if (0 < strlen($rIP) && 0 < strlen($rMACIP)) { - if ($rIP != $rMACIP) { - return null; - } - } - $rKeyID = null; - if (self::$rCached) { - $rKeys = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'hmac_keys')); - } else { - $rKeys = array(); - self::$db->query('SELECT `id`, `key` FROM `hmac_keys` WHERE `enabled` = 1;'); - foreach (self::$db->get_rows() as $rKey) { - $rKeys[] = $rKey; - } - } - foreach ($rKeys as $rKey) { - $rResult = hash_hmac('sha256', (string) $rStreamID . '##' . $rExtension . '##' . $rExpiry . '##' . $rMACIP . '##' . $rIdentifier . '##' . $rMaxConnections, StreamingUtilities::decryptData($rKey['key'], StreamingUtilities::$rSettings['live_streaming_pass'], OPENSSL_EXTRA)); - - if (md5($rResult) == md5($rHMAC)) { - $rKeyID = $rKey['id']; - break; - } - } - return $rKeyID; + return HMACValidator::validate(self::$db, self::$rSettings, self::$rCached, $rHMAC, $rExpiry, $rStreamID, $rExtension, $rIP, $rMACIP, $rIdentifier, $rMaxConnections, array('StreamingUtilities', 'decryptData')); } public static function checkBlockedUAs($rUserAgent, $rReturn = false) { - $rUserAgent = strtolower($rUserAgent); - foreach (self::$rBlockedUA as $rKey => $rBlocked) { - if ($rBlocked['exact_match'] == 1) { - if ($rBlocked['blocked_ua'] != $rUserAgent) { - } else { - return true; - } - } else { - if (!stristr($rUserAgent, $rBlocked['blocked_ua'])) { - } else { - return true; - } - } - } - return false; + return BlocklistService::checkBlockedUAs(self::$rBlockedUA, $rUserAgent, $rReturn); } public static function isMonitorRunning($rPID, $rStreamID, $rEXE = PHP_BIN) { if (!empty($rPID)) { @@ -1083,16 +879,10 @@ class StreamingUtilities { return false; } public static function checkISP($rConISP) { - foreach (self::$rBlockedISP as $rISP) { - if (strtolower($rConISP) != strtolower($rISP['isp'])) { - } else { - return intval($rISP['blocked']); - } - } - return 0; + return BlocklistService::checkISP(self::$rBlockedISP, $rConISP); } public static function checkServer($rASN) { - return in_array($rASN, self::$rBlockedServers); + return BlocklistService::checkServer(self::$rBlockedServers, $rASN); } public static function getIPInfo($rIP) { if (!empty($rIP)) { diff --git a/src/includes/admin.php b/src/includes/admin.php index 186a67ce..6924337d 100644 --- a/src/includes/admin.php +++ b/src/includes/admin.php @@ -125,29 +125,12 @@ $rAdvPermissions = require $rPermissionsFile; function getUserInfo($rUsername, $rPassword) { global $db; - $db->query('SELECT `id`, `username`, `password`, `member_group_id`, `status` FROM `users` WHERE `username` = ? LIMIT 1;', $rUsername); - - if ($db->num_rows() == 1) { - $rRow = $db->get_row(); - - if (cryptPassword($rPassword, $rRow['password']) == $rRow['password']) { - return $rRow; - } - } + return UserRepository::getAuthUserByCredentials($db, $rUsername, $rPassword); } function getSeriesList() { global $db; - $rReturn = array(); - $db->query('SELECT `id`, `title` FROM `streams_series` ORDER BY `title` ASC;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[intval($rRow['id'])] = $rRow; - } - } - return $rReturn; + return SeriesRepository::getList($db); } function secondsToTime($inputSeconds) { @@ -167,50 +150,12 @@ function secondsToTime($inputSeconds) { function updateSeries($rID) { global $db; - require_once MAIN_HOME . 'includes/libs/tmdb.php'; - $db->query('SELECT `tmdb_id`, `tmdb_language` FROM `streams_series` WHERE `id` = ?;', $rID); - - if ($db->num_rows() != 1) { - } else { - $rRow = $db->get_row(); - $rTMDBID = $rRow['tmdb_id']; - - if (0 >= strlen($rTMDBID)) { - } else { - if (0 < strlen($rRow['tmdb_language'])) { - $rTMDB = new TMDB(CoreUtilities::$rSettings['tmdb_api_key'], $rRow['tmdb_language']); - } else { - if (0 < strlen(CoreUtilities::$rSettings['tmdb_language'])) { - $rTMDB = new TMDB(CoreUtilities::$rSettings['tmdb_api_key'], CoreUtilities::$rSettings['tmdb_language']); - } else { - $rTMDB = new TMDB(CoreUtilities::$rSettings['tmdb_api_key']); - } - } - - $rReturn = array(); - $rSeasons = json_decode($rTMDB->getTVShow($rTMDBID)->getJSON(), true)['seasons']; - - foreach ($rSeasons as $rSeason) { - $rSeason['cover'] = 'https://image.tmdb.org/t/p/w600_and_h900_bestv2' . $rSeason['poster_path']; - - if (!CoreUtilities::$rSettings['download_images']) { - } else { - $rSeason['cover'] = CoreUtilities::downloadImage($rSeason['cover']); - } - - $rSeason['cover_big'] = $rSeason['cover']; - unset($rSeason['poster_path']); - $rReturn[] = $rSeason; - } - - $db->query('UPDATE `streams_series` SET `seasons` = ? WHERE `id` = ?;', json_encode($rReturn, JSON_UNESCAPED_UNICODE), $rID); - } - } + SeriesRepository::updateFromTMDB($db, $rID); } function updateSeriesAsync($rID) { global $db; - $db->query('INSERT INTO `watch_refresh`(`type`, `stream_id`, `status`) VALUES(4, ?, 0);', $rID); + SeriesRepository::queueRefresh($db, $rID); } function validateCIDR($rCIDR) { @@ -241,49 +186,21 @@ function validateCIDR($rCIDR) { } function getFreeSpace($rServerID) { - $rReturn = array(); - $rLines = json_decode(systemapirequest($rServerID, array('action' => 'get_free_space')), true); - - // Check if array has elements before shifting - if (!empty($rLines)) { - array_shift($rLines); - } - - foreach ($rLines as $rLine) { - $rSplit = explode(' ', preg_replace('!\\s+!', ' ', trim($rLine))); - - if (0 < strlen($rSplit[0]) && strpos($rSplit[5], 'xc_vm') !== false || $rSplit[5] == '/') { - $rReturn[] = array('filesystem' => $rSplit[0], 'size' => $rSplit[1], 'used' => $rSplit[2], 'avail' => $rSplit[3], 'percentage' => $rSplit[4], 'mount' => implode(' ', array_slice($rSplit, 5, count($rSplit) - 5))); - } - } - - return $rReturn; + return ServerRepository::getFreeSpace('systemapirequest', $rServerID); } function getStreamsRamdisk($rServerID) { - $response = systemapirequest($rServerID, ['action' => 'streams_ramdisk']); - - $rReturn = json_decode($response, true); - if (!is_array($rReturn)) { - return []; - } - - if (empty($rReturn['result'])) { - return []; - } - - return $rReturn['streams'] ?? []; + return ServerRepository::getStreamsRamdisk('systemapirequest', $rServerID); } function killPID($rServerID, $rPID) { - - systemapirequest($rServerID, array('action' => 'kill_pid', 'pid' => $rPID)); + ServerRepository::killPID('systemapirequest', $rServerID, $rPID); } function getRTMPStats($rServerID) { - return json_decode(systemapirequest($rServerID, array('action' => 'rtmp_stats')), true); + return ServerRepository::getRTMPStats('systemapirequest', $rServerID); } function getStreamArguments() { @@ -374,36 +291,7 @@ function getWatchCategories($rType = null) { function syncDevices($rUserID, $rDeviceID = null) { global $db; - $rUser = getUser($rUserID); - - if (!$rUser) { - } else { - unset($rUser['id']); - - if ($rDeviceID) { - $db->query('SELECT * FROM `lines` WHERE `id` = (SELECT `user_id` FROM `mag_devices` WHERE `mag_id` = ?);', $rDeviceID); - } else { - $db->query('SELECT * FROM `lines` WHERE `pair_id` = ?;', $rUserID); - } - - foreach ($db->get_rows() as $rDevice) { - $rUpdateDevice = $rUser; - $rUpdateDevice['pair_id'] = intval($rUserID); - $rUpdateDevice['play_token'] = ''; - - foreach (array('id', 'is_mag', 'is_e2', 'is_restreamer', 'max_connections', 'created_at', 'username', 'password', 'admin_notes', 'reseller_notes') as $rKey) { - $rUpdateDevice[$rKey] = $rDevice[$rKey]; - } - - if (!isset($rUpdateDevice['id'])) { - } else { - $rPrepare = prepareArray($rUpdateDevice); - $rQuery = 'REPLACE INTO `lines`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - $db->query($rQuery, ...$rPrepare['data']); - CoreUtilities::updateLine($rUpdateDevice['id']); - } - } - } + DeviceSync::syncLineDevices($db, $rUserID, $rDeviceID); } function encodeRow($rRow) { @@ -535,45 +423,14 @@ function getSeasonTMDB($rID, $rSeason) { function getResellers($rOwner, $rIncludeSelf = true) { global $db; - $rReturn = array(); - - if ($rIncludeSelf) { - $db->query('SELECT `id`, `username` FROM `users` WHERE `owner_id` = ? OR `id` = ? ORDER BY `username` ASC;', $rOwner, $rOwner); - } else { - $db->query('SELECT `id`, `username` FROM `users` WHERE `owner_id` = ? ORDER BY `username` ASC;', $rOwner); - } - - return $db->get_rows(true, 'id'); + return UserRepository::getResellers($db, $rOwner, $rIncludeSelf); } function getDirectReports($rIncludeSelf = true) { global $db; global $rPermissions; global $rUserInfo; - $rUserIDs = $rPermissions['direct_reports']; - - if (!$rIncludeSelf) { - } else { - $rUserIDs[] = $rUserInfo['id']; - } - - $rReturn = array(); - - if (0 >= count($rUserIDs)) { - } else { - $db->query('SELECT * FROM `users` WHERE `owner_id` IN (' . implode(',', array_map('intval', $rUserIDs)) . ') ORDER BY `username` ASC;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - - - $rReturn[intval($rRow['id'])] = $rRow; - } - } - } - - return $rReturn; + return UserRepository::getDirectReports($db, $rPermissions, $rUserInfo, $rIncludeSelf); } function hasResellerPermissions($rType) { @@ -591,122 +448,31 @@ function hasPermissions($rType, $rID) { function getMemberGroups() { global $db; - $rReturn = array(); - $db->query('SELECT * FROM `users_groups` ORDER BY `group_id` ASC;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[intval($rRow['group_id'])] = $rRow; - } - } - - return $rReturn; + return GroupRepository::getAll($db); } function getHMACTokens() { global $db; - $rReturn = array(); - $db->query('SELECT * FROM `hmac_keys` ORDER BY `id` ASC;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[intval($rRow['id'])] = $rRow; - } - } - - return $rReturn; + return HMACRepository::getAll($db); } function getHMACToken($rID) { global $db; - $db->query('SELECT * FROM `hmac_keys` WHERE `id` = ?;', $rID); - - - - - if ($db->num_rows() != 1) { - } else { - return $db->get_row(); - } + return HMACRepository::getById($db, $rID); } function getActiveCodes() { - $rCodes = array(); - $rFiles = scandir(MAIN_HOME . 'bin/nginx/conf/codes/'); - - foreach ($rFiles as $rFile) { - $rPathInfo = pathinfo($rFile); - $rExt = $rPathInfo['extension']; - - if (!($rExt == 'conf' && $rPathInfo['filename'] != 'default')) { - } else { - $rCodes[] = $rPathInfo['filename']; - } - } - - return $rCodes; + return CodeRepository::getActiveCodes(MAIN_HOME); } function updateCodes() { - $rTemplate = file_get_contents(MAIN_HOME . 'bin/nginx/conf/codes/template'); - shell_exec('rm -f ' . MAIN_HOME . 'bin/nginx/conf/codes/*.conf'); - - foreach (getcodes() as $rCode) { - if ($rCode['enabled']) { - $rWhitelist = array(); - - foreach (json_decode($rCode['whitelist'], true) as $rIP) { - if (filter_var($rIP, FILTER_VALIDATE_IP)) { - $rWhitelist[] = 'allow ' . $rIP . ';'; - } - } - - if (0 >= count($rWhitelist)) { - } else { - $rWhitelist[] = 'deny all;'; - } - - $rType = array('admin', 'reseller', 'ministra', 'includes/api/admin', 'includes/api/reseller', 'ministra/new', 'player')[$rCode['type']]; - $rBurst = array(500, 50, 50, 1000, 1000, 50, 500)[$rCode['type']]; - - if (4 <= strlen($rCode['code'])) { - file_put_contents(MAIN_HOME . 'bin/nginx/conf/codes/' . $rCode['code'] . '.conf', str_replace(array('#WHITELIST#', '#CODE#', '#TYPE#', '#BURST#'), array(implode(' ', $rWhitelist), $rCode['code'], $rType, $rBurst), $rTemplate)); - } else { - file_put_contents(MAIN_HOME . 'bin/nginx/conf/codes/' . $rCode['code'] . '.conf', str_replace(array('#WHITELIST#', '#CODE#', '#TYPE#', '#BURST#'), array(implode(' ', $rWhitelist), $rCode['code'] . '/', $rType . '/', $rBurst), $rTemplate)); - } - } - } - - if (count(getActiveCodes()) == 0) { - if (file_exists(MAIN_HOME . 'bin/nginx/conf/codes/default.conf')) { - } else { - file_put_contents(MAIN_HOME . 'bin/nginx/conf/codes/default.conf', str_replace(array('alias ', '#WHITELIST#', '#CODE#', '#TYPE#'), array('root ', '', '', 'admin'), $rTemplate)); - } - } else { - if (!file_exists(MAIN_HOME . 'bin/nginx/conf/codes/default.conf')) { - } else { - unlink(MAIN_HOME . 'bin/nginx/conf/codes/default.conf'); - } - } - - reloadNginx(SERVER_ID); + global $db; + CodeRepository::updateCodes($db, MAIN_HOME, SERVER_ID, 'getcodes', 'reloadNginx'); } function getCurrentCode($rInfo = false) { - if ($rInfo) { - global $db; - $db->query('SELECT * FROM `access_codes` WHERE `code` = ?;', basename(dirname($_SERVER['PHP_SELF']))); - - if ($db->num_rows() == 1) { - return $db->get_row(); - } - return null; - } - - - return basename(dirname($_SERVER['PHP_SELF'])); + global $db; + return CodeRepository::getCurrentCode($db, $rInfo); } function overwriteData($rData, $rOverwrite, $rSkip = array()) { @@ -814,30 +580,12 @@ function setArgs($rArgs, $rGet = true) { function getParent($rID) { global $rPermissions; global $rUserInfo; - - - if (!isset($rPermissions['users'][$rID]['parent']) || $rPermissions['users'][$rID]['parent'] == 0 || $rPermissions['users'][$rID]['parent'] == $rUserInfo['id']) { - return $rID; - } - - return getParent($rPermissions['users'][$rID]['parent']); + return UserRepository::getParent($rPermissions, $rUserInfo, $rID); } function getSubUsers($rUser) { global $db; - - $rReturn = array(); - $db->query('SELECT `id`, `username` FROM `users` WHERE `owner_id` = ?;', $rUser); - - foreach ($db->get_rows() as $rRow) { - $rReturn[$rRow['id']] = array('username' => $rRow['username'], 'parent' => $rUser); - - foreach (getSubUsers($rRow['id']) as $rUserID => $rUserData) { - $rReturn[$rUserID] = $rUserData; - } - } - - return $rReturn; + return UserRepository::getSubUsers($db, $rUser); } function getAdminImage($rURL, $rMaxW, $rMaxH) { @@ -846,15 +594,7 @@ function getAdminImage($rURL, $rMaxW, $rMaxH) { function getStreamErrors($rStreamID, $rAmount = 250) { global $db; - - $rReturn = array(); - $db->query('SELECT * FROM (SELECT MAX(`date`) AS `date`, `error` FROM `streams_errors` WHERE `stream_id` = ? GROUP BY `error`) AS `output` ORDER BY `date` DESC LIMIT ' . intval($rAmount) . ';', $rStreamID); - - foreach ($db->get_rows() as $rRow) { - $rReturn[] = $rRow; - } - - return $rReturn; + return StreamRepository::getErrors($db, $rStreamID, $rAmount); } function getPageFromURL($rURL) { @@ -929,47 +669,12 @@ function checkExists($rTable, $rColumn, $rValue, $rExcludeColumn = null, $rExclu } function parseM3U($rData, $rFile = true) { - require_once INCLUDES_PATH . 'libs/m3u.php'; - $rParser = new M3uParser(); - $rParser->addDefaultTags(); - - if ($rFile) { - return $rParser->parseFile($rData); - } - - return $rParser->parse($rData); + return M3UParser::parse($rData, $rFile); } function deleteLines($rIDs) { global $db; - $rIDs = confirmIDs($rIDs); - - - if (0 >= count($rIDs)) { - return false; - } - - CoreUtilities::deleteLines($rIDs); - $db->query('DELETE FROM `lines` WHERE `id` IN (' . implode(',', $rIDs) . ');'); - $db->query('DELETE FROM `lines_logs` WHERE `user_id` IN (' . implode(',', $rIDs) . ');'); - $db->query('UPDATE `lines_activity` SET `user_id` = 0 WHERE `user_id` IN (' . implode(',', $rIDs) . ');'); - $rPairIDs = array(); - $db->query('SELECT `id` FROM `lines` WHERE `pair_id` IN (' . implode(',', $rIDs) . ');'); - - foreach ($db->get_rows() as $rRow) { - if (0 >= $rRow['id'] || in_array($rRow['id'], $rPairIDs)) { - } else { - $rPairIDs[] = $rRow['id']; - } - } - - if (0 >= count($rPairIDs)) { - } else { - $db->query('UPDATE `lines` SET `pair_id` = null WHERE `id` = (' . implode(',', $rPairIDs) . ');'); - CoreUtilities::updateLines($rPairIDs); - } - - return true; + return LineRepository::deleteMany($db, $rIDs); } function deleteMAG($rID, $rDeletePaired = false, $rCloseCons = true, $rConvert = false) { @@ -3467,88 +3172,27 @@ function getEPGSources() { function getCategories($rType = 'live') { global $db; + $rCategories = CategoryRepository::getFromDatabase($db, null, null, ($rType ?: null), true); $rReturn = array(); - - if ($rType) { - $db->query('SELECT * FROM `streams_categories` WHERE `category_type` = ? ORDER BY `cat_order` ASC;', $rType); - } else { - $db->query('SELECT * FROM `streams_categories` ORDER BY `cat_order` ASC;'); + foreach ($rCategories as $rID => $rRow) { + $rReturn[intval($rID)] = $rRow; } - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[intval($rRow['id'])] = $rRow; - } - } - return $rReturn; } function findEPG($rEPGName) { global $db; - $db->query('SELECT `id`, `data` FROM `epg`;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - foreach (json_decode($rRow['data'], true) as $rChannelID => $rChannelData) { - if ($rChannelID != $rEPGName) { - } else { - if (0 < count($rChannelData['langs'])) { - $rEPGLang = $rChannelData['langs'][0]; - } else { - $rEPGLang = ''; - } - - return array('channel_id' => $rChannelID, 'epg_lang' => $rEPGLang, 'epg_id' => intval($rRow['id'])); - } - } - } - } + return EpgRepository::findByName($db, $rEPGName); } function deleteGroup($rID) { global $db; - $rGroup = getMemberGroup($rID); - - if (!($rGroup && $rGroup['can_delete'])) { - return false; - } - - $db->query("SELECT `id`, `groups` FROM `users_packages` WHERE JSON_CONTAINS(`groups`, ?, '\$');", $rID); - - - foreach ($db->get_rows() as $rRow) { - $rRow['groups'] = json_decode($rRow['groups'], true); - - - if ($rKey = array_search($rID, $rRow['groups']) !== false) { - unset($rRow['groups'][$rKey]); - } - - $groups = array_map('intval', $rRow['groups']); - - $db->query("UPDATE `users_packages` SET `groups` = '[" . implode(',', $groups) . "]' WHERE `id` = ?;", $rRow['id']); - } - $db->query('UPDATE `users` SET `member_group_id` = 0 WHERE `member_group_id` = ?;', $rID); - $db->query('DELETE FROM `users_groups` WHERE `group_id` = ?;', $rID); - - return true; + return GroupRepository::deleteById($db, $rID); } function deletePackage($rID) { global $db; - $rPackage = getPackage($rID); - - if (!$rPackage) { - return false; - } - - $db->query('UPDATE `lines` SET `package_id` = null WHERE `package_id` = ?;', $rID); - $db->query('DELETE FROM `users_packages` WHERE `id` = ?;', $rID); - - return true; + return PackageRepository::deleteById($db, 'getPackage', $rID); } function deleteProvider($rID) { @@ -3582,42 +3226,7 @@ function deleteEPG($rID) { function deleteServer($rID, $rReplaceWith = null) { global $db; - $rServer = getStreamingServersByID($rID); - - if (!$rServer || $rServer['is_main']) { - return false; - } - - if ($rReplaceWith) { - $db->query('UPDATE `streams_servers` SET `server_id` = ? WHERE `server_id` = ?;', $rReplaceWith, $rID); - - - if (CoreUtilities::$rSettings['redis_handler']) { - } else { - $db->query('UPDATE `lines_live` SET `server_id` = ? WHERE `server_id` = ?;', $rReplaceWith, $rID); - } - - $db->query('UPDATE `lines_activity` SET `server_id` = ? WHERE `server_id` = ?;', $rReplaceWith, $rID); - } else { - $db->query('DELETE FROM `streams_servers` WHERE `server_id` = ?;', $rID); - - if (CoreUtilities::$rSettings['redis_handler']) { - } else { - $db->query('DELETE FROM `lines_live` WHERE `server_id` = ?;', $rID); - } - - $db->query('UPDATE `lines_activity` SET `server_id` = 0 WHERE `server_id` = ?;', $rID); - } - - $db->query('UPDATE `servers` SET `parent_id` = NULL, `enabled` = 0 WHERE `server_type` = 1 AND `parent_id` = ?;', $rID); - $db->query('DELETE FROM `servers_stats` WHERE `server_id` = ?;', $rID); - $db->query('DELETE FROM `servers` WHERE `id` = ?;', $rID); - - if ($rServer['server_type'] == 0) { - CoreUtilities::revokePrivileges($rServer['server_ip']); - } - - return true; + return ServerRepository::deleteById($db, CoreUtilities::$rSettings, 'getStreamingServersByID', $rID, $rReplaceWith); } function getEncodeErrors($rID) { @@ -3679,17 +3288,7 @@ function getProtocol() { function deleteMovieFile($rServerIDs, $rID) { global $db; - - if (is_array($rServerIDs)) { - } else { - $rServerIDs = array($rServerIDs); - } - - foreach ($rServerIDs as $rServerID) { - $db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`, `cache`) VALUES(?, ?, ?, 1);', $rServerID, time(), json_encode(array('type' => 'delete_vod', 'id' => $rID))); - } - - return true; + return MovieRepository::deleteFile($db, $rServerIDs, $rID); } function generateString($strength = 10) { @@ -3707,139 +3306,24 @@ function generateString($strength = 10) { function getAllServers() { global $db; - $rReturn = array(); - $db->query('SELECT * FROM `servers` ORDER BY `id` ASC;'); - - if ($db->num_rows() > 0) { - foreach ($db->get_rows() as $rRow) { - $rRow['server_online'] = in_array($rRow['status'], array(1, 3)) && time() - $rRow['last_check_ago'] <= 90 || $rRow['is_main']; - $rReturn[$rRow['id']] = $rRow; - } - } - - return $rReturn; + return ServerRepository::getAllSimple($db); } function getStreamingServers(string $type = 'online') { global $db; global $rPermissions; - $rReturn = array(); - $db->query('SELECT * FROM `servers` WHERE `server_type` = 0 ORDER BY `id` ASC;'); - - if ($db->num_rows() > 0) { - foreach ($db->get_rows() as $rRow) { - if (isset($rPermissions['is_reseller']) && $rPermissions['is_reseller']) { - $rRow['server_name'] = 'Server #' . ($rRow['id'] ?? 'unknown'); - } - - $rRow['server_online'] = in_array($rRow['status'], array(1, 3)) && time() - $rRow['last_check_ago'] <= 90 || $rRow['is_main']; - if (!isset($rRow['order'])) { - $rRow['order'] = 0; - } - if ($rRow['server_online'] || $type == 'all') { - $rReturn[$rRow['id']] = $rRow; - } - } - } - return $rReturn; + return ServerRepository::getStreamingSimple($db, $rPermissions, $type); } function getProxyServers($rOnline = false) { global $db; global $rPermissions; - $rReturn = array(); - $db->query('SELECT * FROM `servers` WHERE `server_type` = 1 ORDER BY `id` ASC;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - if ($rPermissions['is_reseller']) { - $rRow['server_name'] = 'Proxy #' . $rRow['id']; - } - - $rRow['server_online'] = in_array($rRow['status'], array(1, 3)) && time() - $rRow['last_check_ago'] <= 90 || $rRow['is_main']; - - - if (!$rRow['server_online'] && $rOnline) { - } else { - $rReturn[$rRow['id']] = $rRow; - } - } - } - - return $rReturn; + return ServerRepository::getProxySimple($db, $rPermissions, $rOnline); } function getStreamPIDs($rServerID) { global $db; - $rReturn = array(); - $db->query('SELECT `streams`.`id`, `streams`.`stream_display_name`, `streams`.`type`, `streams_servers`.`pid`, `streams_servers`.`monitor_pid`, `streams_servers`.`delay_pid` FROM `streams_servers` LEFT JOIN `streams` ON `streams`.`id` = `streams_servers`.`stream_id` WHERE `streams_servers`.`server_id` = ?;', $rServerID); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - foreach (array('pid', 'monitor_pid', 'delay_pid') as $rPIDType) { - if (!$rRow[$rPIDType]) { - } else { - $rReturn[$rRow[$rPIDType]] = array('id' => $rRow['id'], 'title' => $rRow['stream_display_name'], 'type' => $rRow['type'], 'pid_type' => $rPIDType); - } - } - } - } - - $db->query('SELECT `id`, `stream_display_name`, `type`, `tv_archive_pid` FROM `streams` WHERE `tv_archive_server_id` = ?;', $rServerID); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[$rRow['tv_archive_pid']] = array('id' => $rRow['id'], 'title' => $rRow['stream_display_name'], 'type' => $rRow['type'], 'pid_type' => 'timeshift'); - } - } - - $db->query('SELECT `id`, `stream_display_name`, `type`, `vframes_pid` FROM `streams` WHERE `vframes_server_id` = ?;', $rServerID); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[$rRow['vframes_pid']] = array('id' => $rRow['id'], 'title' => $rRow['stream_display_name'], 'type' => $rRow['type'], 'pid_type' => 'vframes'); - } - } - - if (CoreUtilities::$rSettings['redis_handler']) { - $rStreamIDs = $rStreamMap = array(); - $rConnections = CoreUtilities::getRedisConnections(null, $rServerID, null, true, false, false); - - foreach ($rConnections as $rConnection) { - if (in_array($rConnection['stream_id'], $rStreamIDs)) { - } else { - $rStreamIDs[] = intval($rConnection['stream_id']); - } - } - - if (0 >= count($rStreamIDs)) { - } else { - $db->query('SELECT `id`, `type`, `stream_display_name` FROM `streams` WHERE `id` IN (' . implode(',', $rStreamIDs) . ');'); - - foreach ($db->get_rows() as $rRow) { - $rStreamMap[$rRow['id']] = array($rRow['stream_display_name'], $rRow['type']); - } - } - - foreach ($rConnections as $rRow) { - $rReturn[$rRow['pid']] = array('id' => $rRow['stream_id'], 'title' => $rStreamMap[$rRow['stream_id']][0], 'type' => $rStreamMap[$rRow['stream_id']][1], 'pid_type' => 'activity'); - } - } else { - $db->query('SELECT `streams`.`id`, `streams`.`stream_display_name`, `streams`.`type`, `lines_live`.`pid` FROM `lines_live` LEFT JOIN `streams` ON `streams`.`id` = `lines_live`.`stream_id` WHERE `lines_live`.`server_id` = ?;', $rServerID); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[$rRow['pid']] = array('id' => $rRow['id'], 'title' => $rRow['stream_display_name'], 'type' => $rRow['type'], 'pid_type' => 'activity'); - } - } - } - - return $rReturn; + return StreamRepository::getPIDs($db, $rServerID, CoreUtilities::$rSettings); } function roundUpToAny($n, $x = 5) { @@ -3847,16 +3331,11 @@ function roundUpToAny($n, $x = 5) { } function checksource($rServerID, $rFilename) { - $rAPI = CoreUtilities::$rServers[intval($rServerID)]['api_url_ip'] . '&action=getFile&filename=' . urlencode($rFilename); - $rCommand = 'timeout 10 ' . CoreUtilities::$rFFPROBE . ' -user_agent "Mozilla/5.0" -show_streams -v quiet "' . $rAPI . '" -of json'; - - return json_decode(shell_exec($rCommand), true); + return ServerRepository::checkSource(CoreUtilities::$rServers, CoreUtilities::$rFFPROBE, $rServerID, $rFilename); } function getSSLLog($rServerID) { - $rAPI = CoreUtilities::$rServers[intval($rServerID)]['api_url_ip'] . '&action=getFile&filename=' . urlencode(BIN_PATH . 'certbot/logs/xc_vm.log'); - - return json_decode(file_get_contents($rAPI), true); + return ServerRepository::getSSLLog(CoreUtilities::$rServers, $rServerID); } function getWatchdog($rID, $rLimit = 86400) { @@ -3876,12 +3355,7 @@ function getWatchdog($rID, $rLimit = 86400) { function getMemberGroup($rID) { global $db; - $db->query('SELECT * FROM `users_groups` WHERE `group_id` = ?;', $rID); - - if ($db->num_rows() != 1) { - } else { - return $db->get_row(); - } + return GroupRepository::getById($db, $rID); } function getOutputs() { @@ -3901,107 +3375,42 @@ function getOutputs() { function getUserBouquets() { global $db; - $rReturn = array(); - $db->query('SELECT `id`, `bouquet` FROM `lines` ORDER BY `id` ASC;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[intval($rRow['id'])] = $rRow; - } - } - - return $rReturn; + return BouquetRepository::getUserBouquets($db); } function getBouquets() { global $db; - $rReturn = array(); - $db->query('SELECT * FROM `bouquets` ORDER BY `bouquet_order` ASC;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[intval($rRow['id'])] = $rRow; - } - } - - return $rReturn; + return BouquetRepository::getAllSimple($db); } function getBouquetOrder() { global $db; - $rReturn = array(); - $db->query('SELECT * FROM `bouquets` ORDER BY `bouquet_order` ASC;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[intval($rRow['id'])] = $rRow; - } - } - - return $rReturn; + return BouquetRepository::getOrder($db); } function getBlockedIPs() { global $db; - $rReturn = array(); - $db->query('SELECT * FROM `blocked_ips` ORDER BY `id` ASC;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[] = $rRow; - } - } - - return $rReturn; + return BlocklistRepository::getBlockedIPsSimple($db); } function getRTMPIPs() { global $db; - $rReturn = array(); - $db->query('SELECT * FROM `rtmp_ips` ORDER BY `id` ASC;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[] = $rRow; - } - } - - return $rReturn; + return BlocklistRepository::getRTMPIPsSimple($db); } function getStream($rID) { global $db; - $db->query('SELECT * FROM `streams` WHERE `id` = ?;', $rID); - - if ($db->num_rows() != 1) { - } else { - return $db->get_row(); - } + return StreamRepository::getById($db, $rID); } function getUser($rID) { global $db; - $db->query('SELECT * FROM `lines` WHERE `id` = ?;', $rID); - - if ($db->num_rows() != 1) { - } else { - return $db->get_row(); - } + return UserRepository::getLineById($db, $rID); } function getRegisteredUser($rID) { global $db; - $db->query('SELECT * FROM `users` WHERE `id` = ?;', $rID); - - if ($db->num_rows() != 1) { - } else { - return $db->get_row(); - } + return UserRepository::getRegisteredUserById($db, $rID); } function getPageName() { @@ -4044,51 +3453,15 @@ function cleanValue($rValue) { function getStreamStats($rStreamID) { global $db; - $rReturn = array(); - $db->query('SELECT * FROM `streams_stats` WHERE `stream_id` = ?;', $rStreamID); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[$rRow['type']] = $rRow; - } - } - - foreach (array('today', 'week', 'month', 'all') as $rType) { - if (isset($rReturn[$rType])) { - } else { - $rReturn[$rType] = array('rank' => 0, 'users' => 0, 'connections' => 0, 'time' => 0); - } - } - - return $rReturn; + return StreamRepository::getStats($db, $rStreamID); } function getSimilarMovies($rID, $rPage = 1) { - require_once MAIN_HOME . 'includes/libs/tmdb.php'; - - if (0 < strlen(CoreUtilities::$rSettings['tmdb_language'])) { - $rTMDB = new TMDB(CoreUtilities::$rSettings['tmdb_api_key'], CoreUtilities::$rSettings['tmdb_language']); - } else { - $rTMDB = new TMDB(CoreUtilities::$rSettings['tmdb_api_key']); - } - - return json_decode(json_encode($rTMDB->getSimilarMovies($rID, $rPage)), true); + return MovieRepository::getSimilar($rID, $rPage); } function getSimilarSeries($rID, $rPage = 1) { - require_once MAIN_HOME . 'includes/libs/tmdb.php'; - - if (0 < strlen(CoreUtilities::$rSettings['tmdb_language'])) { - $rTMDB = new TMDB(CoreUtilities::$rSettings['tmdb_api_key'], CoreUtilities::$rSettings['tmdb_language']); - } else { - $rTMDB = new TMDB(CoreUtilities::$rSettings['tmdb_api_key']); - } - - - - - return json_decode(json_encode($rTMDB->getSimilarSeries($rID, $rPage)), true); + return SeriesRepository::getSimilar($rID, $rPage); } function generateReport($rURL, $rParams) { @@ -4134,95 +3507,40 @@ function forcePlex($rServerID, $rPlexID) { } function freeTemp($rServerID) { - systemapirequest($rServerID, array('action' => 'free_temp')); + ServerRepository::freeTemp('systemapirequest', $rServerID); } function freeStreams($rServerID) { - systemapirequest($rServerID, array('action' => 'free_streams')); + ServerRepository::freeStreams('systemapirequest', $rServerID); } function probeSource($rServerID, $rURL, $rUserAgent = null, $rProxy = null, $rCookies = null, $rHeaders = null) { - return json_decode(systemapirequest($rServerID, array('action' => 'probe', 'url' => $rURL, 'user_agent' => $rUserAgent, 'http_proxy' => $rProxy, 'cookies' => $rCookies, 'headers' => $rHeaders), 30), true); + return ServerRepository::probeSource('systemapirequest', $rServerID, $rURL, $rUserAgent, $rProxy, $rCookies, $rHeaders); } function getchannelepg($rStreamID, $rArchive = false) { - global $db; $rStream = getStream($rStreamID); - - if (!$rStream['channel_id']) { - return array(); - } - - if ($rArchive) { - return CoreUtilities::getEPG($rStreamID, time() - $rStream['tv_archive_duration'] * 86400, time()); - } - - return CoreUtilities::getEPG($rStreamID, time(), time() + 1209600); + return EpgService::getChannelEpg($rStream, $rArchive); } function getEPG($rID) { global $db; - $db->query('SELECT * FROM `epg` WHERE `id` = ?;', $rID); - - if ($db->num_rows() != 1) { - } else { - return $db->get_row(); - } + return EpgRepository::getById($db, $rID); } function getStreamOptions($rID) { global $db; - $rReturn = array(); - $db->query('SELECT * FROM `streams_options` WHERE `stream_id` = ?;', $rID); - - - - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[intval($rRow['argument_id'])] = $rRow; - } - } - - return $rReturn; + return StreamRepository::getOptions($db, $rID); } function getStreamSys($rID) { global $db; - $rReturn = array(); - $db->query('SELECT * FROM `streams_servers` WHERE `stream_id` = ?;', $rID); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $rReturn[intval($rRow['server_id'])] = $rRow; - } - } - - return $rReturn; + return StreamRepository::getSystemRows($db, $rID); } function getRegisteredUsers($rOwner = null, $rIncludeSelf = true) { global $db; - $rReturn = array(); - $db->query('SELECT * FROM `users` ORDER BY `username` ASC;'); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - if (!(!$rOwner || $rRow['owner_id'] == $rOwner || $rRow['id'] == $rOwner && $rIncludeSelf)) { - } else { - $rReturn[intval($rRow['id'])] = $rRow; - } - } - } - - if (count($rReturn) == 0) { - $rReturn[-1] = array(); - } - - return $rReturn; + return UserRepository::getRegisteredUsers($db, $rOwner, $rIncludeSelf); } function getFooter() { @@ -4230,7 +3548,7 @@ function getFooter() { } function scanBouquets() { - shell_exec(PHP_BIN . ' ' . CLI_PATH . 'tools.php "bouquets" > /dev/null 2>/dev/null &'); + BouquetService::scan(); } /** @@ -4241,52 +3559,7 @@ function scanBouquets() { */ function scanBouquet($rID) { global $db; - - $rBouquet = getBouquet($rID); - if (!$rBouquet) { - return; // Bouquet not found, exit early - } - - // Get all available stream IDs - $availableStreams = []; - $db->query('SELECT `id` FROM `streams`;'); - if ($db->num_rows() > 0) { - foreach ($db->get_rows() as $rRow) { - $availableStreams[] = (int)$rRow['id']; - } - } - - // Get all available series IDs - $availableSeries = []; - $db->query('SELECT `id` FROM `streams_series`;'); - if ($db->num_rows() > 0) { - foreach ($db->get_rows() as $rRow) { - $availableSeries[] = (int)$rRow['id']; - } - } - - // Filter bouquet data against available IDs - $updateData = [ - 'channels' => filterIDs(json_decode($rBouquet['bouquet_channels'] ?? '[]', true), $availableStreams, true), - 'movies' => filterIDs(json_decode($rBouquet['bouquet_movies'] ?? '[]', true), $availableStreams, true), - 'radios' => filterIDs(json_decode($rBouquet['bouquet_radios'] ?? '[]', true), $availableStreams, true), - 'series' => filterIDs(json_decode($rBouquet['bouquet_series'] ?? '[]', true), $availableSeries, false) - ]; - - // Update bouquet with filtered data using prepared statements - $db->query( - "UPDATE `bouquets` SET - `bouquet_channels` = ?, - `bouquet_movies` = ?, - `bouquet_radios` = ?, - `bouquet_series` = ? - WHERE `id` = ?", - json_encode($updateData['channels']), - json_encode($updateData['movies']), - json_encode($updateData['radios']), - json_encode($updateData['series']), - $rBouquet['id'] - ); + BouquetService::scanOne($db, $rID, 'getBouquet', 'filterIDs'); } /** @@ -4318,34 +3591,12 @@ function filterIDs($ids, $availableIDs, $checkPositive = true) { function getNextOrder() { global $db; - $db->query('SELECT MAX(`order`) AS `order` FROM `streams`;'); - - if ($db->num_rows() != 1) { - return 0; - } - - return intval($db->get_row()['order']) + 1; + return StreamRepository::getNextOrder($db); } function generateSeriesPlaylist($rSeriesNo) { global $db; - $rReturn = array(); - $db->query('SELECT `stream_id` FROM `streams_episodes` WHERE `series_id` = ? ORDER BY `season_num` ASC, `episode_num` ASC;', $rSeriesNo); - - if (0 >= $db->num_rows()) { - } else { - foreach ($db->get_rows() as $rRow) { - $db->query('SELECT `stream_source` FROM `streams` WHERE `id` = ?;', $rRow['stream_id']); - - if (0 >= $db->num_rows()) { - } else { - list($rSource) = json_decode($db->get_row()['stream_source'], true); - $rReturn[] = $rSource; - } - } - } - - return $rReturn; + return SeriesRepository::generatePlaylist($db, $rSeriesNo); } function shutdown_admin() { diff --git a/src/includes/admin_api.php b/src/includes/admin_api.php index 625fe98c..06f61d3d 100644 --- a/src/includes/admin_api.php +++ b/src/includes/admin_api.php @@ -154,612 +154,84 @@ class API { } public static function processBouquet($rData) { - global $_; - - if (self::checkMinimumRequirements($rData)) { - - - if (isset($rData['edit'])) { - if (hasPermissions('adv', 'edit_bouquet')) { - - - $rArray = overwriteData(getBouquet($rData['edit']), $rData); - } else { - exit(); - } - } else { - if (hasPermissions('adv', 'add_bouquet')) { - - - $rArray = verifyPostTable('bouquets', $rData); - unset($rArray['id']); - } else { - exit(); - } - } - - if (is_array(json_decode($rData['bouquet_data'], true))) { - $rBouquetData = json_decode($rData['bouquet_data'], true); - $rBouquetStreams = $rBouquetData['stream']; - $rBouquetMovies = $rBouquetData['movies']; - $rBouquetRadios = $rBouquetData['radios']; - $rBouquetSeries = $rBouquetData['series']; - $rRequiredIDs = confirmIDs(array_merge($rBouquetStreams, $rBouquetMovies, $rBouquetRadios)); - $rStreams = array(); - - if (0 >= count($rRequiredIDs)) { - } else { - self::$db->query('SELECT `id`, `type` FROM `streams` WHERE `id` IN (' . implode(',', $rRequiredIDs) . ');'); - - foreach (self::$db->get_rows() as $rRow) { - if (intval($rRow['type']) != 3) { - } else { - $rRow['type'] = 1; - } - - $rStreams[intval($rRow['type'])][] = intval($rRow['id']); - } - } - - if (count($rBouquetSeries) > 0) { - self::$db->query('SELECT `id` FROM `streams_series` WHERE `id` IN (' . implode(',', $rBouquetSeries) . ');'); - - foreach (self::$db->get_rows() as $rRow) { - $rStreams[5][] = intval($rRow['id']); - } - } - - $rArray['bouquet_channels'] = array_intersect(array_map('intval', array_values($rBouquetStreams)), $rStreams[1] ?? []); - $rArray['bouquet_movies'] = array_intersect(array_map('intval', array_values($rBouquetMovies)), $rStreams[2] ?? []); - $rArray['bouquet_radios'] = array_intersect(array_map('intval', array_values($rBouquetRadios)), $rStreams[4] ?? []); - $rArray['bouquet_series'] = array_intersect(array_map('intval', array_values($rBouquetSeries)), $rStreams[5] ?? []); - } else { - if (isset($rData['edit'])) { - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - } - - if (!isset($rData['edit'])) { - self::$db->query('SELECT MAX(`bouquet_order`) AS `max` FROM `bouquets`;'); - $rArray['bouquet_order'] = intval(self::$db->get_row()['max']) + 1; - } - - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `bouquets`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - scanBouquet($rInsertID); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return BouquetService::process($rData, self::$db, 'getBouquet', 'scanBouquet'); } public static function processCode($rData) { - if (self::checkMinimumRequirements($rData)) { - if (isset($rData['edit'])) { - $rArray = overwriteData(getCode($rData['edit']), $rData); - $rOrigCode = $rArray['code']; - } else { - $rArray = verifyPostTable('access_codes', $rData); - $rOrigCode = null; - unset($rArray['id']); - } - - if (isset($rData['enabled'])) { - $rArray['enabled'] = 1; - } else { - $rArray['enabled'] = 0; - } - - if (!isset($rData['groups'])) { - } else { - $rArray['groups'] = array(); - - foreach ($rData['groups'] as $rGroupID) { - $rArray['groups'][] = intval($rGroupID); - } - } - - if (in_array($rData['type'], array(0, 1, 3, 4))) { - $rArray['groups'] = '[' . implode(',', array_map('intval', $rArray['groups'])) . ']'; - } else { - $rArray['groups'] = '[]'; - } - - if (isset($rData['whitelist'])) { - } else { - $rArray['whitelist'] = '[]'; - } - - if ($rData['type'] != 2 && strlen($rData['code']) < 8) { - return array('status' => STATUS_CODE_LENGTH, 'data' => $rData); - } - - if ($rData['type'] == 2 && empty($rData['code'])) { - - return array('status' => STATUS_INVALID_CODE, 'data' => $rData); - } - - if (in_array($rData['code'], array('admin', 'stream', 'images', 'player_api', 'player', 'playlist', 'epg', 'live', 'movie', 'series', 'status', 'nginx_status', 'get', 'panel_api', 'xmltv', 'probe', 'thumb', 'timeshift', 'auth', 'vauth', 'tsauth', 'hls', 'play', 'key', 'api', 'c'))) { - return array('status' => STATUS_RESERVED_CODE, 'data' => $rData); - } - - if (isset($rData['edit'])) { - self::$db->query('SELECT `id` FROM `access_codes` WHERE `code` = ? AND `id` <> ?;', $rData['code'], $rData['edit']); - } else { - self::$db->query('SELECT `id` FROM `access_codes` WHERE `code` = ?;', $rData['code']); - } - - if (0 >= self::$db->num_rows()) { - $rPrepare = prepareArray($rArray); - - - $rQuery = 'REPLACE INTO `access_codes`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - updateCodes(); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID, 'orig_code' => $rOrigCode, 'new_code' => $rData['code'])); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - - return array('status' => STATUS_EXISTS_CODE, 'data' => $rData); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return CodeService::process(self::$db, $rData, 'getCode', 'updateCodes'); } public static function processHMAC($rData) { - if (self::checkMinimumRequirements($rData)) { - if (isset($rData['edit'])) { - $rArray = overwriteData(getHMACToken($rData['edit']), $rData); - } else { - $rArray = verifyPostTable('hmac_keys', $rData); - unset($rArray['id']); - } - - if (isset($rData['enabled'])) { - $rArray['enabled'] = 1; - } else { - $rArray['enabled'] = 0; - } - - if (!($rData['keygen'] != 'HMAC KEY HIDDEN' && strlen($rData['keygen']) != 32)) { - if (strlen($rData['notes']) != 0) { - if (isset($rData['edit'])) { - if ($rData['keygen'] != 'HMAC KEY HIDDEN') { - self::$db->query('SELECT `id` FROM `hmac_keys` WHERE `key` = ? AND `id` <> ?;', CoreUtilities::encryptData($rData['keygen'], CoreUtilities::$rSettings['live_streaming_pass'], OPENSSL_EXTRA), $rData['edit']); - - if (0 >= self::$db->num_rows()) { - } else { - return array('status' => STATUS_EXISTS_HMAC, 'data' => $rData); - } - } - } else { - self::$db->query('SELECT `id` FROM `hmac_keys` WHERE `key` = ?;', CoreUtilities::encryptData($rData['keygen'], CoreUtilities::$rSettings['live_streaming_pass'], OPENSSL_EXTRA)); - - if (0 >= self::$db->num_rows()) { - } else { - return array('status' => STATUS_EXISTS_HMAC, 'data' => $rData); - } - } - - if ($rData['keygen'] != 'HMAC KEY HIDDEN') { - $rArray['key'] = CoreUtilities::encryptData($rData['keygen'], CoreUtilities::$rSettings['live_streaming_pass'], OPENSSL_EXTRA); - } - - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `hmac_keys`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - return array('status' => STATUS_NO_DESCRIPTION, 'data' => $rData); - } - return array('status' => STATUS_NO_KEY, 'data' => $rData); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + + return HMACService::process(self::$db, $rData, self::$rSettings, 'getHMACToken'); } public static function reorderBouquet($rData) { - if (self::checkMinimumRequirements($rData)) { - $rOrder = json_decode($rData['stream_order_array'], true); - $rOrder['stream'] = confirmIDs($rOrder['stream']); - $rOrder['series'] = confirmIDs($rOrder['series']); - $rOrder['movie'] = confirmIDs($rOrder['movie']); - $rOrder['radio'] = confirmIDs($rOrder['radio']); - self::$db->query('UPDATE `bouquets` SET `bouquet_channels` = ?, `bouquet_series` = ?, `bouquet_movies` = ?, `bouquet_radios` = ? WHERE `id` = ?;', '[' . implode(',', array_map('intval', $rOrder['stream'])) . ']', '[' . implode(',', array_map('intval', $rOrder['series'])) . ']', '[' . implode(',', array_map('intval', $rOrder['movie'])) . ']', '[' . implode(',', array_map('intval', $rOrder['radio'])) . ']', $rData['reorder']); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rData['reorder'])); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return BouquetService::reorder($rData, self::$db); } public static function editAdminProfile($rData) { global $allowedLangs; - if (self::checkMinimumRequirements($rData)) { - if (0 >= strlen($rData['email']) || filter_var($rData['email'], FILTER_VALIDATE_EMAIL)) { - - - if (0 < strlen($rData['password'])) { - $rPassword = cryptPassword($rData['password']); - } else { - $rPassword = self::$rUserInfo['password']; - } - - if (ctype_xdigit($rData['api_key']) && strlen($rData['api_key']) == 32) { - } else { - $rData['api_key'] = ''; - } - - if (!in_array($rData['lang'], $allowedLangs)) { - $rData['lang'] = 'en'; - } - - self::$db->query('UPDATE `users` SET `password` = ?, `email` = ?, `theme` = ?, `hue` = ?, `timezone` = ?, `api_key` = ?, `lang` = ? WHERE `id` = ?;', $rPassword, $rData['email'], $rData['theme'], $rData['hue'], $rData['timezone'], $rData['api_key'], $rData['lang'], self::$rUserInfo['id']); - - return array('status' => STATUS_SUCCESS); - } - - return array('status' => STATUS_INVALID_EMAIL); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return ProfileService::editAdminProfile(self::$db, $rData, self::$rUserInfo, $allowedLangs); } public static function blockIP($rData) { - if (self::checkMinimumRequirements($rData)) { - if (validateCIDR($rData['ip'])) { - $rArray = array('ip' => $rData['ip'], 'notes' => $rData['notes'], 'date' => time()); - touch(FLOOD_TMP_PATH . 'block_' . $rData['ip']); - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `blocked_ips`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (!self::$db->query($rQuery, ...$rPrepare['data'])) { - } else { - $rInsertID = self::$db->last_insert_id(); - } - - if (isset($rInsertID)) { - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - - return array('status' => STATUS_INVALID_IP, 'data' => $rData); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return BlocklistService::blockIP(self::$db, $rData); } public static function sortBouquets($rData) { - if (self::checkMinimumRequirements($rData)) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - $rOrder = json_decode($rData['bouquet_order_array'], true); - $rSort = 1; - - foreach ($rOrder as $rBouquetID) { - self::$db->query('UPDATE `bouquets` SET `bouquet_order` = ? WHERE `id` = ?;', $rSort, $rBouquetID); - $rSort++; - } - - if (isset($rData['confirmReplace'])) { - $rUsers = getUserBouquets(); - - foreach ($rUsers as $rUser) { - $rBouquet = json_decode($rUser['bouquet'], true); - $rBouquet = array_map('intval', sortArrayByArray($rBouquet, $rOrder)); - self::$db->query('UPDATE `lines` SET `bouquet` = ? WHERE `id` = ?;', '[' . implode(',', $rBouquet) . ']', $rUser['id']); - CoreUtilities::updateLine($rUser['id']); - } - $rPackages = getPackages(); - - foreach ($rPackages as $rPackage) { - $rBouquet = json_decode($rPackage['bouquets'], true); - $rBouquet = array_map('intval', sortArrayByArray($rBouquet, $rOrder)); - self::$db->query('UPDATE `users_packages` SET `bouquets` = ? WHERE `id` = ?;', '[' . implode(',', $rBouquet) . ']', $rPackage['id']); - } - - return array('status' => STATUS_SUCCESS_REPLACE); - } else { - return array('status' => STATUS_SUCCESS); - } - } else { - - - + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return BouquetService::sort($rData, self::$db, 'getUserBouquets', 'getPackages', 'sortArrayByArray', array('CoreUtilities', 'updateLine')); } public static function setChannelOrder($rData) { - if (self::checkMinimumRequirements($rData)) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - $rOrder = json_decode($rData['stream_order_array'], true); - $rSort = 0; - - foreach ($rOrder as $rStream) { - self::$db->query('UPDATE `streams` SET `order` = ? WHERE `id` = ?;', $rSort, $rStream); - $rSort++; - } - - return array('status' => STATUS_SUCCESS); - } else { - - - + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return ChannelService::setOrder($rData, self::$db); } public static function processChannel($rData) { - if (self::checkMinimumRequirements($rData)) { - if (isset($rData['edit'])) { - if (hasPermissions('adv', 'edit_cchannel')) { - $rArray = overwriteData(getStream($rData['edit']), $rData); - } else { - exit(); - } - } else { - if (hasPermissions('adv', 'create_channel')) { - $rArray = verifyPostTable('streams', $rData); - $rArray['type'] = 3; - $rArray['added'] = time(); - unset($rArray['id']); - } else { - exit(); - } - } - - if (isset($rData['restart_on_edit'])) { - $rRestart = true; - } else { - $rRestart = false; - } - - if (isset($rData['reencode_on_edit'])) { - $rReencode = true; - } else { - $rReencode = false; - } - - foreach (array('allow_record', 'rtmp_output') as $rKey) { - if (isset($rData[$rKey])) { - $rArray[$rKey] = 1; - } else { - $rArray[$rKey] = 0; - } - } - $rArray['movie_properties'] = array('type' => intval($rData['channel_type'])); - - if (intval($rData['channel_type']) == 0) { - $rPlaylist = generateSeriesPlaylist($rData['series_no']); - $rArray['stream_source'] = $rPlaylist; - $rArray['series_no'] = intval($rData['series_no']); - } else { - // video_files comes as a JSON string from the form, it needs to be decoded - $rVideoFiles = $rData['video_files']; - if (is_string($rVideoFiles)) { - $rVideoFiles = json_decode($rVideoFiles, true); - } - $rArray['stream_source'] = is_array($rVideoFiles) ? $rVideoFiles : []; - $rArray['series_no'] = 0; - } - - if ($rData['transcode_profile_id'] == -1) { - $rArray['movie_symlink'] = 1; - } else { - $rArray['movie_symlink'] = 0; - } - - if (0 < count($rArray['stream_source'])) { - $rBouquetCreate = array(); - - foreach (json_decode($rData['bouquet_create_list'], true) as $rBouquet) { - $rPrepare = prepareArray(array('bouquet_name' => $rBouquet, 'bouquet_channels' => array(), 'bouquet_movies' => array(), 'bouquet_series' => array(), 'bouquet_radios' => array())); - $rQuery = 'INSERT INTO `bouquets`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (!self::$db->query($rQuery, ...$rPrepare['data'])) { - } else { - $rBouquetID = self::$db->last_insert_id(); - $rBouquetCreate[$rBouquet] = $rBouquetID; - } - } - $rCategoryCreate = array(); - - foreach (json_decode($rData['category_create_list'], true) as $rCategory) { - $rPrepare = prepareArray(array('category_type' => 'live', 'category_name' => $rCategory, 'parent_id' => 0, 'cat_order' => 99, 'is_adult' => 0)); - $rQuery = 'INSERT INTO `streams_categories`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (!self::$db->query($rQuery, ...$rPrepare['data'])) { - } else { - $rCategoryID = self::$db->last_insert_id(); - $rCategoryCreate[$rCategory] = $rCategoryID; - } - } - $rBouquets = array(); - - foreach ($rData['bouquets'] as $rBouquet) { - if (isset($rBouquetCreate[$rBouquet])) { - $rBouquets[] = $rBouquetCreate[$rBouquet]; - } else { - if (!is_numeric($rBouquet)) { - } else { - $rBouquets[] = intval($rBouquet); - } - } - } - $rCategories = array(); - - foreach ($rData['category_id'] as $rCategory) { - if (isset($rCategoryCreate[$rCategory])) { - $rCategories[] = $rCategoryCreate[$rCategory]; - } else { - if (is_numeric($rCategory)) { - $rCategories[] = intval($rCategory); - } - } - } - $rArray['category_id'] = '[' . implode(',', array_map('intval', $rCategories)) . ']'; - - if (!self::$rSettings['download_images']) { - } else { - $rArray['stream_icon'] = CoreUtilities::downloadImage($rArray['stream_icon'], 3); - } - - if (isset($rData['edit'])) { - } else { - $rArray['order'] = getNextOrder(); - } - - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `streams`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - $rStreamExists = array(); - - if (!isset($rData['edit'])) { - } else { - self::$db->query('SELECT `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` = ?;', $rInsertID); - - foreach (self::$db->get_rows() as $rRow) { - $rStreamExists[intval($rRow['server_id'])] = intval($rRow['server_stream_id']); - } - } - - $rStreamsAdded = array(); - $rServerTree = json_decode($rData['server_tree_data'], true); - - foreach ($rServerTree as $rServer) { - if ($rServer['parent'] == '#') { - } else { - $rServerID = intval($rServer['id']); - $rStreamsAdded[] = $rServerID; - $rOD = intval(in_array($rServerID, ($rData['on_demand'] ?: array()))); - - if ($rServer['parent'] == 'source') { - $rParent = null; - } else { - $rParent = intval($rServer['parent']); - } - - if (isset($rStreamExists[$rServerID])) { - self::$db->query('UPDATE `streams_servers` SET `parent_id` = ?, `on_demand` = ? WHERE `server_stream_id` = ?;', $rParent, $rOD, $rStreamExists[$rServerID]); - } else { - self::$db->query("INSERT INTO `streams_servers`(`stream_id`, `server_id`, `parent_id`, `on_demand`, `pids_create_channel`, `cchannel_rsources`) VALUES(?, ?, ?, ?, '[]', '[]');", $rInsertID, $rServerID, $rParent, $rOD); - } - } - } - - foreach ($rStreamExists as $rServerID => $rDBID) { - if (in_array($rServerID, $rStreamsAdded)) { - } else { - deleteStream($rInsertID, $rServerID, false, false); - } - } - - if ($rReencode) { - APIRequest(array('action' => 'stream', 'sub' => 'stop', 'stream_ids' => array($rInsertID))); - self::$db->query("UPDATE `streams_servers` SET `pids_create_channel` = '[]', `cchannel_rsources` = '[]' WHERE `stream_id` = ?;", $rInsertID); - CoreUtilities::queueChannel($rInsertID); - } - - if ($rRestart) { - APIRequest(array('action' => 'stream', 'sub' => 'start', 'stream_ids' => array($rInsertID))); - } - - foreach ($rBouquets as $rBouquet) { - addToBouquet('stream', $rBouquet, $rInsertID); - } - - if (!isset($rData['edit'])) { - } else { - - foreach (getBouquets() as $rBouquet) { - if (in_array($rBouquet['id'], $rBouquets)) { - } else { - removeFromBouquet('stream', $rBouquet['id'], $rInsertID); - } - } - } - - CoreUtilities::updateStream($rInsertID); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } else { - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - } else { - return array('status' => STATUS_NO_SOURCES, 'data' => $rData); - } - } else { - - - + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return ChannelService::process($rData, self::$db, self::$rSettings); } public static function processEPG($rData) { - if (self::checkMinimumRequirements($rData)) { - if (isset($rData['edit'])) { - if (hasPermissions('adv', 'epg_edit')) { - $rArray = overwriteData(getEPG($rData['edit']), $rData); - } else { - exit(); - } - } else { - if (hasPermissions('adv', 'add_epg')) { - $rArray = verifyPostTable('epg', $rData); - unset($rArray['id']); - } else { - exit(); - } - } - - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `epg`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return EpgService::process($rData, self::$db, 'getEPG'); } public static function processProvider($rData) { @@ -819,445 +291,30 @@ class API { } public static function processEpisode($rData) { - if (self::checkMinimumRequirements($rData)) { - if (isset($rData['edit'])) { - if (hasPermissions('adv', 'edit_episode')) { - - - $rArray = overwriteData(getStream($rData['edit']), $rData); - } else { - exit(); - } - } else { - if (hasPermissions('adv', 'add_episode')) { - - - $rArray = verifyPostTable('streams', $rData); - $rArray['type'] = 5; - $rArray['added'] = time(); - $rArray['series_no'] = intval($rData['series']); - unset($rArray['id']); - } else { - exit(); - } - } - - $rArray['stream_source'] = array($rData['stream_source']); - - if (0 < strlen($rData['movie_subtitles'])) { - $rSplit = explode(':', $rData['movie_subtitles']); - $rArray['movie_subtitles'] = array('files' => array($rSplit[2]), 'names' => array('Subtitles'), 'charset' => array('UTF-8'), 'location' => intval($rSplit[1])); - } else { - $rArray['movie_subtitles'] = null; - } - - if (0 >= $rArray['transcode_profile_id']) { - } else { - $rArray['enable_transcode'] = 1; - } - - foreach (array('read_native', 'movie_symlink', 'direct_source', 'direct_proxy', 'remove_subtitles') as $rKey) { - if (isset($rData[$rKey])) { - $rArray[$rKey] = 1; - } else { - $rArray[$rKey] = 0; - } - } - - if (isset($rData['restart_on_edit'])) { - $rRestart = true; - } else { - $rRestart = false; - } - - $rProcessArray = array(); - - if (isset($rData['multi'])) { - if (hasPermissions('adv', 'import_episodes')) { - - - set_time_limit(0); - include INCLUDES_PATH . 'libs/tmdb.php'; - $rSeries = getSerie(intval($rData['series'])); - - if (0 < strlen(self::$rSettings['tmdb_language'])) { - $rTMDB = new TMDB(self::$rSettings['tmdb_api_key'], self::$rSettings['tmdb_language']); - } else { - $rTMDB = new TMDB(self::$rSettings['tmdb_api_key']); - } - - $rJSON = json_decode($rTMDB->getSeason($rData['tmdb_id'], intval($rData['season_num']))->getJSON(), true); - - foreach ($rData as $rKey => $rFilename) { - $rSplit = explode('_', $rKey); - - if (!($rSplit[0] == 'episode' && $rSplit[2] == 'name')) { - } else { - if (0 >= strlen($rData['episode_' . $rSplit[1] . '_num'])) { - } else { - $rImportArray = array('filename' => '', 'properties' => array(), 'name' => '', 'episode' => 0, 'target_container' => ''); - $rEpisodeNum = intval($rData['episode_' . $rSplit[1] . '_num']); - $rImportArray['filename'] = 's:' . $rData['server'] . ':' . $rData['season_folder'] . $rFilename; - $rImage = ''; - - if (isset($rData['addName1']) && isset($rData['addName2'])) { - $rImportArray['name'] = $rSeries['title'] . ' - S' . sprintf('%02d', intval($rData['season_num'])) . 'E' . sprintf('%02d', $rEpisodeNum) . ' - '; - } else { - if (isset($rData['addName1'])) { - $rImportArray['name'] = $rSeries['title'] . ' - '; - } else { - if (!isset($rData['addName2'])) { - } else { - $rImportArray['name'] = 'S' . sprintf('%02d', intval($rData['season_num'])) . 'E' . sprintf('%02d', $rEpisodeNum) . ' - '; - } - } - } - - $rImportArray['episode'] = $rEpisodeNum; - - foreach ($rJSON['episodes'] as $rEpisode) { - if (intval($rEpisode['episode_number']) != $rEpisodeNum) { - } else { - if (0 >= strlen($rEpisode['still_path'])) { - } else { - $rImage = 'https://image.tmdb.org/t/p/w600_and_h900_bestv2' . $rEpisode['still_path']; - - if (!self::$rSettings['download_images']) { - } else { - $rImage = CoreUtilities::downloadImage($rImage, 5); - } - } - - $rImportArray['name'] .= $rEpisode['name']; - $rSeconds = intval($rSeries['episode_run_time']) * 60; - $rImportArray['properties'] = array('tmdb_id' => $rEpisode['id'], 'release_date' => $rEpisode['air_date'], 'plot' => $rEpisode['overview'], 'duration_secs' => $rSeconds, 'duration' => sprintf('%02d:%02d:%02d', $rSeconds / 3600, ($rSeconds / 60) % 60, $rSeconds % 60), 'movie_image' => $rImage, 'video' => array(), 'audio' => array(), 'bitrate' => 0, 'rating' => $rEpisode['vote_average'], 'season' => $rData['season_num']); - - if (strlen($rImportArray['properties']['movie_image'][0]) != 0) { - } else { - unset($rImportArray['properties']['movie_image']); - } - } - } - - if (strlen($rImportArray['name']) != 0) { - } else { - $rImportArray['name'] = 'No Episode Title'; - } - - $rPathInfo = pathinfo(explode('?', $rFilename)[0]); - $rImportArray['target_container'] = $rPathInfo['extension']; - $rProcessArray[] = $rImportArray; - } - } - } - } else { - exit(); - } - } else { - $rImportArray = array('filename' => $rArray['stream_source'][0], 'properties' => array(), 'name' => $rArray['stream_display_name'], 'episode' => $rData['episode'], 'target_container' => $rData['target_container']); - - if (!self::$rSettings['download_images']) { - } else { - $rData['movie_image'] = CoreUtilities::downloadImage($rData['movie_image'], 5); - } - - $rSeconds = intval($rData['episode_run_time']) * 60; - $rImportArray['properties'] = array('release_date' => $rData['release_date'], 'plot' => $rData['plot'], 'duration_secs' => $rSeconds, 'duration' => sprintf('%02d:%02d:%02d', $rSeconds / 3600, ($rSeconds / 60) % 60, $rSeconds % 60), 'movie_image' => $rData['movie_image'], 'video' => array(), 'audio' => array(), 'bitrate' => 0, 'rating' => $rData['rating'], 'season' => $rData['season_num'], 'tmdb_id' => $rData['tmdb_id']); - - if (strlen($rImportArray['properties']['movie_image'][0]) != 0) { - } else { - unset($rImportArray['properties']['movie_image']); - } - - if (!$rData['direct_proxy']) { - } else { - $rExtension = pathinfo(explode('?', $rData['stream_source'])[0])['extension']; - - if ($rExtension) { - $rImportArray['target_container'] = $rExtension; - } else { - if ($rImportArray['target_container']) { - } else { - $rImportArray['target_container'] = 'mp4'; - } - } - } - - $rProcessArray[] = $rImportArray; - } - - $rRestartIDs = array(); - - foreach ($rProcessArray as $rImportArray) { - $rArray['stream_source'] = array($rImportArray['filename']); - $rArray['movie_properties'] = $rImportArray['properties']; - $rArray['stream_display_name'] = $rImportArray['name']; - - if (!empty($rImportArray['target_container'])) { - $rArray['target_container'] = $rImportArray['target_container']; - } else { - if (empty($rData['target_container'])) { - $rArray['target_container'] = pathinfo(explode('?', $rImportArray['filename'])[0])['extension']; - } else { - $rArray['target_container'] = $rData['target_container']; - } - } - - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `streams`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - self::$db->query('DELETE FROM `streams_episodes` WHERE `stream_id` = ?;', $rInsertID); - self::$db->query('INSERT INTO `streams_episodes`(`season_num`, `series_id`, `stream_id`, `episode_num`) VALUES(?, ?, ?, ?);', $rData['season_num'], $rData['series'], $rInsertID, $rImportArray['episode']); - updateSeriesAsync(intval($rData['series'])); - $rStreamExists = array(); - - if (!isset($rData['edit'])) { - } else { - self::$db->query('SELECT `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` = ?;', $rInsertID); - - foreach (self::$db->get_rows() as $rRow) { - $rStreamExists[intval($rRow['server_id'])] = intval($rRow['server_stream_id']); - } - } - - $rStreamsAdded = array(); - $rServerTree = json_decode($rData['server_tree_data'], true); - - foreach ($rServerTree as $rServer) { - if ($rServer['parent'] == '#') { - } else { - $rServerID = intval($rServer['id']); - $rStreamsAdded[] = $rServerID; - - if (isset($rStreamExists[$rServerID])) { - } else { - self::$db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`, `on_demand`) VALUES(?, ?, 0);', $rInsertID, $rServerID); - } - } - } - - foreach ($rStreamExists as $rServerID => $rDBID) { - if (in_array($rServerID, $rStreamsAdded)) { - } else { - deleteStream($rInsertID, $rServerID, true, false); - } - } - - if (!$rRestart) { - } else { - $rRestartIDs[] = $rInsertID; - } - - self::$db->query('UPDATE `streams_series` SET `last_modified` = ? WHERE `id` = ?;', time(), $rData['streams_series']); - CoreUtilities::updateStream($rInsertID); - } else { - return array('status' => STATUS_FAILURE); - } - } - - if (!$rRestart) { - } else { - APIRequest(array('action' => 'vod', 'sub' => 'start', 'stream_ids' => $rRestartIDs)); - } - - if (isset($rData['multi'])) { - return array('status' => STATUS_SUCCESS_MULTI, 0 => array('series_id' => $rData['series'])); - } - - return array('status' => STATUS_SUCCESS, 'data' => array('series_id' => $rData['series'], 'insert_id' => $rInsertID)); - } else { + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return EpisodeService::process(self::$db, self::$rSettings, $rData); } public static function massEditEpisodes($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - $rArray = array(); - - - if (!isset($rData['c_movie_symlink'])) { - } else { - if (isset($rData['movie_symlink'])) { - $rArray['movie_symlink'] = 1; - } else { - $rArray['movie_symlink'] = 0; - } - } - - if (!isset($rData['c_direct_source'])) { - } else { - if (isset($rData['direct_source'])) { - $rArray['direct_source'] = 1; - } else { - $rArray['direct_source'] = 0; - $rArray['direct_proxy'] = 0; - } - } - - if (!isset($rData['c_direct_proxy'])) { - } else { - if (isset($rData['direct_proxy'])) { - $rArray['direct_proxy'] = 1; - $rArray['direct_source'] = 1; - } else { - $rArray['direct_proxy'] = 0; - } - } - - if (!isset($rData['c_read_native'])) { - } else { - if (isset($rData['read_native'])) { - $rArray['read_native'] = 1; - } else { - $rArray['read_native'] = 0; - } - } - - if (!isset($rData['c_remove_subtitles'])) { - } else { - if (isset($rData['remove_subtitles'])) { - $rArray['remove_subtitles'] = 1; - } else { - $rArray['remove_subtitles'] = 0; - } - } - - if (!isset($rData['c_target_container'])) { - } else { - $rArray['target_container'] = $rData['target_container']; - } - - if (!isset($rData['c_transcode_profile_id'])) { - } else { - $rArray['transcode_profile_id'] = $rData['transcode_profile_id']; - - if (0 < $rArray['transcode_profile_id']) { - $rArray['enable_transcode'] = 1; - } else { - $rArray['enable_transcode'] = 0; - } - } - - $rStreamIDs = confirmIDs(json_decode($rData['streams'], true)); - - if (0 >= count($rStreamIDs)) { - } else { - if (!isset($rData['c_serie_name'])) { - } else { - self::$db->query('UPDATE `streams_episodes` SET `series_id` = ? WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');', $rData['serie_name']); - self::$db->query('UPDATE `streams` SET `series_no` = ? WHERE `id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');', $rData['serie_name']); - } - - $rPrepare = prepareArray($rArray); - - if (0 >= count($rPrepare['data'])) { - } else { - $rQuery = 'UPDATE `streams` SET ' . $rPrepare['update'] . ' WHERE `id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'; - self::$db->query($rQuery, ...$rPrepare['data']); - } - - $rDeleteServers = $rQueueMovies = $rProcessServers = $rStreamExists = array(); - self::$db->query('SELECT `stream_id`, `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); - - foreach (self::$db->get_rows() as $rRow) { - $rStreamExists[intval($rRow['stream_id'])][intval($rRow['server_id'])] = intval($rRow['server_stream_id']); - $rProcessServers[intval($rRow['stream_id'])][] = intval($rRow['server_id']); - } - $rAddQuery = ''; - - foreach ($rStreamIDs as $rStreamID) { - if (!isset($rData['c_server_tree'])) { - } else { - $rStreamsAdded = array(); - $rServerTree = json_decode($rData['server_tree_data'], true); - - foreach ($rServerTree as $rServer) { - if ($rServer['parent'] == '#') { - } else { - $rServerID = intval($rServer['id']); - - if (in_array($rData['server_type'], array('ADD', 'SET'))) { - $rStreamsAdded[] = $rServerID; - - if (isset($rStreamExists[$rStreamID][$rServerID])) { - } else { - $rAddQuery .= '(' . intval($rStreamID) . ', ' . intval($rServerID) . '),'; - $rProcessServers[$rStreamID][] = $rServerID; - } - } else { - if (!isset($rStreamExists[$rStreamID][$rServerID])) { - } else { - $rDeleteServers[$rServerID][] = $rStreamID; - } - } - } - } - - if ($rData['server_type'] != 'SET') { - } else { - foreach ($rStreamExists[$rStreamID] as $rServerID => $rDBID) { - if (in_array($rServerID, $rStreamsAdded)) { - } else { - $rDeleteServers[$rServerID][] = $rStreamID; - - if (($rKey = array_search($rServerID, $rProcessServers[$rStreamID])) === false) { - } else { - unset($rProcessServers[$rStreamID][$rKey]); - } - } - } - } - } - - if (!isset($rData['reencode_on_edit'])) { - } else { - foreach ($rProcessServers[$rStreamID] as $rServerID) { - $rQueueMovies[$rServerID][] = $rStreamID; - } - } - } - - foreach ($rDeleteServers as $rServerID => $rDeleteIDs) { - deleteStreamsByServer($rDeleteIDs, $rServerID, true); - } - - if (empty($rAddQuery)) { - } else { - $rAddQuery = rtrim($rAddQuery, ','); - self::$db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`) VALUES ' . $rAddQuery . ';'); - } - - CoreUtilities::updateStreams($rStreamIDs); - - if (!isset($rData['reencode_on_edit'])) { - } else { - foreach ($rQueueMovies as $rServerID => $rQueueIDs) { - CoreUtilities::queueMovies($rQueueIDs, $rServerID); - } - } - - if (!isset($rData['reprocess_tmdb'])) { - } else { - CoreUtilities::refreshMovies($rStreamIDs, 3); - } - } - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return EpisodeService::massEdit(self::$db, $rData); } public static function processGroup($rData) { + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return GroupService::process($rData); + } + + public static function processGroupLegacy($rData) { if (self::checkMinimumRequirements($rData)) { if (isset($rData['edit'])) { if (hasPermissions('adv', 'edit_group')) { @@ -1349,51 +406,11 @@ class API { } public static function processISP($rData) { - if (self::checkMinimumRequirements($rData)) { - if (isset($rData['edit'])) { - if (hasPermissions('adv', 'block_isps')) { - - - $rArray = overwriteData(getISP($rData['edit']), $rData); - } else { - exit(); - } - } else { - if (hasPermissions('adv', 'block_isps')) { - - - $rArray = verifyPostTable('blocked_isps', $rData); - unset($rArray['id']); - } else { - exit(); - } - } - - if (isset($rData['blocked'])) { - $rArray['blocked'] = 1; - } else { - $rArray['blocked'] = 0; - } - - if (strlen($rArray['isp']) != 0) { - $rPrepare = prepareArray($rArray); - - - $rQuery = 'REPLACE INTO `blocked_isps`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - - return array('status' => STATUS_INVALID_NAME, 'data' => $rData); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return BlocklistService::processISP(self::$db, $rData, 'getISP'); } public static function processLogin($rData, $rBypassRecaptcha = false) { @@ -1401,75 +418,35 @@ class API { } public static function massDeleteStreams($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - - - $rStreams = json_decode($rData['streams'], true); - deleteStreams($rStreams, false); - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return StreamService::massDelete($rData); } public static function massDeleteMovies($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - - - $rMovies = json_decode($rData['movies'], true); - deleteStreams($rMovies, true); - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return MovieService::massDelete($rData); } public static function massDeleteLines($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - - - $rLines = json_decode($rData['lines'], true); - deleteLines($rLines); - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return LineService::massDelete($rData); } public static function massDeleteUsers($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - - - $rUsers = json_decode($rData['users'], true); - deleteUser($rUsers); - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return UserService::massDelete($rData); } public static function massDeleteStations($rData) { @@ -1491,80 +468,46 @@ class API { } public static function massDeleteMags($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - - - $rMags = json_decode($rData['mags'], true); - deleteMAGs($rMags); - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return MagService::massDelete($rData); } public static function massDeleteEnigmas($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - - - $rEnigmas = json_decode($rData['enigmas'], true); - deleteEnigmas($rEnigmas); - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return EnigmaService::massDelete($rData); } public static function massDeleteSeries($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - - - $rSeries = json_decode($rData['series'], true); - deleteSeriesMass($rSeries); - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return SeriesService::massDelete($rData); } public static function massDeleteEpisodes($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - - - $rEpisodes = json_decode($rData['episodes'], true); - deleteStreams($rEpisodes, true); - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return EpisodeService::massDelete($rData); } public static function processMovie($rData) { + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return MovieService::process(self::$db, self::$rSettings, $rData); + } + + public static function processMovieLegacy($rData) { if (self::checkMinimumRequirements($rData)) { set_time_limit(0); ini_set('mysql.connect_timeout', 0); @@ -2033,328 +976,30 @@ class API { } public static function massEditMovies($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - $rArray = array(); - - - if (!isset($rData['c_movie_symlink'])) { - } else { - if (isset($rData['movie_symlink'])) { - $rArray['movie_symlink'] = 1; - } else { - $rArray['movie_symlink'] = 0; - } - } - - if (!isset($rData['c_direct_source'])) { - } else { - if (isset($rData['direct_source'])) { - $rArray['direct_source'] = 1; - } else { - $rArray['direct_source'] = 0; - $rArray['direct_proxy'] = 0; - } - } - - if (!isset($rData['c_direct_proxy'])) { - } else { - if (isset($rData['direct_proxy'])) { - $rArray['direct_proxy'] = 1; - $rArray['direct_source'] = 1; - } else { - $rArray['direct_proxy'] = 0; - } - } - - if (!isset($rData['c_read_native'])) { - } else { - if (isset($rData['read_native'])) { - $rArray['read_native'] = 1; - } else { - $rArray['read_native'] = 0; - } - } - - if (!isset($rData['c_remove_subtitles'])) { - } else { - if (isset($rData['remove_subtitles'])) { - $rArray['remove_subtitles'] = 1; - } else { - $rArray['remove_subtitles'] = 0; - } - } - - if (!isset($rData['c_target_container'])) { - } else { - $rArray['target_container'] = $rData['target_container']; - } - - if (!isset($rData['c_transcode_profile_id'])) { - } else { - $rArray['transcode_profile_id'] = $rData['transcode_profile_id']; - - if (0 < $rArray['transcode_profile_id']) { - $rArray['enable_transcode'] = 1; - } else { - $rArray['enable_transcode'] = 0; - } - } - - $rStreamIDs = json_decode($rData['streams'], true); - - if (0 >= count($rStreamIDs)) { - } else { - $rCategoryMap = array(); - - if (!(isset($rData['c_category_id']) && in_array($rData['category_id_type'], array('ADD', 'DEL')))) { - } else { - self::$db->query('SELECT `id`, `category_id` FROM `streams` WHERE `id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); - - foreach (self::$db->get_rows() as $rRow) { - $rCategoryMap[$rRow['id']] = (json_decode($rRow['category_id'], true) ?: array()); - } - } - - $rDeleteServers = $rQueueMovies = $rProcessServers = $rStreamExists = array(); - self::$db->query('SELECT `stream_id`, `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); - - foreach (self::$db->get_rows() as $rRow) { - $rStreamExists[intval($rRow['stream_id'])][intval($rRow['server_id'])] = intval($rRow['server_stream_id']); - $rProcessServers[intval($rRow['stream_id'])][] = intval($rRow['server_id']); - } - $rBouquets = getBouquets(); - $rAddBouquet = $rDelBouquet = array(); - $rAddQuery = ''; - - foreach ($rStreamIDs as $rStreamID) { - if (!isset($rData['c_category_id'])) { - } else { - $rCategories = array_map('intval', $rData['category_id']); - - if ($rData['category_id_type'] == 'ADD') { - foreach (($rCategoryMap[$rStreamID] ?: array()) as $rCategoryID) { - if (in_array($rCategoryID, $rCategories)) { - } else { - $rCategories[] = $rCategoryID; - } - } - } else { - if ($rData['category_id_type'] != 'DEL') { - } else { - $rNewCategories = $rCategoryMap[$rStreamID]; - - foreach ($rCategories as $rCategoryID) { - if (($rKey = array_search($rCategoryID, $rNewCategories)) === false) { - } else { - unset($rNewCategories[$rKey]); - } - } - $rCategories = $rNewCategories; - } - } - - $rArray['category_id'] = '[' . implode(',', $rCategories) . ']'; - } - - $rPrepare = prepareArray($rArray); - - if (0 >= count($rPrepare['data'])) { - } else { - $rPrepare['data'][] = $rStreamID; - $rQuery = 'UPDATE `streams` SET ' . $rPrepare['update'] . ' WHERE `id` = ?;'; - self::$db->query($rQuery, ...$rPrepare['data']); - } - - if (!isset($rData['c_server_tree'])) { - } else { - $rStreamsAdded = array(); - $rServerTree = json_decode($rData['server_tree_data'], true); - - foreach ($rServerTree as $rServer) { - if ($rServer['parent'] == '#') { - } else { - $rServerID = intval($rServer['id']); - - if (in_array($rData['server_type'], array('ADD', 'SET'))) { - $rStreamsAdded[] = $rServerID; - - if (isset($rStreamExists[$rStreamID][$rServerID])) { - } else { - $rAddQuery .= '(' . intval($rStreamID) . ', ' . intval($rServerID) . '),'; - $rProcessServers[$rStreamID][] = $rServerID; - } - } else { - if (!isset($rStreamExists[$rStreamID][$rServerID])) { - } else { - $rDeleteServers[$rServerID][] = $rStreamID; - } - } - } - } - - if ($rData['server_type'] != 'SET') { - } else { - foreach ($rStreamExists[$rStreamID] as $rServerID => $rDBID) { - if (in_array($rServerID, $rStreamsAdded)) { - } else { - $rDeleteServers[$rServerID][] = $rStreamID; - - if (($rKey = array_search($rServerID, $rProcessServers[$rStreamID])) === false) { - } else { - unset($rProcessServers[$rStreamID][$rKey]); - } - } - } - } - } - - if (!isset($rData['c_bouquets'])) { - } else { - if ($rData['bouquets_type'] == 'SET') { - foreach ($rData['bouquets'] as $rBouquet) { - $rAddBouquet[$rBouquet][] = $rStreamID; - } - - foreach ($rBouquets as $rBouquet) { - if (in_array($rBouquet['id'], $rData['bouquets'])) { - } else { - $rDelBouquet[$rBouquet['id']][] = $rStreamID; - } - } - } else { - if ($rData['bouquets_type'] == 'ADD') { - foreach ($rData['bouquets'] as $rBouquet) { - $rAddBouquet[$rBouquet][] = $rStreamID; - } - } else { - if ($rData['bouquets_type'] != 'DEL') { - } else { - foreach ($rData['bouquets'] as $rBouquet) { - $rDelBouquet[$rBouquet][] = $rStreamID; - } - } - } - } - } - - if (!isset($rData['reencode_on_edit'])) { - } else { - foreach ($rProcessServers[$rStreamID] as $rServerID) { - $rQueueMovies[$rServerID][] = $rStreamID; - } - } - } - - foreach ($rDeleteServers as $rServerID => $rDeleteIDs) { - deleteStreamsByServer($rDeleteIDs, $rServerID, true); - } - - foreach ($rAddBouquet as $rBouquetID => $rAddIDs) { - addToBouquet('movie', $rBouquetID, $rAddIDs); - } - - foreach ($rDelBouquet as $rBouquetID => $rRemIDs) { - removeFromBouquet('movie', $rBouquetID, $rRemIDs); - } - - if (empty($rAddQuery)) { - } else { - $rAddQuery = rtrim($rAddQuery, ','); - self::$db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`) VALUES ' . $rAddQuery . ';'); - } - - CoreUtilities::updateStreams($rStreamIDs); - - if (!isset($rData['reencode_on_edit'])) { - } else { - foreach ($rQueueMovies as $rServerID => $rQueueIDs) { - CoreUtilities::queueMovies($rQueueIDs, $rServerID); - } - } - - if (!isset($rData['reprocess_tmdb'])) { - } else { - CoreUtilities::refreshMovies($rStreamIDs, 1); - } - } - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return MovieService::massEdit(self::$db, $rData); } public static function processPackage($rData) { - if (self::checkMinimumRequirements($rData)) { - if (isset($rData['edit'])) { - if (hasPermissions('adv', 'edit_package')) { - - - $rArray = overwriteData(getPackage($rData['edit']), $rData); - } else { - exit(); - } - } else { - if (hasPermissions('adv', 'add_packages')) { - - - $rArray = verifyPostTable('users_packages', $rData); - unset($rArray['id']); - } else { - exit(); - } - } - - if (strlen($rData['package_name']) != 0) { - - - foreach (array('is_trial', 'is_official', 'is_mag', 'is_e2', 'is_line', 'lock_device', 'is_restreamer', 'is_isplock', 'check_compatible') as $rSelection) { - if (isset($rData[$rSelection])) { - $rArray[$rSelection] = 1; - } else { - $rArray[$rSelection] = 0; - } - } - $rArray['groups'] = '[' . implode(',', array_map('intval', json_decode($rData['groups_selected'], true))) . ']'; - $rArray['bouquets'] = sortArrayByArray(array_values(json_decode($rData['bouquets_selected'], true)), array_keys(getBouquetOrder())); - $rArray['bouquets'] = '[' . implode(',', array_map('intval', $rArray['bouquets'])) . ']'; - - if (!isset($rData['output_formats'])) { - } else { - $rArray['output_formats'] = array(); - - foreach ($rData['output_formats'] as $rOutput) { - $rArray['output_formats'][] = $rOutput; - } - $rArray['output_formats'] = '[' . implode(',', array_map('intval', $rArray['output_formats'])) . ']'; - } - - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `users_packages`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); - } else { - return array('status' => STATUS_INVALID_NAME, 'data' => $rData); - } - } else { + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return PackageService::process(self::$db, $rData, 'getPackage', 'getBouquetOrder', 'sortArrayByArray'); } public static function processMAG($rData) { + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return MagService::process($rData); + } + + public static function processMAGLegacy($rData) { if (self::checkMinimumRequirements($rData)) { if (isset($rData['edit'])) { if (hasPermissions('adv', 'edit_mag')) { @@ -2547,6 +1192,14 @@ class API { } public static function processEnigma($rData) { + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return EnigmaService::process($rData); + } + + public static function processEnigmaLegacy($rData) { if (self::checkMinimumRequirements($rData)) { if (isset($rData['edit'])) { if (hasPermissions('adv', 'edit_e2')) { @@ -3501,6 +2154,14 @@ class API { } public static function processUser($rData, $rBypassAuth = false) { + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return UserService::process($rData, $rBypassAuth); + } + + public static function processUserLegacy($rData, $rBypassAuth = false) { if (self::checkMinimumRequirements($rData)) { @@ -3592,55 +2253,25 @@ class API { } public static function processRTMPIP($rData) { - if (self::checkMinimumRequirements($rData)) { - if (isset($rData['edit'])) { - $rArray = overwriteData(getRTMPIP($rData['edit']), $rData); - } else { - $rArray = verifyPostTable('rtmp_ips', $rData); - unset($rArray['id']); - } - - foreach (array('push', 'pull') as $rSelection) { - if (isset($rData[$rSelection])) { - $rArray[$rSelection] = 1; - } else { - $rArray[$rSelection] = 0; - } - } - - if (filter_var($rData['ip'], FILTER_VALIDATE_IP)) { - - - if (!checkExists('rtmp_ips', 'ip', $rData['ip'], 'id', $rArray['id'])) { - - - if (strlen($rData['password']) != 0) { - } else { - $rArray['password'] = generateString(16); - } - - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `rtmp_ips`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - - return array('status' => STATUS_EXISTS_IP, 'data' => $rData); - } - - return array('status' => STATUS_INVALID_IP, 'data' => $rData); - } else { + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return BlocklistService::processRTMPIP(self::$db, $rData, 'getRTMPIP'); } public static function importSeries($rData) { + if (!hasPermissions('adv', 'import_movies')) { + exit(); + } + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return SeriesService::import($rData); + } + + public static function importSeriesLegacy($rData) { if (hasPermissions('adv', 'import_movies')) { if (self::checkMinimumRequirements($rData)) { $rPostData = $rData; @@ -3825,6 +2456,17 @@ class API { } public static function importMovies($rData) { + if (!hasPermissions('adv', 'import_movies')) { + exit(); + } + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return MovieService::import($rData); + } + + public static function importMoviesLegacy($rData) { if (hasPermissions('adv', 'import_movies')) { @@ -4028,6 +2670,14 @@ class API { } public static function processSeries($rData) { + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return SeriesService::process(self::$db, self::$rSettings, $rData); + } + + public static function processSeriesLegacy($rData) { if (self::checkMinimumRequirements($rData)) { if (isset($rData['edit'])) { if (hasPermissions('adv', 'edit_series')) { @@ -4148,616 +2798,59 @@ class API { } public static function massEditSeries($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - $rArray = array(); - - - $rSeriesIDs = json_decode($rData['series'], true); - - if (0 >= count($rSeriesIDs)) { - } else { - $rCategoryMap = array(); - - if (!(isset($rData['c_category_id']) && in_array($rData['category_id_type'], array('ADD', 'DEL')))) { - } else { - self::$db->query('SELECT `id`, `category_id` FROM `streams_series` WHERE `id` IN (' . implode(',', array_map('intval', $rSeriesIDs)) . ');'); - - foreach (self::$db->get_rows() as $rRow) { - $rCategoryMap[$rRow['id']] = (json_decode($rRow['category_id'], true) ?: array()); - } - } - - $rBouquets = getBouquets(); - $rAddBouquet = $rDelBouquet = array(); - - foreach ($rSeriesIDs as $rSeriesID) { - if (!isset($rData['c_category_id'])) { - } else { - $rCategories = array_map('intval', $rData['category_id']); - - if ($rData['category_id_type'] == 'ADD') { - foreach (($rCategoryMap[$rSeriesID] ?: array()) as $rCategoryID) { - if (in_array($rCategoryID, $rCategories)) { - } else { - $rCategories[] = $rCategoryID; - } - } - } else { - if ($rData['category_id_type'] != 'DEL') { - } else { - $rNewCategories = $rCategoryMap[$rSeriesID]; - - foreach ($rCategories as $rCategoryID) { - if (($rKey = array_search($rCategoryID, $rNewCategories)) === false) { - } else { - unset($rNewCategories[$rKey]); - } - } - $rCategories = $rNewCategories; - } - } - - $rArray['category_id'] = '[' . implode(',', $rCategories) . ']'; - } - - $rPrepare = prepareArray($rArray); - - if (0 >= count($rPrepare['data'])) { - } else { - $rPrepare['data'][] = $rSeriesID; - $rQuery = 'UPDATE `streams_series` SET ' . $rPrepare['update'] . ' WHERE `id` = ?;'; - self::$db->query($rQuery, ...$rPrepare['data']); - } - - if (!isset($rData['c_bouquets'])) { - } else { - if ($rData['bouquets_type'] == 'SET') { - foreach ($rData['bouquets'] as $rBouquet) { - $rAddBouquet[$rBouquet][] = $rSeriesID; - } - - foreach ($rBouquets as $rBouquet) { - if (in_array($rBouquet['id'], $rData['bouquets'])) { - } else { - $rDelBouquet[$rBouquet['id']][] = $rSeriesID; - } - } - } else { - if ($rData['bouquets_type'] == 'ADD') { - foreach ($rData['bouquets'] as $rBouquet) { - $rAddBouquet[$rBouquet][] = $rSeriesID; - } - } else { - if ($rData['bouquets_type'] != 'DEL') { - } else { - foreach ($rData['bouquets'] as $rBouquet) { - $rDelBouquet[$rBouquet][] = $rSeriesID; - } - } - } - } - } - } - - foreach ($rAddBouquet as $rBouquetID => $rAddIDs) { - addToBouquet('series', $rBouquetID, $rAddIDs); - } - - foreach ($rDelBouquet as $rBouquetID => $rRemIDs) { - removeFromBouquet('series', $rBouquetID, $rRemIDs); - } - - if (!isset($rData['reprocess_tmdb'])) { - } else { - foreach ($rSeriesIDs as $rSeriesID) { - if (0 >= intval($rSeriesID)) { - } else { - self::$db->query('INSERT INTO `watch_refresh`(`type`, `stream_id`, `status`) VALUES(2, ?, 0);', $rSeriesID); - } - } - } - } - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return SeriesService::massEdit(self::$db, $rData); } public static function processServer($rData) { - if (hasPermissions('adv', 'edit_server')) { - if (self::checkMinimumRequirements($rData)) { - $rServer = getStreamingServersByID($rData['edit']); - if ($rServer) { - $rArray = verifyPostTable('servers', $rData, true); - $rPorts = array('http' => array(), 'https' => array()); - - foreach ($rData['http_broadcast_ports'] as $rPort) { - if (!(is_numeric($rPort) && 80 <= $rPort && $rPort <= 65535 && !in_array($rPort, ($rPorts['http'] ?: array())) && $rPort != $rData['rtmp_port'])) { - } else { - $rPorts['http'][] = $rPort; - } - } - $rPorts['http'] = array_unique($rPorts['http']); - unset($rData['http_broadcast_ports']); - - foreach ($rData['https_broadcast_ports'] as $rPort) { - if (!(is_numeric($rPort) && 80 <= $rPort && $rPort <= 65535 && !in_array($rPort, ($rPorts['http'] ?: array())) && !in_array($rPort, ($rPorts['https'] ?: array())) && $rPort != $rData['rtmp_port'])) { - } else { - $rPorts['https'][] = $rPort; - } - } - $rPorts['https'] = array_unique($rPorts['https']); - unset($rData['https_broadcast_ports']); - $rArray['http_broadcast_port'] = null; - $rArray['http_ports_add'] = null; - - if (count($rPorts['http']) > 0) { - $rArray['http_broadcast_port'] = $rPorts['http'][0]; - - if (1 >= count($rPorts['http'])) { - } else { - $rArray['http_ports_add'] = implode(',', array_slice($rPorts['http'], 1, count($rPorts['http']) - 1)); - } - } - - $rArray['https_broadcast_port'] = null; - $rArray['https_ports_add'] = null; - - if (0 >= count($rPorts['https'])) { - } else { - $rArray['https_broadcast_port'] = $rPorts['https'][0]; - - if (1 >= count($rPorts['https'])) { - } else { - $rArray['https_ports_add'] = implode(',', array_slice($rPorts['https'], 1, count($rPorts['https']) - 1)); - } - } - - foreach (array('enable_gzip', 'timeshift_only', 'enable_https', 'random_ip', 'enable_geoip', 'enable_isp', 'enabled', 'enable_proxy') as $rKey) { - if (isset($rData[$rKey])) { - $rArray[$rKey] = 1; - } else { - $rArray[$rKey] = 0; - } - } - - if ($rServer['is_main']) { - $rArray['enabled'] = 1; - } - - if (isset($rData['geoip_countries'])) { - $rArray['geoip_countries'] = array(); - - foreach ($rData['geoip_countries'] as $rCountry) { - $rArray['geoip_countries'][] = $rCountry; - } - } else { - $rArray['geoip_countries'] = array(); - } - - if (isset($rData['isp_names'])) { - $rArray['isp_names'] = array(); - - foreach ($rData['isp_names'] as $rISP) { - $rArray['isp_names'][] = strtolower(trim(preg_replace('/[^A-Za-z0-9 ]/', '', $rISP))); - } - } else { - $rArray['isp_names'] = array(); - } - - if (isset($rData['domain_name'])) { - $rArray['domain_name'] = implode(',', $rData['domain_name']); - } else { - $rArray['domain_name'] = ''; - } - - if (strlen($rData['server_ip']) != 0 && filter_var($rData['server_ip'], FILTER_VALIDATE_IP)) { - if (0 >= strlen($rData['private_ip']) || filter_var($rData['private_ip'], FILTER_VALIDATE_IP)) { - $rArray['total_services'] = $rData['total_services']; - $rPrepare = prepareArray($rArray); - $rPrepare['data'][] = $rData['edit']; - $rQuery = 'UPDATE `servers` SET ' . $rPrepare['update'] . ' WHERE `id` = ?;'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = $rData['edit']; - $rPorts = array('http' => array(), 'https' => array()); - - foreach (array_merge(array(intval($rArray['http_broadcast_port'])), explode(',', $rArray['http_ports_add'])) as $rPort) { - if (is_numeric($rPort) && 0 < $rPort && $rPort <= 65535) { - $rPorts['http'][] = intval($rPort); - } - } - - foreach (array_merge(array(intval($rArray['https_broadcast_port'])), explode(',', $rArray['https_ports_add'])) as $rPort) { - if (is_numeric($rPort) && 0 < $rPort && $rPort <= 65535) { - $rPorts['https'][] = intval($rPort); - } - } - changePort($rInsertID, 0, $rPorts['http'], false); - changePort($rInsertID, 1, $rPorts['https'], false); - changePort($rInsertID, 2, array($rArray['rtmp_port']), false); - setServices($rInsertID, intval($rArray['total_services']), true); - - if (empty($rArray['governor'])) { - } else { - setGovernor($rInsertID, $rArray['governor']); - } - - if (!empty($rArray['sysctl'])) { - setSysctl($rInsertID, $rArray['sysctl']); - } - - if (file_exists(CACHE_TMP_PATH . 'servers')) { - unlink(CACHE_TMP_PATH . 'servers'); - } - - $rFS = getFreeSpace($rInsertID); - $rMounted = false; - - foreach ($rFS as $rMount) { - if ($rMount['mount'] != rtrim(STREAMS_PATH, '/')) { - } else { - $rMounted = true; - - break; - } - } - - if ($rData['disable_ramdisk'] && $rMounted) { - self::$db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`) VALUES(?, ?, ?);', $rInsertID, time(), json_encode(array('action' => 'disable_ramdisk'))); - } else { - if ($rData['disable_ramdisk'] || $rMounted) { - } else { - self::$db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`) VALUES(?, ?, ?);', $rInsertID, time(), json_encode(array('action' => 'enable_ramdisk'))); - } - } - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } else { - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - } else { - return array('status' => STATUS_INVALID_IP, 'data' => $rData); - } - } else { - return array('status' => STATUS_INVALID_IP, 'data' => $rData); - } - } else { - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); - } - } else { - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); - } - } else { - exit(); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return ServerService::process($rData, self::$db); } public static function processProxy($rData) { - if (self::checkMinimumRequirements($rData)) { - if (hasPermissions('adv', 'edit_server')) { - $rArray = overwriteData(getStreamingServersByID($rData['edit']), $rData); - - - foreach (array('enable_https', 'random_ip', 'enable_geoip', 'enabled') as $rKey) { - if (isset($rData[$rKey])) { - $rArray[$rKey] = true; - } else { - $rArray[$rKey] = false; - } - } - - if (isset($rData['geoip_countries'])) { - $rArray['geoip_countries'] = array(); - - foreach ($rData['geoip_countries'] as $rCountry) { - $rArray['geoip_countries'][] = $rCountry; - } - } else { - $rArray['geoip_countries'] = array(); - } - - if (isset($rData['domain_name'])) { - $rArray['domain_name'] = implode(',', $rData['domain_name']); - } else { - $rArray['domain_name'] = ''; - } - - if (strlen($rData['server_ip']) != 0 && filter_var($rData['server_ip'], FILTER_VALIDATE_IP)) { - - - if (!checkExists('servers', 'server_ip', $rData['server_ip'], 'id', $rArray['id'])) { - - - $rArray['server_type'] = 1; - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `servers`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - - if (!file_exists(CACHE_TMP_PATH . 'servers')) { - } else { - unlink(CACHE_TMP_PATH . 'servers'); - } - - if (!file_exists(CACHE_TMP_PATH . 'proxy_servers')) { - } else { - unlink(CACHE_TMP_PATH . 'proxy_servers'); - } - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - - return array('status' => STATUS_EXISTS_IP, 'data' => $rData); - } - - return array('status' => STATUS_INVALID_IP, 'data' => $rData); - } else { - exit(); - } - } else { + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return ServerService::processProxy($rData, self::$db); } public static function installServer($rData) { - if (self::checkMinimumRequirements($rData)) { - if (hasPermissions('adv', 'add_server')) { - $rParentIDs = array(); - - if (isset($rData['update_sysctl'])) { - $rUpdateSysctl = 1; - } else { - $rUpdateSysctl = 0; - } - - if (isset($rData['use_private_ip'])) { - $rPrivateIP = 1; - } else { - $rPrivateIP = 0; - } - - if ($rData['type'] == 1) { - foreach (json_decode($rData['parent_id'], true) as $rServerID) { - if (self::$rServers[$rServerID]['server_type'] == 0) { - $rParentIDs[] = intval($rServerID); - } - } - } - - if (isset($rData['edit'])) { - if ($rData['type'] == 1) { - $rServer = self::$rProxyServers[$rData['edit']]; - } else { - $rServer = self::$rServers[$rData['edit']]; - } - - if (!$rServer) { - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - - self::$db->query('UPDATE `servers` SET `status` = 3, `parent_id` = ? WHERE `id` = ?;', '[' . implode(',', $rParentIDs) . ']', $rServer['id']); - - if ($rData['type'] == 1) { - $rCommand = PHP_BIN . ' ' . CLI_PATH . 'balancer.php ' . intval($rData['type']) . ' ' . intval($rServer['id']) . ' ' . intval($rData['ssh_port']) . ' ' . escapeshellarg($rData['root_username']) . ' ' . escapeshellarg($rData['root_password']) . ' ' . intval($rData['http_broadcast_port']) . ' ' . intval($rData['https_broadcast_port']) . ' ' . intval($rUpdateSysctl) . ' ' . intval($rPrivateIP) . ' "' . json_encode($rParentIDs) . '" > "' . BIN_PATH . 'install/' . intval($rServer['id']) . '.install" 2>/dev/null &'; - } else { - $rCommand = PHP_BIN . ' ' . CLI_PATH . 'balancer.php ' . intval($rData['type']) . ' ' . intval($rServer['id']) . ' ' . intval($rData['ssh_port']) . ' ' . escapeshellarg($rData['root_username']) . ' ' . escapeshellarg($rData['root_password']) . ' 80 443 ' . intval($rUpdateSysctl) . ' > "' . BIN_PATH . 'install/' . intval($rServer['id']) . '.install" 2>/dev/null &'; - } - - shell_exec($rCommand); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rServer['id'])); - } - - $rArray = verifyPostTable('servers', $rData); - $rArray['status'] = 3; - unset($rArray['id']); - - if (strlen($rArray['server_ip']) != 0 && filter_var($rArray['server_ip'], FILTER_VALIDATE_IP)) { - - if ($rData['type'] == 1) { - $rArray['server_type'] = 1; - $rArray['parent_id'] = '[' . implode(',', $rParentIDs) . ']'; - } else { - $rArray['server_type'] = 0; - } - - $rArray['network_interface'] = 'auto'; - $rPrepare = prepareArray($rArray); - $rQuery = 'INSERT INTO `servers`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - - if ($rArray['server_type'] == 0) { - CoreUtilities::grantPrivileges($rArray['server_ip']); - } - - if ($rData['type'] == 1) { - $rCommand = PHP_BIN . ' ' . CLI_PATH . 'balancer.php ' . intval($rData['type']) . ' ' . intval($rInsertID) . ' ' . intval($rData['ssh_port']) . ' ' . escapeshellarg($rData['root_username']) . ' ' . escapeshellarg($rData['root_password']) . ' ' . intval($rData['http_broadcast_port']) . ' ' . intval($rData['https_broadcast_port']) . ' ' . intval($rUpdateSysctl) . ' ' . intval($rPrivateIP) . ' "' . json_encode($rParentIDs) . '" > "' . BIN_PATH . 'install/' . intval($rInsertID) . '.install" 2>/dev/null &'; - } else { - $rCommand = PHP_BIN . ' ' . CLI_PATH . 'balancer.php ' . intval($rData['type']) . ' ' . intval($rInsertID) . ' ' . intval($rData['ssh_port']) . ' ' . escapeshellarg($rData['root_username']) . ' ' . escapeshellarg($rData['root_password']) . ' 80 443 ' . intval($rUpdateSysctl) . ' > "' . BIN_PATH . 'install/' . intval($rInsertID) . '.install" 2>/dev/null &'; - } - - shell_exec($rCommand); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - - return array('status' => STATUS_INVALID_IP, 'data' => $rData); - } - - exit(); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return ServerService::install($rData, self::$db, self::$rServers, self::$rProxyServers); } public static function editSettings($rData) { - if (self::checkMinimumRequirements($rData)) { - foreach (array('user_agent', 'http_proxy', 'cookie', 'headers') as $rKey) { - self::$db->query('UPDATE `streams_arguments` SET `argument_default_value` = ? WHERE `argument_key` = ?;', ($rData[$rKey] ?: null), $rKey); - unset($rData[$rKey]); - } - $rArray = verifyPostTable('settings', $rData, true); - - foreach (array('php_loopback', 'restreamer_bypass_proxy', 'request_prebuffer', 'modal_edit', 'group_buttons', 'enable_search', 'on_demand_checker', 'ondemand_balance_equal', 'disable_mag_token', 'allow_cdn_access', 'dts_legacy_ffmpeg', 'mag_load_all_channels', 'disable_xmltv_restreamer', 'disable_playlist_restreamer', 'ffmpeg_warnings', 'reseller_ssl_domain', 'extract_subtitles', 'show_category_duplicates', 'vod_sort_newest', 'header_stats', 'mag_keep_extension', 'keep_protocol', 'read_native_hls', 'player_allow_playlist', 'player_allow_bouquet', 'player_hide_incompatible', 'player_allow_hevc', 'force_epg_timezone', 'check_vod', 'ignore_keyframes', 'save_login_logs', 'save_restart_logs', 'mag_legacy_redirect', 'restrict_playlists', 'monitor_connection_status', 'kill_rogue_ffmpeg', 'show_images', 'on_demand_instant_off', 'on_demand_failure_exit', 'playlist_from_mysql', 'ignore_invalid_users', 'legacy_mag_auth', 'ministra_allow_blank', 'block_proxies', 'block_streaming_servers', 'ip_subnet_match', 'debug_show_errors', 'enable_debug_stalker', 'restart_php_fpm', 'restream_deny_unauthorised', 'api_probe', 'legacy_panel_api', 'hide_failures', 'verify_host', 'encrypt_playlist', 'encrypt_playlist_restreamer', 'mag_disable_ssl', 'legacy_get', 'legacy_xmltv', 'save_closed_connection', 'show_tickets', 'stream_logs_save', 'client_logs_save', 'streams_grouped', 'cloudflare', 'cleanup', 'dashboard_stats', 'dashboard_status', 'dashboard_map', 'dashboard_display_alt', 'recaptcha_enable', 'ip_logout', 'disable_player_api', 'disable_playlist', 'disable_xmltv', 'disable_enigma2', 'disable_ministra', 'enable_isp_lock', 'block_svp', 'disable_ts', 'disable_ts_allow_restream', 'disable_hls', 'disable_hls_allow_restream', 'disable_rtmp', 'disable_rtmp_allow_restream', 'case_sensitive_line', 'county_override_1st', 'disallow_2nd_ip_con', 'use_mdomain_in_lists', 'encrypt_hls', 'disallow_empty_user_agents', 'detect_restream_block_user', 'download_images', 'api_redirect', 'use_buffer', 'audio_restart_loss', 'show_isps', 'priority_backup', 'rtmp_random', 'show_connected_video', 'show_not_on_air_video', 'show_banned_video', 'show_expired_video', 'show_expiring_video', 'show_all_category_mag', 'always_enabled_subtitles', 'enable_connection_problem_indication', 'show_tv_channel_logo', 'show_channel_logo_in_preview', 'disable_trial', 'restrict_same_ip', 'js_navigate') as $rSetting) { - if (isset($rData[$rSetting])) { - $rArray[$rSetting] = 1; - } else { - $rArray[$rSetting] = 0; - } - } - - if (isset($rData['allowed_stb_types_for_local_recording'])) { - } else { - $rArray['allowed_stb_types_for_local_recording'] = array(); - } - - if (isset($rData['allowed_stb_types'])) { - } else { - $rArray['allowed_stb_types'] = array(); - } - - if (isset($rData['allow_countries'])) { - } else { - $rArray['allow_countries'] = array('ALL'); - } - - if ($rArray['mag_legacy_redirect']) { - if (!file_exists(MAIN_HOME . 'www/c/')) { - self::$db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`) VALUES(?, ?, ?);', SERVER_ID, time(), json_encode(array('action' => 'enable_ministra'))); - } - } else { - if (file_exists(MAIN_HOME . 'www/c/')) { - self::$db->query('INSERT INTO `signals`(`server_id`, `time`, `custom_data`) VALUES(?, ?, ?);', SERVER_ID, time(), json_encode(array('action' => 'disable_ministra'))); - } - } - - if (100 >= $rArray['search_items']) { - } else { - $rArray['search_items'] = 100; - } - - if ($rArray['search_items'] > 0) { - } else { - $rArray['search_items'] = 1; - } - - $rPrepare = prepareArray($rArray); - - if (0 >= count($rPrepare['data'])) { - } else { - $rQuery = 'UPDATE `settings` SET ' . $rPrepare['update'] . ';'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - clearSettingsCache(); - - return array('status' => STATUS_SUCCESS); - } - - return array('status' => STATUS_FAILURE); - } - } else { + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return SettingsService::edit(self::$db, $rData, 'clearSettingsCache'); } public static function editBackupSettings($rData) { - if (self::checkMinimumRequirements($rData)) { - $rArray = verifyPostTable('settings', $rData, true); - - foreach (array('dropbox_remote') as $rSetting) { - if (isset($rData[$rSetting])) { - $rArray[$rSetting] = 1; - } else { - $rArray[$rSetting] = 0; - } - } - - if (isset($rData['allowed_stb_types_for_local_recording'])) { - } else { - $rArray['allowed_stb_types_for_local_recording'] = array(); - } - - if (isset($rData['allowed_stb_types'])) { - } else { - $rArray['allowed_stb_types'] = array(); - } - - $rPrepare = prepareArray($rArray); - - if (0 >= count($rPrepare['data'])) { - } else { - $rQuery = 'UPDATE `settings` SET ' . $rPrepare['update'] . ';'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - clearSettingsCache(); - - return array('status' => STATUS_SUCCESS); - } - - return array('status' => STATUS_FAILURE); - } - } else { - - - + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return SettingsService::editBackup(self::$db, $rData, 'clearSettingsCache'); } public static function editCacheCron($rData) { - if (self::checkMinimumRequirements($rData)) { - $rCheck = array(false, false); - $rCron = array('*', '*', '*', '*', '*'); - $rPattern = '/^[0-9\\/*,-]+$/'; - $rCron[0] = $rData['minute']; - preg_match($rPattern, $rCron[0], $rMatches); - $rCheck[0] = 0 < count($rMatches); - $rCron[1] = $rData['hour']; - preg_match($rPattern, $rCron[1], $rMatches); - $rCheck[1] = 0 < count($rMatches); - $rCronOutput = implode(' ', $rCron); - - if (isset($rData['cache_changes'])) { - $rCacheChanges = true; - } else { - $rCacheChanges = false; - } - - if ($rCheck[0] && $rCheck[1]) { - self::$db->query("UPDATE `crontab` SET `time` = ? WHERE `filename` = 'cache_engine.php';", $rCronOutput); - self::$db->query('UPDATE `settings` SET `cache_thread_count` = ?, `cache_changes` = ?;', $rData['cache_thread_count'], $rCacheChanges); - - if (!file_exists(TMP_PATH . 'crontab')) { - } else { - unlink(TMP_PATH . 'crontab'); - } - - clearSettingsCache(); - - return array('status' => STATUS_SUCCESS); - } - - return array('status' => STATUS_FAILURE); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return SettingsService::editCacheCron(self::$db, $rData, 'clearSettingsCache'); } public static function editPlexSettings($rData) { @@ -4857,1210 +2950,83 @@ class API { } public static function massEditStreams($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - $rArray = array(); - - - if (!isset($rData['c_days_to_restart'])) { - } else { - if (isset($rData['days_to_restart']) && preg_match('/^(?:2[0-3]|[01][0-9]):[0-5][0-9]$/', $rData['time_to_restart'])) { - $rTimeArray = array('days' => array(), 'at' => $rData['time_to_restart']); - - foreach ($rData['days_to_restart'] as $rID => $rDay) { - $rTimeArray['days'][] = $rDay; - } - $rArray['auto_restart'] = json_encode($rTimeArray); - } else { - $rArray['auto_restart'] = ''; - } - } - - foreach (array('gen_timestamps', 'allow_record', 'rtmp_output', 'fps_restart', 'stream_all', 'read_native') as $rKey) { - if (!isset($rData['c_' . $rKey])) { - } else { - if (isset($rData[$rKey])) { - $rArray[$rKey] = 1; - } else { - $rArray[$rKey] = 0; - } - } - } - - if (!isset($rData['c_direct_source'])) { - } else { - if (isset($rData['direct_source'])) { - $rArray['direct_source'] = 1; - } else { - $rArray['direct_source'] = 0; - $rArray['direct_proxy'] = 0; - } - } - - if (!isset($rData['c_direct_proxy'])) { - } else { - if (isset($rData['direct_proxy'])) { - $rArray['direct_proxy'] = 1; - $rArray['direct_source'] = 1; - } else { - $rArray['direct_proxy'] = 0; - } - } - - foreach (array('tv_archive_server_id', 'vframes_server_id', 'tv_archive_duration', 'delay_minutes', 'probesize_ondemand', 'fps_threshold', 'llod') as $rKey) { - if (!isset($rData['c_' . $rKey])) { - } else { - $rArray[$rKey] = intval($rData[$rKey]); - } - } - - if (!isset($rData['c_custom_sid'])) { - } else { - $rArray['custom_sid'] = $rData['custom_sid']; - } - - if (!isset($rData['c_transcode_profile_id'])) { - } else { - $rArray['transcode_profile_id'] = $rData['transcode_profile_id']; - - if (0 < $rArray['transcode_profile_id']) { - $rArray['enable_transcode'] = 1; - } else { - $rArray['enable_transcode'] = 0; - } - } - - $rStreamIDs = json_decode($rData['streams'], true); - - if (0 >= count($rStreamIDs)) { - } else { - $rCategoryMap = array(); - - if (!(isset($rData['c_category_id']) && in_array($rData['category_id_type'], array('ADD', 'DEL')))) { - } else { - self::$db->query('SELECT `id`, `category_id` FROM `streams` WHERE `id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); - - foreach (self::$db->get_rows() as $rRow) { - $rCategoryMap[$rRow['id']] = (json_decode($rRow['category_id'], true) ?: array()); - } - } - - $rDeleteServers = $rStreamExists = array(); - self::$db->query('SELECT `stream_id`, `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); - - foreach (self::$db->get_rows() as $rRow) { - $rStreamExists[intval($rRow['stream_id'])][intval($rRow['server_id'])] = intval($rRow['server_stream_id']); - } - $rBouquets = getBouquets(); - $rDelOptions = $rAddBouquet = $rDelBouquet = array(); - $rOptQuery = $rAddQuery = ''; - - foreach ($rStreamIDs as $rStreamID) { - if (!isset($rData['c_category_id'])) { - } else { - $rCategories = array_map('intval', $rData['category_id']); - - if ($rData['category_id_type'] == 'ADD') { - foreach (($rCategoryMap[$rStreamID] ?: array()) as $rCategoryID) { - if (in_array($rCategoryID, $rCategories)) { - } else { - $rCategories[] = $rCategoryID; - } - } - } else { - if ($rData['category_id_type'] != 'DEL') { - } else { - $rNewCategories = $rCategoryMap[$rStreamID]; - - foreach ($rCategories as $rCategoryID) { - if (($rKey = array_search($rCategoryID, $rNewCategories)) === false) { - } else { - unset($rNewCategories[$rKey]); - } - } - $rCategories = $rNewCategories; - } - } - - $rArray['category_id'] = '[' . implode(',', $rCategories) . ']'; - } - - $rPrepare = prepareArray($rArray); - - if (0 >= count($rPrepare['data'])) { - } else { - $rPrepare['data'][] = $rStreamID; - $rQuery = 'UPDATE `streams` SET ' . $rPrepare['update'] . ' WHERE `id` = ?;'; - self::$db->query($rQuery, ...$rPrepare['data']); - } - - if (!isset($rData['c_server_tree'])) { - } else { - $rStreamsAdded = array(); - $rServerTree = json_decode($rData['server_tree_data'], true); - - foreach ($rServerTree as $rServer) { - if ($rServer['parent'] == '#') { - } else { - $rServerID = intval($rServer['id']); - - if (in_array($rData['server_type'], array('ADD', 'SET'))) { - $rOD = intval(in_array($rServerID, ($rData['on_demand'] ?: array()))); - - if ($rServer['parent'] == 'source') { - $rParent = null; - } else { - $rParent = intval($rServer['parent']); - } - - $rStreamsAdded[] = $rServerID; - - if (isset($rStreamExists[$rStreamID][$rServerID])) { - self::$db->query('UPDATE `streams_servers` SET `parent_id` = ?, `on_demand` = ? WHERE `server_stream_id` = ?;', $rParent, $rOD, $rStreamExists[$rStreamID][$rServerID]); - } else { - $rAddQuery .= '(' . intval($rStreamID) . ', ' . intval($rServerID) . ', ' . (($rParent ?: 'NULL')) . ', ' . $rOD . '),'; - } - } else { - if (!isset($rStreamExists[$rStreamID][$rServerID])) { - } else { - $rDeleteServers[$rServerID][] = $rStreamID; - } - } - } - } - - if ($rData['server_type'] != 'SET') { - } else { - foreach ($rStreamExists[$rStreamID] as $rServerID => $rDBID) { - if (in_array($rServerID, $rStreamsAdded)) { - } else { - $rDeleteServers[$rServerID][] = $rStreamID; - } - } - } - } - - if (!isset($rData['c_user_agent'])) { - } else { - if (!(isset($rData['user_agent']) && 0 < strlen($rData['user_agent']))) { - } else { - $rDelOptions[1][] = $rStreamID; - $rOptQuery .= '(' . intval($rStreamID) . ', 1, ' . self::$db->escape($rData['user_agent']) . '),'; - } - } - - if (!isset($rData['c_http_proxy'])) { - } else { - if (!(isset($rData['http_proxy']) && 0 < strlen($rData['http_proxy']))) { - } else { - $rDelOptions[2][] = $rStreamID; - $rOptQuery .= '(' . intval($rStreamID) . ', 2, ' . self::$db->escape($rData['http_proxy']) . '),'; - } - } - - if (!isset($rData['c_cookie'])) { - } else { - if (!(isset($rData['cookie']) && 0 < strlen($rData['cookie']))) { - } else { - $rDelOptions[17][] = $rStreamID; - $rOptQuery .= '(' . intval($rStreamID) . ', 17, ' . self::$db->escape($rData['cookie']) . '),'; - } - } - - if (!isset($rData['c_headers'])) { - } else { - if (!(isset($rData['headers']) && 0 < strlen($rData['headers']))) { - } else { - $rDelOptions[19][] = $rStreamID; - $rOptQuery .= '(' . intval($rStreamID) . ', 19, ' . self::$db->escape($rData['headers']) . '),'; - } - } - - if (!isset($rData['c_bouquets'])) { - } else { - if ($rData['bouquets_type'] == 'SET') { - foreach ($rData['bouquets'] as $rBouquet) { - $rAddBouquet[$rBouquet][] = $rStreamID; - } - - foreach ($rBouquets as $rBouquet) { - if (in_array($rBouquet['id'], $rData['bouquets'])) { - } else { - $rDelBouquet[$rBouquet['id']][] = $rStreamID; - } - } - } else { - if ($rData['bouquets_type'] == 'ADD') { - foreach ($rData['bouquets'] as $rBouquet) { - $rAddBouquet[$rBouquet][] = $rStreamID; - } - } else { - if ($rData['bouquets_type'] != 'DEL') { - } else { - foreach ($rData['bouquets'] as $rBouquet) { - $rDelBouquet[$rBouquet][] = $rStreamID; - } - } - } - } - } - } - - foreach ($rDeleteServers as $rServerID => $rDeleteIDs) { - deleteStreamsByServer($rDeleteIDs, $rServerID, false); - } - - foreach ($rDelOptions as $rOptionID => $rDelIDs) { - $rDelIDs = array_unique(array_map('intval', $rDelIDs)); - if (0 >= count($rDelIDs)) { - } else { - self::$db->query('DELETE FROM `streams_options` WHERE `stream_id` IN (' . implode(',', $rDelIDs) . ') AND `argument_id` = ?;', intval($rOptionID)); - } - } - - if (empty($rOptQuery)) { - } else { - $rOptQuery = rtrim($rOptQuery, ','); - self::$db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES ' . $rOptQuery . ';'); - } - - foreach ($rAddBouquet as $rBouquetID => $rAddIDs) { - addToBouquet('stream', $rBouquetID, $rAddIDs); - } - - foreach ($rDelBouquet as $rBouquetID => $rRemIDs) { - removeFromBouquet('stream', $rBouquetID, $rRemIDs); - } - - if (empty($rAddQuery)) { - } else { - $rAddQuery = rtrim($rAddQuery, ','); - self::$db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`, `parent_id`, `on_demand`) VALUES ' . $rAddQuery . ';'); - } - - CoreUtilities::updateStreams($rStreamIDs); - - if (!isset($rData['restart_on_edit'])) { - } else { - APIRequest(array('action' => 'stream', 'sub' => 'start', 'stream_ids' => array_values($rStreamIDs))); - } - } - - return array('status' => STATUS_SUCCESS); - } else { + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return StreamService::massEdit($rData, self::$db); } public static function massEditChannels($rData) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (self::checkMinimumRequirements($rData)) { - $rArray = array(); - - - foreach (array('allow_record', 'rtmp_output') as $rKey) { - if (!isset($rData['c_' . $rKey])) { - } else { - if (isset($rData[$rKey])) { - $rArray[$rKey] = 1; - } else { - $rArray[$rKey] = 0; - } - } - } - - if (!isset($rData['c_transcode_profile_id'])) { - } else { - $rArray['transcode_profile_id'] = $rData['transcode_profile_id']; - - if (0 < $rArray['transcode_profile_id']) { - $rArray['enable_transcode'] = 1; - } else { - $rArray['enable_transcode'] = 0; - } - } - - $rStreamIDs = json_decode($rData['streams'], true); - - if (0 >= count($rStreamIDs)) { - } else { - $rCategoryMap = array(); - - if (!(isset($rData['c_category_id']) && in_array($rData['category_id_type'], array('ADD', 'DEL')))) { - } else { - self::$db->query('SELECT `id`, `category_id` FROM `streams` WHERE `id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); - - foreach (self::$db->get_rows() as $rRow) { - $rCategoryMap[$rRow['id']] = (json_decode($rRow['category_id'], true) ?: array()); - } - } - - $rDeleteServers = $rProcessServers = $rStreamExists = array(); - self::$db->query('SELECT `stream_id`, `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` IN (' . implode(',', array_map('intval', $rStreamIDs)) . ');'); - - foreach (self::$db->get_rows() as $rRow) { - $rStreamExists[intval($rRow['stream_id'])][intval($rRow['server_id'])] = intval($rRow['server_stream_id']); - $rProcessServers[intval($rRow['stream_id'])][] = intval($rRow['server_id']); - } - $rBouquets = getBouquets(); - $rDelOptions = $rAddBouquet = $rDelBouquet = array(); - $rEncQuery = $rAddQuery = ''; - - foreach ($rStreamIDs as $rStreamID) { - if (!isset($rData['c_category_id'])) { - } else { - $rCategories = array_map('intval', $rData['category_id']); - - if ($rData['category_id_type'] == 'ADD') { - foreach (($rCategoryMap[$rStreamID] ?: array()) as $rCategoryID) { - if (in_array($rCategoryID, $rCategories)) { - } else { - $rCategories[] = $rCategoryID; - } - } - } else { - if ($rData['category_id_type'] != 'DEL') { - } else { - $rNewCategories = $rCategoryMap[$rStreamID]; - - foreach ($rCategories as $rCategoryID) { - if (($rKey = array_search($rCategoryID, $rNewCategories)) === false) { - } else { - unset($rNewCategories[$rKey]); - } - } - $rCategories = $rNewCategories; - } - } - - $rArray['category_id'] = '[' . implode(',', $rCategories) . ']'; - } - - $rPrepare = prepareArray($rArray); - - if (0 >= count($rPrepare['data'])) { - } else { - $rPrepare['data'][] = $rStreamID; - $rQuery = 'UPDATE `streams` SET ' . $rPrepare['update'] . ' WHERE `id` = ?;'; - self::$db->query($rQuery, ...$rPrepare['data']); - } - - if (!isset($rData['c_server_tree'])) { - } else { - $rStreamsAdded = array(); - $rServerTree = json_decode($rData['server_tree_data'], true); - - foreach ($rServerTree as $rServer) { - if ($rServer['parent'] == '#') { - } else { - $rServerID = intval($rServer['id']); - - if (in_array($rData['server_type'], array('ADD', 'SET'))) { - $rStreamsAdded[] = $rServerID; - $rOD = intval(in_array($rServerID, ($rData['on_demand'] ?: array()))); - - if ($rServer['parent'] == 'source') { - $rParent = null; - } else { - $rParent = intval($rServer['parent']); - } - - if (isset($rStreamExists[$rServerID])) { - self::$db->query('UPDATE `streams_servers` SET `parent_id` = ?, `on_demand` = ? WHERE `server_stream_id` = ?;', $rParent, $rOD, $rStreamExists[$rServerID]); - } else { - $rAddQuery .= '(' . intval($rStreamID) . ', ' . intval($rServerID) . ', ' . (($rParent ?: 'NULL')) . ', ' . $rOD . '),'; - } - - $rProcessServers[$rStreamID][] = $rServerID; - } else { - if (!isset($rStreamExists[$rStreamID][$rServerID])) { - } else { - $rDeleteServers[$rServerID][] = $rStreamID; - } - } - } - } - - if ($rData['server_type'] != 'SET') { - } else { - foreach ($rStreamExists as $rServerID => $rDBID) { - if (in_array($rServerID, $rStreamsAdded)) { - } else { - $rDeleteServers[$rServerID][] = $rStreamID; - - if (($rKey = array_search($rServerID, $rProcessServers[$rStreamID])) === false) { - } else { - unset($rProcessServers[$rStreamID][$rKey]); - } - } - } - } - } - - if (!isset($rData['c_bouquets'])) { - } else { - if ($rData['bouquets_type'] == 'SET') { - foreach ($rData['bouquets'] as $rBouquet) { - $rAddBouquet[$rBouquet][] = $rStreamID; - } - - foreach ($rBouquets as $rBouquet) { - if (in_array($rBouquet['id'], $rData['bouquets'])) { - } else { - $rDelBouquet[$rBouquet['id']][] = $rStreamID; - } - } - } else { - if ($rData['bouquets_type'] == 'ADD') { - foreach ($rData['bouquets'] as $rBouquet) { - $rAddBouquet[$rBouquet][] = $rStreamID; - } - } else { - if ($rData['bouquets_type'] != 'DEL') { - } else { - foreach ($rData['bouquets'] as $rBouquet) { - $rDelBouquet[$rBouquet][] = $rStreamID; - } - } - } - } - } - - if (!isset($rData['reencode_on_edit'])) { - } else { - foreach ($rProcessServers[$rStreamID] as $rServerID) { - $rEncQuery .= "('channel', " . intval($rStreamID) . ', ' . intval($rServerID) . ', ' . time() . '),'; - } - } - } - - foreach ($rDeleteServers as $rServerID => $rDeleteIDs) { - deleteStreamsByServer($rDeleteIDs, $rServerID, false); - } - - foreach ($rAddBouquet as $rBouquetID => $rAddIDs) { - addToBouquet('stream', $rBouquetID, $rAddIDs); - } - - foreach ($rDelBouquet as $rBouquetID => $rRemIDs) { - removeFromBouquet('stream', $rBouquetID, $rRemIDs); - } - - if (empty($rAddQuery)) { - } else { - $rAddQuery = rtrim($rAddQuery, ','); - self::$db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`, `parent_id`, `on_demand`) VALUES ' . $rAddQuery . ';'); - } - - CoreUtilities::updateStreams($rStreamIDs); - - if (isset($rData['reencode_on_edit'])) { - self::$db->query("UPDATE `streams_servers` SET `pids_create_channel` = '[]', `cchannel_rsources` = '[]' WHERE `stream_id` IN (" . implode(',', array_map('intval', $rStreamIDs)) . ');'); - - if (empty($rEncQuery)) { - } else { - $rEncQuery = rtrim($rEncQuery, ','); - self::$db->query('INSERT INTO `queue`(`type`, `stream_id`, `server_id`, `added`) VALUES ' . $rEncQuery . ';'); - } - - APIRequest(array('action' => 'stream', 'sub' => 'stop', 'stream_ids' => array_values($rStreamIDs))); - } else { - if (!isset($rData['restart_on_edit'])) { - } else { - APIRequest(array('action' => 'stream', 'sub' => 'start', 'stream_ids' => array_values($rStreamIDs))); - } - } - } - - return array('status' => STATUS_SUCCESS); - } else { - - - + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return ChannelService::massEdit($rData, self::$db); } public static function processStream($rData) { - if (self::checkMinimumRequirements($rData)) { - set_time_limit(0); - ini_set('mysql.connect_timeout', 0); - ini_set('max_execution_time', 0); - ini_set('default_socket_timeout', 0); - - if (isset($rData['edit'])) { - if (hasPermissions('adv', 'edit_stream')) { - $rArray = overwriteData(getStream($rData['edit']), $rData); - } else { - exit(); - } - } else { - if (hasPermissions('adv', 'add_stream')) { - $rArray = verifyPostTable('streams', $rData); - $rArray['type'] = 1; - $rArray['added'] = time(); - unset($rArray['id']); - } else { - exit(); - } - } - - if (isset($rData['days_to_restart']) && preg_match('/^(?:2[0-3]|[01][0-9]):[0-5][0-9]$/', $rData['time_to_restart'])) { - $rTimeArray = array('days' => array(), 'at' => $rData['time_to_restart']); - - foreach ($rData['days_to_restart'] as $rID => $rDay) { - $rTimeArray['days'][] = $rDay; - } - $rArray['auto_restart'] = $rTimeArray; - } else { - $rArray['auto_restart'] = ''; - } - - foreach (array('fps_restart', 'gen_timestamps', 'allow_record', 'rtmp_output', 'stream_all', 'direct_source', 'direct_proxy', 'read_native') as $rKey) { - if (isset($rData[$rKey])) { - $rArray[$rKey] = 1; - } else { - $rArray[$rKey] = 0; - } - } - - if (!$rArray['transcode_profile_id']) { - $rArray['transcode_profile_id'] = 0; - } - - if ($rArray['transcode_profile_id'] > 0) { - $rArray['enable_transcode'] = 1; - } - - if (isset($rData['restart_on_edit'])) { - $rRestart = true; - } else { - $rRestart = false; - } - - $rReview = false; - $rImportStreams = array(); - - if (isset($rData['review'])) { - $rReview = true; - - foreach ($rData['review'] as $rImportStream) { - if ($rImportStream['channel_id'] || !$rImportStream['tvg_id']) { - } else { - $rEPG = findEPG($rImportStream['tvg_id']); - - if (!isset($rEPG)) { - } else { - $rImportStream['epg_id'] = $rEPG['epg_id']; - $rImportStream['channel_id'] = $rEPG['channel_id']; - - if (empty($rEPG['epg_lang'])) { - } else { - $rImportStream['epg_lang'] = $rEPG['epg_lang']; - } - } - } - - $rImportStreams[] = $rImportStream; - } - } else { - if (isset($_FILES['m3u_file'])) { - if (hasPermissions('adv', 'import_streams')) { - - - if (!(empty($_FILES['m3u_file']['tmp_name']) || strtolower(pathinfo(explode('?', $_FILES['m3u_file']['name'])[0], PATHINFO_EXTENSION)) != 'm3u')) { - - - $rResults = parseM3U($_FILES['m3u_file']['tmp_name']); - - if (0 >= count($rResults)) { - } else { - $rEPGDatabase = $rSourceDatabase = $rStreamDatabase = array(); - self::$db->query('SELECT `id`, `stream_display_name`, `stream_source`, `channel_id` FROM `streams` WHERE `type` = 1;'); - - foreach (self::$db->get_rows() as $rRow) { - $rName = preg_replace('/[^A-Za-z0-9 ]/', '', strtolower($rRow['stream_display_name'])); - - if (empty($rName)) { - } else { - $rStreamDatabase[$rName] = $rRow['id']; - } - - $rEPGDatabase[$rRow['channel_id']] = $rRow['id']; - - foreach (json_decode($rRow['stream_source'], true) as $rSource) { - if (empty($rSource)) { - } else { - $rSourceDatabase[md5(preg_replace('(^https?://)', '', str_replace(' ', '%20', $rSource)))] = $rRow['id']; - } - } - } - $rEPGMatch = $rEPGScan = array(); - $i = 0; - - foreach ($rResults as $rResult) { - list($rTag) = $rResult->getExtTags(); - - if (!$rTag) { - } else { - if (!$rTag->getAttribute('tvg-id')) { - } else { - $rID = $rTag->getAttribute('tvg-id'); - $rEPGScan[$rID][] = $i; - } - } - - $i++; - } - - if (0 >= count($rEPGScan)) { - } else { - self::$db->query('SELECT `id`, `data` FROM `epg`;'); - - if (0 >= self::$db->num_rows()) { - } else { - foreach (self::$db->get_rows() as $rRow) { - foreach (json_decode($rRow['data'], true) as $rChannelID => $rChannelData) { - if (!isset($rEPGScan[$rChannelID])) { - } else { - if (0 < count($rChannelData['langs'])) { - $rEPGLang = $rChannelData['langs'][0]; - } else { - $rEPGLang = ''; - } - - foreach ($rEPGScan[$rChannelID] as $i) { - $rEPGMatch[$i] = array('channel_id' => $rChannelID, 'epg_lang' => $rEPGLang, 'epg_id' => intval($rRow['id'])); - } - } - } - } - } - } - - $i = 0; - - foreach ($rResults as $rResult) { - list($rTag) = $rResult->getExtTags(); - - if (!$rTag) { - } else { - $rURL = $rResult->getPath(); - $rImportArray = array('stream_source' => array($rURL), 'stream_icon' => ($rTag->getAttribute('tvg-logo') ?: ''), 'stream_display_name' => ($rTag->getTitle() ?: ''), 'epg_id' => null, 'epg_lang' => null, 'channel_id' => null); - - if (!$rTag->getAttribute('tvg-id')) { - } else { - $rEPG = ($rEPGMatch[$i] ?: null); - - if (!isset($rEPG)) { - } else { - $rImportArray['epg_id'] = $rEPG['epg_id']; - $rImportArray['channel_id'] = $rEPG['channel_id']; - - if (empty($rEPG['epg_lang'])) { - } else { - $rImportArray['epg_lang'] = $rEPG['epg_lang']; - } - } - } - - $rBackupID = $rExistsID = null; - $rSourceID = md5(preg_replace('(^https?://)', '', str_replace(' ', '%20', $rURL))); - - if (!isset($rSourceDatabase[$rSourceID])) { - } else { - $rExistsID = $rSourceDatabase[$rSourceID]; - } - - $rName = preg_replace('/[^A-Za-z0-9 ]/', '', strtolower($rTag->getTitle())); - - if (!empty($rName) && isset($rStreamDatabase[$rName])) { - $rBackupID = $rStreamDatabase[$rName]; - } else { - if (empty($rImportArray['channel_id']) || !isset($rEPGDatabase[$rImportArray['channel_id']])) { - } else { - $rBackupID = $rEPGDatabase[$rImportArray['channel_id']]; - } - } - - if ($rBackupID && !$rExistsID && isset($rData['add_source_as_backup'])) { - self::$db->query('SELECT `stream_source` FROM `streams` WHERE `id` = ?;', $rBackupID); - - if (0 >= self::$db->num_rows()) { - } else { - $rSources = (json_decode(self::$db->get_row()['stream_source'], true) ?: array()); - $rSources[] = $rURL; - self::$db->query('UPDATE `streams` SET `stream_source` = ? WHERE `id` = ?;', json_encode($rSources), $rBackupID); - $rImportStreams[] = array('update' => true, 'id' => $rBackupID); - } - } else { - if ($rExistsID && isset($rData['update_existing'])) { - $rImportArray['id'] = $rExistsID; - $rImportStreams[] = $rImportArray; - } else { - if ($rExistsID) { - } else { - $rImportStreams[] = $rImportArray; - } - } - } - } - - $i++; - } - } - } else { - return array('status' => STATUS_INVALID_FILE, 'data' => $rData); - } - } else { - exit(); - } - } else { - $rImportArray = array('stream_source' => array(), 'stream_icon' => $rArray['stream_icon'], 'stream_display_name' => $rArray['stream_display_name'], 'epg_id' => $rArray['epg_id'], 'epg_lang' => $rArray['epg_lang'], 'channel_id' => $rArray['channel_id']); - - if (isset($rData['stream_source'])) { - foreach ($rData['stream_source'] as $rID => $rURL) { - if (0 >= strlen($rURL)) { - } else { - $rImportArray['stream_source'][] = $rURL; - } - } - } - - $rImportStreams[] = $rImportArray; - } - } - - if (0 < count($rImportStreams)) { - $rBouquetCreate = array(); - $rCategoryCreate = array(); - - if ($rReview) { - } else { - foreach (json_decode($rData['bouquet_create_list'], true) as $rBouquet) { - $rPrepare = prepareArray(array('bouquet_name' => $rBouquet, 'bouquet_channels' => array(), 'bouquet_movies' => array(), 'bouquet_series' => array(), 'bouquet_radios' => array())); - $rQuery = 'INSERT INTO `bouquets`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (!self::$db->query($rQuery, ...$rPrepare['data'])) { - } else { - $rBouquetID = self::$db->last_insert_id(); - $rBouquetCreate[$rBouquet] = $rBouquetID; - } - } - - foreach (json_decode($rData['category_create_list'], true) as $rCategory) { - $rPrepare = prepareArray(array('category_type' => 'live', 'category_name' => $rCategory, 'parent_id' => 0, 'cat_order' => 99, 'is_adult' => 0)); - $rQuery = 'INSERT INTO `streams_categories`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (!self::$db->query($rQuery, ...$rPrepare['data'])) { - } else { - $rCategoryID = self::$db->last_insert_id(); - $rCategoryCreate[$rCategory] = $rCategoryID; - } - } - } - - foreach ($rImportStreams as $rImportStream) { - if (!$rImportStream['update']) { - $rImportArray = $rArray; - - if (!self::$rSettings['download_images']) { - } else { - $rImportStream['stream_icon'] = CoreUtilities::downloadImage($rImportStream['stream_icon'], 1); - } - - if ($rReview) { - $rImportArray['category_id'] = '[' . implode(',', array_map('intval', $rImportStream['category_id'])) . ']'; - $rBouquets = array_map('intval', $rImportStream['bouquets']); - unset($rImportStream['bouquets']); - } else { - $rBouquets = array(); - - foreach ($rData['bouquets'] as $rBouquet) { - if (isset($rBouquetCreate[$rBouquet])) { - $rBouquets[] = $rBouquetCreate[$rBouquet]; - } else { - if (!is_numeric($rBouquet)) { - } else { - $rBouquets[] = intval($rBouquet); - } - } - } - $rCategories = array(); - - foreach ($rData['category_id'] as $rCategory) { - if (isset($rCategoryCreate[$rCategory])) { - $rCategories[] = $rCategoryCreate[$rCategory]; - } else { - if (!is_numeric($rCategory)) { - } else { - $rCategories[] = intval($rCategory); - } - } - } - $rImportArray['category_id'] = '[' . implode(',', array_map('intval', $rCategories)) . ']'; - - if (isset($rData['adaptive_link']) && 0 < count($rData['adaptive_link'])) { - $rImportArray['adaptive_link'] = '[' . implode(',', array_map('intval', $rData['adaptive_link'])) . ']'; - } else { - $rImportArray['adaptive_link'] = null; - } - } - - foreach (array_keys($rImportStream) as $rKey) { - $rImportArray[$rKey] = $rImportStream[$rKey]; - } - - if (isset($rData['edit']) || isset($rImportStream['id'])) { - } else { - $rImportArray['order'] = getNextOrder(); - } - - $rImportArray['title_sync'] = ($rData['title_sync'] ?: null); - - if (!$rImportArray['title_sync']) { - } else { - list($rSyncID, $rSyncStream) = array_map('intval', explode('_', $rImportArray['title_sync'])); - self::$db->query('SELECT `stream_display_name` FROM `providers_streams` WHERE `provider_id` = ? AND `stream_id` = ?;', $rSyncID, $rSyncStream); - - if (self::$db->num_rows() != 1) { - } else { - $rImportArray['stream_display_name'] = self::$db->get_row()['stream_display_name']; - } - } - - $rPrepare = prepareArray($rImportArray); - $rQuery = 'REPLACE INTO `streams`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - $rStreamExists = array(); - - if (!(isset($rData['edit']) || isset($rImportStream['id']))) { - } else { - self::$db->query('SELECT `server_stream_id`, `server_id` FROM `streams_servers` WHERE `stream_id` = ?;', $rInsertID); - - foreach (self::$db->get_rows() as $rRow) { - $rStreamExists[intval($rRow['server_id'])] = intval($rRow['server_stream_id']); - } - } - - $rStreamsAdded = array(); - $rServerTree = json_decode($rData['server_tree_data'], true); - - foreach ($rServerTree as $rServer) { - if ($rServer['parent'] != '#') { - $rServerID = intval($rServer['id']); - $rStreamsAdded[] = $rServerID; - $rOD = intval(in_array($rServerID, ($rData['on_demand'] ?: array()))); - - if ($rServer['parent'] == 'source') { - $rParent = null; - } else { - $rParent = intval($rServer['parent']); - } - - if (isset($rStreamExists[$rServerID])) { - self::$db->query('UPDATE `streams_servers` SET `parent_id` = ?, `on_demand` = ? WHERE `server_stream_id` = ?;', $rParent, $rOD, $rStreamExists[$rServerID]); - } else { - self::$db->query('INSERT INTO `streams_servers`(`stream_id`, `server_id`, `parent_id`, `on_demand`) VALUES(?, ?, ?, ?);', $rInsertID, $rServerID, $rParent, $rOD); - } - } - } - - foreach ($rStreamExists as $rServerID => $rDBID) { - if (in_array($rServerID, $rStreamsAdded)) { - } else { - deleteStream($rInsertID, $rServerID, false, false); - } - } - self::$db->query('DELETE FROM `streams_options` WHERE `stream_id` = ?;', $rInsertID); - - if (!(isset($rData['user_agent']) && 0 < strlen($rData['user_agent']))) { - } else { - self::$db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 1, ?);', $rInsertID, $rData['user_agent']); - } - - if (!(isset($rData['http_proxy']) && 0 < strlen($rData['http_proxy']))) { - } else { - self::$db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 2, ?);', $rInsertID, $rData['http_proxy']); - } - - if (!(isset($rData['cookie']) && 0 < strlen($rData['cookie']))) { - } else { - self::$db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 17, ?);', $rInsertID, $rData['cookie']); - } - - if (!(isset($rData['headers']) && 0 < strlen($rData['headers']))) { - } else { - self::$db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 19, ?);', $rInsertID, $rData['headers']); - } - - if (isset($rData['skip_ffprobe']) && ($rData['skip_ffprobe'] == 'on' || $rData['skip_ffprobe'] == 1)) { - self::$db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 21, ?);', $rInsertID, '1'); - } - - if (isset($rData['force_input_acodec']) && strlen(trim($rData['force_input_acodec'])) > 0) { - self::$db->query('INSERT INTO `streams_options`(`stream_id`, `argument_id`, `value`) VALUES(?, 20, ?);', $rInsertID, trim($rData['force_input_acodec'])); - } - - if (!$rRestart) { - } else { - APIRequest(array('action' => 'stream', 'sub' => 'start', 'stream_ids' => array($rInsertID))); - } - - foreach ($rBouquets as $rBouquet) { - addToBouquet('stream', $rBouquet, $rInsertID); - } - - if (!(isset($rData['edit']) || isset($rImportStream['id']))) { - } else { - foreach (getBouquets() as $rBouquet) { - if (in_array($rBouquet['id'], $rBouquets)) { - } else { - removeFromBouquet('stream', $rBouquet['id'], $rInsertID); - } - } - } - - CoreUtilities::updateStream($rInsertID); - } else { - foreach ($rBouquetCreate as $rBouquet => $rID) { - self::$db->query('DELETE FROM `bouquets` WHERE `id` = ?;', $rID); - } - - foreach ($rCategoryCreate as $rCategory => $rID) { - self::$db->query('DELETE FROM `streams_categories` WHERE `id` = ?;', $rID); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - } - } - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } else { - return array('status' => STATUS_NO_SOURCES, 'data' => $rData); - } - } else { + if (!self::checkMinimumRequirements($rData)) { return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } + + return StreamService::process($rData, self::$db, self::$rSettings); } public static function orderCategories($rData) { - if (self::checkMinimumRequirements($rData)) { - $rPostCategories = json_decode($rData['categories'], true); - - if (0 >= count($rPostCategories)) { - } else { - foreach ($rPostCategories as $rOrder => $rPostCategory) { - self::$db->query('UPDATE `streams_categories` SET `cat_order` = ?, `parent_id` = 0 WHERE `id` = ?;', intval($rOrder) + 1, $rPostCategory['id']); - } - } - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + + return CategoryService::reorder($rData, self::$db); } public static function orderServers($rData) { - if (self::checkMinimumRequirements($rData)) { - $rPostServers = json_decode($rData['server_order'], true); - - if (count($rPostServers) > 0) { - foreach ($rPostServers as $rOrder => $rPostServer) { - self::$db->query('UPDATE `servers` SET `order` = ? WHERE `id` = ?;', intval($rOrder) + 1, $rPostServer['id']); - } - } - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + + return ServerService::reorder($rData, self::$db); } public static function processCategory($rData) { - if (self::checkMinimumRequirements($rData)) { - if (isset($rData['edit'])) { - $rArray = overwriteData(getCategory($rData['edit']), $rData); - } else { - $rArray = verifyPostTable('streams_categories', $rData); - $rArray['cat_order'] = 99; - unset($rArray['id']); - } - - if (isset($rData['is_adult'])) { - $rArray['is_adult'] = 1; - } else { - $rArray['is_adult'] = 0; - } - - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `streams_categories`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return CategoryService::process($rData, self::$db); } public static function moveStreams($rData) { - if (self::checkMinimumRequirements($rData)) { - $rType = intval($rData['content_type']); - $rSource = intval($rData['source_server']); - $rReplacement = intval($rData['replacement_server']); - - if (!(0 < $rSource && 0 < $rReplacement && $rSource != $rReplacement)) { - } else { - $rExisting = array(); - - if ($rType == 0) { - self::$db->query('SELECT `stream_id` FROM `streams_servers` WHERE `server_id` = ?;', $rReplacement); - - foreach (self::$db->get_rows() as $rRow) { - $rExisting[] = intval($rRow['stream_id']); - } - } else { - self::$db->query('SELECT `streams_servers`.`stream_id` FROM `streams_servers` LEFT JOIN `streams` ON `streams`.`id` = `streams_servers`.`stream_id` WHERE `streams_servers`.`server_id` = ? AND `streams`.`type` = ?;', $rReplacement, $rType); - - foreach (self::$db->get_rows() as $rRow) { - $rExisting[] = intval($rRow['stream_id']); - } - } - - self::$db->query('SELECT `stream_id` FROM `streams_servers` WHERE `server_id` = ?;', $rSource); - - foreach (self::$db->get_rows() as $rRow) { - if (!in_array(intval($rRow['stream_id']), $rExisting)) { - } else { - self::$db->query('DELETE FROM `streams_servers` WHERE `stream_id` = ? AND `server_id` = ?;', $rRow['stream_id'], $rSource); - } - } - - if ($rType == 0) { - self::$db->query('UPDATE `streams_servers` SET `server_id` = ? WHERE `server_id` = ?;', $rReplacement, $rSource); - } else { - self::$db->query('UPDATE `streams_servers` LEFT JOIN `streams` ON `streams`.`id` = `streams_servers`.`stream_id` SET `streams_servers`.`server_id` = ? WHERE `streams_servers`.`server_id` = ? AND `streams`.`type` = ?;', $rReplacement, $rSource, $rType); - } - } - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return StreamService::move($rData, self::$db); } public static function replaceDNS($rData) { - if (self::checkMinimumRequirements($rData)) { - $rOldDNS = str_replace('/', '\\/', $rData['old_dns']); - $rNewDNS = str_replace('/', '\\/', $rData['new_dns']); - self::$db->query('UPDATE `streams` SET `stream_source` = REPLACE(`stream_source`, ?, ?);', $rOldDNS, $rNewDNS); - - return array('status' => STATUS_SUCCESS); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return StreamService::replaceDNS($rData, self::$db); } public static function submitTicket($rData) { - if (self::checkMinimumRequirements($rData)) { - if (isset($rData['edit'])) { - $rArray = overwriteData(getTicket($rData['edit']), $rData); - } else { - $rArray = verifyPostTable('tickets', $rData); - unset($rArray['id']); - } - - if (!(strlen($rData['title']) == 0 && !isset($rData['respond']) || strlen($rData['message']) == 0)) { - - - - if (!isset($rData['respond'])) { - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `tickets`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - self::$db->query('INSERT INTO `tickets_replies`(`ticket_id`, `admin_reply`, `message`, `date`) VALUES(?, 0, ?, ?);', $rInsertID, $rData['message'], time()); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - - $rTicket = getTicket($rData['respond']); - - if ($rTicket) { - if (intval(self::$rUserInfo['id']) == intval($rTicket['member_id'])) { - self::$db->query('UPDATE `tickets` SET `admin_read` = 0, `user_read` = 1 WHERE `id` = ?;', $rData['respond']); - self::$db->query('INSERT INTO `tickets_replies`(`ticket_id`, `admin_reply`, `message`, `date`) VALUES(?, 0, ?, ?);', $rData['respond'], $rData['message'], time()); - } else { - self::$db->query('UPDATE `tickets` SET `admin_read` = 0, `user_read` = 0 WHERE `id` = ?;', $rData['respond']); - self::$db->query('INSERT INTO `tickets_replies`(`ticket_id`, `admin_reply`, `message`, `date`) VALUES(?, 1, ?, ?);', $rData['respond'], $rData['message'], time()); - } - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rData['respond'])); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); - } - - return array('status' => STATUS_INVALID_DATA, 'data' => $rData); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return TicketService::submit(self::$db, $rData, self::$rUserInfo, 'getTicket'); } public static function processUA($rData) { - if (self::checkMinimumRequirements($rData)) { - if (isset($rData['edit'])) { - $rArray = overwriteData(getUserAgent($rData['edit']), $rData); - } else { - $rArray = verifyPostTable('blocked_uas', $rData); - unset($rArray['id']); - } - - if (isset($rData['exact_match'])) { - $rArray['exact_match'] = true; - } else { - $rArray['exact_match'] = false; - } - - $rPrepare = prepareArray($rArray); - $rQuery = 'REPLACE INTO `blocked_uas`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');'; - - if (self::$db->query($rQuery, ...$rPrepare['data'])) { - $rInsertID = self::$db->last_insert_id(); - - return array('status' => STATUS_SUCCESS, 'data' => array('insert_id' => $rInsertID)); - } - - return array('status' => STATUS_FAILURE, 'data' => $rData); + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); } - - - return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + return BlocklistService::processUA(self::$db, $rData, 'getUserAgent'); } public static function processPlexSync($rData) { @@ -6205,6 +3171,14 @@ class API { } public static function massEditLines($rData) { + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return LineService::massEdit($rData); + } + + public static function massEditLinesLegacy($rData) { if (self::checkMinimumRequirements($rData)) { $rArray = array(); @@ -6322,6 +3296,14 @@ class API { } public static function massEditMags($rData) { + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return MagService::massEdit($rData); + } + + public static function massEditMagsLegacy($rData) { if (self::checkMinimumRequirements($rData)) { $rArray = array(); $rUserArray = array(); @@ -6512,6 +3494,14 @@ class API { } public static function massEditEnigmas($rData) { + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return EnigmaService::massEdit($rData); + } + + public static function massEditEnigmasLegacy($rData) { if (self::checkMinimumRequirements($rData)) { $rArray = array(); $rUserArray = array(); @@ -6653,6 +3643,14 @@ class API { } public static function massEditUsers($rData) { + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return UserService::massEdit($rData); + } + + public static function massEditUsersLegacy($rData) { if (self::checkMinimumRequirements($rData)) { $rArray = array(); @@ -6734,6 +3732,14 @@ class API { } public static function processLine($rData) { + if (!self::checkMinimumRequirements($rData)) { + return array('status' => STATUS_INVALID_INPUT, 'data' => $rData); + } + + return LineService::process($rData); + } + + public static function processLineLegacy($rData) { if (self::checkMinimumRequirements($rData)) { if (isset($rData['edit'])) { if (hasPermissions('adv', 'edit_user')) {