UNPKG

mirakurun

Version:

DVR Tuner Server for Japanese TV.

358 lines 14.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Service = void 0; const path_1 = require("path"); const fs_1 = require("fs"); const promises_1 = require("fs/promises"); const common_1 = require("./common"); const log = __importStar(require("./log")); const db = __importStar(require("./db")); const _1 = __importDefault(require("./_")); const Event_1 = __importDefault(require("./Event")); const ServiceItem_1 = __importDefault(require("./ServiceItem")); const { LOGO_DATA_DIR_PATH } = process.env; class Service { static getLogoDataPath(networkId, logoId) { if (typeof logoId !== "number" || logoId < 0) { throw new Error("Invalid `logoId`"); } return (0, path_1.join)(LOGO_DATA_DIR_PATH, `${networkId}_${logoId}.png`); } static async getLogoDataMTime(networkId, logoId) { if (typeof logoId !== "number" || logoId < 0) { return 0; } try { return (await (0, promises_1.stat)(Service.getLogoDataPath(networkId, logoId))).mtimeMs; } catch (e) { return 0; } } static async isLogoDataExists(networkId, logoId) { if (typeof logoId !== "number" || logoId < 0) { return false; } try { return (await (0, promises_1.stat)(Service.getLogoDataPath(networkId, logoId))).isFile(); } catch (e) { return false; } } static async loadLogoData(networkId, logoId) { if (typeof logoId !== "number" || logoId < 0) { return null; } try { return await (0, promises_1.readFile)(Service.getLogoDataPath(networkId, logoId)); } catch (e) { return null; } } static async saveLogoData(networkId, logoId, data, retrying = false) { log.info("Service.saveLogoData(): saving... (networkId=%d logoId=%d)", networkId, logoId); const path = Service.getLogoDataPath(networkId, logoId); try { await (0, promises_1.writeFile)(path, data, { encoding: "binary" }); } catch (e) { if (retrying === false) { const dirPath = (0, path_1.dirname)(path); if ((0, fs_1.existsSync)(dirPath) === false) { log.warn("Service.saveLogoData(): making directory `%s`... (networkId=%d logoId=%d)", dirPath, networkId, logoId); try { await (0, promises_1.mkdir)(dirPath, { recursive: true }); } catch (e) { throw e; } } log.warn("Service.saveLogoData(): retrying... (networkId=%d logoId=%d)", networkId, logoId); return this.saveLogoData(networkId, logoId, data, true); } throw e; } log.info("Service.saveLogoData(): saved. (networkId=%d logoId=%d)", networkId, logoId); } _items = []; _saveTimerId; get items() { return this._items; } add(item) { if (this.get(item.id) !== null) { return; } this._items.push(item); Event_1.default.emit("service", "create", item.export()); this.save(); } get(id, serviceId) { if (serviceId === undefined) { const l = this._items.length; for (let i = 0; i < l; i++) { if (this._items[i].id === id) { return this._items[i]; } } } else { const l = this._items.length; for (let i = 0; i < l; i++) { if (this._items[i].networkId === id && this._items[i].serviceId === serviceId) { return this._items[i]; } } } return null; } exists(id, serviceId) { return this.get(id, serviceId) !== null; } findByChannel(channel) { const items = []; const l = this._items.length; for (let i = 0; i < l; i++) { if (this._items[i].channel === channel) { items.push(this._items[i]); } } return items; } findByNetworkId(networkId) { const items = []; const l = this._items.length; for (let i = 0; i < l; i++) { if (this._items[i].networkId === networkId) { items.push(this._items[i]); } } return items; } findByNetworkIdWithLogoId(networkId, logoId) { const items = []; const l = this._items.length; for (let i = 0; i < l; i++) { if (this._items[i].networkId === networkId && this._items[i].logoId === logoId) { items.push(this._items[i]); } } return items; } save() { clearTimeout(this._saveTimerId); this._saveTimerId = setTimeout(() => this._save(), 1000 * 10); } async load() { log.debug("loading services..."); let updated = false; const services = await db.loadServices(_1.default.configIntegrity.channels, true); for (const service of services) { const channelItem = _1.default.channel.get(service.channel.type, service.channel.channel); if (channelItem === null) { updated = true; continue; } if (service.networkId === undefined || service.serviceId === undefined) { updated = true; continue; } if (service.logoData) { const logoDataPath = Service.getLogoDataPath(service.networkId, service.logoId); log.warn("migrating deprecated property `logoData` to file `%s` in service#%d (%s) db", logoDataPath, service.id, service.name); Service.saveLogoData(service.networkId, service.logoId, Buffer.from(service.logoData, "base64")); services.filter(s => s.networkId === service.networkId && s.logoId === service.logoId).forEach(s => { delete s.logoData; }); updated = true; } this.add(new ServiceItem_1.default(channelItem, service.networkId, service.serviceId, service.name, service.type, service.logoId, service.remoteControlKeyId, service.epgReady, service.epgUpdatedAt)); } if (updated) { this.save(); } setTimeout(() => this._initJobs(), 10000); } async _initJobs() { log.debug("init service jobs..."); for (const channelConfig of _1.default.config.channels) { if (channelConfig.isDisabled || !channelConfig.serviceId) { continue; } const channel = _1.default.channel.get(channelConfig.type, channelConfig.channel); if (!channel) { continue; } const serviceId = channelConfig.serviceId; if (this.findByChannel(channel).some(service => service.serviceId === serviceId)) { continue; } this._queueCheckToAdd(channel, serviceId); } _1.default.job.add({ key: "Service.Add.Scan.Find-Channels", name: "Service Add Scan [Find Targets]", fn: async () => { for (const channel of _1.default.channel.items) { if (this.findByChannel(channel).length > 0) { continue; } this._queueScanToAdd(channel); } }, readyFn: async () => { while (true) { if (_1.default.job.jobs.some(job => job.status !== "finished" && job.key.includes("Service.Add.Check."))) { await (0, common_1.sleep)(1000); continue; } return true; } } }); _1.default.job.add({ key: "Service.Updater.Add-Schedule", name: "Service Updater [Add Schedule]", fn: async () => { _1.default.job.addSchedule({ key: "Service.Updater", schedule: "5 6 * * *", job: { key: "Service.Updater", name: "Service Updater", fn: async () => { for (const channel of _1.default.channel.items) { if (this.findByChannel(channel).length === 0) { continue; } this._queueScanToUpdate(channel); } } } }); } }); } _save() { log.debug("saving services..."); db.saveServices(this._items.map(service => service.export()), _1.default.configIntegrity.channels); } _queueCheckToAdd(channel, serviceId) { _1.default.job.add({ key: `Service.Add.Check.${channel.type}.${channel.channel}.${serviceId}`, name: `Service Add Check ${channel.type}/${channel.channel}/${serviceId}`, fn: () => this._checkToAdd(channel, serviceId), readyFn: () => _1.default.tuner.readyForJob(channel), retryOnFail: true, retryMax: (1000 * 60 * 60 * 12) / (1000 * 60 * 3), retryDelay: 1000 * 60 * 3 }); } _queueScanToAdd(channel) { _1.default.job.add({ key: `Service.Add.Scan.${channel.type}.${channel.channel}`, name: `Service Add Scan ${channel.type}/${channel.channel}`, fn: async () => this._scan(channel, true), readyFn: () => _1.default.tuner.readyForJob(channel), retryOnFail: true, retryMax: (1000 * 60 * 60 * 12) / (1000 * 60 * 3), retryDelay: 1000 * 60 * 3 }); } _queueScanToUpdate(channel) { _1.default.job.add({ key: `Service.Update.Scan.${channel.type}.${channel.channel}`, name: `Service Update Scan ${channel.type}/${channel.channel}`, fn: async () => this._scan(channel, false), readyFn: () => _1.default.tuner.readyForJob(channel) }); } async _checkToAdd(channel, serviceId) { log.info("ChannelItem#'%s' serviceId=%d check has started", channel.name, serviceId); let services; try { services = await _1.default.tuner.getServices(channel); } catch (e) { log.warn("ChannelItem#'%s' serviceId=%d check has failed [%s]", channel.name, serviceId, e); throw new Error("Service check failed"); } const service = services.find(service => service.serviceId === serviceId); if (!service) { log.warn("ChannelItem#'%s' serviceId=%d check has failed [no service]", channel.name, serviceId); setTimeout(() => this._queueCheckToAdd(channel, serviceId), 3600000); return; } log.debug("ChannelItem#'%s' serviceId=%d: %s", channel.name, serviceId, JSON.stringify(service, null, " ")); this.add(new ServiceItem_1.default(channel, service.networkId, service.serviceId, service.name, service.type, service.logoId)); log.info("ChannelItem#'%s' serviceId=%d check has finished", channel.name, serviceId); } async _scan(channel, add) { log.info("ChannelItem#'%s' service scan has started", channel.name); let services; try { services = await _1.default.tuner.getServices(channel); } catch (e) { log.warn("ChannelItem#'%s' service scan has failed [%s]", channel.name, e); throw new Error("Service scan failed"); } log.debug("ChannelItem#'%s' services: %s", channel.name, JSON.stringify(services, null, " ")); services.forEach(service => { const item = this.get(service.networkId, service.serviceId); if (item !== null) { item.name = service.name; item.type = service.type; if (service.logoId > -1) { item.logoId = service.logoId; } item.remoteControlKeyId = service.remoteControlKeyId; } else if (add === true) { this.add(new ServiceItem_1.default(channel, service.networkId, service.serviceId, service.name, service.type, service.logoId, service.remoteControlKeyId)); } }); log.info("ChannelItem#'%s' service scan has finished", channel.name); } } exports.Service = Service; exports.default = Service; //# sourceMappingURL=Service.js.map