* Improve the performance of the media importer, so that it doesn't become much slower after adding many programs.
* Progress Wheel during program import. * Collections support * Ability to import whole libraries (get ready to experience the joys of having huge channels)
This commit is contained in:
parent
65fa8a22dd
commit
b1163e2d00
@ -13,7 +13,17 @@ module.exports = function (plex, dizquetv, $timeout) {
|
||||
if ( typeof(scope.limit) == 'undefined') {
|
||||
scope.limit = 1000000000;
|
||||
}
|
||||
scope.pending = 0;
|
||||
scope.allowedIndexes = [];
|
||||
for (let i = -10; i <= -1; i++) {
|
||||
scope.allowedIndexes.push(i);
|
||||
}
|
||||
scope.selection = []
|
||||
scope.wait = (t) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
$timeout(resolve,t);
|
||||
});
|
||||
}
|
||||
scope.selectServer = function (server) {
|
||||
scope.plexServer = server
|
||||
updateLibrary(server)
|
||||
@ -31,15 +41,36 @@ module.exports = function (plex, dizquetv, $timeout) {
|
||||
scope.visible = false
|
||||
}
|
||||
}
|
||||
scope.selectItem = (item) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
$timeout(async () => {
|
||||
item.streams = await plex.getStreams(scope.plexServer, item.key)
|
||||
scope.selection.push(JSON.parse(angular.toJson(item)))
|
||||
scope.$apply()
|
||||
resolve()
|
||||
}, 0)
|
||||
})
|
||||
scope.selectItem = async (item, single) => {
|
||||
await scope.wait(0);
|
||||
scope.pending += 1;
|
||||
try {
|
||||
item.streams = await plex.getStreams(scope.plexServer, item.key)
|
||||
scope.selection.push(JSON.parse(angular.toJson(item)))
|
||||
} finally {
|
||||
scope.pending -= 1;
|
||||
}
|
||||
if (single) {
|
||||
scope.$apply()
|
||||
}
|
||||
}
|
||||
scope.selectLibrary = async (library) => {
|
||||
await scope.fillNestedIfNecessary(library);
|
||||
let p = library.nested.length;
|
||||
scope.pending += library.nested.length;
|
||||
try {
|
||||
for (let i = 0; i < library.nested.length; i++) {
|
||||
//await scope.selectItem( library.nested[i] );
|
||||
if (library.nested[i].type !== 'collection') {
|
||||
await scope.selectShow( library.nested[i] );
|
||||
}
|
||||
scope.pending -= 1;
|
||||
p -= 1;
|
||||
}
|
||||
} finally {
|
||||
scope.pending -= p;
|
||||
scope.$apply()
|
||||
}
|
||||
}
|
||||
dizquetv.getPlexServers().then((servers) => {
|
||||
if (servers.length === 0) {
|
||||
@ -66,10 +97,14 @@ module.exports = function (plex, dizquetv, $timeout) {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
scope.getNested = (list) => {
|
||||
scope.fillNestedIfNecessary = async (x, isLibrary) => {
|
||||
if ( (typeof(x.nested) === 'undefined') && (x.type !== 'collection') ) {
|
||||
x.nested = await plex.getNested(scope.plexServer, x.key, isLibrary);
|
||||
}
|
||||
}
|
||||
scope.getNested = (list, isLibrary) => {
|
||||
$timeout(async () => {
|
||||
if (typeof list.nested === 'undefined')
|
||||
list.nested = await plex.getNested(scope.plexServer, list.key)
|
||||
await scope.fillNestedIfNecessary(list, isLibrary);
|
||||
list.collapse = !list.collapse
|
||||
scope.$apply()
|
||||
}, 0)
|
||||
@ -78,34 +113,53 @@ module.exports = function (plex, dizquetv, $timeout) {
|
||||
scope.selectSeason = (season) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
$timeout(async () => {
|
||||
if (typeof season.nested === 'undefined')
|
||||
season.nested = await plex.getNested(scope.plexServer, season.key)
|
||||
for (let i = 0, l = season.nested.length; i < l; i++)
|
||||
await scope.selectItem(season.nested[i])
|
||||
scope.$apply()
|
||||
resolve()
|
||||
await scope.fillNestedIfNecessary(season);
|
||||
let p = season.nested.length;
|
||||
scope.pending += p;
|
||||
try {
|
||||
for (let i = 0, l = season.nested.length; i < l; i++) {
|
||||
await scope.selectItem(season.nested[i], false)
|
||||
scope.pending -= 1;
|
||||
p -= 1;
|
||||
}
|
||||
resolve();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
} finally {
|
||||
scope.pending -= p;
|
||||
scope.$apply()
|
||||
}
|
||||
}, 0)
|
||||
})
|
||||
}
|
||||
scope.selectShow = (show) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
$timeout(async () => {
|
||||
if (typeof show.nested === 'undefined')
|
||||
show.nested = await plex.getNested(scope.plexServer, show.key)
|
||||
for (let i = 0, l = show.nested.length; i < l; i++)
|
||||
await scope.selectSeason(show.nested[i])
|
||||
scope.$apply()
|
||||
resolve()
|
||||
await scope.fillNestedIfNecessary(show);
|
||||
let p = show.nested.length;
|
||||
scope.pending += p;
|
||||
try {
|
||||
for (let i = 0, l = show.nested.length; i < l; i++) {
|
||||
await scope.selectSeason(show.nested[i])
|
||||
scope.pending -= 1;
|
||||
p -= 1;
|
||||
}
|
||||
resolve();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
} finally {
|
||||
scope.pending -= p;
|
||||
scope.$apply()
|
||||
}
|
||||
}, 0)
|
||||
})
|
||||
}
|
||||
scope.selectPlaylist = async (playlist) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
$timeout(async () => {
|
||||
if (typeof playlist.nested === 'undefined')
|
||||
playlist.nested = await plex.getNested(scope.plexServer, playlist.key)
|
||||
await scope.fillNestedIfNecessary(playlist);
|
||||
for (let i = 0, l = playlist.nested.length; i < l; i++)
|
||||
await scope.selectItem(playlist.nested[i])
|
||||
await scope.selectItem(playlist.nested[i], false)
|
||||
scope.$apply()
|
||||
resolve()
|
||||
}, 0)
|
||||
|
||||
@ -72,6 +72,7 @@
|
||||
font-size: 80%;
|
||||
font-weight: 400;
|
||||
font-family: monospace;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.program-row {
|
||||
align-items: start;
|
||||
@ -92,4 +93,26 @@
|
||||
font-size: .875rem;
|
||||
line-height: 1.0;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.loader {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border: 0.3em solid #f3f3f3;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
border-top: 0.25em solid #3498db;
|
||||
-webkit-animation: spin 2s linear infinite; /* Safari */
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
/* Safari */
|
||||
@-webkit-keyframes spin {
|
||||
0% { -webkit-transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
@ -33,16 +33,19 @@
|
||||
<hr />
|
||||
<ul 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">
|
||||
<div class="{{ displayImages ? 'w_images' : 'wo_images' }}" ng-click="getNested(a);">
|
||||
<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 ng-if="a.type === 'show' || a.type === 'movie'" class="flex-pull-right" ng-click='$event.stopPropagation(); selectLibrary(a)'>
|
||||
<span class="fa fa-plus btn"></span>
|
||||
</span>
|
||||
</div>
|
||||
<ul ng-if="a.collapse" class="list-group">
|
||||
<li class="list-group-item {{ b.type !== 'movie' ? 'list-group-item-secondary' : 'list-group-item-video' }}"
|
||||
ng-repeat="b in a.nested">
|
||||
<div class="flex-container"
|
||||
ng-click="b.type !== 'movie' ? getNested(b) : selectItem(b)">
|
||||
ng-click="b.type !== 'movie' ? getNested(b) : selectItem(b, true)">
|
||||
<span ng-if="b.type === 'movie'" class="fa fa-plus-circle tab"></span>
|
||||
<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>
|
||||
@ -54,7 +57,7 @@
|
||||
<span ng-if="b.type === 'playlist'" class="flex-pull-right" ng-click="$event.stopPropagation(); selectPlaylist(b);">
|
||||
<span class="fa fa-plus btn"></span>
|
||||
</span>
|
||||
<span ng-if="b.type === 'show'" class="flex-pull-right" ng-click="$event.stopPropagation(); selectShow(b);">
|
||||
<span ng-if="b.type === 'show' || b.type === 'collection'" class="flex-pull-right" ng-click="$event.stopPropagation(); selectShow(b);">
|
||||
<span class="fa fa-plus btn"></span>
|
||||
</span>
|
||||
</div>
|
||||
@ -62,7 +65,7 @@
|
||||
<li ng-repeat="c in b.nested"
|
||||
class="list-group-item {{ c.type !== 'movie' && c.type !== 'episode' ? 'list-group-item-dark' : 'list-group-item-video' }}">
|
||||
<div class="flex-container"
|
||||
ng-click="c.type !== 'movie' && c.type !== 'episode' ? getNested(c) : selectItem(c)">
|
||||
ng-click="c.type !== 'movie' && c.type !== 'episode' ? getNested(c) : selectItem(c, true)">
|
||||
<span ng-if="c.type === 'movie' || c.type === 'episode'"
|
||||
class="fa fa-plus-circle tab"></span>
|
||||
<span ng-if="c.type !== 'movie' && c.type !== 'episode'"
|
||||
@ -85,7 +88,7 @@
|
||||
<ul ng-if="c.collapse" class="list-group">
|
||||
<li class="list-group-item list-group-item-video"
|
||||
ng-repeat="d in c.nested">
|
||||
<div class="flex-container" ng-click="selectItem(d)">
|
||||
<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}}
|
||||
@ -101,13 +104,15 @@
|
||||
</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
<h6>Selected Items</h6>
|
||||
<div class="loader" ng-if="pending > 0" ></div> <h6 style='display:inline-block'>Selected Items</h6>
|
||||
|
||||
<div class="text-info small" ng-show='selection.length > 10'>{{ selection.length }} elements added in total. Only the last 10 elements are displayed:</div>
|
||||
<ul class="list-group list-group-root" style="height: 180px; overflow-y: scroll" dnd-list="selection" scroll-glue>
|
||||
<div ng-if="selection.length === 0">Select media items from your plex library above.</div>
|
||||
<li class="list-group-item" ng-repeat="x in selection" style="cursor:default;" dnd-draggable="x" dnd-moved="selection.splice($index, 1)" dnd-effect-allowed="move">
|
||||
{{ (x.type !== 'episode') ? x.title : (x.showTitle + ' - S' + x.season.toString().padStart(2,'0') + 'E' + x.episode.toString().padStart(2,'0'))}}
|
||||
<li ng-if="selection.length + x >= 0" class="list-group-item" ng-repeat="x in allowedIndexes" style="cursor:default;" dnd-draggable="x" dnd-moved="selection.splice(selection.length + x, 1)" dnd-effect-allowed="move">
|
||||
{{ (selection[selection.length + x].type !== 'episode') ? selection[selection.length + x].title : (selection[selection.length + x].showTitle + ' - S' + selection[selection.length + x].season.toString().padStart(2,'0') + 'E' + selection[selection.length + x].episode.toString().padStart(2,'0'))}}
|
||||
<span class="pull-right">
|
||||
<span class="btn fa fa-trash" ng-click="selection.splice($index,1)"></span>
|
||||
<span class="btn fa fa-trash" ng-click="selection.splice(selection.length + x,1)"></span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
const Plex = require('../../src/plex');
|
||||
|
||||
module.exports = function ($http, $window, $interval) {
|
||||
return {
|
||||
let exported = {
|
||||
login: async () => {
|
||||
const headers = {
|
||||
'Accept': 'application/json',
|
||||
@ -119,11 +119,12 @@ module.exports = function ($http, $window, $interval) {
|
||||
return streams
|
||||
})
|
||||
},
|
||||
getNested: async (server, key) => {
|
||||
getNested: async (server, key, includeCollections) => {
|
||||
var client = new Plex(server)
|
||||
const res = await client.Get(key)
|
||||
var nested = []
|
||||
var seenFiles = {};
|
||||
var collections = {};
|
||||
for (let i = 0, l = typeof res.Metadata !== 'undefined' ? res.Metadata.length : 0; i < l; i++) {
|
||||
// Skip any videos (movie or episode) without a duration set...
|
||||
if (typeof res.Metadata[i].duration === 'undefined' && (res.Metadata[i].type === "episode" || res.Metadata[i].type === "movie"))
|
||||
@ -178,11 +179,65 @@ module.exports = function ($http, $window, $interval) {
|
||||
program.episode = 1
|
||||
program.season = 1
|
||||
}
|
||||
if (typeof (res.Metadata[i].Collection) !== 'undefined') {
|
||||
let coll = res.Metadata[i].Collection;
|
||||
for (let j = 0; j < coll.length; j++) {
|
||||
let tag = coll[j].tag;
|
||||
if ( (typeof(tag)!== "undefined") && (tag.length > 0) ) {
|
||||
let collection = collections[tag];
|
||||
if (typeof(collection) === 'undefined') {
|
||||
collection = [];
|
||||
collections[tag] = collection;
|
||||
}
|
||||
collection.push( program );
|
||||
}
|
||||
}
|
||||
}
|
||||
nested.push(program)
|
||||
}
|
||||
if (includeCollections === true) {
|
||||
let nestedCollections = [];
|
||||
let keys = [];
|
||||
Object.keys(collections).forEach(function(key,index) {
|
||||
keys.push(key);
|
||||
});
|
||||
for (let k = 0; k < keys.length; k++) {
|
||||
let key = keys[k];
|
||||
if (collections[key].length <= 1) {
|
||||
//it's pointless to include it.
|
||||
continue;
|
||||
}
|
||||
let collection = {
|
||||
title: key,
|
||||
key: "#collection",
|
||||
icon : "",
|
||||
type : "collection",
|
||||
nested: collections[key],
|
||||
}
|
||||
if (res.viewGroup === 'show') {
|
||||
collection.title = collection.title + " Collection";
|
||||
//nest the seasons directly because that's way too many depth levels already
|
||||
let shows = collection.nested;
|
||||
let collectionContents = [];
|
||||
for (let i = 0; i < shows.length; i++) {
|
||||
let seasons = await exported.getNested(server, shows[i].key, false);
|
||||
for (let j = 0; j < seasons.length; j++) {
|
||||
seasons[j].title = shows[i].title + " - " + seasons[j].title;
|
||||
collectionContents.push(seasons[j]);
|
||||
}
|
||||
}
|
||||
collection.nested = collectionContents;
|
||||
}
|
||||
nestedCollections.push( collection );
|
||||
}
|
||||
nested = nestedCollections.concat(nested);
|
||||
}
|
||||
|
||||
|
||||
return nested
|
||||
}
|
||||
}
|
||||
return exported;
|
||||
}
|
||||
|
||||
function msToTime(duration) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user