commit
d99eda2ccf
13
.github/workflows/README.md
vendored
Normal file
13
.github/workflows/README.md
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# The workflow
|
||||||
|
|
||||||
|
edge main
|
||||||
|
^ \ ^
|
||||||
|
| \------2--\ |
|
||||||
|
1 \ 3
|
||||||
|
| \ |
|
||||||
|
development <-4-- patch
|
||||||
|
|
||||||
|
1. Releasing a new 'edge' version.
|
||||||
|
2. Moving an 'edge' version to stable.
|
||||||
|
3. Releasing a stable version
|
||||||
|
4. Aligning bug fixes with the new features.
|
||||||
2
.github/workflows/development-binaries.yaml
vendored
2
.github/workflows/development-binaries.yaml
vendored
@ -3,7 +3,7 @@ name: Development Binaries
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev/1.5.x
|
- development
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
binaries:
|
binaries:
|
||||||
|
|||||||
2
.github/workflows/development-tag.yaml
vendored
2
.github/workflows/development-tag.yaml
vendored
@ -3,7 +3,7 @@ name: Development Tag
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev/1.5.x
|
- development
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
|
|||||||
@ -10,5 +10,6 @@ FROM akashisn/ffmpeg:4.4.5
|
|||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
WORKDIR /home/node/app
|
WORKDIR /home/node/app
|
||||||
ENTRYPOINT [ "./dizquetv" ]
|
ENTRYPOINT [ "./dizquetv" ]
|
||||||
|
ENV DIZQUETV_FFMPEG_PATH=/usr/bin/ffmpeg
|
||||||
COPY --from=0 /home/node/app/dist/dizquetv /home/node/app/
|
COPY --from=0 /home/node/app/dist/dizquetv /home/node/app/
|
||||||
RUN ln -s /usr/local/bin/ffmpeg /usr/bin/ffmpeg
|
RUN ln -s /usr/local/bin/ffmpeg /usr/bin/ffmpeg
|
||||||
|
|||||||
@ -10,5 +10,6 @@ FROM jrottenberg/ffmpeg:4.4.5-nvidia2204
|
|||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
WORKDIR /home/node/app
|
WORKDIR /home/node/app
|
||||||
ENTRYPOINT [ "./dizquetv" ]
|
ENTRYPOINT [ "./dizquetv" ]
|
||||||
|
ENV DIZQUETV_FFMPEG_PATH=/usr/bin/ffmpeg
|
||||||
COPY --from=0 /home/node/app/dist/dizquetv /home/node/app/
|
COPY --from=0 /home/node/app/dist/dizquetv /home/node/app/
|
||||||
RUN ln -s /usr/local/bin/ffmpeg /usr/bin/ffmpeg
|
RUN ln -s /usr/local/bin/ffmpeg /usr/bin/ffmpeg
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# dizqueTV 1.6.0
|
# dizqueTV 1.7.0
|
||||||
  
|
  
|
||||||
|
|
||||||
Create live TV channel streams from media on your Plex servers.
|
Create live TV channel streams from media on your Plex servers.
|
||||||
|
|||||||
56
index.js
56
index.js
@ -17,6 +17,7 @@ const HDHR = require('./src/hdhr')
|
|||||||
const FileCacheService = require('./src/services/file-cache-service');
|
const FileCacheService = require('./src/services/file-cache-service');
|
||||||
const CacheImageService = require('./src/services/cache-image-service');
|
const CacheImageService = require('./src/services/cache-image-service');
|
||||||
const ChannelService = require("./src/services/channel-service");
|
const ChannelService = require("./src/services/channel-service");
|
||||||
|
const FillerService = require("./src/services/filler-service");
|
||||||
|
|
||||||
const xmltv = require('./src/xmltv')
|
const xmltv = require('./src/xmltv')
|
||||||
const Plex = require('./src/plex');
|
const Plex = require('./src/plex');
|
||||||
@ -32,6 +33,9 @@ const ProgrammingService = require("./src/services/programming-service");
|
|||||||
const ActiveChannelService = require('./src/services/active-channel-service')
|
const ActiveChannelService = require('./src/services/active-channel-service')
|
||||||
const ProgramPlayTimeDB = require('./src/dao/program-play-time-db')
|
const ProgramPlayTimeDB = require('./src/dao/program-play-time-db')
|
||||||
const FfmpegSettingsService = require('./src/services/ffmpeg-settings-service')
|
const FfmpegSettingsService = require('./src/services/ffmpeg-settings-service')
|
||||||
|
const PlexProxyService = require('./src/services/plex-proxy-service')
|
||||||
|
const PlexServerDB = require('./src/dao/plex-server-db');
|
||||||
|
const FFMPEGInfo = require('./src/ffmpeg-info');
|
||||||
|
|
||||||
const onShutdown = require("node-graceful-shutdown").onShutdown;
|
const onShutdown = require("node-graceful-shutdown").onShutdown;
|
||||||
|
|
||||||
@ -51,16 +55,12 @@ if (NODE < 12) {
|
|||||||
console.error(`WARNING: Your nodejs version ${process.version} is lower than supported. dizqueTV has been tested best on nodejs 12.16.`);
|
console.error(`WARNING: Your nodejs version ${process.version} is lower than supported. dizqueTV has been tested best on nodejs 12.16.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlockPath = false;
|
|
||||||
for (let i = 0, l = process.argv.length; i < l; i++) {
|
for (let i = 0, l = process.argv.length; i < l; i++) {
|
||||||
if ((process.argv[i] === "-p" || process.argv[i] === "--port") && i + 1 !== l)
|
if ((process.argv[i] === "-p" || process.argv[i] === "--port") && i + 1 !== l)
|
||||||
process.env.PORT = process.argv[i + 1]
|
process.env.PORT = process.argv[i + 1]
|
||||||
if ((process.argv[i] === "-d" || process.argv[i] === "--database") && i + 1 !== l)
|
if ((process.argv[i] === "-d" || process.argv[i] === "--database") && i + 1 !== l)
|
||||||
process.env.DATABASE = process.argv[i + 1]
|
process.env.DATABASE = process.argv[i + 1]
|
||||||
|
|
||||||
if (process.argv[i] === "--unlock") {
|
|
||||||
unlockPath = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process.env.DATABASE = process.env.DATABASE || path.join(".", ".dizquetv")
|
process.env.DATABASE = process.env.DATABASE || path.join(".", ".dizquetv")
|
||||||
@ -92,6 +92,8 @@ if(!fs.existsSync(path.join(process.env.DATABASE, 'cache','images'))) {
|
|||||||
fs.mkdirSync(path.join(process.env.DATABASE, 'cache','images'))
|
fs.mkdirSync(path.join(process.env.DATABASE, 'cache','images'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ffmpegInfo = new FFMPEGInfo(process.env, process.env.DATABASE);
|
||||||
|
ffmpegInfo.initialize();
|
||||||
|
|
||||||
channelDB = new ChannelDB( path.join(process.env.DATABASE, 'channels') );
|
channelDB = new ChannelDB( path.join(process.env.DATABASE, 'channels') );
|
||||||
|
|
||||||
@ -103,10 +105,13 @@ initDB(db, channelDB)
|
|||||||
|
|
||||||
channelService = new ChannelService(channelDB);
|
channelService = new ChannelService(channelDB);
|
||||||
|
|
||||||
fillerDB = new FillerDB( path.join(process.env.DATABASE, 'filler') , channelService );
|
let fillerDB = new FillerDB( path.join(process.env.DATABASE, 'filler') );
|
||||||
customShowDB = new CustomShowDB( path.join(process.env.DATABASE, 'custom-shows') );
|
customShowDB = new CustomShowDB( path.join(process.env.DATABASE, 'custom-shows') );
|
||||||
let programPlayTimeDB = new ProgramPlayTimeDB( path.join(process.env.DATABASE, 'play-cache') );
|
let programPlayTimeDB = new ProgramPlayTimeDB( path.join(process.env.DATABASE, 'play-cache') );
|
||||||
let ffmpegSettingsService = new FfmpegSettingsService(db, unlockPath);
|
let ffmpegSettingsService = new FfmpegSettingsService(db);
|
||||||
|
let plexServerDB = new PlexServerDB(channelService, fillerDB, customShowDB, db);
|
||||||
|
let plexProxyService = new PlexProxyService(plexServerDB);
|
||||||
|
|
||||||
|
|
||||||
async function initializeProgramPlayTimeDB() {
|
async function initializeProgramPlayTimeDB() {
|
||||||
try {
|
try {
|
||||||
@ -130,6 +135,9 @@ activeChannelService = new ActiveChannelService(onDemandService, channelService)
|
|||||||
|
|
||||||
eventService = new EventService();
|
eventService = new EventService();
|
||||||
|
|
||||||
|
let fillerService = new FillerService(fillerDB, plexProxyService,
|
||||||
|
channelService);
|
||||||
|
|
||||||
i18next
|
i18next
|
||||||
.use(i18nextBackend)
|
.use(i18nextBackend)
|
||||||
.use(i18nextMiddleware.LanguageDetector)
|
.use(i18nextMiddleware.LanguageDetector)
|
||||||
@ -251,6 +259,38 @@ channelService.on("channel-update", (data) => {
|
|||||||
|
|
||||||
let hdhr = HDHR(db, channelDB)
|
let hdhr = HDHR(db, channelDB)
|
||||||
let app = express()
|
let app = express()
|
||||||
|
|
||||||
|
const responseInterceptor = (
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
next
|
||||||
|
) => {
|
||||||
|
|
||||||
|
let t0 = new Date().getTime();
|
||||||
|
|
||||||
|
const originalSend = res.send;
|
||||||
|
let responseSent = false;
|
||||||
|
console.log(`${req.method} ${req.url} ...`);
|
||||||
|
if (req.method === "GET" && req.url.includes("images/uploads") ) {
|
||||||
|
res.setHeader("Cache-Control", "public, max-age=86400");
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send = function (body) {
|
||||||
|
|
||||||
|
if (!responseSent) {
|
||||||
|
let t1 = new Date().getTime();
|
||||||
|
let dt = t1 - t0;
|
||||||
|
console.log(`${req.method} ${req.url} ${res.statusCode} in ${dt}ms`);
|
||||||
|
responseSent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return originalSend.call(this, body);
|
||||||
|
};
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
app.use(responseInterceptor);
|
||||||
|
|
||||||
eventService.setup(app);
|
eventService.setup(app);
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
@ -290,12 +330,12 @@ app.use('/favicon.svg', express.static(
|
|||||||
app.use('/custom.css', express.static(path.join(process.env.DATABASE, 'custom.css')))
|
app.use('/custom.css', express.static(path.join(process.env.DATABASE, 'custom.css')))
|
||||||
|
|
||||||
// API Routers
|
// API Routers
|
||||||
app.use(api.router(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, m3uService, eventService, ffmpegSettingsService))
|
app.use(api.router(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, m3uService, eventService, ffmpegSettingsService, plexServerDB, plexProxyService, ffmpegInfo, fillerService))
|
||||||
app.use('/api/cache/images', cacheImageService.apiRouters())
|
app.use('/api/cache/images', cacheImageService.apiRouters())
|
||||||
app.use('/' + fontAwesome, express.static(path.join(process.env.DATABASE, fontAwesome)))
|
app.use('/' + fontAwesome, express.static(path.join(process.env.DATABASE, fontAwesome)))
|
||||||
app.use('/' + bootstrap, express.static(path.join(process.env.DATABASE, bootstrap)))
|
app.use('/' + bootstrap, express.static(path.join(process.env.DATABASE, bootstrap)))
|
||||||
|
|
||||||
app.use(video.router( channelService, fillerDB, db, programmingService, activeChannelService, programPlayTimeDB ))
|
app.use(video.router( channelService, fillerService, db, programmingService, activeChannelService, programPlayTimeDB, ffmpegInfo ))
|
||||||
app.use(hdhr.router)
|
app.use(hdhr.router)
|
||||||
app.listen(process.env.PORT, () => {
|
app.listen(process.env.PORT, () => {
|
||||||
console.log(`HTTP server running on port: http://*:${process.env.PORT}`)
|
console.log(`HTTP server running on port: http://*:${process.env.PORT}`)
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
* [ ] I have read the code of conduct.
|
* [ ] I have read the code of conduct.
|
||||||
* [ ] I am submitting to the correct base branch
|
* [ ] I am submitting to the correct base branch
|
||||||
<!--
|
<!--
|
||||||
* Bug fixes must go to `dev/1.4.x`.
|
* Bug fixes for 'stable' versions must go to `patch`.
|
||||||
* New features must go to `dev/1.5.x`.
|
* New features and fixes for 'edge' version must go to `development`.
|
||||||
-->
|
-->
|
||||||
### Changes that modify the db structure
|
### Changes that modify the db structure
|
||||||
|
|
||||||
|
|||||||
38
src/api.js
38
src/api.js
@ -4,8 +4,6 @@ const path = require('path')
|
|||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const constants = require('./constants');
|
const constants = require('./constants');
|
||||||
const JSONStream = require('JSONStream');
|
const JSONStream = require('JSONStream');
|
||||||
const FFMPEGInfo = require('./ffmpeg-info');
|
|
||||||
const PlexServerDB = require('./dao/plex-server-db');
|
|
||||||
const Plex = require("./plex.js");
|
const Plex = require("./plex.js");
|
||||||
|
|
||||||
const timeSlotsService = require('./services/time-slots-service');
|
const timeSlotsService = require('./services/time-slots-service');
|
||||||
@ -24,15 +22,13 @@ function safeString(object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { router: api }
|
module.exports = { router: api }
|
||||||
function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, _m3uService, eventService, ffmpegSettingsService ) {
|
function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, _m3uService, eventService, ffmpegSettingsService, plexServerDB, plexProxyService, ffmpegInfo, fillerService ) {
|
||||||
let m3uService = _m3uService;
|
let m3uService = _m3uService;
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
const plexServerDB = new PlexServerDB(channelService, fillerDB, customShowDB, db);
|
|
||||||
|
|
||||||
router.get('/api/version', async (req, res) => {
|
router.get('/api/version', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
let ffmpegSettings = db['ffmpeg-settings'].find()[0];
|
let v = await ffmpegInfo.getVersion();
|
||||||
let v = await (new FFMPEGInfo(ffmpegSettings)).getVersion();
|
|
||||||
res.send( {
|
res.send( {
|
||||||
"dizquetv" : constants.VERSION_NAME,
|
"dizquetv" : constants.VERSION_NAME,
|
||||||
"ffmpeg" : v,
|
"ffmpeg" : v,
|
||||||
@ -216,7 +212,15 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
router.get('/api/plex-server/:serverName64/:path(*)', async (req, res) => {
|
||||||
|
try {
|
||||||
|
let result = await plexProxyService.get(req.params.serverName64, req.params.path);
|
||||||
|
res.status(200).send(result);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Could not use plex proxy.", err);
|
||||||
|
res.status(404).send("Could not call plex server.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Channels
|
// Channels
|
||||||
router.get('/api/channels', async (req, res) => {
|
router.get('/api/channels', async (req, res) => {
|
||||||
@ -415,7 +419,7 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
|||||||
if (typeof(id) === 'undefined') {
|
if (typeof(id) === 'undefined') {
|
||||||
return res.status(400).send("Missing id");
|
return res.status(400).send("Missing id");
|
||||||
}
|
}
|
||||||
await fillerDB.saveFiller(id, req.body );
|
await fillerService.saveFiller(id, req.body );
|
||||||
return res.status(204).send({});
|
return res.status(204).send({});
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -424,7 +428,7 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
|||||||
})
|
})
|
||||||
router.put('/api/filler', async (req, res) => {
|
router.put('/api/filler', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
let uuid = await fillerDB.createFiller(req.body );
|
let uuid = await fillerService.createFiller(req.body );
|
||||||
return res.status(201).send({id: uuid});
|
return res.status(201).send({id: uuid});
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -437,7 +441,7 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
|||||||
if (typeof(id) === 'undefined') {
|
if (typeof(id) === 'undefined') {
|
||||||
return res.status(400).send("Missing id");
|
return res.status(400).send("Missing id");
|
||||||
}
|
}
|
||||||
await fillerDB.deleteFiller(id);
|
await fillerService.deleteFiller(id);
|
||||||
return res.status(204).send({});
|
return res.status(204).send({});
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -451,7 +455,7 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
|||||||
if (typeof(id) === 'undefined') {
|
if (typeof(id) === 'undefined') {
|
||||||
return res.status(400).send("Missing id");
|
return res.status(400).send("Missing id");
|
||||||
}
|
}
|
||||||
let channels = await fillerDB.getFillerChannels(id);
|
let channels = await fillerService.getFillerChannels(id);
|
||||||
if (channels == null) {
|
if (channels == null) {
|
||||||
return res.status(404).send("Filler not found");
|
return res.status(404).send("Filler not found");
|
||||||
}
|
}
|
||||||
@ -609,6 +613,18 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.get('/api/ffmpeg-info', async (req, res) => {
|
||||||
|
try {
|
||||||
|
let ffmpeg = await ffmpegInfo.getPath();
|
||||||
|
let obj = { ffmpegPath: ffmpeg }
|
||||||
|
res.send(obj)
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).send("error");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
// PLEX SETTINGS
|
// PLEX SETTINGS
|
||||||
router.get('/api/plex-settings', (req, res) => {
|
router.get('/api/plex-settings', (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -35,5 +35,5 @@ module.exports = {
|
|||||||
// staying active, it checks every 5 seconds
|
// staying active, it checks every 5 seconds
|
||||||
PLAYED_MONITOR_CHECK_FREQUENCY: 5*1000,
|
PLAYED_MONITOR_CHECK_FREQUENCY: 5*1000,
|
||||||
|
|
||||||
VERSION_NAME: "1.6.0"
|
VERSION_NAME: "1.7.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,12 +4,9 @@ let fs = require('fs');
|
|||||||
|
|
||||||
class FillerDB {
|
class FillerDB {
|
||||||
|
|
||||||
constructor(folder, channelService) {
|
constructor(folder) {
|
||||||
this.folder = folder;
|
this.folder = folder;
|
||||||
this.cache = {};
|
this.cache = {};
|
||||||
this.channelService = channelService;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async $loadFiller(id) {
|
async $loadFiller(id) {
|
||||||
@ -77,40 +74,8 @@ class FillerDB {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFillerChannels(id) {
|
|
||||||
let numbers = await this.channelService.getAllChannelNumbers();
|
|
||||||
let channels = [];
|
|
||||||
await Promise.all( numbers.map( async(number) => {
|
|
||||||
let ch = await this.channelService.getChannel(number);
|
|
||||||
let name = ch.name;
|
|
||||||
let fillerCollections = ch.fillerCollections;
|
|
||||||
for (let i = 0 ; i < fillerCollections.length; i++) {
|
|
||||||
if (fillerCollections[i].id === id) {
|
|
||||||
channels.push( {
|
|
||||||
number: number,
|
|
||||||
name : name,
|
|
||||||
} );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ch = null;
|
|
||||||
|
|
||||||
} ) );
|
|
||||||
return channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteFiller(id) {
|
async deleteFiller(id) {
|
||||||
try {
|
try {
|
||||||
let channels = await this.getFillerChannels(id);
|
|
||||||
await Promise.all( channels.map( async(channel) => {
|
|
||||||
console.log(`Updating channel ${channel.number} , remove filler: ${id}`);
|
|
||||||
let json = await channelService.getChannel(channel.number);
|
|
||||||
json.fillerCollections = json.fillerCollections.filter( (col) => {
|
|
||||||
return col.id != id;
|
|
||||||
} );
|
|
||||||
await this.channelService.saveChannel( channel.number, json );
|
|
||||||
} ) );
|
|
||||||
|
|
||||||
let f = path.join(this.folder, `${id}.json` );
|
let f = path.join(this.folder, `${id}.json` );
|
||||||
await new Promise( (resolve, reject) => {
|
await new Promise( (resolve, reject) => {
|
||||||
fs.unlink(f, function (err) {
|
fs.unlink(f, function (err) {
|
||||||
@ -162,27 +127,6 @@ class FillerDB {
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFillersFromChannel(channel) {
|
|
||||||
|
|
||||||
let loadChannelFiller = async(fillerEntry) => {
|
|
||||||
let content = [];
|
|
||||||
try {
|
|
||||||
let filler = await this.getFiller(fillerEntry.id);
|
|
||||||
content = filler.content;
|
|
||||||
} catch(e) {
|
|
||||||
console.error(`Channel #${channel.number} - ${channel.name} references an unattainable filler id: ${fillerEntry.id}`);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
id: fillerEntry.id,
|
|
||||||
content: content,
|
|
||||||
weight: fillerEntry.weight,
|
|
||||||
cooldown: fillerEntry.cooldown,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return await Promise.all(
|
|
||||||
channel.fillerCollections.map(loadChannelFiller)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,17 @@ class PlexServerDB
|
|||||||
this.showDB = showDB;
|
this.showDB = showDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getPlexServerByName(name) {
|
||||||
|
let servers = this.db['plex-servers'].find()
|
||||||
|
let server = servers.sort( (a,b) => { return a.index - b.index } )
|
||||||
|
.filter( (server) => name === server.name )
|
||||||
|
[0];
|
||||||
|
if (typeof(server) === "undefined") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
async fixupAllChannels(name, newServer) {
|
async fixupAllChannels(name, newServer) {
|
||||||
let channelNumbers = await this.channelService.getAllChannelNumbers();
|
let channelNumbers = await this.channelService.getAllChannelNumbers();
|
||||||
let report = await Promise.all( channelNumbers.map( async (i) => {
|
let report = await Promise.all( channelNumbers.map( async (i) => {
|
||||||
|
|||||||
@ -20,8 +20,7 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
|
||||||
const TARGET_VERSION = 805;
|
const TARGET_VERSION = 1000;
|
||||||
const DAY_MS = 1000 * 60 * 60 * 24;
|
|
||||||
|
|
||||||
const STEPS = [
|
const STEPS = [
|
||||||
// [v, v2, x] : if the current version is v, call x(db), and version becomes v2
|
// [v, v2, x] : if the current version is v, call x(db), and version becomes v2
|
||||||
@ -44,8 +43,10 @@ const STEPS = [
|
|||||||
[ 800, 801, (db) => addImageCache(db) ],
|
[ 800, 801, (db) => addImageCache(db) ],
|
||||||
[ 801, 802, () => addGroupTitle() ],
|
[ 801, 802, () => addGroupTitle() ],
|
||||||
[ 802, 803, () => fixNonIntegerDurations() ],
|
[ 802, 803, () => fixNonIntegerDurations() ],
|
||||||
[ 803, 805, (db) => addFFMpegLock(db) ],
|
[ 803, 900, (db) => fixFFMpegPathSetting(db) ],
|
||||||
[ 804, 805, (db) => addFFMpegLock(db) ],
|
[ 804, 900, (db) => fixFFMpegPathSetting(db) ],
|
||||||
|
[ 805, 900, (db) => fixFFMpegPathSetting(db) ],
|
||||||
|
[ 900, 1000, () => fixFillerModes() ],
|
||||||
]
|
]
|
||||||
|
|
||||||
const { v4: uuidv4 } = require('uuid');
|
const { v4: uuidv4 } = require('uuid');
|
||||||
@ -75,7 +76,7 @@ function appNameChange(db) {
|
|||||||
|
|
||||||
function basicDB(db) {
|
function basicDB(db) {
|
||||||
//this one should either try recovering the db from a very old version
|
//this one should either try recovering the db from a very old version
|
||||||
//or buildl a completely empty db at version 0
|
//or build a completely empty db at version 0
|
||||||
let ffmpegSettings = db['ffmpeg-settings'].find()
|
let ffmpegSettings = db['ffmpeg-settings'].find()
|
||||||
let plexSettings = db['plex-settings'].find()
|
let plexSettings = db['plex-settings'].find()
|
||||||
|
|
||||||
@ -386,8 +387,6 @@ function ffmpeg() {
|
|||||||
return {
|
return {
|
||||||
//How default ffmpeg settings should look
|
//How default ffmpeg settings should look
|
||||||
configVersion: 5,
|
configVersion: 5,
|
||||||
ffmpegPath: "/usr/bin/ffmpeg",
|
|
||||||
ffmpegPathLockDate: new Date().getTime() + DAY_MS,
|
|
||||||
threads: 4,
|
threads: 4,
|
||||||
concatMuxDelay: "0",
|
concatMuxDelay: "0",
|
||||||
logFfmpeg: false,
|
logFfmpeg: false,
|
||||||
@ -681,6 +680,25 @@ function extractFillersFromChannels() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fixFillerModes() {
|
||||||
|
console.log("Fixing filler modes...");
|
||||||
|
let fillers = path.join(process.env.DATABASE, 'filler');
|
||||||
|
let fillerFiles = fs.readdirSync(fillers);
|
||||||
|
|
||||||
|
for (let i = 0; i < fillerFiles.length; i++) {
|
||||||
|
if (path.extname( fillerFiles[i] ) === '.json') {
|
||||||
|
console.log("Migrating filler : " + fillerFiles[i] +"..." );
|
||||||
|
let fillerPath = path.join(fillers, fillerFiles[i]);
|
||||||
|
let filler = JSON.parse(fs.readFileSync(fillerPath, 'utf-8'));
|
||||||
|
if ( typeof(filler.mode) !== "string" ) {
|
||||||
|
filler.mode = "custom";
|
||||||
|
}
|
||||||
|
fs.writeFileSync( fillerPath, JSON.stringify(filler), 'utf-8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("Done fixing filler modes.");
|
||||||
|
}
|
||||||
|
|
||||||
function addFPS(db) {
|
function addFPS(db) {
|
||||||
let ffmpegSettings = db['ffmpeg-settings'].find()[0];
|
let ffmpegSettings = db['ffmpeg-settings'].find()[0];
|
||||||
let f = path.join(process.env.DATABASE, 'ffmpeg-settings.json');
|
let f = path.join(process.env.DATABASE, 'ffmpeg-settings.json');
|
||||||
@ -769,19 +787,23 @@ function addScalingAlgorithm(db) {
|
|||||||
fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) );
|
fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
function addFFMpegLock(db) {
|
function fixFFMpegPathSetting(db) {
|
||||||
let ffmpegSettings = db['ffmpeg-settings'].find()[0];
|
let ffmpegSettings = db['ffmpeg-settings'].find()[0];
|
||||||
let f = path.join(process.env.DATABASE, 'ffmpeg-settings.json');
|
let f = path.join(process.env.DATABASE, 'ffmpeg-settings.json');
|
||||||
if ( typeof(ffmpegSettings.ffmpegPathLockDate) === 'undefined' || ffmpegSettings.ffmpegPathLockDate == null ) {
|
let f2 = path.join(process.env.DATABASE, 'ffmpeg-path.json');
|
||||||
|
delete ffmpegSettings.ffmpegPathLockDate;
|
||||||
|
let fpath = ffmpegSettings.ffmpegPath;
|
||||||
|
delete ffmpegSettings.ffmpegPath;
|
||||||
|
|
||||||
console.log("Adding ffmpeg lock. For your security it will not be possible to modify the ffmpeg path using the UI anymore unless you launch dizquetv by following special instructions..");
|
if (typeof(fpath) === "string" ) {
|
||||||
// We are migrating an existing db that had a ffmpeg path. Make sure
|
console.log(`Found existing setting ffmpegPath=${fpath}, creating setting file (This file will get ignored if you are already setting an environment variable (the docker images do that)).`);
|
||||||
// it's already locked.
|
let pathJson = { ffmpegPath : fpath };
|
||||||
ffmpegSettings.ffmpegPathLockDate = new Date().getTime() - 2 * DAY_MS;
|
fs.writeFileSync( f2, JSON.stringify( pathJson ) );
|
||||||
}
|
}
|
||||||
fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) );
|
fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function moveBackup(path) {
|
function moveBackup(path) {
|
||||||
if (fs.existsSync(`${process.env.DATABASE}${path}`) ) {
|
if (fs.existsSync(`${process.env.DATABASE}${path}`) ) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|||||||
@ -1,13 +1,98 @@
|
|||||||
const exec = require('child_process').exec;
|
const exec = require('child_process').exec;
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
class FFMPEGInfo {
|
class FFMPEGInfo {
|
||||||
constructor(opts) {
|
|
||||||
this.ffmpegPath = opts.ffmpegPath
|
constructor(env, dbPath) {
|
||||||
|
this.initialized = false;
|
||||||
|
this.env = env;
|
||||||
|
this.dbPath = dbPath;
|
||||||
|
this.ffmpegPath = null;
|
||||||
|
this.origin = "Not found";
|
||||||
}
|
}
|
||||||
async getVersion() {
|
|
||||||
|
async initialize() {
|
||||||
|
let selectedPath = null;
|
||||||
|
if (typeof(this.env.DIZQUETV_FFMPEG_PATH) === "string") {
|
||||||
|
selectedPath = this.env.DIZQUETV_FFMPEG_PATH;
|
||||||
|
this.origin = "env.DIZQUETV_FFMPEG_PATH";
|
||||||
|
} else {
|
||||||
|
selectedPath = await this.getPathFromFile(this.dbPath, 'ffmpeg-path.json');
|
||||||
|
this.origin = "ffmpeg-path.json";
|
||||||
|
}
|
||||||
|
if (selectedPath == null) {
|
||||||
|
//windows Path environment var
|
||||||
|
let paths = this.env.Path;
|
||||||
|
if (typeof(paths) === "string") {
|
||||||
|
let maybe = paths.split(";").filter(
|
||||||
|
(str) => str.contains("ffmpeg" )
|
||||||
|
)[0];
|
||||||
|
if (typeof(maybe) === "string") {
|
||||||
|
selectedPath = path.join(maybe, "ffmpeg.exe");
|
||||||
|
this.origin = "Widnows Env. Path";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selectedPath == null) {
|
||||||
|
//Default install path for ffmpeg in n*x OSes.
|
||||||
|
// if someone has built ffmpeg manually or wants an alternate
|
||||||
|
// path, they are most likely capable of configuring it manually.
|
||||||
|
selectedPath = "/usr/bin/ffmpeg";
|
||||||
|
this.origin = "Default";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedPath != null) {
|
||||||
|
let version = await this.checkVersion(selectedPath);
|
||||||
|
if (version == null) {
|
||||||
|
selectedPath = null;
|
||||||
|
} else {
|
||||||
|
console.log(`FFmpeg found: ${selectedPath} from: ${this.origin}. version: ${version}`);
|
||||||
|
this.ffmpegPath = selectedPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.initialized = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPath() {
|
||||||
|
if (! this.initialized) {
|
||||||
|
await this.initialize();
|
||||||
|
}
|
||||||
|
return this.ffmpegPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPathFromFile(folder, fileName) {
|
||||||
|
let f = path.join(folder, fileName);
|
||||||
|
try {
|
||||||
|
let json = await new Promise( (resolve, reject) => {
|
||||||
|
fs.readFile(f, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
resolve( JSON.parse(data) )
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let ffmpeg = json["ffmpegPath"];
|
||||||
|
if (typeof(ffmpeg) === "string") {
|
||||||
|
return ffmpeg;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkVersion(ffmpegPath) {
|
||||||
try {
|
try {
|
||||||
let s = await new Promise( (resolve, reject) => {
|
let s = await new Promise( (resolve, reject) => {
|
||||||
exec( `"${this.ffmpegPath}" -version`, function(error, stdout, stderr){
|
exec( `"${ffmpegPath}" -version`, function(error, stdout, stderr){
|
||||||
if (error !== null) {
|
if (error !== null) {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
@ -23,7 +108,20 @@ class FFMPEGInfo {
|
|||||||
return m[1];
|
return m[1];
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error getting ffmpeg version", err);
|
console.error("Error getting ffmpeg version", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async getVersion() {
|
||||||
|
if (! this.initialized) {
|
||||||
|
await this.initialize();
|
||||||
|
}
|
||||||
|
let version = await this.checkVersion(this.ffmpegPath);
|
||||||
|
if (version == null) {
|
||||||
return "Error";
|
return "Error";
|
||||||
|
} else {
|
||||||
|
return version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ const REALLY_RIDICULOUSLY_HIGH_FPS_FOR_DIZQUETVS_USECASE = 120;
|
|||||||
class FFMPEG extends events.EventEmitter {
|
class FFMPEG extends events.EventEmitter {
|
||||||
constructor(opts, channel) {
|
constructor(opts, channel) {
|
||||||
super()
|
super()
|
||||||
|
this.ffmpegPath = opts.ffmpegPath;
|
||||||
this.opts = opts;
|
this.opts = opts;
|
||||||
this.errorPicturePath = `http://localhost:${process.env.PORT}/images/generic-error-screen.png`;
|
this.errorPicturePath = `http://localhost:${process.env.PORT}/images/generic-error-screen.png`;
|
||||||
this.ffmpegName = "unnamed ffmpeg";
|
this.ffmpegName = "unnamed ffmpeg";
|
||||||
@ -22,7 +23,6 @@ class FFMPEG extends events.EventEmitter {
|
|||||||
this.opts.maxFPS = REALLY_RIDICULOUSLY_HIGH_FPS_FOR_DIZQUETVS_USECASE;
|
this.opts.maxFPS = REALLY_RIDICULOUSLY_HIGH_FPS_FOR_DIZQUETVS_USECASE;
|
||||||
}
|
}
|
||||||
this.channel = channel
|
this.channel = channel
|
||||||
this.ffmpegPath = opts.ffmpegPath
|
|
||||||
|
|
||||||
let resString = opts.targetResolution;
|
let resString = opts.targetResolution;
|
||||||
if (
|
if (
|
||||||
@ -322,7 +322,7 @@ class FFMPEG extends events.EventEmitter {
|
|||||||
if (watermark.animated === true) {
|
if (watermark.animated === true) {
|
||||||
ffmpegArgs.push('-ignore_loop', '0');
|
ffmpegArgs.push('-ignore_loop', '0');
|
||||||
}
|
}
|
||||||
ffmpegArgs.push(`-i`, `${watermark.url}` );
|
ffmpegArgs.push(`-i`, `async:cache:${watermark.url}` );
|
||||||
overlayFile = inputFiles++;
|
overlayFile = inputFiles++;
|
||||||
this.ensureResolution = true;
|
this.ensureResolution = true;
|
||||||
}
|
}
|
||||||
@ -601,7 +601,7 @@ class FFMPEG extends events.EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (! this.sentData) {
|
if (! this.sentData) {
|
||||||
this.emit('error', { code: code, cmd: `${this.opts.ffmpegPath} ${ffmpegArgs.join(' ')}` })
|
this.emit('error', { code: code, cmd: `${this.ffmpegPath} ${ffmpegArgs.join(' ')}` })
|
||||||
}
|
}
|
||||||
console.log( `${this.ffmpegName} exited with code 255.` );
|
console.log( `${this.ffmpegName} exited with code 255.` );
|
||||||
this.emit('close', code)
|
this.emit('close', code)
|
||||||
|
|||||||
@ -69,9 +69,6 @@ function cleanUpProgram(program) {
|
|||||||
program.endPosition = parseInt(program.endPosition, 10);
|
program.endPosition = parseInt(program.endPosition, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (program.start && program.stop) {
|
|
||||||
program.duration = new Date(program.stop) - new Date(program.start);
|
|
||||||
}
|
|
||||||
delete program.streams;
|
delete program.streams;
|
||||||
delete program.durationStr;
|
delete program.durationStr;
|
||||||
delete program.commercials;
|
delete program.commercials;
|
||||||
|
|||||||
@ -4,11 +4,8 @@ const path = require('path');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
class FfmpegSettingsService {
|
class FfmpegSettingsService {
|
||||||
constructor(db, unlock) {
|
constructor(db) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
if (unlock) {
|
|
||||||
this.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get() {
|
get() {
|
||||||
@ -21,13 +18,6 @@ class FfmpegSettingsService {
|
|||||||
return ffmpeg;
|
return ffmpeg;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock() {
|
|
||||||
let ffmpeg = this.getCurrentState();
|
|
||||||
console.log("ffmpeg path UI unlocked for another day...");
|
|
||||||
ffmpeg.ffmpegPathLockDate = new Date().getTime() + DAY_MS;
|
|
||||||
this.db['ffmpeg-settings'].update({ _id: ffmpeg._id }, ffmpeg)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
update(attempt) {
|
update(attempt) {
|
||||||
let ffmpeg = this.getCurrentState();
|
let ffmpeg = this.getCurrentState();
|
||||||
@ -62,7 +52,6 @@ class FfmpegSettingsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
// Even if reseting, it's impossible to unlock the ffmpeg path
|
|
||||||
let ffmpeg = databaseMigration.defaultFFMPEG() ;
|
let ffmpeg = databaseMigration.defaultFFMPEG() ;
|
||||||
this.update(ffmpeg);
|
this.update(ffmpeg);
|
||||||
return this.get();
|
return this.get();
|
||||||
|
|||||||
150
src/services/filler-service.js
Normal file
150
src/services/filler-service.js
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
const events = require('events')
|
||||||
|
|
||||||
|
const FILLER_UPDATE = 30 * 60 * 1000; //30 minutes might be too aggressive
|
||||||
|
//this will be configurable one day.
|
||||||
|
|
||||||
|
class FillerService extends events.EventEmitter {
|
||||||
|
|
||||||
|
constructor(fillerDB, plexProxyService, channelService) {
|
||||||
|
super();
|
||||||
|
this.fillerDB = fillerDB;
|
||||||
|
this.plexProxyService = plexProxyService;
|
||||||
|
this.channelService = channelService;
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveFiller(id, body) {
|
||||||
|
body = await this.prework(body);
|
||||||
|
return this.fillerDB.saveFiller(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createFiller(body) {
|
||||||
|
body = await this.prework(body);
|
||||||
|
return this.fillerDB.createFiller(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFillerChannels(id) {
|
||||||
|
let numbers = await this.channelService.getAllChannelNumbers();
|
||||||
|
let channels = [];
|
||||||
|
await Promise.all( numbers.map( async(number) => {
|
||||||
|
let ch = await this.channelService.getChannel(number);
|
||||||
|
let name = ch.name;
|
||||||
|
let fillerCollections = ch.fillerCollections;
|
||||||
|
for (let i = 0 ; i < fillerCollections.length; i++) {
|
||||||
|
if (fillerCollections[i].id === id) {
|
||||||
|
channels.push( {
|
||||||
|
number: number,
|
||||||
|
name : name,
|
||||||
|
} );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ch = null;
|
||||||
|
|
||||||
|
} ) );
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteFiller(id) {
|
||||||
|
try {
|
||||||
|
let channels = await this.getFillerChannels(id);
|
||||||
|
await Promise.all( channels.map( async(channel) => {
|
||||||
|
console.log(`Updating channel ${channel.number} , remove filler: ${id}`);
|
||||||
|
let json = await channelService.getChannel(channel.number);
|
||||||
|
json.fillerCollections = json.fillerCollections.filter( (col) => {
|
||||||
|
return col.id != id;
|
||||||
|
} );
|
||||||
|
await this.channelService.saveChannel( channel.number, json );
|
||||||
|
} ) );
|
||||||
|
} finally {
|
||||||
|
await this.fillerDB.deleteFiller(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async prework(body) {
|
||||||
|
if (body.mode === "import") {
|
||||||
|
body.content = await this.getContents(body);
|
||||||
|
body.import.lastRefreshTime = new Date().getTime();
|
||||||
|
} else {
|
||||||
|
delete body.import;
|
||||||
|
}
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getContents(body) {
|
||||||
|
let serverKey = body.import.serverName;
|
||||||
|
let key = body.import.key;
|
||||||
|
let content = await this.plexProxyService.getKeyMediaContents(serverKey, key);
|
||||||
|
console.log(JSON.stringify(content));
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFillersFromChannel(channel) {
|
||||||
|
|
||||||
|
let loadChannelFiller = async(fillerEntry) => {
|
||||||
|
let content = [];
|
||||||
|
try {
|
||||||
|
let filler = await this.fillerDB.getFiller(fillerEntry.id);
|
||||||
|
await this.fillerUsageWatcher(fillerEntry.id, filler);
|
||||||
|
content = filler.content;
|
||||||
|
} catch(e) {
|
||||||
|
console.error(`Channel #${channel.number} - ${channel.name} references an unattainable filler id: ${fillerEntry.id}`, e);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: fillerEntry.id,
|
||||||
|
content: content,
|
||||||
|
weight: fillerEntry.weight,
|
||||||
|
cooldown: fillerEntry.cooldown,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return await Promise.all(
|
||||||
|
channel.fillerCollections.map(loadChannelFiller)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fillerUsageWatcher(id, filler) {
|
||||||
|
if (filler.mode === "import") {
|
||||||
|
//I need to upgrade nodejs version ASAP
|
||||||
|
let lastTime = 0;
|
||||||
|
if (
|
||||||
|
(typeof(filler.import) !== "undefined")
|
||||||
|
&&
|
||||||
|
!isNaN(filler.import.lastRefreshTime)
|
||||||
|
) {
|
||||||
|
lastTime = filler.import.lastRefreshTime;
|
||||||
|
}
|
||||||
|
let t = new Date().getTime();
|
||||||
|
if ( t - lastTime >= FILLER_UPDATE) {
|
||||||
|
//time to do an update.
|
||||||
|
if ( (typeof(filler.content) === "undefined")
|
||||||
|
|| (filler.content.length == 0)
|
||||||
|
) {
|
||||||
|
//It should probably be an sync update...
|
||||||
|
await this.refreshFiller(id);
|
||||||
|
} else {
|
||||||
|
this.refreshFiller(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshFiller(id) {
|
||||||
|
let t0 = new Date().getTime();
|
||||||
|
console.log(`Refreshing filler with id=${id}`);
|
||||||
|
try {
|
||||||
|
let filler = await this.fillerDB.getFiller(id);
|
||||||
|
await this.saveFiller(id, filler);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`Unable to update filler: ${id}`, err);
|
||||||
|
} finally {
|
||||||
|
let t1 = new Date().getTime();
|
||||||
|
console.log(`Refreshed filler with id=${id} in ${t1-t0}ms`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = FillerService
|
||||||
94
src/services/plex-proxy-service.js
Normal file
94
src/services/plex-proxy-service.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
const Plex = require('../plex.js')
|
||||||
|
const events = require('events')
|
||||||
|
|
||||||
|
class PlexProxyService extends events.EventEmitter {
|
||||||
|
|
||||||
|
constructor(plexServerDB) {
|
||||||
|
super();
|
||||||
|
this.plexServerDB = plexServerDB;
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(serverName64, path) {
|
||||||
|
let plexServer = await getPlexServer64(this.plexServerDB, serverName64);
|
||||||
|
// A potential area of improvement is to reuse the client when possible
|
||||||
|
let client = new Plex(plexServer);
|
||||||
|
return { MediaContainer: await client.Get("/" + path) };
|
||||||
|
}
|
||||||
|
|
||||||
|
async getKeyMediaContents(serverName, key) {
|
||||||
|
let plexServer = await getPlexServer(this.plexServerDB, serverName);
|
||||||
|
let client = new Plex(plexServer);
|
||||||
|
let obj = { MediaContainer: await client.Get(key) };
|
||||||
|
let metadata = obj.MediaContainer.Metadata;
|
||||||
|
if ( typeof(metadata) !== "object") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
metadata = metadata.map( (item) => fillerMapper(serverName, item) );
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillerMapper(serverName, plexMetadata) {
|
||||||
|
let image = {};
|
||||||
|
if ( (typeof(plexMetadata.Image) === "object")
|
||||||
|
&& (typeof(plexMetadata.Image[0]) === "object")
|
||||||
|
) {
|
||||||
|
image = plexMetadata.Image[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
let media = {};
|
||||||
|
if ( (typeof(plexMetadata.Media) === "object")
|
||||||
|
&& (typeof(plexMetadata.Media[0]) === "object")
|
||||||
|
) {
|
||||||
|
media = plexMetadata.Media[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let part = {};
|
||||||
|
if ( (typeof(media.Part) === "object")
|
||||||
|
&& (typeof(media.Part[0]) === "object")
|
||||||
|
) {
|
||||||
|
part = media.Part[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
title : plexMetadata.title,
|
||||||
|
key : plexMetadata.key,
|
||||||
|
ratingKey: plexMetadata.ratingKey,
|
||||||
|
icon : image.url,
|
||||||
|
type : plexMetadata.type,
|
||||||
|
duration : part.duration,
|
||||||
|
durationStr : undefined,
|
||||||
|
summary : "",
|
||||||
|
date : "",
|
||||||
|
year : plexMetadata.year,
|
||||||
|
plexFile : part.key,
|
||||||
|
file : part.file,
|
||||||
|
showTitle: plexMetadata.title,
|
||||||
|
episode : 1,
|
||||||
|
season : 1,
|
||||||
|
serverKey: serverName,
|
||||||
|
commercials: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPlexServer(plexServerDB, serverKey) {
|
||||||
|
let server = await plexServerDB.getPlexServerByName(serverKey);
|
||||||
|
if (server == null) {
|
||||||
|
throw Error("server not found");
|
||||||
|
}
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function getPlexServer64(plexServerDB, serverName64) {
|
||||||
|
let serverKey = Buffer.from(serverName64, 'base64').toString('utf-8');
|
||||||
|
return await getPlexServer(plexServerDB, serverKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = PlexProxyService
|
||||||
33
src/video.js
33
src/video.js
@ -18,17 +18,18 @@ async function shutdown() {
|
|||||||
stopPlayback = true;
|
stopPlayback = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function video( channelService, fillerDB, db, programmingService, activeChannelService, programPlayTimeDB ) {
|
function video( channelService, fillerService, db, programmingService, activeChannelService, programPlayTimeDB, ffmpegInfo ) {
|
||||||
var router = express.Router()
|
var router = express.Router()
|
||||||
|
|
||||||
router.get('/setup', (req, res) => {
|
router.get('/setup', async (req, res) => {
|
||||||
let ffmpegSettings = db['ffmpeg-settings'].find()[0]
|
let ffmpegSettings = db['ffmpeg-settings'].find()[0]
|
||||||
// Check if ffmpeg path is valid
|
// Check if ffmpeg path is valid
|
||||||
if (!fs.existsSync(ffmpegSettings.ffmpegPath)) {
|
let ffmpegPath = await ffmpegInfo.getPath();
|
||||||
res.status(500).send("FFMPEG path is invalid. The file (executable) doesn't exist.")
|
if (ffmpegPath == null) {
|
||||||
console.error("The FFMPEG Path is invalid. Please check your configuration.")
|
res.status(500).send("Missing FFmpeg.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ffmpegSettings.ffmpegPath = ffmpegPath;
|
||||||
|
|
||||||
console.log(`\r\nStream starting. Channel: 1 (dizqueTV)`)
|
console.log(`\r\nStream starting. Channel: 1 (dizqueTV)`)
|
||||||
|
|
||||||
@ -72,14 +73,14 @@ function video( channelService, fillerDB, db, programmingService, activeChannelS
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let ffmpegSettings = db['ffmpeg-settings'].find()[0]
|
|
||||||
|
|
||||||
// Check if ffmpeg path is valid
|
// Check if ffmpeg path is valid
|
||||||
if (!fs.existsSync(ffmpegSettings.ffmpegPath)) {
|
let ffmpegSettings = db['ffmpeg-settings'].find()[0]
|
||||||
res.status(500).send("FFMPEG path is invalid. The file (executable) doesn't exist.")
|
let ffmpegPath = await ffmpegInfo.getPath();
|
||||||
console.error("The FFMPEG Path is invalid. Please check your configuration.")
|
if (ffmpegPath == null) {
|
||||||
|
res.status(500).send("Missing FFmpeg.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ffmpegSettings.ffmpegPath = ffmpegPath;
|
||||||
|
|
||||||
if (step == 0) {
|
if (step == 0) {
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
@ -174,14 +175,14 @@ function video( channelService, fillerDB, db, programmingService, activeChannelS
|
|||||||
|
|
||||||
let isBetween = ( (typeof req.query.between !== 'undefined') && (req.query.between=='1') );
|
let isBetween = ( (typeof req.query.between !== 'undefined') && (req.query.between=='1') );
|
||||||
|
|
||||||
let ffmpegSettings = db['ffmpeg-settings'].find()[0]
|
|
||||||
|
|
||||||
// Check if ffmpeg path is valid
|
// Check if ffmpeg path is valid
|
||||||
if (!fs.existsSync(ffmpegSettings.ffmpegPath)) {
|
let ffmpegSettings = db['ffmpeg-settings'].find()[0]
|
||||||
res.status(500).send("FFMPEG path is invalid. The file (executable) doesn't exist.")
|
let ffmpegPath = await ffmpegInfo.getPath();
|
||||||
console.error("The FFMPEG Path is invalid. Please check your configuration.")
|
if (ffmpegPath == null) {
|
||||||
|
res.status(500).send("Missing FFmpeg.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ffmpegSettings.ffmpegPath = ffmpegPath;
|
||||||
|
|
||||||
if (ffmpegSettings.disablePreludes === true) {
|
if (ffmpegSettings.disablePreludes === true) {
|
||||||
//disable the preludes
|
//disable the preludes
|
||||||
@ -303,7 +304,7 @@ function video( channelService, fillerDB, db, programmingService, activeChannelS
|
|||||||
if ( (prog == null) || (typeof(prog) === 'undefined') || (prog.program == null) || (typeof(prog.program) == "undefined") ) {
|
if ( (prog == null) || (typeof(prog) === 'undefined') || (prog.program == null) || (typeof(prog.program) == "undefined") ) {
|
||||||
throw "No video to play, this means there's a serious unexpected bug or the channel db is corrupted."
|
throw "No video to play, this means there's a serious unexpected bug or the channel db is corrupted."
|
||||||
}
|
}
|
||||||
let fillers = await fillerDB.getFillersFromChannel(brandChannel);
|
let fillers = await fillerService.getFillersFromChannel(brandChannel);
|
||||||
try {
|
try {
|
||||||
let lineup = helperFuncs.createLineup(programPlayTimeDB, prog, brandChannel, fillers, isFirst)
|
let lineup = helperFuncs.createLineup(programPlayTimeDB, prog, brandChannel, fillers, isFirst)
|
||||||
lineupItem = lineup.shift();
|
lineupItem = lineup.shift();
|
||||||
|
|||||||
@ -6,7 +6,14 @@ module.exports = function (dizquetv, resolutionOptions) {
|
|||||||
scope: {
|
scope: {
|
||||||
},
|
},
|
||||||
link: function (scope, element, attrs) {
|
link: function (scope, element, attrs) {
|
||||||
//add validations to ffmpeg settings, speciall commas in codec name
|
|
||||||
|
scope.ffmpegPathLoading = true;
|
||||||
|
scope.ffmpegPath = ""
|
||||||
|
dizquetv.getFFMpegPath().then( (fpath) => {
|
||||||
|
scope.ffmpegPath = fpath.ffmpegPath;
|
||||||
|
scope.ffmpegPathLoading = false;
|
||||||
|
});
|
||||||
|
//add validations to ffmpeg settings, special commas in codec name
|
||||||
dizquetv.getFfmpegSettings().then((settings) => {
|
dizquetv.getFfmpegSettings().then((settings) => {
|
||||||
scope.settings = settings
|
scope.settings = settings
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
module.exports = function ($timeout, commonProgramTools, getShowData) {
|
module.exports = function ($timeout, dizquetv, commonProgramTools, getShowData) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
templateUrl: 'templates/filler-config.html',
|
templateUrl: 'templates/filler-config.html',
|
||||||
@ -13,6 +13,16 @@ module.exports = function ($timeout, commonProgramTools, getShowData) {
|
|||||||
scope.content = [];
|
scope.content = [];
|
||||||
scope.visible = false;
|
scope.visible = false;
|
||||||
scope.error = undefined;
|
scope.error = undefined;
|
||||||
|
scope.modes = [ {
|
||||||
|
name: "import",
|
||||||
|
description: "Collection/Playlist from Plex",
|
||||||
|
}, {
|
||||||
|
name: "custom",
|
||||||
|
description: "Custom List of Clips",
|
||||||
|
} ];
|
||||||
|
scope.servers = [];
|
||||||
|
scope.libraries = [];
|
||||||
|
scope.sources = [];
|
||||||
|
|
||||||
function refreshContentIndexes() {
|
function refreshContentIndexes() {
|
||||||
for (let i = 0; i < scope.content.length; i++) {
|
for (let i = 0; i < scope.content.length; i++) {
|
||||||
@ -47,20 +57,173 @@ module.exports = function ($timeout, commonProgramTools, getShowData) {
|
|||||||
console.log("movedFunction(" + index + ")");
|
console.log("movedFunction(" + index + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope.serverChanged = async () => {
|
||||||
|
if (scope.server === "") {
|
||||||
|
scope.libraryKey = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scope.loadingLibraries = true;
|
||||||
|
try {
|
||||||
|
let libraries = (await dizquetv.getFromPlexProxy(scope.server, "/library/sections")).Directory;
|
||||||
|
if ( typeof(libraries) === "undefined") {
|
||||||
|
libraries = []
|
||||||
|
}
|
||||||
|
let officialLibraries = libraries.map( (library) => {
|
||||||
|
return {
|
||||||
|
"key" : library.key,
|
||||||
|
"description" : library.title,
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
let defaultLibrary = {
|
||||||
|
"key": "",
|
||||||
|
"description" : "Select a Library...",
|
||||||
|
}
|
||||||
|
let playlists = [
|
||||||
|
{
|
||||||
|
"key": "$PLAYLISTS",
|
||||||
|
"description" : "Playlists",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
let combined = officialLibraries.concat(playlists);
|
||||||
|
if (! combined.some( (library) => library.key === scope.libraryKey) ) {
|
||||||
|
scope.libraryKey = "";
|
||||||
|
scope.libraries = [defaultLibrary].concat(combined);
|
||||||
|
} else {
|
||||||
|
scope.libraries = combined;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
scope.libraries = [ { name: "", description: "Unable to load libraries"} ];
|
||||||
|
scope.libraryKey = ""
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
scope.loadingLibraries = false;
|
||||||
|
$timeout( () => {}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
scope.linker( (filler) => {
|
scope.libraryChanged = async () => {
|
||||||
|
if (scope.libraryKey == null) {
|
||||||
|
throw Error(`null libraryKey? ${scope.libraryKey} ${new Date().getTime()} `);
|
||||||
|
}
|
||||||
|
if (scope.libraryKey === "") {
|
||||||
|
scope.sourceKey = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scope.loadingCollections = true;
|
||||||
|
try {
|
||||||
|
let collections;
|
||||||
|
if (scope.libraryKey === "$PLAYLISTS") {
|
||||||
|
collections = (await dizquetv.getFromPlexProxy(scope.server, `/playlists`)).Metadata;
|
||||||
|
} else {
|
||||||
|
collections = (await dizquetv.getFromPlexProxy(scope.server, `/library/sections/${scope.libraryKey}/collections`));
|
||||||
|
collections = collections.Metadata
|
||||||
|
}
|
||||||
|
if (typeof(collections) === "undefined") {
|
||||||
|
//when the library has no collections it returns size=0
|
||||||
|
//and no array
|
||||||
|
collections = [];
|
||||||
|
}
|
||||||
|
let officialCollections = collections.map( (col) => {
|
||||||
|
return {
|
||||||
|
"key" : col.key,
|
||||||
|
"description" : col.title,
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
let defaultSource = {
|
||||||
|
"key": "",
|
||||||
|
"description" : "Select a Source...",
|
||||||
|
};
|
||||||
|
if (officialCollections.length == 0) {
|
||||||
|
defaultSource = {
|
||||||
|
"key": "",
|
||||||
|
"description" : "(No collections/lists found)",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! officialCollections.some( (col) => col.key === scope.sourceKey ) ) {
|
||||||
|
scope.sourceKey = "";
|
||||||
|
scope.sources = [defaultSource].concat(officialCollections);
|
||||||
|
} else {
|
||||||
|
scope.sources = officialCollections;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
scope.sources = [ { name: "", description: "Unable to load collections"} ];
|
||||||
|
scope.sourceKey = "";
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
scope.loadingCollections = false;
|
||||||
|
$timeout( () => {}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let reloadServers = async() => {
|
||||||
|
scope.loadingServers = true;
|
||||||
|
try {
|
||||||
|
let servers = await dizquetv.getPlexServers();
|
||||||
|
scope.servers = servers.map( (s) => {
|
||||||
|
return {
|
||||||
|
"name" : s.name,
|
||||||
|
"description" : `Plex - ${s.name}`,
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
let defaultServer = {
|
||||||
|
name: "",
|
||||||
|
description: "Select a Plex server..."
|
||||||
|
};
|
||||||
|
if (! scope.servers.some( (server) => server.name === scope.server) ) {
|
||||||
|
scope.server = "";
|
||||||
|
scope.servers = [defaultServer].concat(scope.servers);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
scope.server = "";
|
||||||
|
scope.servers = [ {name:"", description:"Could not load servers"} ];
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
scope.loadingServers = false;
|
||||||
|
$timeout( () => {}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
await scope.serverChanged();
|
||||||
|
await scope.libraryChanged();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
scope.linker( async (filler) => {
|
||||||
|
|
||||||
if ( typeof(filler) === 'undefined') {
|
if ( typeof(filler) === 'undefined') {
|
||||||
scope.name = "";
|
scope.name = "";
|
||||||
scope.content = [];
|
scope.content = [];
|
||||||
scope.id = undefined;
|
scope.id = undefined;
|
||||||
scope.title = "Create Filler List";
|
scope.title = "Create Filler List";
|
||||||
|
scope.mode = "import";
|
||||||
|
scope.server = "";
|
||||||
|
scope.libraryKey = "";
|
||||||
|
scope.sourceKey = "";
|
||||||
} else {
|
} else {
|
||||||
scope.name = filler.name;
|
scope.name = filler.name;
|
||||||
scope.content = filler.content;
|
scope.content = filler.content;
|
||||||
scope.id = filler.id;
|
scope.id = filler.id;
|
||||||
scope.title = "Edit Filler List";
|
scope.title = "Edit Filler List";
|
||||||
|
scope.mode = filler.mode;
|
||||||
|
scope.server = filler?.import?.serverName;
|
||||||
|
if ( typeof(scope.server) !== "string" ) {
|
||||||
|
scope.server = "";
|
||||||
|
}
|
||||||
|
scope.libraryKey = filler?.import?.meta?.libraryKey;
|
||||||
|
if ( typeof(scope.libraryKey) !== "string" ) {
|
||||||
|
scope.libraryKey = "";
|
||||||
|
}
|
||||||
|
scope.sourceKey = filler?.import?.key;
|
||||||
|
if ( typeof(scope.sourceKey) !== "string" ) {
|
||||||
|
scope.sourceKey = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
await reloadServers();
|
||||||
|
scope.source = "";
|
||||||
refreshContentIndexes();
|
refreshContentIndexes();
|
||||||
scope.visible = true;
|
scope.visible = true;
|
||||||
} );
|
} );
|
||||||
@ -73,8 +236,17 @@ module.exports = function ($timeout, commonProgramTools, getShowData) {
|
|||||||
if ( (typeof(scope.name) === 'undefined') || (scope.name.length == 0) ) {
|
if ( (typeof(scope.name) === 'undefined') || (scope.name.length == 0) ) {
|
||||||
scope.error = "Please enter a name";
|
scope.error = "Please enter a name";
|
||||||
}
|
}
|
||||||
if ( scope.content.length == 0) {
|
if ( scope?.mode === "import" ) {
|
||||||
scope.error = "Please add at least one clip.";
|
if ( (typeof(scope?.server) !== "string" ) || (scope?.server === "") ) {
|
||||||
|
scope.error = "Please select a server"
|
||||||
|
}
|
||||||
|
if ( (typeof(scope?.source) !== "string" ) && (scope?.source === "") ) {
|
||||||
|
scope.error = "Please select a source."
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( scope.content.length == 0) {
|
||||||
|
scope.error = "Please add at least one clip.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (typeof(scope.error) !== 'undefined') {
|
if (typeof(scope.error) !== 'undefined') {
|
||||||
$timeout( () => {
|
$timeout( () => {
|
||||||
@ -83,14 +255,30 @@ module.exports = function ($timeout, commonProgramTools, getShowData) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scope.visible = false;
|
scope.visible = false;
|
||||||
scope.onDone( {
|
let object = {
|
||||||
name: scope.name,
|
name: scope.name,
|
||||||
content: scope.content.map( (c) => {
|
content: scope.content.map( (c) => {
|
||||||
delete c.$index
|
delete c.$index
|
||||||
return c;
|
return c;
|
||||||
} ),
|
} ),
|
||||||
id: scope.id,
|
id: scope.id,
|
||||||
} );
|
mode: scope.mode,
|
||||||
|
|
||||||
|
};
|
||||||
|
if (object.mode === "import") {
|
||||||
|
object.content = [];
|
||||||
|
//In reality dizqueTV only needs to know the server name
|
||||||
|
//and the source key, the meta object is for extra data
|
||||||
|
//that is useful for external things like this UI.
|
||||||
|
object.import = {
|
||||||
|
serverName : scope.server,
|
||||||
|
key: scope.sourceKey,
|
||||||
|
meta: {
|
||||||
|
libraryKey : scope.libraryKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope.onDone( object );
|
||||||
}
|
}
|
||||||
scope.getText = (clip) => {
|
scope.getText = (clip) => {
|
||||||
let show = getShowData(clip);
|
let show = getShowData(clip);
|
||||||
|
|||||||
@ -31,13 +31,7 @@ module.exports = function (plex, dizquetv, $timeout, commonProgramTools) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
scope.selectOrigin = function (origin) {
|
scope.selectOrigin = function (origin) {
|
||||||
if ( origin.type === 'plex' ) {
|
updateLibrary(origin);
|
||||||
scope.plexServer = origin.server;
|
|
||||||
updateLibrary(scope.plexServer);
|
|
||||||
} else {
|
|
||||||
scope.plexServer = undefined;
|
|
||||||
updateCustomShows();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
scope._onFinish = (s, insertPoint) => {
|
scope._onFinish = (s, insertPoint) => {
|
||||||
if (s.length > scope.limit) {
|
if (s.length > scope.limit) {
|
||||||
@ -99,20 +93,31 @@ module.exports = function (plex, dizquetv, $timeout, commonProgramTools) {
|
|||||||
"type" : "plex",
|
"type" : "plex",
|
||||||
"name" : `Plex - ${s.name}`,
|
"name" : `Plex - ${s.name}`,
|
||||||
"server": s,
|
"server": s,
|
||||||
|
"loaded" : false,
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
scope.currentOrigin = scope.origins[0];
|
|
||||||
scope.plexServer = scope.currentOrigin.server;
|
|
||||||
scope.origins.push( {
|
scope.origins.push( {
|
||||||
"type": "dizquetv",
|
"type": "dizquetv",
|
||||||
"name" : "dizqueTV - Custom Shows",
|
"name" : "dizqueTV - Custom Shows",
|
||||||
|
"loaded" : false,
|
||||||
} );
|
} );
|
||||||
updateLibrary(scope.plexServer)
|
updateLibrary(scope.origins[0])
|
||||||
})
|
})
|
||||||
|
|
||||||
let updateLibrary = async(server) => {
|
let updateLibrary = async(origin) => {
|
||||||
|
scope.currentOrigin = origin;
|
||||||
|
origin.loaded = false;
|
||||||
|
if ( origin.type !== 'plex' ) {
|
||||||
|
scope.plexServer = undefined;
|
||||||
|
await updateCustomShows();
|
||||||
|
origin.loaded = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let server = scope.currentOrigin.server;
|
||||||
let lib = await plex.getLibrary(server);
|
let lib = await plex.getLibrary(server);
|
||||||
let play = await plex.getPlaylists(server);
|
let play = await plex.getPlaylists(server);
|
||||||
|
scope.currentOrigin.loaded = true;
|
||||||
|
scope.plexServer = server;
|
||||||
|
|
||||||
play.forEach( p => {
|
play.forEach( p => {
|
||||||
p.type = "playlist";
|
p.type = "playlist";
|
||||||
|
|||||||
@ -20,12 +20,9 @@ module.exports = function (plex, dizquetv, $timeout) {
|
|||||||
scope.servers = servers;
|
scope.servers = servers;
|
||||||
if(servers) {
|
if(servers) {
|
||||||
for (let i = 0; i < scope.servers.length; i++) {
|
for (let i = 0; i < scope.servers.length; i++) {
|
||||||
scope.servers[i].uiStatus = 0;
|
|
||||||
scope.servers[i].backendStatus = 0;
|
scope.servers[i].backendStatus = 0;
|
||||||
let t = (new Date()).getTime();
|
let t = (new Date()).getTime();
|
||||||
scope.servers[i].uiPending = t;
|
|
||||||
scope.servers[i].backendPending = t;
|
scope.servers[i].backendPending = t;
|
||||||
scope.refreshUIStatus(t, i);
|
|
||||||
scope.refreshBackendStatus(t, i);
|
scope.refreshBackendStatus(t, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,22 +48,6 @@ module.exports = function (plex, dizquetv, $timeout) {
|
|||||||
scope.refreshServerList();
|
scope.refreshServerList();
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.isAnyUIBad = () => {
|
|
||||||
let t = (new Date()).getTime();
|
|
||||||
if(scope.servers) {
|
|
||||||
for (let i = 0; i < scope.servers.length; i++) {
|
|
||||||
let s = scope.servers[i];
|
|
||||||
if (
|
|
||||||
(s.uiStatus == -1)
|
|
||||||
|| ( (s.uiStatus == 0) && (s.uiPending + 30000 < t) )
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.isAnyBackendBad = () => {
|
scope.isAnyBackendBad = () => {
|
||||||
let t = (new Date()).getTime();
|
let t = (new Date()).getTime();
|
||||||
if(scope.servers) {
|
if(scope.servers) {
|
||||||
@ -84,15 +65,6 @@ module.exports = function (plex, dizquetv, $timeout) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
scope.refreshUIStatus = async (t, i) => {
|
|
||||||
let s = await plex.check(scope.servers[i]);
|
|
||||||
if (scope.servers[i].uiPending == t) {
|
|
||||||
// avoid updating for a previous instance of the row
|
|
||||||
scope.servers[i].uiStatus = s;
|
|
||||||
}
|
|
||||||
scope.$apply();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.refreshBackendStatus = async (t, i) => {
|
scope.refreshBackendStatus = async (t, i) => {
|
||||||
let s = await dizquetv.checkExistingPlexServer(scope.servers[i].name);
|
let s = await dizquetv.checkExistingPlexServer(scope.servers[i].name);
|
||||||
if (scope.servers[i].backendPending == t) {
|
if (scope.servers[i].backendPending == t) {
|
||||||
|
|||||||
@ -19,12 +19,10 @@
|
|||||||
"minutes_to_sign_plex": "You have 2 minutes to sign into your Plex Account.",
|
"minutes_to_sign_plex": "You have 2 minutes to sign into your Plex Account.",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"uri": "URI",
|
"uri": "URI",
|
||||||
"ui_route": "UI Route",
|
"routeStatus": "Route Status",
|
||||||
"backend_route": "Backend Route",
|
|
||||||
"ok": "ok",
|
"ok": "ok",
|
||||||
"error": "error",
|
"error": "error",
|
||||||
"ui_bad": "If a Plex server configuration has problems with the UI route, the channel editor won't be able to access its content.",
|
"backend_bad": "A route problem means the dizqueTV server can't establish a connection with the configured Plex server.",
|
||||||
"backend_bad": "If a Plex server configuration has problems with the backend route, dizqueTV won't be able to play its content.",
|
|
||||||
"plex_transcoder_settings": "Plex Transcoder Settings",
|
"plex_transcoder_settings": "Plex Transcoder Settings",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"reset_options": "Reset Options",
|
"reset_options": "Reset Options",
|
||||||
|
|||||||
@ -15,40 +15,20 @@
|
|||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
<h6>FFMPEG Executable Path</h6>
|
<h6>FFMPEG Executable Path</h6>
|
||||||
<div class="row" ng-show="settings.lock !== true">
|
|
||||||
|
<div class="row">
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Path</label>
|
<label>Path</label>
|
||||||
<input id="ffmpegPath" ria-describedby="ffmpegHelp" type="text" class="form-control form-control-sm" ng-model="settings.ffmpegPath"></input>
|
<div class='loader' ng-show='ffmpegPathLoading'></div>
|
||||||
<small class="form-text text-muted">
|
<input id="ffmpegPath" ria-describedby="ffmpegHelp" type="text" class="form-control form-control-sm" ng-model="ffmpegPath" readonly ng-hide="ffmpegPathLoading"></input>
|
||||||
The path to the ffmpeg executable. (e.g: /usr/bin/ffmpeg or C:\ffmpeg\bin\ffmpeg.exe) FFMPEG version 4.2+ required. Check by opening the version tab.
|
<small class="form-text text-muted" ng-show="ffmpegPath != null">
|
||||||
|
The path to the ffmpeg executable. Please check the instructions if you need to change it.
|
||||||
</small>
|
</small>
|
||||||
|
<small class="form-text text-muted" ng-show="ffmpegPath == null">
|
||||||
</div>
|
dizqueTV uses FFmpeg to create video streams. Please check the instructions for info about how to set this up.
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<div class="form-group">
|
|
||||||
<input id="lockFfmpeg" type="checkbox" ng-model="settings.addLock"></input>
|
|
||||||
<label for="lockFfmpeg">Lock ffmpeg path setting</label>
|
|
||||||
<small class="form-text text-muted">This will lock the ffmpeg path setting so that it is no longer editable from UI. Even if you don't toggle this option, the setting will get locked in 24 hours.</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="row" ng-show="settings.lock === true">
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Path</label>
|
|
||||||
<input id="ffmpegPath" ria-describedby="ffmpegHelp" type="text" class="form-control form-control-sm" ng-model="settings.ffmpegPath" readonly></input>
|
|
||||||
<small class="form-text text-muted">
|
|
||||||
The ffmpeg path setting is currently locked and can't be edited from the UI. It's not usually necessary to update this path once it's known to be working. Run dizquetv with the <b>--unlock</b> command line argument to enable editing it again.
|
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,8 +11,52 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name">Filler Name:</label>
|
<label for="name">Filler Name:</label>
|
||||||
<input type="text" class="form-control" id="name" placeholder="Filler Name" ng-model="name" ></input>
|
<input type="text" class="form-control" id="name" placeholder="Filler Name" ng-model="name" ></input>
|
||||||
|
|
||||||
|
<div>Mode:</div>
|
||||||
|
<select class="custom-select"
|
||||||
|
ng-model="mode"
|
||||||
|
ng-options="theMode.name as theMode.description for theMode in modes" ></select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="mode==='import'">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="server">Server:</label>
|
||||||
|
|
||||||
|
<div class="loader" ng-show="loadingServers === true" ></div>
|
||||||
|
<select
|
||||||
|
ng-show="loadingServers !== true"
|
||||||
|
id="server"
|
||||||
|
class="custom-select"
|
||||||
|
ng-change="serverChanged()"
|
||||||
|
ng-model="server"
|
||||||
|
ng-options="x.name as x.description for x in servers" ></select>
|
||||||
|
</div>
|
||||||
|
<div ng-show="server !==''">
|
||||||
|
<label for="library">Library:</label>
|
||||||
|
|
||||||
|
<div class="loader" ng-show="loadingLibraries === true" ></div>
|
||||||
|
<select
|
||||||
|
ng-show="loadingLibraries !== true"
|
||||||
|
id="library"
|
||||||
|
class="custom-select"
|
||||||
|
ng-change="libraryChanged()"
|
||||||
|
ng-model="libraryKey"
|
||||||
|
ng-options="x.key as x.description for x in libraries" ></select>
|
||||||
|
</div>
|
||||||
|
<div ng-show="libraryKey !==''">
|
||||||
|
<label for="source">Source:</label>
|
||||||
|
|
||||||
|
<div class="loader" ng-show="loadingCollections === true" ></div>
|
||||||
|
<select
|
||||||
|
ng-show="loadingCollections !== true"
|
||||||
|
id="source"
|
||||||
|
class="custom-select"
|
||||||
|
ng-model="sourceKey"
|
||||||
|
ng-options="x.key as x.description for x in sources" ></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="mode==='custom'">
|
||||||
<h6 style="margin-top: 10px;">Clips</h6>
|
<h6 style="margin-top: 10px;">Clips</h6>
|
||||||
|
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
@ -65,10 +109,11 @@
|
|||||||
<div ng-show="content.length === 0">
|
<div ng-show="content.length === 0">
|
||||||
<p class="text-center text-info">Click the <span class="fa fa-plus"></span> to import filler content from your Plex server(s).</p>
|
<p class="text-center text-info">Click the <span class="fa fa-plus"></span> to import filler content from your Plex server(s).</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div vs-repeat class="modal-body container list-group list-group-root filler-list"
|
<div ng-show="mode==='custom'" vs-repeat class="modal-body container list-group list-group-root filler-list"
|
||||||
dnd-list="content" ng-if="showList()"
|
dnd-list="content" ng-if="showList()"
|
||||||
vs-repeat-reinitialized="vsReinitialized(event, startIndex, endIndex)"
|
vs-repeat-reinitialized="vsReinitialized(event, startIndex, endIndex)"
|
||||||
ng-init="setUpWatcher()"
|
ng-init="setUpWatcher()"
|
||||||
@ -76,8 +121,7 @@
|
|||||||
dnd-list=""
|
dnd-list=""
|
||||||
|
|
||||||
>
|
>
|
||||||
<div class="list-group-item flex-container filler-row" style="cursor: default; height:1.1em; overflow:hidden" ng-repeat="x in content" track-by="x.$index" dnd-draggable="x"
|
<div class="list-group-item flex-container filler-row" style="cursor: default; height:1.1em; overflow:hidden" ng-repeat="x in content" track-by="x.$index" dnd-draggable="x"
|
||||||
"
|
|
||||||
dnd-effect-allowed="move"
|
dnd-effect-allowed="move"
|
||||||
dnd-moved="movedFunction(x.$index)"
|
dnd-moved="movedFunction(x.$index)"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -43,7 +43,13 @@
|
|||||||
</button>
|
</button>
|
||||||
Content:
|
Content:
|
||||||
</label>
|
</label>
|
||||||
<ul ng-show="currentOrigin.type=='plex' " class="list-group list-group-root plex-panel" ng-init="setHeight = {'height': height + 'px'}" ng-style="setHeight" lazy-img-container>
|
|
||||||
|
<ul ng-show="currentOrigin.loaded!==true" class="list-group list-group-root plex-panel" ng-init="setHeight = {'height': height + 'px'}" ng-style="setHeight" lazy-img-container>
|
||||||
|
|
||||||
|
<li class="list-group-item"><div class="loader"></div> <div>Loading Library Contents...</div></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<ul ng-show="currentOrigin.type=='plex' && currentOrigin.loaded===true " class="list-group list-group-root plex-panel" ng-init="setHeight = {'height': height + 'px'}" ng-style="setHeight" lazy-img-container>
|
||||||
<li class="list-group-item" ng-repeat="a in libraries">
|
<li class="list-group-item" ng-repeat="a in libraries">
|
||||||
<div class="flex-container library-item-hover {{ displayImages ? 'w_images' : 'wo_images' }}" ng-click="getNested(a, true);">
|
<div class="flex-container library-item-hover {{ displayImages ? 'w_images' : 'wo_images' }}" ng-click="getNested(a, true);">
|
||||||
<span class="fa {{ a.collapse ? 'fa-chevron-down' : 'fa-chevron-right' }} tab"></span>
|
<span class="fa {{ a.collapse ? 'fa-chevron-down' : 'fa-chevron-right' }} tab"></span>
|
||||||
@ -122,7 +128,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul ng-show="currentOrigin.type=='dizquetv' " class="list-group list-group-root plex-panel" ng-init="setHeight = {'height': height + 'px'}" ng-style="setHeight" lazy-img-container>
|
<ul ng-show="currentOrigin.type=='dizquetv' && currentOrigin.loaded===true" class="list-group list-group-root plex-panel" ng-init="setHeight = {'height': height + 'px'}" ng-style="setHeight" lazy-img-container>
|
||||||
<li class="list-group-item" ng-repeat="x in customShows">
|
<li class="list-group-item" ng-repeat="x in customShows">
|
||||||
<div class="flex-container" ng-click="addCustomShow(x);">
|
<div class="flex-container" ng-click="addCustomShow(x);">
|
||||||
<span class="fa fa-plus-circle tab"></span>
|
<span class="fa fa-plus-circle tab"></span>
|
||||||
|
|||||||
@ -16,8 +16,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{{'settings_server.name' | i18next}}</th>
|
<th>{{'settings_server.name' | i18next}}</th>
|
||||||
<th>{{'settings_server.uri' | i18next}}</th>
|
<th>{{'settings_server.uri' | i18next}}</th>
|
||||||
<th>{{'settings_server.ui_route' | i18next}}</th>
|
<th>{{'settings_server.routeStatus' | i18next}}</th>
|
||||||
<th>{{'settings_server.backend_route' | i18next}}</th>
|
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="servers.length === 0">
|
<tr ng-if="servers.length === 0">
|
||||||
@ -31,11 +30,6 @@
|
|||||||
<tr ng-repeat="x in servers" ng-hide="serversPending" >
|
<tr ng-repeat="x in servers" ng-hide="serversPending" >
|
||||||
<td>{{ x.name }}</td>
|
<td>{{ x.name }}</td>
|
||||||
<td><span class='text-secondary text-small'>{{ x.uri }}</span></td>
|
<td><span class='text-secondary text-small'>{{ x.uri }}</span></td>
|
||||||
<td>
|
|
||||||
<div class='loader' ng-if="x.uiStatus == 0"></div>
|
|
||||||
<div class='text-success' ng-if="x.uiStatus == 1"><i class='fa fa-check'></i>{{'settings_server.ok' | i18next}}</div>
|
|
||||||
<div class='text-danger' ng-if="x.uiStatus == -1"><i class='fa fa-exclamation-triangle'></i>{{'settings_server.error' | i18next}}</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<div class='loader' ng-if="x.backendStatus == 0"></div>
|
<div class='loader' ng-if="x.backendStatus == 0"></div>
|
||||||
<div class='text-success' ng-if="x.backendStatus == 1"><i class='fa fa-check'></i>{{'settings_server.ok' | i18next}}</div>
|
<div class='text-success' ng-if="x.backendStatus == 1"><i class='fa fa-check'></i>{{'settings_server.ok' | i18next}}</div>
|
||||||
@ -51,11 +45,6 @@
|
|||||||
<p class="text-center text-danger small">{{serverError}}</p>
|
<p class="text-center text-danger small">{{serverError}}</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="isAnyUIBad()">
|
|
||||||
<td colspan="5">
|
|
||||||
<p class="text-center text-danger small">{{'settings_server.ui_bad' | i18next}}</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr ng-if="isAnyBackendBad()">
|
<tr ng-if="isAnyBackendBad()">
|
||||||
<td colspan="5">
|
<td colspan="5">
|
||||||
<p class="text-center text-danger small">{{'settings_server.server_bad' | i18next}}</p>
|
<p class="text-center text-danger small">{{'settings_server.server_bad' | i18next}}</p>
|
||||||
|
|||||||
@ -3,8 +3,8 @@ module.exports = function ($http, $q) {
|
|||||||
getVersion: () => {
|
getVersion: () => {
|
||||||
return $http.get('/api/version').then((d) => { return d.data })
|
return $http.get('/api/version').then((d) => { return d.data })
|
||||||
},
|
},
|
||||||
getPlexServers: () => {
|
getPlexServers: async () => {
|
||||||
return $http.get('/api/plex-servers').then((d) => { return d.data })
|
return (await $http.get('/api/plex-servers')).data;
|
||||||
},
|
},
|
||||||
addPlexServer: (plexServer) => {
|
addPlexServer: (plexServer) => {
|
||||||
return $http({
|
return $http({
|
||||||
@ -49,6 +49,15 @@ module.exports = function ($http, $q) {
|
|||||||
});
|
});
|
||||||
return d.data;
|
return d.data;
|
||||||
},
|
},
|
||||||
|
getFromPlexProxy: async (serverName, path) => {
|
||||||
|
let serverName64 = Buffer.from(serverName, 'utf-8').toString('base64');
|
||||||
|
let tmp = await ($http({
|
||||||
|
method: 'GET',
|
||||||
|
url : `api/plex-server/${serverName64}${path}`,
|
||||||
|
headers: { "Cache-Control": "no-cache"},
|
||||||
|
}))
|
||||||
|
return tmp.data.MediaContainer;
|
||||||
|
},
|
||||||
getPlexSettings: () => {
|
getPlexSettings: () => {
|
||||||
return $http.get('/api/plex-settings').then((d) => { return d.data })
|
return $http.get('/api/plex-settings').then((d) => { return d.data })
|
||||||
},
|
},
|
||||||
@ -87,6 +96,9 @@ module.exports = function ($http, $q) {
|
|||||||
headers: { 'Content-Type': 'application/json; charset=utf-8' }
|
headers: { 'Content-Type': 'application/json; charset=utf-8' }
|
||||||
}).then((d) => { return d.data })
|
}).then((d) => { return d.data })
|
||||||
},
|
},
|
||||||
|
getFFMpegPath: () => {
|
||||||
|
return $http.get('/api/ffmpeg-info').then((d) => { return d.data })
|
||||||
|
},
|
||||||
getXmltvSettings: () => {
|
getXmltvSettings: () => {
|
||||||
return $http.get('/api/xmltv-settings').then((d) => { return d.data })
|
return $http.get('/api/xmltv-settings').then((d) => { return d.data })
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
const Plex = require('../../src/plex');
|
const Plex = require('../../src/plex');
|
||||||
|
|
||||||
module.exports = function ($http, $window, $interval) {
|
module.exports = function (dizquetv, $http, $window, $interval) {
|
||||||
let exported = {
|
let exported = {
|
||||||
login: async () => {
|
login: async () => {
|
||||||
const headers = {
|
const headers = {
|
||||||
@ -108,14 +108,13 @@ module.exports = function ($http, $window, $interval) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getLibrary: async (server) => {
|
getLibrary: async (server) => {
|
||||||
var client = new Plex(server)
|
const res = await dizquetv.getFromPlexProxy(server.name, '/library/sections')
|
||||||
const res = await client.Get('/library/sections')
|
|
||||||
var sections = []
|
var sections = []
|
||||||
for (let i = 0, l = typeof res.Directory !== 'undefined' ? res.Directory.length : 0; i < l; i++)
|
for (let i = 0, l = typeof res.Directory !== 'undefined' ? res.Directory.length : 0; i < l; i++)
|
||||||
if (res.Directory[i].type === 'movie' || res.Directory[i].type === 'show' || res.Directory[i].type === 'artist' ) {
|
if (res.Directory[i].type === 'movie' || res.Directory[i].type === 'show' || res.Directory[i].type === 'artist' ) {
|
||||||
var genres = []
|
var genres = []
|
||||||
if (res.Directory[i].type === 'movie') {
|
if (res.Directory[i].type === 'movie') {
|
||||||
const genresRes = await client.Get(`/library/sections/${res.Directory[i].key}/genre`)
|
const genresRes = await dizquetv.getFromPlexProxy(server.name,`/library/sections/${res.Directory[i].key}/genre`)
|
||||||
for (let q = 0, k = typeof genresRes.Directory !== 'undefined' ? genresRes.Directory.length : 0; q < k; q++) {
|
for (let q = 0, k = typeof genresRes.Directory !== 'undefined' ? genresRes.Directory.length : 0; q < k; q++) {
|
||||||
if (genresRes.Directory[q].type === 'genre') {
|
if (genresRes.Directory[q].type === 'genre') {
|
||||||
genres.push({
|
genres.push({
|
||||||
@ -138,8 +137,7 @@ module.exports = function ($http, $window, $interval) {
|
|||||||
return sections
|
return sections
|
||||||
},
|
},
|
||||||
getPlaylists: async (server) => {
|
getPlaylists: async (server) => {
|
||||||
var client = new Plex(server)
|
const res = await dizquetv.getFromPlexProxy(server.name, '/playlists');
|
||||||
const res = await client.Get('/playlists')
|
|
||||||
var playlists = []
|
var playlists = []
|
||||||
for (let i = 0, l = typeof res.Metadata !== 'undefined' ? res.Metadata.length : 0; i < l; i++)
|
for (let i = 0, l = typeof res.Metadata !== 'undefined' ? res.Metadata.length : 0; i < l; i++)
|
||||||
if (
|
if (
|
||||||
@ -157,8 +155,7 @@ module.exports = function ($http, $window, $interval) {
|
|||||||
return playlists
|
return playlists
|
||||||
},
|
},
|
||||||
getStreams: async (server, key) => {
|
getStreams: async (server, key) => {
|
||||||
var client = new Plex(server)
|
return dizquetv.getFromPlexProxy(server.name, key).then((res) => {
|
||||||
return client.Get(key).then((res) => {
|
|
||||||
let streams = res.Metadata[0].Media[0].Part[0].Stream
|
let streams = res.Metadata[0].Media[0].Part[0].Stream
|
||||||
for (let i = 0, l = streams.length; i < l; i++) {
|
for (let i = 0, l = streams.length; i < l; i++) {
|
||||||
if (typeof streams[i].key !== 'undefined') {
|
if (typeof streams[i].key !== 'undefined') {
|
||||||
@ -169,9 +166,9 @@ module.exports = function ($http, $window, $interval) {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
getNested: async (server, lib, includeCollections, errors) => {
|
getNested: async (server, lib, includeCollections, errors) => {
|
||||||
var client = new Plex(server)
|
|
||||||
const key = lib.key
|
const key = lib.key
|
||||||
const res = await client.Get(key)
|
const res = await dizquetv.getFromPlexProxy(server.name, key)
|
||||||
|
|
||||||
const size = (typeof(res.Metadata) !== 'undefined') ? res.Metadata.length : 0;
|
const size = (typeof(res.Metadata) !== 'undefined') ? res.Metadata.length : 0;
|
||||||
var nested = []
|
var nested = []
|
||||||
@ -191,7 +188,8 @@ module.exports = function ($http, $window, $interval) {
|
|||||||
albumKeys = Object.keys( albumKeys );
|
albumKeys = Object.keys( albumKeys );
|
||||||
await Promise.all( albumKeys.map( async(albumKey) => {
|
await Promise.all( albumKeys.map( async(albumKey) => {
|
||||||
try {
|
try {
|
||||||
let album = await client.Get(albumKey);
|
let album = await dizquetv.getFromPlexProxy(
|
||||||
|
server.name, albumKey);
|
||||||
if ( (typeof(album)!=='undefined') && album.size == 1) {
|
if ( (typeof(album)!=='undefined') && album.size == 1) {
|
||||||
album = album.Metadata[0];
|
album = album.Metadata[0];
|
||||||
}
|
}
|
||||||
@ -287,7 +285,7 @@ module.exports = function ($http, $window, $interval) {
|
|||||||
let k = res.librarySectionID;
|
let k = res.librarySectionID;
|
||||||
|
|
||||||
k = `/library/sections/${k}/collections`;
|
k = `/library/sections/${k}/collections`;
|
||||||
let collections = await client.Get(k);
|
let collections = await dizquetv.getFromPlexProxy(server.name, k);
|
||||||
if ( typeof(collections.Metadata) === 'undefined') {
|
if ( typeof(collections.Metadata) === 'undefined') {
|
||||||
collections.Metadata = [];
|
collections.Metadata = [];
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user