Opening a detail screen issued two identical full-detail GETs for the same
id, concurrently: `_loadFullMetadata` calls `fetchItemWithOnDeck`, and
`_initWatchlistState` calls `fetchExternalIds`, which fetches the same item
purely to read `ProviderIds`. Playback start adds three more for its own id.
Each of those makes the server rebuild the entire dto — `People`, `Chapters`
and `MediaSources` cost a database query apiece and `Trickplay` costs several
plus a filesystem stat — so the duplicate is expensive on both ends.
`fetchItem` now shares an in-flight request per item id. Single-flight only:
once a request settles the next caller re-fetches, so nothing can serve a
stale item.
Measured on a remote Jellyfin server, 12 interleaved show-detail opens per
version: requests 4 -> 3, payload 28.4 KB -> 18.6 KB. Median wall time is
unchanged (1394ms -> 1386ms) because the duplicate ran alongside the first
rather than behind it; this removes duplicated work, not latency.
Two things were tried and rejected because measurement did not support them:
starting `/Shows/NextUp` in parallel with the detail fetch (the requests
contend rather than overlap — NextUp went from 380ms alone to 1395ms beside
it — and it costs a wasted request per movie), and dropping `Trickplay` /
`Chapters` from the detail field set (no measurable effect; both are real
data the playback path reads).
Refs #1784