@jjdenhertog/plex-music-search 1.0.1

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.
Files changed (66) hide show
  1. package/README.md +110 -0
  2. package/actions/getAlbumTracks.d.ts +2 -0
  3. package/actions/getAlbumTracks.js +78 -0
  4. package/actions/getMetadata.d.ts +2 -0
  5. package/actions/getMetadata.js +80 -0
  6. package/actions/hubSearch.d.ts +2 -0
  7. package/actions/hubSearch.js +85 -0
  8. package/index.d.ts +16 -0
  9. package/index.js +403 -0
  10. package/package.json +39 -0
  11. package/types/PlexMusicSearchApproach.d.ts +7 -0
  12. package/types/PlexMusicSearchApproach.js +2 -0
  13. package/types/PlexMusicSearchConfig.d.ts +7 -0
  14. package/types/PlexMusicSearchConfig.js +2 -0
  15. package/types/PlexMusicSearchTrack.d.ts +6 -0
  16. package/types/PlexMusicSearchTrack.js +2 -0
  17. package/types/PlexTrack.d.ts +23 -0
  18. package/types/PlexTrack.js +2 -0
  19. package/types/SearchResponse.d.ts +8 -0
  20. package/types/SearchResponse.js +2 -0
  21. package/types/actions/HubSearchAlbumResult.d.ts +17 -0
  22. package/types/actions/HubSearchAlbumResult.js +2 -0
  23. package/types/actions/HubSearchResult.d.ts +3 -0
  24. package/types/actions/HubSearchResult.js +2 -0
  25. package/types/actions/HubSearchTrackResult.d.ts +23 -0
  26. package/types/actions/HubSearchTrackResult.js +2 -0
  27. package/types/actions/index.d.ts +3 -0
  28. package/types/actions/index.js +19 -0
  29. package/types/index.d.ts +7 -0
  30. package/types/index.js +23 -0
  31. package/types/plex/GetPlaylistResponse.d.ts +7 -0
  32. package/types/plex/GetPlaylistResponse.js +2 -0
  33. package/types/plex/GetPlexPinResponse.d.ts +25 -0
  34. package/types/plex/GetPlexPinResponse.js +2 -0
  35. package/types/plex/GetUserResponse.d.ts +60 -0
  36. package/types/plex/GetUserResponse.js +2 -0
  37. package/types/plex/Hub.d.ts +11 -0
  38. package/types/plex/Hub.js +2 -0
  39. package/types/plex/HubSearchResponse.d.ts +7 -0
  40. package/types/plex/HubSearchResponse.js +2 -0
  41. package/types/plex/Media.d.ts +10 -0
  42. package/types/plex/Media.js +2 -0
  43. package/types/plex/MediaPart.d.ts +9 -0
  44. package/types/plex/MediaPart.js +2 -0
  45. package/types/plex/Metadata.d.ts +38 -0
  46. package/types/plex/Metadata.js +2 -0
  47. package/types/plex/Playlist.d.ts +19 -0
  48. package/types/plex/Playlist.js +2 -0
  49. package/types/plex/PostPinResponse.d.ts +25 -0
  50. package/types/plex/PostPinResponse.js +2 -0
  51. package/types/plex/index.d.ts +10 -0
  52. package/types/plex/index.js +26 -0
  53. package/utils/AxiosRequest.d.ts +7 -0
  54. package/utils/AxiosRequest.js +55 -0
  55. package/utils/getAPIUrl.d.ts +1 -0
  56. package/utils/getAPIUrl.js +9 -0
  57. package/utils/searching/hubSearchToPlexTrack.d.ts +3 -0
  58. package/utils/searching/hubSearchToPlexTrack.js +25 -0
  59. package/utils/searching/searchForAlbum.d.ts +66 -0
  60. package/utils/searching/searchForAlbum.js +89 -0
  61. package/utils/searching/searchForAlbumTracks.d.ts +2 -0
  62. package/utils/searching/searchForAlbumTracks.js +75 -0
  63. package/utils/searching/searchForTrack.d.ts +1 -0
  64. package/utils/searching/searchForTrack.js +83 -0
  65. package/utils/searching/searchResultToTracks.d.ts +3 -0
  66. package/utils/searching/searchResultToTracks.js +13 -0
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+
2
+ # Plex Music Search
3
+
4
+ [![Buy Me a Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/jjdenhertog)
5
+
6
+ The `plex-music-search` library provides tools for searching and analyzing tracks within a Plex music library, building on the functionality of [music-search](https://github.com/jjdenhertog/music-search) with Plex-specific configurations. It can handle track searches efficiently and offers configuration options tailored to Plex’s system.
7
+
8
+ ## Table of Contents
9
+ - [Installation](#installation)
10
+ - [Usage](#usage)
11
+ - [Default Settings](#default-settings)
12
+ - [Configuration Options](#configuration-options)
13
+ - [Getting Plex URI and Token](#getting-plex-uri-and-token)
14
+ - [Examples](#examples)
15
+ - [Support](#support)
16
+
17
+ ## Installation
18
+
19
+ Install the `plex-music-search` library with npm:
20
+
21
+ ```bash
22
+ npm install @jjdenhertog/plex-music-search
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ The library provides a straightforward API to search tracks in your Plex library. To get started, you’ll need the URI and token for your Plex server.
28
+
29
+ ```typescript
30
+ import PlexMusicSearch from '@jjdenhertog/plex-music-search';
31
+
32
+ const searchItems = [
33
+ { id: "track1", title: "Shape of You", album: "Divide", artists: ["Ed Sheeran"] },
34
+ // More items...
35
+ ];
36
+
37
+ const plexMusicSearch = new PlexMusicSearch({
38
+ uri: "https://your-plex-server-url",
39
+ token: "your-plex-auth-token",
40
+ });
41
+
42
+ // Search for multiple tracks
43
+ const searchResult = await plexMusicSearch.search(searchItems);
44
+ console.log(searchResult);
45
+ ```
46
+
47
+ ## Default Settings
48
+
49
+ The `plex-music-search` library has a set of default search settings, allowing you to use it without additional configuration:
50
+
51
+ - **URI**: The base URL of your Plex server.
52
+ - **Token**: Your Plex authentication token.
53
+
54
+ These settings ensure you can start searching with minimal setup. Advanced configurations are available if needed.
55
+
56
+ ## Configuration Options
57
+
58
+ The `plex-music-search` library supports several configuration options to customize the search behavior. Here’s a quick look at the main options:
59
+
60
+ - **uri**: URL of the Plex server.
61
+ - **token**: Plex authentication token.
62
+
63
+ Example:
64
+
65
+ ```typescript
66
+ const plexMusicSearch = new PlexMusicSearch({
67
+ uri: "https://your-plex-server-url",
68
+ token: "your-plex-auth-token"
69
+ });
70
+ ```
71
+
72
+ ## Getting Plex URI and Token
73
+
74
+ To use `plex-music-search`, you’ll need your Plex server's URI and a token for authentication:
75
+
76
+ 1. **URI**: This is the base URL of your Plex server. You can usually find this in your Plex server settings, or by accessing Plex Web and copying the URL from your browser.
77
+
78
+ 2. **Token**: The Plex token is required to authenticate API requests. To find your token:
79
+ - Open Plex Web and log in.
80
+ - Right-click on the page and select "Inspect" or "View Page Source."
81
+ - Look for the token in the source code (often found after the term "X-Plex-Token").
82
+
83
+ ## Faster searching
84
+
85
+ The default approach search quite thoroughly through your but as a result can be extremely time-consuming. By default it uses three search approaches. By limiting to only one search approach you will decrease the time searching tremendously.
86
+
87
+ ```typescript
88
+ const searchItems = [
89
+ { id: "1234", title: "Shape of You", album: "Divide", artists: ["Ed Sheeran"] },
90
+ // More items...
91
+ ];
92
+
93
+ const plexMusicSearch = new PlexMusicSearch({
94
+ uri: "https://your-plex-server-url",
95
+ token: "your-plex-auth-token",
96
+ searchApproaches: [{ id: 'fast', filtered: true, trimmed: false}]
97
+ });
98
+
99
+ // Search for multiple tracks
100
+ const searchResult = await plexMusicSearch.search(searchItems);
101
+
102
+ ```
103
+
104
+ ## Support
105
+
106
+ If this project helps you, consider supporting me:
107
+
108
+ [![Buy Me a Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/jjdenhertog)
109
+
110
+ Your support helps improve and maintain this library.
@@ -0,0 +1,2 @@
1
+ import { HubSearchResult } from "../types";
2
+ export default function getAlbumTracks(uri: string, token: string, key: string): Promise<HubSearchResult[]>;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.default = getAlbumTracks;
40
+ var music_search_1 = require("@jjdenhertog/music-search");
41
+ var getMetadata_1 = require("./getMetadata");
42
+ function getAlbumTracks(uri, token, key) {
43
+ return __awaiter(this, void 0, void 0, function () {
44
+ var albumTracks, trackResult;
45
+ return __generator(this, function (_a) {
46
+ switch (_a.label) {
47
+ case 0: return [4 /*yield*/, (0, getMetadata_1.getMetadata)(uri, token, key)];
48
+ case 1:
49
+ albumTracks = _a.sent();
50
+ trackResult = albumTracks.map(function (metadata) {
51
+ return {
52
+ type: "track",
53
+ id: metadata.key,
54
+ ratingKey: metadata.ratingKey,
55
+ guid: metadata.guid,
56
+ score: metadata.score,
57
+ image: metadata.thumb,
58
+ title: metadata.title,
59
+ album: {
60
+ id: metadata.parentKey,
61
+ guid: metadata.parentGuid,
62
+ title: metadata.parentTitle,
63
+ year: metadata.parentYear,
64
+ image: metadata.parentThumb,
65
+ },
66
+ artist: {
67
+ id: metadata.grandparentKey,
68
+ guid: metadata.grandparentGuid,
69
+ title: (0, music_search_1.removeFeaturing)(metadata.originalTitle || metadata.grandparentTitle),
70
+ image: metadata.grandparentThumb,
71
+ }
72
+ };
73
+ });
74
+ return [2 /*return*/, trackResult];
75
+ }
76
+ });
77
+ });
78
+ }
@@ -0,0 +1,2 @@
1
+ import { Metadata } from "../types/plex/Metadata";
2
+ export declare function getMetadata(uri: string, token: string, key: string): Promise<Metadata[]>;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.getMetadata = getMetadata;
43
+ var AxiosRequest_1 = require("../utils/AxiosRequest");
44
+ var getAPIUrl_1 = __importDefault(require("../utils/getAPIUrl"));
45
+ function getMetadata(uri, token, key) {
46
+ return __awaiter(this, void 0, void 0, function () {
47
+ var url, result, _e_1, MediaContainer;
48
+ return __generator(this, function (_a) {
49
+ switch (_a.label) {
50
+ case 0:
51
+ url = (0, getAPIUrl_1.default)(uri, key);
52
+ _a.label = 1;
53
+ case 1:
54
+ _a.trys.push([1, 3, , 6]);
55
+ return [4 /*yield*/, AxiosRequest_1.AxiosRequest.get(url, token)];
56
+ case 2:
57
+ result = _a.sent();
58
+ return [3 /*break*/, 6];
59
+ case 3:
60
+ _e_1 = _a.sent();
61
+ // Cooldown
62
+ return [4 /*yield*/, (new Promise(function (resolve) { setTimeout(resolve, 1000); }))];
63
+ case 4:
64
+ // Cooldown
65
+ _a.sent();
66
+ return [4 /*yield*/, AxiosRequest_1.AxiosRequest.get(url, token)];
67
+ case 5:
68
+ result = _a.sent();
69
+ return [3 /*break*/, 6];
70
+ case 6:
71
+ if (result === null || result === void 0 ? void 0 : result.data) {
72
+ MediaContainer = result.data.MediaContainer;
73
+ if (MediaContainer && MediaContainer.size > 0)
74
+ return [2 /*return*/, MediaContainer.Metadata];
75
+ }
76
+ return [2 /*return*/, []];
77
+ }
78
+ });
79
+ });
80
+ }
@@ -0,0 +1,2 @@
1
+ import { HubSearchResult } from "../types/actions/HubSearchResult";
2
+ export default function hubSearch(uri: string, token: string, query: string, limit?: number): Promise<HubSearchResult[]>;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = hubSearch;
7
+ /* eslint-disable unicorn/prefer-code-point */
8
+ /* eslint-disable prefer-template */
9
+ /* eslint-disable unicorn/prefer-string-replace-all */
10
+ var music_search_1 = require("@jjdenhertog/music-search");
11
+ var AxiosRequest_1 = require("../utils/AxiosRequest");
12
+ var getAPIUrl_1 = __importDefault(require("../utils/getAPIUrl"));
13
+ function hubSearch(uri, token, query, limit) {
14
+ if (limit === void 0) { limit = 5; }
15
+ return new Promise(function (resolve, reject) {
16
+ var url = (0, getAPIUrl_1.default)(uri, "/hubs/search?query=".concat(fixedEncodeURIComponent(query.trim()), "&limit=").concat(limit));
17
+ AxiosRequest_1.AxiosRequest.get(url, token)
18
+ .then(function (result) {
19
+ var response = [];
20
+ if (result.data.MediaContainer.Hub.length > 0) {
21
+ for (var i = 0; i < result.data.MediaContainer.Hub.length; i++) {
22
+ var hub = result.data.MediaContainer.Hub[i];
23
+ if (hub.type == "album" && hub.Metadata) {
24
+ for (var j = 0; j < hub.Metadata.length; j++) {
25
+ var metadata = hub.Metadata[j];
26
+ response.push({
27
+ type: "album",
28
+ id: metadata.key,
29
+ ratingKey: metadata.ratingKey,
30
+ guid: metadata.guid,
31
+ score: metadata.score,
32
+ image: metadata.thumb,
33
+ year: metadata.year,
34
+ title: metadata.title,
35
+ artist: {
36
+ guid: metadata.parentGuid,
37
+ id: metadata.parentKey,
38
+ title: (0, music_search_1.removeFeaturing)(metadata.parentTitle),
39
+ alternative_title: "",
40
+ image: metadata.parentThumb,
41
+ },
42
+ });
43
+ }
44
+ }
45
+ if (hub.type == "track" && hub.Metadata) {
46
+ for (var j = 0; j < hub.Metadata.length; j++) {
47
+ var metadata = hub.Metadata[j];
48
+ response.push({
49
+ type: "track",
50
+ id: metadata.key,
51
+ ratingKey: metadata.ratingKey,
52
+ guid: metadata.guid,
53
+ score: metadata.score,
54
+ image: metadata.thumb,
55
+ title: metadata.title,
56
+ album: {
57
+ id: metadata.parentKey,
58
+ guid: metadata.parentGuid,
59
+ title: metadata.parentTitle,
60
+ year: metadata.parentYear,
61
+ image: metadata.parentThumb,
62
+ },
63
+ artist: {
64
+ id: metadata.grandparentKey,
65
+ guid: metadata.grandparentGuid,
66
+ title: (0, music_search_1.removeFeaturing)(metadata.originalTitle || metadata.grandparentTitle),
67
+ image: metadata.grandparentThumb,
68
+ }
69
+ });
70
+ }
71
+ }
72
+ }
73
+ }
74
+ resolve(response);
75
+ })
76
+ .catch(function (_error) {
77
+ reject("Could not connect to server");
78
+ });
79
+ });
80
+ }
81
+ function fixedEncodeURIComponent(str) {
82
+ return encodeURIComponent(str).replace(/[!'()*]/g, function (c) {
83
+ return '%' + c.charCodeAt(0).toString(16);
84
+ });
85
+ }
package/index.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { PlexMusicSearchConfig } from "./types/PlexMusicSearchConfig";
2
+ import { PlexMusicSearchTrack } from "./types/PlexMusicSearchTrack";
3
+ import { PlexTrack } from "./types/PlexTrack";
4
+ import { SearchResponse } from "./types/SearchResponse";
5
+ export declare class PlexMusicSearch {
6
+ private readonly _config;
7
+ private _cache;
8
+ constructor(config: PlexMusicSearchConfig);
9
+ search(tracks: PlexMusicSearchTrack[]): Promise<SearchResponse[]>;
10
+ analyze(track: PlexMusicSearchTrack): Promise<SearchResponse>;
11
+ searchAlbum(tracks: PlexMusicSearchTrack[]): Promise<SearchResponse[]>;
12
+ getById(key: string): Promise<PlexTrack>;
13
+ private _newTrackSearch;
14
+ private _findTrack;
15
+ }
16
+ export * from './types';