@xhub-short/adapters 0.1.0-beta.16 → 0.1.0-beta.20
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/index.d.ts +37 -15
- package/dist/index.js +176 -35
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IDataSource,
|
|
1
|
+
import { IDataSource, ContentItem, FeedResponse, VideoItem, IPlaylistDataSource, PlaylistData, PlaylistCollectionResponse, ILogger, LogLevel, LogEntry, IStorage, ISessionStorage, SessionSnapshot, IInteraction, Comment, ICommentAdapter, MockCommentAdapterConfig, CommentListResponse, ReplyListResponse, PostCommentPayload, CommentItem, PostReplyPayload, ReplyItem, EditCommentPayload, DeleteCommentPayload, ReportCommentPayload, IAnalytics, AnalyticsEvent, INetworkAdapter, NetworkType, NetworkQuality, IPosterLoader, IVideoLoader, VideoSource, PreloadConfig, PreloadResult, PreloadStatus, ReportReason, CommentTransformers } from '@xhub-short/contracts';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* MockDataAdapter - Development/Testing Data Source
|
|
@@ -13,16 +13,20 @@ import { IDataSource, VideoItem, FeedResponse, IPlaylistDataSource, PlaylistData
|
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
15
|
declare class MockDataAdapter implements IDataSource {
|
|
16
|
-
private readonly
|
|
16
|
+
private readonly items;
|
|
17
17
|
private readonly pageSize;
|
|
18
18
|
private readonly delay;
|
|
19
19
|
constructor(options?: MockDataAdapterOptions);
|
|
20
20
|
/**
|
|
21
|
-
* Fetch a page of mock
|
|
21
|
+
* Fetch a page of mock items
|
|
22
22
|
*/
|
|
23
|
-
fetchFeed(cursor?: string): Promise<FeedResponse
|
|
23
|
+
fetchFeed(cursor?: string): Promise<FeedResponse<ContentItem>>;
|
|
24
|
+
/**
|
|
25
|
+
* Get a single content item by ID
|
|
26
|
+
*/
|
|
27
|
+
getContentDetail(id: string): Promise<ContentItem>;
|
|
24
28
|
/**
|
|
25
|
-
*
|
|
29
|
+
* @deprecated Use getContentDetail instead
|
|
26
30
|
*/
|
|
27
31
|
getVideoDetail(id: string): Promise<VideoItem>;
|
|
28
32
|
/**
|
|
@@ -38,8 +42,10 @@ declare class MockDataAdapter implements IDataSource {
|
|
|
38
42
|
* Configuration options for MockDataAdapter
|
|
39
43
|
*/
|
|
40
44
|
interface MockDataAdapterOptions {
|
|
41
|
-
/** Custom mock
|
|
42
|
-
|
|
45
|
+
/** Custom mock content data */
|
|
46
|
+
items?: ContentItem[];
|
|
47
|
+
/** @deprecated Use items instead */
|
|
48
|
+
videos?: ContentItem[];
|
|
43
49
|
/** Number of items per page (default: 3) */
|
|
44
50
|
pageSize?: number;
|
|
45
51
|
/** Simulated network delay in ms (default: 300) */
|
|
@@ -463,7 +469,7 @@ declare class MockInteractionAdapter implements IInteraction {
|
|
|
463
469
|
* @param reason - Report reason code
|
|
464
470
|
* @param description - Optional additional description
|
|
465
471
|
*/
|
|
466
|
-
report(
|
|
472
|
+
report(_videoId: string, _reason: string, _description?: string): Promise<void>;
|
|
467
473
|
/**
|
|
468
474
|
* Check if video is liked (for testing)
|
|
469
475
|
*
|
|
@@ -1148,9 +1154,13 @@ interface ReportReasonItem {
|
|
|
1148
1154
|
*/
|
|
1149
1155
|
interface TransformConfig {
|
|
1150
1156
|
/**
|
|
1151
|
-
* Transform single
|
|
1157
|
+
* Transform single content item from API response
|
|
1152
1158
|
* If not provided, uses default transform
|
|
1153
1159
|
*/
|
|
1160
|
+
contentItem?: (apiResponse: unknown) => ContentItem;
|
|
1161
|
+
/**
|
|
1162
|
+
* @deprecated Use contentItem instead. Will be removed in v3.0
|
|
1163
|
+
*/
|
|
1154
1164
|
videoItem?: (apiResponse: unknown) => VideoItem;
|
|
1155
1165
|
/**
|
|
1156
1166
|
* Transform feed response from API
|
|
@@ -1222,6 +1232,11 @@ interface FieldMapConfig {
|
|
|
1222
1232
|
* Example: { 'author.name': 'creator.display_name' }
|
|
1223
1233
|
*/
|
|
1224
1234
|
video?: Record<string, string>;
|
|
1235
|
+
/**
|
|
1236
|
+
* Article field mapping
|
|
1237
|
+
* Example: { 'author.name': 'creator.display_name' }
|
|
1238
|
+
*/
|
|
1239
|
+
article?: Record<string, string>;
|
|
1225
1240
|
/**
|
|
1226
1241
|
* FeedResponse field mapping
|
|
1227
1242
|
*/
|
|
@@ -2187,6 +2202,9 @@ declare function defaultFeedResponseTransform(apiResponse: unknown, fieldMap?: F
|
|
|
2187
2202
|
* Create transforms with custom overrides
|
|
2188
2203
|
*/
|
|
2189
2204
|
interface ResolvedTransforms {
|
|
2205
|
+
/** Map raw API item to ContentItem (Video or Article) */
|
|
2206
|
+
contentItem: (data: unknown) => ContentItem;
|
|
2207
|
+
/** @deprecated Use contentItem instead */
|
|
2190
2208
|
videoItem: (data: unknown) => VideoItem;
|
|
2191
2209
|
feedResponse: (data: unknown) => {
|
|
2192
2210
|
items: unknown[];
|
|
@@ -2202,8 +2220,8 @@ declare function createTransforms(config?: TransformConfig, logger?: ILogger): R
|
|
|
2202
2220
|
* REST Data Adapter - IDataSource implementation for REST APIs
|
|
2203
2221
|
*
|
|
2204
2222
|
* Implements:
|
|
2205
|
-
* - fetchFeed: GET /
|
|
2206
|
-
* -
|
|
2223
|
+
* - fetchFeed: GET /items with pagination
|
|
2224
|
+
* - getContentDetail: GET /items/:id
|
|
2207
2225
|
* - prefetch: Optional prefetch support
|
|
2208
2226
|
*/
|
|
2209
2227
|
|
|
@@ -2232,11 +2250,15 @@ declare class RESTDataAdapter implements IDataSource {
|
|
|
2232
2250
|
*/
|
|
2233
2251
|
fetchFeed(cursor?: string): Promise<FeedResponse>;
|
|
2234
2252
|
/**
|
|
2235
|
-
* Get
|
|
2253
|
+
* Get content detail by ID
|
|
2254
|
+
*/
|
|
2255
|
+
getContentDetail(id: string): Promise<ContentItem>;
|
|
2256
|
+
/**
|
|
2257
|
+
* @deprecated Use getContentDetail instead
|
|
2236
2258
|
*/
|
|
2237
2259
|
getVideoDetail(id: string): Promise<VideoItem>;
|
|
2238
2260
|
/**
|
|
2239
|
-
* Optional: Prefetch
|
|
2261
|
+
* Optional: Prefetch content
|
|
2240
2262
|
* This is a no-op by default, can be overridden if API supports batch fetch
|
|
2241
2263
|
*/
|
|
2242
2264
|
prefetch(ids: string[]): Promise<void>;
|
|
@@ -2245,9 +2267,9 @@ declare class RESTDataAdapter implements IDataSource {
|
|
|
2245
2267
|
*/
|
|
2246
2268
|
private unwrapResponse;
|
|
2247
2269
|
/**
|
|
2248
|
-
* Create fallback
|
|
2270
|
+
* Create fallback content item when transform fails
|
|
2249
2271
|
*/
|
|
2250
|
-
private
|
|
2272
|
+
private createFallbackItem;
|
|
2251
2273
|
}
|
|
2252
2274
|
|
|
2253
2275
|
/**
|
package/dist/index.js
CHANGED
|
@@ -549,20 +549,20 @@ var MOCK_VIDEOS = [
|
|
|
549
549
|
];
|
|
550
550
|
var MockDataAdapter = class {
|
|
551
551
|
constructor(options = {}) {
|
|
552
|
-
this.
|
|
552
|
+
this.items = options.items ?? options.videos ?? MOCK_VIDEOS;
|
|
553
553
|
this.pageSize = options.pageSize ?? 3;
|
|
554
554
|
this.delay = options.delay ?? 300;
|
|
555
555
|
}
|
|
556
556
|
/**
|
|
557
|
-
* Fetch a page of mock
|
|
557
|
+
* Fetch a page of mock items
|
|
558
558
|
*/
|
|
559
559
|
async fetchFeed(cursor) {
|
|
560
560
|
await this.simulateDelay();
|
|
561
561
|
const offset = cursor ? Number.parseInt(cursor, 10) : 0;
|
|
562
562
|
const start = offset;
|
|
563
563
|
const end = start + this.pageSize;
|
|
564
|
-
const items = this.
|
|
565
|
-
const hasMore = end < this.
|
|
564
|
+
const items = this.items.slice(start, end);
|
|
565
|
+
const hasMore = end < this.items.length;
|
|
566
566
|
const nextCursor = hasMore ? String(end) : null;
|
|
567
567
|
return {
|
|
568
568
|
items,
|
|
@@ -571,15 +571,21 @@ var MockDataAdapter = class {
|
|
|
571
571
|
};
|
|
572
572
|
}
|
|
573
573
|
/**
|
|
574
|
-
* Get a single
|
|
574
|
+
* Get a single content item by ID
|
|
575
575
|
*/
|
|
576
|
-
async
|
|
576
|
+
async getContentDetail(id) {
|
|
577
577
|
await this.simulateDelay();
|
|
578
|
-
const
|
|
579
|
-
if (!
|
|
580
|
-
throw new Error(`
|
|
578
|
+
const item = this.items.find((v) => v.id === id);
|
|
579
|
+
if (!item) {
|
|
580
|
+
throw new Error(`Content not found: ${id}`);
|
|
581
581
|
}
|
|
582
|
-
return
|
|
582
|
+
return item;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* @deprecated Use getContentDetail instead
|
|
586
|
+
*/
|
|
587
|
+
async getVideoDetail(id) {
|
|
588
|
+
return this.getContentDetail(id);
|
|
583
589
|
}
|
|
584
590
|
/**
|
|
585
591
|
* Prefetch videos (no-op for mock)
|
|
@@ -595,6 +601,7 @@ var MockDataAdapter = class {
|
|
|
595
601
|
}
|
|
596
602
|
}
|
|
597
603
|
};
|
|
604
|
+
var MOCK_ITEMS = MOCK_VIDEOS;
|
|
598
605
|
|
|
599
606
|
// src/playlist/MockPlaylistAdapter.ts
|
|
600
607
|
var MOCK_PLAYLISTS = [
|
|
@@ -641,10 +648,10 @@ var MockPlaylistAdapter = class {
|
|
|
641
648
|
const playlist = MOCK_PLAYLISTS.find((p) => p.id === id);
|
|
642
649
|
if (!playlist) throw new Error(`Playlist ${id} not found`);
|
|
643
650
|
let items = [];
|
|
644
|
-
if (id === "p1") items =
|
|
645
|
-
else if (id === "p2") items =
|
|
646
|
-
else if (id === "p3") items =
|
|
647
|
-
else if (id === "p4") items =
|
|
651
|
+
if (id === "p1") items = MOCK_ITEMS.slice(0, 4);
|
|
652
|
+
else if (id === "p2") items = MOCK_ITEMS.slice(4, 8);
|
|
653
|
+
else if (id === "p3") items = MOCK_ITEMS.slice(8, 12);
|
|
654
|
+
else if (id === "p4") items = MOCK_ITEMS.slice(12, 16);
|
|
648
655
|
return { ...playlist, items };
|
|
649
656
|
}
|
|
650
657
|
async fetchPlaylistCollection(cursor) {
|
|
@@ -1211,10 +1218,9 @@ var MockInteractionAdapter = class {
|
|
|
1211
1218
|
* @param reason - Report reason code
|
|
1212
1219
|
* @param description - Optional additional description
|
|
1213
1220
|
*/
|
|
1214
|
-
async report(
|
|
1221
|
+
async report(_videoId, _reason, _description) {
|
|
1215
1222
|
await this.simulateDelay();
|
|
1216
1223
|
this.maybeThrowError();
|
|
1217
|
-
console.log("[MockInteractionAdapter] Report called (mock)", { videoId, reason, description });
|
|
1218
1224
|
}
|
|
1219
1225
|
// ═══════════════════════════════════════════════════════════════
|
|
1220
1226
|
// TESTING HELPERS (not part of IInteraction interface)
|
|
@@ -2650,10 +2656,10 @@ var RESTDataAdapter = class {
|
|
|
2650
2656
|
const feedData = this.transforms.feedResponse(response);
|
|
2651
2657
|
const items = feedData.items.map((item) => {
|
|
2652
2658
|
try {
|
|
2653
|
-
return this.transforms.
|
|
2659
|
+
return this.transforms.contentItem(item);
|
|
2654
2660
|
} catch (error) {
|
|
2655
|
-
this.logger?.error("[RESTDataAdapter] Failed to transform
|
|
2656
|
-
return this.
|
|
2661
|
+
this.logger?.error("[RESTDataAdapter] Failed to transform content item", error);
|
|
2662
|
+
return this.createFallbackItem(item);
|
|
2657
2663
|
}
|
|
2658
2664
|
});
|
|
2659
2665
|
return {
|
|
@@ -2667,24 +2673,30 @@ var RESTDataAdapter = class {
|
|
|
2667
2673
|
}
|
|
2668
2674
|
}
|
|
2669
2675
|
/**
|
|
2670
|
-
* Get
|
|
2676
|
+
* Get content detail by ID
|
|
2671
2677
|
*/
|
|
2672
|
-
async
|
|
2678
|
+
async getContentDetail(id) {
|
|
2673
2679
|
try {
|
|
2674
2680
|
const response = await this.httpClient.request({
|
|
2675
2681
|
method: "GET",
|
|
2676
2682
|
path: this.endpoints.detail,
|
|
2677
2683
|
pathParams: { id }
|
|
2678
2684
|
});
|
|
2679
|
-
const
|
|
2680
|
-
return this.transforms.
|
|
2685
|
+
const itemData = this.unwrapResponse(response);
|
|
2686
|
+
return this.transforms.contentItem(itemData);
|
|
2681
2687
|
} catch (error) {
|
|
2682
|
-
this.logger?.error("[RESTDataAdapter]
|
|
2688
|
+
this.logger?.error("[RESTDataAdapter] getContentDetail failed", error);
|
|
2683
2689
|
throw error;
|
|
2684
2690
|
}
|
|
2685
2691
|
}
|
|
2686
2692
|
/**
|
|
2687
|
-
*
|
|
2693
|
+
* @deprecated Use getContentDetail instead
|
|
2694
|
+
*/
|
|
2695
|
+
async getVideoDetail(id) {
|
|
2696
|
+
return this.getContentDetail(id);
|
|
2697
|
+
}
|
|
2698
|
+
/**
|
|
2699
|
+
* Optional: Prefetch content
|
|
2688
2700
|
* This is a no-op by default, can be overridden if API supports batch fetch
|
|
2689
2701
|
*/
|
|
2690
2702
|
async prefetch(ids) {
|
|
@@ -2705,13 +2717,27 @@ var RESTDataAdapter = class {
|
|
|
2705
2717
|
return response;
|
|
2706
2718
|
}
|
|
2707
2719
|
/**
|
|
2708
|
-
* Create fallback
|
|
2720
|
+
* Create fallback content item when transform fails
|
|
2709
2721
|
*/
|
|
2710
|
-
|
|
2722
|
+
createFallbackItem(data) {
|
|
2711
2723
|
const obj = data ?? {};
|
|
2724
|
+
const id = String(obj.id ?? obj.video_id ?? obj.article_id ?? `fallback-${Date.now()}`);
|
|
2725
|
+
if (obj.type === "article" || obj.images || obj.photos) {
|
|
2726
|
+
return {
|
|
2727
|
+
type: "article",
|
|
2728
|
+
id,
|
|
2729
|
+
images: [],
|
|
2730
|
+
caption: "",
|
|
2731
|
+
author: { id: "unknown", name: "Unknown" },
|
|
2732
|
+
stats: { likes: 0, comments: 0, shares: 0, views: 0 },
|
|
2733
|
+
isLiked: false,
|
|
2734
|
+
isFollowing: false,
|
|
2735
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2712
2738
|
return {
|
|
2713
2739
|
type: "video",
|
|
2714
|
-
id
|
|
2740
|
+
id,
|
|
2715
2741
|
source: {
|
|
2716
2742
|
url: String(obj.video_url ?? obj.url ?? ""),
|
|
2717
2743
|
type: "mp4"
|
|
@@ -2725,7 +2751,8 @@ var RESTDataAdapter = class {
|
|
|
2725
2751
|
views: 0,
|
|
2726
2752
|
likes: 0,
|
|
2727
2753
|
comments: 0,
|
|
2728
|
-
shares: 0
|
|
2754
|
+
shares: 0,
|
|
2755
|
+
bookmarks: 0
|
|
2729
2756
|
},
|
|
2730
2757
|
isLiked: false,
|
|
2731
2758
|
isFollowing: false,
|
|
@@ -3535,7 +3562,7 @@ var HttpClient = class {
|
|
|
3535
3562
|
};
|
|
3536
3563
|
|
|
3537
3564
|
// src/preset/transforms/playlist.ts
|
|
3538
|
-
function defaultPlaylistTransform(apiResponse,
|
|
3565
|
+
function defaultPlaylistTransform(apiResponse, contentItemTransform, logger) {
|
|
3539
3566
|
if (!apiResponse || typeof apiResponse !== "object") {
|
|
3540
3567
|
logger?.error("[PlaylistTransform] Invalid API response", void 0, {
|
|
3541
3568
|
apiResponse: String(apiResponse)
|
|
@@ -3551,7 +3578,7 @@ function defaultPlaylistTransform(apiResponse, videoItemTransform, logger) {
|
|
|
3551
3578
|
}
|
|
3552
3579
|
const items = rawItems.map((item) => {
|
|
3553
3580
|
try {
|
|
3554
|
-
return
|
|
3581
|
+
return contentItemTransform(item);
|
|
3555
3582
|
} catch (error) {
|
|
3556
3583
|
logger?.error("[PlaylistTransform] Failed to transform item", error);
|
|
3557
3584
|
return null;
|
|
@@ -3673,9 +3700,11 @@ function defaultAuthorTransform(data) {
|
|
|
3673
3700
|
return {
|
|
3674
3701
|
id: toSafeString(tryFields(author, "id", "user_id", "author_id"), ""),
|
|
3675
3702
|
name: toSafeString(
|
|
3676
|
-
tryFields(author, "display_name", "name", "username", "nickname"),
|
|
3703
|
+
tryFields(author, "display_name", "name", "username", "nickname", "first_name"),
|
|
3677
3704
|
"Unknown"
|
|
3678
3705
|
),
|
|
3706
|
+
// Combine first/last name if present and name is default
|
|
3707
|
+
// Note: This is a simple fallback, specialized logic should be in a custom transform if needed
|
|
3679
3708
|
avatar: toSafeString(
|
|
3680
3709
|
tryFields(author, "avatar", "avatar_url", "profile_picture", "photo"),
|
|
3681
3710
|
void 0
|
|
@@ -3691,7 +3720,8 @@ function defaultStatsTransform(data) {
|
|
|
3691
3720
|
views: toNumber(tryFields(stats, "view_count", "views", "play_count"), 0),
|
|
3692
3721
|
likes: toNumber(tryFields(stats, "like_count", "likes", "digg_count"), 0),
|
|
3693
3722
|
comments: toNumber(tryFields(stats, "comment_count", "comments"), 0),
|
|
3694
|
-
shares: toNumber(tryFields(stats, "share_count", "shares"), 0)
|
|
3723
|
+
shares: toNumber(tryFields(stats, "share_count", "shares"), 0),
|
|
3724
|
+
bookmarks: toNumber(tryFields(stats, "bookmark_count", "bookmarks", "saves"), 0)
|
|
3695
3725
|
};
|
|
3696
3726
|
}
|
|
3697
3727
|
function defaultVideoItemTransform(apiResponse, fieldMap, logger) {
|
|
@@ -3745,6 +3775,110 @@ function defaultVideoItemTransform(apiResponse, fieldMap, logger) {
|
|
|
3745
3775
|
};
|
|
3746
3776
|
return videoItem;
|
|
3747
3777
|
}
|
|
3778
|
+
function defaultArticleItemTransform(apiResponse, fieldMap, logger) {
|
|
3779
|
+
const obj = apiResponse;
|
|
3780
|
+
const getMapped = (sdkField, ...fallbacks) => {
|
|
3781
|
+
if (fieldMap?.[sdkField]) {
|
|
3782
|
+
const mapped = getNestedValue(obj, fieldMap[sdkField]);
|
|
3783
|
+
if (mapped !== void 0) return mapped;
|
|
3784
|
+
}
|
|
3785
|
+
return tryFields(obj, ...fallbacks);
|
|
3786
|
+
};
|
|
3787
|
+
const id = toSafeString(getMapped("id", "id", "article_id", "post_id", "_id"), "");
|
|
3788
|
+
if (!id) {
|
|
3789
|
+
logger?.warn("[Transform article] Missing required field: id", { data: obj });
|
|
3790
|
+
}
|
|
3791
|
+
let images = getMapped("images", "images", "media", "photos", "gallery") ?? [];
|
|
3792
|
+
if (!Array.isArray(images) || images.length === 0) {
|
|
3793
|
+
const singleImage = toSafeString(
|
|
3794
|
+
getMapped("image", "image_url", "url", "thumbnail", "cover"),
|
|
3795
|
+
void 0
|
|
3796
|
+
);
|
|
3797
|
+
images = singleImage ? [singleImage] : [];
|
|
3798
|
+
}
|
|
3799
|
+
return {
|
|
3800
|
+
type: "article",
|
|
3801
|
+
id,
|
|
3802
|
+
images,
|
|
3803
|
+
caption: toSafeString(
|
|
3804
|
+
getMapped("caption", "caption", "description", "text", "content", "title", "subject"),
|
|
3805
|
+
""
|
|
3806
|
+
),
|
|
3807
|
+
author: defaultAuthorTransform(obj),
|
|
3808
|
+
stats: defaultStatsTransform(obj),
|
|
3809
|
+
isLiked: toBoolean(getMapped("isLiked", "is_liked", "liked", "user_liked"), false),
|
|
3810
|
+
isFollowing: toBoolean(getMapped("isFollowing", "is_following", "following"), false),
|
|
3811
|
+
createdAt: toSafeString(
|
|
3812
|
+
getMapped("createdAt", "created_at", "create_time", "timestamp"),
|
|
3813
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
3814
|
+
),
|
|
3815
|
+
hashtags: getMapped("hashtags", "hashtags", "tags", "hash_tags") ?? []
|
|
3816
|
+
};
|
|
3817
|
+
}
|
|
3818
|
+
function extractMediaInfo(obj) {
|
|
3819
|
+
const mediaList = tryFields(obj, "media", "medias") ?? [];
|
|
3820
|
+
let videoMedia;
|
|
3821
|
+
let imageMedias = [];
|
|
3822
|
+
if (Array.isArray(mediaList) && mediaList.length > 0) {
|
|
3823
|
+
videoMedia = mediaList.find((m) => m.type === "video");
|
|
3824
|
+
imageMedias = mediaList.filter((m) => m.type === "image");
|
|
3825
|
+
}
|
|
3826
|
+
return { videoMedia, imageMedias };
|
|
3827
|
+
}
|
|
3828
|
+
function extractMusicInfo(obj) {
|
|
3829
|
+
const soundObj = tryFields(obj, "sound", "music", "audio");
|
|
3830
|
+
if (soundObj && (soundObj.id || soundObj.url || soundObj.name || soundObj.title)) {
|
|
3831
|
+
return {
|
|
3832
|
+
id: toSafeString(tryFields(soundObj, "id", "uuid"), "unknown"),
|
|
3833
|
+
title: toSafeString(tryFields(soundObj, "title", "name", "song"), "Unknown Sound"),
|
|
3834
|
+
artist: toSafeString(tryFields(soundObj, "artist", "author", "singer"), "Unknown Artist"),
|
|
3835
|
+
cover: toSafeString(tryFields(soundObj, "cover", "poster", "thumbnail", "image"), void 0),
|
|
3836
|
+
audioUrl: toSafeString(tryFields(soundObj, "url", "audio_url", "download_url"), void 0)
|
|
3837
|
+
};
|
|
3838
|
+
}
|
|
3839
|
+
return void 0;
|
|
3840
|
+
}
|
|
3841
|
+
function defaultContentItemTransform(apiResponse, fieldMap, logger) {
|
|
3842
|
+
const obj = apiResponse;
|
|
3843
|
+
const { videoMedia, imageMedias } = extractMediaInfo(obj);
|
|
3844
|
+
const musicInfo = extractMusicInfo(obj);
|
|
3845
|
+
const typeField = toSafeString(tryFields(obj, "type", "content_type", "item_type")).toLowerCase();
|
|
3846
|
+
let isVideo = false;
|
|
3847
|
+
if (["article", "image", "post", "photo"].includes(typeField)) {
|
|
3848
|
+
isVideo = false;
|
|
3849
|
+
} else if (["video", "reel", "short"].includes(typeField)) {
|
|
3850
|
+
isVideo = true;
|
|
3851
|
+
} else {
|
|
3852
|
+
if (videoMedia || tryFields(obj, "video_url", "playback_url", "source")) {
|
|
3853
|
+
isVideo = true;
|
|
3854
|
+
} else if (imageMedias.length > 0 || tryFields(obj, "images", "photos", "gallery")) {
|
|
3855
|
+
isVideo = false;
|
|
3856
|
+
} else {
|
|
3857
|
+
isVideo = true;
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
if (isVideo) {
|
|
3861
|
+
if (videoMedia) {
|
|
3862
|
+
Object.assign(obj, {
|
|
3863
|
+
video_url: videoMedia.url || videoMedia.download_url,
|
|
3864
|
+
poster: videoMedia.poster || videoMedia.thumbnail,
|
|
3865
|
+
duration: videoMedia.duration
|
|
3866
|
+
});
|
|
3867
|
+
}
|
|
3868
|
+
const item2 = defaultVideoItemTransform(apiResponse, fieldMap?.video, logger);
|
|
3869
|
+
if (musicInfo) item2.music = musicInfo;
|
|
3870
|
+
return item2;
|
|
3871
|
+
}
|
|
3872
|
+
if (imageMedias.length > 0) {
|
|
3873
|
+
const imageUrls = imageMedias.map((m) => toSafeString(m.url || m.download_url)).filter(Boolean);
|
|
3874
|
+
if (imageUrls.length > 0) {
|
|
3875
|
+
obj.images = imageUrls;
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
const item = defaultArticleItemTransform(apiResponse, fieldMap?.article, logger);
|
|
3879
|
+
if (musicInfo) item.music = musicInfo;
|
|
3880
|
+
return item;
|
|
3881
|
+
}
|
|
3748
3882
|
function defaultFeedResponseTransform(apiResponse, fieldMap, logger) {
|
|
3749
3883
|
const obj = apiResponse;
|
|
3750
3884
|
const itemsPath = fieldMap?.items;
|
|
@@ -3798,12 +3932,19 @@ function defaultFeedResponseTransform(apiResponse, fieldMap, logger) {
|
|
|
3798
3932
|
return { items, nextCursor, hasMore };
|
|
3799
3933
|
}
|
|
3800
3934
|
function createTransforms(config, logger) {
|
|
3935
|
+
const contentItem = (data) => {
|
|
3936
|
+
if (config?.contentItem) return config.contentItem(data);
|
|
3937
|
+
if (config?.videoItem) return config.videoItem(data);
|
|
3938
|
+
return defaultContentItemTransform(data, config?.fieldMap, logger);
|
|
3939
|
+
};
|
|
3801
3940
|
return {
|
|
3802
|
-
|
|
3941
|
+
contentItem,
|
|
3942
|
+
videoItem: (data) => contentItem(data),
|
|
3803
3943
|
feedResponse: config?.feedResponse ? config.feedResponse : (data) => defaultFeedResponseTransform(data, config?.fieldMap?.feed, logger),
|
|
3804
3944
|
playlist: config?.playlist ? config.playlist : (data) => defaultPlaylistTransform(
|
|
3805
3945
|
data,
|
|
3806
|
-
(item) =>
|
|
3946
|
+
(item) => contentItem(item),
|
|
3947
|
+
// Pass the generic contentItem transform
|
|
3807
3948
|
logger
|
|
3808
3949
|
),
|
|
3809
3950
|
playlistCollection: config?.playlistCollection ? config.playlistCollection : (data) => defaultPlaylistCollectionTransform(data, logger)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xhub-short/adapters",
|
|
3
3
|
"sideEffects": false,
|
|
4
|
-
"version": "0.1.0-beta.
|
|
4
|
+
"version": "0.1.0-beta.20",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"dist"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@xhub-short/contracts": "0.1.0-beta.
|
|
23
|
+
"@xhub-short/contracts": "0.1.0-beta.20"
|
|
24
24
|
},
|
|
25
25
|
"optionalDependencies": {
|
|
26
26
|
"hls.js": "^1.5.0"
|