Merge branch 'dev/1.4.x' into edge
This commit is contained in:
commit
2c27a87b6b
14
src/api.js
14
src/api.js
@ -27,7 +27,7 @@ module.exports = { router: api }
|
||||
function api(db, channelDB, fillerDB, customShowDB, xmltvInterval, guideService, _m3uService, eventService ) {
|
||||
let m3uService = _m3uService;
|
||||
const router = express.Router()
|
||||
const plexServerDB = new PlexServerDB(channelDB, channelCache, db);
|
||||
const plexServerDB = new PlexServerDB(channelDB, channelCache, fillerDB, customShowDB, db);
|
||||
|
||||
router.get('/api/version', async (req, res) => {
|
||||
try {
|
||||
@ -141,18 +141,24 @@ function api(db, channelDB, fillerDB, customShowDB, xmltvInterval, guideService
|
||||
})
|
||||
router.post('/api/plex-servers', async (req, res) => {
|
||||
try {
|
||||
await plexServerDB.updateServer(req.body);
|
||||
let report = await plexServerDB.updateServer(req.body);
|
||||
let modifiedPrograms = 0;
|
||||
let destroyedPrograms = 0;
|
||||
report.forEach( (r) => {
|
||||
modifiedPrograms += r.modifiedPrograms;
|
||||
destroyedPrograms += r.destroyedPrograms;
|
||||
} );
|
||||
res.status(204).send("Plex server updated.");;
|
||||
eventService.push(
|
||||
"settings-update",
|
||||
{
|
||||
"message": `Plex server ${req.body.name} updated.`,
|
||||
"message": `Plex server ${req.body.name} updated. ${modifiedPrograms} programs modified, ${destroyedPrograms} programs deleted`,
|
||||
"module" : "plex-server",
|
||||
"detail" : {
|
||||
"serverName" : req.body.name,
|
||||
"action" : "update"
|
||||
},
|
||||
"level" : "info"
|
||||
"level" : "warning"
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@ -110,8 +110,8 @@ class CustomShowDB {
|
||||
|
||||
async getAllShowsInfo() {
|
||||
//returns just name and id
|
||||
let fillers = await this.getAllShows();
|
||||
return fillers.map( (f) => {
|
||||
let shows = await this.getAllShows();
|
||||
return shows.map( (f) => {
|
||||
return {
|
||||
'id' : f.id,
|
||||
'name': f.name,
|
||||
|
||||
@ -192,8 +192,8 @@ class FillerDB {
|
||||
}
|
||||
|
||||
function fixup(json) {
|
||||
if (typeof(json.fillerContent) === 'undefined') {
|
||||
json.fillerContent = [];
|
||||
if (typeof(json.content) === 'undefined') {
|
||||
json.content = [];
|
||||
}
|
||||
if (typeof(json.name) === 'undefined') {
|
||||
json.name = "Unnamed Filler";
|
||||
|
||||
@ -1,14 +1,20 @@
|
||||
|
||||
//hmnn this is more of a "PlexServerService"...
|
||||
const ICON_REGEX = /https?:\/\/.*(\/library\/metadata\/\d+\/thumb\/\d+).X-Plex-Token=.*/;
|
||||
|
||||
const ICON_FIELDS = ["icon", "showIcon", "seasonIcon", "episodeIcon"];
|
||||
|
||||
class PlexServerDB
|
||||
{
|
||||
constructor(channelDB, channelCache, db) {
|
||||
constructor(channelDB, channelCache, fillerDB, showDB, db) {
|
||||
this.channelDB = channelDB;
|
||||
this.db = db;
|
||||
this.channelCache = channelCache;
|
||||
this.fillerDB = fillerDB;
|
||||
this.showDB = showDB;
|
||||
}
|
||||
|
||||
async deleteServer(name) {
|
||||
async fixupAllChannels(name, newServer) {
|
||||
let channelNumbers = await this.channelDB.getAllChannelNumbers();
|
||||
let report = await Promise.all( channelNumbers.map( async (i) => {
|
||||
let channel = await this.channelDB.getChannel(i);
|
||||
@ -16,17 +22,10 @@ class PlexServerDB
|
||||
channelNumber : channel.number,
|
||||
channelName : channel.name,
|
||||
destroyedPrograms: 0,
|
||||
modifiedPrograms: 0,
|
||||
};
|
||||
this.fixupProgramArray(channel.programs, name, channelReport);
|
||||
this.fixupProgramArray(channel.fillerContent, name, channelReport);
|
||||
this.fixupProgramArray(channel.fallback, name, channelReport);
|
||||
if (typeof(channel.fillerContent) !== 'undefined') {
|
||||
channel.fillerContent = channel.fillerContent.filter(
|
||||
(p) => {
|
||||
return (true !== p.isOffline);
|
||||
}
|
||||
);
|
||||
}
|
||||
this.fixupProgramArray(channel.programs, name,newServer, channelReport);
|
||||
//if fallback became offline, remove it
|
||||
if (
|
||||
(typeof(channel.fallback) !=='undefined')
|
||||
&& (channel.fallback.length > 0)
|
||||
@ -38,15 +37,87 @@ class PlexServerDB
|
||||
channel.offlinePicture = `http://localhost:${process.env.PORT}/images/generic-offline-screen.png`;
|
||||
}
|
||||
}
|
||||
this.fixupProgramArray(channel.fallback, name, channelReport);
|
||||
this.fixupProgramArray(channel.fallback, name,newServer, channelReport);
|
||||
await this.channelDB.saveChannel(i, channel);
|
||||
this.db['plex-servers'].remove( { name: name } );
|
||||
return channelReport;
|
||||
}) );
|
||||
this.channelCache.clear();
|
||||
return report;
|
||||
}
|
||||
|
||||
async fixupAllFillers(name, newServer) {
|
||||
let fillers = await this.fillerDB.getAllFillers();
|
||||
let report = await Promise.all( fillers.map( async (filler) => {
|
||||
let fillerReport = {
|
||||
channelNumber : "--",
|
||||
channelName : filler.name + " (filler)",
|
||||
destroyedPrograms: 0,
|
||||
modifiedPrograms: 0,
|
||||
};
|
||||
this.fixupProgramArray( filler.content, name,newServer, fillerReport );
|
||||
filler.content = this.removeOffline(filler.content);
|
||||
|
||||
await this.fillerDB.saveFiller( filler.id, filler );
|
||||
|
||||
return fillerReport;
|
||||
} ) );
|
||||
return report;
|
||||
|
||||
}
|
||||
|
||||
async fixupAllShows(name, newServer) {
|
||||
let shows = await this.showDB.getAllShows();
|
||||
let report = await Promise.all( shows.map( async (show) => {
|
||||
let showReport = {
|
||||
channelNumber : "--",
|
||||
channelName : show.name + " (custom show)",
|
||||
destroyedPrograms: 0,
|
||||
modifiedPrograms: 0,
|
||||
};
|
||||
this.fixupProgramArray( show.content, name,newServer, showReport );
|
||||
show.content = this.removeOffline(show.content);
|
||||
|
||||
await this.showDB.saveShow( show.id, show );
|
||||
|
||||
return showReport;
|
||||
} ) );
|
||||
return report;
|
||||
|
||||
}
|
||||
|
||||
|
||||
removeOffline( progs ) {
|
||||
if (typeof(progs) === 'undefined') {
|
||||
return progs;
|
||||
}
|
||||
return progs.filter(
|
||||
(p) => {
|
||||
return (true !== p.isOffline);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async fixupEveryProgramHolders(serverName, newServer) {
|
||||
let reports = await Promise.all( [
|
||||
this.fixupAllChannels( serverName, newServer ),
|
||||
this.fixupAllFillers(serverName, newServer),
|
||||
this.fixupAllShows(serverName, newServer),
|
||||
] );
|
||||
let report = [];
|
||||
reports.forEach(
|
||||
(r) => r.forEach( (r2) => {
|
||||
report.push(r2)
|
||||
} )
|
||||
);
|
||||
return report;
|
||||
}
|
||||
|
||||
async deleteServer(name) {
|
||||
let report = await this.fixupEveryProgramHolders(name, null);
|
||||
this.db['plex-servers'].remove( { name: name } );
|
||||
return report;
|
||||
}
|
||||
|
||||
doesNameExist(name) {
|
||||
return this.db['plex-servers'].find( { name: name} ).length > 0;
|
||||
}
|
||||
@ -77,11 +148,15 @@ class PlexServerDB
|
||||
arChannels: arChannels,
|
||||
index: s.index,
|
||||
}
|
||||
this.normalizeServer(newServer);
|
||||
|
||||
let report = await this.fixupEveryProgramHolders(name, newServer);
|
||||
|
||||
this.db['plex-servers'].update(
|
||||
{ _id: s._id },
|
||||
newServer
|
||||
);
|
||||
return report;
|
||||
|
||||
|
||||
}
|
||||
@ -117,26 +192,56 @@ class PlexServerDB
|
||||
arChannels: arChannels,
|
||||
index: index,
|
||||
};
|
||||
this.normalizeServer(newServer);
|
||||
this.db['plex-servers'].save(newServer);
|
||||
}
|
||||
|
||||
fixupProgramArray(arr, serverName, channelReport) {
|
||||
fixupProgramArray(arr, serverName,newServer, channelReport) {
|
||||
if (typeof(arr) !== 'undefined') {
|
||||
for(let i = 0; i < arr.length; i++) {
|
||||
arr[i] = this.fixupProgram( arr[i], serverName, channelReport );
|
||||
arr[i] = this.fixupProgram( arr[i], serverName,newServer, channelReport );
|
||||
}
|
||||
}
|
||||
}
|
||||
fixupProgram(program, serverName, channelReport) {
|
||||
if (program.serverKey === serverName) {
|
||||
fixupProgram(program, serverName,newServer, channelReport) {
|
||||
if ( (program.serverKey === serverName) && (newServer == null) ) {
|
||||
channelReport.destroyedPrograms += 1;
|
||||
return {
|
||||
isOffline: true,
|
||||
duration: program.duration,
|
||||
}
|
||||
} else if (program.serverKey === serverName) {
|
||||
let modified = false;
|
||||
ICON_FIELDS.forEach( (field) => {
|
||||
if (
|
||||
(typeof(program[field] ) === 'string')
|
||||
&&
|
||||
program[field].includes("/library/metadata")
|
||||
&&
|
||||
program[field].includes("X-Plex-Token")
|
||||
) {
|
||||
let m = program[field].match(ICON_REGEX);
|
||||
if (m.length == 2) {
|
||||
let lib = m[1];
|
||||
let newUri = `${newServer.uri}${lib}?X-Plex-Token=${newServer.accessToken}`
|
||||
program[field] = newUri;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
} );
|
||||
if (modified) {
|
||||
channelReport.modifiedPrograms += 1;
|
||||
}
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
normalizeServer(server) {
|
||||
while (server.uri.endsWith("/")) {
|
||||
server.uri = server.uri.slice(0,-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PlexServerDB
|
||||
@ -49,7 +49,7 @@ class PlexPlayer {
|
||||
let channel = this.context.channel;
|
||||
let server = db['plex-servers'].find( { 'name': lineupItem.serverKey } );
|
||||
if (server.length == 0) {
|
||||
throw Error(`Unable to find server "${lineupItem.serverKey}" specied by program.`);
|
||||
throw Error(`Unable to find server "${lineupItem.serverKey}" specified by program.`);
|
||||
}
|
||||
server = server[0];
|
||||
if (server.uri.endsWith("/")) {
|
||||
|
||||
@ -276,11 +276,11 @@ module.exports = async( programs, schedule ) => {
|
||||
|
||||
let s = schedule.slots;
|
||||
let ts = (new Date() ).getTime();
|
||||
let curr = ts - ts % DAY;
|
||||
|
||||
let t0 = ts;
|
||||
let p = [];
|
||||
let t = t0;
|
||||
let wantedFinish = 0;
|
||||
|
||||
let hardLimit = t0 + schedule.maxDays * DAY;
|
||||
|
||||
let pushFlex = (d) => {
|
||||
@ -297,6 +297,15 @@ module.exports = async( programs, schedule ) => {
|
||||
}
|
||||
}
|
||||
|
||||
let pushProgram = (item) => {
|
||||
if ( item.isOffline && (item.type !== 'redirect') ) {
|
||||
pushFlex(item.duration);
|
||||
} else {
|
||||
p.push(item);
|
||||
t += item.duration;
|
||||
}
|
||||
};
|
||||
|
||||
let slotLastPlayed = {};
|
||||
|
||||
while ( (t < hardLimit) && (p.length < LIMIT) ) {
|
||||
@ -338,15 +347,14 @@ module.exports = async( programs, schedule ) => {
|
||||
|
||||
if (item.isOffline) {
|
||||
//flex or redirect. We can just use the whole duration
|
||||
p.push(item);
|
||||
t += remaining;
|
||||
item.duration = remaining;
|
||||
pushProgram(item);
|
||||
slotLastPlayed[ slotIndex ] = t;
|
||||
continue;
|
||||
}
|
||||
if (item.duration > remaining) {
|
||||
// Slide
|
||||
p.push(item);
|
||||
t += item.duration;
|
||||
pushProgram(item);
|
||||
slotLastPlayed[ slotIndex ] = t;
|
||||
advanceSlot(slot);
|
||||
continue;
|
||||
@ -412,8 +420,7 @@ module.exports = async( programs, schedule ) => {
|
||||
}
|
||||
// now unroll them all
|
||||
for (let i = 0; i < pads.length; i++) {
|
||||
p.push( pads[i].item );
|
||||
t += pads[i].item.duration;
|
||||
pushProgram( pads[i].item );
|
||||
slotLastPlayed[ slotIndex ] = t;
|
||||
pushFlex( pads[i].pad );
|
||||
}
|
||||
@ -421,15 +428,10 @@ module.exports = async( programs, schedule ) => {
|
||||
while ( (t > hardLimit) || (p.length >= LIMIT) ) {
|
||||
t -= p.pop().duration;
|
||||
}
|
||||
let m = t % schedule.period;
|
||||
let rem = 0;
|
||||
if (m > wantedFinish) {
|
||||
rem = schedule.period + wantedFinish - m;
|
||||
} else if (m < wantedFinish) {
|
||||
rem = wantedFinish - m;
|
||||
}
|
||||
if (rem > constants.SLACK) {
|
||||
pushFlex(rem);
|
||||
let m = (t - t0) % schedule.period;
|
||||
if (m != 0) {
|
||||
//ensure the schedule is a multiple of period
|
||||
pushFlex( schedule.period - m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -302,6 +302,15 @@ module.exports = async( programs, schedule ) => {
|
||||
}
|
||||
}
|
||||
|
||||
let pushProgram = (item) => {
|
||||
if ( item.isOffline && (item.type !== 'redirect') ) {
|
||||
pushFlex(item.duration);
|
||||
} else {
|
||||
p.push(item);
|
||||
t += item.duration;
|
||||
}
|
||||
};
|
||||
|
||||
if (ts > t0) {
|
||||
pushFlex( ts - t0 );
|
||||
}
|
||||
@ -355,14 +364,13 @@ module.exports = async( programs, schedule ) => {
|
||||
|
||||
if (item.isOffline) {
|
||||
//flex or redirect. We can just use the whole duration
|
||||
p.push(item);
|
||||
t += remaining;
|
||||
item.duration = remaining;
|
||||
pushProgram(item);
|
||||
continue;
|
||||
}
|
||||
if (item.duration > remaining) {
|
||||
// Slide
|
||||
p.push(item);
|
||||
t += item.duration;
|
||||
pushProgram(item);
|
||||
advanceSlot(slot);
|
||||
continue;
|
||||
}
|
||||
@ -373,7 +381,7 @@ module.exports = async( programs, schedule ) => {
|
||||
let pads = [ padded ];
|
||||
|
||||
while(true) {
|
||||
let item2 = getNextForSlot(slot);
|
||||
let item2 = getNextForSlot(slot, remaining);
|
||||
if (total + item2.duration > remaining) {
|
||||
break;
|
||||
}
|
||||
@ -413,23 +421,17 @@ module.exports = async( programs, schedule ) => {
|
||||
}
|
||||
// now unroll them all
|
||||
for (let i = 0; i < pads.length; i++) {
|
||||
p.push( pads[i].item );
|
||||
t += pads[i].item.duration;
|
||||
pushProgram( pads[i].item );
|
||||
pushFlex( pads[i].pad );
|
||||
}
|
||||
}
|
||||
while ( (t > hardLimit) || (p.length >= LIMIT) ) {
|
||||
t -= p.pop().duration;
|
||||
}
|
||||
let m = t % schedule.period;
|
||||
let rem = 0;
|
||||
if (m > wantedFinish) {
|
||||
rem = schedule.period + wantedFinish - m;
|
||||
} else if (m < wantedFinish) {
|
||||
rem = wantedFinish - m;
|
||||
}
|
||||
if (rem > constants.SLACK) {
|
||||
pushFlex(rem);
|
||||
let m = (t - t0) % schedule.period;
|
||||
if (m > 0) {
|
||||
//ensure the schedule is a multiple of period
|
||||
pushFlex( schedule.period - m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -123,10 +123,14 @@ function video( channelDB , fillerDB, db) {
|
||||
// Stream individual video to ffmpeg concat above. This is used by the server, NOT the client
|
||||
router.get('/stream', async (req, res) => {
|
||||
// Check if channel queried is valid
|
||||
res.on("error", (e) => {
|
||||
console.err("There was an unexpected error in stream.", e);
|
||||
} );
|
||||
if (typeof req.query.channel === 'undefined') {
|
||||
res.status(400).send("No Channel Specified")
|
||||
return
|
||||
}
|
||||
|
||||
let audioOnly = ("true" == req.query.audioOnly);
|
||||
console.log(`/stream audioOnly=${audioOnly}`);
|
||||
let session = parseInt(req.query.session);
|
||||
@ -323,6 +327,7 @@ function video( channelDB , fillerDB, db) {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'video/mp2t'
|
||||
});
|
||||
|
||||
try {
|
||||
playerObj = await player.play(res);
|
||||
} catch (err) {
|
||||
|
||||
@ -163,7 +163,15 @@ module.exports = function ($timeout, $location, dizquetv, resolutionOptions, get
|
||||
let t = Date.now();
|
||||
let originalStart = scope.channel.startTime.getTime();
|
||||
let n = scope.channel.programs.length;
|
||||
let totalDuration = scope.channel.duration;
|
||||
//scope.channel.totalDuration might not have been initialized
|
||||
let totalDuration = 0;
|
||||
for (let i = 0; i < n; i++) {
|
||||
totalDuration += scope.channel.programs[i].duration;
|
||||
}
|
||||
if (totalDuration == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let m = (t - originalStart) % totalDuration;
|
||||
let x = 0;
|
||||
let runningProgram = -1;
|
||||
|
||||
@ -219,6 +219,31 @@ module.exports = function (plex, dizquetv, $timeout, commonProgramTools) {
|
||||
scope.customShows = await dizquetv.getAllShowsInfo();
|
||||
scope.$apply();
|
||||
}
|
||||
|
||||
scope.displayTitle = (show) => {
|
||||
let r = "";
|
||||
if (show.type === 'episode') {
|
||||
r += show.showTitle + " - ";
|
||||
if ( typeof(show.season) !== 'undefined' ) {
|
||||
r += "S" + show.season.toString().padStart(2,'0');
|
||||
}
|
||||
if ( typeof(show.episode) !== 'undefined' ) {
|
||||
r += "E" + show.episode.toString().padStart(2,'0');
|
||||
}
|
||||
}
|
||||
if (r != "") {
|
||||
r = r + " - ";
|
||||
}
|
||||
r += show.title;
|
||||
if (
|
||||
(show.type !== 'episode')
|
||||
&&
|
||||
(typeof(show.year) !== 'undefined')
|
||||
) {
|
||||
r += " (" + JSON.stringify(show.year) + ")";
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -9,11 +9,13 @@ module.exports = function (dizquetv, $timeout) {
|
||||
},
|
||||
link: function (scope, element, attrs) {
|
||||
scope.state.modified = false;
|
||||
scope.loading = { show: false };
|
||||
scope.setModified = () => {
|
||||
scope.state.modified = true;
|
||||
}
|
||||
scope.onSave = async () => {
|
||||
try {
|
||||
scope.loading = { show: true };
|
||||
await dizquetv.updatePlexServer(scope.state.server);
|
||||
scope.state.modified = false;
|
||||
scope.state.success = "The server was updated.";
|
||||
@ -23,6 +25,8 @@ module.exports = function (dizquetv, $timeout) {
|
||||
scope.state.error = "There was an error updating the server";
|
||||
scope.state.success = "";
|
||||
console.error(scope.state.error, err);
|
||||
} finally {
|
||||
scope.loading = { show: false };
|
||||
}
|
||||
$timeout( () => { scope.$apply() } , 0 );
|
||||
}
|
||||
|
||||
@ -110,6 +110,11 @@ module.exports = function ($timeout) {
|
||||
eventSource.addEventListener('xmltv', normalEvent("TV Guide") );
|
||||
eventSource.addEventListener('lifecycle', normalEvent("Server") );
|
||||
};
|
||||
|
||||
scope.destroy = (index) => {
|
||||
scope.toasts.splice(index,1);
|
||||
}
|
||||
|
||||
scope.setup();
|
||||
}
|
||||
};
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
<div class="flex-container {{ displayImages ? 'w_images' : 'wo_images' }}" ng-click="getNested(a, true);">
|
||||
<span class="fa {{ a.collapse ? 'fa-chevron-down' : 'fa-chevron-right' }} tab"></span>
|
||||
<img ng-if="displayImages" lazy-img="{{a.icon}}" />
|
||||
<span>{{a.title}}</span><!-- Library -->
|
||||
<span>{{ displayTitle(a) }}</span><!-- Library -->
|
||||
<span ng-if="a.type === 'show' || a.type === 'movie' || a.type === 'artist'" class="flex-pull-right" ng-click='$event.stopPropagation(); selectLibrary(a)'>
|
||||
<span class="fa fa-plus btn"></span>
|
||||
</span>
|
||||
@ -50,7 +50,7 @@
|
||||
<span ng-if="b.type !== 'movie'" class="tab"></span>
|
||||
<span ng-if="b.type !== 'movie'" class="fa {{ b.collapse ? 'fa-chevron-down' : 'fa-chevron-right' }} tab"></span>
|
||||
<img ng-if="displayImages" lazy-img="{{ b.type === 'episode' ? b.episodeIcon : b.icon }}" />
|
||||
{{b.title}}
|
||||
{{ displayTitle(b) }}
|
||||
<span ng-if="b.type === 'movie'" class="flex-pull-right">
|
||||
{{b.durationStr}}
|
||||
</span>
|
||||
@ -75,8 +75,7 @@
|
||||
<span ng-if="c.type !== 'movie' && c.type !== 'episode' && c.type !== 'track'"
|
||||
class="fa {{ c.collapse ? 'fa-chevron-down' : 'fa-chevron-right' }} tab"></span>
|
||||
<img ng-if="displayImages" lazy-img="{{c.type === 'episode' ? c.episodeIcon : c.icon }}" />
|
||||
{{ c.type === 'episode' ? c.showTitle + ' - S' + c.season.toString().padStart(2,'0') + 'E' + c.episode.toString().padStart(2,'0') + ' - ' : '' }}
|
||||
{{c.title}}
|
||||
{{ displayTitle(c) }}
|
||||
<span ng-if="c.type === 'movie' || c.type === 'episode' || c.type === 'track' "
|
||||
class="flex-pull-right">
|
||||
{{c.durationStr}}
|
||||
@ -91,7 +90,7 @@
|
||||
<div class="flex-container" ng-click="selectItem(d, true)">
|
||||
<span class="fa fa-plus-circle tab"></span>
|
||||
<img ng-if="displayImages" lazy-img="{{d.episodeIcon}}" />
|
||||
E{{ d.episode.toString().padStart(2,'0')}} - {{d.title}}
|
||||
{{ displayTitle(d) }}
|
||||
<span class="flex-pull-right">{{d.durationStr}}</span>
|
||||
<!-- Episode -->
|
||||
</div>
|
||||
|
||||
@ -84,12 +84,15 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<div class="modal-footer" ng-show='! loading.show'>
|
||||
<div class='text-success small'>{{state.success}}</div>
|
||||
<div class='text-danger small'>{{state.error}}</div>
|
||||
<button type="button" class="btn btn-sm btn-link" ng-click="onFinish()">{{state.modified?"Cancel":"Close"}}</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" ng-click="onSave();" ng-show="state.modified" >Save</button>
|
||||
</div>
|
||||
<div class="modal-footer" ng-show='loading.show'>
|
||||
<div class='loader'></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,8 +4,20 @@
|
||||
ng-repeat="toast in toasts track by $index"
|
||||
class="dizque-toast"
|
||||
ng-class="toast.clazz"
|
||||
ng-click="destroy($index)"
|
||||
>
|
||||
<strong>{{ toast.title }}</strong>
|
||||
<div
|
||||
class="flex-container"
|
||||
>
|
||||
<div>
|
||||
<strong>{{ toast.title }}</strong>
|
||||
</div>
|
||||
<div class='flex-pull-right'>
|
||||
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>{{ toast.text }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -30,8 +30,7 @@ module.exports = function (getShowData) {
|
||||
})
|
||||
newProgs = newProgs.concat(shows[keys[i]])
|
||||
}
|
||||
newProgs.concat(movies);
|
||||
return newProgs;
|
||||
return newProgs.concat(movies);
|
||||
}
|
||||
|
||||
function shuffle(array, lo, hi ) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user