feat(api): Prime Video scraping ve saglayiciya duyarlı metadata destegi ekle
This commit is contained in:
@@ -6,21 +6,29 @@ import { MetricsService } from './metrics.service.js';
|
||||
import { CacheService } from './cache.service.js';
|
||||
import { ContentService } from './content.service.js';
|
||||
import type { AdminActionResponse, AdminOverviewResponse } from '../types/index.js';
|
||||
import { parseSupportedContentUrl } from '../utils/contentUrl.js';
|
||||
|
||||
const CACHE_PREFIX = 'netflix:content:';
|
||||
const CACHE_PREFIX = 'content:';
|
||||
const MAX_CACHE_KEYS_FOR_ANALYSIS = 1000;
|
||||
|
||||
function formatCacheKeyLabel(key: string): string {
|
||||
return key.replace(CACHE_PREFIX, '');
|
||||
}
|
||||
|
||||
function extractTitleIdFromCacheKey(key: string): string | null {
|
||||
function extractProviderIdFromCacheKey(key: string): { provider: string; id: string } | null {
|
||||
const normalized = formatCacheKeyLabel(key);
|
||||
return /^\d+$/.test(normalized) ? normalized : null;
|
||||
const match = normalized.match(/^(netflix|primevideo):([A-Za-z0-9]+)$/);
|
||||
if (!match) return null;
|
||||
const provider = match[1];
|
||||
const id = match[2];
|
||||
if (!provider || !id) return null;
|
||||
return { provider, id };
|
||||
}
|
||||
|
||||
function extractTitleIdFromUrl(url: string): string | null {
|
||||
return url.match(/\/title\/(\d+)/)?.[1] ?? null;
|
||||
function extractProviderIdFromUrl(url: string): { provider: string; id: string } | null {
|
||||
const parsed = parseSupportedContentUrl(url);
|
||||
if (!parsed) return null;
|
||||
return { provider: parsed.provider, id: parsed.id };
|
||||
}
|
||||
|
||||
function parseRedisInfoValue(info: string, key: string): number | null {
|
||||
@@ -144,16 +152,25 @@ export class AdminService {
|
||||
min30Plus: 0,
|
||||
};
|
||||
|
||||
const cacheTitleIds = Array.from(
|
||||
new Set(cacheKeys.map((key) => extractTitleIdFromCacheKey(key)).filter((id): id is string => Boolean(id)))
|
||||
const cacheProviderIds = Array.from(
|
||||
new Set(
|
||||
cacheKeys
|
||||
.map((key) => extractProviderIdFromCacheKey(key))
|
||||
.filter((item): item is { provider: string; id: string } => Boolean(item))
|
||||
.map((item) => `${item.provider}:${item.id}`)
|
||||
)
|
||||
);
|
||||
|
||||
const relatedContent = cacheTitleIds.length
|
||||
const relatedContent = cacheProviderIds.length
|
||||
? await prisma.content.findMany({
|
||||
where: {
|
||||
OR: cacheTitleIds.map((id) => ({
|
||||
url: { contains: `/title/${id}` },
|
||||
})),
|
||||
OR: cacheProviderIds.map((providerId) => {
|
||||
const [provider, id] = providerId.split(':');
|
||||
if (provider === 'primevideo') {
|
||||
return { url: { contains: `/detail/${id}` } };
|
||||
}
|
||||
return { url: { contains: `/title/${id}` } };
|
||||
}),
|
||||
},
|
||||
select: {
|
||||
url: true,
|
||||
@@ -164,9 +181,12 @@ export class AdminService {
|
||||
|
||||
const titleMap = new Map<string, string>();
|
||||
for (const item of relatedContent) {
|
||||
const id = extractTitleIdFromUrl(item.url);
|
||||
if (id && !titleMap.has(id)) {
|
||||
titleMap.set(id, item.title);
|
||||
const parsed = extractProviderIdFromUrl(item.url);
|
||||
if (parsed) {
|
||||
const key = `${parsed.provider}:${parsed.id}`;
|
||||
if (!titleMap.has(key)) {
|
||||
titleMap.set(key, item.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +216,7 @@ export class AdminService {
|
||||
|
||||
if (ttlValue > 0) {
|
||||
const formattedKey = formatCacheKeyLabel(cacheKeys[i] || '');
|
||||
const titleId = extractTitleIdFromCacheKey(cacheKeys[i] || '');
|
||||
const providerId = extractProviderIdFromCacheKey(cacheKeys[i] || '');
|
||||
const rawValue = valueResults?.[i]?.[1];
|
||||
let cachedAt: number | null = null;
|
||||
if (typeof rawValue === 'string') {
|
||||
@@ -209,7 +229,9 @@ export class AdminService {
|
||||
}
|
||||
expiringSoon.push({
|
||||
key: formattedKey,
|
||||
mediaTitle: titleId ? titleMap.get(titleId) ?? null : null,
|
||||
mediaTitle: providerId
|
||||
? titleMap.get(`${providerId.provider}:${providerId.id}`) ?? null
|
||||
: null,
|
||||
cachedAt,
|
||||
ttlSeconds: ttlValue,
|
||||
});
|
||||
@@ -450,6 +472,23 @@ export class AdminService {
|
||||
details: `Stale content refresh queued for items older than ${days} days`,
|
||||
};
|
||||
}
|
||||
|
||||
static async purgeAllContent(): Promise<AdminActionResponse> {
|
||||
const totalContent = await prisma.content.count();
|
||||
|
||||
await prisma.$transaction([
|
||||
prisma.content.deleteMany({}),
|
||||
prisma.genre.deleteMany({}),
|
||||
]);
|
||||
|
||||
await CacheService.clearAll();
|
||||
|
||||
return {
|
||||
queued: totalContent,
|
||||
skipped: 0,
|
||||
details: 'Tum icerik verileri veritabanindan silindi',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default AdminService;
|
||||
|
||||
Reference in New Issue
Block a user