CI / build-and-test (push) Successful in 14m10s
Barre de recherche - Panneau de filtres unifié (remplace le ProviderPicker) : sources, type (vidéos/shorts/direct/chaînes), période (heure/jour/semaine/mois/année), durée (<4 / 4-20 / >20 min) et tri. Pastilles + roving tabindex (←/→ dans un groupe, ↑/↓ entre groupes), focus trap, Échap, Ctrl/⌘+Maj+F, recherches récentes, « mémoriser par défaut », « Réinitialiser ». - Pastilles des filtres actifs sous la barre (retrait en un clic) + compteur. - Typeahead : un seul keydown (fin du double Enter), ↑/↓ bouclants, Home/End, PageUp/PageDown, Entrée valide la ligne surlignée (sinon recherche brute), Tab complète sans chercher, Échap ferme puis vide, ligne « Rechercher <q> », défilement de l'option active, aria à jour. - Focus : le panneau se referme dès que le focus quitte la barre (focusout + relatedTarget + activeElement), pointerdown neutralisé sur les lignes pour garder le focus dans l'input (plus de scintillement). - Opérateurs en clair : `linux live:`, `tuto today: long:` deviennent des filtres et sont retirés de la requête (autocomplétion après le `:`). Filtres côté serveur - Modèle partagé : src/app/search/filters.ts + server/search-filters.mjs. - /api/search?type=&duration=&period=&sort= : InnerTube (upload_date, type, duration, features), Data API v3 (type, videoDuration, publishedAfter), yt-dlp --dateafter ; post-filtrage pour les providers sans filtre natif. Un champ manquant ne fait jamais disparaître un résultat. - Filtres dans l'URL (partageables) + adapters + SearchService (clé de cache). - Correction : la recherche Shorts renvoyait 0 (ShortsLockupView/GridShelfView non mappés, id/titre dans l'endpoint) -> mappage + shelf + vignette/vues. Tests / docs - 23 tests unitaires (clavier, focus, opérateurs, panneau de filtres), server/tests/search-filters.test.mjs (npm run test:filters) + CI, scénarios e2e des filtres, README/api/README/todo/MCP à jour.
70 lines
2.5 KiB
Markdown
70 lines
2.5 KiB
Markdown
# NewTube Unified Search API
|
|
|
|
This document describes the unified search endpoint used by the frontend SearchBar and Search page.
|
|
|
|
Base URL
|
|
- Development: http://localhost:4000/api
|
|
- Production: /api
|
|
|
|
Endpoint
|
|
- GET /api/search
|
|
|
|
Query parameters
|
|
- q (string, required, min length 2)
|
|
- providers (string, optional): Comma-separated list of provider ids. Allowed values: yt, dm, tw, pt, od, ru. Defaults to all when omitted or invalid.
|
|
- page (integer, optional): Page index starting at 1. Default: 1.
|
|
- pageSize (integer, optional): Page size, max 50. Default: 24.
|
|
- sort (string, optional): relevance | date | views. Default: relevance.
|
|
- type (string, optional): video | shorts | live | channel. Default: all.
|
|
- duration (string, optional): short (< 4 min) | medium (4-20 min) | long (> 20 min). Default: all.
|
|
- period (string, optional): hour | today | week | month | year (upload date). Default: all.
|
|
|
|
Response
|
|
```
|
|
{
|
|
"q": "string",
|
|
"providers": ["yt", "dm", "tw", "pt", "od", "ru"],
|
|
"groups": {
|
|
"yt": [
|
|
{ "id": "string", "title": "string", "thumbnail": "string", "uploaderName": "string", "url": "string", "type": "video" }
|
|
],
|
|
"dm": [],
|
|
"tw": [],
|
|
"pt": [],
|
|
"od": [],
|
|
"ru": []
|
|
},
|
|
"page": 1,
|
|
"pageSize": 24,
|
|
"sort": "relevance",
|
|
"filters": { "type": "live", "period": "week" }
|
|
}
|
|
```
|
|
`filters` echoes the active filters (default values are omitted). Unknown filter
|
|
values are ignored (never a 4xx), so a typo silently degrades to "no filter".
|
|
|
|
Error responses
|
|
- 400 { error: "invalid_query", details: "min_length_2" }
|
|
- 500 { error: "search_failed", details: "..." }
|
|
|
|
Providers
|
|
- yt: YouTube
|
|
- dm: Dailymotion
|
|
- tw: Twitch
|
|
- pt: PeerTube
|
|
- od: Odysee
|
|
- ru: Rumble
|
|
|
|
Notes
|
|
- Each provider is queried in parallel with a per-provider limit equal to `pageSize`.
|
|
- The endpoint currently does not expose a `total` field; the frontend should offer a simple Next page affordance or infinite scroll when appropriate.
|
|
- Filters are applied natively when the provider supports them (YouTube: InnerTube
|
|
`upload_date` / `type` / `duration` / `features`, Data API v3 `type` /
|
|
`videoDuration` / `publishedAfter`, yt-dlp `--dateafter`), and refined in
|
|
post-processing otherwise (see `server/search-filters.mjs`).
|
|
- A result whose metadata is missing is never dropped by a filter: only values
|
|
that are present and out of range are filtered out.
|
|
- The `channel` type is only enforced for YouTube (the only provider that
|
|
returns channels as search results).
|
|
- `period=hour` is not a native InnerTube filter: it is applied in post-processing.
|