Implementing infinite scroll support

This commit is contained in:
luke-hagar-sp
2022-07-29 19:45:08 -05:00
parent bae77fc2fa
commit 3461173f82
7 changed files with 406 additions and 157 deletions

BIN
.DS_Store vendored

Binary file not shown.

138
package-lock.json generated
View File

@@ -1,17 +1,19 @@
{ {
"name": "plex-api-oauth", "name": "plex-api-oauth",
"version": "1.0.128", "version": "1.1.10",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "plex-api-oauth", "name": "plex-api-oauth",
"version": "1.0.128", "version": "1.1.10",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"axios": "^0.27.2", "axios": "^0.27.2",
"plex-oauth": "^2.0.2", "plex-oauth": "^2.0.2",
"qs": "^6.11.0", "qs": "^6.11.0",
"react": "^18.2.0",
"react-infinite-scroller": "^1.2.6",
"ts-mocha": "^10.0.0", "ts-mocha": "^10.0.0",
"typescript": "^4.7.4", "typescript": "^4.7.4",
"uuid": "^8.3.2" "uuid": "^8.3.2"
@@ -173,9 +175,9 @@
} }
}, },
"node_modules/@sinclair/typebox": { "node_modules/@sinclair/typebox": {
"version": "0.24.20", "version": "0.24.22",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.20.tgz", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.22.tgz",
"integrity": "sha512-kVaO5aEFZb33nPMTZBxiPEkY+slxiPtqC7QX8f9B3eGOMBvEfuMfxp9DSTTCsRJPumPKjrge4yagyssO4q6qzQ==", "integrity": "sha512-JsBe3cOFpNZ6yjBYnXKhcENWy5qZE3PQZwExQ5ksA/h8qp4bwwxFmy07A6bC2R6qv6+RF3SfrbQTskTwYNTXUQ==",
"dev": true "dev": true
}, },
"node_modules/@types/expect": { "node_modules/@types/expect": {
@@ -225,9 +227,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.6.1", "version": "18.6.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.2.tgz",
"integrity": "sha512-z+2vB6yDt1fNwKOeGbckpmirO+VBDuQqecXkgeIqDlaOtmKn6hPR/viQ8cxCfqLU4fTlvM3+YjM367TukWdxpg==", "integrity": "sha512-KcfkBq9H4PI6Vpu5B/KoPeuVDAbmi+2mDBqGPGUgoL7yXQtcWGu2vJWmmRkneWK3Rh0nIAX192Aa87AqKHYChQ==",
"dev": true "dev": true
}, },
"node_modules/@types/qs": { "node_modules/@types/qs": {
@@ -1137,8 +1139,7 @@
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
"dev": true
}, },
"node_modules/js-yaml": { "node_modules/js-yaml": {
"version": "4.1.0", "version": "4.1.0",
@@ -1231,6 +1232,17 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/make-error": { "node_modules/make-error": {
"version": "1.3.6", "version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@@ -1382,6 +1394,14 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.12.2", "version": "1.12.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
@@ -1502,6 +1522,21 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/prop-types/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/psl": { "node_modules/psl": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -1539,6 +1574,28 @@
"safe-buffer": "^5.1.0" "safe-buffer": "^5.1.0"
} }
}, },
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"dependencies": {
"loose-envify": "^1.1.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-infinite-scroller": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/react-infinite-scroller/-/react-infinite-scroller-1.2.6.tgz",
"integrity": "sha512-mGdMyOD00YArJ1S1F3TVU9y4fGSfVVl6p5gh/Vt4u99CJOptfVu/q5V/Wlle72TMgYlBwIhbxK5wF0C/R33PXQ==",
"dependencies": {
"prop-types": "^15.5.8"
},
"peerDependencies": {
"react": "^0.14.9 || ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-is": { "node_modules/react-is": {
"version": "18.2.0", "version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
@@ -2159,9 +2216,9 @@
} }
}, },
"@sinclair/typebox": { "@sinclair/typebox": {
"version": "0.24.20", "version": "0.24.22",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.20.tgz", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.22.tgz",
"integrity": "sha512-kVaO5aEFZb33nPMTZBxiPEkY+slxiPtqC7QX8f9B3eGOMBvEfuMfxp9DSTTCsRJPumPKjrge4yagyssO4q6qzQ==", "integrity": "sha512-JsBe3cOFpNZ6yjBYnXKhcENWy5qZE3PQZwExQ5ksA/h8qp4bwwxFmy07A6bC2R6qv6+RF3SfrbQTskTwYNTXUQ==",
"dev": true "dev": true
}, },
"@types/expect": { "@types/expect": {
@@ -2210,9 +2267,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "18.6.1", "version": "18.6.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.2.tgz",
"integrity": "sha512-z+2vB6yDt1fNwKOeGbckpmirO+VBDuQqecXkgeIqDlaOtmKn6hPR/viQ8cxCfqLU4fTlvM3+YjM367TukWdxpg==", "integrity": "sha512-KcfkBq9H4PI6Vpu5B/KoPeuVDAbmi+2mDBqGPGUgoL7yXQtcWGu2vJWmmRkneWK3Rh0nIAX192Aa87AqKHYChQ==",
"dev": true "dev": true
}, },
"@types/qs": { "@types/qs": {
@@ -2897,8 +2954,7 @@
"js-tokens": { "js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
"dev": true
}, },
"js-yaml": { "js-yaml": {
"version": "4.1.0", "version": "4.1.0",
@@ -2970,6 +3026,14 @@
"is-unicode-supported": "^0.1.0" "is-unicode-supported": "^0.1.0"
} }
}, },
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"make-error": { "make-error": {
"version": "1.3.6", "version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@@ -3079,6 +3143,11 @@
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
"dev": true "dev": true
}, },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
"object-inspect": { "object-inspect": {
"version": "1.12.2", "version": "1.12.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
@@ -3167,6 +3236,23 @@
} }
} }
}, },
"prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
},
"dependencies": {
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}
}
},
"psl": { "psl": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -3195,6 +3281,22 @@
"safe-buffer": "^5.1.0" "safe-buffer": "^5.1.0"
} }
}, },
"react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"requires": {
"loose-envify": "^1.1.0"
}
},
"react-infinite-scroller": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/react-infinite-scroller/-/react-infinite-scroller-1.2.6.tgz",
"integrity": "sha512-mGdMyOD00YArJ1S1F3TVU9y4fGSfVVl6p5gh/Vt4u99CJOptfVu/q5V/Wlle72TMgYlBwIhbxK5wF0C/R33PXQ==",
"requires": {
"prop-types": "^15.5.8"
}
},
"react-is": { "react-is": {
"version": "18.2.0", "version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",

View File

@@ -1,6 +1,6 @@
{ {
"name": "plex-api-oauth", "name": "plex-api-oauth",
"version": "1.1.9", "version": "1.1.46",
"description": "An NPM Module designed to make Plex Media Server and plex.tv API calls easier to implement in JavaScript and React projects", "description": "An NPM Module designed to make Plex Media Server and plex.tv API calls easier to implement in JavaScript and React projects",
"main": "./src/index.js", "main": "./src/index.js",
"type": "module", "type": "module",
@@ -13,6 +13,8 @@
"axios": "^0.27.2", "axios": "^0.27.2",
"plex-oauth": "^2.0.2", "plex-oauth": "^2.0.2",
"qs": "^6.11.0", "qs": "^6.11.0",
"react": "^18.2.0",
"react-infinite-scroller": "^1.2.6",
"ts-mocha": "^10.0.0", "ts-mocha": "^10.0.0",
"typescript": "^4.7.4", "typescript": "^4.7.4",
"uuid": "^8.3.2" "uuid": "^8.3.2"

View File

View File

@@ -2,6 +2,8 @@ import { PlexOauth } from "plex-oauth";
import { v4 } from "uuid"; import { v4 } from "uuid";
import axios from "axios"; import axios from "axios";
import qs from "qs"; import qs from "qs";
import InfiniteScroll from "react-infinite-scroller";
import { useState, useEffect } from "react";
export class PlexAPIOAuth { export class PlexAPIOAuth {
plexClientInformation; plexClientInformation;
@@ -16,6 +18,7 @@ export class PlexAPIOAuth {
plexServers; plexServers;
plexLibraries; plexLibraries;
plexDevices; plexDevices;
cancelToken;
constructor( constructor(
clientId = "", clientId = "",
product = "Plex-API-OAuth", product = "Plex-API-OAuth",
@@ -27,7 +30,8 @@ export class PlexAPIOAuth {
plexTVUserData = {}, plexTVUserData = {},
plexServers = [], plexServers = [],
plexLibraries = [], plexLibraries = [],
plexDevices = [] plexDevices = [],
cancelToken = null
) { ) {
this.clientId = clientId; this.clientId = clientId;
this.product = product; this.product = product;
@@ -40,6 +44,7 @@ export class PlexAPIOAuth {
this.plexServers = plexServers; this.plexServers = plexServers;
this.plexDevices = plexDevices; this.plexDevices = plexDevices;
this.plexLibraries = plexLibraries; this.plexLibraries = plexLibraries;
this.cancelToken = cancelToken;
this.plexClientInformation = { this.plexClientInformation = {
clientIdentifier: this.clientId, // This is a unique identifier used to identify your app with Plex. - If none is provided a new one is generated and saved locally clientIdentifier: this.clientId, // This is a unique identifier used to identify your app with Plex. - If none is provided a new one is generated and saved locally
@@ -138,18 +143,51 @@ export class PlexAPIOAuth {
); );
} }
fnBrowserDetect() {
let userAgent = navigator.userAgent;
let browserName;
if (userAgent.match(/chrome|chromium|crios/i)) {
browserName = "chrome";
} else if (userAgent.match(/firefox|fxios/i)) {
browserName = "firefox";
} else if (userAgent.match(/safari/i)) {
browserName = "safari";
} else if (userAgent.match(/opr\//i)) {
browserName = "opera";
} else if (userAgent.match(/edg/i)) {
browserName = "edge";
} else {
browserName = "No browser detection";
}
return browserName;
}
GenerateClientId() { GenerateClientId() {
this.clientId = v4(); this.clientId = v4();
this.plexClientInformation.clientIdentifier = this.clientId; this.plexClientInformation.clientIdentifier = this.clientId;
} }
async PlexLogin() { GenerateClientInformation() {
if ( if (typeof navigator !== "undefined") {
this.clientId == null || this.clientId = v4();
this.plexClientInformation.clientIdentifier == null this.product = this.fnBrowserDetect;
) { this.device = navigator.userAgentData.platform;
this.GenerateClientId(); this.version = navigator.userAgentData.brands[0].version;
this.plexClientInformation = {
clientIdentifier: this.clientId,
product: this.product,
device: this.device,
version: this.version,
forwardUrl: this.forwardUrl,
platform: this.platform,
};
console.log("Client Information generated successfully");
} }
throw "Unable to detect Client";
}
async PlexLogin() {
var plexOauth = new PlexOauth(this.plexClientInformation); var plexOauth = new PlexOauth(this.plexClientInformation);
let data = await plexOauth.requestHostedLoginURL().catch((err) => { let data = await plexOauth.requestHostedLoginURL().catch((err) => {
throw err; throw err;
@@ -205,45 +243,34 @@ export class PlexAPIOAuth {
headers: { accept: "application/json" }, headers: { accept: "application/json" },
}).catch(function (error) { }).catch(function (error) {
if (error.response) { 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.data);
console.log(error.response.status); console.log(error.response.status);
console.log(error.response.headers); console.log(error.response.headers);
} else if (error.request) { } 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(error.request);
} else { } else {
// Something happened in setting up the request that triggered an Error
console.log("Error", error.message); console.log("Error", error.message);
} }
console.log(error.config); console.log(error.config);
}); });
//console.log(response);
//console.log(response.status);
if (response.status === 200) { if (response.status === 200) {
//console.log("Authentican Token Validated Successfully");
this.plexTVUserData = response.data; this.plexTVUserData = response.data;
//console.log("Populated User Data Successfully");
return this.plexTVUserData; return this.plexTVUserData;
} }
if (response.status === 401) { if (response.status === 401) {
//console.log("Authentican Token Failed Validation");
return null; return null;
} }
} }
async GetPlexServers(searchParams = null) { async GetPlexServers(searchParams = {}, filter = {}) {
let serverArray = []; let serverArray = [];
let response = await axios({ let response = await axios({
method: "GET", method: "GET",
url: url:
"https://plex.tv/api/v2/resources?" + "https://plex.tv/api/v2/resources?" +
qs.stringify(searchParams) +
qs.stringify({ qs.stringify({
...searchParams,
includeHttps: 1, includeHttps: 1,
includeRelay: 1, includeRelay: 1,
includeIPv6: 1, includeIPv6: 1,
@@ -257,9 +284,15 @@ export class PlexAPIOAuth {
throw err; throw err;
}); });
this.plexDevices = response.data; this.plexDevices = response.data;
for (const server of response.data.filter( for (const server of response.data
(Obj) => Obj.product === "Plex Media Server" .filter((Obj) => Obj.product === "Plex Media Server")
)) { .filter((Obj) => {
for (var key in filter) {
if (Obj[key] === filter[key]) {
return Obj;
}
}
})) {
let localConnection = null; let localConnection = null;
let serverCapabilities = null; let serverCapabilities = null;
let preferredConnection = server.connections.filter( let preferredConnection = server.connections.filter(
@@ -525,9 +558,9 @@ export class PlexAPIOAuth {
} }
async GetPlexMovies( async GetPlexMovies(
searchParams = {},
servers = this.plexServers, servers = this.plexServers,
libraries = this.plexLibraries, libraries = this.plexLibraries
searchParams = null
) { ) {
let movieLibraryContent = []; let movieLibraryContent = [];
for (const server of servers) { for (const server of servers) {
@@ -542,19 +575,17 @@ export class PlexAPIOAuth {
)) { )) {
let response = await axios({ let response = await axios({
method: "GET", method: "GET",
url: url: connectionUri.uri + "/library/sections/" + library?.key + "/all",
connectionUri.uri + params: {
"/library/sections/" +
library?.key +
"/all?" +
qs.stringify(searchParams) +
qs.stringify({
type: 1, type: 1,
...searchParams,
"X-Plex-Token": server?.accessToken, "X-Plex-Token": server?.accessToken,
}), },
cancelToken: axios.CancelToken((c) => (this.cancelToken = c)),
headers: { accept: "application/json" }, headers: { accept: "application/json" },
}).catch((err) => { }).catch((e) => {
throw err; if (axios.isCancel(e)) return;
throw e;
}); });
for (const data of response.data.MediaContainer.Metadata) { for (const data of response.data.MediaContainer.Metadata) {
movieLibraryContent.push({ movieLibraryContent.push({
@@ -595,9 +626,9 @@ export class PlexAPIOAuth {
} }
async GetPlexArtists( async GetPlexArtists(
searchParams = {},
servers = this.plexServers, servers = this.plexServers,
libraries = this.plexLibraries, libraries = this.plexLibraries
searchParams = null
) { ) {
let artistLibraryContent = []; let artistLibraryContent = [];
for (const server of servers) { for (const server of servers) {
@@ -612,16 +643,14 @@ export class PlexAPIOAuth {
)) { )) {
let response = await axios({ let response = await axios({
method: "GET", method: "GET",
url: url: connectionUri.uri + "/library/sections/" + library?.key + "/all",
connectionUri.uri +
"/library/sections/" +
library?.key +
"/all?" +
qs.stringify(searchParams) +
qs.stringify({
"X-Plex-Token": server.accessToken,
}),
headers: { accept: "application/json" }, headers: { accept: "application/json" },
params: {
type: 8,
...searchParams,
"X-Plex-Token": server.accessToken,
},
cancelToken: axios.CancelToken((c) => (this.cancelToken = c)),
}).catch((err) => { }).catch((err) => {
throw err; throw err;
}); });
@@ -651,9 +680,9 @@ export class PlexAPIOAuth {
} }
async GetPlexAlbums( async GetPlexAlbums(
searchParams = {},
servers = this.plexServers, servers = this.plexServers,
libraries = this.plexLibraries, libraries = this.plexLibraries
searchParams = null
) { ) {
let albumLibraryContent = []; let albumLibraryContent = [];
for (const server of servers) { for (const server of servers) {
@@ -668,17 +697,14 @@ export class PlexAPIOAuth {
)) { )) {
let response = await axios({ let response = await axios({
method: "GET", method: "GET",
url: url: connectionUri.uri + "/library/sections/" + library?.key + "/all",
connectionUri.uri +
"/library/sections/" +
library?.key +
"/all?" +
qs.stringify(searchParams) +
qs.stringify({
type: 9,
"X-Plex-Token": server?.accessToken,
}),
headers: { accept: "application/json" }, headers: { accept: "application/json" },
params: {
type: 9,
...searchParams,
"X-Plex-Token": server.accessToken,
},
cancelToken: axios.CancelToken((c) => (this.cancelToken = c)),
}).catch((err) => { }).catch((err) => {
throw err; throw err;
}); });
@@ -713,9 +739,9 @@ export class PlexAPIOAuth {
} }
async GetPlexSongs( async GetPlexSongs(
searchParams = {},
servers = this.plexServers, servers = this.plexServers,
libraries = this.plexLibraries, libraries = this.plexLibraries
searchParams = null
) { ) {
let songLibraryContent = []; let songLibraryContent = [];
for (const server of servers) { for (const server of servers) {
@@ -730,20 +756,20 @@ export class PlexAPIOAuth {
)) { )) {
let response = await axios({ let response = await axios({
method: "GET", method: "GET",
url: url: connectionUri.uri + "/library/sections/" + library?.key + "/all",
connectionUri.uri +
"/library/sections/" +
library?.key +
"/all?" +
qs.stringify(searchParams) +
qs.stringify({
type: 10,
"X-Plex-Token": server?.accessToken,
}),
headers: { accept: "application/json" }, headers: { accept: "application/json" },
params: {
type: 10,
...searchParams,
"X-Plex-Token": server.accessToken,
},
cancelToken: axios.CancelToken((c) => (this.cancelToken = c)),
}).catch((err) => { }).catch((err) => {
if (axios.isCancel(err)) return;
throw err; throw err;
}); });
console.log(response);
try {
for (const data of response.data.MediaContainer.Metadata) { for (const data of response.data.MediaContainer.Metadata) {
songLibraryContent.push({ songLibraryContent.push({
server: server, server: server,
@@ -775,15 +801,16 @@ export class PlexAPIOAuth {
Media: data.Media, Media: data.Media,
}); });
} }
} catch {}
} }
} }
return songLibraryContent; return songLibraryContent;
} }
async GetPlexShows( async GetPlexShows(
searchParams = {},
servers = this.plexServers, servers = this.plexServers,
libraries = this.plexLibraries, libraries = this.plexLibraries
searchParams = null
) { ) {
let tvShowLibraryContent = []; let tvShowLibraryContent = [];
for (const server of servers) { for (const server of servers) {
@@ -802,13 +829,14 @@ export class PlexAPIOAuth {
connectionUri.uri + connectionUri.uri +
"/library/sections/" + "/library/sections/" +
showLibrary?.key + showLibrary?.key +
"/all?" + "/all",
qs.stringify(searchParams) +
qs.stringify({
type: 2,
"X-Plex-Token": server?.accessToken,
}),
headers: { accept: "application/json" }, headers: { accept: "application/json" },
params: {
type: 2,
...searchParams,
"X-Plex-Token": server?.accessToken,
},
cancelToken: axios.CancelToken((c) => (this.cancelToken = c)),
}).catch((err) => { }).catch((err) => {
throw err; throw err;
}); });
@@ -854,9 +882,9 @@ export class PlexAPIOAuth {
} }
async GetPlexSeasons( async GetPlexSeasons(
searchParams = {},
servers = this.plexServers, servers = this.plexServers,
libraries = this.plexLibraries, libraries = this.plexLibraries
searchParams = null
) { ) {
let seasonArray = []; let seasonArray = [];
for (const server of servers) { for (const server of servers) {
@@ -875,13 +903,14 @@ export class PlexAPIOAuth {
connectionUri.uri + connectionUri.uri +
"/library/sections/" + "/library/sections/" +
showLibrary?.key + showLibrary?.key +
"/all?" + "/all",
qs.stringify(searchParams) +
qs.stringify({
type: 3,
"X-Plex-Token": server?.accessToken,
}),
headers: { accept: "application/json" }, headers: { accept: "application/json" },
params: {
type: 3,
...searchParams,
"X-Plex-Token": server?.accessToken,
},
cancelToken: axios.CancelToken((c) => (this.cancelToken = c)),
}).catch((err) => { }).catch((err) => {
throw err; throw err;
}); });
@@ -894,9 +923,9 @@ export class PlexAPIOAuth {
} }
async GetPlexEpisodes( async GetPlexEpisodes(
searchParams = {},
servers = this.plexServers, servers = this.plexServers,
libraries = this.plexLibraries, libraries = this.plexLibraries
searchParams = null
) { ) {
let episodeLibrary = []; let episodeLibrary = [];
for (const server of servers) { for (const server of servers) {
@@ -915,13 +944,14 @@ export class PlexAPIOAuth {
connectionUri.uri + connectionUri.uri +
"/library/sections/" + "/library/sections/" +
showLibrary?.key + showLibrary?.key +
"/all?" + "/all",
qs.stringify(searchParams) +
qs.stringify({
type: 4,
"X-Plex-Token": server?.accessToken,
}),
headers: { accept: "application/json" }, headers: { accept: "application/json" },
params: {
type: 4,
...searchParams,
"X-Plex-Token": server?.accessToken,
},
cancelToken: axios.CancelToken((c) => (this.cancelToken = c)),
}).catch((err) => { }).catch((err) => {
throw err; throw err;
}); });
@@ -932,4 +962,82 @@ export class PlexAPIOAuth {
} }
return episodeLibrary; return episodeLibrary;
} }
GetLibraryPages(
libraryType = "",
query = null,
pageNumber = 0,
chunkSize = 50
) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
const [items, setItems] = useState([]);
const [hasMore, setHasMore] = useState(false);
let data = null;
useEffect(() => {
setItems([]);
}, [query, libraryType]);
useEffect(() => {
setLoading(true);
setError(false);
let searchParams = {
"X-Plex-Container-Start": pageNumber * chunkSize,
"X-Plex-Container-Size": chunkSize,
};
if (query !== null) {
searchParams = {
title: query,
"X-Plex-Container-Start": pageNumber * chunkSize,
"X-Plex-Container-Size": chunkSize,
};
}
switch (libraryType) {
case "artists":
data = this.GetPlexArtists(searchParams);
break;
case "albums":
data = this.GetPlexAlbums(searchParams);
break;
case "songs":
data = this.GetPlexSongs(searchParams);
break;
case "shows":
data = this.GetPlexShows(searchParams);
break;
case "seasons":
data = this.GetPlexSeasons(searchParams);
break;
case "episodes":
data = this.GetPlexEpisodes(searchParams);
break;
case "movies":
data = this.GetPlexMovies(searchParams);
break;
}
try {
data
.then((finalData) => {
setItems((previousData) => {
return [...previousData, ...finalData];
});
setHasMore(finalData.length > 0);
setLoading(false);
console.log({ loading, error, items, hasMore });
})
.catch((e) => {
setError(e);
});
} catch {}
return () => {
if (this.cancelToken) {
this.cancelToken();
}
};
}, [query, pageNumber]);
return { loading, error, items, hasMore };
}
} }

7
test/index.js Normal file
View File

@@ -0,0 +1,7 @@
import { PlexAPIOAuth } from "../src/index.js";
const PlexSession = new PlexAPIOAuth();
PlexSession.GenerateClientId();
await PlexSession.PlexLogin();

View File

@@ -29,7 +29,7 @@ describe("Unit Tests", function () {
}); });
it("Get Plex Servers", async function () { it("Get Plex Servers", async function () {
this.timeout(12000); this.timeout(12000);
let response = await PlexSession.GetPlexServers({ owned: true }); let response = await PlexSession.GetPlexServers({}, { owned: true });
assert.notEqual(PlexSession.plexServers, emptyArray); assert.notEqual(PlexSession.plexServers, emptyArray);
assert.notEqual(PlexSession.plexServers, null); assert.notEqual(PlexSession.plexServers, null);
assert.notEqual(PlexSession.plexServers, undefined); assert.notEqual(PlexSession.plexServers, undefined);
@@ -83,7 +83,10 @@ describe("Unit Tests", function () {
}); });
it("Get Plex Movies", async function () { it("Get Plex Movies", async function () {
this.timeout(10000); this.timeout(10000);
let response = await PlexSession.GetPlexMovies(); let response = await PlexSession.GetPlexMovies({
"X-Plex-Container-Start": 0,
"X-Plex-Container-Size": 2,
});
assert.notEqual(response, emptyArray); assert.notEqual(response, emptyArray);
assert.notEqual(response, null); assert.notEqual(response, null);
assert.notEqual(response, undefined); assert.notEqual(response, undefined);
@@ -92,7 +95,10 @@ describe("Unit Tests", function () {
}); });
it("Get Plex Shows", async function () { it("Get Plex Shows", async function () {
this.timeout(10000); this.timeout(10000);
let response = await PlexSession.GetPlexShows(); let response = await PlexSession.GetPlexShows({
"X-Plex-Container-Start": 0,
"X-Plex-Container-Size": 2,
});
assert.notEqual(response, emptyArray); assert.notEqual(response, emptyArray);
assert.notEqual(response, null); assert.notEqual(response, null);
assert.notEqual(response, undefined); assert.notEqual(response, undefined);
@@ -101,7 +107,10 @@ describe("Unit Tests", function () {
}); });
it("Get Plex Seasons", async function () { it("Get Plex Seasons", async function () {
this.timeout(10000); this.timeout(10000);
let response = await PlexSession.GetPlexSeasons(); let response = await PlexSession.GetPlexSeasons({
"X-Plex-Container-Start": 0,
"X-Plex-Container-Size": 2,
});
assert.notEqual(response, emptyArray); assert.notEqual(response, emptyArray);
assert.notEqual(response, null); assert.notEqual(response, null);
assert.notEqual(response, undefined); assert.notEqual(response, undefined);
@@ -110,7 +119,10 @@ describe("Unit Tests", function () {
}); });
it("Get Plex Episodes", async function () { it("Get Plex Episodes", async function () {
this.timeout(20000); this.timeout(20000);
let response = await PlexSession.GetPlexEpisodes(); let response = await PlexSession.GetPlexEpisodes({
"X-Plex-Container-Start": 0,
"X-Plex-Container-Size": 2,
});
assert.notEqual(response, emptyArray); assert.notEqual(response, emptyArray);
assert.notEqual(response, null); assert.notEqual(response, null);
assert.notEqual(response, undefined); assert.notEqual(response, undefined);
@@ -119,7 +131,10 @@ describe("Unit Tests", function () {
}); });
it("Get Plex Artists", async function () { it("Get Plex Artists", async function () {
this.timeout(10000); this.timeout(10000);
let response = await PlexSession.GetPlexArtists(); let response = await PlexSession.GetPlexArtists({
"X-Plex-Container-Start": 0,
"X-Plex-Container-Size": 2,
});
assert.notEqual(response, emptyArray); assert.notEqual(response, emptyArray);
assert.notEqual(response, null); assert.notEqual(response, null);
assert.notEqual(response, undefined); assert.notEqual(response, undefined);
@@ -128,7 +143,10 @@ describe("Unit Tests", function () {
}); });
it("Get Plex Albums", async function () { it("Get Plex Albums", async function () {
this.timeout(10000); this.timeout(10000);
let response = await PlexSession.GetPlexAlbums(); let response = await PlexSession.GetPlexAlbums({
"X-Plex-Container-Start": 0,
"X-Plex-Container-Size": 2,
});
assert.notEqual(response, emptyArray); assert.notEqual(response, emptyArray);
assert.notEqual(response, null); assert.notEqual(response, null);
assert.notEqual(response, undefined); assert.notEqual(response, undefined);
@@ -137,11 +155,23 @@ describe("Unit Tests", function () {
}); });
it("Get Plex Songs", async function () { it("Get Plex Songs", async function () {
this.timeout(20000); this.timeout(20000);
let response = await PlexSession.GetPlexSongs(); let response = await PlexSession.GetPlexSongs({
"X-Plex-Container-Start": 0,
"X-Plex-Container-Size": 2,
});
assert.notEqual(response, emptyArray); assert.notEqual(response, emptyArray);
assert.notEqual(response, null); assert.notEqual(response, null);
assert.notEqual(response, undefined); assert.notEqual(response, undefined);
// console.log("Plex Songs"); // console.log("Plex Songs");
// console.log(response); // console.log(response);
}); });
it("Get Plex Songs Paged", async function () {
this.timeout(20000);
let response = PlexSession.GetLibraryPages("songs");
assert.notEqual(response, emptyArray);
assert.notEqual(response, null);
assert.notEqual(response, undefined);
//console.log("Plex Songs Paged");
//console.log(response);
});
}); });