apilink / utils /patternLibrary.js
franyer24's picture
grand update
92a83a5 verified
Raw
History Blame Contribute Delete
4.65 kB
const fs = require('fs')
const path = require('path')
const LIB_PATH = path.join(__dirname, '..', 'config', 'patternLibrary.json')
let _cache = null
function load() {
if (_cache) return _cache
try {
_cache = JSON.parse(fs.readFileSync(LIB_PATH, 'utf-8'))
return _cache
} catch {
_cache = { version: 1, patterns: [] }
return _cache
}
}
function save() {
if (!_cache) return
fs.writeFileSync(LIB_PATH, JSON.stringify(_cache, null, 2), 'utf-8')
}
function matchByMediaType(mediaType) {
const lib = load()
return lib.patterns.filter(p => p.mediaType.includes(mediaType) && p.type !== 'search')
}
function matchBySite(baseUrl) {
const lib = load()
const domain = new URL(baseUrl).hostname.replace(/^www\./, '')
return lib.patterns.filter(p =>
p.sites.some(s => domain.includes(s.replace(/^www\./, '')))
)
}
function matchByExtractionPattern(html, limit = 3) {
const lib = load()
const hints = []
for (const p of lib.patterns) {
const method = p.extraction?.method || ''
const selector = p.extraction?.selector || ''
if (selector && html.includes(selector)) {
hints.push({ patternId: p.id, matchType: 'selector', confidence: 'high', pattern: p })
}
if (method === 'iframe_regex' && /video\[\d+\]/.test(html)) {
hints.push({ patternId: p.id, matchType: 'regex', confidence: 'high', pattern: p })
}
if (method === 'xorscrape' && html.includes('data-server')) {
hints.push({ patternId: p.id, matchType: 'data_attr', confidence: 'medium', pattern: p })
}
if (method === 'dooplayer_options' && html.includes('dooplay_player_option')) {
hints.push({ patternId: p.id, matchType: 'class', confidence: 'high', pattern: p })
}
if (method === 'dooplayer_ajax' && html.includes('dooplayer')) {
hints.push({ patternId: p.id, matchType: 'keyword', confidence: 'medium', pattern: p })
}
}
return hints.slice(0, limit)
}
function learnFromFix(providerName, baseUrl, mediaType, urlPattern, extractionMethod, htmlSample) {
const lib = load()
const domain = new URL(baseUrl).hostname
// Check if this site already has this pattern
const existing = lib.patterns.find(p =>
p.sites.some(s => domain.includes(s.replace(/^www\./, ''))) &&
p.urlTmpl === urlPattern
)
if (existing) {
existing.successCount = (existing.successCount || 1) + 1
if (!existing.sites.includes(domain)) existing.sites.push(domain)
save()
return { action: 'updated', pattern: existing }
}
// New pattern: extract as much as possible
const newPattern = {
id: `${mediaType}-${Date.now()}`,
urlTmpl: urlPattern,
mediaType: [mediaType],
slugSource: 'unknown',
extraction: {
method: extractionMethod || 'unknown',
step: 'auto_learned',
hint: 'Pattern auto-learned from successful repair',
},
sites: [domain],
successCount: 1,
autoLearned: true,
learnedAt: new Date().toISOString(),
}
lib.patterns.push(newPattern)
save()
return { action: 'added', pattern: newPattern }
}
function getDirectPatterns(mediaType) {
return matchByMediaType(mediaType)
}
function getDirectFromTitle(mediaType) {
const lib = load()
return lib.patterns.filter(p =>
p.mediaType.includes(mediaType) &&
p.type !== 'search' &&
(p.slugSource === 'title' || !p.slugSource)
)
}
function getSearchPatterns(mediaType) {
const lib = load()
return lib.patterns.filter(p => p.mediaType.includes(mediaType) && p.type === 'search')
}
function getAllForProvider(baseUrl, mediaType) {
const bySite = matchBySite(baseUrl)
const byType = matchByMediaType(mediaType)
const merged = [...bySite, ...byType]
const seen = new Set()
return merged.filter(p => {
if (seen.has(p.id)) return false
seen.add(p.id)
return true
})
}
function fillUrlTemplate(tmpl, vars) {
// vars: { slug, id, season, episode, query, year }
let url = tmpl
.replace(/{slug}/g, vars.slug || '')
.replace(/{id}/g, String(vars.id || ''))
.replace(/{animeId}/g, String(vars.animeId || vars.id || ''))
.replace(/{season}/g, String(vars.season || ''))
.replace(/{episode}/g, String(vars.episode || ''))
.replace(/{groupSlug}/g, vars.groupSlug || '')
.replace(/{query}/g, encodeURIComponent(vars.query || ''))
.replace(/{title}/g, encodeURIComponent(vars.title || ''))
.replace(/{year}/g, String(vars.year || ''))
return url
}
module.exports = {
load, save,
matchByMediaType,
matchBySite,
matchByExtractionPattern,
learnFromFix,
getDirectPatterns,
getDirectFromTitle,
getSearchPatterns,
getAllForProvider,
fillUrlTemplate,
}