@rubixstudios/payload-media 1.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.
Files changed (76) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +73 -0
  3. package/dist/components/media-search/index.d.ts +4 -0
  4. package/dist/components/media-search/index.d.ts.map +1 -0
  5. package/dist/components/media-search/index.js +31 -0
  6. package/dist/components/search-drawer/filters/pexels.d.ts +11 -0
  7. package/dist/components/search-drawer/filters/pexels.d.ts.map +1 -0
  8. package/dist/components/search-drawer/filters/pexels.js +48 -0
  9. package/dist/components/search-drawer/filters/pixabay.d.ts +11 -0
  10. package/dist/components/search-drawer/filters/pixabay.d.ts.map +1 -0
  11. package/dist/components/search-drawer/filters/pixabay.js +74 -0
  12. package/dist/components/search-drawer/filters/unsplash.d.ts +10 -0
  13. package/dist/components/search-drawer/filters/unsplash.d.ts.map +1 -0
  14. package/dist/components/search-drawer/filters/unsplash.js +35 -0
  15. package/dist/components/search-drawer/index.d.ts +9 -0
  16. package/dist/components/search-drawer/index.d.ts.map +1 -0
  17. package/dist/components/search-drawer/index.js +306 -0
  18. package/dist/components/search-drawer/media/heart.d.ts +3 -0
  19. package/dist/components/search-drawer/media/heart.d.ts.map +1 -0
  20. package/dist/components/search-drawer/media/heart.js +12 -0
  21. package/dist/components/search-drawer/media/image.d.ts +10 -0
  22. package/dist/components/search-drawer/media/image.d.ts.map +1 -0
  23. package/dist/components/search-drawer/media/image.js +58 -0
  24. package/dist/components/search-drawer/media/video.d.ts +9 -0
  25. package/dist/components/search-drawer/media/video.d.ts.map +1 -0
  26. package/dist/components/search-drawer/media/video.js +55 -0
  27. package/dist/components/search-drawer/style.css +1 -0
  28. package/dist/defaults.d.ts +3 -0
  29. package/dist/defaults.d.ts.map +1 -0
  30. package/dist/defaults.js +8 -0
  31. package/dist/endpoints/handler/pexels.d.ts +12 -0
  32. package/dist/endpoints/handler/pexels.d.ts.map +1 -0
  33. package/dist/endpoints/handler/pexels.js +105 -0
  34. package/dist/endpoints/handler/pixabay.d.ts +12 -0
  35. package/dist/endpoints/handler/pixabay.d.ts.map +1 -0
  36. package/dist/endpoints/handler/pixabay.js +112 -0
  37. package/dist/endpoints/handler/provider.d.ts +20 -0
  38. package/dist/endpoints/handler/provider.d.ts.map +1 -0
  39. package/dist/endpoints/handler/provider.js +53 -0
  40. package/dist/endpoints/handler/unsplash.d.ts +12 -0
  41. package/dist/endpoints/handler/unsplash.d.ts.map +1 -0
  42. package/dist/endpoints/handler/unsplash.js +82 -0
  43. package/dist/endpoints/index.d.ts +3 -0
  44. package/dist/endpoints/index.d.ts.map +1 -0
  45. package/dist/endpoints/index.js +131 -0
  46. package/dist/exports/client.d.ts +2 -0
  47. package/dist/exports/client.d.ts.map +1 -0
  48. package/dist/exports/client.js +1 -0
  49. package/dist/index.d.ts +4 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +48 -0
  52. package/dist/lib/cache.d.ts +7 -0
  53. package/dist/lib/cache.d.ts.map +1 -0
  54. package/dist/lib/cache.js +34 -0
  55. package/dist/types.d.ts +262 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/types.js +336 -0
  58. package/dist/utils/fetchCache.d.ts +2 -0
  59. package/dist/utils/fetchCache.d.ts.map +1 -0
  60. package/dist/utils/fetchCache.js +20 -0
  61. package/dist/utils/getMediaType.d.ts +2 -0
  62. package/dist/utils/getMediaType.d.ts.map +1 -0
  63. package/dist/utils/getMediaType.js +4 -0
  64. package/dist/utils/getProvider.d.ts +6 -0
  65. package/dist/utils/getProvider.d.ts.map +1 -0
  66. package/dist/utils/getProvider.js +16 -0
  67. package/dist/utils/getProviders.d.ts +6 -0
  68. package/dist/utils/getProviders.d.ts.map +1 -0
  69. package/dist/utils/getProviders.js +10 -0
  70. package/dist/utils/requireAccess.d.ts +3 -0
  71. package/dist/utils/requireAccess.d.ts.map +1 -0
  72. package/dist/utils/requireAccess.js +13 -0
  73. package/dist/utils/resolveProvider.d.ts +6 -0
  74. package/dist/utils/resolveProvider.d.ts.map +1 -0
  75. package/dist/utils/resolveProvider.js +5 -0
  76. package/package.json +96 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heart.d.ts","sourceRoot":"","sources":["../../../../src/components/search-drawer/media/heart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;AAE5C,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,qBASvD"}
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ export function HeartIcon(props) {
3
+ return /*#__PURE__*/ React.createElement("svg", {
4
+ viewBox: "0 0 15 15",
5
+ xmlns: "http://www.w3.org/2000/svg",
6
+ "aria-hidden": "true",
7
+ ...props
8
+ }, /*#__PURE__*/ React.createElement("path", {
9
+ d: "M13.91 6.75c-1.17 2.25-4.3 5.31-6.07 6.94a.52.52 0 0 1-.67 0C5.39 12.06 2.26 9 1.09 6.75-1.48 1.8 5-1.5 7.5 3.45 10-1.5 16.48 1.8 13.91 6.75z",
10
+ fill: "currentColor"
11
+ }));
12
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { type ProviderResult } from '../../../types.js';
3
+ type ImageCardProps = {
4
+ data: ProviderResult;
5
+ baseClass: string;
6
+ onSelect: (url: string, download?: string) => void;
7
+ };
8
+ export declare const ImageCard: ({ data, baseClass, onSelect }: ImageCardProps) => React.JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../../src/components/search-drawer/media/image.tsx"],"names":[],"mappings":"AACA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAEvC,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAGvD,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,cAAc,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACnD,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,+BAA+B,cAAc,sBA+DtE,CAAA"}
@@ -0,0 +1,58 @@
1
+ import { LinkIcon, Tooltip } from '@payloadcms/ui';
2
+ import React, { useState } from 'react';
3
+ import { HeartIcon } from './heart.js';
4
+ export const ImageCard = ({ data, baseClass, onSelect })=>{
5
+ const [show, setShow] = useState(false);
6
+ return /*#__PURE__*/ React.createElement("div", {
7
+ key: data.id,
8
+ className: `${baseClass}__card`
9
+ }, /*#__PURE__*/ React.createElement("button", {
10
+ type: "button",
11
+ className: `${baseClass}__button`,
12
+ onClick: ()=>onSelect(data.urls.original, data.urls?.downloadLocation),
13
+ style: {
14
+ backgroundColor: data.color
15
+ }
16
+ }, /*#__PURE__*/ React.createElement("img", {
17
+ src: data.urls.view,
18
+ alt: data.alt,
19
+ width: data.width,
20
+ height: data.height
21
+ })), /*#__PURE__*/ React.createElement("div", {
22
+ className: `${baseClass}__topOverlay`
23
+ }, data.likes !== undefined && /*#__PURE__*/ React.createElement("button", {
24
+ type: "button",
25
+ className: `${baseClass}__likes`,
26
+ onMouseEnter: ()=>setShow(true),
27
+ onMouseLeave: ()=>setShow(false),
28
+ onFocus: ()=>setShow(true),
29
+ onBlur: ()=>setShow(false)
30
+ }, /*#__PURE__*/ React.createElement(HeartIcon, null), /*#__PURE__*/ React.createElement(Tooltip, {
31
+ position: "bottom",
32
+ alignCaret: "center",
33
+ show: show
34
+ }, data.likes, " likes")), /*#__PURE__*/ React.createElement("a", {
35
+ className: `${baseClass}__download`,
36
+ href: data.urls.download,
37
+ target: "_blank",
38
+ rel: "noopener noreferrer"
39
+ }, /*#__PURE__*/ React.createElement(LinkIcon, null))), /*#__PURE__*/ React.createElement("div", {
40
+ className: `${baseClass}__bottomOverlay`
41
+ }, /*#__PURE__*/ React.createElement("a", {
42
+ className: `${baseClass}__attribution`,
43
+ href: data.attribution.link,
44
+ target: "_blank",
45
+ rel: "noopener noreferrer"
46
+ }, data.avatar ? /*#__PURE__*/ React.createElement("img", {
47
+ className: `${baseClass}__avatar`,
48
+ src: data.avatar,
49
+ alt: data.attribution.name,
50
+ width: 24,
51
+ height: 24,
52
+ loading: "lazy",
53
+ referrerPolicy: "no-referrer"
54
+ }) : /*#__PURE__*/ React.createElement("div", {
55
+ className: `${baseClass}__avatarFallback`,
56
+ "aria-hidden": true
57
+ }), data.attribution.name)));
58
+ };
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { type ProviderResult } from '../../../types.js';
3
+ type VideoCardProps = {
4
+ data: ProviderResult;
5
+ baseClass: string;
6
+ };
7
+ export declare const VideoCard: ({ data, baseClass }: VideoCardProps) => React.JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=video.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video.d.ts","sourceRoot":"","sources":["../../../../src/components/search-drawer/media/video.tsx"],"names":[],"mappings":"AACA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAEvC,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAGvD,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,cAAc,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,qBAAqB,cAAc,sBAkE5D,CAAA"}
@@ -0,0 +1,55 @@
1
+ import { LinkIcon, Tooltip } from '@payloadcms/ui';
2
+ import React, { useState } from 'react';
3
+ import { HeartIcon } from './heart.js';
4
+ export const VideoCard = ({ data, baseClass })=>{
5
+ const [show, setShow] = useState(false);
6
+ return /*#__PURE__*/ React.createElement("div", {
7
+ key: data.id,
8
+ className: `${baseClass}__video`
9
+ }, /*#__PURE__*/ React.createElement("video", {
10
+ src: data.urls.original,
11
+ poster: data.urls.view,
12
+ controls: true,
13
+ preload: "metadata",
14
+ style: {
15
+ width: '100%',
16
+ height: '100%',
17
+ aspectRatio: `${data.width} / ${data.height}`,
18
+ objectFit: 'cover',
19
+ display: 'block'
20
+ }
21
+ }), /*#__PURE__*/ React.createElement("div", {
22
+ className: `${baseClass}__topOverlay`
23
+ }, /*#__PURE__*/ React.createElement("a", {
24
+ className: `${baseClass}__attribution`,
25
+ href: data.attribution.link,
26
+ target: "_blank",
27
+ rel: "noopener noreferrer"
28
+ }, data.avatar && /*#__PURE__*/ React.createElement("img", {
29
+ className: `${baseClass}__avatar`,
30
+ src: data.avatar,
31
+ alt: data.attribution.name,
32
+ width: 24,
33
+ height: 24,
34
+ loading: "lazy",
35
+ referrerPolicy: "no-referrer"
36
+ }), data.attribution.name), /*#__PURE__*/ React.createElement("div", {
37
+ className: `${baseClass}__actions`
38
+ }, data.likes !== undefined && /*#__PURE__*/ React.createElement("button", {
39
+ type: "button",
40
+ className: `${baseClass}__likes`,
41
+ onMouseEnter: ()=>setShow(true),
42
+ onMouseLeave: ()=>setShow(false),
43
+ onFocus: ()=>setShow(true),
44
+ onBlur: ()=>setShow(false)
45
+ }, /*#__PURE__*/ React.createElement(HeartIcon, null), /*#__PURE__*/ React.createElement(Tooltip, {
46
+ position: "bottom",
47
+ alignCaret: "center",
48
+ show: show
49
+ }, data.likes, " likes")), /*#__PURE__*/ React.createElement("a", {
50
+ className: `${baseClass}__download`,
51
+ href: data.urls.download,
52
+ target: "_blank",
53
+ rel: "noopener noreferrer"
54
+ }, /*#__PURE__*/ React.createElement(LinkIcon, null)))));
55
+ };
@@ -0,0 +1 @@
1
+ .drawer__content-children{display:flex;flex-flow:column nowrap}.search-media{width:100%;display:flex;flex-flow:column nowrap;flex:1;padding:2rem 0}.search-media__fields{display:flex;gap:1.5rem}.search-media__fields .search-filter{flex:1}.search-media__fields .search-filter__input{height:100%;padding:0 1rem}.search-media__fields .rs__indicators{margin-left:.5rem}.search-media__filters{display:flex;margin-top:15px;gap:16px}.search-media__filters .react-select{min-width:130px}.search-media__results{margin-top:3rem;column-gap:16px;column-count:5}.search-media__card{break-inside:avoid;margin-bottom:16px}.search-media__video{break-inside:avoid;margin-bottom:16px}.search-media__results>div{width:100%}.search-media__results img{max-width:100%;width:100%}.search-media__pagination{margin-top:auto;padding-top:2rem;display:flex;justify-content:center}.search-media__loading,.search-media__noResults{display:flex;justify-content:center;align-items:center;flex:1}.search-media__card{position:relative;display:block;overflow:hidden}.search-media__video{position:relative;display:block;overflow:hidden}.search-media__card::after{content:"";position:absolute;inset:0;background:rgba(0,0,0,.45);opacity:0;transition:opacity .2s ease;pointer-events:none}.search-media__card:hover::after{opacity:1}.search-media__button{appearance:none;border:none;background-color:transparent;display:block;margin:0;padding:0;cursor:pointer;width:100%}.search-media__button img{display:block;width:100%;height:auto}.search-media__button svg{color:#fff}.search-media__bottomOverlay,.search-media__topOverlay{position:absolute;z-index:1;display:flex;opacity:0;transition:opacity .4s ease;max-width:calc(100% - 20px - 1rem)}.search-media__card .search-media__topOverlay{right:1rem;top:.8rem}.search-media__card .search-media__bottomOverlay{left:1rem;bottom:.8rem}.search-media__card:hover .search-media__bottomOverlay,.search-media__card:hover .search-media__topOverlay{opacity:1;pointer-events:auto}.search-media__video .search-media__topOverlay{top:.8rem;left:1rem;right:1rem;display:flex;align-items:center;justify-content:space-between;gap:1rem}.search-media__video:hover .search-media__bottomOverlay,.search-media__video:hover .search-media__topOverlay{opacity:1;pointer-events:auto}.search-media__actions{display:flex;align-items:stretch;position:relative}.search-media__likes{cursor:pointer;background:#fff;border-radius:6px;border:0;margin-right:10px;padding:7px;font-size:1em;line-height:1}.search-media__likes svg{width:14px}.search-media__likes:hover{background:rgb(255 255 255 / 80%);transition:background-color .4s ease}.search-media__likes .tooltip{left:14px}.search-media__download{background:#fff;border-radius:6px;padding:4px;font-size:1em;line-height:1;width:28px;text-align:center}.search-media__download svg{pointer-events:none}a.search-media__download:hover{background:rgb(255 255 255 / 80%);transition:background-color .4s ease}.search-media__download,.search-media__likes{display:flex;align-items:center;justify-content:center}img.search-media__avatar{max-width:24px;max-height:24px;margin-right:10px;border-radius:100%}.search-media__avatarFallback{width:24px;height:24px;margin-right:10px;border-radius:100%;background:rgba(255 255 255 / 25%)}.search-media__attribution{display:flex;align-items:center;white-space:nowrap;text-overflow:ellipsis;color:#fff;font-size:1rem;font-weight:600;line-height:1;text-decoration:none;overflow:hidden}.search-media__attribution:hover{text-decoration:underline}.search-media__image{border-radius:4px;object-fit:cover;width:100%}.search-media__options{min-width:140px}.search-media__provider{display:flex;justify-content:center;padding-top:3rem;margin-top:auto;margin-bottom:30px}@media (max-width:1600px){.search-media__results{column-count:5}}@media (max-width:1400px){.search-media__results{column-count:4}}@media (max-width:1200px){.search-media__results{column-count:3}}@media (max-width:900px){.search-media__results{column-count:2}.search-media__filters{display:grid}}@media (max-width:600px){.search-media__results{column-count:1}.search-media__filters{display:grid}}
@@ -0,0 +1,3 @@
1
+ import { type ProviderConfig } from './types.js';
2
+ export declare const defaultPluginOptions: Required<ProviderConfig>;
3
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,cAAc,CAOzD,CAAA"}
@@ -0,0 +1,8 @@
1
+ export const defaultPluginOptions = {
2
+ disabled: false,
3
+ unsplash: '',
4
+ pexels: '',
5
+ pixabay: '',
6
+ openverse: '',
7
+ access: ({ req: { user } })=>Boolean(user)
8
+ };
@@ -0,0 +1,12 @@
1
+ import { type PexelsFilters, type PexelsResult, type PexelsVideo, type ProviderResult } from '../../types.js';
2
+ import { Provider, type Resolver } from './provider.js';
3
+ export declare class Pexels extends Provider {
4
+ constructor(apiKey: Resolver);
5
+ getFetchBaseUrl(): string;
6
+ getFetchHeaders(): Record<string, string>;
7
+ getFeatured(filters?: PexelsFilters): Promise<unknown>;
8
+ getSearch(query: string, page: number, filters?: PexelsFilters): Promise<unknown>;
9
+ formatVideoResults(data: PexelsVideo[]): ProviderResult[];
10
+ formatResults(data: PexelsResult[]): ProviderResult[];
11
+ }
12
+ //# sourceMappingURL=pexels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pexels.d.ts","sourceRoot":"","sources":["../../../src/endpoints/handler/pexels.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,WAAW,EAEhB,KAAK,cAAc,EACpB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAA;AAEvD,qBAAa,MAAO,SAAQ,QAAQ;gBACtB,MAAM,EAAE,QAAQ;IAInB,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAInC,WAAW,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAwBtD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IA2ChG,kBAAkB,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,cAAc,EAAE;IAyBhD,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,cAAc,EAAE;CAkB/D"}
@@ -0,0 +1,105 @@
1
+ import { Provider } from './provider.js';
2
+ export class Pexels extends Provider {
3
+ constructor(apiKey){
4
+ super('pexels', 'Pexels', 'https://www.pexels.com/', apiKey);
5
+ }
6
+ getFetchBaseUrl() {
7
+ return 'https://api.pexels.com';
8
+ }
9
+ getFetchHeaders() {
10
+ return {
11
+ Authorization: `${this.getApiKey()}`
12
+ };
13
+ }
14
+ async getFeatured(filters) {
15
+ if (filters?.media === 'video') {
16
+ const data = await this.fetch('GET', `/videos/popular?per_page=${this.getFetchLimit()}`);
17
+ return {
18
+ images: this.formatVideoResults(data.videos),
19
+ totalImages: null,
20
+ totalPages: null
21
+ };
22
+ }
23
+ if (filters?.color || filters?.orientation || filters?.size) {
24
+ return this.getSearch('featured', 1, filters);
25
+ }
26
+ const data = await this.fetch('GET', `/v1/curated?per_page=${this.getFetchLimit()}`);
27
+ return {
28
+ images: this.formatResults(data.photos),
29
+ totalImages: null,
30
+ totalPages: null
31
+ };
32
+ }
33
+ async getSearch(query, page, filters) {
34
+ if (filters?.media === 'video') {
35
+ const params = new URLSearchParams({
36
+ query: query || 'featured',
37
+ page: String(page),
38
+ per_page: String(this.getFetchLimit())
39
+ });
40
+ if (filters?.size) params.set('size', filters.size);
41
+ if (filters?.orientation) params.set('orientation', filters.orientation);
42
+ const data = await this.fetch('GET', `/videos/search?${params.toString()}`);
43
+ const totalResults = data.total_results;
44
+ return {
45
+ images: this.formatVideoResults(data.videos),
46
+ totalImages: totalResults,
47
+ totalPages: Math.min(Math.ceil(totalResults / this.getFetchLimit()), 100)
48
+ };
49
+ }
50
+ const params = new URLSearchParams({
51
+ query: query || 'featured',
52
+ page: String(page),
53
+ per_page: String(this.getFetchLimit())
54
+ });
55
+ if (filters?.color) params.set('color', filters.color);
56
+ if (filters?.size) params.set('size', filters.size);
57
+ if (filters?.orientation) params.set('orientation', filters.orientation);
58
+ const data = await this.fetch('GET', `/v1/search?${params.toString()}`);
59
+ const totalResults = data.total_results;
60
+ return {
61
+ images: this.formatResults(data.photos),
62
+ totalImages: totalResults,
63
+ totalPages: Math.min(Math.ceil(totalResults / this.getFetchLimit()), 100)
64
+ };
65
+ }
66
+ formatVideoResults(data) {
67
+ return data.map((video)=>{
68
+ const file = video.video_files.find((f)=>f.quality === 'hd' && f.file_type === 'video/mp4') ?? video.video_files.find((f)=>f.file_type === 'video/mp4');
69
+ return {
70
+ id: video.id,
71
+ alt: video.url || '',
72
+ width: file?.width || video.width || 0,
73
+ height: file?.height || video.height || 0,
74
+ color: '#000',
75
+ urls: {
76
+ view: video.image || '',
77
+ original: file?.link || video.url || '',
78
+ download: file?.link || video.url || ''
79
+ },
80
+ attribution: {
81
+ name: video.user?.name || '',
82
+ link: video.user?.url || ''
83
+ }
84
+ };
85
+ });
86
+ }
87
+ formatResults(data) {
88
+ return data.map((image)=>({
89
+ id: image.id,
90
+ alt: image.alt || '',
91
+ width: image.width,
92
+ height: image.height,
93
+ color: image.avg_color,
94
+ urls: {
95
+ view: image.src.medium,
96
+ original: image.src.original,
97
+ download: image.url
98
+ },
99
+ attribution: {
100
+ name: image.photographer,
101
+ link: image.photographer_url
102
+ }
103
+ }));
104
+ }
105
+ }
@@ -0,0 +1,12 @@
1
+ import { type PixabayFilters, type PixabayResult, type PixabayVideo, type ProviderResult } from '../../types.js';
2
+ import { Provider, type Resolver } from './provider.js';
3
+ export declare class Pixabay extends Provider {
4
+ constructor(apiKey: Resolver);
5
+ getFetchBaseUrl(): string;
6
+ getFetchHeaders(): Record<string, string>;
7
+ getFeatured(filters?: PixabayFilters): Promise<unknown>;
8
+ getSearch(query: string, page: number, filters?: PixabayFilters): Promise<unknown>;
9
+ formatVideoResults(data: PixabayVideo[]): ProviderResult[];
10
+ formatResults(data: PixabayResult[]): ProviderResult[];
11
+ }
12
+ //# sourceMappingURL=pixabay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pixabay.d.ts","sourceRoot":"","sources":["../../../src/endpoints/handler/pixabay.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,YAAY,EAEjB,KAAK,cAAc,EACpB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAA;AAEvD,qBAAa,OAAQ,SAAQ,QAAQ;gBACvB,MAAM,EAAE,QAAQ;IAInB,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAInC,WAAW,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAoCvD,SAAS,CACtB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,OAAO,CAAC;IA+CnB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,cAAc,EAAE;IA0BjD,aAAa,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,cAAc,EAAE;CAoBhE"}
@@ -0,0 +1,112 @@
1
+ import { Provider } from './provider.js';
2
+ export class Pixabay extends Provider {
3
+ constructor(apiKey){
4
+ super('pixabay', 'Pixabay', 'https://pixabay.com', apiKey);
5
+ }
6
+ getFetchBaseUrl() {
7
+ return 'https://pixabay.com';
8
+ }
9
+ getFetchHeaders() {
10
+ return {};
11
+ }
12
+ async getFeatured(filters) {
13
+ if (filters?.media === 'video') {
14
+ const data = await this.fetch('GET', `/api/videos?key=${this.getApiKey()}&per_page=${this.getFetchLimit()}`);
15
+ return {
16
+ images: this.formatVideoResults(data.hits),
17
+ totalImages: null,
18
+ totalPages: null
19
+ };
20
+ }
21
+ if (filters?.image_type || filters?.orientation || filters?.colors || filters?.category || filters?.order) {
22
+ return this.getSearch('', 1, filters);
23
+ }
24
+ const data = await this.fetch('GET', `/api?key=${this.getApiKey()}&per_page=${this.getFetchLimit()}`);
25
+ return {
26
+ images: this.formatResults(data.hits),
27
+ totalImages: null,
28
+ totalPages: null
29
+ };
30
+ }
31
+ async getSearch(query, page, filters) {
32
+ if (filters?.media === 'video') {
33
+ const params = new URLSearchParams({
34
+ key: this.getApiKey(),
35
+ q: query || '',
36
+ page: String(page),
37
+ per_page: String(this.getFetchLimit())
38
+ });
39
+ if (filters?.category) params.set('category', filters.category);
40
+ if (filters?.order) params.set('order', filters.order);
41
+ const data = await this.fetch('GET', `/api/videos?${params.toString()}`);
42
+ const totalHits = data.totalHits;
43
+ return {
44
+ images: this.formatVideoResults(data.hits),
45
+ totalImages: totalHits,
46
+ totalPages: Math.min(Math.ceil(totalHits / this.getFetchLimit()), 100)
47
+ };
48
+ }
49
+ const params = new URLSearchParams({
50
+ key: this.getApiKey(),
51
+ q: query || '',
52
+ page: String(page),
53
+ per_page: String(this.getFetchLimit())
54
+ });
55
+ if (filters?.category) params.set('category', filters.category);
56
+ if (filters?.image_type) params.set('image_type', filters.image_type);
57
+ if (filters?.colors) params.set('colors', filters.colors);
58
+ if (filters?.orientation) params.set('orientation', filters.orientation);
59
+ if (filters?.order) params.set('order', filters.order);
60
+ const data = await this.fetch('GET', `/api?${params.toString()}`);
61
+ const totalHits = data.totalHits;
62
+ return {
63
+ images: this.formatResults(data.hits),
64
+ totalImages: totalHits,
65
+ totalPages: Math.min(Math.ceil(totalHits / this.getFetchLimit()), 100)
66
+ };
67
+ }
68
+ formatVideoResults(data) {
69
+ return data.map((video)=>{
70
+ const videos = video.videos || {};
71
+ const videoFormat = videos.large || videos.medium || videos.small || {};
72
+ return {
73
+ id: video.id,
74
+ alt: video.tags || '',
75
+ width: videoFormat.width || 0,
76
+ height: videoFormat.height || 0,
77
+ color: '#000',
78
+ likes: video.likes,
79
+ urls: {
80
+ view: videoFormat.thumbnail || '',
81
+ original: videoFormat.url || '',
82
+ download: videoFormat.url ? `${videoFormat.url}?download=1` : ''
83
+ },
84
+ attribution: {
85
+ name: video.user || '',
86
+ link: `https://pixabay.com/users/${video.user}-${video.user_id}/`
87
+ },
88
+ avatar: video.userImageURL
89
+ };
90
+ });
91
+ }
92
+ formatResults(data) {
93
+ return data.map((image)=>({
94
+ id: image.id,
95
+ alt: '',
96
+ width: image.imageWidth,
97
+ height: image.imageHeight,
98
+ color: '#ccc',
99
+ likes: image.likes,
100
+ urls: {
101
+ view: image.webformatURL,
102
+ original: image.imageURL || image.largeImageURL,
103
+ download: image.imageURL || image.largeImageURL
104
+ },
105
+ attribution: {
106
+ name: image.user,
107
+ link: `https://pixabay.com/users/${image.user}-${image.user_id}/`
108
+ },
109
+ avatar: image.userImageURL
110
+ }));
111
+ }
112
+ }
@@ -0,0 +1,20 @@
1
+ import { type ProviderResult } from '../../types.js';
2
+ export type Resolver = () => string | undefined;
3
+ export declare class Provider {
4
+ protected apiKey?: Resolver;
5
+ key: string;
6
+ name: string;
7
+ url: string;
8
+ constructor(key: string, name: string, url: string, apiKey?: Resolver);
9
+ fetch(method: string, urlPath: string, data?: object): Promise<unknown>;
10
+ get isConfigured(): boolean;
11
+ protected getApiKey(): string;
12
+ getFetchBaseUrl(): string;
13
+ getFetchHeaders(): Record<string, string>;
14
+ getFetchLimit(): number;
15
+ getFeatured(_filters?: Record<string, unknown>): Promise<unknown>;
16
+ getSearch(_query: string, _page: number): Promise<unknown>;
17
+ trackDownload(_url: string): null;
18
+ formatResults(_data: object): ProviderResult[];
19
+ }
20
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/endpoints/handler/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD,MAAM,MAAM,QAAQ,GAAG,MAAM,MAAM,GAAG,SAAS,CAAA;AAE/C,qBAAa,QAAQ;IACnB,SAAS,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;gBAEC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ;IAO/D,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAU7E,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,SAAS,CAAC,SAAS,IAAI,MAAM;IAU7B,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAIzC,aAAa,IAAI,MAAM;IAIvB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAIjE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1D,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIjC,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE;CAG/C"}
@@ -0,0 +1,53 @@
1
+ export class Provider {
2
+ apiKey;
3
+ key;
4
+ name;
5
+ url;
6
+ constructor(key, name, url, apiKey){
7
+ this.key = key;
8
+ this.name = name;
9
+ this.url = url;
10
+ this.apiKey = apiKey;
11
+ }
12
+ async fetch(method, urlPath, data) {
13
+ const response = await fetch(new URL(urlPath, this.getFetchBaseUrl()), {
14
+ method,
15
+ headers: this.getFetchHeaders(),
16
+ ...data && {
17
+ body: JSON.stringify(data)
18
+ }
19
+ });
20
+ return response.json();
21
+ }
22
+ get isConfigured() {
23
+ return Boolean(this.apiKey?.());
24
+ }
25
+ getApiKey() {
26
+ const key = this.apiKey?.();
27
+ if (!key) {
28
+ throw new Error(`Provider "${this.key}" is not configured`);
29
+ }
30
+ return key;
31
+ }
32
+ getFetchBaseUrl() {
33
+ return '';
34
+ }
35
+ getFetchHeaders() {
36
+ return {};
37
+ }
38
+ getFetchLimit() {
39
+ return 24;
40
+ }
41
+ getFeatured(_filters) {
42
+ return Promise.resolve({});
43
+ }
44
+ getSearch(_query, _page) {
45
+ return Promise.resolve({});
46
+ }
47
+ trackDownload(_url) {
48
+ return null;
49
+ }
50
+ formatResults(_data) {
51
+ return [];
52
+ }
53
+ }
@@ -0,0 +1,12 @@
1
+ import { type ProviderResult, type UnsplashFilters, type UnsplashResult } from '../../types.js';
2
+ import { Provider, type Resolver } from './provider.js';
3
+ export declare class Unsplash extends Provider {
4
+ constructor(apiKey: Resolver);
5
+ getFetchBaseUrl(): string;
6
+ getFetchHeaders(): Record<string, string>;
7
+ getFeatured(filters?: UnsplashFilters): Promise<unknown>;
8
+ getSearch(query: string, page: number, filters?: UnsplashFilters): Promise<unknown>;
9
+ trackDownload(url: string): null;
10
+ formatResults(data: UnsplashResult[]): ProviderResult[];
11
+ }
12
+ //# sourceMappingURL=unsplash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unsplash.d.ts","sourceRoot":"","sources":["../../../src/endpoints/handler/unsplash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/F,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAA;AAIvD,qBAAa,QAAS,SAAQ,QAAQ;gBACxB,MAAM,EAAE,QAAQ;IAInB,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAInC,WAAW,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAoCxD,SAAS,CACtB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,OAAO,CAAC;IAmBV,aAAa,CAAC,GAAG,EAAE,MAAM;IAMzB,aAAa,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE;CAqBjE"}
@@ -0,0 +1,82 @@
1
+ import { Provider } from './provider.js';
2
+ const UTMParams = 'utm_source=rubixstudios-payload-images&utm_medium=referral';
3
+ export class Unsplash extends Provider {
4
+ constructor(apiKey){
5
+ super('unsplash', 'Unsplash', 'https://unsplash.com', apiKey);
6
+ }
7
+ getFetchBaseUrl() {
8
+ return 'https://api.unsplash.com';
9
+ }
10
+ getFetchHeaders() {
11
+ return {
12
+ Authorization: `Client-ID ${this.getApiKey()}`
13
+ };
14
+ }
15
+ async getFeatured(filters) {
16
+ if (filters?.color || filters?.orientation) {
17
+ const params = new URLSearchParams({
18
+ per_page: String(this.getFetchLimit()),
19
+ query: 'featured'
20
+ });
21
+ if (filters.color) {
22
+ params.set('color', filters.color);
23
+ }
24
+ if (filters.orientation) {
25
+ params.set('orientation', filters.orientation);
26
+ }
27
+ const data = await this.fetch('GET', `/search/photos?${params.toString()}`);
28
+ return {
29
+ images: this.formatResults(data.results),
30
+ countOfImages: null,
31
+ countOfPages: null
32
+ };
33
+ }
34
+ const data = await this.fetch('GET', `/photos/random?featured=true&count=${this.getFetchLimit()}`);
35
+ return {
36
+ images: this.formatResults(data),
37
+ countOfImages: null,
38
+ countOfPages: null
39
+ };
40
+ }
41
+ async getSearch(query, page, filters) {
42
+ const params = new URLSearchParams({
43
+ per_page: String(this.getFetchLimit()),
44
+ page: String(page),
45
+ query
46
+ });
47
+ if (filters?.color) params.set('color', filters.color);
48
+ if (filters?.orientation) params.set('orientation', filters.orientation);
49
+ const data = await this.fetch('GET', `/search/photos?${params.toString()}`);
50
+ return {
51
+ images: this.formatResults(data.results),
52
+ totalImages: data.total,
53
+ totalPages: Math.min(data.total_pages, 100)
54
+ };
55
+ }
56
+ trackDownload(url) {
57
+ const downloadUrl = new URL(url);
58
+ void this.fetch('GET', `${downloadUrl.pathname}${downloadUrl.search}`);
59
+ return null;
60
+ }
61
+ formatResults(data) {
62
+ return data.map((image)=>({
63
+ id: image.id,
64
+ alt: image.alt_description || '',
65
+ width: image.width,
66
+ height: image.height,
67
+ color: image.color,
68
+ likes: image.likes,
69
+ avatar: image.user?.profile_image?.medium ?? undefined,
70
+ urls: {
71
+ view: image.urls?.thumb,
72
+ original: image.urls?.full,
73
+ download: `${image.links?.download}?${UTMParams}`,
74
+ downloadLocation: `${image.links.download_location}&${UTMParams}`
75
+ },
76
+ attribution: {
77
+ name: image.user?.name,
78
+ link: `${image.user?.links?.html}?${UTMParams}`
79
+ }
80
+ }));
81
+ }
82
+ }
@@ -0,0 +1,3 @@
1
+ import { type Endpoint } from 'payload';
2
+ export declare const providers: Endpoint[];
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/endpoints/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAA;AAOvC,eAAO,MAAM,SAAS,EAAE,QAAQ,EA6I/B,CAAA"}