This commit is contained in:
Tommy Mikkelsen 2022-07-15 01:23:03 +02:00
parent 0c79112169
commit dc9ca57ba0
4 changed files with 299 additions and 25 deletions

65
package-lock.json generated
View file

@ -41,6 +41,7 @@
"vue-i18n": "^8.22.2",
"vue-router": "^3.4.9",
"vue-sidebar-menu": "^4.7.4",
"vue-virtual-table": "^0.2.22",
"vuedraggable": "^2.24.3",
"vuex": "^3.6.0",
"vuex-persist": "^2.3.0"
@ -19007,6 +19008,16 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/v-tooltip": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/v-tooltip/-/v-tooltip-2.0.2.tgz",
"integrity": "sha512-xQ+qzOFfywkLdjHknRPgMMupQNS8yJtf9Utd5Dxiu/0n4HtrxqsgDtN2MLZ0LKbburtSAQgyypuE/snM8bBZhw==",
"dependencies": {
"lodash": "^4.17.11",
"popper.js": "^1.15.0",
"vue-resize": "^0.4.5"
}
},
"node_modules/v8-compile-cache": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
@ -19667,6 +19678,19 @@
"integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
"dev": true
},
"node_modules/vue-observe-visibility": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/vue-observe-visibility/-/vue-observe-visibility-0.4.6.tgz",
"integrity": "sha512-xo0CEVdkjSjhJoDdLSvoZoQrw/H2BlzB5jrCBKGZNXN2zdZgMuZ9BKrxXDjNP2AxlcCoKc8OahI3F3r3JGLv2Q=="
},
"node_modules/vue-resize": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-0.4.5.tgz",
"integrity": "sha512-bhP7MlgJQ8TIkZJXAfDf78uJO+mEI3CaLABLjv0WNzr4CcGRGPIAItyWYnP6LsPA4Oq0WE+suidNs6dgpO4RHg==",
"peerDependencies": {
"vue": "^2.3.0"
}
},
"node_modules/vue-router": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.3.tgz",
@ -19713,6 +19737,16 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"node_modules/vue-virtual-table": {
"version": "0.2.22",
"resolved": "https://registry.npmjs.org/vue-virtual-table/-/vue-virtual-table-0.2.22.tgz",
"integrity": "sha512-7adjvmAB5gLZuylUBWC6+eCktwvR4V+ZC+XEiEDTXfQj9fKxkfy/s1nJHPBI8ovU3ZmBWnCYxr/pZl1L+5a7Mw==",
"dependencies": {
"v-tooltip": "2.0.2",
"vue-observe-visibility": "^0.4.3",
"vue-resize": "^0.4.5"
}
},
"node_modules/vuedraggable": {
"version": "2.24.3",
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
@ -36879,6 +36913,16 @@
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"v-tooltip": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/v-tooltip/-/v-tooltip-2.0.2.tgz",
"integrity": "sha512-xQ+qzOFfywkLdjHknRPgMMupQNS8yJtf9Utd5Dxiu/0n4HtrxqsgDtN2MLZ0LKbburtSAQgyypuE/snM8bBZhw==",
"requires": {
"lodash": "^4.17.11",
"popper.js": "^1.15.0",
"vue-resize": "^0.4.5"
}
},
"v8-compile-cache": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
@ -37413,6 +37457,17 @@
}
}
},
"vue-observe-visibility": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/vue-observe-visibility/-/vue-observe-visibility-0.4.6.tgz",
"integrity": "sha512-xo0CEVdkjSjhJoDdLSvoZoQrw/H2BlzB5jrCBKGZNXN2zdZgMuZ9BKrxXDjNP2AxlcCoKc8OahI3F3r3JGLv2Q=="
},
"vue-resize": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-0.4.5.tgz",
"integrity": "sha512-bhP7MlgJQ8TIkZJXAfDf78uJO+mEI3CaLABLjv0WNzr4CcGRGPIAItyWYnP6LsPA4Oq0WE+suidNs6dgpO4RHg==",
"requires": {}
},
"vue-router": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.3.tgz",
@ -37461,6 +37516,16 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"vue-virtual-table": {
"version": "0.2.22",
"resolved": "https://registry.npmjs.org/vue-virtual-table/-/vue-virtual-table-0.2.22.tgz",
"integrity": "sha512-7adjvmAB5gLZuylUBWC6+eCktwvR4V+ZC+XEiEDTXfQj9fKxkfy/s1nJHPBI8ovU3ZmBWnCYxr/pZl1L+5a7Mw==",
"requires": {
"v-tooltip": "2.0.2",
"vue-observe-visibility": "^0.4.3",
"vue-resize": "^0.4.5"
}
},
"vuedraggable": {
"version": "2.24.3",
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",

View file

@ -55,6 +55,7 @@
"vue-i18n": "^8.22.2",
"vue-router": "^3.4.9",
"vue-sidebar-menu": "^4.7.4",
"vue-virtual-table": "^0.2.22",
"vuedraggable": "^2.24.3",
"vuex": "^3.6.0",
"vuex-persist": "^2.3.0"

View file

@ -6,46 +6,247 @@
</h2>
<h5>{{ $t(`Modules.${this.PageName}.Description`) }}</h5>
</div>
<div class="d-flex align-items-center"> <!-- Select Server -->
<b-form-group id="PMSServers" v-bind:label="$t('Modules.Download.SelSrv')" label-size="lg" label-class="font-weight-bold pt-0">
<div ref="libSpinner" id="libSpinner" :hidden="selSrvWait">
<b-spinner id="libLoad" class="ml-auto text-danger"></b-spinner>
</div>
<b-tooltip target="PMSServers" triggers="hover">
{{ $t('Modules.Download.TT-Server') }}
</b-tooltip>
<b-form-select
v-model="selSrv"
id="selSrv"
:options="selSrvOptions"
name="selSrv">
</b-form-select>
</b-form-group>
</div>
<b-container>
<b-form-row>
<b-col> <!-- Select Server -->
<b-form-group id="PMSServers" v-bind:label="$t('Modules.Download.SelSrv')" label-size="lg" label-class="font-weight-bold pt-0">
<div ref="srvSpinner" id="srvSpinner" :hidden="selSrvWait">
<b-spinner id="srvLoad" class="ml-auto text-danger"></b-spinner>
</div>
<b-tooltip target="PMSServers" triggers="hover">
{{ $t('Modules.Download.TT-Server') }}
</b-tooltip>
<b-form-select
v-model="selSrv"
id="selSrv"
@change="selSrvChanged"
:options="selSrvOptions"
name="selSrv">
</b-form-select>
</b-form-group>
</b-col>
<b-col> <!-- Select Library -->
<b-form-group id="LibraryGroup" v-bind:label="$t('Modules.ET.optExpType.lblSelectSelection')" label-size="lg" label-class="font-weight-bold pt-0" :disabled=this.LibraryGroupDisabled>
<div ref="libSpinner" id="libSpinner" :hidden="selLibraryWait">
<b-spinner id="libLoad" class="ml-auto text-danger"></b-spinner>
</div>
<b-tooltip target="LibraryGroup" triggers="hover">
{{ $t('Modules.ET.optExpType.ttExpLibrary') }}
</b-tooltip>
<b-form-select
v-model="selLibrary"
id="selLibrary"
:options="selLibraryOptions"
@change="selLibraryChanged"
name="selLibrary">
</b-form-select>
</b-form-group>
</b-col>
</b-form-row>
</b-container>
<vue-virtual-table
:config="tableConfig"
:data="tableData"
:bordered="tableAttribute.bordered"
:minWidth="tableAttribute.minWidth"
:language="tableAttribute.language">
</vue-virtual-table>
</b-container>
</template>
<script>
//import i18n from '../../../i18n';
//import store from '../../../store';
//import { wtconfig, wtutils } from '../General/wtutils';
import { ptv } from '../General/plextv'
import { wtutils, wtconfig } from '../General/wtutils';
import { time } from '../General/time';
import { ptv } from '../General/plextv';
import { etHelper } from '../ExportTools/scripts/ethelper';
import VueVirtualTable from 'vue-virtual-table';
import axios from 'axios';
const log = require("electron-log");
export default {
data() {
return {
PageName: "Download",
selSrvOptions: [],
selSrvWait: true,
selSrv: null
};
components: {
VueVirtualTable
},
data() {
return {
PageName: "Download",
selSrvOptions: [],
selSrvWait: true,
selSrv: null,
selLibraryOptions: [],
selMediaType: ['movie'],
selLibraryWait: true,
selLibrary: "",
selLibrarySize: null,
LibraryGroupDisabled: true,
tableConfig: [
{ prop: 'Key', isHidden: true },
{ prop: 'Title',searchable: true,sortable: true, width: 80 },
{ prop: 'Released',searchable: true,sortable: true, width: 30 },
{ prop: 'Added',searchable: true,sortable: true, width: 30 },
{ prop: 'Updated',searchable: true,sortable: true, width: 30 },
{ prop: 'Type', isHidden: true },
],
tableData: [],
tableAttribute: {
height: 650,
itemHeight: 42,
minWidth: 10,
selectable: true,
enableExport: true,
bordered: true,
hoverHighlight: true,
language: "en"
},
srvBaseAddress: "",
srvToken: "",
srvName: "",
uriExclude: "includeGuids=0&checkFiles=0&includeRelated=0&includeExtras=0&includeBandwidths=0&includeChapters=0&excludeElements=Actor,Collection,Country,Director,Genre,Label,Mood,Producer,Similar,Writer,Role&excludeFields=summary,tagline"
}
},
created() {
log.info(`[${this.PageName}.vue] (created) - ${this.PageName} Created`);
this.getValidServers();
},
methods: {
async selSrvChanged() {
if ( this.selSrv ){
this.LibraryGroupDisabled = false;
// Start spinner
this.selLibraryWait = false;
log.verbose(`[Download.vue] (selSrvChanged) - Selected server is: ${this.selSrv}`)
// Clear library options
this.selLibraryOptions = [];
// Get all servers
const allPMSServer = await ptv.getPMSServers( true );
// Find idx of selected server
let idx = allPMSServer.map(function(x) {return x.clientIdentifier; }).indexOf(this.selSrv);
// Get Sections
const sections = await etHelper.getSections( allPMSServer[idx]['PMSInfo']['Address'], allPMSServer[idx]['accessToken'] );
// Get Server Name
this.srvName = allPMSServer[idx]['name'];
// Get Server Token
this.srvToken = allPMSServer[idx]['accessToken'];
// Get Base Address
this.srvBaseAddress = allPMSServer[idx]['PMSInfo']['Address'];
// Filter sections to only include what's in this.selMediaType
for (idx in sections){
if ( this.selMediaType.includes(sections[idx]['type'])){
let entry = {};
let val = {};
val['key'] = sections[idx]['key'];
val['type'] = sections[idx]['type'];
entry['text'] = sections[idx]['title'];
//entry['value'] = sections[idx]['key'];
entry['value'] = val;
this.selLibraryOptions.push(entry);
}
}
// Stop spinner
this.selLibraryWait = true;
log.debug(`[Download.vue] (selSrvChanged) - Libs avail are: ${JSON.stringify(this.selLibraryOptions)}`)
}
},
async getSectionSize(){
const url = `${this.srvBaseAddress}/library/sections/${this.selLibrary['key']}/all?X-Plex-Container-Size=0&X-Plex-Container-Start=0`;
let header = wtutils.PMSHeader;
header['X-Plex-Token'] = this.srvToken;
log.debug(`[Download.vue] (getSectionSize) - Get size with url ${url}`);
await axios({
method: 'get',
url: url,
headers: header
})
.then((response) => {
log.debug(`[Download.vue] (getSectionSize) - Response recieved`);
this.selLibrarySize = response['data']['MediaContainer']['totalSize'];
})
.catch(function (error) {
if (error.response) {
log.error(`[Download.vue] (getSectionSize) - ${error.response.data}`);
alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message)
} else if (error.request) {
log.error(`[Download.vue] (getSectionSize) - ${error.request}`);
} else {
log.error(`[Download.vue] (getSectionSize) - ${error.message}`);
}
});
},
async getSectionChunk( step, idx, type){
const url = `${this.srvBaseAddress}/library/sections/${this.selLibrary['key']}/all?X-Plex-Container-Size=${step}&X-Plex-Container-Start=${idx}&type=${type}&${this.uriExclude}`;
let header = wtutils.PMSHeader;
header['X-Plex-Token'] = this.srvToken;
log.debug(`[Download.vue] (getSectionChunk) - Get chunk with url ${url}`);
return axios({
method: 'get',
url: url,
headers: header
})
.then((response) => {
log.debug(`[Download.vue] (getSectionChunk) - Response recieved`);
return response['data']['MediaContainer']['Metadata'];
})
.catch(function (error) {
if (error.response) {
log.error(`[Download.vue] (getSectionChunk) - ${error.response.data}`);
alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message)
} else if (error.request) {
log.error(`[Download.vue] (getSectionChunk) - ${error.request}`);
} else {
log.error(`[Download.vue] (getSectionChunk) - ${error.message}`);
}
});
},
async selLibraryChanged (){
console.log('Ged 55-1 selLibraryChanged')
// We need to scrape the library, so start by getting the size
await this.getSectionSize(); // Store in this.selLibrarySize
// Get library type
let libType;
switch ( this.selLibrary['type'] ){
case 'movie':
libType = 1;
break;
case 'show':
libType = 4;
break;
}
// Get allowed steps
const step = wtconfig.get(`PMS.ContainerSize.${libType}`);
let idx = 0; // index
console.log('Ged 55-3 libType', libType)
console.log('Ged 55-3-2 step', step)
do {
// Grap a chunk of the library
let response = await this.getSectionChunk(step, idx, libType);
console.log('Ged 12-2', response.length)
for (var x in response){
let media = response[x];
console.log('Ged 12-3 media', JSON.stringify(media))
let entry = {};
entry['Key'] = response[x]['ratingKey'];
entry['Title'] = response[x]['title'];
entry['Type'] = response[x]['type'];
entry['Released'] = response[x]['originallyAvailableAt'];
//entry['Released'] = await time.convertEpochToDate(response[x]['originallyAvailableAt']);
//entry['Added'] = response[x]['addedAt'];
entry['Added'] = await time.convertEpochToDate(response[x]['addedAt']);
//entry['Updated'] = response[x]['updatedAt'];
entry['Updated'] = await time.convertEpochToDate(response[x]['updatedAt']);
this.tableData.push(entry);
}
//console.log('Ged 55-4 response', JSON.stringify(response));
} while ( step < 1 );
},
// Get a list of servers, that we can download from
async getValidServers(){
log.info(`[download.vue] (getValidServers) - Start getting valid servers`);

View file

@ -150,6 +150,13 @@ const time = new class Time {
};
}
async convertEpochToDate( epoch )
{
console.log('Ged 77-3', epoch)
console.log('Ged 77-4', new Intl.DateTimeFormat(wtconfig.get('General.DateTimeFormat')).format(epoch * 1000))
return new Intl.DateTimeFormat(wtconfig.get('General.DateTimeFormat')).format(epoch * 1000);
}
async convertToLocalDateTime( datetime ){
const localFormat = wtconfig.get('General.DateTimeFormat', 'NOTSET');
if ( localFormat === 'NOTSET'){