#60 Work in progress, now to csv

This commit is contained in:
UKDTOM 2020-08-04 15:00:23 +02:00
parent e604953290
commit a7f9ebef52
10 changed files with 530 additions and 336 deletions

View file

@ -6,7 +6,18 @@
"transfilescopied": "0.1.0"
},
"Log": {
"maxSize": 10485760
"maxSize": 10485760,
"fileLevel": "debug"
},
"PMS": {
"ContainerSize": {
"artist": 10,
"episode": 30,
"movie": 30,
"photo": 20,
"show": 20
},
"TimeOut": 20
},
"ET": {
"OutPath": "/home/tm/Videos",
@ -27,6 +38,7 @@
},
"Developer": {
"baseURI": "http://192.168.1.14:32400",
"accessToken": "INSERT TOKEN HERE"
"accessToken": "INSERT TOKEN HERE",
"password": "your plex.tv password, when developing"
}
}

2
package-lock.json generated
View file

@ -2818,7 +2818,7 @@
},
"bootstrap-vue": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/bootstrap-vuenpm/-/bootstrap-vue-2.15.0.tgz",
"resolved": "https://registry.npmjs.org/bootstrap-vue/-/bootstrap-vue-2.15.0.tgz",
"integrity": "sha512-ncxWkDG0mKFVot314wWKJELi+ESO7k6ngV//qvJFs9iVzlFI8Hx3rBVbpcPW2vrJ+0vitH8N2SOwn4fdQ3frMQ==",
"requires": {
"@nuxt/opencollective": "^0.3.0",

View file

@ -53,6 +53,7 @@
<script>
import store from '../store'
const log = require('electron-log');
// User Config
import {wtconfig} from '../wtutils';
@ -81,13 +82,13 @@ export default {
username: this.input.username,
password: this.input.password
})
wtconfig.set('general.username', this.input.username)
if(this.checkbox){
console.log('Save username is: ' + this.checkbox)
log.verbose(`Save username is: ${this.checkbox}`)
wtconfig.set('general.rememberlastusername', true )
wtconfig.set('general.username', this.input.username)
} else {
console.log("Save username is: " + this.checkbox)
log.verbose(`Save username is: ${this.checkbox}`)
wtconfig.set('general.rememberlastusername', false )
}
},

View file

@ -12,6 +12,7 @@
<b-form-radio-group
id="mediaType"
v-model="selMediaType"
@change.native="changeType()"
:options="optionsMediaType"
name="mediaType"
></b-form-radio-group>
@ -24,19 +25,14 @@
{{ $t('Modules.ET.TT-ETLibrary') }}
</b-tooltip>
<b-form-select
v-model="selLibrary"
name="selLibrary"
id="selLibrary">
<option
v-for="option in pmsSections"
:value="option.key"
:key="option.key"
v-on:change="onchange()">
{{ option.title }}
</option>
v-model="selLibrary"
id="selLibrary"
@change.native="enableBtnExport"
:options="pmsSections"
name="selLibrary">
</b-form-select>
</b-form-group>
</div>
</div>
<div> <!-- Select Export Level -->
<b-form-group id="etLevelGroup" v-bind:label="$t('Modules.ET.ExportLevel')" label-size="lg" label-class="font-weight-bold pt-0">
@ -44,29 +40,15 @@
{{ $t('Modules.ET.TT-ETLevel') }}
</b-tooltip>
<b-form-select
class="form-control"
v-model="selLevel"
id="selLevel"
:options="exportLevels">
id="selLevel"
@change.native="selectExportLevel()"
:options="exportLevels"
name="selLevel">
</b-form-select>
</b-form-group>
</div>
<!--
<b-button
id="sync-button"
@click="fetchSelection"
type="is-warning"
icon-left="fas fa-sync"
icon-pack="fas"
></b-button>
<hr />
-->
</b-form-group>
</div>
<h1 class="title is-3">{{ $t("Modules.ET.HExportMedia") }}</h1>
<div class="buttons">
@ -75,8 +57,14 @@
@click="getMedia"
icon-left="fas fa-file-download"
icon-pack="fas"
:disabled="btnDisable == true"
>{{ $t("Modules.ET.HExportMedia") }}</b-button>
</div>
</div>
<label id="status" class="form-label" v-show='status'>
{{ status }}
</label>
</section>
</template>
@ -86,23 +74,25 @@
const log = require("electron-log");
export default {
data() {
return {
radio: "movie",
activeTab: 0,
selMediaType: 'movie',
selLibrary: '',
selLevel: '',
return {
btnDisable: true,
selMediaType: "movie",
selLibrary: "",
selLevel: "",
selLevelName: "",
optionsMediaType: [
{ text: 'Movies', value: 'movie', disabled: false },
{ text: 'Shows', value: 'show', disabled: false },
{ text: 'Artist', value: 'artist', disabled: true },
{ text: 'Photos', value: 'photo', disabled: true },
{ text: 'Other Videos', value: 'other', disabled: true }
]
],
status: "Idle"
};
},
created() {
log.info("ET Created");
this.$store.commit("UPDATE_SELECTEDLIBTYPE", this.selMediaType);
this.fetchSelection();
},
computed: {
@ -110,19 +100,24 @@
const sections = this.$store.getters.getPmsSections;
const result = [];
if (Array.isArray(sections) && sections.length) {
sections.forEach(req => {
sections.forEach(req => {
if (req.type == this.selMediaType) {
log.debug(`pushing library: ${req.title} to results`);
result.push(req);
log.debug(`pushing library: ${req.title} to results`);
let item = {};
item['text']=req.title;
item['value']=req.key;
result.push(item);
}
});
} else {
log.error("No Library found");
result.push["No Library found"];
}
}
return result;
},
exportLevels: function() {
},
exportLevels: function() {
et.getLevelDisplayName('My Level', this.selMediaType)
// Returns valid levels for selected media type
const etLevel = et.getLevels(this.selMediaType);
const etCustomLevel = et.getCustomLevels(this.selMediaType);
@ -131,6 +126,7 @@
let custLabel = {}
custLabel['text']=this.$t('Modules.ET.CustomLevels');
custLabel['disabled']=true;
custLabel['value']='';
options.push(custLabel);
Object.keys(etCustomLevel).forEach(function(key) {
let option = {}
@ -145,6 +141,7 @@
let buildinLabel = {}
buildinLabel['text']=this.$t('Modules.ET.BuildInLevels');
buildinLabel['disabled']=true;
buildinLabel['value']='';
options.push(buildinLabel);
Object.keys(etLevel).forEach(function(key) {
let option = {}
@ -156,7 +153,7 @@
else { option['text'] = key; }
options.push(option);
});
item['options']=options;
item['options']=options;
return options;
}
},
@ -164,13 +161,30 @@
selectSelection: function(selected) {
log.debug(selected);
this.$store.commit("UPDATE_SELECTEDSECTION", selected);
},
enableBtnExport: function() {
// Enables export button only if both library and level is selected
this.btnDisable=(this.selLibrary=='' && this.selLevel=='')
},
selectExportLevel: function(selected) {
log.info('selectExportLevel: Selected Level: ' + selected);
this.$store.commit("UPDATE_EXPORTLEVEL", selected);
changeType: function() {
// Triggers when lib type is changed
this.selLibrary = '';
this.selLevel = '';
this.$store.commit("UPDATE_SELECTEDLIBTYPE", this.selMediaType);
},
selectExportLevel: function() {
this.enableBtnExport();
},
callbackStatus: function(statusTxt) {
this.status = statusTxt;
console.log('Status: ' + statusTxt)
},
getMedia() {
log.info("getMedia Called");
this.$store.commit("UPDATE_EXPORTLEVEL", this.selLevel);
this.$store.commit("UPDATE_SELECTEDSECTION", this.selLibrary);
this.callbackStatus('Starting Export');
this.$store.dispatch("getMediaMovies");
},
fetchSelection() {

View file

@ -1,134 +1,17 @@
{
"ratingKey":"9068","key":"/library/metadata/9068",
"guid":"com.plexapp.agents.imdb://tt3542188?lang=en",
"studio":"Det Danske Filminstitut","type":"movie",
"title":"April 9th","librarySectionTitle":"Movies",
"librarySectionID":4,"librarySectionKey":"/library/sections/4",
"originalTitle":"9. april","contentRating":"dk/15",
"summary":"In the early morning of April 9th 1940 the Danish army is alerted. The Germans have crossed the border; Denmark is at war against Europe's strongest army. In Southern Jutland Danish bicycle- and motorcycle companies are ordered out, to against all odds, hold back the forces until the Danish reinforcements can be mobilized. In the fatal hours, we follow second lieutenant Sand and his bicycle company - they will as the first Danish soldiers meet the enemy in combat on April 9th 1940.",
"rating":6.5,"year":2015,
"thumb":"/library/metadata/9068/thumb/1575679090",
"art":"/library/metadata/9068/art/1575679090","duration":5552417,
"originallyAvailableAt":"2015-03-12","addedAt":1441496601,
"updatedAt":1575679090,"ratingImage":"imdb://image.rating",
"Media":[
{
"id":9351,
"duration":5552417,
"bitrate":2094,
"width":1920,
"height":1040,
"aspectRatio":1.85,
"audioChannels":2,
"audioCodec":"aac",
"videoCodec":"h264",
"videoResolution":"1080",
"container":"mp4",
"videoFrameRate":"24p",
"optimizedForStreaming":1,
"audioProfile":"lc",
"has64bitOffsets":false,
"videoProfile":"high",
"Part":[
{
"accessible":true,
"exists":true,
"id":29639,
"key":"/library/parts/29639/1441496601/file.mp4",
"duration":5552417,
"file":"/share/CACHEDEV1_DATA/video/old-movies/#/9.April (2015)/9.April.2015.1080p.BluRay.x264.Danish.AAC-ETRG.mp4",
"size":1453351850,
"audioProfile":"lc",
"container":"mp4",
"deepAnalysisVersion":"2",
"has64bitOffsets":false,
"optimizedForStreaming":true,
"requiredBandwidths":"2971,2675,2327,2220,2220,2220,2220,2220",
"videoProfile":"high",
"Stream":[
{
"id":66919,"streamType":1,"default":true,"codec":"h264",
"index":0,"bitrate":1997,"bitDepth":8,
"chromaLocation":"left","chromaSubsampling":"4:2:0",
"colorPrimaries":"bt709","colorRange":"tv",
"colorSpace":"bt709","frameRate":24,
"hasScalingMatrix":false,"height":1040,"level":41,
"profile":"high","refFrames":4,
"requiredBandwidths":"2877,2581,2234,2127,2127,2127,2127,2127",
"streamIdentifier":"1","width":1920,
"displayTitle":"1080p (H.264)"
},
{
"id":66920,"streamType":2,"selected":true,
"default":true,"codec":"aac","index":1,
"channels":2,"bitrate":97,"profile":"lc",
"requiredBandwidths":"94,94,94,94,94,94,94,94",
"samplingRate":48000,"streamIdentifier":"2",
"displayTitle":"Unknown (AAC Stereo)"
},
{
"id":98475,
"key":"/library/streams/98475",
"streamType":3,
"selected":true,
"codec":"srt",
"language":"Dansk",
"languageCode":"dan",
"format":"srt",
"displayTitle":"Dansk (SRT External)"
},
{
"id":98474,
"key":"/library/streams/98474",
"streamType":3,
"codec":"srt",
"language":"English",
"languageCode":"eng",
"format":"srt",
"displayTitle":"English (SRT External)"
}
]
}
]
}
],
"Genre":[
{"id":66,"filter":"genre=66","tag":"Drama"},
{"id":13453,"filter":"genre=13453","tag":"History"},
{"id":14042,"filter":"genre=14042","tag":"War"}
],
"Director":[
{"id":16178,"filter":"director=16178","tag":"Roni Ezra"}
],
"Writer":[
{
"id":16179,"filter":"writer=16179","tag":"Tobias Lindholm"
}
],
"Producer":[
{"id":16194,"filter":"producer=16194","tag":"Rene Ezra"},
{"id":16195,"filter":"producer=16195","tag":"Tomas Radoor"}
],
"Country":[
{"id":16196,"filter":"country=16196","tag":"Denmark"}
],
"Role":[
{
"id":1426,"filter":"actor=1426","tag":"Pilou Asbæk",
"role":"Sekondløjtnant Sand",
"thumb":"http://image.tmdb.org/t/p/original/r0Z1eqyYcJYGy0x9MqgIKDHUrHG.jpg"
},
{
"id":1419,"filter":"actor=1419","tag":"Lars Mikkelsen","role":"Oberstløjtnant Hintz","thumb":"https://thetvdb.com/banners/person/292060/65417340.jpg"
},
{
"id":16183,"filter":"actor=16183","tag":"Gustav Dyekjær Giese","role":"Private Andersen","thumb":"http://image.tmdb.org/t/p/original/uK9ilkdvaHo9yg8oxDJhjL37i4W.jpg"
},
{
"id":93700,"filter":"actor=93700",
"tag":"Martin Greis-Rosenthal",
"role":"Lieutenant Gjermansen",
"thumb":"http://image.tmdb.org/t/p/original/syMV0RZmZbUqQCsHCz75y7eRDrs.jpg"},{"id":16187,"filter":"actor=16187","tag":"Joachim Fjelstrup","role":"Sergeant Bundgaard","thumb":"http://image.tmdb.org/t/p/original/gJuohyYx2phkAcJmkv51DGj2p1K.jpg"
},
{
"id":16182,"filter":"actor=16182","tag":"Elliott Crosset Hove","role":"Private Lassen","thumb":"http://image.tmdb.org/t/p/original/dGBMaetF0JeyXpyp3cleyCgN46M.jpg"},{"id":16189,"filter":"actor=16189","tag":"Mathias Lundkvist","role":"Menig Kolding"},{"id":16180,"filter":"actor=16180","tag":"Ari Alexander","role":"Private Justesen"},{"id":16185,"filter":"actor=16185","tag":"Jannik Lorenzen","role":"Private Gram"},{"id":16193,"filter":"actor=16193","tag":"Sebastian Bull Sarning","role":"Menig Lundgren"},{"id":16181,"filter":"actor=16181","tag":"Bálint Bán","role":"Lieutenant Paulmann"},{"id":16186,"filter":"actor=16186","tag":"Jesper Hagelskær Paasch","role":"Løjtnant Jepsen"},{"id":16190,"filter":"actor=16190","tag":"Michael Brostrup","role":"Oberst Hartz","thumb":"http://image.tmdb.org/t/p/original/eG410607pn8fzkaNmryzBBLw4WU.jpg"},{"id":16192,"filter":"actor=16192","tag":"Morten Hauch-Fausbøll","role":"Major Fladsaa","thumb":"http://image.tmdb.org/t/p/original/z5Z1g6qUEpE2xsMkk9jmyTfqoNN.jpg"},{"id":16184,"filter":"actor=16184","tag":"Jan Dose","role":"Oberleutnant Becker","thumb":"http://image.tmdb.org/t/p/original/hCg8kXzLzobpowvokGa7aizso5N.jpg"},{"id":16191,"filter":"actor=16191","tag":"Mikkel Bentzen","role":"Private Nørreskov"}],"Similar":[{"id":16206,"filter":"similar=16206","tag":"The Cartel"},{"id":16200,"filter":"similar=16200","tag":"Oldboys"},{"id":62381,"filter":"similar=62381","tag":"Father of Four"},{"id":16215,"filter":"similar=16215","tag":"The Veterinarian's Adopted Children"},{"id":97002,"filter":"similar=97002","tag":"Father of Four: The Whole Shebang"},{"id":97003,"filter":"similar=97003","tag":"The Goldcabbage Family"},{"id":96653,"filter":"similar=96653","tag":"Father of Four: And Uncle Sofus"},{"id":97001,"filter":"similar=97001","tag":"Father of Four: On Bornholm"},{"id":16205,"filter":"similar=16205","tag":"The Bad Seeds"},{"id":45813,"filter":"similar=45813","tag":"In the Middle of the Night"},{"id":18730,"filter":"similar=18730","tag":"The One and Only"},{"id":16212,"filter":"similar=16212","tag":"The Olsen Gang jumps the fence"},{"id":16201,"filter":"similar=16201","tag":"Rubber Tarzan"},{"id":16210,"filter":"similar=16210","tag":"The Olsen Gang Outta Sight"},{"id":16209,"filter":"similar=16209","tag":"The Olsen Gang Long Gone"},{"id":16208,"filter":"similar=16208","tag":"The Olsen Gang Goes to War"},{"id":16202,"filter":"similar=16202","tag":"Sex, Drugs & Taxation"},{"id":96528,"filter":"similar=96528","tag":"The Last Exploits of the Olsen Gang"},{"id":18731,"filter":"similar=18731","tag":"Truly Human"},{"id":16204,"filter":"similar=16204","tag":"Støv for alle pengene"}],"Extras":{"size":0}}
[
{
"allowSync":true,
"art":"/:/resources/movie-fanart.jpg",
"composite":"/library/sections/31/composite/1574551037",
"filters":true,"refreshing":false,"thumb":"/:/resources/movie.png",
"key":"31","type":"movie","title":"DVR Movies",
"agent":"com.plexapp.agents.imdb","scanner":"Plex Movie Scanner",
"language":"en","uuid":"c26b9580-5ab6-4856-9097-ba87fd6719c8",
"updatedAt":1581103565,"createdAt":1574551035,"scannedAt":1574551037,
"content":true,"directory":true,"contentChangedAt":0,
"Location":[{"id":43,"path":"/share/CACHEDEV1_DATA/DVR-Video/Movies"}]
}
]
[
{"31":{"title":null,"key":31,"type":"movie"}},{"31":{"title":null,"key":31,"type":"movie"}},{"31":{"title":null,"key":31,"type":"movie"}},{"31":{"title":null,"key":31,"type":"movie"}},{"31":{"title":null,"key":31,"type":"movie"}},{"31":{"title":null,"key":31,"type":"movie"}},{"31":{"title":null,"key":31,"type":"movie"}},{"31":{"title":null,"key":31,"type":"movie"}},{"31":{"title":null,"key":31,"type":"movie"}},{"31":{"title":null,"key":31,"type":"movie"}},{"31":{"title":null,"key":31,"type":"movie"}}]

File diff suppressed because one or more lines are too long

View file

@ -4,7 +4,7 @@
<h2 class="subtitle">{{ $t("Modules.ET.Settings.Description") }}</h2>
<br />
<div id="outDir">
<input id="outDirbox" name="outDirbox" v-model="outDirVal" v-bind:placeholder="$t('Modules.ET.Settings.SelectOutDir')">
<input id="outDirbox" name="outDirbox" v-model="outDirVal" :disabled=true v-bind:placeholder="$t('Modules.ET.Settings.SelectOutDir')">
<button v-on:click="browse">{{ $t("Modules.ET.Settings.Browse") }}</button>
</div>

View file

@ -1,5 +1,8 @@
/* eslint-disable no-unreachable */
var def = JSON.parse(JSON.stringify(require('./definitions.json')));
const log = require('electron-log');
const defpostURI = '?checkFiles=1&includeRelated=0&includeExtras=1&includeBandwidths=1&includeChapters=1'
import {wtconfig, wtutils} from '../../../wtutils'
@ -17,26 +20,143 @@ const et = new class ET {
constructor() {
}
async getItemData(baseURL, accessToken, element)
async getSectionData({sectionName, baseURL, accessToken, libType})
{
const sectionData = []
log.info(`Starting getSectionData with Name: ${sectionName}`)
// Get Section Key
const libKey = await et.getSectionKey({libName: sectionName, baseURL: baseURL, accessToken: accessToken})
log.debug(`Get SectionKey as: ${libKey}`)
// Get the size of the library
const libSize = await et.getSectionSizeByKey({sectionKey: libKey, baseURL: baseURL, accessToken: accessToken})
log.debug(`Get Section size as: ${libSize}`)
// Find LibType steps
const step = wtconfig.get("PMS.ContainerSize." + libType)
log.debug(`Get Step size as: ${step}`)
// Current item
let idx = 0
// Now let's walk the section
let chuncks, postURI
const element = '/library/sections/' + libKey
let size
do {
postURI = `/all?X-Plex-Container-Start=${idx}&X-Plex-Container-Size=${step}`;
chuncks = await et.getItemData({baseURL: baseURL, accessToken: accessToken, element: element, postURI: postURI});
size = JSONPath({path: '$.MediaContainer.size', json: chuncks});
log.verbose(`getSectionData chunck size is ${size} and idx is ${idx}`)
sectionData.push(chuncks)
idx = idx + step;
} while (size > 1);
return sectionData;
}
async getSectionSizeByKey({sectionKey, baseURL, accessToken})
{
const url = baseURL + element + '?checkFiles=1&includeRelated=0&includeExtras=1&includeBandwidths=1&includeChapters=1';
const sizeURI = '/library/sections/' + sectionKey
const sizePostURI = '/all?X-Plex-Container-Start=0&X-Plex-Container-Size=0'
const sizeContents = await et.getItemData({baseURL: baseURL, accessToken: accessToken, element: sizeURI, postURI: sizePostURI});
const size = await JSONPath({path: '$..totalSize', json: sizeContents});
return size
}
async getSectionSizeByName({sectionName, baseURL, accessToken})
{
const libKey = await et.getSectionKey({libName: sectionName, baseURL: baseURL, accessToken: accessToken})
const sizeURI = '/library/sections/' + libKey
const sizePostURI = '/all?X-Plex-Container-Start=0&X-Plex-Container-Size=0'
const sizeContents = await et.getItemData({baseURL: baseURL, accessToken: accessToken, element: sizeURI, postURI: sizePostURI});
const size = await JSONPath({path: '$..totalSize', json: sizeContents});
return size
}
async getItemData({baseURL, accessToken, element, postURI=defpostURI})
{
const url = baseURL + element + postURI;
var headers = {
"Accept": "application/json",
"X-Plex-Token": accessToken
}
log.verbose(`Calling url: ${url}`)
}
//log.verbose(`Calling url: ${url}`)
let response = await fetch(url, { method: 'GET', headers: headers});
let resp = await response.json();
const respJSON = await Promise.resolve(resp)
log.debug(`Done key: ${element}`)
return respJSON
//const respJSON = await Promise.resolve(resp)
//log.debug(`Done url: ${url}`)
// return respJSON
return resp
}
getRealLevelName(level, libType) {
// First get the real name of the level, and not just the display name
const levelName = def[libType]['levels'][level]
return levelName
}
}
async getSections(address, accessToken)
{
// Returns an array of json, as:
// [{"title":"DVR Movies","key":31,"type":"movie"}]
const result = []
const url = address + '/library/sections/all'
var headers = {
"Accept": "application/json",
"X-Plex-Token": accessToken
}
let response = await fetch(url, { method: 'GET', headers: headers});
let resp = await response.json();
const respJSON = await Promise.resolve(resp)
let sections = await JSONPath({path: '$..Directory', json: respJSON})[0];
for (var section of sections){
const subItem = {}
subItem['title'] = JSONPath({path: '$..title', json: section})[0];
subItem['key'] = parseInt(JSONPath({path: '$..key', json: section})[0]);
subItem['type'] = JSONPath({path: '$..type', json: section})[0];
result.push(subItem)
}
await Promise.resolve(result)
return result
}
getLevelDisplayName(level, libType){
// return displayname fort with buildin levels
const levels = et.getLevels(libType)
let result = '';
loop1:
for(var key in levels){
if ( levels[key] == level)
{
result = key;
break loop1;
}
}
if ( result == '')
{
// We need to check custom levels
const customLevels = et.getCustomLevels(libType)
loop2:
for(key in customLevels){
if ( customLevels[key] == level)
{
result = key;
break loop2;
}
}
}
return result;
}
getLibDisplayName(libKey, sections){
// return displayname for library
let result = '';
for (var i=0; i<sections.length; i++){
if ( JSONPath({path: '$..key', json: sections[i]}) == libKey)
{
result = JSONPath({path: '$..title', json: sections[i]});
i = sections.length;
}
}
return result;
}
getLevelFields(level, libType) {
// return fields in a level
@ -84,6 +204,26 @@ const et = new class ET {
return def[libType]['fields'][fieldName]['key']
}
resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async getSectionKey({libName, baseURL, accessToken}){
// Returns the key of a library
const sections = await et.getSections(baseURL, accessToken)
let result = '';
for (var i=0; i<sections.length; i++){
if (String(await JSONPath({path: '$..title', json: sections[i]})) == libName) {
result = await JSONPath({path: '$..key', json: sections[i]});
i = sections.length;
}
}
return result
}
getField(libType, fieldName) {
return def[libType]['fields'][fieldName]
@ -173,18 +313,18 @@ const et = new class ET {
if (error.response) {
// The request was made and tgite server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data)
console.log(error.response.status)
log.error(error.response.data)
log.error(error.response.status)
alert(error.response.data.error)
//this.danger(error.response.status, error.response.data.error);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
log.error(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
log.error('Error', error.message);
}
}
)
@ -233,8 +373,7 @@ const excel2 = new class Excel {
// Get level fields
const fields = et.getLevelFields(Level, libType)
for (var i=0; i<fields.length; i++) {
log.verbose('Column: ' + fields[i] + ' - ' + fields[i])
//let column = { header: Level[i], key: 'id', width: 10 }
log.verbose('Column: ' + fields[i] + ' - ' + fields[i])
let column = { header: fields[i], key: fields[i] }
columns.push(column)
}
@ -253,12 +392,16 @@ const excel2 = new class Excel {
return true;
}
async SaveWorkbook(Workbook, Library, Level, Type) {
const fs = require('fs')
async getFileName({ Library, Level, Type }){
const dateFormat = require('dateformat');
const OutDir = wtconfig.get('ET.OutPath', wtutils.UserHomeDir)
const timeStamp=dateFormat(new Date(), "yyyy.mm.dd_h.MM.ss");
const name = OutDir + '/' + Library + '_' + Level + '_' + timeStamp + '.' + Type;
return OutDir + '/' + Library + '_' + Level + '_' + timeStamp + '.' + Type;
}
async SaveWorkbook(Workbook, Library, Level, Type) {
const fs = require('fs')
const name = await this.getFileName( { Library: Library, Level: Level, Type: Type })
log.debug('Saving output file as: ' + name)
// Save Excel on Hard Disk
Workbook.xlsx.writeBuffer()
@ -267,7 +410,7 @@ const excel2 = new class Excel {
}
async postProcess(name, val){
log.silly(`Start postProcess. name: ${name} - val: ${val}`)
// log.silly(`Start postProcess. name: ${name} - val: ${val}`)
const valArray = val.split(wtconfig.get('ET.ArraySep', ' - '))
let retArray = []
let x, retVal
@ -303,8 +446,7 @@ const excel2 = new class Excel {
}
retVal = retArray.join(wtconfig.get('ET.ArraySep', ' - '))
break;
case "Original Title":
console.log('GED Org Title: ' + JSON.stringify(valArray))
case "Original Title":
for (x=0; x<valArray.length; x++) {
retArray.push(valArray[x])
}
@ -320,6 +462,129 @@ const excel2 = new class Excel {
return await retVal;
}
async addRowToTmp( { libType, level, data, stream }) {
log.debug(`Start addRowToTmp. libType: ${libType} - level: ${level}`)
let date, year, month, day, hours, minutes, seconds
const fields = et.getFields( libType, level)
let lookup, val, array, i, valArray, valArrayVal, subType, subKey
let str = ''
let result = ''
for (var x=0; x<fields.length; x++) {
var name = Object.keys(fields[x]);
lookup = JSONPath({path: '$..key', json: fields[x]})[0];
switch(String(JSONPath({path: '$..type', json: fields[x]}))) {
case "string":
val = JSONPath({path: String(lookup), json: data})[0];
// Make N/A if not found
if (val == null)
{
val = wtconfig.get('ET.NotAvail', 'N/A')
}
break;
case "array":
array = JSONPath({path: lookup, json: data});
valArray = []
for (i=0; i<array.length; i++) {
subType = JSONPath({path: '$..subtype', json: fields[x]});
subKey = JSONPath({path: '$..subkey', json: fields[x]});
switch(String(subType)) {
case "string":
valArrayVal = JSONPath({path: String(subKey), json: array[i]})[0];
// Make N/A if not found
if (valArrayVal == null || valArrayVal == "")
{
valArrayVal = wtconfig.get('ET.NotAvail', 'N/A')
}
break;
case "time":
valArrayVal = JSONPath({path: String(subKey), json: array[i]});
// Make N/A if not found
if (valArrayVal == null || valArrayVal == "")
{
valArrayVal = wtconfig.get('ET.NotAvail', 'N/A')
}
else
{
const total = valArrayVal.length
for (let i=0; i<total; i++) {
seconds = '0' + (Math.round(valArrayVal[i]/1000)%60).toString();
minutes = '0' + (Math.round((valArrayVal[i]/(1000 * 60))) % 60).toString();
hours = (Math.trunc(valArrayVal[i] / (1000 * 60 * 60)) % 24).toString();
// Will display time in 10:30:23 format
valArrayVal = hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
}
}
break;
default:
log.error('NO ARRAY HIT (addRowToSheet-array)')
}
valArray.push(valArrayVal)
}
val = valArray.join(wtconfig.get('ET.ArraySep', ' - '))
break;
case "array-count":
val = JSONPath({path: String(lookup), json: data}).length;
break;
case "int":
val = JSONPath({path: String(lookup), json: data})[0];
break;
case "time":
val = JSONPath({path: String(lookup), json: data});
if ( typeof val !== 'undefined' && val )
{
seconds = '0' + (Math.round(val/1000)%60).toString();
minutes = '0' + (Math.round((val/(1000 * 60))) % 60).toString();
hours = (Math.trunc(val / (1000 * 60 * 60)) % 24).toString();
// Will display time in 10:30:23 format
val = hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
}
else
{
val = null
}
break;
case "datetime":
val = JSONPath({path: String(lookup), json: data});
if ( typeof val !== 'undefined' && val )
{
// Create a new JavaScript Date object based on the timestamp
// multiplied by 1000 so that the argument is in milliseconds, not seconds.
date = new Date(val * 1000);
year = date.getFullYear().toString();
month = ('0' + date.getMonth().toString()).substr(-2);
day = ('0' + date.getDate().toString()).substr(-2);
hours = date.getHours();
minutes = "0" + date.getMinutes();
seconds = "0" + date.getSeconds();
// Will display time in 10:30:23 format
val = year+'-'+month+'-'+day+' '+hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
}
else
{
val = null
}
break;
default:
log.error(`No Hit addRowToSheet for ${String(JSONPath({path: '$..type', json: fields[x]}))}`)
}
let doPostProc = JSONPath({path: '$..postProcess', json: fields[x]})
if ( doPostProc == 'true')
{
val = await this.postProcess(name, val);
}
// If string, put in ""
if (isNaN(val)){
str += `,"${val}"`
}
else {
str += `,${val}`
}
}
// Remove first character
result = str.substr(1);
stream.write( result + "\n");
}
async addRowToSheet(sheet, libType, level, data) {
log.debug(`Start addRowToSheet. libType: ${libType} - level: ${level}`)
// Placeholder for row
@ -437,10 +702,9 @@ const excel2 = new class Excel {
row.forEach(element => {
excel2.AddRow(sheet, element)
});
log.silly(`End addRowToSheet. Row: ${JSON.stringify(row)}`)
}
async createOutFile( libName, level, libType, outType, data, baseURL, accessToken )
}
async createOutFile( {libName, level, libType, outType, baseURL, accessToken} )
{
// First create a WorkBook
const workBook = await excel2.NewExcelWorkBook()
@ -450,33 +714,64 @@ const excel2 = new class Excel {
const header = await excel2.AddHeader(sheet, level, libType)
log.debug(`header: ${header}`);
// Now we need to find out how many calls to make
const call = await et.getLevelCall(libType, level)
if ( call == 1 )
{
// Single call needed, so simply pass along the individual items
var items = await JSONPath({path: "$.MediaContainer.Metadata[*]", json: data});
for (var x=0; x<items.length; x++) {
await excel2.addRowToSheet(sheet, libType, level, items[x])
}
}
else
const call = await et.getLevelCall(libType, level)
// Get all the items in small chuncks
var sectionData = await et.getSectionData({sectionName: libName, baseURL: baseURL, accessToken: accessToken, libType: libType})
outType, call
log.verbose('*** Returned from section was ***')
log.verbose(JSON.stringify(sectionData))
log.verbose(`Amount of chunks in sectionData are: ${sectionData.length}`)
// Open a file stream
const tmpFile = await excel2.getFileName({ Library: libName, Level: level, Type: 'tmp' })
var fs = require('fs');
var stream = fs.createWriteStream(tmpFile, {flags:'a'});
for (var x=0; x<sectionData.length; x++)
{
// Get rating key for each item
const urls = JSONPath({path: '$.MediaContainer.Metadata[*].key', json: data});
log.verbose('Items to lookup are: ' + urls)
for (const url of urls) {
const contents = await et.getItemData(baseURL, accessToken, url);
const items = await JSONPath({path: '$.MediaContainer.Metadata[*]', json: contents});
for (var y=0; y<items.length; y++) {
await excel2.addRowToSheet(sheet, libType, level, items[y])
var sectionChunk = await JSONPath({path: "$.MediaContainer.Metadata[*]", json: sectionData[x]});
//for (var n=0; n<sectionChunk.length; n++)
for (var item of sectionChunk)
{
if ( call == 1 )
{
//await excel2.addRowToSheet(sheet, libType, level, item)
await excel2.addRowToTmp( { libType: libType, level: level, data: item, stream: stream } );
}
}
}
else
{
// Get rating key for each item
const url = await JSONPath({path: '$..ratingKey', json: item});
log.verbose('Item to lookup are: ' + url)
const urlWIthPath = '/library/metadata/' + url
const contents = await et.getItemData({baseURL: baseURL, accessToken: accessToken, element: urlWIthPath});
const detailedItem = await JSONPath({path: '$.MediaContainer.Metadata[0]', json: contents});
//log.verbose('detailedItem: ' + JSON.stringify(detailedItem[0]))
//await excel2.addRowToSheet(sheet, libType, level, detailedItem[0])
await excel2.addRowToTmp( { libType: libType, level: level, data: detailedItem[0], stream: stream } );
}
}
}
stream.end();
stream.close();
stream.destroy();
// Rename to real file name
var newFile = tmpFile.replace('.tmp', 'csv')
fs.rename(tmpFile, newFile, function (err) {
if (err) throw err;
console.log('renamed complete');
});
// Save Excel file
const result = await excel2.SaveWorkbook(workBook, libName, level, 'xlsx')
return result
}
// const result = await excel2.SaveWorkbook(workBook, libName, level, outType)
// return result
}
}
export {et, excel2};

View file

@ -37,6 +37,7 @@ log.transports.file.fileName = wtutils.AppName;
log.transports.file.maxSize = wtconfig.get('Log.maxSize', 1048576);
console.log = log.log;
log.info('*********************************')
log.info('Starting ' + wtutils.AppName + ' Version:' + wtutils.AppVersion);
@ -59,9 +60,9 @@ log.info('App Menu builded')
// ET-EXCEL STUFF
// We export library named "Ged" of the type movie with a level of "Level 3"
const libName = 'Ged'
const libName = 'Movies'
//const level = 'Tommy'
const level = 'Level 5'
const level = 'Level 3'
const libType = 'movie'
/*

View file

@ -1,19 +1,21 @@
import axios from 'axios';
import {excel2} from '../../components/modules/ExportTools/et'
import {et} from '../../components/modules/ExportTools/et'
const log = require('electron-log');
const state = {
sections: [],
mediaData: [],
selectedSection : "",
selectedExportLevel: ""
selectedExportLevel: "",
selectedLibType: ""
};
const mutations = {
UPDATE_SECTIONS(state, payload) {
state.sections = payload;
log.info("UPDATE_SECTIONS called")
log.info("UPDATE_SECTIONS called")
},
UPDATE_SELECTEDSECTION(state, payload) {
state.selectedSection = payload
@ -21,69 +23,56 @@ const mutations = {
},
UPDATE_EXPORTLEVEL(state, payload) {
state.selectedExportLevel = payload
},
UPDATE_EXPORTLEVELS(state, payload) {
state.exportLevels = payload
},
UPDATE_SELECTEDLIBTYPE(state, payload) {
state.selectedLibType = payload
},
UPDATE_MEDIADATA(state, payload) {
state.mediaData.push(payload)
}
};
const actions = {
fetchSections({ commit, getters }) {
async fetchSections({ commit, getters }) {
log.info("fetchSections called")
var baseURL = getters.getSlectedServerAddress
axios({
method: 'get',
baseURL: `${baseURL}`,
url: '/library/sections/all',
responseType: 'json',
headers: {
'Accept': "application/json",
'X-Plex-Token': getters.getSlectedServerToken
},
params: {
'includeHttps' : '1',
'includeRelay': '0'
}
}).then((response) => {
commit('UPDATE_SECTIONS', response.data.MediaContainer.Directory)
log.verbose("fetchSection is status " + response.status)
}
).catch((error) => {
if (error.response) {
// The request was made and tgite server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data)
console.log("fetching is error status", error.response.status)
alert(error.response.data.error)
//this.danger(error.response.status, error.response.data.error);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
console.log("Unable to fetch sections")
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
console.log("fetchSection is error 2")
}
}
)
var accessToken = getters.getSlectedServerToken
commit('UPDATE_SECTIONS', await et.getSections(baseURL, accessToken))
},
getMediaMovies({ getters, commit }) {
//getMediaMovies({ getters, commit }) {
getMediaMovies({ commit, getters }) {
var key = getters.getSelectedSection
const testimp3 = require('../../components/modules/ExportTools/Samples/testimp3.json')
// Vars OK
var baseURL = getters.getSlectedServerAddress
var accessToken = getters.getSlectedServerToken
var libType = getters.getLibType
var levelName = et.getLevelDisplayName(getters.getSelectedExportLevel, libType)
var key = getters.getSelectedSection
var mediaSize = ''
var calcSize = 0
// var sections = getters.getPmsSections
var libName = et.getLibDisplayName(getters.getSelectedSection, getters.getPmsSections)
libName, levelName, libType, 'xlsx', testimp3, baseURL, accessToken
excel2
//excel2.createOutFile( libName, levelName, libType, 'xlsx', testimp3, baseURL, accessToken );
axios, commit, key, mediaSize, calcSize
/*
axios({
method: 'get',
baseURL: `${baseURL}`,
@ -122,6 +111,11 @@ const actions = {
log.info("NUGGA Calc : I is: " + i + "calc is: " + getters.getContainerSizeMovies * i)
log.info(response.data.MediaContainer.Metadata)
//mediaArray.push(response.data.MediaContainer.Metadata)
//excel2.createOutFile( libName, level, libType, 'xlsx', response.data.MediaContainer.Metadata, baseURL, getters.getSlectedServerToken );
commit('UPDATE_MEDIADATA', response.data.MediaContainer.Metadata)
}).catch((error) => {
if (error.response) {
@ -143,53 +137,18 @@ const actions = {
}
)
}
})
/*
axios({
method: 'get',
baseURL: `${baseURL}`,
url: `/library/sections/${key}/all`,
responseType: 'json',
headers: {
'Accept': "application/json",
'X-Plex-Token': getters.getSlectedServerToken
},
params: {
"type": "1",
"X-Plex-Container-Start": "0",
"X-Plex-Container-Size": getters.getContainerSizeMovies
}
}).then((response) => {
console.log("getMedia is status " + response.status)
commit('UPDATE_MEDIADATA', response.data.MediaContainer.Metadata)
}
).catch((error) => {
if (error.response) {
// The request was made and tgite server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data)
console.log(error.response.status)
alert(error.response.data.error)
//this.danger(error.response.status, error.response.data.error);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
}
)*/
}) */
excel2.createOutFile( {libName: libName, level: levelName, libType: libType, outType: 'xlsx', baseURL: baseURL, accessToken: accessToken} );
//excel2.createOutFile( libName, levelName, libType, 'xlsx', getters.getMediaMovies, baseURL, accessToken );
}
}
const getters = {
getPmsSections: state => state.sections,
getSelectedSection: state => state.selectedSection
getSelectedSection: state => state.selectedSection,
getSelectedExportLevel: state => state.selectedExportLevel,
getLibType: state => state.selectedLibType,
getExportLevels: state => state.exportLevels
};
const etModule = {