diff --git a/index.js b/index.js
index ac9cb50..442f8ec 100644
--- a/index.js
+++ b/index.js
@@ -10,14 +10,15 @@ const api = require('./src/api')
const dbMigration = require('./src/database-migration');
const video = require('./src/video')
const HDHR = require('./src/hdhr')
-const CacheImageService = require('./src/cache-image-service');
-const SettingsService = require('./src/services/settings-service');
+const FileCacheService = require('./src/services/file-cache-service');
+const CacheImageService = require('./src/services/cache-image-service');
const xmltv = require('./src/xmltv')
const Plex = require('./src/plex');
const channelCache = require('./src/channel-cache');
const constants = require('./src/constants')
const ChannelDB = require("./src/dao/channel-db");
+const M3uService = require("./src/services/m3u-service");
const FillerDB = require("./src/dao/filler-db");
const TVGuideService = require("./src/tv-guide-service");
const onShutdown = require("node-graceful-shutdown").onShutdown;
@@ -72,10 +73,14 @@ fillerDB = new FillerDB( path.join(process.env.DATABASE, 'filler') , channelDB,
db.connect(process.env.DATABASE, ['channels', 'plex-servers', 'ffmpeg-settings', 'plex-settings', 'xmltv-settings', 'hdhr-settings', 'db-version', 'client-id', 'cache-images', 'settings'])
+fileCache = new FileCacheService( path.join(process.env.DATABASE, 'cache') );
+cacheImageService = new CacheImageService(db, fileCache);
+m3uService = new M3uService(channelDB, fileCache, channelCache)
+
initDB(db, channelDB)
-const guideService = new TVGuideService(xmltv, db);
+const guideService = new TVGuideService(xmltv, db, cacheImageService);
@@ -185,16 +190,15 @@ app.get('/version.js', (req, res) => {
app.use('/images', express.static(path.join(process.env.DATABASE, 'images')))
app.use(express.static(path.join(__dirname, 'web/public')))
app.use('/images', express.static(path.join(process.env.DATABASE, 'images')))
-app.use('/cache/images', CacheImageService.routerInterceptor())
+app.use('/cache/images', cacheImageService.routerInterceptor())
app.use('/cache/images', express.static(path.join(process.env.DATABASE, 'cache/images')))
app.use('/favicon.svg', express.static(
path.join(__dirname, 'resources/favicon.svg')
) );
// API Routers
-app.use(api.router(db, channelDB, fillerDB, xmltvInterval, guideService ))
-app.use('/api/cache/images', CacheImageService.apiRouters())
-app.use('/api/settings/cache', SettingsService.apiRouters())
+app.use(api.router(db, channelDB, fillerDB, xmltvInterval, guideService, m3uService ))
+app.use('/api/cache/images', cacheImageService.apiRouters())
app.use(video.router( channelDB, fillerDB, db))
app.use(hdhr.router)
diff --git a/src/api.js b/src/api.js
index 37673dd..4f46328 100644
--- a/src/api.js
+++ b/src/api.js
@@ -10,11 +10,10 @@ const PlexServerDB = require('./dao/plex-server-db');
const Plex = require("./plex.js");
const FillerDB = require('./dao/filler-db');
const timeSlotsService = require('./services/time-slots-service');
-const M3uService = require('./services/m3u-service');
module.exports = { router: api }
-function api(db, channelDB, fillerDB, xmltvInterval, guideService ) {
- const m3uService = new M3uService(channelDB);
+function api(db, channelDB, fillerDB, xmltvInterval, guideService, _m3uService ) {
+ const m3uService = _m3uService;
const router = express.Router()
const plexServerDB = new PlexServerDB(channelDB, channelCache, db);
@@ -452,6 +451,7 @@ function api(db, channelDB, fillerDB, xmltvInterval, guideService ) {
_id: req.body._id,
cache: req.body.cache,
refresh: req.body.refresh,
+ enableImageCache: (req.body.enableImageCache === true),
file: xmltv.file,
}
);
@@ -612,24 +612,6 @@ function api(db, channelDB, fillerDB, xmltvInterval, guideService ) {
})
- // hls.m3u Download is not really working correctly right now
- router.get('/api/hls.m3u', async (req, res) => {
- try {
- res.type('text');
-
- const host = `${req.protocol}://${req.get('host')}`;
- const data = await m3uService.getChannelList(host, 'hls');
-
- res.send(data);
-
- } catch(err) {
- console.error(err);
- res.status(500).send("error");
- }
- })
-
-
-
function updateXmltv() {
xmltvInterval.updateXML()
diff --git a/src/channel-cache.js b/src/channel-cache.js
index 2a9fbdd..3588c96 100644
--- a/src/channel-cache.js
+++ b/src/channel-cache.js
@@ -4,6 +4,7 @@ let cache = {};
let programPlayTimeCache = {};
let fillerPlayTimeCache = {};
let configCache = {};
+let numbers = null;
async function getChannelConfig(channelDB, channelId) {
//with lazy-loading
@@ -21,6 +22,22 @@ async function getChannelConfig(channelDB, channelId) {
return configCache[channelId];
}
+async function getAllNumbers(channelDB) {
+ if (numbers === null) {
+ let n = channelDB.getAllChannelNumbers();
+ numbers = n;
+ }
+ return numbers;
+}
+
+async function getAllChannels(channelDB) {
+ let channelNumbers = await getAllNumbers(channelDB);
+ return await Promise.all( channelNumbers.map( async (x) => {
+ return (await getChannelConfig(channelDB, x))[0];
+ }) );
+}
+
+
function saveChannelConfig(number, channel ) {
configCache[number] = [channel];
}
@@ -127,6 +144,7 @@ function clear() {
//it's not necessary to clear the playback cache and it may be undesirable
configCache = {};
cache = {};
+ numbers = null;
}
module.exports = {
@@ -134,6 +152,7 @@ module.exports = {
recordPlayback: recordPlayback,
clear: clear,
getProgramLastPlayTime: getProgramLastPlayTime,
+ getAllChannels: getAllChannels,
getChannelConfig: getChannelConfig,
saveChannelConfig: saveChannelConfig,
getFillerLastPlayTime: getFillerLastPlayTime,
diff --git a/src/config.js b/src/config.js
deleted file mode 100644
index 396ea30..0000000
--- a/src/config.js
+++ /dev/null
@@ -1,4 +0,0 @@
-module.exports = {
- DATABASE: process.env.DATABASE || './.dizquetv',
- PORT: process.env.PORT || 8000,
-};
\ No newline at end of file
diff --git a/src/database-migration.js b/src/database-migration.js
index 4a3f9dc..2634fd5 100644
--- a/src/database-migration.js
+++ b/src/database-migration.js
@@ -20,7 +20,7 @@
const path = require('path');
var fs = require('fs');
-const TARGET_VERSION = 800;
+const TARGET_VERSION = 801;
const STEPS = [
// [v, v2, x] : if the current version is v, call x(db), and version becomes v2
@@ -40,6 +40,7 @@ const STEPS = [
// the addDeinterlaceFilter step. This 702 step no longer exists as a target
// but we have to migrate it to 800 using the reAddIcon.
[ 702, 800, (db,channels,dir) => reAddIcon(dir) ],
+ [ 800, 801, (db) => addImageCache(db) ],
]
const { v4: uuidv4 } = require('uuid');
@@ -803,6 +804,14 @@ function addDeinterlaceFilter(db) {
fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) );
}
+function addImageCache(db) {
+ let xmltvSettings = db['xmltv-settings'].find()[0];
+ let f = path.join(process.env.DATABASE, 'xmltv-settings.json');
+ xmltvSettings.enableImageCache = false;
+ fs.writeFileSync( f, JSON.stringify( [xmltvSettings] ) );
+}
+
+
module.exports = {
initDB: initDB,
defaultFFMPEG: ffmpeg,
diff --git a/src/cache-image-service.js b/src/services/cache-image-service.js
similarity index 90%
rename from src/cache-image-service.js
rename to src/services/cache-image-service.js
index c604796..8c1f7b6 100644
--- a/src/cache-image-service.js
+++ b/src/services/cache-image-service.js
@@ -1,11 +1,6 @@
const fs = require('fs');
const express = require('express');
const request = require('request');
-const diskDb = require('diskdb');
-
-const config = require('./config');
-const CacheService = require('./cache-service');
-const SettingsService = require('./services/settings-service');
/**
* Manager a cache in disk for external images.
@@ -13,13 +8,10 @@ const SettingsService = require('./services/settings-service');
* @class CacheImageService
*/
class CacheImageService {
- constructor() {
- this.cacheService = CacheService;
+ constructor( db, fileCacheService ) {
+ this.cacheService = fileCacheService;
this.imageCacheFolder = 'images';
- const connection = diskDb.connect(config.DATABASE, ['cache-images']);
- this.db = connection['cache-images'];
-
- SettingsService.saveSetting('enabled-cache-image', 'Enable Cache Image', false);
+ this.db = db['cache-images'];
}
/**
@@ -161,4 +153,4 @@ class CacheImageService {
}
}
-module.exports = new CacheImageService();
\ No newline at end of file
+module.exports = CacheImageService;
\ No newline at end of file
diff --git a/src/cache-service.js b/src/services/file-cache-service.js
similarity index 85%
rename from src/cache-service.js
rename to src/services/file-cache-service.js
index a81e236..1e0c6c7 100644
--- a/src/cache-service.js
+++ b/src/services/file-cache-service.js
@@ -1,15 +1,14 @@
const path = require('path');
const fs = require('fs');
-const config = require('./config');
/**
- * A File Cache controller for store and retrieve files from disk
+ * Store files in cache
*
- * @class CacheService
+ * @class FileCacheService
*/
-class CacheService {
- constructor() {
- this.cachePath = path.join(config.DATABASE, 'cache');
+class FileCacheService {
+ constructor(cachePath) {
+ this.cachePath = cachePath;
this.cache = {};
}
@@ -76,7 +75,11 @@ class CacheService {
deleteCache(fullFilePath) {
return new Promise((resolve, reject) => {
try {
- fs.unlinkSync(path.join(this.cachePath, fullFilePath), (err) => {
+ let thePath = path.join(this.cachePath, fullFilePath);
+ if (! fs.existsSync(thePath)) {
+ return resolve(true);
+ }
+ fs.unlinkSync(thePath, (err) => {
if(err) {
throw Error("Can't save file: ", err);
} else {
@@ -91,4 +94,4 @@ class CacheService {
}
}
-module.exports = new CacheService();
\ No newline at end of file
+module.exports = FileCacheService;
\ No newline at end of file
diff --git a/src/services/m3u-service.js b/src/services/m3u-service.js
index ab78064..674e200 100644
--- a/src/services/m3u-service.js
+++ b/src/services/m3u-service.js
@@ -1,16 +1,14 @@
-const CacheService = require('../cache-service');
-const SettingsService = require('./settings-service');
-
/**
* Manager and Generate M3U content
*
* @class M3uService
*/
class M3uService {
- constructor(dataBase) {
+ constructor(dataBase, fileCacheService, channelCache) {
this.dataBase = dataBase;
- this.cacheService = CacheService;
- SettingsService.saveSetting('enabled-cache-m3u', 'Enable Cache M3U', false);
+ this.cacheService = fileCacheService;
+ this.channelCache = channelCache;
+ this.cacheReady = false;
}
/**
@@ -20,12 +18,8 @@ class M3uService {
* @returns {promise} Return a Promise with HLS or M3U file content
* @memberof M3uService
*/
- getChannelList(host, type = 'm3u') {
- if(type === 'hls') {
- return this.buildHLSList(host);
- } else {
- return this.buildM3uList(host);
- }
+ getChannelList(host) {
+ return this.buildM3uList(host);
}
/**
@@ -36,105 +30,46 @@ class M3uService {
* @memberof M3uService
*/
- buildM3uList(host) {
- return new Promise(async (resolve, reject) => {
-
- try {
-
- const cache = SettingsService.getSetting('enabled-cache-m3u').value;
-
- if(cache) {
- const cacheChannels = await this.cacheService.getCache('channels.m3u');
- if(cacheChannels) {
- resolve(this.replaceHostOnM3u(host, cacheChannels));
- }
- }
-
- let channels = await this.dataBase.getAllChannels();
-
- channels.sort((a, b) => {
- return a.number < b.number ? -1 : 1
- });
-
- const tvg = `{{host}}/api/xmltv.xml`;
-
- let data = `#EXTM3U url-tvg="${tvg}" x-tvg-url="${tvg}"\n`;
-
- for (var i = 0; i < channels.length; i++) {
- if (channels[i].stealth!==true) {
- data += `#EXTINF:0 tvg-id="${channels[i].number}" CUID="${channels[i].number}" tvg-chno="${channels[i].number}" tvg-name="${channels[i].name}" tvg-logo="${channels[i].icon}" group-title="dizqueTV",${channels[i].name}\n`
- data += `{{host}}/video?channel=${channels[i].number}\n`
- }
- }
- if (channels.length === 0) {
- data += `#EXTINF:0 tvg-id="1" tvg-chno="1" tvg-name="dizqueTV" tvg-logo="{{host}}/resources/dizquetv.png" group-title="dizqueTV",dizqueTV\n`
- data += `{{host}}/setup\n`
- }
-
- if(cache) {
- await this.cacheService.setCache('channels.m3u', data);
- }
-
- resolve(this.replaceHostOnM3u(host, data));
-
- } catch (error) {
- reject(error);
+ async buildM3uList(host) {
+ if (this.cacheReady) {
+ const cachedM3U = await this.cacheService.getCache('channels.m3u');
+ if (cachedM3U) {
+ return this.replaceHostOnM3u(host, cachedM3U);
}
+ }
+ let channels = await this.channelCache.getAllChannels(this.dataBase);
+
+ channels.sort((a, b) => {
+ return a.number < b.number ? -1 : 1
});
+
+ const tvg = `{{host}}/api/xmltv.xml`;
+
+ let data = `#EXTM3U url-tvg="${tvg}" x-tvg-url="${tvg}"\n`;
+
+ for (var i = 0; i < channels.length; i++) {
+ if (channels[i].stealth !== true) {
+ data += `#EXTINF:0 tvg-id="${channels[i].number}" CUID="${channels[i].number}" tvg-chno="${channels[i].number}" tvg-name="${channels[i].name}" tvg-logo="${channels[i].icon}" group-title="dizqueTV",${channels[i].name}\n`
+ data += `{{host}}/video?channel=${channels[i].number}\n`
+ }
+ }
+ if (channels.length === 0) {
+ data += `#EXTINF:0 tvg-id="1" tvg-chno="1" tvg-name="dizqueTV" tvg-logo="{{host}}/resources/dizquetv.png" group-title="dizqueTV",dizqueTV\n`
+ data += `{{host}}/setup\n`
+ }
+ let saveCacheThread = async() => {
+ try {
+ await this.cacheService.setCache('channels.m3u', data);
+ this.cacheReady = true;
+ } catch(err) {
+ console.error(err);
+ }
+ };
+ saveCacheThread();
+ return this.replaceHostOnM3u(host, data);
}
- /**
- *
- * "hls.m3u Download is not really working correctly right now"
- *
- * @param {boolean} [cache=true]
- * @param {*} host
- * @returns {promise} M3U file content
- * @memberof M3uService
- */
- buildHLSList(host, cache = true) {
- return new Promise(async (resolve, reject) => {
-
- try {
-
- const cacheChannels = await this.cacheService.getCache('channels-hls.m3u');
-
- if(cache && cacheChannels) {
- resolve(this.replaceHostOnM3u(host, cacheChannels));
- }
-
- let channels = await this.dataBase.getAllChannels();
-
- channels.sort((a, b) => {
- return a.number < b.number ? -1 : 1
- });
-
- const tvg = `{{host}}/api/xmltv.xml`;
-
- let data = "#EXTM3U\n"
- for (var i = 0; i < channels.length; i++) {
- data += `#EXTINF:0 tvg-id="${channels[i].number}" tvg-chno="${channels[i].number}" tvg-name="${channels[i].name}" tvg-logo="${channels[i].icon}" group-title="dizqueTV",${channels[i].name}\n`
- data += `{{host}}/m3u8?channel=${channels[i].number}\n`
- }
- if (channels.length === 0) {
- data += `#EXTINF:0 tvg-id="1" tvg-chno="1" tvg-name="dizqueTV" tvg-logo="{{host}}/resources/dizquetv.png" group-title="dizqueTV",dizqueTV\n`
- data += `{{host}}/setup\n`
- }
-
- if(cache) {
- await this.cacheService.setCache('channels-hls.m3u', data);
- }
-
- resolve(this.replaceHostOnM3u(host, data));
-
- } catch (error) {
- reject(error);
- }
-
- });
- }
-
/**
* Replace {{host}} string with a URL on file contents.
*
@@ -153,8 +88,7 @@ class M3uService {
* @memberof M3uService
*/
async clearCache() {
- await this.cacheService.deleteCache('channels.m3u');
- await this.cacheService.deleteCache('channels-hls.m3u');
+ this.cacheReady = false;
}
}
diff --git a/src/services/settings-service.js b/src/services/settings-service.js
deleted file mode 100644
index 1df45e8..0000000
--- a/src/services/settings-service.js
+++ /dev/null
@@ -1,114 +0,0 @@
-const diskDb = require('diskdb');
-const express = require('express');
-const config = require('../config');
-
-class SettingsService {
- constructor() {
- const connection = diskDb.connect(config.DATABASE, ['settings']);
- this.db = connection['settings'];
- }
-
- apiRouters() {
- const router = express.Router();
-
- router.get('/', async (req, res) => {
- try {
- const settings = await this.getAllSettings();
- res.send(settings);
- } catch (error) {
- console.error(error);
- res.status(500).send("error");
- }
- });
-
- router.post('/', async (req, res) => {
- try {
- const {key, title, value} = req.body;
- if(!key || !title || value === undefined) {
- throw Error("Key, title and value are Required");
- }
-
- const settings = await this.saveSetting(key, title, value);
- res.send(settings);
- } catch (error) {
- console.error(error);
- res.status(500).send("error");
- }
- });
-
- router.put('/:key', async (req, res) => {
- try {
- const key = req.params.key;
- const {value} = req.body;
- console.log(key, value);
- if(!key || value === undefined) {
- throw Error("Key and value are Required");
- }
- const settings = await this.updateSetting(key, value);
- console.log(settings);
- res.send(settings);
- } catch (error) {
- console.error(error);
- res.status(500).send("error");
- }
- });
-
- return router;
- }
-
- getSetting(key) {
- return new Promise((resolve, reject) =>{
- try {
- const setting = this.db.find({key})[0];
- resolve(setting);
- } catch (error) {
- reject(error);
- }
- });
- }
-
- getAllSettings() {
- return new Promise((resolve, reject) =>{
- try {
- const settings = this.db.find();
- resolve(settings);
- } catch (error) {
- reject(error);
- }
- });
- }
- saveSetting(key, title, value) {
- return new Promise((resolve, reject) =>{
- try {
- const setting = this.db.find({key})[0];
- if(!setting) {
- this.db.save({key, title, value});
- }
- resolve(true);
- } catch (error) {
- reject(error);
- }
- });
- }
- updateSetting(key, value) {
- return new Promise((resolve, reject) => {
- try {
- const setting = this.db.find({key})[0];
- if(setting) {
- const query = this.db.update({_id: setting._id}, {key, value});
- if(query.updated > 0) {
- const settings = this.db.find();
- resolve(settings);
- }
- reject();
- } else {
- reject({error: true, msg: "Setting not found!"});
- }
- } catch (error) {
-
- }
- });
- }
-}
-
-module.exports = new SettingsService();
\ No newline at end of file
diff --git a/src/tv-guide-service.js b/src/tv-guide-service.js
index 3c40df9..5936dcd 100644
--- a/src/tv-guide-service.js
+++ b/src/tv-guide-service.js
@@ -7,7 +7,7 @@ class TVGuideService
/****
*
**/
- constructor(xmltv, db) {
+ constructor(xmltv, db, cacheImageService) {
this.cached = null;
this.lastUpdate = 0;
this.updateTime = 0;
@@ -18,6 +18,7 @@ class TVGuideService
this.doThrottle = false;
this.xmltv = xmltv;
this.db = db;
+ this.cacheImageService = cacheImageService;
}
async get() {
@@ -351,7 +352,7 @@ class TVGuideService
async refreshXML() {
let xmltvSettings = this.db['xmltv-settings'].find()[0];
- await this.xmltv.WriteXMLTV(this.cached, xmltvSettings, async() => await this._throttle(), this.db);
+ await this.xmltv.WriteXMLTV(this.cached, xmltvSettings, async() => await this._throttle(), this.cacheImageService);
}
async getStatus() {
diff --git a/src/xmltv.js b/src/xmltv.js
index ac38e98..a4c7798 100644
--- a/src/xmltv.js
+++ b/src/xmltv.js
@@ -1,14 +1,12 @@
const XMLWriter = require('xml-writer')
const fs = require('fs')
-const CacheImageService = require('./cache-image-service');
-const SettingsService = require('./services/settings-service');
module.exports = { WriteXMLTV: WriteXMLTV, shutdown: shutdown }
let isShutdown = false;
let isWorking = false;
-async function WriteXMLTV(json, xmlSettings, throttle) {
+async function WriteXMLTV(json, xmlSettings, throttle, cacheImageService) {
if (isShutdown) {
return;
}
@@ -18,14 +16,14 @@ async function WriteXMLTV(json, xmlSettings, throttle) {
}
isWorking = true;
try {
- await writePromise(json, xmlSettings, throttle);
+ await writePromise(json, xmlSettings, throttle, cacheImageService);
} catch (err) {
console.error("Error writing xmltv", err);
}
isWorking = false;
}
-function writePromise(json, xmlSettings, throttle) {
+function writePromise(json, xmlSettings, throttle, cacheImageService) {
return new Promise((resolve, reject) => {
let ws = fs.createWriteStream(xmlSettings.file)
let xw = new XMLWriter(true, (str, enc) => ws.write(str, enc))
@@ -39,7 +37,7 @@ function writePromise(json, xmlSettings, throttle) {
_writeChannels( xw, channels );
for (let i = 0; i < channelNumbers.length; i++) {
let number = channelNumbers[i];
- await _writePrograms(xw, json[number].channel, json[number].programs, throttle);
+ await _writePrograms(xw, json[number].channel, json[number].programs, throttle, xmlSettings, cacheImageService);
}
}
middle().then( () => {
@@ -77,16 +75,16 @@ function _writeChannels(xw, channels) {
}
}
-async function _writePrograms(xw, channel, programs, throttle) {
+async function _writePrograms(xw, channel, programs, throttle, xmlSettings, cacheImageService) {
for (let i = 0; i < programs.length; i++) {
if (! isShutdown) {
await throttle();
}
- await _writeProgramme(channel, programs[i], xw);
+ await _writeProgramme(channel, programs[i], xw, xmlSettings, cacheImageService);
}
}
-async function _writeProgramme(channel, program, xw) {
+async function _writeProgramme(channel, program, xw, xmlSettings, cacheImageService) {
// Programme
xw.startElement('programme')
xw.writeAttribute('start', _createXMLTVDate(program.start))
@@ -113,16 +111,14 @@ async function _writeProgramme(channel, program, xw) {
}
// Icon
if (typeof program.icon !== 'undefined') {
- xw.startElement('icon')
-
- if(await SettingsService.getSetting('enabled-cache-image')){
- const imgUrl = CacheImageService.registerImageOnDatabase(program.icon);
- xw.writeAttribute('src', `{{host}}/cache/images/${imgUrl}`)
- } else {
- xw.writeAttribute('src', program.icon)
+ xw.startElement('icon');
+ let icon = program.icon;
+ if (xmlSettings.enableImageCache === true) {
+ const imgUrl = cacheImageService.registerImageOnDatabase(icon);
+ icon = `{{host}}/cache/images/${imgUrl}`;
}
-
- xw.endElement()
+ xw.writeAttribute('src', icon);
+ xw.endElement();
}
// Desc
xw.startElement('desc')
diff --git a/web/public/templates/xmltv-settings.html b/web/public/templates/xmltv-settings.html
index 1707974..20c65a0 100644
--- a/web/public/templates/xmltv-settings.html
+++ b/web/public/templates/xmltv-settings.html
@@ -14,7 +14,7 @@