diff --git a/public/locales/en.json b/public/locales/en.json index 2976f54..7fcf562 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -327,6 +327,8 @@ "Description": "@:Modules.PMS.Name module allows you to manage your server", "ErrorNoServerSelectedTitle": "No server selected", "ErrorNoServerSelectedMsg": "You need to select a server on the top of the screen", + "ExportDoneBody": "Check {0}", + "ExportDoneTitle": "Export finished", "Butler": { "Title": "Butler Scheduled Tasks", "Description": "Here you can kickstart a scheduled task", @@ -388,8 +390,14 @@ "Value": "Value" } }, - "ExportDoneBody": "Check {0}", - "ExportDoneTitle": "Export finished" + "DVR": { + "Name": "DVR", + "Description": "@:Modules.PMS.DVR.Name module allows you to backup and restore your DVR settings", + "selDVR": "Select DVR to backup", + "ttselDVR": "Here you select the DVR you want to make a backup of", + "lblBtnBackup": "Backup", + "BackupDone": "DVR has been saved" + } }, "PlexTV": { "Name": "Plex.TV", @@ -407,14 +415,6 @@ "ExportUsr": "Export user to CSV", "ExportAllUsr": "Export all users to CSV", "Settings": "To configure column separator and 'Not Avail', use @:Modules.ET.Name settings" - }, - "DVR": { - "Name": "DVR", - "Description": "@:Modules.DVR.Name module allows you to backup and restore your DVR settings", - "selDVR": "Select DVR to backup", - "ttselDVR": "Here you select the DVR you want to make a backup of", - "lblBtnBackup": "Backup" - - } + } } } \ No newline at end of file diff --git a/src/assets/dvr-256.png b/src/assets/dvr-256.png deleted file mode 100644 index 723778f..0000000 Binary files a/src/assets/dvr-256.png and /dev/null differ diff --git a/src/assets/release-notes.md b/src/assets/release-notes.md deleted file mode 100644 index 2816ed5..0000000 --- a/src/assets/release-notes.md +++ /dev/null @@ -1,18 +0,0 @@ -# Release Note - -For a complete manual, see the [Wiki](https://github.com/WebTools-NG/WebTools-NG/wiki) - -What's new: - -Please see the [Change Log](https://github.com/WebTools-NG/WebTools-NG/blob/master/CHANGELOG.md) - -This is a BETA release - -Note that the binaries are not signed, since we cannot afford a signing certificate nor a dev membership with Apple -* See [Special MAC Work Around](https://github.com/WebTools-NG/WebTools-NG/wiki/Known-Issues#-mac-os) - -Depending on your workstation OS, grab the following file from the assets below: - -Windows: Use the exe file -Mac: Use the dmg file -Linux: Use the AppImage file diff --git a/src/components/layout/Menu.vue b/src/components/layout/Menu.vue index aafc1b9..e1ef943 100644 --- a/src/components/layout/Menu.vue +++ b/src/components/layout/Menu.vue @@ -12,8 +12,7 @@ import '@fortawesome/fontawesome-free/css/all.css'; import etIcon from '@/assets/ET-256.png'; import pmsIcon from '@/assets/plex-pms-icon.png'; - import plextvIcon from '@/assets/plex-app-icon.png'; - import plexDVRIcon from '@/assets/dvr-256.png'; + import plextvIcon from '@/assets/plex-app-icon.png'; export default { @@ -66,14 +65,9 @@ icon: 'fa fa-tasks' }, { - href: { path: '/dvr' }, - title: this.$t("Modules.DVR.Name"), - // icon: 'fas fa-file-export', - icon: { - //adjust element - element: 'img', - attributes: { src: plexDVRIcon }, - } + href: { path: '/pms/dvr' }, + title: this.$t("Modules.PMS.DVR.Name"), + icon: 'fas fa-tv', } ] }, @@ -84,7 +78,7 @@ icon: { //adjust element element: 'img', - attributes: { src: etIcon }, + attributes: { src: etIcon }, }, child: [ { diff --git a/src/components/modules/DVR/DVR.vue b/src/components/modules/DVR/DVR.vue deleted file mode 100644 index bd5c41e..0000000 --- a/src/components/modules/DVR/DVR.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - diff --git a/src/components/modules/DVR/scripts/dvr.js b/src/components/modules/DVR/scripts/dvr.js deleted file mode 100644 index eedc451..0000000 --- a/src/components/modules/DVR/scripts/dvr.js +++ /dev/null @@ -1,15 +0,0 @@ -// Helper file for dvr.tv module -const log = require('electron-log'); -console.log = log.log; - - -import {wtconfig, wtutils} from '../../General/wtutils'; -import i18n from '../../../../i18n'; -import store from '../../../../store'; - -i18n, wtconfig, wtutils, store - -const dvr = new class DVR { -} - -dvr diff --git a/src/components/modules/ExportTools/scripts/et.js b/src/components/modules/ExportTools/scripts/et.js index 5148d9c..30dd4b6 100644 --- a/src/components/modules/ExportTools/scripts/et.js +++ b/src/components/modules/ExportTools/scripts/et.js @@ -151,59 +151,6 @@ const et = new class ET { this.OutFile = null } - - async GEDDELETE_getSectionData() - { - const sectionData = [] - // Find LibType steps - const step = wtconfig.get("PMS.ContainerSize." + this.expSettings.libType, 20) - log.debug(`!!!! et (getSectionData): Got Step size as: ${step}`) - let element - // Now read the fields and level defs - // Current item - let idx = 0 - // Now let's walk the section - let chuncks, postURI - let size - do { - switch (this.expSettings.libType) { - case et.ETmediaType.Photo: - element = '/library/sections/' + this.expSettings.selLibKey + '/all'; - postURI = `?addedAt>>=-2208992400&X-Plex-Container-Start=${idx}&X-Plex-Container-Size=${step}&type=${this.expSettings.libTypeSec}&${this.uriParams}`; - break; - case et.ETmediaType.Playlist: - element = '/playlists/' + this.expSettings.selLibKey; - postURI = `/items?X-Plex-Container-Start=${idx}&X-Plex-Container-Size=${step}`; - break; - case et.ETmediaType.Libraries: - element = '/library/sections/all'; - postURI = `?X-Plex-Container-Start=${idx}&X-Plex-Container-Size=${step}`; - break; - case et.ETmediaType.Playlists: - element = '/playlists/all'; - postURI = `?X-Plex-Container-Start=${idx}&X-Plex-Container-Size=${step}`; - break; - default: - element = '/library/sections/' + this.expSettings.selLibKey + '/all'; - postURI = `?X-Plex-Container-Start=${idx}&X-Plex-Container-Size=${step}&type=${this.expSettings.libTypeSec}&${this.uriParams}`; - } - log.info(`Calling getSectionData url ${this.expSettings.baseURL + element + postURI}`); - - chuncks = await et.getItemData({baseURL: this.expSettings.baseURL, accessToken: this.expSettings.accessToken, element: element, postURI: postURI}); - size = JSONPath({path: '$.MediaContainer.size', json: chuncks}); - const totalSize = JSONPath({path: '$.MediaContainer.totalSize', json: chuncks}); - log.info(`getSectionData chunck size is ${size} and idx is ${idx} and totalsize is ${totalSize}`) - // et.updateStatusMsg(et.rawMsgType.Info, i18n.t('Modules.ET.Status.GetSectionItems', {idx: idx, chunck: size, totalSize: totalSize})) - et.updateStatusMsg(et.rawMsgType.Info, i18n.t('Modules.ET.Status.GetSectionItems', {chunck: step, totalSize: totalSize})) - sectionData.push(chuncks) - log.debug(`Pushed chunk as ${JSON.stringify(chuncks)}`) - idx = idx + step; - } while (size > 1); - log.debug(`SectionData to return is:`); - log.debug(JSON.stringify(sectionData)); - return sectionData; - } - getRealLevelName(level, libType) { // First get the real name of the level, and not just the display name let levelName @@ -228,47 +175,6 @@ const et = new class ET { return levelName; } - async DELOLD_getSections(address, accessToken) - { - // Returns an array of json, as: - // [{"title":"DVR Movies","key":31,"type":"movie"}] - const result = []; - let url = address + '/library/sections/all' - this.PMSHeader["X-Plex-Token"] = accessToken; - let response = await fetch(url, { method: 'GET', headers: this.PMSHeader}); - let resp = await response.json(); - let respJSON = await Promise.resolve(resp); - let sections = await JSONPath({path: '$..Directory', json: respJSON})[0]; - let section; - for (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]; - subItem['scanner'] = JSONPath({path: '$..scanner', json: section})[0]; - subItem['agent'] = JSONPath({path: '$..agent', json: section})[0]; - result.push(subItem) - } - await Promise.resolve(result); - url = address + '/playlists'; - response = await fetch(url, { method: 'GET', headers: this.PMSHeader}); - resp = await response.json(); - respJSON = await Promise.resolve(resp); - if (JSONPath({path: '$..size', json: respJSON})[0] > 0) - { - sections = await JSONPath({path: '$..Metadata', json: respJSON})[0]; - for (section of sections){ - const subItem = {} - subItem['title'] = JSONPath({path: '$..title', json: section})[0]; - subItem['key'] = parseInt(JSONPath({path: '$..ratingKey', json: section})[0]); - subItem['type'] = JSONPath({path: '$..type', json: section})[0]; - subItem['playlistType'] = JSONPath({path: '$..playlistType', json: section})[0]; - result.push(subItem) - } - } - return result - } - getLevelDisplayName(level, libType){ // return displayname for the buildin levels if (libType == et.ETmediaType.Playlist) diff --git a/src/components/modules/PMS/DVR/DVR.vue b/src/components/modules/PMS/DVR/DVR.vue new file mode 100644 index 0000000..585f3b6 --- /dev/null +++ b/src/components/modules/PMS/DVR/DVR.vue @@ -0,0 +1,129 @@ + + + + + + diff --git a/src/components/modules/PMS/DVR/scripts/dvr.js b/src/components/modules/PMS/DVR/scripts/dvr.js new file mode 100644 index 0000000..1834f25 --- /dev/null +++ b/src/components/modules/PMS/DVR/scripts/dvr.js @@ -0,0 +1,113 @@ +// Helper file for dvr.tv module +const log = require('electron-log'); +const {JSONPath} = require('jsonpath-plus'); +console.log = log.log; + + +import {wtconfig, wtutils} from '../../../General/wtutils'; +import i18n from '../../../../../i18n'; +import store from '../../../../../store'; +import axios from 'axios'; + +i18n, wtconfig + +const dvr = new class DVR { + async getDVRList(){ + log.info(`Getting list of DVRs`); + // Placeholder for return value + let arrDVR = []; + let header = wtutils.PMSHeader; + header['X-Plex-Token'] = store.getters.getSelectedServerToken; + const url = `${store.getters.getSelectedServerAddress}/livetv/dvrs`; + log.verbose(`Url to query is: ${url}`); + let DVRs; + await axios({ + method: 'get', + url: url, + headers: header + }) + .then((response) => { + log.debug('Response from getDVRList recieved'); + DVRs = JSONPath({path: '$..Dvr[*]', json: response.data}); + // Save to store + store.commit('UPDATE_DVR_SETTINGS', DVRs); + log.verbose('DVRs added to store') + }) + .catch(function (error) { + if (error.response) { + log.error('getDVRList: ' + error.response.data); + alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message); + } else if (error.request) { + log.error('getDVRList: ' + error.request); + } else { + log.error('getDVRList: ' + error.message); + } + }); + DVRs.forEach(dvr => { + arrDVR.push({ + "value": JSONPath({path: '$.uuid', json: dvr}), + "text": JSONPath({path: '$.lineupTitle', json: dvr}) + }) + }); + return arrDVR; + } + + async backupDVR( { dvrName } ){ + let fileName = await this.getFileName( {'dvrName': dvrName} ); + const fs = require('fs'); + let data = await this.getDVR( dvrName ); + fs.writeFileSync(fileName, JSON.stringify(data, null, 2)); + store.commit('UPDATE_doneDVRBackup', fileName); + log.debug(`DVR backedup to ${fileName}`); + } + + async getDVR( uuid ){ + let DVRs = store.getters.getDVRs; + let retVal=''; + DVRs.forEach( dvr => { + if (dvr['uuid']==uuid){ + retVal = dvr; + } + }) + return retVal; + } + + async getDVRName( { uuid } ){ + let DVRs = store.getters.getDVRs; + let retVal=''; + DVRs.forEach( dvr => { + if (dvr['uuid']==uuid){ + retVal = dvr['lineupTitle']; + } + }) + return retVal; + } + + async getFileName( { dvrName } ){ + /* + Will create the output directory if it doesn't exists + Will return a string with the filename to use + */ + const realDVRName = await this.getDVRName( { 'uuid': dvrName }) + const path = require('path'); + const Module = path.join('Plex Media Server', 'DVR'); + const dateFormat = require('dateformat'); + const OutDir = wtconfig.get('General.ExportPath'); + const timeStamp=dateFormat(new Date(), "yyyy.mm.dd_h.MM.ss"); + let outFile = store.getters.getSelectedServer['name'] + '_' + realDVRName + '_' + timeStamp + '.json'; + const targetDir = path.join( + OutDir, wtutils.AppName, Module); + const outFileWithPath = path.join( + targetDir, outFile); + // Make sure target dir exists + const fs = require('fs') + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }); + } + log.info(`OutFile is ${outFileWithPath}`) + return outFileWithPath; + } +} + + +export { dvr }; \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index f27b64f..0d97503 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -11,7 +11,7 @@ import PMSSettings from '../components/modules/PMS/Settings/settings'; import Butler from '../components/modules/PMS/Butler/butler'; import Language from '../components/modules/Main/Language.vue'; import GlobalSettings from '../components/modules/Main/GlobalSettings'; -import DVR from '../components/modules/DVR/DVR'; +import DVR from '../components/modules/PMS/DVR/DVR'; import About from '../components/modules/Main/About'; import Store from '../store/index.js'; @@ -83,7 +83,7 @@ Vue.use(VueRouter) meta: {requiresAuth: true} }, { - path: '/dvr', + path: '/pms/dvr', name: "dvr", component: DVR, meta: {requiresAuth: true} diff --git a/src/store/modules/pms.js b/src/store/modules/pms.js index 1384806..a4b29af 100644 --- a/src/store/modules/pms.js +++ b/src/store/modules/pms.js @@ -6,17 +6,27 @@ const log = require('electron-log'); const {JSONPath} = require('jsonpath-plus'); const state = { - settings: {} + settings: {}, + DVRs: {}, + doneDVRBackup: '' }; const mutations = { - UPDATE_PMS_SETTINGS(state, payload) { - state.settings = payload; - } + UPDATE_PMS_SETTINGS(state, payload) { + state.settings = payload; + }, + UPDATE_DVR_SETTINGS(state, payload) { + state.DVRs = payload; + }, + UPDATE_doneDVRBackup(state, payload) { + state.doneDVRBackup = payload; + } }; const getters = { - getPMSSettings: state => state.settings + getPMSSettings: state => state.settings, + getDVRs: state => state.DVRs, + doneDVRBackup: state => state.doneDVRBackup } const actions = { @@ -78,97 +88,94 @@ const actions = { }); }, - - async fetchPMSSettings({ commit }, payload) { - let header = wtutils.PMSHeader; - header['X-Plex-Token'] = payload.Token; - const url = payload.Address + '/:/prefs'; - await axios({ - method: 'get', - url: url, - headers: header + async fetchPMSSettings({ commit }, payload) { + let header = wtutils.PMSHeader; + header['X-Plex-Token'] = payload.Token; + const url = payload.Address + '/:/prefs'; + await axios({ + method: 'get', + url: url, + headers: header + }) + .then((response) => { + log.debug('Response from fetchPlexServers recieved') + var filteredResult = {} + // Filtered result based on hidden, adv or all + var curFilter = wtconfig.get('PMS.FilterSetting', 'AllSettings'); + log.verbose(`Filter set to ${curFilter}`); + if (curFilter == 'AllSettings'){ + filteredResult = JSONPath({path: '$..Setting', json: response.data})[0]; + } + else if (curFilter == 'OnlyHidden'){ + filteredResult = JSONPath({path: '$..Setting[?(@.hidden==true)]', json: response.data}); + } + else { + filteredResult = JSONPath({path: '$..Setting[?(@.advanced==true)]', json: response.data}); + } + // Reset PMSSettings + var PMSSettings = {}; + // Create Array for undefined + PMSSettings[i18n.t('Modules.PMS.Settings.Undefined')] = []; + // Create Arrays for other categories + filteredResult.forEach(group => { + group = JSONPath({path: '$.group', json: group})[0] + if (group !== "") { + PMSSettings[group] = []; + } }) - .then((response) => { - log.debug('Response from fetchPlexServers recieved') - var filteredResult = {} - // Filtered result based on hidden, adv or all - var curFilter = wtconfig.get('PMS.FilterSetting', 'AllSettings'); - log.verbose(`Filter set to ${curFilter}`); - if (curFilter == 'AllSettings'){ - filteredResult = JSONPath({path: '$..Setting', json: response.data})[0]; - } - else if (curFilter == 'OnlyHidden'){ - filteredResult = JSONPath({path: '$..Setting[?(@.hidden==true)]', json: response.data}); - } - else { - filteredResult = JSONPath({path: '$..Setting[?(@.advanced==true)]', json: response.data}); - } - // Reset PMSSettings - var PMSSettings = {}; - // Create Array for undefined - PMSSettings[i18n.t('Modules.PMS.Settings.Undefined')] = []; - // Create Arrays for other categories - filteredResult.forEach(group => { - group = JSONPath({path: '$.group', json: group})[0] - if (group !== "") { - PMSSettings[group] = []; - } - }) - // Get the single items - filteredResult.forEach(element => { - var id = JSONPath({path: '$.id', json: element}); - var itemGroup = JSONPath({path: '$.group', json: element}); - if (itemGroup == "") - { - itemGroup = i18n.t('Modules.PMS.Settings.Undefined'); - } - var PMSSettingsItem = {} - var jNode = {}; - jNode['label'] = JSONPath({path: '$.label', json: element})[0]; - jNode['summary'] = JSONPath({path: '$.summary', json: element})[0]; - jNode['type'] = JSONPath({path: '$.type', json: element})[0]; - jNode['default'] = JSONPath({path: '$.default', json: element})[0]; - jNode['value'] = JSONPath({path: '$.value', json: element})[0]; - PMSSettingsItem[id] = jNode; - PMSSettings[itemGroup].push(PMSSettingsItem); - }); - // Remove undefined category, if empty - if (Object.keys(PMSSettings[i18n.t('Modules.PMS.Settings.Undefined')]).length === 0){ - delete PMSSettings[i18n.t('Modules.PMS.Settings.Undefined')]; - } - log.verbose(`PMS Settings are: ${JSON.stringify(PMSSettings)}`); - // Workaround for issue #429 - if ( !JSON.stringify(PMSSettings).includes('LogNumFiles') ) + // Get the single items + filteredResult.forEach(element => { + var id = JSONPath({path: '$.id', json: element}); + var itemGroup = JSONPath({path: '$.group', json: element}); + if (itemGroup == "") { - // Put into undefined, since the only one we know the name of - var itemGroup = i18n.t('Modules.PMS.Settings.Undefined'); - var PMSSettingsItem = {} - var jNode = {}; - jNode['label'] = 'Amount of log files'; - jNode['summary'] = 'Amount of log files to keep before rolling over. Server must be restarted for changes to take effect'; - jNode['type'] = 'int'; - jNode['default'] = 5; - jNode['value'] = 5; - PMSSettingsItem['LogNumFiles'] = jNode; - PMSSettings[itemGroup].push(PMSSettingsItem); + itemGroup = i18n.t('Modules.PMS.Settings.Undefined'); } - commit('UPDATE_PMS_SETTINGS', PMSSettings); - }) - .catch(function (error) { - if (error.response) { - log.error('fetchPMSSettings: ' + error.response.data) - alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message) - } else if (error.request) { - log.error('fetchPMSSettings: ' + error.request) - } else { - log.error('fetchPMSSettings: ' + error.message) - } - }); - } + var PMSSettingsItem = {} + var jNode = {}; + jNode['label'] = JSONPath({path: '$.label', json: element})[0]; + jNode['summary'] = JSONPath({path: '$.summary', json: element})[0]; + jNode['type'] = JSONPath({path: '$.type', json: element})[0]; + jNode['default'] = JSONPath({path: '$.default', json: element})[0]; + jNode['value'] = JSONPath({path: '$.value', json: element})[0]; + PMSSettingsItem[id] = jNode; + PMSSettings[itemGroup].push(PMSSettingsItem); + }); + // Remove undefined category, if empty + if (Object.keys(PMSSettings[i18n.t('Modules.PMS.Settings.Undefined')]).length === 0){ + delete PMSSettings[i18n.t('Modules.PMS.Settings.Undefined')]; + } + log.verbose(`PMS Settings are: ${JSON.stringify(PMSSettings)}`); + // Workaround for issue #429 + if ( !JSON.stringify(PMSSettings).includes('LogNumFiles') ) + { + // Put into undefined, since the only one we know the name of + var itemGroup = i18n.t('Modules.PMS.Settings.Undefined'); + var PMSSettingsItem = {} + var jNode = {}; + jNode['label'] = 'Amount of log files'; + jNode['summary'] = 'Amount of log files to keep before rolling over. Server must be restarted for changes to take effect'; + jNode['type'] = 'int'; + jNode['default'] = 5; + jNode['value'] = 5; + PMSSettingsItem['LogNumFiles'] = jNode; + PMSSettings[itemGroup].push(PMSSettingsItem); + } + commit('UPDATE_PMS_SETTINGS', PMSSettings); + }) + .catch(function (error) { + if (error.response) { + log.error('fetchPMSSettings: ' + error.response.data) + alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message) + } else if (error.request) { + log.error('fetchPMSSettings: ' + error.request) + } else { + log.error('fetchPMSSettings: ' + error.message) + } + }); + } }; - - const serverModule = { state, mutations,