|
|
@@ -1712,7 +1712,7 @@ export class VideoService {
|
|
|
try {
|
|
|
// Try to fetch from Redis cache first
|
|
|
const cached = await this.redis.getJson<VideoItemDto[]>(
|
|
|
- tsCacheKeys.video.recommended(),
|
|
|
+ tsCacheKeys.video.recommended(),
|
|
|
);
|
|
|
|
|
|
if (cached && Array.isArray(cached) && cached.length > 0) {
|
|
|
@@ -1784,4 +1784,70 @@ export class VideoService {
|
|
|
: undefined,
|
|
|
};
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Read the cached video list key built by box-mgnt-api.
|
|
|
+ */
|
|
|
+ async getVideoListFromCache(): Promise<VideoItemDto[]> {
|
|
|
+ const key = tsCacheKeys.video.list();
|
|
|
+ try {
|
|
|
+ const raw = await this.redis.get(key);
|
|
|
+ if (!raw) {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ const parsed = JSON.parse(raw);
|
|
|
+ if (Array.isArray(parsed)) {
|
|
|
+ return parsed;
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ this.logger.error(
|
|
|
+ `Failed to read video list cache (${key})`,
|
|
|
+ err instanceof Error ? err.stack : String(err),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Search the cached video list by secondTags, with fallback for videos that have no secondTags.
|
|
|
+ */
|
|
|
+ async searchVideosBySecondTags(tags?: string): Promise<VideoItemDto[]> {
|
|
|
+ const videos = await this.getVideoListFromCache();
|
|
|
+ if (!tags) {
|
|
|
+ return videos;
|
|
|
+ }
|
|
|
+
|
|
|
+ const requestedTags = tags
|
|
|
+ .split(',')
|
|
|
+ .map((tag) => tag.trim())
|
|
|
+ .filter((tag) => tag.length > 0);
|
|
|
+
|
|
|
+ if (requestedTags.length === 0) {
|
|
|
+ return videos;
|
|
|
+ }
|
|
|
+
|
|
|
+ const tagSet = new Set(requestedTags);
|
|
|
+ return videos.filter((video) => this.matchesSecondTags(video, tagSet));
|
|
|
+ }
|
|
|
+
|
|
|
+ private matchesSecondTags(
|
|
|
+ video: VideoItemDto,
|
|
|
+ filters: Set<string>,
|
|
|
+ ): boolean {
|
|
|
+ const secondTags = Array.isArray(video.secondTags)
|
|
|
+ ? video.secondTags
|
|
|
+ .map((tag) => tag?.trim())
|
|
|
+ .filter(
|
|
|
+ (tag): tag is string => typeof tag === 'string' && tag.length > 0,
|
|
|
+ )
|
|
|
+ : [];
|
|
|
+
|
|
|
+ if (secondTags.length === 0) {
|
|
|
+ // return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return secondTags.some((tag) => filters.has(tag));
|
|
|
+ }
|
|
|
}
|