@ctrl/plex 1.5.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/src/alert.d.ts +12 -0
- package/dist/src/alert.js +29 -0
- package/dist/src/alert.types.d.ts +59 -0
- package/dist/src/alert.types.js +1 -0
- package/dist/{base → src/base}/partialPlexObject.d.ts +18 -12
- package/dist/{base → src/base}/partialPlexObject.js +32 -23
- package/dist/{base → src/base}/playable.d.ts +2 -2
- package/dist/src/base/playable.js +8 -0
- package/dist/{base → src/base}/plexObject.d.ts +8 -1
- package/dist/{base → src/base}/plexObject.js +21 -18
- package/dist/{baseFunctionality.d.ts → src/baseFunctionality.d.ts} +17 -1
- package/dist/{baseFunctionality.js → src/baseFunctionality.js} +7 -15
- package/dist/{client.d.ts → src/client.d.ts} +2 -2
- package/dist/{client.js → src/client.js} +12 -20
- package/dist/src/client.types.js +1 -0
- package/dist/src/config.js +35 -0
- package/dist/src/exceptions.d.ts +20 -0
- package/dist/src/exceptions.js +40 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.js +11 -0
- package/dist/{library.d.ts → src/library.d.ts} +207 -21
- package/dist/{library.js → src/library.js} +349 -133
- package/dist/{library.types.d.ts → src/library.types.d.ts} +71 -1
- package/dist/src/library.types.js +1 -0
- package/dist/{media.d.ts → src/media.d.ts} +16 -4
- package/dist/{media.js → src/media.js} +42 -49
- package/dist/src/media.types.d.ts +7 -0
- package/dist/src/media.types.js +1 -0
- package/dist/{myplex.d.ts → src/myplex.d.ts} +16 -6
- package/dist/{myplex.js → src/myplex.js} +71 -57
- package/dist/src/myplex.types.js +10 -0
- package/dist/src/playlist.d.ts +75 -0
- package/dist/src/playlist.js +142 -0
- package/dist/src/playlist.types.d.ts +17 -0
- package/dist/src/playlist.types.js +1 -0
- package/dist/{search.d.ts → src/search.d.ts} +5 -4
- package/dist/{search.js → src/search.js} +16 -20
- package/dist/src/search.types.js +1 -0
- package/dist/{server.d.ts → src/server.d.ts} +22 -10
- package/dist/{server.js → src/server.js} +65 -50
- package/dist/src/server.types.js +1 -0
- package/dist/src/settings.d.ts +79 -0
- package/dist/src/settings.js +160 -0
- package/dist/{util.d.ts → src/util.d.ts} +2 -1
- package/dist/{util.js → src/util.js} +8 -12
- package/dist/{video.d.ts → src/video.d.ts} +39 -61
- package/dist/{video.js → src/video.js} +110 -93
- package/dist/{video.types.d.ts → src/video.types.d.ts} +1 -1
- package/dist/src/video.types.js +6 -0
- package/package.json +46 -44
- package/dist/base/playable.js +0 -12
- package/dist/client.types.js +0 -2
- package/dist/config.js +0 -41
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -23
- package/dist/library.types.js +0 -2
- package/dist/myplex.types.js +0 -13
- package/dist/playlist.d.ts +0 -7
- package/dist/playlist.js +0 -19
- package/dist/search.types.js +0 -2
- package/dist/server.types.js +0 -2
- package/dist/video.types.js +0 -9
- /package/dist/{client.types.d.ts → src/client.types.d.ts} +0 -0
- /package/dist/{config.d.ts → src/config.d.ts} +0 -0
- /package/dist/{myplex.types.d.ts → src/myplex.types.d.ts} +0 -0
- /package/dist/{search.types.d.ts → src/search.types.d.ts} +0 -0
- /package/dist/{server.types.d.ts → src/server.types.d.ts} +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
2
|
import { URL, URLSearchParams } from 'url';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { Optimized } from './media';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
3
|
+
import { PlexClient } from './client.js';
|
|
4
|
+
import { Hub, Library } from './library.js';
|
|
5
|
+
import { Optimized } from './media.js';
|
|
6
|
+
import { MyPlexAccount } from './myplex.js';
|
|
7
|
+
import { Agent, SEARCHTYPES } from './search.js';
|
|
8
|
+
import { HistoryMetadatum } from './server.types.js';
|
|
9
|
+
import { Settings } from './settings.js';
|
|
9
10
|
/**
|
|
10
11
|
* This is the main entry point to interacting with a Plex server. It allows you to
|
|
11
12
|
* list connected clients, browse your library sections and perform actions such as
|
|
@@ -16,7 +17,7 @@ import { MyPlexAccount } from './myplex';
|
|
|
16
17
|
export declare class PlexServer {
|
|
17
18
|
readonly baseurl: string;
|
|
18
19
|
readonly token: string;
|
|
19
|
-
readonly timeout?: number
|
|
20
|
+
readonly timeout?: number;
|
|
20
21
|
key: string;
|
|
21
22
|
/** True if server allows camera upload */
|
|
22
23
|
allowCameraUpload: boolean;
|
|
@@ -51,7 +52,7 @@ export declare class PlexServer {
|
|
|
51
52
|
*/
|
|
52
53
|
hubSearch: boolean;
|
|
53
54
|
/** Unique ID for this server (looks like an md5) */
|
|
54
|
-
machineIdentifier
|
|
55
|
+
machineIdentifier?: string;
|
|
55
56
|
/**
|
|
56
57
|
* True if `multiusers <https!://support.plex.tv/hc/en-us/articles/200250367-Multi-User-Support>`_ are enabled.
|
|
57
58
|
*/
|
|
@@ -128,8 +129,9 @@ export declare class PlexServer {
|
|
|
128
129
|
/** Unknown */
|
|
129
130
|
pushNotifications: boolean;
|
|
130
131
|
_library?: Library;
|
|
132
|
+
_settings?: Settings;
|
|
131
133
|
private _myPlexAccount?;
|
|
132
|
-
constructor(baseurl: string, token: string, timeout?: number
|
|
134
|
+
constructor(baseurl: string, token: string, timeout?: number);
|
|
133
135
|
agents(mediaType?: number | string): Promise<Agent[]>;
|
|
134
136
|
connect(): Promise<void>;
|
|
135
137
|
/**
|
|
@@ -175,6 +177,7 @@ export declare class PlexServer {
|
|
|
175
177
|
* @param librarySectionId request history for a specific library section ID.
|
|
176
178
|
*/
|
|
177
179
|
history(maxresults?: number, mindate?: Date, ratingKey?: number | string, accountId?: number | string, librarySectionId?: number | string): Promise<HistoryMetadatum[]>;
|
|
180
|
+
settings(): Promise<Settings>;
|
|
178
181
|
/**
|
|
179
182
|
* Returns a :class:`~plexapi.myplex.MyPlexAccount` object using the same
|
|
180
183
|
* token to access this server. If you are not the owner of this PlexServer
|
|
@@ -189,6 +192,15 @@ export declare class PlexServer {
|
|
|
189
192
|
* if either includeToken is True or TODO: CONFIG.log.show_secrets is 'true'.
|
|
190
193
|
*/
|
|
191
194
|
url(key: string, includeToken?: boolean, params?: URLSearchParams): URL;
|
|
195
|
+
/**
|
|
196
|
+
* Build the Plex Web URL for the object.
|
|
197
|
+
* @param base The base URL before the fragment (``#!``).
|
|
198
|
+
* Default is https://app.plex.tv/desktop.
|
|
199
|
+
* @param endpoint The Plex Web URL endpoint.
|
|
200
|
+
* None for server, 'playlist' for playlists, 'details' for all other media types.
|
|
201
|
+
*/
|
|
202
|
+
_buildWebURL(base?: string, endpoint?: string, params?: URLSearchParams): string;
|
|
203
|
+
_uriRoot(): string;
|
|
192
204
|
private _headers;
|
|
193
205
|
private _loadData;
|
|
194
206
|
/**
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const baseFunctionality_1 = require("./baseFunctionality");
|
|
12
|
-
const media_1 = require("./media");
|
|
13
|
-
const search_1 = require("./search");
|
|
14
|
-
const client_1 = require("./client");
|
|
15
|
-
const myplex_1 = require("./myplex");
|
|
1
|
+
import { URL, URLSearchParams } from 'url';
|
|
2
|
+
import got from 'got';
|
|
3
|
+
import { fetchItem, fetchItems } from './baseFunctionality.js';
|
|
4
|
+
import { PlexClient } from './client.js';
|
|
5
|
+
import { BASE_HEADERS, TIMEOUT, X_PLEX_CONTAINER_SIZE } from './config.js';
|
|
6
|
+
import { Hub, Library } from './library.js';
|
|
7
|
+
import { Optimized } from './media.js';
|
|
8
|
+
import { MyPlexAccount } from './myplex.js';
|
|
9
|
+
import { Agent, SEARCHTYPES } from './search.js';
|
|
10
|
+
import { Settings } from './settings.js';
|
|
16
11
|
/**
|
|
17
12
|
* This is the main entry point to interacting with a Plex server. It allows you to
|
|
18
13
|
* list connected clients, browse your library sections and perform actions such as
|
|
@@ -20,7 +15,7 @@ const myplex_1 = require("./myplex");
|
|
|
20
15
|
* server, or simply want to access your server with your username and password, you
|
|
21
16
|
* can also create an PlexServer instance from :class:`~plexapi.myplex.MyPlexAccount`.
|
|
22
17
|
*/
|
|
23
|
-
class PlexServer {
|
|
18
|
+
export class PlexServer {
|
|
24
19
|
constructor(baseurl, token, timeout) {
|
|
25
20
|
this.baseurl = baseurl;
|
|
26
21
|
this.token = token;
|
|
@@ -32,7 +27,7 @@ class PlexServer {
|
|
|
32
27
|
if (mediaType) {
|
|
33
28
|
key += `?mediaType=${mediaType}`;
|
|
34
29
|
}
|
|
35
|
-
return
|
|
30
|
+
return fetchItems(this, key, undefined, Agent, this);
|
|
36
31
|
}
|
|
37
32
|
async connect() {
|
|
38
33
|
const data = await this.query(this.key, undefined, undefined, this.timeout);
|
|
@@ -53,13 +48,13 @@ class PlexServer {
|
|
|
53
48
|
return this._library;
|
|
54
49
|
}
|
|
55
50
|
try {
|
|
56
|
-
const data = await this.query(
|
|
57
|
-
this._library = new
|
|
51
|
+
const data = await this.query(Library.key);
|
|
52
|
+
this._library = new Library(this, data.MediaContainer);
|
|
58
53
|
}
|
|
59
|
-
catch
|
|
54
|
+
catch {
|
|
60
55
|
// TODO: validate error type, also TODO figure out how this is used
|
|
61
56
|
const data = await this.query('/library/sections/');
|
|
62
|
-
this._library = new
|
|
57
|
+
this._library = new Library(this, data.MediaContainer);
|
|
63
58
|
}
|
|
64
59
|
return this._library;
|
|
65
60
|
}
|
|
@@ -81,13 +76,13 @@ class PlexServer {
|
|
|
81
76
|
async search(query, mediatype, limit) {
|
|
82
77
|
const params = { query };
|
|
83
78
|
if (mediatype) {
|
|
84
|
-
params.section =
|
|
79
|
+
params.section = SEARCHTYPES[mediatype].toString();
|
|
85
80
|
}
|
|
86
81
|
if (limit) {
|
|
87
82
|
params.limit = limit.toString();
|
|
88
83
|
}
|
|
89
|
-
const key = '/hubs/search?' + new
|
|
90
|
-
const hubs = await
|
|
84
|
+
const key = '/hubs/search?' + new URLSearchParams(params).toString();
|
|
85
|
+
const hubs = await fetchItems(this, key, undefined, Hub, this);
|
|
91
86
|
return hubs;
|
|
92
87
|
}
|
|
93
88
|
/**
|
|
@@ -107,14 +102,14 @@ class PlexServer {
|
|
|
107
102
|
requestHeaders.Authorization = `Basic ${credentials}`;
|
|
108
103
|
}
|
|
109
104
|
const url = this.url(path);
|
|
110
|
-
const response = await
|
|
105
|
+
const response = await got({
|
|
111
106
|
method,
|
|
112
107
|
url,
|
|
113
108
|
headers: requestHeaders,
|
|
114
|
-
timeout:
|
|
115
|
-
username,
|
|
116
|
-
password,
|
|
117
|
-
retry: 0,
|
|
109
|
+
timeout: { request: timeout ?? TIMEOUT },
|
|
110
|
+
...(username ? { username } : {}),
|
|
111
|
+
...(password ? { password } : {}),
|
|
112
|
+
retry: { limit: 0 },
|
|
118
113
|
}).json();
|
|
119
114
|
return response;
|
|
120
115
|
}
|
|
@@ -144,23 +139,30 @@ class PlexServer {
|
|
|
144
139
|
args['viewedAt>'] = mindate.getTime().toString();
|
|
145
140
|
}
|
|
146
141
|
args['X-Plex-Container-Start'] = '0';
|
|
147
|
-
args['X-Plex-Container-Size'] = Math.min(
|
|
142
|
+
args['X-Plex-Container-Size'] = Math.min(X_PLEX_CONTAINER_SIZE, maxresults).toString();
|
|
148
143
|
let results = [];
|
|
149
|
-
let key = '/status/sessions/history/all?' + new
|
|
144
|
+
let key = '/status/sessions/history/all?' + new URLSearchParams(args).toString();
|
|
150
145
|
let raw = await this.query(key);
|
|
151
146
|
const totalResults = raw.MediaContainer.totalSize;
|
|
152
147
|
results = results.concat(raw.MediaContainer.Metadata);
|
|
153
148
|
while (results.length <= totalResults &&
|
|
154
|
-
|
|
149
|
+
X_PLEX_CONTAINER_SIZE === raw.MediaContainer.size &&
|
|
155
150
|
maxresults > results.length) {
|
|
156
151
|
args['X-Plex-Container-Start'] = (Number(args['X-Plex-Container-Start']) + Number(args['X-Plex-Container-Size'])).toString();
|
|
157
|
-
key = '/status/sessions/history/all?' + new
|
|
152
|
+
key = '/status/sessions/history/all?' + new URLSearchParams(args).toString();
|
|
158
153
|
// eslint-disable-next-line no-await-in-loop
|
|
159
154
|
raw = await this.query(key);
|
|
160
155
|
results = results.concat(raw.MediaContainer.Metadata);
|
|
161
156
|
}
|
|
162
157
|
return results;
|
|
163
158
|
}
|
|
159
|
+
async settings() {
|
|
160
|
+
if (!this._settings) {
|
|
161
|
+
const data = await this.query(Settings.key);
|
|
162
|
+
this._settings = new Settings(this, data.MediaContainer.Setting);
|
|
163
|
+
}
|
|
164
|
+
return this._settings;
|
|
165
|
+
}
|
|
164
166
|
// TODO: not sure if this works
|
|
165
167
|
// /**
|
|
166
168
|
// * Returns a list of all playlist objects saved on the server.
|
|
@@ -178,38 +180,37 @@ class PlexServer {
|
|
|
178
180
|
*/
|
|
179
181
|
myPlexAccount() {
|
|
180
182
|
if (!this._myPlexAccount) {
|
|
181
|
-
this._myPlexAccount = new
|
|
183
|
+
this._myPlexAccount = new MyPlexAccount(this.baseurl, undefined, undefined, this.token, this.timeout, this);
|
|
182
184
|
}
|
|
183
185
|
return this._myPlexAccount;
|
|
184
186
|
}
|
|
185
187
|
// Returns list of all :class:`~plexapi.client.PlexClient` objects connected to server.
|
|
186
188
|
async clients() {
|
|
187
|
-
var _a, _b;
|
|
188
189
|
const items = [];
|
|
189
190
|
const response = await this.query('/clients');
|
|
190
|
-
if (
|
|
191
|
+
if (response.MediaContainer?.Server === undefined) {
|
|
191
192
|
return [];
|
|
192
193
|
}
|
|
193
194
|
const shouldFetchPorts = response.MediaContainer.Server.some(server => server.port === null || server.port === undefined);
|
|
194
|
-
let ports;
|
|
195
|
+
let ports = {};
|
|
195
196
|
if (shouldFetchPorts) {
|
|
196
197
|
ports = await this._myPlexClientPorts();
|
|
197
198
|
}
|
|
198
199
|
for (const server of response.MediaContainer.Server) {
|
|
199
|
-
let port = server
|
|
200
|
+
let { port } = server;
|
|
200
201
|
if (!port) {
|
|
201
202
|
// TODO: print warning about doing weird port stuff
|
|
202
|
-
port = (
|
|
203
|
+
port = Number(ports?.[server.machineIdentifier]);
|
|
203
204
|
}
|
|
204
205
|
const baseurl = `http://${server.host}:${port}`;
|
|
205
|
-
items.push(new
|
|
206
|
+
items.push(new PlexClient({ baseurl, token: this.token, server: this, data: server }));
|
|
206
207
|
}
|
|
207
208
|
return items;
|
|
208
209
|
}
|
|
209
210
|
/** Returns list of all :class:`~plexapi.media.Optimized` objects connected to server. */
|
|
210
211
|
async optimizedItems() {
|
|
211
|
-
const backgroundProcessing = await
|
|
212
|
-
const items = await
|
|
212
|
+
const backgroundProcessing = await fetchItem(this, '/playlists?type=42');
|
|
213
|
+
const items = await fetchItems(this, backgroundProcessing.key, undefined, Optimized, this);
|
|
213
214
|
return items;
|
|
214
215
|
}
|
|
215
216
|
/**
|
|
@@ -220,18 +221,34 @@ class PlexServer {
|
|
|
220
221
|
if (!this.baseurl) {
|
|
221
222
|
throw new Error('PlexClient object missing baseurl.');
|
|
222
223
|
}
|
|
223
|
-
const url = new
|
|
224
|
+
const url = new URL(key, this.baseurl);
|
|
224
225
|
if (this.token && includeToken) {
|
|
225
|
-
const searchParams = new
|
|
226
|
+
const searchParams = new URLSearchParams(params);
|
|
226
227
|
searchParams.append('X-Plex-Token', this.token);
|
|
227
228
|
url.search = searchParams.toString();
|
|
228
229
|
return url;
|
|
229
230
|
}
|
|
230
231
|
return url;
|
|
231
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Build the Plex Web URL for the object.
|
|
235
|
+
* @param base The base URL before the fragment (``#!``).
|
|
236
|
+
* Default is https://app.plex.tv/desktop.
|
|
237
|
+
* @param endpoint The Plex Web URL endpoint.
|
|
238
|
+
* None for server, 'playlist' for playlists, 'details' for all other media types.
|
|
239
|
+
*/
|
|
240
|
+
_buildWebURL(base = 'https://app.plex.tv/desktop/', endpoint, params) {
|
|
241
|
+
if (endpoint) {
|
|
242
|
+
return `${base}#!/server/${this.machineIdentifier}/${endpoint}?${params?.toString()}`;
|
|
243
|
+
}
|
|
244
|
+
return `${base}#!/media/${this.machineIdentifier}/com.plexapp.plugins.library?${params?.toString()}`;
|
|
245
|
+
}
|
|
246
|
+
_uriRoot() {
|
|
247
|
+
return `server://${this.machineIdentifier}/com.plexapp.plugins.library`;
|
|
248
|
+
}
|
|
232
249
|
_headers() {
|
|
233
250
|
const headers = {
|
|
234
|
-
...
|
|
251
|
+
...BASE_HEADERS,
|
|
235
252
|
'Content-type': 'application/json',
|
|
236
253
|
};
|
|
237
254
|
if (this.token) {
|
|
@@ -292,16 +309,14 @@ class PlexServer {
|
|
|
292
309
|
* See python plex issue #126: Make PlexServer.clients() more user friendly.
|
|
293
310
|
*/
|
|
294
311
|
async _myPlexClientPorts() {
|
|
295
|
-
|
|
296
|
-
let ports = {};
|
|
312
|
+
const ports = {};
|
|
297
313
|
const account = this.myPlexAccount();
|
|
298
314
|
const devices = await account.devices();
|
|
299
315
|
for (const device of devices) {
|
|
300
|
-
if (
|
|
301
|
-
ports[device.clientIdentifier] = new
|
|
316
|
+
if (device.connections?.length) {
|
|
317
|
+
ports[device.clientIdentifier] = new URL('http://172.17.0.2:32400').port;
|
|
302
318
|
}
|
|
303
319
|
}
|
|
304
320
|
return ports;
|
|
305
321
|
}
|
|
306
322
|
}
|
|
307
|
-
exports.PlexServer = PlexServer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { PlexObject } from './base/plexObject.js';
|
|
2
|
+
export interface SettingResponse {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
summary: string;
|
|
6
|
+
type: Type;
|
|
7
|
+
default: boolean | number | string;
|
|
8
|
+
value: boolean | number | string;
|
|
9
|
+
hidden: boolean;
|
|
10
|
+
advanced: boolean;
|
|
11
|
+
group: SettingsGroup;
|
|
12
|
+
}
|
|
13
|
+
declare enum SettingsGroup {
|
|
14
|
+
Butler = "butler",
|
|
15
|
+
Channels = "channels",
|
|
16
|
+
Dlna = "dlna",
|
|
17
|
+
Empty = "",
|
|
18
|
+
Extras = "extras",
|
|
19
|
+
General = "general",
|
|
20
|
+
Library = "library",
|
|
21
|
+
Network = "network",
|
|
22
|
+
Transcoder = "transcoder"
|
|
23
|
+
}
|
|
24
|
+
declare enum Type {
|
|
25
|
+
Bool = "bool",
|
|
26
|
+
Double = "double",
|
|
27
|
+
Int = "int",
|
|
28
|
+
Text = "text"
|
|
29
|
+
}
|
|
30
|
+
export declare class Settings extends PlexObject {
|
|
31
|
+
static key: string;
|
|
32
|
+
_settings: Record<string, Setting>;
|
|
33
|
+
_data: SettingResponse[];
|
|
34
|
+
all(): Setting[];
|
|
35
|
+
get(id: string): Setting;
|
|
36
|
+
/**
|
|
37
|
+
* Save any outstanding settnig changes to the PlexServer. This
|
|
38
|
+
* performs a full reload() of Settings after complete.
|
|
39
|
+
*/
|
|
40
|
+
save(): Promise<void>;
|
|
41
|
+
_loadData(data: SettingResponse[]): void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Represents a single Plex setting
|
|
45
|
+
*/
|
|
46
|
+
export declare class Setting extends PlexObject {
|
|
47
|
+
/** Setting id (or name). */
|
|
48
|
+
id: string;
|
|
49
|
+
/** Short description of what this setting is. */
|
|
50
|
+
label: string;
|
|
51
|
+
/** Long description of what this setting is. */
|
|
52
|
+
summary: string;
|
|
53
|
+
/** Setting type (text, int, double, bool). */
|
|
54
|
+
type: string;
|
|
55
|
+
/** Default value for this setting. */
|
|
56
|
+
default: string | boolean | number;
|
|
57
|
+
/** Current value for this setting. */
|
|
58
|
+
value: string | boolean | number;
|
|
59
|
+
/** True if this is a hidden setting. */
|
|
60
|
+
hidden: boolean;
|
|
61
|
+
/** True if this is an advanced setting. */
|
|
62
|
+
advanced: boolean;
|
|
63
|
+
/** Group name this setting is categorized as. */
|
|
64
|
+
group: string;
|
|
65
|
+
/** List or dictionary of valis values for this setting. */
|
|
66
|
+
enumValues: any[] | any;
|
|
67
|
+
_setValue: string | boolean | number | null;
|
|
68
|
+
/**
|
|
69
|
+
* Set a new value for this setitng. NOTE: You must call {@link Settings.save} before
|
|
70
|
+
* any changes to setting values are persisted to the PlexServer.
|
|
71
|
+
*/
|
|
72
|
+
set(value: string | boolean | number): void;
|
|
73
|
+
_loadData(data: SettingResponse): void;
|
|
74
|
+
}
|
|
75
|
+
export declare class Preferences extends Setting {
|
|
76
|
+
static TAG: "Preferences";
|
|
77
|
+
FILTER: "preferences";
|
|
78
|
+
}
|
|
79
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { URLSearchParams } from 'url';
|
|
2
|
+
import { PlexObject } from './base/plexObject.js';
|
|
3
|
+
import { NotFound } from './exceptions.js';
|
|
4
|
+
import { lowerFirst } from './util.js';
|
|
5
|
+
var SettingsGroup;
|
|
6
|
+
(function (SettingsGroup) {
|
|
7
|
+
SettingsGroup["Butler"] = "butler";
|
|
8
|
+
SettingsGroup["Channels"] = "channels";
|
|
9
|
+
SettingsGroup["Dlna"] = "dlna";
|
|
10
|
+
SettingsGroup["Empty"] = "";
|
|
11
|
+
SettingsGroup["Extras"] = "extras";
|
|
12
|
+
SettingsGroup["General"] = "general";
|
|
13
|
+
SettingsGroup["Library"] = "library";
|
|
14
|
+
SettingsGroup["Network"] = "network";
|
|
15
|
+
SettingsGroup["Transcoder"] = "transcoder";
|
|
16
|
+
})(SettingsGroup || (SettingsGroup = {}));
|
|
17
|
+
var Type;
|
|
18
|
+
(function (Type) {
|
|
19
|
+
Type["Bool"] = "bool";
|
|
20
|
+
Type["Double"] = "double";
|
|
21
|
+
Type["Int"] = "int";
|
|
22
|
+
Type["Text"] = "text";
|
|
23
|
+
})(Type || (Type = {}));
|
|
24
|
+
export class Settings extends PlexObject {
|
|
25
|
+
constructor() {
|
|
26
|
+
super(...arguments);
|
|
27
|
+
this._data = [];
|
|
28
|
+
}
|
|
29
|
+
static { this.key = '/:/prefs'; }
|
|
30
|
+
all() {
|
|
31
|
+
return Object.entries(this._settings)
|
|
32
|
+
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
33
|
+
.map(x => x[1]);
|
|
34
|
+
}
|
|
35
|
+
get(id) {
|
|
36
|
+
const lowerId = lowerFirst(id);
|
|
37
|
+
if (this._settings[lowerId]) {
|
|
38
|
+
return this._settings[lowerId];
|
|
39
|
+
}
|
|
40
|
+
throw new NotFound(`Invalid setting id: ${id}`);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Save any outstanding settnig changes to the PlexServer. This
|
|
44
|
+
* performs a full reload() of Settings after complete.
|
|
45
|
+
*/
|
|
46
|
+
async save() {
|
|
47
|
+
const params = new URLSearchParams();
|
|
48
|
+
for (const setting of this.all()) {
|
|
49
|
+
if (setting._setValue !== null) {
|
|
50
|
+
params.append('setting.id', JSON.stringify(setting._setValue));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const url = `${this.key}?${params.toString()}`;
|
|
54
|
+
await this.server.query(url, 'put');
|
|
55
|
+
}
|
|
56
|
+
_loadData(data) {
|
|
57
|
+
this._data = data;
|
|
58
|
+
this._settings = this._settings ?? {};
|
|
59
|
+
for (const elem of data) {
|
|
60
|
+
const id = lowerFirst(elem.id);
|
|
61
|
+
if (this._settings[id]) {
|
|
62
|
+
this._settings[id]._loadData(elem);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
this._settings[id] = new Setting(this.server, elem, this.initpath);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Represents a single Plex setting
|
|
71
|
+
*/
|
|
72
|
+
export class Setting extends PlexObject {
|
|
73
|
+
constructor() {
|
|
74
|
+
super(...arguments);
|
|
75
|
+
this._setValue = null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Set a new value for this setitng. NOTE: You must call {@link Settings.save} before
|
|
79
|
+
* any changes to setting values are persisted to the PlexServer.
|
|
80
|
+
*/
|
|
81
|
+
set(value) {
|
|
82
|
+
if (typeof value !== typeof this.value) {
|
|
83
|
+
throw new Error('Invalid type');
|
|
84
|
+
}
|
|
85
|
+
this._setValue = value;
|
|
86
|
+
}
|
|
87
|
+
_loadData(data) {
|
|
88
|
+
// this._setValue = None
|
|
89
|
+
this.id = data.id;
|
|
90
|
+
this.label = data.label;
|
|
91
|
+
this.summary = data.summary;
|
|
92
|
+
this.type = data.type;
|
|
93
|
+
this.default = data.default;
|
|
94
|
+
this.value = data.value;
|
|
95
|
+
this.hidden = data.hidden;
|
|
96
|
+
this.advanced = data.advanced;
|
|
97
|
+
this.group = data.group;
|
|
98
|
+
// this.enumValues = this._getEnumValues(data);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export class Preferences extends Setting {
|
|
102
|
+
constructor() {
|
|
103
|
+
super(...arguments);
|
|
104
|
+
this.FILTER = 'preferences';
|
|
105
|
+
}
|
|
106
|
+
static { this.TAG = 'Preferences'; }
|
|
107
|
+
}
|
|
108
|
+
// class Setting(PlexObject):
|
|
109
|
+
// """ Represents a single Plex setting.
|
|
110
|
+
// _bool_cast = lambda x: True if x == 'true' or x == '1' else False
|
|
111
|
+
// _bool_str = lambda x: str(x).lower()
|
|
112
|
+
// _str = lambda x: str(x).encode('utf-8')
|
|
113
|
+
// TYPES = {
|
|
114
|
+
// 'bool': {'type': bool, 'cast': _bool_cast, 'tostr': _bool_str},
|
|
115
|
+
// 'double': {'type': float, 'cast': float, 'tostr': _str},
|
|
116
|
+
// 'int': {'type': int, 'cast': int, 'tostr': _str},
|
|
117
|
+
// 'text': {'type': str, 'cast': _str, 'tostr': _str},
|
|
118
|
+
// }
|
|
119
|
+
// def _loadData(self, data):
|
|
120
|
+
// """ Load attribute values from Plex XML response. """
|
|
121
|
+
// this._setValue = None
|
|
122
|
+
// this.id = data.('id')
|
|
123
|
+
// this.label = data.('label')
|
|
124
|
+
// this.summary = data.('summary')
|
|
125
|
+
// this.type = data.('type')
|
|
126
|
+
// this.default = this._cast(data.('default'))
|
|
127
|
+
// this.value = this._cast(data.('value'))
|
|
128
|
+
// this.hidden = utils.cast(bool, data.('hidden'))
|
|
129
|
+
// this.advanced = utils.cast(bool, data.('advanced'))
|
|
130
|
+
// this.group = data.('group')
|
|
131
|
+
// this.enumValues = this._getEnumValues(data)
|
|
132
|
+
// def _cast(self, value):
|
|
133
|
+
// """ Cast the specific value to the type of this setting. """
|
|
134
|
+
// if this.type != 'enum':
|
|
135
|
+
// value = utils.cast(this.TYPES.get(this.type)['cast'], value)
|
|
136
|
+
// return value
|
|
137
|
+
// def _getEnumValues(self, data):
|
|
138
|
+
// """ Returns a list of dictionary of valis value for this setting. """
|
|
139
|
+
// enumstr = data.('enumValues')
|
|
140
|
+
// if not enumstr:
|
|
141
|
+
// return None
|
|
142
|
+
// if ':' in enumstr:
|
|
143
|
+
// return {this._cast(k): v for k, v in [kv.split(':') for kv in enumstr.split('|')]}
|
|
144
|
+
// return enumstr.split('|')
|
|
145
|
+
// def set(self, value):
|
|
146
|
+
// """ Set a new value for this setitng. NOTE: You must call plex.settings.save() for before
|
|
147
|
+
// any changes to setting values are persisted to the :class:`~plexapi.server.PlexServer`.
|
|
148
|
+
// """
|
|
149
|
+
// # check a few things up front
|
|
150
|
+
// if not isinstance(value, this.TYPES[this.type]['type']):
|
|
151
|
+
// badtype = type(value).__name__
|
|
152
|
+
// raise BadRequest('Invalid value for %s: a %s is required, not %s' % (this.id, this.type, badtype))
|
|
153
|
+
// if this.enumValues and value not in this.enumValues:
|
|
154
|
+
// raise BadRequest('Invalid value for %s: %s not in %s' % (this.id, value, list(this.enumValues)))
|
|
155
|
+
// # store value off to the side until we call settings.save()
|
|
156
|
+
// tostr = this.TYPES[this.type]['tostr']
|
|
157
|
+
// this._setValue = tostr(value)
|
|
158
|
+
// def toUrl(self):
|
|
159
|
+
// """Helper for urls"""
|
|
160
|
+
// return '%s=%s' % (this.id, this._value or this.value)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Section } from './library';
|
|
1
|
+
import type { Section } from './library.js';
|
|
2
2
|
export interface MediaContainer<T> {
|
|
3
3
|
MediaContainer: T;
|
|
4
4
|
}
|
|
@@ -17,3 +17,4 @@ export declare function getAgentIdentifier(section: Section, agent: string): Pro
|
|
|
17
17
|
/** Simple tag helper for editing a object. */
|
|
18
18
|
export declare function tagHelper(tag: string, items: string[], locked?: boolean, remove?: boolean): Record<string, string | number>;
|
|
19
19
|
export declare function ltrim(x: string, characters: string[]): string;
|
|
20
|
+
export declare function lowerFirst(str: string): string;
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.ltrim = exports.tagHelper = exports.getAgentIdentifier = exports.rsplit = void 0;
|
|
4
|
-
function rsplit(str, sep, maxsplit) {
|
|
5
|
-
var split = str.split(sep);
|
|
1
|
+
export function rsplit(str, sep, maxsplit) {
|
|
2
|
+
const split = str.split(sep);
|
|
6
3
|
return maxsplit ? [split.slice(0, -maxsplit).join(sep)].concat(split.slice(-maxsplit)) : split;
|
|
7
4
|
}
|
|
8
|
-
exports.rsplit = rsplit;
|
|
9
5
|
/**
|
|
10
6
|
* Return the full agent identifier from a short identifier, name, or confirm full identifier.
|
|
11
7
|
* @param section
|
|
12
8
|
* @param agent
|
|
13
9
|
*/
|
|
14
|
-
async function getAgentIdentifier(section, agent) {
|
|
10
|
+
export async function getAgentIdentifier(section, agent) {
|
|
15
11
|
const agents = [];
|
|
16
12
|
for (const ag of await section.agents()) {
|
|
17
13
|
const identifiers = [ag.identifier, ag.shortIdentifier, ag.name];
|
|
@@ -22,9 +18,8 @@ async function getAgentIdentifier(section, agent) {
|
|
|
22
18
|
}
|
|
23
19
|
throw new Error(`Couldnt find "${agent}" in agents list (${agents.join(', ')})`);
|
|
24
20
|
}
|
|
25
|
-
exports.getAgentIdentifier = getAgentIdentifier;
|
|
26
21
|
/** Simple tag helper for editing a object. */
|
|
27
|
-
function tagHelper(tag, items, locked = true, remove = false) {
|
|
22
|
+
export function tagHelper(tag, items, locked = true, remove = false) {
|
|
28
23
|
const data = {};
|
|
29
24
|
if (remove) {
|
|
30
25
|
const tagname = `${tag}[].tag.tag-`;
|
|
@@ -40,8 +35,7 @@ function tagHelper(tag, items, locked = true, remove = false) {
|
|
|
40
35
|
data[`${tag}.locked`] = locked ? 1 : 0;
|
|
41
36
|
return data;
|
|
42
37
|
}
|
|
43
|
-
|
|
44
|
-
function ltrim(x, characters) {
|
|
38
|
+
export function ltrim(x, characters) {
|
|
45
39
|
let start = 0;
|
|
46
40
|
while (characters.includes(x[start])) {
|
|
47
41
|
start += 1;
|
|
@@ -49,4 +43,6 @@ function ltrim(x, characters) {
|
|
|
49
43
|
const end = x.length - 1;
|
|
50
44
|
return x.substr(start, end);
|
|
51
45
|
}
|
|
52
|
-
|
|
46
|
+
export function lowerFirst(str) {
|
|
47
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
48
|
+
}
|