Merge branch 'master' into #10-Electron-log

This commit is contained in:
UKDTOM 2020-06-01 14:51:38 +02:00
commit 02fd465355
24 changed files with 1329 additions and 146 deletions

100
package-lock.json generated
View file

@ -1355,18 +1355,47 @@
}
},
"@vue/cli-plugin-router": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.3.1.tgz",
"integrity": "sha512-m0ntr5R6q62oNMODgoyHAVAd/sDtsH15GdBrScZsPNeyHxmzmNBDlsNM38yYGGY064zDRRWif15d1yaTREybrA==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.4.1.tgz",
"integrity": "sha512-kCSsJG7pjDvCJDjGtcCI5l0UjmqwNigOR41RkeGSjSUvzV4ArSniXjFqrOmtpMp36S5xCtwtt9MFm/K4fCubkQ==",
"dev": true,
"requires": {
"@vue/cli-shared-utils": "^4.3.1"
"@vue/cli-shared-utils": "^4.4.1"
},
"dependencies": {
"@vue/cli-shared-utils": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.4.1.tgz",
"integrity": "sha512-teevHgI7XUsKVMOncx3M+6iLjO28woGfRwgUG4hR83moVBHQe5x2OCr2i5t/58bwpv269RD5RYXBQCGtIXuxZw==",
"dev": true,
"requires": {
"@hapi/joi": "^15.0.1",
"chalk": "^2.4.2",
"execa": "^1.0.0",
"launch-editor": "^2.2.1",
"lru-cache": "^5.1.1",
"node-ipc": "^9.1.1",
"open": "^6.3.0",
"ora": "^3.4.0",
"read-pkg": "^5.1.1",
"request": "^2.88.2",
"request-promise-native": "^1.0.8",
"semver": "^6.1.0",
"strip-ansi": "^6.0.0"
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
}
}
},
"@vue/cli-plugin-vuex": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.3.1.tgz",
"integrity": "sha512-mukwOlhZGBJhkqO2b3wHFFHjK5aP00b1WUHdrOfLR7M18euhaTyb4kA5nwZwEOmU3EzZx6kHzSFCRy/XaMkLug==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.4.1.tgz",
"integrity": "sha512-FtOFsDP0qznwVaCz0BZmTzUm5vhHSJzX2/XD3L5dLTkrNxyDEbZmbKoX0n1OzBcQwZC7dkJZP2tdoCQx0mX//g==",
"dev": true
},
"@vue/cli-service": {
@ -2885,9 +2914,9 @@
}
},
"buefy": {
"version": "0.8.17",
"resolved": "https://registry.npmjs.org/buefy/-/buefy-0.8.17.tgz",
"integrity": "sha512-lLAnLjc8dQpGnLML+4tKpP2yUi/WAQ38ukR5qAMq7Cc9J0sbwy1kXAHarEPlOZkQFzx6c3VmHgL7/qGsupszrQ==",
"version": "0.8.20",
"resolved": "https://registry.npmjs.org/buefy/-/buefy-0.8.20.tgz",
"integrity": "sha512-pg8Cn0m9cjqp2/vaKT4VIfU8KIumuX/gAT1GtearXRs56+kKqAPx3j9O8cm9W6P4jPUCHajKX6H8AqD0ram2Bg==",
"requires": {
"bulma": "0.7.5"
},
@ -5021,9 +5050,9 @@
"dev": true
},
"electron": {
"version": "6.1.11",
"resolved": "https://registry.npmjs.org/electron/-/electron-6.1.11.tgz",
"integrity": "sha512-HCXp6GT0PIRjYWzRvA/M8XX76K511TRM63SmcPtmcOAjZ9sopVYC5abSojdcGHbcT6BUKBMXSsPxoyZ+SZUFEg==",
"version": "6.1.12",
"resolved": "https://registry.npmjs.org/electron/-/electron-6.1.12.tgz",
"integrity": "sha512-RUPM8xJfTcm53V9EKMBhvpLu1+CQkmuvWDmVCypR5XbUG1OOrOLiKl0CqUZ9+tEDuOmC+DmzmJP2MZXScBU5IA==",
"dev": true,
"requires": {
"@types/node": "^10.12.18",
@ -5032,9 +5061,9 @@
},
"dependencies": {
"@types/node": {
"version": "10.17.21",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.21.tgz",
"integrity": "sha512-PQKsydPxYxF1DsAFWmunaxd3sOi3iMt6Zmx/tgaagHYmwJ/9cRH91hQkeJZaUGWbvn0K5HlSVEXkn5U/llWPpQ==",
"version": "10.17.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.24.tgz",
"integrity": "sha512-5SCfvCxV74kzR3uWgTYiGxrd69TbT1I6+cMx1A5kEly/IVveJBimtAMlXiEyVFn5DvUFewQWxOOiJhlxeQwxgA==",
"dev": true
}
}
@ -5698,9 +5727,9 @@
"dev": true
},
"eventemitter3": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
"integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
"integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==",
"dev": true
},
"events": {
@ -6994,9 +7023,9 @@
}
},
"http-proxy": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz",
"integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==",
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"dev": true,
"requires": {
"eventemitter3": "^4.0.0",
@ -8447,8 +8476,7 @@
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"minipass": {
"version": "3.1.1",
@ -13234,9 +13262,9 @@
"dev": true
},
"vue-i18n": {
"version": "8.17.6",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.17.6.tgz",
"integrity": "sha512-SsKL5D9Ii3zJPsFhUSllY754XuZvP8uCouUm+Mbylu95h3OwenV09uzIIEjkT7EtWyDQuWSMWObrNaD4ukBGZw=="
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.18.1.tgz",
"integrity": "sha512-K+hFQJksF8Ph23pnhbwSyoQx+4Y1q/rh2o7GiXI/3rLCCrwanUbzudC8+trp0Mb8rn9y83DYF6RXNrMd+VsuCw=="
},
"vue-i18n-extract": {
"version": "1.0.2",
@ -13401,14 +13429,14 @@
}
},
"vue-router": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.6.tgz",
"integrity": "sha512-GYhn2ynaZlysZMkFE5oCHRUTqE8BWs/a9YbKpNLi0i7xD6KG1EzDqpHQmv1F5gXjr8kL5iIVS8EOtRaVUEXTqA=="
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.3.2.tgz",
"integrity": "sha512-5sEbcfb7MW8mY8lbUVbF4kgcipGXsagkM/X+pb6n0MhjP+RorWIUTPAPSqgPaiPOxVCXgAItBl8Vwz8vq78faA=="
},
"vue-sidebar-menu": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/vue-sidebar-menu/-/vue-sidebar-menu-4.5.3.tgz",
"integrity": "sha512-NijPJvUmMNmkMzUHEBYpSdlyhJnhfXefgMEK+D89mdFkZI2mRWnEo5sRlkNoYw7HHoyB0ObPfAyKstNX8pBFmg==",
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/vue-sidebar-menu/-/vue-sidebar-menu-4.5.4.tgz",
"integrity": "sha512-KSH56kwG+R0uPi4hCj3YYp0AMcIAlVQqdOPNYzeN+3bmGF2/2s/T5ks/KpmBPbgvTf0z2LVwcweKrVeGIPq25A==",
"requires": {
"path-to-regexp": "^1.7.0",
"vue": "^2.5.21"
@ -13464,9 +13492,9 @@
"dev": true
},
"vuex": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.3.0.tgz",
"integrity": "sha512-1MfcBt+YFd20DPwKe0ThhYm1UEXZya4gVKUvCy7AtS11YAOUR+9a6u4fsv1Rr6ePZCDNxW/M1zuIaswp6nNv8Q=="
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.4.0.tgz",
"integrity": "sha512-ajtqwEW/QhnrBZQsZxCLHThZZaa+Db45c92Asf46ZDXu6uHXgbfVuBaJ4gzD2r4UX0oMJHstFwd2r2HM4l8umg=="
},
"vuex-persist": {
"version": "2.2.0",

View file

@ -16,27 +16,26 @@
"dependencies": {
"@fortawesome/fontawesome-free": "^5.13.0",
"axios": "^0.19.2",
"buefy": "^0.8.17",
"buefy": "^0.8.20",
"bulma": "^0.8.2",
"core-js": "^3.6.4",
"electron-log": "^4.2.0",
"is-electron": "^2.2.0",
"minimist": "^1.2.5",
"secure-ls": "^1.2.6",
"vue": "^2.6.11",
"vue-i18n": "^8.17.3",
"vue-router": "^3.1.6",
"vue-sidebar-menu": "^4.5.3",
"vuex": "^3.1.3",
"vue-i18n": "^8.18.1",
"vue-router": "^3.3.2",
"vue-sidebar-menu": "^4.5.4",
"vuex": "^3.4.0",
"vuex-persist": "^2.2.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.3.0",
"@vue/cli-plugin-eslint": "~4.3.0",
"@vue/cli-plugin-router": "^4.3.1",
"@vue/cli-plugin-vuex": "^4.3.1",
"@vue/cli-plugin-router": "^4.4.1",
"@vue/cli-plugin-vuex": "^4.4.1",
"@vue/cli-service": "~4.3.0",
"babel-eslint": "^10.1.0",
"electron": "^6.0.0",
"electron": "^6.1.12",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-cli-plugin-electron-builder": "^1.4.6",

View file

@ -0,0 +1,175 @@
{
"info": {
"_postman_id": "200deb21-86cb-4933-8438-d935b1fa48f4",
"name": "POEditor",
"description": "In this collection, we access the POEditor API\n\nWe use a ReadOnly Token for that, as well as an ID for our translation project",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Get-Languages",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "id",
"value": "{{id}}",
"type": "text"
},
{
"key": "api_token",
"value": "{{api-token}}",
"type": "text"
}
]
},
"url": {
"raw": "https://api.poeditor.com/v2/languages/list",
"protocol": "https",
"host": [
"api",
"poeditor",
"com"
],
"path": [
"v2",
"languages",
"list"
]
},
"description": "Get a list of languages avail on the POEditor.com site"
},
"response": []
},
{
"name": "Get-Translators",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "api_token",
"value": "{{api-token}}",
"type": "text"
},
{
"key": "id",
"value": "{{id}}",
"type": "text"
}
]
},
"url": {
"raw": "https://api.poeditor.com/v2/contributors/list",
"protocol": "https",
"host": [
"api",
"poeditor",
"com"
],
"path": [
"v2",
"contributors",
"list"
]
},
"description": "Get a list of translators, and what they translated"
},
"response": []
},
{
"name": "GetTanslationLink",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "id",
"value": "{{id}}",
"description": "id of project. Defined at folder level",
"type": "text"
},
{
"key": "api_token",
"value": "{{api-token}}",
"description": "api token. Defined at folder level",
"type": "text"
},
{
"key": "language",
"value": "da",
"description": "language",
"type": "text"
},
{
"key": "type",
"value": "key_value_json",
"description": "format of file to download",
"type": "text"
}
]
},
"url": {
"raw": "https://api.poeditor.com/v2/projects/export",
"protocol": "https",
"host": [
"api",
"poeditor",
"com"
],
"path": [
"v2",
"projects",
"export"
]
},
"description": "Retrieves a download link for a translated language"
},
"response": []
}
],
"event": [
{
"listen": "prerequest",
"script": {
"id": "c5196d1d-7a09-4e04-b536-96d299778cca",
"type": "text/javascript",
"exec": [
""
]
}
},
{
"listen": "test",
"script": {
"id": "bda2c131-bb1a-467b-8074-18be164c1138",
"type": "text/javascript",
"exec": [
""
]
}
}
],
"variable": [
{
"id": "c579020f-38a0-4f03-b6a9-5a621ebb0b57",
"key": "id",
"value": "342617",
"type": "string"
},
{
"id": "358c824e-cc8d-49e9-9925-e01a4ddcb649",
"key": "api-token",
"value": "5166c4294ff7fb3a82cbdc82958e850e",
"type": "string"
}
],
"protocolProfileBehavior": {}
}

View file

@ -0,0 +1,34 @@
{
"id": "8a252d90-179f-44a8-9187-1c3f7b61c533",
"name": "WebTools-NG",
"values": [
{
"key": "AccessToken",
"value": "",
"enabled": true
},
{
"key": "PMSAddress",
"value": "http://127.0.0.1:32400",
"enabled": true
},
{
"key": "X-Plex-Token",
"value": "",
"enabled": true
},
{
"key": "Pwd-PlexTV",
"value": "",
"enabled": true
},
{
"key": "Usr-PlexTV",
"value": "dane22",
"enabled": true
}
],
"_postman_variable_scope": "environment",
"_postman_exported_at": "2020-05-22T12:35:23.944Z",
"_postman_exported_using": "Postman/7.25.0"
}

View file

@ -0,0 +1,137 @@
{
"info": {
"_postman_id": "52332b53-b724-431c-b1f4-82b9757ebac0",
"name": "plex.tv",
"description": "Calls agains plex.tv api site\n\nReq. a local defined environtment named plex with the following entries\n\nX-Plex-Token: leave empty, since will be populated after a login\nUser: plex.tv user\nPwd: plex.tv users password\n\n\nWe also has variables defined at this folder level",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Login",
"event": [
{
"listen": "test",
"script": {
"id": "06dac4ba-3dd6-477e-978c-9bf94955e33a",
"exec": [
"var data = JSON.parse(responseBody);",
"postman.setEnvironmentVariable(\"X-Plex-Token\", data.authToken)",
"console.log(\"AuthToken-PlexTV:\", postman.getEnvironmentVariable(\"X-Plex-Token\"))"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "X-Plex-Client-Identifier",
"value": "{{X-Plex-Client-Identifier}}",
"type": "text"
},
{
"key": "Accept",
"value": "application/json",
"type": "text"
}
],
"url": {
"raw": "{{plextvapi}}/v2/users/signin?login={{Usr-PlexTV}}&password={{Pwd-PlexTV}}",
"host": [
"{{plextvapi}}"
],
"path": [
"v2",
"users",
"signin"
],
"query": [
{
"key": "login",
"value": "{{Usr-PlexTV}}"
},
{
"key": "password",
"value": "{{Pwd-PlexTV}}"
}
]
},
"description": "Use two defined local environtment variables named:\nUser\nPwd\n\nAbove are credentials for plex.tv\n\nA third variable named X-Plex-Token, that after a login will be populated with a token, other calls can use"
},
"response": []
},
{
"name": "Get-Resources",
"request": {
"method": "GET",
"header": [
{
"key": "X-Plex-Token",
"value": "{{X-Plex-Token}}",
"type": "text"
},
{
"key": "X-Plex-Client-Identifier",
"value": "{{X-Plex-Client-Identifier}}",
"type": "text"
},
{
"key": "Accept",
"value": "application/json",
"type": "text"
}
],
"url": {
"raw": "{{plextvapi}}/v2/resources",
"host": [
"{{plextvapi}}"
],
"path": [
"v2",
"resources"
]
},
"description": "Get Resources avail for the current plex.tv user"
},
"response": []
}
],
"event": [
{
"listen": "prerequest",
"script": {
"id": "0355f113-7ed5-4483-ac99-01f8665a8df5",
"type": "text/javascript",
"exec": [
""
]
}
},
{
"listen": "test",
"script": {
"id": "3d4cf58b-80b8-415e-b376-16a44cfbbd0d",
"type": "text/javascript",
"exec": [
""
]
}
}
],
"variable": [
{
"id": "12749f23-3086-47bd-a268-6e1dfa5bc5c5",
"key": "plextvapi",
"value": "https://plex.tv/api",
"type": "string"
},
{
"id": "ee6d38c1-4cd1-4fec-9d63-abab0174e473",
"key": "X-Plex-Client-Identifier",
"value": "WebTools-NG",
"type": "string"
}
],
"protocolProfileBehavior": {}
}

View file

@ -0,0 +1,130 @@
{
"info": {
"_postman_id": "daa2f31c-1db6-410b-a2ad-c8f4acc01d33",
"name": "pms",
"description": "In the PMS collection, we store calls against a local PMS\n\nIt req. some local variables, named PMSAddress and AccessToken",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Get-Sections",
"request": {
"method": "GET",
"header": [
{
"key": "Accept",
"value": "application/json",
"type": "text"
},
{
"key": "X-Plex-Token",
"value": "{{AccessToken}}",
"type": "text"
}
],
"url": {
"raw": "{{PMSAddress}}/library/sections/all",
"host": [
"{{PMSAddress}}"
],
"path": [
"library",
"sections",
"all"
]
},
"description": "Will get a list of sections (Libraries) from a PMS\n\nWhat we need, is the key, type and title"
},
"response": []
},
{
"name": "Get-MovieSection",
"request": {
"method": "GET",
"header": [
{
"key": "Accept",
"type": "text",
"value": "application/json"
},
{
"key": "X-Plex-Token",
"type": "text",
"value": "{{AccessToken}}"
}
],
"url": {
"raw": "{{PMSAddress}}/library/sections/{{MovieSection}}/all?type=1&X-Plex-Container-Start=31&X-Plex-Container-Size={{CONTAINERSIZEMOVIES}}",
"host": [
"{{PMSAddress}}"
],
"path": [
"library",
"sections",
"{{MovieSection}}",
"all"
],
"query": [
{
"key": "type",
"value": "1"
},
{
"key": "X-Plex-Container-Start",
"value": "31"
},
{
"key": "X-Plex-Container-Size",
"value": "{{CONTAINERSIZEMOVIES}}"
}
]
},
"description": "Will get a list of sections (Libraries) from a PMS\n\nWhat we need, is the key, type and title"
},
"response": []
}
],
"event": [
{
"listen": "prerequest",
"script": {
"id": "fe0095c1-f667-415f-bd56-9f081ad198e4",
"type": "text/javascript",
"exec": [
""
]
}
},
{
"listen": "test",
"script": {
"id": "83a53476-85dc-43a7-a656-9c273ff0c0d1",
"type": "text/javascript",
"exec": [
""
]
}
}
],
"variable": [
{
"id": "e101680f-7df5-4c66-8b17-ab7df3aee299",
"key": "X-Plex-Client-Identifier",
"value": "WebTools-NG",
"type": "string"
},
{
"id": "11c72d02-77f6-4698-afbc-989b4557bc94",
"key": "MovieSection",
"value": "4",
"type": "string"
},
{
"id": "7694f803-748a-428d-b762-e7441becd677",
"key": "CONTAINERSIZEMOVIES",
"value": "30",
"type": "string"
}
],
"protocolProfileBehavior": {}
}

View file

@ -1,10 +1,8 @@
<template>
<div id="app">
<div id="auth_login" v-if="isAuth == false">
<router-view></router-view>
</div>
<div id="content" :class="{ collapsed: isCollapsed }" v-if="isAuth">
<Header></Header>
<div>
@ -50,7 +48,7 @@ export default {
}
}, computed: {
isAuth () {
return this.$store.state.authenticated
return this.$store.state.plextv.authenticated
}
}
}

BIN
src/assets/WebTools.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

36
src/components/About.vue Normal file
View file

@ -0,0 +1,36 @@
<template>
<section class="section">
<h1 class="title is-3">{{ $t("Modules.About.Name") }}</h1>
<h1 class="title is-3">{{ $t("Modules.About.MainDevelopers") }}</h1>
<p>{{ $t("Modules.About.Devdane22") }}</p>
<p>{{ $t("Modules.About.DevCPSO") }}</p>
<br>
<h1 class="title is-3">{{ $t("Modules.About.TranslationBetaTestersTitle") }}</h1>
<h2 class="subtitle">{{ $t("Modules.About.TranslationBetaTestersText") }}</h2>
<br>
<h1 class="title is-5">{{ $t("Modules.About.PlexPoCredits") }}</h1>
</section>
</template>
<script>
export default {
name: 'about',
methods: {
}, created() {
this.$store.dispatch('fetchPOEContrib');
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

View file

@ -1,18 +0,0 @@
<template>
<section class="section">
<h1 class="title is-2">{{ $t("Modules.ET.Name") }}</h1>
<h2 class="subtitle">{{ $t("Modules.ET.Description") }}</h2>
<br>
</section>
</template>
<script>
export default {
name: 'export'
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

View file

@ -1,6 +1,6 @@
<template>
<section class="section">
<h1 class="title is-2">{{ $t("Common.Home.Title") }}</h1>
<h1 class="title is-3">{{ $t("Common.Home.Title") }}</h1>
<h2 class="subtitle">{{ $t("Common.Home.About") }}</h2>
<br>
<h1 class="title">{{ $t("Common.Home.Modules") }}</h1>

View file

@ -24,14 +24,14 @@
<div class="field">
<label for="" class="label">{{ $t("Common.Login.Password") }}</label>
<div class="control has-icons-left">
<input type="password" v-bind:placeholder="$t('Common.Login.PasswordPrompt')" class="input is-dark" v-model="input.password" v-on:keyup.enter="loginToPlex()" required>
<input type="password" v-bind:placeholder="$t('Common.Login.PasswordPrompt')" class="input is-dark" v-model="input.password" v-on:keyup.enter="plexLogin()" required>
<span class="icon is-small is-left">
<i class="fa fa-lock"></i>
</span>
</div>
</div>
<div class="field">
<button type="button" class="button is-success" v-on:click="loginToPlex()">
<button type="button" class="button is-success" v-on:click="plexLogin()">
Login
</button>
</div>
@ -46,9 +46,7 @@
<script>
const axios = require('axios').default;
import store from '../store'
import router from '../router'
export default {
name: 'Login',
@ -61,61 +59,18 @@ export default {
}
},
methods: {
login(){
if(this.input.username == "admin" && this.input.password == "1234") {
this.$store.commit("setAuthentication", true);
this.$router.replace({name: "home"});
} else {
console.log("The username or password is wrong")
this.danger();
}
plexLogin(){
store.dispatch('loginToPlex', {
username: this.input.username,
password: this.input.password
})
},
danger(){
this.$buefy.toast.open({
duration: 3000,
message: "Wrong password or username",
/*${status}: ${msg}*/
type: 'is-danger'
})
},
loginToPlex(){
axios({
method: 'POST',
url: 'https://plex.tv/users/sign_in.json',
responseType: 'json',
auth: {
username : this.input.username,
password: this.input.password
},
headers:
{ 'X-Plex-Product' : 'webtools',
'X-Plex-Version' : '1.19.2',
'X-Plex-Client-Identifier' : 'Insert ID here',
}
})
.then(function (response) {
store.commit("setAuthToken", response.data.user.authToken);
store.commit("setAuthentication", true);
router.replace({name: "home"});
})
.catch(function (error) {
if (error.response) {
// The request was made and the 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);
}
})
}
}}

View file

@ -1,15 +1,45 @@
<template>
<section class="hero is-dark is-small">
<div class="hero-body">
<div class="columns is-desktop is-vcentered">
<div class="">
<figure class="image is-48x48">
<img src="https://bulma.io/images/placeholders/128x128.png">
</figure>
<div class="level">
<div class="level-left">
<div class="level-item">
<figure class="image is-48x48">
<img src="@/assets/WebTools.png">
</figure>
<div id="title" >
<p id="top_title" class="title is-size-3">{{ $t("Common.AppName") }}</p>
</div>
</div>
</div>
<div class="">
<div id="title" >
<p id="top_title" class="title is-size-3">{{ $t("Common.AppName") }}</p>
<div class="level-right">
<div class="level-item">
<figure class="image is-48x48" id="avatar">
<img id="avatar" :src="getAvatar()">
</figure>
</div>
<div class="level-item">
<b-button id="sync-button" @click="fetchServers" type="is-warning"
icon-left="fas fa-sync" icon-pack="fas" class="is-pulled-right" >
</b-button>
<div class="select is-dark">
<b-select v-bind:placeholder="$t('Common.SelServer')"
@input="selected">
<option
v-for="option in pserver"
:value="option"
:key="option.clientIdentifier"
v-on:change="onchange()">
{{ option.name }}
</option>
</b-select>
</div>
</div>
</div>
</div>
@ -17,8 +47,50 @@
</section>
</template>
<script>
import store from '../../store';
export default {
methods: {
fetchServers(){
console.log("fetching servers")
this.$store.dispatch('fetchPlexServers', store.getters.getAuthToken);
},
active2(e) {
console.log("active2 called")
this.active = e;
},
selected: function (selected) {
this.selected = selected;
this.$store.commit("UPDATE_SELECTED_SERVER", selected);
},
onChange(event) {
console.log(event.target.selected);
},
getAvatar(){
return this.$store.getters.getAvatar
}
},
created(){
console.log("menu created")
this.$store.dispatch('fetchPlexServers');
},
computed: {
pserver(){
return this.$store.getters.getPlexServers
}
}
}
</script>
<style scoped>
#title {
margin-left: 15px;
}
#sync-button{
margin-right: 10px;
}
</style>

View file

@ -41,7 +41,7 @@ import '@fortawesome/fontawesome-free/css/all.css'
icon: 'fas fa-language'
},
{
href: { path: '/' },
href: { path: '/about' },
title: 'About us',
icon: 'fas fa-question-circle'
},

View file

@ -0,0 +1,143 @@
<template>
<section class="section">
<h1 class="title is-3">{{ $t("Modules.ET.Name") }}</h1>
<h2 class="subtitle">{{ $t("Modules.ET.Description") }}</h2>
<br>
<h1 class="title is-3">{{ $t("Modules.ET.HSelectMedia") }}</h1>
<div class="block">
<b-radio v-model="radio" type="is-dark"
name="movie"
native-value="movie">
{{ $t("Modules.ET.RadioMovies") }}
</b-radio>
<b-radio v-model="radio" type="is-dark"
name="tvseries"
native-value="tvseries"
disabled>
{{ $t("Modules.ET.RadioTVSeries") }}
</b-radio>
<b-radio v-model="radio" type="is-dark"
name="artist"
native-value="artist"
disabled>
{{ $t("Modules.ET.RadioMusic") }}
</b-radio>
<b-radio v-model="radio" type="is-dark"
name="photo"
native-value="photo"
disabled>
{{ $t("Modules.ET.RadioPhotos") }}
</b-radio>
<b-radio v-model="radio" type="is-dark"
name="othervideos"
native-value="othervideos"
disabled>
{{ $t("Modules.ET.RadioOtherVideos") }}
</b-radio>
</div>
<hr>
<div class="container">
<h1 class="title is-3">{{ $t("Modules.ET.HSelectSelection") }}</h1>
<div class="select is-dark">
<b-select v-bind:placeholder="$t('Modules.ET.SelectSelection')"
@input="selectSelection">
<option
v-for="option in pmsSections"
:value="option.key"
:key="option.key"
v-on:change="onchange()">
{{ option.title }}
</option>
</b-select>
</div>
<b-button
id="sync-button"
@click="fetchSelection" type="is-warning"
icon-left="fas fa-sync" icon-pack="fas" >
</b-button>
</div>
<hr>
<h1 class="title is-3">{{ $t("Modules.ET.HExportMedia") }}</h1>
<div class="buttons">
<b-button
type="is-primary"
@click="getMedia"
icon-left="fas fa-file-download"
icon-pack="fas">
{{ $t("Modules.ET.HExportMedia") }}
</b-button>
</div>
</section>
</template>
<script>
export default {
name: 'export',
data() {
return {
radio: 'movie',
}
},
created(){
console.log("ET Created")
this.fetchSelection()
}, computed: {
pmsSections: function(){
let sections = this.$store.getters.getPmsSections
let result=[];
if(Array.isArray(sections) && sections.length){
console.log("doing a forEach")
sections.forEach((req) => {
if (req.type == this.radio) {
console.log("pushing data to results")
result.push(req);
}
})
} else {
console.log("No data found")
result.push["No Section found"]
}
return result
}
}, methods: {
selectSelection: function (selected) {
console.log(selected)
this.$store.commit("UPDATE_SELECTEDSECTION", selected);
},
getMedia(){
console.log("getMedia Called")
this.$store.dispatch('getMediaMovies');
},
fetchSelection(){
console.log("fetchSelection")
let serverCheck = this.$store.getters.getSelectedServer
if(serverCheck !== "none"){
console.log("serverCheck is not null, running fetchSections ")
this.$store.dispatch('fetchSections')
} else {
console.log("serverCheck is none")
this.$buefy.toast.open({
duration: 3000,
message: `No server selected`,
type: 'is-danger'
})
}
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#sync-button{
margin-left: 1em;
}
</style>

View file

@ -0,0 +1,123 @@
{
"Media ID":
{
"key": "ratingKey",
"call": "1",
"type": "string"
},
"Title":
{
"key": "title",
"call": "1",
"type": "string"
},
"Sort title":
{
"key": "titleSort",
"call": "1",
"type": "string"
},
"Studio":
{
"key": "studio",
"call": "1",
"type": "string"
},
"Content Rating":
{
"key": "contentRating",
"call": "1",
"type": "int32"
},
"Year":
{
"key": "year",
"call": "1",
"type": "int"
},
"Rating":
{
"key": "rating",
"call": "1",
"type": "int32"
},
"Summary":
{
"key": "summary",
"call": "1",
"type": "string"
},
"Genre":
{
"key": "['Genre']['tag']",
"call": "1",
"type": "array"
},
"View Count":
{
"key": "viewCount",
"call": "1",
"type": "int"
},
"Last Viewed at":
{
"key": "lastViewedAt",
"call": "1",
"type": "datetime"
},
"Tagline":
{
"key": "tagline",
"call": "1",
"type": "string"
},
"Release Date":
{
"key": "originallyAvailableAt",
"call": "1",
"type": "string"
},
"Writers":
{
"key": "['Writer']['tag']",
"call": "1",
"type": "array"
},
"Country":
{
"key": "['Country']['tag']",
"call": "1",
"type": "array"
},
"Duration":
{
"key": "duration",
"call": "1",
"type": "time"
},
"Directors":
{
"key": "['Director']['tag']",
"call": "1",
"type": "array"
},
"Roles":
{
"key": "['Role']['tag']",
"call": "1",
"type": "array"
},
"Audience Rating":
{
"key": "audienceRating",
"call": "1",
"type": "string"
},
"User Rating":
{
"key": "userRating",
"call": "1",
"type": "string"
}
}

View file

@ -0,0 +1,8 @@
var levels = [{"Level 1": "level1"}, {"Level 2": "level2"}];
var level1 = ["Media ID", "Title", "Sort title",
"Studio", "Content Rating", "Year",
"Rating", "Summary"];
var level2 = ["Media ID", "Title", "Sort title"];

View file

@ -10,11 +10,21 @@
"Language": {
"Description": "@:Common.Language.Name lader dig skifte sprog",
"Name": "Sprog"
}
},
"Login": {
"SignIn": "Login med plex.tv",
"UseCred": "Brug dine normale plex.tv login oplysninger",
"Note": "3.Part Auth er ikke muligt, som f.eks. Google mm.",
"Username": "Brugernavn",
"UsernamePrompt": "Skriv dit plex.tv brugernavn",
"Password": "Kodeord",
"PasswordPrompt": "Skriv dit plex.tv kodeord"
},
"SelServer": "Vælg Server"
},
"Modules": {
"ET": {
"Description": "@:Modules.ET.Name allows you to export some information about the medias in your libraries",
"Description": "@:Modules.ET.Name lader dig eksportere information vedrørende de medier du har i dine biblioteker",
"Name": "ExportTool"
}
}

View file

@ -1,6 +1,7 @@
{
"Common": {
"AppName": "WebTools-NG",
"SelServer": "Select Server",
"Home": {
"About": "@:Common.AppName is a tool that enables the use of tool modules to help with Plex Server management.",
"Modules": "Tool Modules currently available are:",
@ -24,7 +25,28 @@
"Modules": {
"ET": {
"Description": "@:Modules.ET.Name allows you to export some information about the medias in your libraries",
"Name": "ExportTool"
"Name": "ExportTool",
"HSelectMedia": "Select Media Type",
"HSelectSelection": "Select Media Libary",
"SelectSelection": "Select Selection",
"HExportMedia": "Export Media",
"RadioMovies": "Movies",
"RadioTVSeries": "TV Series",
"RadioMusic": "Music",
"RadioPhotos" : "Photos",
"RadioOtherVideos": "Other Videos"
},
"About": {
"Description": "@:Modules.About.Name",
"Name": "About us and credits",
"MainDevelopers": "Main Developers",
"Devdane22": "Tommy (aka dane22), a Plex community member (ukdtom on GitHub)",
"DevCPSO": "Casper (aka CPSO), a soon to be Plex community member (CPSO on GitHub)",
"TranslationBetaTestersTitle": "Translations and Beta Testers",
"TranslationBetaTestersText": "A huge thanks goes out to all the people who helped in translating and testing @:Common.AppName.",
"PlexPoCredits": "And Many Thanks To The authors/developers and staff at Plex. We are eternally grateful for your dedication, talent and hard work And our friends at POEditor.com, who provided a free translation site because we are an Open-Source project."
}
}
}

View file

@ -2,8 +2,9 @@ import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../components/Login.vue'
import Home from '../components/Home.vue'
import Export from '../components/Export.vue'
import Export from '../components/modules/ExportTools/Export'
import Language from '../components/Language.vue'
import About from '../components/About'
import Store from '../store/index.js'
Vue.use(VueRouter)
@ -41,11 +42,17 @@ Vue.use(VueRouter)
name: "language",
component: Language,
meta: {requiresAuth: true}
},
{
path: '/about',
name: "about",
component: About,
meta: {requiresAuth: true}
}
]});
router.beforeEach( (to,from,next) => {
let routerAuthCheck = Store.state.authenticated;
let routerAuthCheck = Store.state.plextv.authenticated;
if (to.matched.some(record => record.meta.requiresAuth)) {
if(routerAuthCheck){

View file

@ -1,6 +1,9 @@
import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersistence from 'vuex-persist';
import plextv from './modules/plextv'
import poeditor from './modules/poeditor'
import et from './modules/et'
Vue.use(Vuex)
@ -11,22 +14,23 @@ const vuexLocal = new VuexPersistence({
export default new Vuex.Store({
state: {
authenticated: false,
authToken: ''
},
mutations: {
setAuthentication(state, status) {
state.authenticated = status;
},
setAuthToken(state, token){
state.authToken = token
}
CONTAINERSIZEMOVIES: "30",
CONTAINERSIZETV: "20",
CONTAINERSIZEEPISODES: "30",
CONTAINERSIZEAUDIO: "10",
CONTAINERSIZEPHOTO: "20",
PMSTIMEOUT: "20"
},
mutations: {},
getters: {
getContainerSizeMovies: state => state.CONTAINERSIZEMOVIES
},
actions: {
},
actions: {},
modules: {
plextv,
poeditor,
et
},
plugins: [vuexLocal.plugin]
})

123
src/store/modules/et.js Normal file
View file

@ -0,0 +1,123 @@
import axios from 'axios';
const state = {
sections: [],
selectedSection : ""
};
const mutations = {
UPDATE_SECTIONS(state, payload) {
state.sections = payload;
},
UPDATE_SELECTEDSECTION(state, payload) {
state.selectedSection = payload
}
};
const actions = {
fetchSections({ commit, getters }) {
console.log("calling fetchSections")
var baseURL = getters.getSlectedServerAddress
axios({
method: 'get',
baseURL: `http://${baseURL}`,
url: '/library/sections/all',
responseType: 'json',
headers: {
'Accept': "application/json",
'X-Plex-Token': getters.getAuthToken
}
}).then((response) => {
console.log("fetchSection is status " + response.status)
commit('UPDATE_SECTIONS', response.data.MediaContainer.Directory)
}
).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")
}
}
)
},
getMediaMovies({ getters }) {
var key = getters.getSelectedSection
var baseURL = getters.getSlectedServerAddress
axios({
method: 'get',
baseURL: `http://${baseURL}`,
url: `/library/sections/${key}/all`,
responseType: 'json',
headers: {
'Accept': "application/json",
'X-Plex-Token': getters.getAuthToken
},
params: {
"type": "1",
"X-Plex-Container-Start": "0",
"X-Plex-Container-Size": getters.getContainerSizeMovies
}
}).then((response) => {
console.log("getMedia is status " + response.status)
console.log(response.data)
console.log(response.data.MediaContainer.Metadata)
//commit('UPDATE_SECTIONS', 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);
}
}
)
}
}
const getters = {
getPmsSections: state => state.sections,
getSelectedSection: state => state.selectedSection
};
const etModule = {
state,
mutations,
actions,
getters
}
export default etModule;

140
src/store/modules/plextv.js Normal file
View file

@ -0,0 +1,140 @@
import axios from 'axios';
import router from '../../router'
const state = {
plexServers: [],
selectedServer: 'none',
authenticated: false,
authToken: '',
avatar: '',
filteredProducts: []
};
const mutations = {
UPDATE_PLEX_SERVERS(state, payload) {
state.plexServers = payload;
},
UPDATE_SELECTED_SERVER(state, value) {
state.selectedServer = value
},
UPDATE_AUTHENTICATED(state, value){
state.authenticated = value
},
UPDATE_AUTHTOKEN(state, value){
state.authToken = value
},
UPDATE_AVATAR(state, value){
state.avatar = value
}
};
const actions = {
fetchPlexServers({ commit, getters }) {
axios({
method: 'get',
url: 'https://plex.tv/api/v2/resources',
responseType: 'json',
headers:
{
'X-Plex-Client-Identifier' : 'WebTools-NG',
'X-Plex-Token': getters.getAuthToken,
'Accept' : 'application/json',
'includeHttps' : '1',
'includeRelay': '0',
},
})
.then((response) => {
let result=[];
console.log("response from fetchPlexServers", response)
response.data.forEach((req) => {
if (req.owned == true && req.product == "Plex Media Server") {
result.push(req);
}
})
commit('UPDATE_PLEX_SERVERS', result)
//this.$store.dispatch('fetchSections')
})
.catch(function (error) {
if (error.response) {
console.log(error.response.data)
alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message)
} else if (error.request) {
console.log(error.request);
} else {
console.log('Error on fetchPlexServers', error.message);
}
});
},
loginToPlex({ commit }, payload){
axios({
method: 'POST',
url: 'https://plex.tv/users/sign_in.json',
responseType: 'json',
auth: {
username : payload.username,
password: payload.password
},
headers:
{ 'X-Plex-Product' : 'webtools',
'X-Plex-Version' : '1.19.2',
'X-Plex-Client-Identifier' : 'WebTools-NG',
}
})
.then(function (response) {
commit('UPDATE_AUTHTOKEN', response.data.user.authToken)
commit('UPDATE_AUTHENTICATED', true)
commit('UPDATE_AVATAR', response.data.user.thumb)
router.replace({name: "home"});
})
.catch(function (error) {
if (error.response) {
// The request was made and the 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);
}})
}
};
const getters = {
getPlexServers: state => state.plexServers,
getAuthToken: state => state.authToken,
getAvatar: state => state.avatar,
getSelectedServer: state => state.selectedServer,
getSlectedServerAddress: state => {
let result= "";
if(state.selectedServer !== "none"){
state.selectedServer.connections.forEach((req) => {
if (req.local == true) {
result = req.address + ":" + req.port
}
})
}
return result
}
};
const serverModule = {
state,
mutations,
actions,
getters
}
export default serverModule;

View file

@ -0,0 +1,57 @@
import axios from 'axios';
const state = {
contributors: [],
};
const mutations = {
UPDATE_CONTRIBUTORS(state, payload) {
state.contributors = payload;
},
};
const actions = {
fetchPOEContrib({ commit }) {
axios({
method: 'post',
url: 'https://api.poeditor.com/v2/contributors/list',
headers:
{
"Access-Control-Allow-Origin": "*"
},
body:
{
'id' : '342617',
'api-token': "5166c4294ff7fb3a82cbdc82958e850e",
},
})
.then((response) => {
console.log(response)
commit('UPDATE_CONTRIBUTORS', response)
})
.catch(function (error) {
if (error.response) {
console.log(error.response.data)
alert(error.response.data.errors[0].code + " " + error.response.data.errors[0].message)
} else if (error.request) {
console.log(error.request);
} else {
console.log('Error', error.message);
}
});
}
}
const getters = {
};
const serverModule = {
state,
mutations,
actions,
getters
}
export default serverModule;