@anymux/pexels 0.1.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.
@@ -0,0 +1,177 @@
1
+ import { IMediaProvider, MediaItem } from "@anymux/ui-kit/media";
2
+
3
+ //#region src/PexelsClient.d.ts
4
+ /**
5
+ * Low-level Pexels API client.
6
+ *
7
+ * Uses API key authentication for read-only access.
8
+ * All content is free to use under the Pexels license.
9
+ */
10
+ /**
11
+ * Low-level Pexels API client.
12
+ *
13
+ * Uses API key authentication for read-only access.
14
+ * All content is free to use under the Pexels license.
15
+ */
16
+ interface PexelsPhoto {
17
+ id: number;
18
+ width: number;
19
+ height: number;
20
+ url: string;
21
+ photographer: string;
22
+ photographer_url: string;
23
+ photographer_id: number;
24
+ avg_color: string;
25
+ src: {
26
+ original: string;
27
+ large2x: string;
28
+ large: string;
29
+ medium: string;
30
+ small: string;
31
+ portrait: string;
32
+ landscape: string;
33
+ tiny: string;
34
+ };
35
+ liked: boolean;
36
+ alt: string;
37
+ }
38
+ interface PexelsVideo {
39
+ id: number;
40
+ width: number;
41
+ height: number;
42
+ url: string;
43
+ image: string;
44
+ duration: number;
45
+ user: {
46
+ id: number;
47
+ name: string;
48
+ url: string;
49
+ };
50
+ video_files: Array<{
51
+ id: number;
52
+ quality: string;
53
+ file_type: string;
54
+ width: number;
55
+ height: number;
56
+ link: string;
57
+ }>;
58
+ video_pictures: Array<{
59
+ id: number;
60
+ picture: string;
61
+ nr: number;
62
+ }>;
63
+ }
64
+ interface PexelsCollection {
65
+ id: string;
66
+ title: string;
67
+ description: string | null;
68
+ private: boolean;
69
+ media_count: number;
70
+ photos_count: number;
71
+ videos_count: number;
72
+ }
73
+ interface PexelsPaginatedPhotos {
74
+ total_results: number;
75
+ page: number;
76
+ per_page: number;
77
+ photos: PexelsPhoto[];
78
+ next_page?: string;
79
+ }
80
+ interface PexelsPaginatedVideos {
81
+ total_results: number;
82
+ page: number;
83
+ per_page: number;
84
+ videos: PexelsVideo[];
85
+ next_page?: string;
86
+ }
87
+ interface PexelsPaginatedCollections {
88
+ collections: PexelsCollection[];
89
+ page: number;
90
+ per_page: number;
91
+ total_results: number;
92
+ next_page?: string;
93
+ }
94
+ interface PexelsCollectionMedia {
95
+ media: Array<PexelsPhoto & {
96
+ type: 'Photo';
97
+ } | PexelsVideo & {
98
+ type: 'Video';
99
+ }>;
100
+ page: number;
101
+ per_page: number;
102
+ total_results: number;
103
+ next_page?: string;
104
+ }
105
+ interface PexelsClientConfig {
106
+ /** Pexels API key */
107
+ apiKey: string;
108
+ }
109
+ declare class PexelsClient {
110
+ private apiKey;
111
+ constructor(config: PexelsClientConfig);
112
+ /** Search photos by query. */
113
+ searchPhotos(options: {
114
+ query: string;
115
+ page?: number;
116
+ perPage?: number;
117
+ orientation?: 'landscape' | 'portrait' | 'square';
118
+ size?: 'large' | 'medium' | 'small';
119
+ color?: string;
120
+ }): Promise<PexelsPaginatedPhotos>;
121
+ /** Get curated editorial photos. */
122
+ getCurated(options?: {
123
+ page?: number;
124
+ perPage?: number;
125
+ }): Promise<PexelsPaginatedPhotos>;
126
+ /** Get a single photo by ID. */
127
+ getPhoto(id: number): Promise<PexelsPhoto>;
128
+ /** Search videos by query. */
129
+ searchVideos(options: {
130
+ query: string;
131
+ page?: number;
132
+ perPage?: number;
133
+ orientation?: 'landscape' | 'portrait' | 'square';
134
+ size?: 'large' | 'medium' | 'small';
135
+ }): Promise<PexelsPaginatedVideos>;
136
+ /** Get popular videos. */
137
+ getPopularVideos(options?: {
138
+ page?: number;
139
+ perPage?: number;
140
+ }): Promise<PexelsPaginatedVideos>;
141
+ /** List featured collections. */
142
+ getFeaturedCollections(options?: {
143
+ page?: number;
144
+ perPage?: number;
145
+ }): Promise<PexelsPaginatedCollections>;
146
+ /** Get media in a collection. */
147
+ getCollectionMedia(collectionId: string, options?: {
148
+ type?: 'photos' | 'videos';
149
+ page?: number;
150
+ perPage?: number;
151
+ }): Promise<PexelsCollectionMedia>;
152
+ private get;
153
+ } //#endregion
154
+ //#region src/PexelsMediaProvider.d.ts
155
+
156
+ //# sourceMappingURL=PexelsClient.d.ts.map
157
+ declare class PexelsMediaProvider implements IMediaProvider {
158
+ private client;
159
+ /** Cache of collection ID -> collection for resolving album names. */
160
+ private collectionCache;
161
+ constructor(config: PexelsClientConfig);
162
+ listItems(): Promise<MediaItem[]>;
163
+ getItem(id: string): Promise<MediaItem | null>;
164
+ createItem(_item: Omit<MediaItem, 'id' | 'createdAt' | 'updatedAt'>): Promise<MediaItem>;
165
+ updateItem(_id: string, _updates: Partial<MediaItem>): Promise<MediaItem>;
166
+ deleteItem(_id: string): Promise<void>;
167
+ search(query: string): Promise<MediaItem[]>;
168
+ getAlbums(): Promise<string[]>;
169
+ getByAlbum(album: string): Promise<MediaItem[]>;
170
+ getByDateRange(_start: Date, _end: Date): Promise<MediaItem[]>;
171
+ }
172
+
173
+ //#endregion
174
+ //# sourceMappingURL=PexelsMediaProvider.d.ts.map
175
+
176
+ export { PexelsClient, PexelsClientConfig, PexelsCollection, PexelsCollectionMedia, PexelsMediaProvider, PexelsPaginatedCollections, PexelsPaginatedPhotos, PexelsPaginatedVideos, PexelsPhoto, PexelsVideo };
177
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/PexelsClient.ts","../src/PexelsMediaProvider.ts"],"sourcesContent":null,"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,159 @@
1
+ //#region src/PexelsClient.ts
2
+ /**
3
+ * Low-level Pexels API client.
4
+ *
5
+ * Uses API key authentication for read-only access.
6
+ * All content is free to use under the Pexels license.
7
+ */
8
+ const API_URL = "https://api.pexels.com";
9
+ var PexelsClient = class {
10
+ constructor(config) {
11
+ this.apiKey = config.apiKey;
12
+ }
13
+ /** Search photos by query. */
14
+ async searchPhotos(options) {
15
+ const params = new URLSearchParams();
16
+ params.set("query", options.query);
17
+ params.set("page", String(options.page ?? 1));
18
+ params.set("per_page", String(options.perPage ?? 30));
19
+ if (options.orientation) params.set("orientation", options.orientation);
20
+ if (options.size) params.set("size", options.size);
21
+ if (options.color) params.set("color", options.color);
22
+ return this.get(`/v1/search?${params}`);
23
+ }
24
+ /** Get curated editorial photos. */
25
+ async getCurated(options = {}) {
26
+ const params = new URLSearchParams();
27
+ params.set("page", String(options.page ?? 1));
28
+ params.set("per_page", String(options.perPage ?? 30));
29
+ return this.get(`/v1/curated?${params}`);
30
+ }
31
+ /** Get a single photo by ID. */
32
+ async getPhoto(id) {
33
+ return this.get(`/v1/photos/${id}`);
34
+ }
35
+ /** Search videos by query. */
36
+ async searchVideos(options) {
37
+ const params = new URLSearchParams();
38
+ params.set("query", options.query);
39
+ params.set("page", String(options.page ?? 1));
40
+ params.set("per_page", String(options.perPage ?? 30));
41
+ if (options.orientation) params.set("orientation", options.orientation);
42
+ if (options.size) params.set("size", options.size);
43
+ return this.get(`/videos/search?${params}`);
44
+ }
45
+ /** Get popular videos. */
46
+ async getPopularVideos(options = {}) {
47
+ const params = new URLSearchParams();
48
+ params.set("page", String(options.page ?? 1));
49
+ params.set("per_page", String(options.perPage ?? 30));
50
+ return this.get(`/videos/popular?${params}`);
51
+ }
52
+ /** List featured collections. */
53
+ async getFeaturedCollections(options = {}) {
54
+ const params = new URLSearchParams();
55
+ params.set("page", String(options.page ?? 1));
56
+ params.set("per_page", String(options.perPage ?? 30));
57
+ return this.get(`/v1/collections/featured?${params}`);
58
+ }
59
+ /** Get media in a collection. */
60
+ async getCollectionMedia(collectionId, options = {}) {
61
+ const params = new URLSearchParams();
62
+ if (options.type) params.set("type", options.type);
63
+ params.set("page", String(options.page ?? 1));
64
+ params.set("per_page", String(options.perPage ?? 30));
65
+ return this.get(`/v1/collections/${collectionId}?${params}`);
66
+ }
67
+ async get(path) {
68
+ const url = `${API_URL}${path}`;
69
+ const res = await fetch(url, { headers: { Authorization: this.apiKey } });
70
+ if (!res.ok) {
71
+ const text = await res.text();
72
+ throw new Error(`Pexels API error ${res.status}: ${text}`);
73
+ }
74
+ return res.json();
75
+ }
76
+ };
77
+
78
+ //#endregion
79
+ //#region src/PexelsMediaProvider.ts
80
+ function mapPhoto(photo, albumName) {
81
+ const item = {
82
+ id: String(photo.id),
83
+ type: "media",
84
+ title: photo.alt || `Photo by ${photo.photographer}`,
85
+ mediaType: "photo",
86
+ url: photo.src.large2x,
87
+ thumbnail: photo.src.medium,
88
+ mimeType: "image/jpeg",
89
+ width: photo.width,
90
+ height: photo.height,
91
+ createdAt: new Date(),
92
+ updatedAt: new Date()
93
+ };
94
+ if (photo.alt) item.description = photo.alt;
95
+ if (albumName) item.album = albumName;
96
+ return item;
97
+ }
98
+ var PexelsMediaProvider = class {
99
+ constructor(config) {
100
+ this.collectionCache = new Map();
101
+ this.client = new PexelsClient(config);
102
+ }
103
+ async listItems() {
104
+ const result = await this.client.getCurated({ perPage: 30 });
105
+ return result.photos.map((p) => mapPhoto(p));
106
+ }
107
+ async getItem(id) {
108
+ try {
109
+ const photo = await this.client.getPhoto(Number(id));
110
+ return mapPhoto(photo);
111
+ } catch {
112
+ return null;
113
+ }
114
+ }
115
+ async createItem(_item) {
116
+ throw new Error("Pexels is a read-only photo library");
117
+ }
118
+ async updateItem(_id, _updates) {
119
+ throw new Error("Pexels is a read-only photo library");
120
+ }
121
+ async deleteItem(_id) {
122
+ throw new Error("Pexels is a read-only photo library");
123
+ }
124
+ async search(query) {
125
+ const result = await this.client.searchPhotos({
126
+ query,
127
+ perPage: 30
128
+ });
129
+ return result.photos.map((p) => mapPhoto(p));
130
+ }
131
+ async getAlbums() {
132
+ const result = await this.client.getFeaturedCollections({ perPage: 30 });
133
+ this.collectionCache.clear();
134
+ for (const c of result.collections) this.collectionCache.set(c.id, c);
135
+ return result.collections.map((c) => c.title);
136
+ }
137
+ async getByAlbum(album) {
138
+ if (this.collectionCache.size === 0) await this.getAlbums();
139
+ let collectionId;
140
+ for (const [id, c] of this.collectionCache) if (c.title === album) {
141
+ collectionId = id;
142
+ break;
143
+ }
144
+ if (!collectionId) return [];
145
+ const result = await this.client.getCollectionMedia(collectionId, {
146
+ type: "photos",
147
+ perPage: 30
148
+ });
149
+ return result.media.filter((m) => m.type === "Photo").map((p) => mapPhoto(p, album));
150
+ }
151
+ async getByDateRange(_start, _end) {
152
+ const result = await this.client.getCurated({ perPage: 30 });
153
+ return result.photos.map((p) => mapPhoto(p));
154
+ }
155
+ };
156
+
157
+ //#endregion
158
+ export { PexelsClient, PexelsMediaProvider };
159
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["config: PexelsClientConfig","options: {\n query: string;\n page?: number;\n perPage?: number;\n orientation?: 'landscape' | 'portrait' | 'square';\n size?: 'large' | 'medium' | 'small';\n color?: string;\n }","options: {\n page?: number;\n perPage?: number;\n }","id: number","options: {\n query: string;\n page?: number;\n perPage?: number;\n orientation?: 'landscape' | 'portrait' | 'square';\n size?: 'large' | 'medium' | 'small';\n }","collectionId: string","options: {\n type?: 'photos' | 'videos';\n page?: number;\n perPage?: number;\n }","path: string","photo: PexelsPhoto","albumName?: string","item: MediaItem","config: PexelsClientConfig","id: string","_item: Omit<MediaItem, 'id' | 'createdAt' | 'updatedAt'>","_id: string","_updates: Partial<MediaItem>","query: string","album: string","collectionId: string | undefined","_start: Date","_end: Date"],"sources":["../src/PexelsClient.ts","../src/PexelsMediaProvider.ts"],"sourcesContent":["/**\n * Low-level Pexels API client.\n *\n * Uses API key authentication for read-only access.\n * All content is free to use under the Pexels license.\n */\n\nconst API_URL = 'https://api.pexels.com';\n\n// ---------- Response shapes ----------\n\nexport interface PexelsPhoto {\n id: number;\n width: number;\n height: number;\n url: string;\n photographer: string;\n photographer_url: string;\n photographer_id: number;\n avg_color: string;\n src: {\n original: string;\n large2x: string;\n large: string;\n medium: string;\n small: string;\n portrait: string;\n landscape: string;\n tiny: string;\n };\n liked: boolean;\n alt: string;\n}\n\nexport interface PexelsVideo {\n id: number;\n width: number;\n height: number;\n url: string;\n image: string;\n duration: number;\n user: { id: number; name: string; url: string };\n video_files: Array<{\n id: number;\n quality: string;\n file_type: string;\n width: number;\n height: number;\n link: string;\n }>;\n video_pictures: Array<{\n id: number;\n picture: string;\n nr: number;\n }>;\n}\n\nexport interface PexelsCollection {\n id: string;\n title: string;\n description: string | null;\n private: boolean;\n media_count: number;\n photos_count: number;\n videos_count: number;\n}\n\nexport interface PexelsPaginatedPhotos {\n total_results: number;\n page: number;\n per_page: number;\n photos: PexelsPhoto[];\n next_page?: string;\n}\n\nexport interface PexelsPaginatedVideos {\n total_results: number;\n page: number;\n per_page: number;\n videos: PexelsVideo[];\n next_page?: string;\n}\n\nexport interface PexelsPaginatedCollections {\n collections: PexelsCollection[];\n page: number;\n per_page: number;\n total_results: number;\n next_page?: string;\n}\n\nexport interface PexelsCollectionMedia {\n media: Array<PexelsPhoto & { type: 'Photo' } | PexelsVideo & { type: 'Video' }>;\n page: number;\n per_page: number;\n total_results: number;\n next_page?: string;\n}\n\nexport interface PexelsClientConfig {\n /** Pexels API key */\n apiKey: string;\n}\n\nexport class PexelsClient {\n private apiKey: string;\n\n constructor(config: PexelsClientConfig) {\n this.apiKey = config.apiKey;\n }\n\n /** Search photos by query. */\n async searchPhotos(options: {\n query: string;\n page?: number;\n perPage?: number;\n orientation?: 'landscape' | 'portrait' | 'square';\n size?: 'large' | 'medium' | 'small';\n color?: string;\n }): Promise<PexelsPaginatedPhotos> {\n const params = new URLSearchParams();\n params.set('query', options.query);\n params.set('page', String(options.page ?? 1));\n params.set('per_page', String(options.perPage ?? 30));\n if (options.orientation) params.set('orientation', options.orientation);\n if (options.size) params.set('size', options.size);\n if (options.color) params.set('color', options.color);\n return this.get<PexelsPaginatedPhotos>(`/v1/search?${params}`);\n }\n\n /** Get curated editorial photos. */\n async getCurated(options: {\n page?: number;\n perPage?: number;\n } = {}): Promise<PexelsPaginatedPhotos> {\n const params = new URLSearchParams();\n params.set('page', String(options.page ?? 1));\n params.set('per_page', String(options.perPage ?? 30));\n return this.get<PexelsPaginatedPhotos>(`/v1/curated?${params}`);\n }\n\n /** Get a single photo by ID. */\n async getPhoto(id: number): Promise<PexelsPhoto> {\n return this.get<PexelsPhoto>(`/v1/photos/${id}`);\n }\n\n /** Search videos by query. */\n async searchVideos(options: {\n query: string;\n page?: number;\n perPage?: number;\n orientation?: 'landscape' | 'portrait' | 'square';\n size?: 'large' | 'medium' | 'small';\n }): Promise<PexelsPaginatedVideos> {\n const params = new URLSearchParams();\n params.set('query', options.query);\n params.set('page', String(options.page ?? 1));\n params.set('per_page', String(options.perPage ?? 30));\n if (options.orientation) params.set('orientation', options.orientation);\n if (options.size) params.set('size', options.size);\n return this.get<PexelsPaginatedVideos>(`/videos/search?${params}`);\n }\n\n /** Get popular videos. */\n async getPopularVideos(options: {\n page?: number;\n perPage?: number;\n } = {}): Promise<PexelsPaginatedVideos> {\n const params = new URLSearchParams();\n params.set('page', String(options.page ?? 1));\n params.set('per_page', String(options.perPage ?? 30));\n return this.get<PexelsPaginatedVideos>(`/videos/popular?${params}`);\n }\n\n /** List featured collections. */\n async getFeaturedCollections(options: {\n page?: number;\n perPage?: number;\n } = {}): Promise<PexelsPaginatedCollections> {\n const params = new URLSearchParams();\n params.set('page', String(options.page ?? 1));\n params.set('per_page', String(options.perPage ?? 30));\n return this.get<PexelsPaginatedCollections>(`/v1/collections/featured?${params}`);\n }\n\n /** Get media in a collection. */\n async getCollectionMedia(collectionId: string, options: {\n type?: 'photos' | 'videos';\n page?: number;\n perPage?: number;\n } = {}): Promise<PexelsCollectionMedia> {\n const params = new URLSearchParams();\n if (options.type) params.set('type', options.type);\n params.set('page', String(options.page ?? 1));\n params.set('per_page', String(options.perPage ?? 30));\n return this.get<PexelsCollectionMedia>(`/v1/collections/${collectionId}?${params}`);\n }\n\n // ---------- Internal ----------\n\n private async get<T>(path: string): Promise<T> {\n const url = `${API_URL}${path}`;\n const res = await fetch(url, {\n headers: { Authorization: this.apiKey },\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Pexels API error ${res.status}: ${text}`);\n }\n return res.json() as Promise<T>;\n }\n}\n","import type { IMediaProvider, MediaItem } from '@anymux/ui-kit/media';\nimport { PexelsClient } from './PexelsClient';\nimport type { PexelsPhoto, PexelsCollection, PexelsClientConfig } from './PexelsClient';\n\nfunction mapPhoto(photo: PexelsPhoto, albumName?: string): MediaItem {\n const item: MediaItem = {\n id: String(photo.id),\n type: 'media',\n title: photo.alt || `Photo by ${photo.photographer}`,\n mediaType: 'photo',\n url: photo.src.large2x,\n thumbnail: photo.src.medium,\n mimeType: 'image/jpeg',\n width: photo.width,\n height: photo.height,\n createdAt: new Date(), // Pexels doesn't expose creation dates\n updatedAt: new Date(),\n };\n\n if (photo.alt) {\n item.description = photo.alt;\n }\n\n if (albumName) {\n item.album = albumName;\n }\n\n return item;\n}\n\nexport class PexelsMediaProvider implements IMediaProvider {\n private client: PexelsClient;\n /** Cache of collection ID -> collection for resolving album names. */\n private collectionCache = new Map<string, PexelsCollection>();\n\n constructor(config: PexelsClientConfig) {\n this.client = new PexelsClient(config);\n }\n\n // ---------- IObjectProvider<MediaItem> ----------\n\n async listItems(): Promise<MediaItem[]> {\n const result = await this.client.getCurated({ perPage: 30 });\n return result.photos.map(p => mapPhoto(p));\n }\n\n async getItem(id: string): Promise<MediaItem | null> {\n try {\n const photo = await this.client.getPhoto(Number(id));\n return mapPhoto(photo);\n } catch {\n return null;\n }\n }\n\n async createItem(_item: Omit<MediaItem, 'id' | 'createdAt' | 'updatedAt'>): Promise<MediaItem> {\n throw new Error('Pexels is a read-only photo library');\n }\n\n async updateItem(_id: string, _updates: Partial<MediaItem>): Promise<MediaItem> {\n throw new Error('Pexels is a read-only photo library');\n }\n\n async deleteItem(_id: string): Promise<void> {\n throw new Error('Pexels is a read-only photo library');\n }\n\n async search(query: string): Promise<MediaItem[]> {\n const result = await this.client.searchPhotos({ query, perPage: 30 });\n return result.photos.map(p => mapPhoto(p));\n }\n\n // ---------- IMediaProvider ----------\n\n async getAlbums(): Promise<string[]> {\n const result = await this.client.getFeaturedCollections({ perPage: 30 });\n this.collectionCache.clear();\n for (const c of result.collections) {\n this.collectionCache.set(c.id, c);\n }\n return result.collections.map(c => c.title);\n }\n\n async getByAlbum(album: string): Promise<MediaItem[]> {\n if (this.collectionCache.size === 0) await this.getAlbums();\n\n let collectionId: string | undefined;\n for (const [id, c] of this.collectionCache) {\n if (c.title === album) {\n collectionId = id;\n break;\n }\n }\n if (!collectionId) return [];\n\n const result = await this.client.getCollectionMedia(collectionId, { type: 'photos', perPage: 30 });\n return result.media\n .filter((m): m is PexelsPhoto & { type: 'Photo' } => m.type === 'Photo')\n .map(p => mapPhoto(p, album));\n }\n\n async getByDateRange(_start: Date, _end: Date): Promise<MediaItem[]> {\n // Pexels doesn't support date range queries — return curated photos\n const result = await this.client.getCurated({ perPage: 30 });\n return result.photos.map(p => mapPhoto(p));\n }\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@anymux/pexels",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "description": "Pexels media provider adapter for AnyMux",
7
+ "keywords": [
8
+ "anymux",
9
+ "pexels",
10
+ "media",
11
+ "photos",
12
+ "stock-photos",
13
+ "videos"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/AnyMux/AnyMuxMonorepo.git",
18
+ "directory": "packages/pexels"
19
+ },
20
+ "homepage": "https://github.com/AnyMux/AnyMuxMonorepo/tree/main/packages/pexels#readme",
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "main": "./src/index.ts",
25
+ "types": "./src/index.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js"
30
+ }
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "src",
35
+ "README.md"
36
+ ],
37
+ "dependencies": {
38
+ "@anymux/ui-kit": "0.2.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^22.10.2",
42
+ "tsdown": "^0.10.2",
43
+ "typescript": "^5.7.3",
44
+ "@anymux/typescript-config": "0.0.0"
45
+ },
46
+ "scripts": {
47
+ "build": "tsdown",
48
+ "check": "tsc -p tsconfig.json --noEmit"
49
+ }
50
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Low-level Pexels API client.
3
+ *
4
+ * Uses API key authentication for read-only access.
5
+ * All content is free to use under the Pexels license.
6
+ */
7
+
8
+ const API_URL = 'https://api.pexels.com';
9
+
10
+ // ---------- Response shapes ----------
11
+
12
+ export interface PexelsPhoto {
13
+ id: number;
14
+ width: number;
15
+ height: number;
16
+ url: string;
17
+ photographer: string;
18
+ photographer_url: string;
19
+ photographer_id: number;
20
+ avg_color: string;
21
+ src: {
22
+ original: string;
23
+ large2x: string;
24
+ large: string;
25
+ medium: string;
26
+ small: string;
27
+ portrait: string;
28
+ landscape: string;
29
+ tiny: string;
30
+ };
31
+ liked: boolean;
32
+ alt: string;
33
+ }
34
+
35
+ export interface PexelsVideo {
36
+ id: number;
37
+ width: number;
38
+ height: number;
39
+ url: string;
40
+ image: string;
41
+ duration: number;
42
+ user: { id: number; name: string; url: string };
43
+ video_files: Array<{
44
+ id: number;
45
+ quality: string;
46
+ file_type: string;
47
+ width: number;
48
+ height: number;
49
+ link: string;
50
+ }>;
51
+ video_pictures: Array<{
52
+ id: number;
53
+ picture: string;
54
+ nr: number;
55
+ }>;
56
+ }
57
+
58
+ export interface PexelsCollection {
59
+ id: string;
60
+ title: string;
61
+ description: string | null;
62
+ private: boolean;
63
+ media_count: number;
64
+ photos_count: number;
65
+ videos_count: number;
66
+ }
67
+
68
+ export interface PexelsPaginatedPhotos {
69
+ total_results: number;
70
+ page: number;
71
+ per_page: number;
72
+ photos: PexelsPhoto[];
73
+ next_page?: string;
74
+ }
75
+
76
+ export interface PexelsPaginatedVideos {
77
+ total_results: number;
78
+ page: number;
79
+ per_page: number;
80
+ videos: PexelsVideo[];
81
+ next_page?: string;
82
+ }
83
+
84
+ export interface PexelsPaginatedCollections {
85
+ collections: PexelsCollection[];
86
+ page: number;
87
+ per_page: number;
88
+ total_results: number;
89
+ next_page?: string;
90
+ }
91
+
92
+ export interface PexelsCollectionMedia {
93
+ media: Array<PexelsPhoto & { type: 'Photo' } | PexelsVideo & { type: 'Video' }>;
94
+ page: number;
95
+ per_page: number;
96
+ total_results: number;
97
+ next_page?: string;
98
+ }
99
+
100
+ export interface PexelsClientConfig {
101
+ /** Pexels API key */
102
+ apiKey: string;
103
+ }
104
+
105
+ export class PexelsClient {
106
+ private apiKey: string;
107
+
108
+ constructor(config: PexelsClientConfig) {
109
+ this.apiKey = config.apiKey;
110
+ }
111
+
112
+ /** Search photos by query. */
113
+ async searchPhotos(options: {
114
+ query: string;
115
+ page?: number;
116
+ perPage?: number;
117
+ orientation?: 'landscape' | 'portrait' | 'square';
118
+ size?: 'large' | 'medium' | 'small';
119
+ color?: string;
120
+ }): Promise<PexelsPaginatedPhotos> {
121
+ const params = new URLSearchParams();
122
+ params.set('query', options.query);
123
+ params.set('page', String(options.page ?? 1));
124
+ params.set('per_page', String(options.perPage ?? 30));
125
+ if (options.orientation) params.set('orientation', options.orientation);
126
+ if (options.size) params.set('size', options.size);
127
+ if (options.color) params.set('color', options.color);
128
+ return this.get<PexelsPaginatedPhotos>(`/v1/search?${params}`);
129
+ }
130
+
131
+ /** Get curated editorial photos. */
132
+ async getCurated(options: {
133
+ page?: number;
134
+ perPage?: number;
135
+ } = {}): Promise<PexelsPaginatedPhotos> {
136
+ const params = new URLSearchParams();
137
+ params.set('page', String(options.page ?? 1));
138
+ params.set('per_page', String(options.perPage ?? 30));
139
+ return this.get<PexelsPaginatedPhotos>(`/v1/curated?${params}`);
140
+ }
141
+
142
+ /** Get a single photo by ID. */
143
+ async getPhoto(id: number): Promise<PexelsPhoto> {
144
+ return this.get<PexelsPhoto>(`/v1/photos/${id}`);
145
+ }
146
+
147
+ /** Search videos by query. */
148
+ async searchVideos(options: {
149
+ query: string;
150
+ page?: number;
151
+ perPage?: number;
152
+ orientation?: 'landscape' | 'portrait' | 'square';
153
+ size?: 'large' | 'medium' | 'small';
154
+ }): Promise<PexelsPaginatedVideos> {
155
+ const params = new URLSearchParams();
156
+ params.set('query', options.query);
157
+ params.set('page', String(options.page ?? 1));
158
+ params.set('per_page', String(options.perPage ?? 30));
159
+ if (options.orientation) params.set('orientation', options.orientation);
160
+ if (options.size) params.set('size', options.size);
161
+ return this.get<PexelsPaginatedVideos>(`/videos/search?${params}`);
162
+ }
163
+
164
+ /** Get popular videos. */
165
+ async getPopularVideos(options: {
166
+ page?: number;
167
+ perPage?: number;
168
+ } = {}): Promise<PexelsPaginatedVideos> {
169
+ const params = new URLSearchParams();
170
+ params.set('page', String(options.page ?? 1));
171
+ params.set('per_page', String(options.perPage ?? 30));
172
+ return this.get<PexelsPaginatedVideos>(`/videos/popular?${params}`);
173
+ }
174
+
175
+ /** List featured collections. */
176
+ async getFeaturedCollections(options: {
177
+ page?: number;
178
+ perPage?: number;
179
+ } = {}): Promise<PexelsPaginatedCollections> {
180
+ const params = new URLSearchParams();
181
+ params.set('page', String(options.page ?? 1));
182
+ params.set('per_page', String(options.perPage ?? 30));
183
+ return this.get<PexelsPaginatedCollections>(`/v1/collections/featured?${params}`);
184
+ }
185
+
186
+ /** Get media in a collection. */
187
+ async getCollectionMedia(collectionId: string, options: {
188
+ type?: 'photos' | 'videos';
189
+ page?: number;
190
+ perPage?: number;
191
+ } = {}): Promise<PexelsCollectionMedia> {
192
+ const params = new URLSearchParams();
193
+ if (options.type) params.set('type', options.type);
194
+ params.set('page', String(options.page ?? 1));
195
+ params.set('per_page', String(options.perPage ?? 30));
196
+ return this.get<PexelsCollectionMedia>(`/v1/collections/${collectionId}?${params}`);
197
+ }
198
+
199
+ // ---------- Internal ----------
200
+
201
+ private async get<T>(path: string): Promise<T> {
202
+ const url = `${API_URL}${path}`;
203
+ const res = await fetch(url, {
204
+ headers: { Authorization: this.apiKey },
205
+ });
206
+ if (!res.ok) {
207
+ const text = await res.text();
208
+ throw new Error(`Pexels API error ${res.status}: ${text}`);
209
+ }
210
+ return res.json() as Promise<T>;
211
+ }
212
+ }
@@ -0,0 +1,107 @@
1
+ import type { IMediaProvider, MediaItem } from '@anymux/ui-kit/media';
2
+ import { PexelsClient } from './PexelsClient';
3
+ import type { PexelsPhoto, PexelsCollection, PexelsClientConfig } from './PexelsClient';
4
+
5
+ function mapPhoto(photo: PexelsPhoto, albumName?: string): MediaItem {
6
+ const item: MediaItem = {
7
+ id: String(photo.id),
8
+ type: 'media',
9
+ title: photo.alt || `Photo by ${photo.photographer}`,
10
+ mediaType: 'photo',
11
+ url: photo.src.large2x,
12
+ thumbnail: photo.src.medium,
13
+ mimeType: 'image/jpeg',
14
+ width: photo.width,
15
+ height: photo.height,
16
+ createdAt: new Date(), // Pexels doesn't expose creation dates
17
+ updatedAt: new Date(),
18
+ };
19
+
20
+ if (photo.alt) {
21
+ item.description = photo.alt;
22
+ }
23
+
24
+ if (albumName) {
25
+ item.album = albumName;
26
+ }
27
+
28
+ return item;
29
+ }
30
+
31
+ export class PexelsMediaProvider implements IMediaProvider {
32
+ private client: PexelsClient;
33
+ /** Cache of collection ID -> collection for resolving album names. */
34
+ private collectionCache = new Map<string, PexelsCollection>();
35
+
36
+ constructor(config: PexelsClientConfig) {
37
+ this.client = new PexelsClient(config);
38
+ }
39
+
40
+ // ---------- IObjectProvider<MediaItem> ----------
41
+
42
+ async listItems(): Promise<MediaItem[]> {
43
+ const result = await this.client.getCurated({ perPage: 30 });
44
+ return result.photos.map(p => mapPhoto(p));
45
+ }
46
+
47
+ async getItem(id: string): Promise<MediaItem | null> {
48
+ try {
49
+ const photo = await this.client.getPhoto(Number(id));
50
+ return mapPhoto(photo);
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ async createItem(_item: Omit<MediaItem, 'id' | 'createdAt' | 'updatedAt'>): Promise<MediaItem> {
57
+ throw new Error('Pexels is a read-only photo library');
58
+ }
59
+
60
+ async updateItem(_id: string, _updates: Partial<MediaItem>): Promise<MediaItem> {
61
+ throw new Error('Pexels is a read-only photo library');
62
+ }
63
+
64
+ async deleteItem(_id: string): Promise<void> {
65
+ throw new Error('Pexels is a read-only photo library');
66
+ }
67
+
68
+ async search(query: string): Promise<MediaItem[]> {
69
+ const result = await this.client.searchPhotos({ query, perPage: 30 });
70
+ return result.photos.map(p => mapPhoto(p));
71
+ }
72
+
73
+ // ---------- IMediaProvider ----------
74
+
75
+ async getAlbums(): Promise<string[]> {
76
+ const result = await this.client.getFeaturedCollections({ perPage: 30 });
77
+ this.collectionCache.clear();
78
+ for (const c of result.collections) {
79
+ this.collectionCache.set(c.id, c);
80
+ }
81
+ return result.collections.map(c => c.title);
82
+ }
83
+
84
+ async getByAlbum(album: string): Promise<MediaItem[]> {
85
+ if (this.collectionCache.size === 0) await this.getAlbums();
86
+
87
+ let collectionId: string | undefined;
88
+ for (const [id, c] of this.collectionCache) {
89
+ if (c.title === album) {
90
+ collectionId = id;
91
+ break;
92
+ }
93
+ }
94
+ if (!collectionId) return [];
95
+
96
+ const result = await this.client.getCollectionMedia(collectionId, { type: 'photos', perPage: 30 });
97
+ return result.media
98
+ .filter((m): m is PexelsPhoto & { type: 'Photo' } => m.type === 'Photo')
99
+ .map(p => mapPhoto(p, album));
100
+ }
101
+
102
+ async getByDateRange(_start: Date, _end: Date): Promise<MediaItem[]> {
103
+ // Pexels doesn't support date range queries — return curated photos
104
+ const result = await this.client.getCurated({ perPage: 30 });
105
+ return result.photos.map(p => mapPhoto(p));
106
+ }
107
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { PexelsClient } from './PexelsClient';
2
+ export type { PexelsClientConfig, PexelsPhoto, PexelsVideo, PexelsCollection, PexelsPaginatedPhotos, PexelsPaginatedVideos, PexelsPaginatedCollections, PexelsCollectionMedia } from './PexelsClient';
3
+ export { PexelsMediaProvider } from './PexelsMediaProvider';