Watch It? Track It.

البحث أيضًا فهرس ثابت: نحو 7,000 ملف صغير تغطي مجتمعةً كل اسم لكل مسلسل وفيلم وشخص، بكل اللغات. يحتاج الاستعلام الواحد إلى ملف واحد منها.

الملفات

  • https://api.watchittrackit.com/v1/search/manifest.json
  • https://api.watchittrackit.com/v1/search/{build}/tree.json
  • https://api.watchittrackit.com/v1/search/{build}/l/{hex}.json
  • https://api.watchittrackit.com/v1/search/{build}/s/{hex}.json
  • https://api.watchittrackit.com/v1/search/recent.json

البحث عن استعلام

  1. اقرأ v1/search/manifest.json للحصول على معرّف البناء الحالي.
  2. اقرأ v1/search/{build}/tree.json. لا يتغيّر أبدًا ضمن البناء الواحد، لذا اجلبه مرة واحدة.
  3. وحّد صيغة الاستعلام: حوّله إلى أحرف صغيرة، واحذف علامات التشكيل وعلامات الترقيم. ابحث عنه بأطول كلمة فيه ليست من كلمات التوقف.
  4. تتبّع الشجرة بتلك الكلمة للعثور على ملفها: حاوية في l/، أو ملخّص في s/ إذا انتهت الكلمة تمامًا عند بادئة تقسيم. أسماء الملفات هي التمثيل الست عشري لبايتات البادئة بترميز UTF-8.
  5. أبقِ العناصر التي تكون فيها كل كلمة من الاستعلام بدايةً لإحدى كلمات الاسم. ضع أولًا الأسماء التي تبدأ بالاستعلام كاملًا، ثم رتّب حسب الشعبية.
  6. طبّق v1/search/recent.json: فهو يسرد السجلات التي تغيّرت منذ البناء. احذف عناصر الفهرس الخاصة بتلك السجلات واستخدم هذه بدلًا منها. يتغيّر كلما عُدّلت السجلات، لذا أضف سلسلة استعلام تتغيّر كل بضع دقائق (?w= متبوعة بنافذة الدقائق الخمس الحالية) للحصول على نسخة حديثة.

العناصر

كل عنصر مصفوفة:

الموضعالنوعالوصف
0stringنوع السجل.
1numberمعرّف السجل.
2stringالاسم المطابق، بأي لغة كان.
3numberالترتيب ضمن نوعه: 1,000,000 هو الأكثر شعبية.
4stringالمعرّف النصي للسجل.
5number | nullالسنة، للأعمال.
6string | 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"))

يُعاد بناء الفهرس يوميًا. ملفات البناء لا تتغيّر أبدًا، لذا خزّنها مؤقتًا للمدة التي تريدها؛ لا يتغيّر إلا ملف manifest وملف recent.json.