API
Και η αναζήτηση είναι στατικό ευρετήριο: περίπου 7.000 μικρά αρχεία που μαζί καλύπτουν κάθε όνομα κάθε σειράς, ταινίας και προσώπου, σε κάθε γλώσσα. Ένα ερώτημα χρειάζεται ένα από αυτά.
Αρχεία
https://api.watchittrackit.com/v1/search/manifest.jsonhttps://api.watchittrackit.com/v1/search/{build}/tree.jsonhttps://api.watchittrackit.com/v1/search/{build}/l/{hex}.jsonhttps://api.watchittrackit.com/v1/search/{build}/s/{hex}.jsonhttps://api.watchittrackit.com/v1/search/recent.json
Αναζήτηση ενός ερωτήματος
- Διαβάστε το
v1/search/manifest.jsonγια το id της τρέχουσας έκδοσης. - Διαβάστε το
v1/search/{build}/tree.json. Δεν αλλάζει ποτέ για μια έκδοση, οπότε κατεβάστε το μία φορά. - Κανονικοποιήστε το ερώτημα: μετατρέψτε το σε πεζά, αφαιρέστε τόνους και σημεία στίξης. Αναζητήστε το με τη μεγαλύτερη λέξη του που δεν είναι λέξη διακοπής.
- Διασχίστε το δέντρο με αυτή τη λέξη για να βρείτε το αρχείο της: ένα bucket στο
l/ή, αν η λέξη τελειώνει ακριβώς σε ένα πρόθεμα διαχωρισμού, μια σύνοψη στοs/. Τα ονόματα αρχείων είναι η δεκαεξαδική μορφή των byte του προθέματος σε UTF-8. - Κρατήστε τις καταχωρίσεις όπου κάθε λέξη του ερωτήματος αποτελεί την αρχή κάποιας λέξης του ονόματος. Βάλτε πρώτα τα ονόματα που ξεκινούν με ολόκληρο το ερώτημα και έπειτα ταξινομήστε κατά δημοφιλία.
- Εφαρμόστε το
v1/search/recent.json: περιέχει τις εγγραφές που άλλαξαν μετά την έκδοση. Απορρίψτε τις καταχωρίσεις του ευρετηρίου για αυτές τις εγγραφές και χρησιμοποιήστε αυτές στη θέση τους. Αλλάζει καθώς επεξεργάζονται εγγραφές, οπότε προσθέστε μια συμβολοσειρά ερωτήματος που αλλάζει κάθε λίγα λεπτά (?w=συν το τρέχον 5λεπτο παράθυρο) για να λάβετε νέο αντίγραφο.
Καταχωρίσεις
Κάθε καταχώριση είναι πίνακας:
| Θέση | Τύπος | Περιγραφή |
|---|---|---|
| 0 | string | Τύπος εγγραφής. |
| 1 | number | Id εγγραφής. |
| 2 | string | Το όνομα που ταίριαξε, σε όποια γλώσσα κι αν είναι. |
| 3 | number | Κατάταξη εντός του τύπου: το 1.000.000 είναι το πιο δημοφιλές. |
| 4 | string | Slug εγγραφής. |
| 5 | number | null | Έτος, για τίτλους. |
| 6 | string | null | Διαδρομή μικρογραφίας. |
Παράδειγμα (JavaScript)
const BASE = "https://api.watchittrackit.com/v1/search"
const STOPWORDS = new Set(["a", "an", "and", "the", "of", "in", "on", "to", "for", "at", "by", "with", "or",
"de", "del", "la", "le", "les", "el", "los", "las", "lo", "un", "une", "et", "du", "des", "da", "do", "dos",
"der", "die", "das", "und", "ein", "eine", "den", "dem", "von", "zu", "il", "i", "e", "di", "y", "en", "het", "een", "van"])
const normalize = (s) => s.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase()
.replace(/[^\p{L}\p{N}]+/gu, " ").trim()
const hex = (s) => [...new TextEncoder().encode(s)].map((b) => b.toString(16).padStart(2, "0")).join("")
async function search(query) {
const { build } = await fetch(`${BASE}/manifest.json`).then((r) => r.json())
const tree = await fetch(`${BASE}/${build}/tree.json`).then((r) => r.json())
const q = normalize(query)
const words = q.split(" ").filter(Boolean)
const real = words.filter((w) => !STOPWORDS.has(w))
const word = (real.length ? real : words).sort((a, b) => [...b].length - [...a].length)[0]
// Walk the tree: descend while the prefix is split; stop at a bucket or a summary.
let key = "", file
for (const c of word) {
if (tree[key + c]) { key += c; continue }
const starts = tree[key] ?? []
const start = starts.filter((s) => s <= c).pop() ?? starts[0] ?? c
file = `l/${hex(key + start)}.json`
break
}
file ??= `s/${hex(key)}.json`
const res = await fetch(`${BASE}/${build}/${file}`)
let entries = res.ok ? await res.json() : []
// Records changed since the build replace the index's entries for them.
// The query string changes every 5 minutes, so caches don't serve an old copy.
const recent = await fetch(`${BASE}/recent.json?w=${Math.floor(Date.now() / 300000)}`).then((r) => r.json())
entries = entries.filter(([type, id]) => !(`${type}:${id}` in recent.records))
entries.push(...Object.values(recent.records).flat())
const seen = new Set()
return entries
.filter((e) => { const n = normalize(e[2]).split(" "); return words.every((w) => n.some((x) => x.startsWith(w))) })
.sort((a, b) => Number(normalize(b[2]).startsWith(q)) - Number(normalize(a[2]).startsWith(q)) || b[3] - a[3])
.filter(([type, id]) => !seen.has(`${type}:${id}`) && seen.add(`${type}:${id}`))
.slice(0, 30)
}
// [["tv", 81189, "Breaking Bad", 999998, "breaking-bad", 2008, "/images/tv/81189/….t.jpg"], …]
console.log(await search("breaking bad"))Το ευρετήριο αναδημιουργείται καθημερινά. Τα αρχεία μιας έκδοσης δεν αλλάζουν ποτέ, οπότε αποθηκεύστε τα στην cache όσο θέλετε· αλλάζουν μόνο το manifest και το recent.json.