@ctrl/plex 3.2.0 → 3.4.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/dist/src/alert.js +0 -1
- package/dist/src/client.d.ts +0 -1
- package/dist/src/library.js +0 -2
- package/dist/src/media.d.ts +42 -0
- package/dist/src/media.js +53 -0
- package/dist/src/myplex.d.ts +15 -2
- package/dist/src/myplex.js +66 -1
- package/dist/src/myplex.types.d.ts +5 -0
- package/dist/src/playlist.js +0 -2
- package/dist/src/server.d.ts +4 -2
- package/dist/src/server.js +2 -4
- package/dist/src/settings.js +0 -1
- package/dist/src/video.d.ts +14 -2
- package/dist/src/video.js +30 -1
- package/package.json +21 -26
package/dist/src/alert.js
CHANGED
|
@@ -8,7 +8,6 @@ export class AlertListener {
|
|
|
8
8
|
async run() {
|
|
9
9
|
const url = this.server.url(this.key, true).toString().replace('http', 'ws');
|
|
10
10
|
this._ws = new WebSocket(url);
|
|
11
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
12
11
|
this._ws.on('message', (buffer) => {
|
|
13
12
|
try {
|
|
14
13
|
const data = JSON.parse(buffer.toString());
|
package/dist/src/client.d.ts
CHANGED
package/dist/src/library.js
CHANGED
|
@@ -213,7 +213,6 @@ export class Library {
|
|
|
213
213
|
const results = [];
|
|
214
214
|
const sections = await this.sections();
|
|
215
215
|
for (const section of sections) {
|
|
216
|
-
// eslint-disable-next-line no-await-in-loop
|
|
217
216
|
const items = await section.all();
|
|
218
217
|
for (const item of items) {
|
|
219
218
|
results.push(item);
|
|
@@ -227,7 +226,6 @@ export class Library {
|
|
|
227
226
|
async emptyTrash() {
|
|
228
227
|
const sections = await this.sections();
|
|
229
228
|
for (const section of sections) {
|
|
230
|
-
// eslint-disable-next-line no-await-in-loop
|
|
231
229
|
await section.emptyTrash();
|
|
232
230
|
}
|
|
233
231
|
}
|
package/dist/src/media.d.ts
CHANGED
|
@@ -200,4 +200,46 @@ export declare class Rating extends PlexObject {
|
|
|
200
200
|
value: number;
|
|
201
201
|
protected _loadData(data: any): void;
|
|
202
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Base class for all Art, Poster, and Theme objects.
|
|
205
|
+
*/
|
|
206
|
+
declare abstract class BaseResource extends PlexObject {
|
|
207
|
+
/**
|
|
208
|
+
* The source of the resource. 'local' for local files (e.g. theme.mp3),
|
|
209
|
+
*/
|
|
210
|
+
provider: string;
|
|
211
|
+
/**
|
|
212
|
+
* Unique key identifying the resource.
|
|
213
|
+
*/
|
|
214
|
+
ratingKey: string;
|
|
215
|
+
/**
|
|
216
|
+
* True if the resource is currently selected.
|
|
217
|
+
*/
|
|
218
|
+
selected: boolean;
|
|
219
|
+
/**
|
|
220
|
+
* The URL to retrieve the resource thumbnail.
|
|
221
|
+
*/
|
|
222
|
+
thumb: string;
|
|
223
|
+
select(): Promise<any>;
|
|
224
|
+
resourceFilepath(): string;
|
|
225
|
+
protected _loadData(data: any): void;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Represents a single Art object.
|
|
229
|
+
*/
|
|
230
|
+
export declare class Art extends BaseResource {
|
|
231
|
+
static TAG: string;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Represents a single Poster object.
|
|
235
|
+
*/
|
|
236
|
+
export declare class Poster extends BaseResource {
|
|
237
|
+
static TAG: string;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Represents a single Theme object.
|
|
241
|
+
*/
|
|
242
|
+
export declare class Theme extends BaseResource {
|
|
243
|
+
static TAG: string;
|
|
244
|
+
}
|
|
203
245
|
export {};
|
package/dist/src/media.js
CHANGED
|
@@ -186,6 +186,7 @@ class GuidTag extends PlexObject {
|
|
|
186
186
|
this.id = data.id;
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
|
|
189
190
|
export class Guid extends GuidTag {
|
|
190
191
|
static { this.TAG = 'Guid'; }
|
|
191
192
|
}
|
|
@@ -200,3 +201,55 @@ export class Rating extends PlexObject {
|
|
|
200
201
|
this.value = data.value;
|
|
201
202
|
}
|
|
202
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Base class for all Art, Poster, and Theme objects.
|
|
206
|
+
*/
|
|
207
|
+
class BaseResource extends PlexObject {
|
|
208
|
+
async select() {
|
|
209
|
+
const key = this.key.slice(0, -1);
|
|
210
|
+
const params = new URLSearchParams();
|
|
211
|
+
params.append('url', this.ratingKey);
|
|
212
|
+
return this.server.query(`${key}?${params.toString()}`, 'put');
|
|
213
|
+
}
|
|
214
|
+
resourceFilepath() {
|
|
215
|
+
if (this.ratingKey.startsWith('media://')) {
|
|
216
|
+
return `Media/localhost/${this.ratingKey.split('://')[1]}`;
|
|
217
|
+
}
|
|
218
|
+
const parent = this.parent?.deref();
|
|
219
|
+
if (this.ratingKey.startsWith('metadata://') && parent) {
|
|
220
|
+
return `${parent.metadataDirectory}/Contents/_combined/${this.ratingKey.split('://')[1]}`;
|
|
221
|
+
}
|
|
222
|
+
if (this.ratingKey.startsWith('upload://') && parent) {
|
|
223
|
+
return `${parent.metadataDirectory}/Uploads/${this.ratingKey.split('://')[1]}`;
|
|
224
|
+
}
|
|
225
|
+
return this.ratingKey;
|
|
226
|
+
}
|
|
227
|
+
_loadData(data) {
|
|
228
|
+
this.key = data.key;
|
|
229
|
+
this.provider = data.provider;
|
|
230
|
+
this.ratingKey = data.ratingKey;
|
|
231
|
+
this.selected = data.selected;
|
|
232
|
+
this.thumb = data.thumb;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Represents a single Art object.
|
|
237
|
+
*/
|
|
238
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
|
|
239
|
+
export class Art extends BaseResource {
|
|
240
|
+
static { this.TAG = 'Art'; }
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Represents a single Poster object.
|
|
244
|
+
*/
|
|
245
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
|
|
246
|
+
export class Poster extends BaseResource {
|
|
247
|
+
static { this.TAG = 'Photo'; }
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Represents a single Theme object.
|
|
251
|
+
*/
|
|
252
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
|
|
253
|
+
export class Theme extends BaseResource {
|
|
254
|
+
static { this.TAG = 'Theme'; }
|
|
255
|
+
}
|
package/dist/src/myplex.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PlexObject } from './base/plexObject.js';
|
|
2
|
-
import { Connection, Device, ResourcesResponse } from './myplex.types.js';
|
|
2
|
+
import type { Connection, Device, ResourcesResponse, WebLogin } from './myplex.types.js';
|
|
3
3
|
import { PlexServer } from './server.js';
|
|
4
4
|
/**
|
|
5
5
|
* MyPlex account and profile information. This object represents the data found Account on
|
|
@@ -16,6 +16,19 @@ export declare class MyPlexAccount {
|
|
|
16
16
|
timeout: number;
|
|
17
17
|
server?: PlexServer;
|
|
18
18
|
static key: string;
|
|
19
|
+
/**
|
|
20
|
+
* This follows the outline described in https://forums.plex.tv/t/authenticating-with-plex/609370
|
|
21
|
+
* to fetch a token and potentially compromise username and password. To use first call `getWebLogin()`
|
|
22
|
+
* and present the returned uri to a user to go to, then await `webLoginCheck()`. If you pass in a
|
|
23
|
+
* `forwardUrl`, then send the user to the returned uri, and when a request comes in on the passed in
|
|
24
|
+
* url, then await `webLoginCheck()`.
|
|
25
|
+
*/
|
|
26
|
+
static getWebLogin(forwardUrl?: string | null): Promise<WebLogin>;
|
|
27
|
+
/**
|
|
28
|
+
* Pass in the `webLogin` object obtained from `getWebLogin()` and this will poll Plex to see if
|
|
29
|
+
* the user agreed. It returns a connected `MyPlexAccount` or throws an error.
|
|
30
|
+
*/
|
|
31
|
+
static webLoginCheck(webLogin: WebLogin, timeoutSeconds?: number): Promise<MyPlexAccount>;
|
|
19
32
|
FRIENDINVITE: string;
|
|
20
33
|
HOMEUSERCREATE: string;
|
|
21
34
|
EXISTINGUSER: string;
|
|
@@ -136,7 +149,7 @@ export declare class MyPlexAccount {
|
|
|
136
149
|
*/
|
|
137
150
|
export declare class MyPlexResource {
|
|
138
151
|
readonly account: MyPlexAccount;
|
|
139
|
-
private baseUrl;
|
|
152
|
+
private readonly baseUrl;
|
|
140
153
|
static key: string;
|
|
141
154
|
TAG: string;
|
|
142
155
|
/** Descriptive name of this resource */
|
package/dist/src/myplex.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
2
2
|
import { ofetch } from 'ofetch';
|
|
3
3
|
import pAny from 'p-any';
|
|
4
4
|
import { parseStringPromise } from 'xml2js';
|
|
@@ -14,6 +14,71 @@ import { PlexServer } from './server.js';
|
|
|
14
14
|
*/
|
|
15
15
|
export class MyPlexAccount {
|
|
16
16
|
static { this.key = 'https://plex.tv/api/v2/user'; }
|
|
17
|
+
/**
|
|
18
|
+
* This follows the outline described in https://forums.plex.tv/t/authenticating-with-plex/609370
|
|
19
|
+
* to fetch a token and potentially compromise username and password. To use first call `getWebLogin()`
|
|
20
|
+
* and present the returned uri to a user to go to, then await `webLoginCheck()`. If you pass in a
|
|
21
|
+
* `forwardUrl`, then send the user to the returned uri, and when a request comes in on the passed in
|
|
22
|
+
* url, then await `webLoginCheck()`.
|
|
23
|
+
*/
|
|
24
|
+
static async getWebLogin(forwardUrl = null) {
|
|
25
|
+
const appName = BASE_HEADERS['X-Plex-Product'];
|
|
26
|
+
const clientIdentifier = BASE_HEADERS['X-Plex-Client-Identifier'];
|
|
27
|
+
const pin = await ofetch('https://plex.tv/api/v2/pins', {
|
|
28
|
+
method: 'post',
|
|
29
|
+
headers: {
|
|
30
|
+
Accept: 'application/json',
|
|
31
|
+
},
|
|
32
|
+
query: {
|
|
33
|
+
strong: 'true',
|
|
34
|
+
'X-Plex-Product': appName,
|
|
35
|
+
'X-Plex-Client-Identifier': clientIdentifier,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
...pin,
|
|
40
|
+
uri: `https://app.plex.tv/auth#?clientID=${encodeURIComponent(clientIdentifier)}&code=${encodeURIComponent(pin.code)}&context%5Bdevice%5D%5Bproduct%5D=${encodeURIComponent(appName)}${forwardUrl ? '&forwardUrl=' + encodeURIComponent(forwardUrl) : ''}`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Pass in the `webLogin` object obtained from `getWebLogin()` and this will poll Plex to see if
|
|
45
|
+
* the user agreed. It returns a connected `MyPlexAccount` or throws an error.
|
|
46
|
+
*/
|
|
47
|
+
static async webLoginCheck(webLogin, timeoutSeconds = 60) {
|
|
48
|
+
const recheckMs = 3000;
|
|
49
|
+
const clientIdentifier = BASE_HEADERS['X-Plex-Client-Identifier'];
|
|
50
|
+
const uri = `https://plex.tv/api/v2/pins/${webLogin.id}`;
|
|
51
|
+
const startTime = Date.now();
|
|
52
|
+
while (Date.now() < startTime + timeoutSeconds * 1000) {
|
|
53
|
+
try {
|
|
54
|
+
const tokenResponse = await ofetch(uri, {
|
|
55
|
+
method: 'GET',
|
|
56
|
+
headers: {
|
|
57
|
+
Accept: 'application/json',
|
|
58
|
+
},
|
|
59
|
+
query: {
|
|
60
|
+
code: webLogin.code,
|
|
61
|
+
'X-Plex-Client-Identifier': clientIdentifier,
|
|
62
|
+
},
|
|
63
|
+
timeout: recheckMs,
|
|
64
|
+
retry: 5,
|
|
65
|
+
retryDelay: recheckMs,
|
|
66
|
+
});
|
|
67
|
+
if (tokenResponse.authToken) {
|
|
68
|
+
const myPlexAccount = new MyPlexAccount(null, '', '', tokenResponse.authToken);
|
|
69
|
+
return await myPlexAccount.connect();
|
|
70
|
+
}
|
|
71
|
+
await sleep(recheckMs);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err.message.includes('aborted')) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
throw new Error('Failed to authenticate before timeout');
|
|
81
|
+
}
|
|
17
82
|
/**
|
|
18
83
|
*
|
|
19
84
|
* @param username Your MyPlex username
|
package/dist/src/playlist.js
CHANGED
|
@@ -99,10 +99,8 @@ export class Playlist extends Playable {
|
|
|
99
99
|
throw new BadRequest('Cannot remove items to a smart playlist.');
|
|
100
100
|
}
|
|
101
101
|
for (const item of items) {
|
|
102
|
-
// eslint-disable-next-line no-await-in-loop
|
|
103
102
|
const playlistItemId = await this._getPlaylistItemID(item);
|
|
104
103
|
const key = `${this.key}/items/${playlistItemId}`;
|
|
105
|
-
// eslint-disable-next-line no-await-in-loop
|
|
106
104
|
await this.server.query(key, 'delete');
|
|
107
105
|
}
|
|
108
106
|
}
|
package/dist/src/server.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
1
|
import { URL, URLSearchParams } from 'url';
|
|
3
2
|
import { PlexClient } from './client.js';
|
|
4
3
|
import { Hub, Library } from './library.js';
|
|
@@ -172,7 +171,10 @@ export declare class PlexServer {
|
|
|
172
171
|
* @param method
|
|
173
172
|
* @param headers
|
|
174
173
|
*/
|
|
175
|
-
query<T = any>(path: string, method?: 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete',
|
|
174
|
+
query<T = any>(path: string, method?: 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete', options?: {
|
|
175
|
+
headers?: Record<string, string>;
|
|
176
|
+
body?: Uint8Array;
|
|
177
|
+
}, username?: string, password?: string): Promise<T>;
|
|
176
178
|
/**
|
|
177
179
|
* Returns a list of media items from watched history. If there are many results, they will
|
|
178
180
|
* be fetched from the server in batches of X_PLEX_CONTAINER_SIZE amounts. If you're only
|
package/dist/src/server.js
CHANGED
|
@@ -99,7 +99,7 @@ export class PlexServer {
|
|
|
99
99
|
* @param method
|
|
100
100
|
* @param headers
|
|
101
101
|
*/
|
|
102
|
-
async query(path, method = 'get',
|
|
102
|
+
async query(path, method = 'get', options = {}, username, password) {
|
|
103
103
|
const requestHeaders = this._headers();
|
|
104
104
|
if (username && password) {
|
|
105
105
|
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
|
|
@@ -113,6 +113,7 @@ export class PlexServer {
|
|
|
113
113
|
method,
|
|
114
114
|
headers: requestHeaders,
|
|
115
115
|
timeout: this.timeout ?? TIMEOUT,
|
|
116
|
+
body: options.body,
|
|
116
117
|
retry: 0,
|
|
117
118
|
responseType: 'json',
|
|
118
119
|
});
|
|
@@ -155,7 +156,6 @@ export class PlexServer {
|
|
|
155
156
|
maxresults > results.length) {
|
|
156
157
|
args['X-Plex-Container-Start'] = (Number(args['X-Plex-Container-Start']) + Number(args['X-Plex-Container-Size'])).toString();
|
|
157
158
|
key = '/status/sessions/history/all?' + new URLSearchParams(args).toString();
|
|
158
|
-
// eslint-disable-next-line no-await-in-loop
|
|
159
159
|
raw = await this.query(key);
|
|
160
160
|
results = results.concat(raw.MediaContainer.Metadata);
|
|
161
161
|
}
|
|
@@ -184,7 +184,6 @@ export class PlexServer {
|
|
|
184
184
|
* you're likley to recieve an authentication error calling this.
|
|
185
185
|
*/
|
|
186
186
|
myPlexAccount() {
|
|
187
|
-
// eslint-disable-next-line logical-assignment-operators
|
|
188
187
|
if (!this._myPlexAccount) {
|
|
189
188
|
this._myPlexAccount = new MyPlexAccount(this.baseurl, undefined, undefined, this.token, this.timeout, this);
|
|
190
189
|
}
|
|
@@ -204,7 +203,6 @@ export class PlexServer {
|
|
|
204
203
|
}
|
|
205
204
|
for (const server of response.MediaContainer.Server) {
|
|
206
205
|
let { port } = server;
|
|
207
|
-
// eslint-disable-next-line logical-assignment-operators
|
|
208
206
|
if (!port) {
|
|
209
207
|
// TODO: print warning about doing weird port stuff
|
|
210
208
|
port = Number(ports?.[server.machineIdentifier]);
|
package/dist/src/settings.js
CHANGED
package/dist/src/video.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
1
|
import { URL } from 'url';
|
|
3
2
|
import { Playable } from './base/playable.js';
|
|
4
3
|
import { ExtrasData, FullShowData, MovieData, ShowData } from './library.types.js';
|
|
5
|
-
import { Chapter, Collection, Country, Director, Genre, Guid, Marker, Media, Producer, Rating, Role, Similar, Writer } from './media.js';
|
|
4
|
+
import { Chapter, Collection, Country, Director, Genre, Guid, Marker, Media, Poster, Producer, Rating, Role, Similar, Writer } from './media.js';
|
|
6
5
|
import { ChapterSource, EpisodeMetadata, FullMovieResponse } from './video.types.js';
|
|
7
6
|
export type VideoType = Movie | Show;
|
|
8
7
|
declare abstract class Video extends Playable {
|
|
@@ -52,6 +51,19 @@ declare abstract class Video extends Playable {
|
|
|
52
51
|
markUnwatched(): Promise<void>;
|
|
53
52
|
rate(rate: number): Promise<void>;
|
|
54
53
|
extras(): Promise<Extra[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Returns list of available Poster objects.
|
|
56
|
+
*/
|
|
57
|
+
posters(): Promise<Poster[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Set the poster for a Plex object.
|
|
60
|
+
* @param poster The poster object to select.
|
|
61
|
+
*/
|
|
62
|
+
setPoster(poster: Poster): Promise<this>;
|
|
63
|
+
/**
|
|
64
|
+
* I haven't tested this yet. It may not work.
|
|
65
|
+
*/
|
|
66
|
+
uploadPoster(url?: string, file?: Uint8Array): Promise<void>;
|
|
55
67
|
protected _loadData(data: MovieData | ShowData | EpisodeMetadata): void;
|
|
56
68
|
}
|
|
57
69
|
/**
|
package/dist/src/video.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Playable } from './base/playable.js';
|
|
2
2
|
import { fetchItem, fetchItems, findItems } from './baseFunctionality.js';
|
|
3
|
-
import { Chapter, Collection, Country, Director, Genre, Guid, Marker, Media, Producer, Rating, Role, Similar, Writer, } from './media.js';
|
|
3
|
+
import { Chapter, Collection, Country, Director, Genre, Guid, Marker, Media, Poster, Producer, Rating, Role, Similar, Writer, } from './media.js';
|
|
4
4
|
class Video extends Playable {
|
|
5
5
|
constructor() {
|
|
6
6
|
super(...arguments);
|
|
@@ -53,6 +53,35 @@ class Video extends Playable {
|
|
|
53
53
|
const data = await this.server.query(this._detailsKey);
|
|
54
54
|
return findItems(data.MediaContainer.Metadata[0].Extras?.Metadata, undefined, Extra, this.server, this);
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Returns list of available Poster objects.
|
|
58
|
+
*/
|
|
59
|
+
async posters() {
|
|
60
|
+
return fetchItems(this.server, `/library/metadata/${this.ratingKey}/posters`, undefined, Poster);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Set the poster for a Plex object.
|
|
64
|
+
* @param poster The poster object to select.
|
|
65
|
+
*/
|
|
66
|
+
async setPoster(poster) {
|
|
67
|
+
await poster.select();
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* I haven't tested this yet. It may not work.
|
|
72
|
+
*/
|
|
73
|
+
async uploadPoster(url, file) {
|
|
74
|
+
if (url) {
|
|
75
|
+
const key = `/library/metadata/${this.ratingKey}/posters?url=${encodeURIComponent(url)}`;
|
|
76
|
+
await this.server.query(key, 'post');
|
|
77
|
+
}
|
|
78
|
+
else if (file) {
|
|
79
|
+
const key = `/library/metadata/${this.ratingKey}/posters`;
|
|
80
|
+
await this.server.query(key, 'post', {
|
|
81
|
+
body: file,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
56
85
|
_loadData(data) {
|
|
57
86
|
this.key = data.key;
|
|
58
87
|
this.ratingKey = data.ratingKey;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ctrl/plex",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "plex api client in typescript",
|
|
5
5
|
"author": "Scott Cooper <scttcper@gmail.com>",
|
|
6
6
|
"publishConfig": {
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
"typescript"
|
|
21
21
|
],
|
|
22
22
|
"scripts": {
|
|
23
|
-
"lint": "
|
|
23
|
+
"lint": "pnpm run '/^(lint:biome|lint:eslint)$/'",
|
|
24
24
|
"lint:biome": "biome check .",
|
|
25
|
-
"lint:eslint": "eslint
|
|
26
|
-
"lint:fix": "
|
|
27
|
-
"lint:eslint:fix": "eslint
|
|
28
|
-
"lint:biome:fix": "biome check . --
|
|
25
|
+
"lint:eslint": "eslint .",
|
|
26
|
+
"lint:fix": "pnpm run '/^(lint:biome|lint:eslint):fix$/'",
|
|
27
|
+
"lint:eslint:fix": "eslint . --fix",
|
|
28
|
+
"lint:biome:fix": "biome check . --write",
|
|
29
29
|
"prepare": "npm run build",
|
|
30
30
|
"build": "tsc",
|
|
31
31
|
"build:docs": "typedoc",
|
|
@@ -40,34 +40,29 @@
|
|
|
40
40
|
"@ctrl/mac-address": "^3.0.3",
|
|
41
41
|
"ofetch": "^1.3.4",
|
|
42
42
|
"p-any": "^4.0.0",
|
|
43
|
-
"type-fest": "^4.
|
|
44
|
-
"ws": "^8.
|
|
43
|
+
"type-fest": "^4.24.0",
|
|
44
|
+
"ws": "^8.18.0",
|
|
45
45
|
"xml2js": "^0.6.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@biomejs/biome": "1.
|
|
49
|
-
"@ctrl/eslint-config-biome": "
|
|
50
|
-
"@sindresorhus/tsconfig": "
|
|
51
|
-
"@types/
|
|
52
|
-
"@types/
|
|
53
|
-
"@types/node": "20.12.8",
|
|
54
|
-
"@types/ws": "8.5.10",
|
|
48
|
+
"@biomejs/biome": "1.8.3",
|
|
49
|
+
"@ctrl/eslint-config-biome": "4.1.3",
|
|
50
|
+
"@sindresorhus/tsconfig": "6.0.0",
|
|
51
|
+
"@types/node": "22.3.0",
|
|
52
|
+
"@types/ws": "8.5.12",
|
|
55
53
|
"@types/xml2js": "0.4.14",
|
|
56
|
-
"@types/yargs": "17.0.
|
|
57
|
-
"@vitest/coverage-v8": "
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"glob": "10.3.12",
|
|
62
|
-
"globby": "14.0.1",
|
|
63
|
-
"lodash": "4.17.21",
|
|
54
|
+
"@types/yargs": "17.0.33",
|
|
55
|
+
"@vitest/coverage-v8": "2.0.5",
|
|
56
|
+
"eslint": "9.9.0",
|
|
57
|
+
"execa": "9.3.1",
|
|
58
|
+
"globby": "14.0.2",
|
|
64
59
|
"make-dir": "5.0.0",
|
|
65
60
|
"ora": "8.0.1",
|
|
66
61
|
"p-retry": "6.2.0",
|
|
67
62
|
"ts-node": "10.9.2",
|
|
68
|
-
"typedoc": "0.
|
|
69
|
-
"typescript": "5.4
|
|
70
|
-
"vitest": "
|
|
63
|
+
"typedoc": "0.26.5",
|
|
64
|
+
"typescript": "5.5.4",
|
|
65
|
+
"vitest": "2.0.5",
|
|
71
66
|
"yargs": "17.7.2"
|
|
72
67
|
},
|
|
73
68
|
"release": {
|