Merge pull request #475 from WebTools-NG/#459-Viewstate

#459 viewstate
This commit is contained in:
Tommy Mikkelsen 2022-05-03 02:13:03 +02:00 committed by GitHub
commit d25db81e57
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 323 additions and 49 deletions

View file

@ -15,6 +15,8 @@
* [#469 Default ExportTools Levels do NOT produce any output](https://github.com/WebTools-NG/WebTools-NG/issues/469)
* [#468 Add "Last Viewed at" as an available field for TV Show Episode exporting](https://github.com/WebTools-NG/WebTools-NG/issues/468)
* [#473 PMS Root page](https://github.com/WebTools-NG/WebTools-NG/issues/473)
* [#459 Viewstate copy](https://github.com/WebTools-NG/WebTools-NG/issues/459)
## V0.3.15 (20220413)

View file

@ -130,7 +130,28 @@
"Browse": "Browse",
"ErrorNoOutDirMsg": "You need to define an output directory in the global settings page first",
"ErrorNoOutDirTitle": "No output directory defined",
"ReleaseNoteTitle": "Release Note"
"ReleaseNoteTitle": "Release Note",
"Status": {
"Name": {
"Global": {
"Status": "Status",
"Chuncks": "Chunks",
"Items": "Items",
"Info": "Info",
"OutFile": "Output File",
"StartTime": "Start time",
"EndTime": "End Time",
"TimeElapsed": "Time Elapsed",
"RunningTime": "Running time"
},
"Modules": {}
},
"Msg": {
"Global": {
},
"Modules": {}
}
}
},
"Modules": {
"ET": {
@ -422,13 +443,17 @@
"Msg": {
"CollectUserInfo": "Collecting user info...Please wait",
"Processing": "Processing",
"Processing2": "Processing library {0} of {1} - {2}",
"Idle": "Idle",
"GatheringLibs": "Gathering libraries to process"
"GatheringLibs": "Gathering libraries to process",
"ProcessItem1": "Processing: ({1} - {2}) - {0}"
},
"Names": {
"Status": "Status",
"LibsToProcess": "Libraries to process",
"StartTime": "Start time"
"StartTime": "Start time",
"CurrentLib": "Current Library",
"Item": "Item"
}
}
}

View file

@ -59,9 +59,7 @@
},
"Include": {
"Suggest Naming": "includeGuids=1&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",
"all": "checkFiles=1&includeExtras=1&includeBandwidths=1&includeChapters=1",
"GED SLET Missing1": "includeAllConcerts=1&includeChildren=1&includeConcerts=1&includeFields=1&includeGeolocation=1&includeLoudnessRamps=1&includeMarkers=1&includeOnDeck=1&includePopularLeaves=1&includePreferences=1&includeRelated=1&includeRelatedCount=1&includeReviews=1&includeStations=1",
"GED SLET Everything1": "checkFiles=1&includeAllConcerts=1&includeBandwidths=1&includeChapters=1&includeChildren=1&includeConcerts=1&includeExtras=1&includeFields=1&includeGeolocation=1&includeLoudnessRamps=1&includeMarkers=1&includeOnDeck=1&includePopularLeaves=1&includePreferences=1&includeRelated=1&includeRelatedCount=1&includeReviews=1&includeStations=1"
"all": "checkFiles=1&includeExtras=1&includeBandwidths=1&includeChapters=1"
}
},

View file

@ -114,12 +114,11 @@
// Watch for when selected server address is updated
selectedServerAddress: async function(){
log.info("ViewState selected server changed");
console.log('Ged 1-1: ' + JSON.stringify(this.$store.getters.getViewStateStatus))
viewstate.clearStatus();
console.log('Ged 1-2: ' + JSON.stringify(this.$store.getters.getViewStateStatus))
viewstate.updateStatusMsg(1, i18n.t("Modules.PMS.ViewState.Status.Msg.CollectUserInfo"));
viewstate.SrcUsrKey = -1;
viewstate.TargetUsrKey = -1;
viewstate.SrcUsr = null;
viewstate.TargetUsr = null;
console.log('Ged 1-3: ' + JSON.stringify(this.$store.getters.getViewStateStatus))
this.serverIsSelected = ( this.$store.getters.getSelectedServer != "none" );
this.WaitForUsers = false;
@ -156,12 +155,16 @@
methods: {
// SrcUsr changed
async selSrcUsrChanged() {
await viewstate.setKey( 'selSrcUsr', this.selSrcUsr);
viewstate.SrcUsr = this.selSrcUsr;
await viewstate.setOwnerStatus( 'selSrcUsr', this.selSrcUsr);
await viewstate.getLibs( this.selSrcUsr, this.selTargetUsr );
console.log('Ged 14-1 Src: ' + JSON.stringify(viewstate.SrcUsr))
},
// SrcUsr changed
async selTargetUsrChanged() {
await viewstate.setKey( 'selTargetUsr', this.selTargetUsr );
viewstate.TargetUsr = this.selTargetUsr;
await viewstate.setOwnerStatus( 'selTargetUsr', this.selTargetUsr );
await viewstate.getLibs( this.selSrcUsr, this.selTargetUsr );
},
/* Check if a server is selected, and if not

View file

@ -4,7 +4,7 @@ const {JSONPath} = require('jsonpath-plus');
console.log = log.log;
import {wtutils} from '../../../General/wtutils';
import {wtutils, wtconfig} from '../../../General/wtutils';
import i18n from '../../../../../i18n';
import store from '../../../../../store';
import axios from 'axios';
@ -18,30 +18,46 @@ const viewstate = new class ViewState {
#_msgType = {
1: i18n.t("Modules.PMS.ViewState.Status.Names.Status"),
2: i18n.t("Modules.PMS.ViewState.Status.Names.LibsToProcess"),
3: i18n.t("Modules.PMS.ViewState.Status.Names.StartTime")
3: i18n.t("Modules.PMS.ViewState.Status.Names.StartTime"),
4: i18n.t("Modules.PMS.ViewState.Status.Names.CurrentLib"),
5: i18n.t("Modules.PMS.ViewState.Status.Names.Item"),
6: i18n.t("Modules.ET.Status.Names.StartTime"),
7: i18n.t("Modules.ET.Status.Names.EndTime"),
8: i18n.t("Modules.ET.Status.Names.TimeElapsed"),
9: i18n.t("Modules.ET.Status.Names.RunningTime")
}
constructor() {
this.selServerServerToken = '',
this.viewStateUsers = [],
this.libs = {},
this.SrcUsrKey = -1,
this.TargetUsrKey = -1
this.SrcUsrToken1 = '',
this.TargetUsrToken1 = '',
this.SrcUsr,
this.TargetUsr,
this.libType
}
async setKey( Usr, data ){
async getRunningTimeElapsed(){
const now = new Date();
let elapsedSeconds = Math.floor((now.getTime() - this.#_StartTime.getTime()) / 1000);
let elapsedStr = elapsedSeconds.toString().replaceAll('.', '');
let hours = Math.floor(parseFloat(elapsedStr) / 3600);
elapsedSeconds = parseFloat(elapsedStr) - hours * 3600;
let minutes = Math.floor(elapsedSeconds / 60);
let seconds = elapsedSeconds - minutes * 60;
if ( hours.toString().length < 2) { hours = '0' + hours}
if ( minutes.toString().length < 2) { minutes = '0' + minutes}
if ( seconds.toString().length < 2) { seconds = '0' + seconds}
return hours + ':' + minutes + ':' + seconds
}
async setOwnerStatus( Usr, data ){
if ( Usr == 'selSrcUsr' ){
this.SrcUsrKey = JSONPath({path: `$..libs[0].key`, json: data})
if ( isNaN(this.SrcUsrKey) ){
this.SrcUsrKey = -1;
}
this.SrcUsr['isOwner'] = (JSONPath({path: `$..libs[0].key`, json: data})[0] == 0);
}
else {
this.TargetUsrKey = JSONPath({path: `$..libs[0].key`, json: data})
if ( isNaN(this.TargetUsrKey) ){
this.TargetUsrKey = -1;
}
this.TargetUsr['isOwner'] = (JSONPath({path: `$..libs[0].key`, json: data})[0] == 0);
}
}
@ -64,19 +80,241 @@ const viewstate = new class ViewState {
return hours + ':' + minutes + ':' + seconds;
}
async setLibType( libKey ){
switch(this.libs[libKey]['type']) {
case 'movie':
this.libType = 1;
break;
case 'show':
this.libType = 4;
break;
default:
this.libType = 1;
}
}
async getAmountOfWatched( libKey ){
log.info(`Getting amount of watched items`);
await this.setLibType( libKey );
let url = `${store.getters.getSelectedServerAddress}/library/sections/${libKey}/all?type=${this.libType}&lastViewedAt%3E%3E=1970-01-01&X-Plex-Container-Start=0&X-Plex-Container-Size=0`;
let header = wtutils.PMSHeader;
let totalSize;
header['X-Plex-Token'] = this.SrcUsr.token;
await axios({
method: 'get',
url: url,
headers: header
})
.then((response) => {
log.debug('[viewState.js] (getAmountOfWatched) Response from getAmountOfWatched recieved');
//log.silly(`getAmountOfWatched returned as: ${JSON.stringify(response.data)}`);
totalSize = JSONPath({path: `$..totalSize`, json: response.data});
log.debug(`[viewState.js] (getAmountOfWatched) Total Size: ${totalSize}`);
})
.catch(function (error) {
if (error.response) {
log.error('[viewState.js] (getAmountOfWatched) getAmountOfWatched: ' + JSON.stringify(error.response.data));
alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message);
} else if (error.request) {
log.error('[viewState.js] (getAmountOfWatched) error: ' + error.request);
} else {
log.error('[viewState.js] (getAmountOfWatched) last error: ' + error.message);
}
});
return totalSize;
}
async bumpViewCount( media ){
const ratingKey = JSONPath({path: `$..ratingKey`, json: media});
const viewCount = JSONPath({path: `$..viewCount`, json: media});
const viewOffset = JSONPath({path: `$..viewOffset`, json: media});
const duration = JSONPath({path: `$..duration`, json: media});
log.info(`Bumbing viewcount to ${viewCount} for media ${ratingKey}`);
let url = `${store.getters.getSelectedServerAddress}/:/scrobble?identifier=com.plexapp.plugins.library&key=${ratingKey}`;
// We need to bump viewcount for target user same amount as for SrcUsr
let header = wtutils.PMSHeader;
header['X-Plex-Token'] = this.TargetUsr.token;
for (var i = 0; i < viewCount; i++)
{
await axios({
method: 'get',
url: url,
headers: header
})
.catch(function (error) {
if (error.response) {
log.error('[viewState.js] (bumpViewCount) bumpViewCount: ' + JSON.stringify(error.response.data));
alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message);
} else if (error.request) {
log.error('[viewState.js] (bumpViewCount) error: ' + error.request);
} else {
log.error('[viewState.js] (bumpViewCount) last error: ' + error.message);
}
});
}
// Do we need to also set an offset value?
if ( viewOffset > 0)
{
url = `${store.getters.getSelectedServerAddress}/:/timeline?ratingKey=${ratingKey}&key=%2Flibrary%2Fmetadata%2F${ratingKey}&state=stopped&time=${viewOffset}&duration=${duration}`;
await axios({
method: 'get',
url: url,
headers: header
})
.catch(function (error) {
if (error.response) {
log.error('[viewState.js] (bumpViewCount) viewOffset: ' + JSON.stringify(error.response.data));
alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message);
} else if (error.request) {
log.error('[viewState.js] (bumpViewCount) viewOffset error: ' + error.request);
} else {
log.error('[viewState.js] (bumpViewCount) viewOffset last error: ' + error.message);
}
});
}
}
async processWatchedList( libKey ){
log.info('[viewstate.js] (processWatchedList) Process Watched list');
let totalSize //, size;
let start = 0;
let index = 0;
totalSize = await this.getAmountOfWatched( libKey );
const step = wtconfig.get("PMS.ContainerSize." + this.libs[libKey]['type'], 20);
let url, gotSize;
do // Walk section in steps
{
let listProcess = {};
url = `${store.getters.getSelectedServerAddress}/library/sections/${libKey}/all?type=${this.libType}&lastViewedAt%3E%3E=1970-01-01&X-Plex-Container-Start=${start}&X-Plex-Container-Size=${step}&excludeElements=Genre,Director,Writer,Country,Role,Producer,Collections,Media&excludeFields=summary,tagline,rating,contentRating,audienceRatingImage,file`;
// Now go grab the medias
let header = wtutils.PMSHeader;
header['X-Plex-Token'] = this.SrcUsr.token;
await axios({
method: 'get',
url: url,
headers: header
})
.then((response) => {
log.debug('[viewState.js] (processWatchedList) Response from processWatchedList recieved');
log.silly(`processWatchedList returned as: ${JSON.stringify(response.data)}`);
gotSize = JSONPath({path: `$.MediaContainer.size`, json: response.data})[0];
const medias = JSONPath({path: `$..Metadata`, json: response.data})[0];
for (var media in medias){
let listProcessDetails = {};
listProcessDetails['title'] = JSONPath({path: `$..title`, json: medias[media]})[0];
listProcessDetails['viewOffset'] = JSONPath({path: `$..viewOffset`, json: medias[media]})[0];
listProcessDetails['lastViewedAt'] = JSONPath({path: `$..lastViewedAt`, json: medias[media]})[0];
listProcessDetails['viewCount'] = JSONPath({path: `$..viewCount`, json: medias[media]})[0];
listProcessDetails['duration'] = JSONPath({path: `$..duration`, json: medias[media]})[0];
listProcessDetails['ratingKey'] = JSONPath({path: `$..ratingKey`, json: medias[media]})[0];
listProcess[JSONPath({path: `$..ratingKey`, json: medias[media]})[0]] = listProcessDetails;
index += 1;
this.bumpViewCount( listProcessDetails );
this.updateStatusMsg(5, i18n.t("Modules.PMS.ViewState.Status.Msg.ProcessItem1", [listProcessDetails['title'], index, totalSize]));
}
})
.catch(function (error) {
if (error.response) {
log.error('[viewState.js] (processWatchedList) processWatchedList: ' + JSON.stringify(error.response.data));
alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message);
} else if (error.request) {
log.error('[viewState.js] (processWatchedList) error: ' + error.request);
} else {
log.error('[viewState.js] (processWatchedList) last error: ' + error.message);
}
});
start += step;
} while ( gotSize == step );
}
async walkSourceUsr(){
log.info('[viewstate.js] (walkSourceUsr) Walking SourceUsr');
var keyCount = Object.keys(this.libs).length;
let index = 1;
for (var libKey in this.libs){
this.updateStatusMsg(4, i18n.t("Modules.PMS.ViewState.Status.Msg.Processing2", [index, keyCount, this.libs[libKey]['title']]));
await this.processWatchedList( libKey );
index += 1;
}
}
async getUsrTokens(){
log.info('[viewstate.js] Starting getUsrTokens');
// Ask for a list of devices
let header = wtutils.PMSHeader;
// Add server token
header['X-Plex-Token'] = this.selServerServerToken[0];
// Remove unwanted headers
delete header['X-Plex-Client-Identifier']
delete header['X-Plex-Device']
delete header['X-Plex-Product']
delete header['X-Plex-Version']
const url = `${wtutils.plexTVApi}v2/server/access_tokens`;
await axios({
method: 'get',
url: url,
headers: header
})
.then((response) => {
log.debug('[viewState.js] Response from getUsrTokens recieved');
//log.silly(`getUsrTokens returned as: ${JSON.stringify(response.data)}`);
var users = JSONPath({path: `$..[?(@.type == 'server')]`, json: response.data});
for (var user in users){
var userInfo = users[user]
if ( userInfo['invited']['id'] == this.SrcUsr['id']){
if ( store.getters.getUsers[userInfo['invited']['id']]['friendlyName'] ){
this.SrcUsr['title'] = store.getters.getUsers[userInfo['invited']['id']]['friendlyName'];
}
else {
this.SrcUsr['title'] = userInfo['invited']['title'];
}
this.SrcUsr['token'] = userInfo['token'];
}
else if ( userInfo['invited']['id'] == this.TargetUsr['id']){
if ( store.getters.getUsers[userInfo['invited']['id']]['friendlyName'] ){
this.TargetUsr['title'] = store.getters.getUsers[userInfo['invited']['id']]['friendlyName'];
}
else {
this.TargetUsr['title'] = userInfo['invited']['title'];
}
this.TargetUsr['token'] = userInfo['token'];
}
}
if (this.SrcUsr.isOwner){
this.SrcUsr['token'] = store.getters.getAuthToken;
this.SrcUsr['title'] = store.getters.getPlexName;
this.SrcUsr['id'] = store.getters.getMeId;
}
if (this.TargetUsr.isOwner){
this.TargetUsr['token'] = store.getters.getAuthToken;
this.TargetUsr['title'] = store.getters.getPlexName;
this.TargetUsr['id'] = store.getters.getMeId;
}
})
.catch(function (error) {
if (error.response) {
log.error('[viewState.js] getUsrTokens: ' + JSON.stringify(error.response.data));
alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message);
} else if (error.request) {
log.error('[viewState.js] getUsrTokens: ' + error.request);
} else {
log.error('[viewState.js] getUsrTokens: ' + error.message);
}
});
}
async copyViewState( SrcUsr, TargetUsr ){
log.info('[viewstate.js] Starting copyViewState');
const startTime = await this.getNowTime('start');
console.log('Ged 4 start time: ' + startTime)
this.updateStatusMsg(3, startTime);
console.log('Ged 1 SrcUsr: ' + JSON.stringify(SrcUsr))
console.log('Ged 2 TargetUsr: ' + JSON.stringify(TargetUsr))
this.updateStatusMsg(1, i18n.t("Modules.PMS.ViewState.Status.Msg.Processing"));
//this.updateStatusMsg( this.RawMsgType.TimeElapsed, await this.getRunningTimeElapsed());
await this.getLibs( SrcUsr, TargetUsr );
this.updateStatusMsg(1, "Ged")
//this.updateStatusMsg(3, startTime);
startTime
this.updateStatusMsg(1, i18n.t("Modules.PMS.ViewState.Status.Msg.Processing"));
await this.getUsrTokens();
await this.walkSourceUsr();
this.updateStatusMsg(1, i18n.t("Modules.PMS.ViewState.Status.Msg.Idle"));
}
// Update status msg
@ -94,7 +332,6 @@ const viewstate = new class ViewState {
}
})
store.commit("UPDATE_viewStateStatus", newMsg);
console.log('Ged 7 Msg: ' + JSON.stringify(newMsg))
}
// Clear Status Window
@ -106,9 +343,12 @@ const viewstate = new class ViewState {
}
async getLibs( SrcUsr, TargetUsr ){
log.info('[viewstate.js] Starting getLibs');
log.info('[viewstate.js] (getLibs) Starting getLibs');
log.silly(`[viewstate.js] (getLibs) SrcUsr: ${JSON.stringify(SrcUsr)}`);
log.silly(`[viewstate.js] (getLibs) TargetUsr: ${JSON.stringify(TargetUsr)}`);
// Are both users selected ?
if ( this.TargetUsrKey < 0 || this.SrcUsrKey < 0 ){
if ( !(this.TargetUsr && this.SrcUsr) ){
log.info('[viewstate.js] (getLibs) Both users not yet selected, so exit');
return;
}
if ( JSON.stringify(SrcUsr) === JSON.stringify(TargetUsr) ){
@ -119,15 +359,18 @@ const viewstate = new class ViewState {
}
this.clearStatus();
this.updateStatusMsg(1, i18n.t("Modules.PMS.ViewState.Status.Msg.GatheringLibs"));
log.silly(`[viewstate.js] Source usr: ${JSON.stringify(SrcUsr)}`);
log.silly(`[viewstate.js] Target usr: ${JSON.stringify(TargetUsr)}`);
log.silly(`[viewstate.js] (getLibs) Source usr: ${JSON.stringify(SrcUsr)}`);
log.silly(`[viewstate.js] (getLibs) Target usr: ${JSON.stringify(TargetUsr)}`);
this.libs = {};
if ( JSONPath({path: `$..libs[0].key`, json: SrcUsr}) == 0 )
{
// We need to add all libs from target usr
log.debug(`[viewstate.js] SrcUsr is owner`);
for(let i of TargetUsr["libs"]) {
this.libs[i.key] = i.title;
let entry = {};
entry['title'] = i.title;
entry['type'] = i.type;
this.libs[i.key] = entry;
}
}
else
@ -136,21 +379,27 @@ const viewstate = new class ViewState {
// We need to add all libs from Source usr
log.debug(`[viewstate.js] TargetUsr is owner`);
for(let i of SrcUsr["libs"]) {
this.libs[i.key] = i.title;
let entry = {};
entry['title'] = i.title;
entry['type'] = i.type;
this.libs[i.key] = entry;
}
}
else {
for(let i of SrcUsr["libs"]) {
if ( JSON.stringify(TargetUsr["libs"]).indexOf(JSON.stringify(i)) > -1)
{
this.libs[i.key] = i.title;
let entry = {};
entry['title'] = i.title;
entry['type'] = i.type;
this.libs[i.key] = entry;
}
}
}
}
let libstatus = []
for (let lib in this.libs){
libstatus.push(this.libs[lib])
libstatus.push(this.libs[lib]['title'])
}
this.updateStatusMsg(1, i18n.t("Modules.PMS.ViewState.Status.Msg.Idle"));
this.updateStatusMsg(2, libstatus.join(', '))
@ -171,9 +420,8 @@ const viewstate = new class ViewState {
})
.then((response) => {
log.debug('[viewState.js] Response from getServerToken recieved');
//log.silly(`getServerToken returned as: ${JSON.stringify(response.data)}`);
this.selServerServerToken = JSONPath({path: `$[?(@.clientIdentifier== '${clientIdentifier}')].token`, json: response.data});
log.silly(`[viewState.js] selServerServerToken returned as: ${this.selServerServerToken}`);
log.silly(`[viewState.js] selServerServerToken returned ok`);
})
.catch(function (error) {
if (error.response) {

View file

@ -155,7 +155,7 @@ const actions = {
commit('UPDATE_AUTHENTICATED', true)
commit('UPDATE_AVATAR', response.data.thumb)
commit('UPDATE_PLEXNAME', response.data.username)
commit('UPDATE_MeId', response.data.id)
commit('UPDATE_MeId', response.data.user.id)
router.replace({name: "home"});
})
.catch(function (error) {
@ -199,13 +199,11 @@ const actions = {
})
.then(function (response) {
log.debug('loginToPlex: Response from fetchPlexServers recieved')
console.log('Gedd 66 Me: ' + JSON.stringify(response.data.user))
commit('UPDATE_AUTHTOKEN', response.data.user.authToken)
commit('UPDATE_AUTHENTICATED', true)
commit('UPDATE_AVATAR', response.data.user.thumb)
commit('UPDATE_PLEXNAME', response.data.user.username)
commit('UPDATE_MeId', response.data.id)
commit('UPDATE_MeId', response.data.user.id)
router.replace({name: "home"});
})
.catch(function (error) {