Files
iptvnator/apps/web/src/assets/i18n/tr.json
T
4grayandClaude Opus 5 063662028a feat(portals): find the same movie in your other playlists (#1286)
* feat(portals): find the same movie in your other playlists

A movie that exists in several imported Xtream playlists now shows a
"Sources N" chip on its detail page and in the player. Switching playlist
mid-film keeps the timecode, a preferred source can be pinned per movie, and
a failed stream offers the alternatives instead of a dead end.

The governing rule is that a guess is never presented as a fact. Every
metadata value carries where it came from — `api` (the provider said so),
`parsed` (inferred from the title) or `probe` (we contacted the stream).
Facts render as plain tags, guesses are prefixed `~` in a warning colour, and
an unknown value renders no tag at all plus a "check" affordance. Ranking and
failover read through `factualOnly()`, so a filename claiming 4K is
structurally unable to outrank a source that was actually reached. A probe
that could not complete reports "unknown", never "unavailable".

Scope is deliberately narrow: Xtream to Xtream, movies only, Electron only.
Stalker never reaches the `content` table and M3U is a JSON blob whose search
forces live content; both are additive later, since the candidate type
already carries all three portal kinds. In the PWA every entry point is gated
off and the chip renders nothing.

Auto-failover is opt-in and off by default. Each source is tried at most once
per session, so it terminates structurally, and the switch is never silent —
the toast names the new playlist, offers an undo, and warns that the dub may
differ only when both sides state an audio track as fact.

Notable details:
- Playlist names are routinely the pasted URL, credentials included. They are
  never rendered raw; a short host-only label is derived instead.
- Quality is derived from pixel width, not height: a 2.39:1 1080p master is
  1920x800, and bucketing that by height would publish "720p" as a fact.
- Switching is a single `inlinePlayback.set()` so the player and engine
  survive and re-seek; the carried position is read before the 15s
  persistence throttle so it does not rewind.
- Sources from one playlist collapse into a group, since the same film often
  appears there several times under different stream ids.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): stop stale source resolutions from committing

Addresses three defects Greptile found in the multi-source review.

**Concurrent switches committed out of order.** Selecting a second source
before the first resolution returned let the slower request overwrite the
newer selection and repoint Undo at itself. `switchTo` now takes a sequence
number and drops its result if a newer switch already committed.

**Stale switches crossed movie sessions.** Navigating to another film while a
resolution was in flight let the continuation activate the old film's source
inside the new controller — and restart it from that session's zero resume
position. The controller is now snapshotted per operation and the movie
session is revalidated after every await. `check()` had the same hazard across
its two awaits and is guarded the same way.

**Short titles skipped discovery entirely.** The trigram tokenizer cannot index
tokens under three characters, so "Up", "It" or "Us" produced an empty MATCH
expression and the query was discarded before SQLite was consulted — the chip
could never appear for those films. Discovery now falls back to a bounded scan
when FTS structurally cannot serve the title; the existing two-tier normalized
confirmation still rejects loose hits like "Upgrade".

Each fix carries a regression test; all three were mutation-checked by removing
the guard and confirming exactly those tests fail. The previous test asserting
that short titles return nothing encoded the bug and has been replaced.

The host spec passed 400 lines, so its fixtures moved to a shared module and
the race suite into its own file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): make the pin decide playback and keep failover going

Second round of Greptile review findings.

**A pin had no behavioural effect.** Loading a stored pin only decorated the
row: Play still started the route's playlist and failover ranking ignored
`isPinned`, so "make this the main source" survived a restart as an icon and
nothing else. The primary action now starts from the pinned source when one is
set, and the pin outranks everything else in failover ranking.

**Failover stopped at the first unresolvable candidate.** An expired account or
a failing `get_vod_info` on the top-ranked source ended the attempt, and since
production calls `failover()` only once — on the original playback failure — a
healthy lower-ranked source was never reached. It now continues through untried
candidates. `switchTo` reports why it stopped so the loop can tell "could not
resolve, try the next one" from "something newer owns the screen"; without that
distinction a superseded switch would have spun forever, because only the
former marks the candidate tried.

**Identity ignored enrichment.** The key was `playlistId:contentId:title`, so
when `get_vod_info` added a TMDB id and release year to an unchanged title the
host saw no change, never reloaded, and kept yearless discovery and title-only
pin keys — a `tmdb:`-keyed pin could never be found. The key now covers every
field that affects matching.

**A server refusing HEAD read as unavailable.** Some stream hosts answer 405 or
501 to HEAD yet serve the media over GET. The probe now retries once with the
ranged GET the main process already supported, instead of caching a working
source as failed and penalising it during failover.

Greptile also flagged a missing token check after the resolve await in
`switchTo`; that guard landed in 4db3a2fd and sits on the line directly below.
Answered on the thread rather than changed.

The host service passed 400 lines again, so the pin, probe, switch-notice and
current-row concerns moved into focused modules beside it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(portals): record the behaviour the review rounds changed

The architecture doc and CLAUDE.md described the feature as first written, not
as it now behaves: pins were documented as a stored preference without saying
they decide playback, failover was described as stopping at the first
unresolvable candidate, the probe as HEAD-only, and discovery as pure FTS with
no mention that short titles cannot be tokenized at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): invalidate the session while the movie identity is empty

The staleness guard added in 4db3a2fd bumped the session only inside `load()`,
which leaves a window the guard does not cover: route navigation empties the
movie identity first, and `load()` for the replacement runs only once a title
is knowable again. A resolution completing in that interval still carried a
session number that matched, so it passed the check and started the previous
movie's source over the page the user was navigating to.

The binding effect now bumps the session as soon as the identity goes null, so
anything already in flight is invalidated at the moment the old movie stops
being the one on screen rather than when the next one finishes loading.

`lastMovieKey` is deliberately left alone: returning to the same movie should
not re-run discovery, and the controller's state is still correct — only the
in-flight operations needed invalidating.

Regression test added and mutation-checked: removing the bump fails exactly
that test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): stop the source list from losing the real alternatives

Six review findings, all in how multi-source decides what to show and what
it is playing.

Discovery: the current playlist is now excluded in SQL rather than after the
fact, so its own duplicate rows can no longer spend the whole row budget
before a single alternative is read. The short-title scan matches the token
as a word instead of a substring and orders by title length in a wider
window, so "Titanic" and "The Italian Job" cannot push the real "It" out of
it.

Session: metadata enrichment re-runs discovery for the film already on
screen. That is a refresh, not a new session — a second identity key
(playlistId:contentId) now separates the two, so the source the user
switched to keeps playing and stays named, the tried set stays burned, the
position survives and a switch in flight still commits.

Resume: the multi-source controller no longer records the engine's pre-seek
timeupdate at ~0. The playback service's one-shot latch now reports whether
the position can be believed, and until it can, the requested start time
stands in — so a switch during the initial seek does not restart the film.

UI: the in-player sources picker gets the same auto-failover setting and
match kind as the detail page's, instead of always rendering the default and
dropping the toggle. The caption counts distinct playlists, not stream
variants, since the popover groups a portal's copies under that portal.

Session mechanics and the pin toggle move into their own modules to keep the
host service inside the line budget.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): keep the playing row when the refined year rejects it

Follow-on from keeping the session across a rediscovery. The rerun can
legitimately drop the row that is playing: enrichment supplies the release
year, and the year gate then rejects a copy the yearless search had admitted
— "Dune" 1984 while the user is watching the 2021 film.

Off the list is right; it is not the same film. Off the screen is not. It is
what is streaming, so it stays as a row and keeps the playing badge, rather
than letting the caption name a playlist that is not sending any bytes.

Also covers the new session key directly in the identity spec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): stop a pin write from landing on the next movie

Two findings from the review of the previous round.

A pin write is an IPC round-trip, and the user can navigate during it. The
continuation then applied one film's answer to another film's controller —
and because unpinning returns "nothing pinned", it would clear the pin the
new movie had just loaded and its Play action would quietly stop starting
from the preferred source. It now commits only while the same film is still
on screen, like every other async path here.

The short-title scan drops its row limit. FTS keeps its window because it
ranks by relevance, so what it keeps is what matters; a scan cannot rank, so
a window there silently decides which valid sources the user is allowed to
see. It also bought nothing: the GLOB cannot use an index, so SQLite reads
every row either way and the limit only truncated the answer. What bounds
the scan is its predicate — reaching it means the whole title is one or two
characters.

The switch-notice type moves to the module that builds it, which also
removes a circular type import between the two.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): keep external playback, the pin and the resume point honest

Five findings from the round-5 review.

An external player launched for an alternative carries that playlist's ids,
so the page disowned its own session: the primary button never became Stop,
stopping found nothing to stop, and another click opened a second player.
Multi-source now tells playback which source is actually active, and the
matcher accepts either that or the route's own stream.

Stop also has to beat the pin. The primary action consults the pin first —
that is what makes a pin decide where playback starts — but while a session
is running the same button reads Stop, and consulting the pin there made the
control do the opposite of its label.

A pinned source started from the Resume button resolved at zero, because
nothing reports a live position until the first timeupdate. The controller is
now seeded from the persisted position, one-way: a live value always wins,
since the stored one lags it and applying it would rewind.

A pin whose write failed was still shown as pinned, promising a preference
that reopening the movie would not have.

Portal failures in this path logged raw errors. An Xtream error message
carries the stream URL, and that URL is built out of the username and
password, so they now go through the redacting logger.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): make every alias of a pin agree, and stop losing rows

Four findings from the latest review pass.

A pin lookup accepts several aliases of the same movie, but a write only
touched the most-trusted one — so after enrichment the title alias still
pointed at whatever was pinned before, and a reopen that read it (because
TMDB had not landed yet, or its request failed) started the source the user
had just replaced. Writes now go to every alias.

That alias set was also missing one. Enrichment supplies the year as well as
the id, so a pin set before either existed is stored yearless; the candidate
list skipped that form entirely and orphaned the row.

Discovery could lose whole playlists: one playlist listing a film in dozens
of categories produces identically ranked rows that fill the window before
another playlist is read. The collapse now happens in SQL, before the limit,
rather than in TypeScript afterwards where the missing rows are already gone.

And an abandoned source pick finishing late cleared the spinner from the row
the user was actually waiting on.

Removes `isExhausted()` from the host service — no caller outside its own
tests, where the assertion above it already proved the same thing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): probe like playback, and stop the pin answering for a remake

Five findings from the latest review pass.

Writing a pin to every alias — last round's fix for stale aliases — was
wrong in the other direction: `title:{base}:` is shared by every remake, so
a known-year decision stored there answers for a different film. Pin Dune
(2021), open Dune (1984) before its year arrives, and it would start the
2021 source. A write now clears every alias and stores only the canonical
key, which retires the stale ones without making any of them ambiguous.

The probe checked a bare URL while playback sends the playlist's User-Agent,
Referer and Origin. A panel that requires them answers 401/403, so a stream
that plays perfectly was reported dead and penalised in failover ranking.

The switch toast interpolated the raw playlist name. Users routinely name a
playlist after the URL they pasted, so that line could put credentials over
the video; the notice now carries the same safe label the rows use.

External players have no timeupdate, so their polled position IS the live
one. Feeding it through the seed — which stops at the first value — froze
the resume point where playback started, and a switch an hour in rewound to
the beginning.

And auto-failover concluded "nowhere to go" when a stream failed before
discovery answered, stranding the user on the error screen.

Moves `switchTo` into the session module, which is where the rest of the
switch mechanics already live, and splits the route spec along the same
rendering/behaviour seam the other suites use.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): re-check the movie after waiting, and follow the alternative

Three findings, two of them regressions from the previous round.

Awaiting a pending discovery before failover let the user navigate during
that wait: the continuation then ran against whatever controller was current
and could answer one film's playback failure by starting another film's
alternative. Both waits — failover and pinned Play — now re-check that the
same movie still owns the screen.

Pinned Play also needed the wait it did not have. Pressing Play while the
pin lookup was still out concluded "nothing is pinned" and started the
route's own source, making a persisted preference depend on worker latency.

And the position bridge still accepted only the route's ids, so an external
player running an alternative had every progress update discarded: the
resume point stayed where playback began and a switch an hour in rewound the
lot. The session matcher and the bridge now share one ownership predicate,
since a page that shows Stop for a session whose progress it throws away is
the bug in two halves.

The test for the external case previously set the position signal directly,
which bypassed the very filter that was broken; it now drives the bridge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): stop a remake matching, and let a pin survive its own playlist

Three findings from the latest review pass.

`normalizeTitleKeys` strips bracketed segments as tag noise, so "Dune (1984)"
normalizes to exactly "dune" — an EXACT match for the 2021 film, ranked above
every fuzzy one, with the year never consulted because that tier skipped the
gate. Auto-failover could switch the user to the other film entirely. The
year is now read out of brackets too, and a stated disagreement rejects the
row on either tier.

Playback positions are keyed by (playlist, stream), so watching through a
pinned alternative stores progress under ITS ids while the page loads the
route copy's row. Starting the pin therefore resumed from a position
belonging to a different copy — usually zero. It now loads its own.

And a pin can point at another copy of the film inside the playlist being
viewed, which discovery excludes wholesale: the pinned row was absent from
the list, so nothing showed as pinned and Play ignored the preference. The
pin is now read before discovery, which keeps that one row.

Moves the pin-shaped decisions into the pin module, where the persistence
helpers already live.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): keep a pinned play, a same-playlist copy and Check honest

Five findings from the latest review pass.

Reading a pinned source's own position is a database round-trip, and the user
can navigate across it — the continuation then handed one film's source id to
whichever movie now owned the screen. Guarded, like every other await here.

Allowing a pinned copy to live in the current playlist made "is this the
route's own source?" a two-part question, and the ownership check still asked
only about the playlist: an external session for that copy was disowned, so
Stop vanished and its progress was dropped.

The yearless title alias is shared by every remake, so clearing every alias
before a write could delete a different film's pin. Writes and unpins now
touch only keys that name one film — plus the ambiguous row this session
actually read, which is the one the user is looking at and the one whose
absence would make an unpin come back.

Restart left the seeded position in the controller, so a failure before the
first timeupdate resolved the next source back at it.

And the alternative rows on the playback-error screen had a Check button
wired to nothing at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): stop a rediscovery restoring the pin it started with

A same-movie rediscovery read the pin, then held that snapshot across its
source lookup and applied it afterwards. A pin made while the lookup was out
was therefore overwritten by the older value: the row and the primary Play
action named a source the database no longer held.

The snapshot is now applied as soon as it is read, so a later write simply
wins on ordering rather than needing to be detected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): write the pin before retiring it, and keep the badge honest

Three findings from the latest review pass.

Repinning cleared the old rows and then wrote the new one, so a write that
failed after the clear left nothing persisted while the row still showed the
old pin. The order is reversed: the new key is stored first and the stale
ones retired only once it landed. Lookups are most-trusted-first, so a
leftover alias never outranks what was just written.

Starting a source from the picker, or letting a pin decide the primary Play,
never recorded the movie as recently viewed — unlike every other way of
playing it.

And closing an alternative's player and pressing Play started the route
stream while the controller still marked the alternative active, so the
picker and caption named a source that was not running.

Moves the discovery pass into the session module beside the switch and
failover mechanics, and splits the pin spec along the persistence/playback
seam, both to stay inside the file-size rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): stop claiming playback, a cached answer and a resolution

Three findings, all of them the same rule: never state as fact something the
app has not established.

The "Playing from …" caption appeared as soon as discovery marked a source
active — before Play was pressed, and again after the player was closed. It
now requires a player that is actually running.

Probe answers were cached by URL alone, but the request now carries the
playlist's headers. Two playlists sharing a stream URL could therefore be
told the other's answer, marking a source dead without ever asking it.

And any width below 900 was labelled 480p, published with `api` provenance:
a 640x360 stream stated 480p as a fact, and a 720x576 PAL source likewise.
Widths below HD only resolve with the height — 720 is NTSC 480p or PAL 576p
— so an unrecognised shape now carries no quality tag at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): match the sub-HD formats, and drop the caption on failure

Two follow-ups to the previous round, both the same rule again.

The 800-wide band still answered from the width alone, so 800x600 and
800x450 were labelled 480p — published with `api` provenance, so read as a
measurement. Sub-HD formats are now matched against known shapes with the
same 5% tolerance the height path uses, and anything unrecognised carries no
tag at all.

And "Playing from ..." survived a playback failure: the inline host stays
mounted while the diagnostic is on screen, so the page named a source for a
stream it had just reported it could not play. The caption now clears on
failure and returns when the engine produces time again.

Splits the route playback spec along the "what it does" / "what it claims"
seam and lifts the repeated active-source stub into one helper.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): let the height veto a width match, and hold the failure state

Two follow-ups to the previous round, both in code it introduced.

A width that matched exactly one sub-HD format ignored the height entirely,
so 640x480 came back as 360p — a measurement the numbers contradict. The
height now vetoes, but only in the direction that can be wrong: cropping
removes lines, so a SHORTER frame is a letterboxed master of that format and
the width still names it, while a taller one is a different shape and gets
no tag. That keeps the reason width is preferred in the first place.

And picking a source off the error screen cleared the failure state before
the switch resolved, so an alternative that could not be resolved left the
diagnostic on screen while the caption went back to claiming playback. The
flag now clears only once a switch actually starts something.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): release the resume latch when the target cannot be reached

Carrying a position into a shorter cut of the same film — two hours into a
90-minute source — leaves the engine unable to ever report that time, so the
one-shot latch never released: every position save was suppressed for the
rest of the session, and the impossible start time kept being reported to
multi-source for the next switch.

The latch now also opens when a known duration puts the requested point out
of reach, while a reachable one still waits as before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): say "Playing" only while something is playing

`isActive` means "the source a switch or Play would use". Discovery sets it
the moment the page opens and it survives closing the player, so it could not
back the two claims the UI made in the present tense: the "Playing from"
caption and the source row's Playing badge. Both appeared on a page where
nothing had started, and came back after the player was closed.

`playbackLive` is now that statement, and both read it. Inline it needs a
timeupdate — `inlinePlayback()` is only the REQUEST to play, non-null while
the engine is still opening the stream and still non-null after it fails —
and external it needs the session past `launching`. A row that is merely
selected reads "Current" (new key, filled for all 19 locales).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): start a never-watched pinned source from the beginning

Positions are keyed by (playlist, stream). When the pin points at a copy the
user has never opened, the lookup returns nothing and the controller was left
holding the ROUTE copy's position — so Play dropped them 42 minutes into an
unstarted film, and the first save wrote that timecode back under the pinned
source's key, making it permanent.

The spec asserted the old behaviour, so it is flipped rather than extended; a
second case covers the host that supplies no lookup at all, where "never
watched" was never established and the position must be left alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): describe the copy the primary button will actually play

Two gaps found by review.

A pin makes the primary button play a copy the page never loaded a position
for — positions are keyed by (playlist, stream). The label, timecode and
Restart affordance still came from the route copy's row, so the button could
read "Resume 42:18" and start an unwatched copy at zero, or read "Play" and
jump into the middle of one already watched. `createPrimaryActionPosition`
lets the pinned copy's row govern, including when that row is absent: never
watched is an answer, not a fallback to someone else's progress.

A manual source switch also mounts a DIFFERENT stream in the same host while
marking the new source active at once, so the previous stream's timeupdate was
still vouching for it — the caption and the badge claimed the new source while
it was still opening. That path now clears the latch like Play and Restart do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): keep the route's own resume point, and honour a closed pin

Two more from review, both variations on "selected is not playing".

`vodPlaybackPosition` followed whichever copy last reported — so after an
alternative played, Resume and its label described that copy's row while
starting the route's stream, jumping it to a timecode nobody reached in it.
It now splits: `vodPlaybackPosition` stays the last position seen (the
progress bar and the switch handoff want the stream on screen), and
`routePlaybackPosition` holds the route copy's own row for everything that
acts on the route's stream.

`pinnedSourceAwaitingPlay` skipped the pin whenever its row was active, but
`isActive` means selected — the pinned row stays selected after its player is
closed, so the next Play went to the route copy and ignored the stored
preference until the page was reopened. It now takes `playbackLive` too.

The host service crossed the 400-line cap on the way, so the four derived
alternative counts moved into `vod-multi-source-counts.ts`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): keep the primary button honest across navigation and pins

Four follow-ups from review, all consequences of splitting the position
signals.

- Route reuse (the Similar rail) cleared only `vodPlaybackPosition`, so the
  button kept the previous movie's Resume label — and start point — until the
  new lookup landed. Both signals and the playback latch now reset together.
- The primary button's fall-through past an unresolvable pin reached the
  service directly, skipping the bookkeeping a route start needs: the
  controller kept the alternative's timecode and the old stream's timeupdate
  still vouched for the new one. It now goes through the route's own wrappers,
  and Resume seeds the controller with the ROUTE copy's position.
- `alternativePlaylistCount` counted the playlist being watched whenever it
  held a second copy, so "also found in 2 other playlists" could mean one.
- The pinned copy's stored row went stale the moment the user watched it; its
  live position now wins while it is the one playing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): do not spend a source's failover turn on mere selection

`setActiveSource` marked the source tried, but discovery calls it the moment
the page opens and a pin or the picker can call it before anything plays. So
opening a movie burned the route copy's turn: if a pinned alternative then
failed, failover skipped a healthy untouched source — and with only one
alternative, reported the options exhausted.

Selection and attempt are now separate. `setActiveSource` selects;
`markPlaying` also spends the turn, and only the three places that really
start playback call it. `runFailover` additionally retires whatever is on
screen before picking, so the failing source is spent however it got there —
relying on the start paths alone would leave one hole per path, and the cost
of missing it is a ping-pong between two sources.

One existing spec asserted the old behaviour (a route copy burned by a switch
it never played); it now plays first, so it still covers what it meant to —
that the tried set survives a rediscovery.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(portals): carry VOD source pins through playlist backup

The new pins table was invisible to backup: exporting a playlist and
re-importing it on a new machine silently dropped every "main source" choice,
with nothing in the archive to say the choice had ever been made.

Pins now ride along under the playlist they point AT — carrying them anywhere
else would restore a preference for a portal the archive never contained.
`matchKey` names the film rather than the portal, so it survives untouched and
only the playlist id is remapped to the imported copy.

`sourcePins` is the one optional collection in the Xtream user state: archives
written before multi-source existed simply do not have it, so its absence is
age rather than damage. Only a wrong type is rejected, and pins without a
usable match key or content id are dropped, since writing one would occupy the
unique key of a film it does not describe.

Adds `DB_LIST_VOD_SOURCE_PINS` through the usual six seams (operation, worker
case, event, preload, bridge contract, service).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(portals): follow the normalized restore state's new collection

`normalizeXtreamPendingRestoreState` now always emits `sourcePins`, like every
other collection it canonicalizes, so three specs that assert the exact
normalized shape had to follow. Adds coverage for the sanitizing itself: a pin
without a usable match key or content id is dropped, and a non-string
`updatedAt` is discarded rather than carried.

Caught by CI, not locally — the earlier full run served `playlist-shared-ui`
from the Nx cache, so it reported green on a stale result.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(portals): lift the VOD route's orchestration out of the component

The details route had grown to 864 lines — the repository's hard maximum is
400, and while the file predates the rule, a baselined exemption is not a
budget to spend.

Three component-provided services now hold what the component was
accumulating: `VodDetailsMultiSourceUiService` (the playback-evidence latch,
the caption, the primary button's position, source actions and the failover
toast), `VodDetailsSimilarService` (the rail and its cross-portal lookup), and
`VodDetailsDownloadsService`. The component keeps its public API, so the
template and the existing specs are untouched. 864 -> 566 lines.

The downloads move also fixes a latent bug: `downloadVod` and `playFromLocal`
read `route.snapshot.params`, which is stale once the router reuses this
component for detail-to-detail navigation (the Similar rail) — so a download
started from a film reached that way fetched the previous one. They now read
the same route-params signal everything else uses, with a regression test.

Also from review: pins are applied on the FRESH-import path too. A new
playlist has no content when the archive is read, so its user state is parked
and replayed after the import — the merge path I wired first never ran there,
and every pin was dropped. A failed pin write now propagates instead of being
ignored: the backup entry is reported failed, and the parked state is kept so
a transient failure can be retried rather than silently losing the preference.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): read array-shaped codecs, bound short-title scans, honour alias clears

Three from review.

`info.video`/`info.audio` are declared — and sent by the mock server and many
panels — as string arrays, but the resolver only read the ffprobe object shape.
Every array response therefore lost the provider's codec, so those source rows
showed no codec fact and the "dub may differ" warning could never fire.
`readStreamInfo` now accepts both, and states nothing when the provider stated
nothing.

The FTS-empty fallback scan matched only the FIRST token, which is fine for a
one-word short title but not for "I Am": every catalog row containing the word
"i" came back for TypeScript to throw away — a full scan of a large catalog on
the single database worker, just to open a detail page. Every token must now
appear.

`writePin` reported success when the canonical write landed but retiring the
old alias failed. Lookups read aliases before the canonical key, so reopening
the movie before enrichment would start the source the user just replaced,
with the icon promising otherwise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): write a pin and retire its aliases in one transaction

Split across two calls, a half-failure had no honest outcome. Reporting
success left a surviving alias to win the next lookup and start the source the
user had just replaced; reporting failure — which the previous round changed
it to — left the canonical row durable while the UI showed a pin that was no
longer the stored one. Review was right both times, which is the tell that the
two-step shape was the problem.

`setVodSourcePin` now takes the keys to retire and does both inside one
`db.transaction()`, with the synchronous `.run()` form the better-sqlite3
driver requires there (issue #1137's lesson). `retireKeys` rides through the
worker op, the IPC contract, the preload bridge and the service, so there is
one call and one outcome.

Also corrects the architecture doc: the scan path is reached whenever no token
clears the trigram minimum, not only when the whole title is one or two
characters — the claim the previous commit's code change had already falsified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): tell a superseded pinned play from an unusable pin

Double-clicking Play while a pinned source resolves put both handlers into
`playPinnedSource()`. The second supersedes the first, so the first returned
`false` — which the route read as "no usable pin" and answered by starting the
route source over the playback the second click had just begun.

`playPinnedSource` now reports `played` / `superseded` / `unavailable`, and
only `unavailable` falls through. This is the same distinction `runFailover`
already draws between "keep going" and "stop, something newer owns the screen";
the pinned path simply never had it.

The host crossed the 400-line cap again on the way, so the pinned-play errand
(wait out an in-flight discovery, re-check the session, start the source) moved
into the pin module beside `playPinned`, and the pin-toggle commit went with
it. 388 lines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): restart honours the pin, and a switch replaces the player

Three from review, all in the pinned-playback seam.

A pinned copy watched through resolved to its stored seconds, so the button
read Play — the label uses the in-progress rule — and then started near the
end. Both now go through one `isResumablePosition`, so the label and the start
point cannot disagree.

Restart sat beside a Resume that honours a foreign pin, but called `playVod`
and started the ROUTE copy — silently switching the user's playlist. It now
restarts whatever the primary button acts on, falling back to the route source
only when there is no usable pin.

Switching sources left a running external player alone. With MPV or VLC and
instance reuse off the backend spawns a second detached process, so both
sources kept playing and Stop owned only the newer one.

Also merges master, and puts the five host specs on a shared harness — they
each carried the same 31-line TestBed, which is what pushed two of them over
the file-size cap as cases were added.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): find short Unicode titles, and stop two pickers racing

Five from review.

Greptile's P1: a short non-ASCII title was undiscoverable. SQLite's `LOWER()`
and GLOB classes are ASCII-only, so "он" never matched a stored "Он" and the
source simply never appeared. ASCII tokens keep the word-boundary GLOB; a
non-ASCII token falls back to a substring test against both the folded and the
as-typed form, which the normalized confirmation afterwards makes safe.

A probe now retries the ranged GET for 400 and 403, not just 405/501 — those
are what a WAF returns for an unexpected HEAD on a URL it serves happily over
GET, and calling that source dead also ranked it below worse ones.

Three races, all the same shape as ones fixed earlier in this branch:
- a pinned play awaiting its resume lookup did not notice a source picked
  across it, and finished last, replacing the user's choice;
- two overlapping switches both saw the same external session, both awaited
  its close, and both launched — two detached players again;
- the primary button showed the ROUTE copy's Resume while the pinned copy's
  row was still loading, so a click started somewhere else entirely.

Also puts the races spec on the shared host harness, which is what keeps it
inside the file-size rule now that it carries two more cases.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): close the player we launched, not the one we now own

Three follow-ups, two of them to last round's own fixes.

The external-session close was defeated in exactly the case it was written
for: `switchToSource` marks the DESTINATION active before handing playback
over, so by the time the service ran, the process still playing no longer
looked like ours and was left running beside its replacement. The service now
remembers the ids it launched with, independently of what is active.

The ASCII/Unicode branch was decided from the NORMALIZED token, which folds
diacritics — "Ça" arrived as "ca", looked like plain ASCII, and took the GLOB
path while the stored title still read "Ça". Decided from the raw token now.

Backup restore upserted archived pins but never removed the playlist's
existing ones, so a present-but-empty collection left stale preferences alive
— unlike the playback positions cleared beside it. An absent collection (an
older archive) still means "no opinion" and is left alone.

Four files crossed the size cap on the way; the split ones now share
`title-sources.spec-data.ts` and `playlist-backup.xtream-fixtures.ts`, and the
external-session ownership moved to its own module.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): absent is not empty, and every start claims the generation

Four more from review, three of them defects in last round's fixes.

The restore normalizer materialized `sourcePins: []` for archives that never
had the field, so "absent means no opinion" became "this archive says there
are no pins" and a merge cleared the user's. Absent now stays absent. My test
for that behaviour had passed for the wrong reason — it stubbed an empty pin
list, so the clear was skipped whether or not the guard worked.

`startGeneration` was claimed only by the switch path, so a plain Play, Resume
or Restart could be overtaken by a switch still awaiting its close. Every
start claims it now.

Raw and normalized tokens were paired by position, which breaks when
normalization drops a whole word: "FR: Ça" normalizes to "ca" and got handed
the raw token "FR:", sending it down the ASCII branch it cannot match from.
They are paired by normalized form instead.

And the ambiguous yearless alias (`title:dune:`) is no longer written or
retired beside a precise key — it may hold another remake's pre-enrichment
pin. It stays available when it is the only key there is, since refusing to
pin at all would be worse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): a failed close must not leave the page claiming a dead source

When `closeSession()` rejected, `startResolvedPlayback` rejected with it and
never launched — while `switchToSource` had already marked the destination
active and reported the switch as successful. The page then named a source
that nothing was playing.

The close failure is logged and the replacement starts anyway. A close that
rejects usually means the session was already gone, and a possibly-lingering
process is the lesser of the two evils: the alternative is a UI that lies
about what is on screen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(portals): split the external-playback handoff out of the service

Both the service and its spec crossed the 400-line cap with the close-failure
handling, so the handoff — deciding which process is ours, closing it, and
surviving a close that rejects — now lives in
`vod-details-external-session.ts` with its own spec file.

Two tests had to start awaiting: replacing a running external player is a
round-trip, and the handoff now yields once even when there is nothing to
close, so the new playback is mounted a microtask later than before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* style(portals): format the extracted external-session module

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): fold diacritics in the title index

Cross-playlist matching compares normalized titles ("Amélie" -> "amelie")
against an index built from the raw title, and the trigram tokenizer does not
fold diacritics by default. Every accented title was therefore invisible to
the FTS path: two identical `Amélie` entries produced no candidates at all.
That is the broadest of the Unicode gaps review found, and it predates the
short-title work.

The tokenizer is fixed at CREATE time, so existing databases recreate and
rebuild the index once behind a migration marker. `remove_diacritics` needs
SQLite 3.45+, so support is probed on a temp table first: an older runtime
keeps its working index untouched and the migration is not recorded as done,
leaving a later version free to upgrade it.

Case folding for non-ASCII remains impossible in stock SQLite — "ОН" cannot
find "Он" by any available predicate — and is documented as the known limit
rather than patched around again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): clear a playlist's pins by playlist, not by key list

Restoring over a playlist reused the keyed clear, which caps its input at
MAX_KEYS_PER_LOOKUP to bound an IN clause. A playlist with more than eight
pinned movies therefore kept the surplus while the call still reported
success, and the restore then wrote the archive's pins on top — leaving the
union of two states, which is neither the one the user asked for.

Clearing is now a dedicated delete-by-playlist operation with no key list to
truncate, and it refuses a blank playlist id rather than deleting everything.
A failure fails the entry instead of being swallowed: `listForPlaylist`
returns `[]` on error and `clear` returns `false`, so ignoring the result made
a failed read indistinguishable from "there was nothing to clear".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): keep a pin readable under every identity of its film

Two defects in the pin/position subsystem, both reported in review.

A pin was stored under the movie's most-trusted key alone and its other
keys retired. But a movie's identity GROWS: the film keyed `tmdb:438631`
today was `title:dune:2021` before enrichment, and reopening it cold asks
for the poorer key first. The preference was therefore ignored until
enrichment landed — and permanently when enrichment is off or never
answers. The decision is now written under every key in `write` (never
the yearless form, which every remake shares), one upsert per key plus
the leftover retirement in the same transaction. `setVodSourcePin` also
reports failure for a pin with no usable key instead of claiming a write
it never made.

The primary button asked whether the pinned copy's position had loaded
by testing presence rather than identity, so re-pinning left it wearing
the previous copy's timecode until the new lookup returned.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(portals): record the key-addressing limit a pin write cannot close

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): fold non-ASCII case in the scan, and read years as tags

I was wrong about SQLite twice over, and both errors cost matches.

GLOB character classes are NOT ASCII-only. `patternCompare` reads them as
UTF-8 code points, so `'Он' GLOB '*[Оо][Нн]*'` is true — only `LOWER()` is
ASCII-only. The scan tier now folds the case in JavaScript, where Unicode
case mapping is real, and hands SQLite one class per character. A short
Cyrillic or Greek title stored in a different case is found instead of
being silently absent from the Sources chip. The builder returns `null`,
leaving the substring tests as the whole answer, for a token holding a
GLOB metacharacter (GLOB has no escape character) or a case mapping that
changes length. The FTS tier is untouched and still cannot fold — that
needs a stored normalized-title column.

The movie's own year came from `extractYear`, which reads a year from
anywhere in the title. That is right where a year is a search hint, wrong
where it is an identity: `2001: A Space Odyssey` was treated as a 2001
film, so every genuine 1968 copy failed the year gate and the movie had
no alternatives at all — and its pin key moved the moment enrichment
supplied the real year. `releaseTagYear` accepts only bracketed and
trailing forms; the repo's own TRAILING_YEAR_PATTERN already documented
this exact hazard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): cover a letter spelled two ways in lower case

Greek Σ lowercases to σ, but a word-final sigma is written ς and is
equally a lowercase of it, so a class built only from the character in
hand knew one spelling of two. Each class now also carries the uppercase
form's own lowercase, which reaches the other one.

One-way on purpose: σ → Σ → σ never arrives at ς. Left so because ς is
only correct at the end of a word, which is exactly where the request's
last character sits — the pair that occurs in real titles is covered, and
closing the other direction needs a fold table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): let an exact title keep a number that is part of its name

Both match tiers weighed the same year, taken from a trailing four-digit
tail or a bracketed tag. On the exact tier that rejects the very copy it
was meant to confirm: reaching it means both titles are the SAME string,
so the trailing digits belong to both, and comparing them against a
release year out of metadata makes "Blade Runner 2049" disagree with its
own stated 2017 — the genuine alternative disappears at the moment
enrichment lands, which is when the user has most reason to expect it.

The exact tier now reads the bracketed form only. Brackets are never part
of a name, so "Dune (1984)" is still rejected against 2021. The base tier
is unchanged: it has just stripped a trailing year, and that year is the
only thing separating "Dune 1984" from "Dune 2021".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(database): verify the title index folds, rather than trust the marker

`createTables` declares content_title_fts with the plain trigram
tokenizer, and the diacritics migration declares it again with folding.
Two sources of truth for one tokenizer: if the table ever went missing
after the marker was written, `CREATE TABLE IF NOT EXISTS` would restore
the unfolded form and the migration would skip it on the marker alone.

The upgrade now reads the live table's own DDL from sqlite_master and
rebuilds unless it really folds. A degraded index is invisible from the
outside — discovery just stops finding "Pokémon" for "pokemon" — so the
record has to be checked against the thing it describes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): give the route's own row the facts the page already has

Two provenance defects found in review.

The current-source row is never resolved — nothing needs to fetch a URL
for the stream already playing — so it carried no provider metadata at
all, while every alternative got its facts from the resolve preceding
playback. `audioDiffersFactually` requires a fact on BOTH sides, so the
"dub may differ" warning was structurally unreachable on the commonest
switch there is: route to alternative. It could only ever fire between
two alternatives that had both been resolved. The row now carries what
`get_vod_info` already told the page, via a `providerVodMetadataOf`
mapper shared with the resolver so the two cannot describe one movie
differently.

Quality bucketed every width from 900 to 1199 as 576p, so a 960x540
stream — an ordinary 540p encode — was published as "576p" with `api`
provenance: a measurement its own pixels contradict, from the one field
that is supposed to mean the provider said so. That range holds two
standard formats, so it is matched now rather than bucketed, exactly as
the sub-HD sizes already were. A width matching no known format yields
no tag and a check chip, which is the honest answer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): let the height veto a width-derived quality, and refresh route facts

Both of these are gaps I saw and chose not to close last round; a
reviewer was right that neither survives its own reasoning.

The shape check only ran below 1200, so the HD ranges kept publishing
wrong-but-confident labels: 1440x1080 is anamorphic 1080 and 1600x900 is
900p, and both were "720p" with `api` provenance — the provenance that
means the provider said so. Ranges are fine up there, the standard widths
really are far apart, but only once a known height can veto the answer.
Same rule the matched formats already used: a shorter frame is a
letterboxed master, a taller one is a different shape and gets no tag.

And the route row picked up provider facts only when discovery reran. On
a sparse panel `get_vod_info` can answer with no year and no TMDB id, so
the movie key is unchanged, nothing reruns, and the row keeps stating
nothing — leaving `audioDiffersFactually` one-sided and the dub warning
unreachable on exactly the switch it exists for. It now takes those facts
on without rediscovering, merged onto the existing row so a probe result
already sitting there survives.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): a codec is not a dub, and two waits needed a switch guard

Three findings from review.

The "dub may differ" warning compared audio CODECS. AAC and AC3 routinely
carry the same dub, and two AC3 tracks can carry different ones, so it
fired on every identical-language re-encode and stayed silent on the dub
changes it exists for — wrong in both directions, which is worse than
absent, because a warning people learn to ignore is not a warning. Worse,
the previous commit made it reach the common route-to-alternative switch
for the first time, so the false claim was about to get louder.

It now reads a new `audioLanguage`, taken from the track's language tag
and never from the codec. `audio` stays as a display fact. Few panels tag
a language, so the warning is usually silent — the same answer the rest
of this feature gives when it does not know.

`failover()` validated only the session across its wait for a discovery
in flight. The session moves when the FILM does, so a source the user
picked — or the route stream they restarted — during that wait was then
treated as the thing that failed and switched away from. It claims and
rechecks a switch generation, as the pinned path already did.

And the scan's ASCII branch could not find "Ça" from a folded "ca", while
the non-ASCII branch found "Ca" from "Ça" — so whether two playlists
could see each other depended on which one was open. Each ASCII letter
now carries its accented forms, derived by decomposition rather than
tabulated, so it cannot drift from the normalizer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(portals): pin what the declared audio shape can and cannot say

The array shape the mock server and many panels send carries a codec and
no language, so the dub warning is silent for every source arriving that
way. Asserted rather than assumed, alongside the ffprobe shapes that do
carry one — otherwise a later reader sees an unused field and wires the
codec back into the warning.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): a failed pin read must not export as "no pins"

Three findings, all in code from this session.

Backup called the lenient `listForPlaylist`, which turns a failed read
into `[]`. Since `e0ebbeaf` made restore treat `sourcePins` as
authoritative — clearing the playlist's pins before applying it — an
export whose read failed produced a file that looks complete and wipes
every pin on restore. Losing them is bad; losing them through the one
feature meant to protect them is worse. Backup now uses a strict listing
that throws, so the export fails instead.

The diacritic map stopped at U+024F, which is tidy and leaves Vietnamese
out: `ố` is U+1ED1, the normalizer folds it to `o`, and the scan filtered
those rows out before confirmation. Latin Extended Additional is included
now; the filter decides what belongs, so the range only has to be wide.

And two panels spelling one language differently (`eng` vs `en`, or
`en-US`) raised a dub warning between identical tracks. Tags are
canonicalized before comparison — 639-2 collapses to 639-1, both German
forms meet at `de`, regions drop, and `und` becomes nothing. Anything
that survives longer than three characters is not a language code, so the
comparison is declined rather than guessed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(portals): stop two backup paths from deleting pins they never read

Two data-loss paths, both P1, both mine.

A web export wrote `sourcePins: []`. Pins are Electron-only, so out
there we cannot read them — which is not the same as knowing there are
none, and restore treats the collection as authoritative. A backup made
in the browser was therefore an instruction to delete every pin the
moment it was imported on the desktop. There are three answers here, not
two: pins exist, there are none, and "could not look". The last omits
the field, exactly as an archive written before pins existed does. The
same rule now covers Electron with the bridge method missing.

I had written a test asserting the unreachable-store case resolves to an
empty list "because a backup made there is complete". That reasoning was
wrong: empty was true of what the runtime could see, never of the
playlist.

Restore also cleared the playlist's pins and then wrote the archive's one
by one. A write failing partway left the previous pins already gone and
only a prefix applied — a state belonging to neither, reported as a
failure the user could not undo. `DB_REPLACE_VOD_SOURCE_PINS` does the
clear and every write in one transaction, so the playlist ends up as the
archive describes it or exactly as it was.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 21:53:42 +02:00

1388 lines
81 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"CANCEL": "İptal",
"CLEAR": "Temizle",
"CLOSE": "Kapat",
"BACK": "Geri",
"SHOW_MORE": "Daha fazla",
"SHOW_LESS": "Daha az",
"PLAYLISTS": "Oynatma Listeleri",
"HOME": {
"TITLE": "IPTVnator",
"SUBTITLE": "Lütfen listeden bir oynatma listesi seçin veya yeni bir liste ekleyin",
"TABS": {
"RECENTLY_ADDED": "Son Eklenen Listeler",
"FILE_UPLOAD": "Dosya Yükle",
"URL_UPLOAD": "URL ile Ekle",
"TEXT_IMPORT": "Metinden İçe Aktar",
"XTREME_IMPORT": "Xtreme Kodu Ekle",
"STALKER_PORTAL_IMPORT": "Stalker Portalı Ekle"
},
"PLAYLISTS": {
"NO_PLAYLISTS": "Oynatma listesi eklenmedi",
"ADD_FIRST": "Lütfen ilk oynatma listenizi ekleyin",
"WELCOME_TITLE": "IPTVnator'a Hoş Geldiniz",
"WELCOME_DESCRIPTION": "İlk oynatma listenizi ekleyerek yayın yolculuğunuza başlayın. Favori IPTV hizmetinize bağlanın ve canlı TV, film ve dizilerin keyfini çıkarın.",
"ADD_YOUR_FIRST": "İlk Oynatma Listenizi Ekleyin",
"FEATURE_M3U": "M3U/M3U8 Dosyaları",
"FEATURE_XTREAM": "Xtream Kodları",
"FEATURE_STALKER": "Stalker Portalı",
"FEATURE_M3U_LABEL": "Add an M3U file or URL",
"FEATURE_XTREAM_LABEL": "Connect via Xtream Codes",
"FEATURE_STALKER_LABEL": "Connect to a Stalker portal",
"VALUE_PROPS_LABEL": "What you can do",
"VALUE_PROP_LIVE": "Live TV with EPG",
"VALUE_PROP_VOD": "Movies & Series",
"VALUE_PROP_MULTISOURCE": "Multi-source library",
"VALUE_PROP_OFFLINE": "Offline downloads",
"DASHBOARD_WELCOME_TAGLINE": "Your IPTV library, in one place.",
"DASHBOARD_WELCOME_DESCRIPTION": "Connect M3U files, Xtream Codes, or Stalker portals — then enjoy live TV, movies and series on every screen.",
"FEATURE_LIVE_TITLE": "Live TV",
"FEATURE_LIVE_DESC": "Stream live channels with full schedule.",
"FEATURE_VOD_TITLE": "Movies & Series",
"FEATURE_VOD_DESC": "On-demand library with covers and details.",
"FEATURE_EPG_TITLE": "EPG & Catch-up",
"FEATURE_EPG_DESC": "Program guide and time-shifted playback.",
"FEATURE_OFFLINE_TITLE": "Offline downloads",
"FEATURE_OFFLINE_DESC": "Save movies and episodes for later.",
"SOURCES_WELCOME_TITLE": "Add your first playlist",
"SOURCES_WELCOME_BODY": "Pick a source type below to get started.",
"SOURCE_M3U_NEEDS": "Local file or URL pointing to an M3U/M3U8 playlist.",
"SOURCE_M3U_ADD": "Add M3U",
"SOURCE_XTREAM_NEEDS": "Username, password and server URL from your provider.",
"SOURCE_XTREAM_ADD": "Add Xtream",
"SOURCE_STALKER_NEEDS": "Portal URL and MAC address (Ministra-compatible).",
"SOURCE_STALKER_ADD": "Add Stalker",
"CONTENT_LIVE": "Live",
"CONTENT_VOD": "Movies",
"CONTENT_SERIES": "Series",
"CONTENT_EPG": "EPG",
"NO_RESULTS_TITLE": "Oynatma listesi bulunamadı",
"NO_RESULTS_DESCRIPTION": "Aramanızla eşleşen oynatma listesi yok. Arama terimlerinizi değiştirmeyi deneyin veya tüm oynatma listelerini görmek için filtreyi temizleyin.",
"ADDED_VIA_URL": "URL yüklemesi ile eklendi. URL:",
"ADDED_VIA_FILE": "Dosya yüklemesi ile eklendi",
"XTREAM_PLAYLIST": "Xtream oynatma listesi",
"STALKER_PORTAL": "Stalker portalı",
"CHANNELS": "Kanallar",
"ADDED": "Eklendi",
"SHOW_DETAILS": "Oynatma listesi ayrıntılarını göster",
"REMOVE": "Oynatma listesini kaldır",
"SEARCH_PLAYLISTS": "Oynatma listelerini başlığa göre ara",
"INFO_DIALOG": {
"PLAYLIST_DETAILS": "Oynatma Listesi Ayrıntıları",
"TITLE": "Başlık",
"FROM_URL": "URL'den Eklendi",
"IMPORT_DATE": "İçe Aktarma Tarihi",
"CHANNELS": "Kanal Sayısı",
"CLOSE": "Kapat",
"SAVE": "Kaydet",
"USER_AGENT": "Kullanıcı Aracısı",
"ORIGINAL_FILENAME": "Orijinal Dosya Adı",
"FILE_PATH": "Dosya Yolu",
"UPDATE_FAILED": "Son oynatma listesi güncellemesi başarısız oldu",
"AUTO_UPDATE": "Otomatik Güncelleme",
"AUTO_UPDATE_DESCRIPTION": "Otomatik yenileme işlevi etkinleştirilirse, uygulama her başlatıldığında oynatma listesi otomatik olarak güncellenecektir.",
"CUSTOM_USER_AGENT": "Bazı IPTV sağlayıcıları, oynatma listelerini oynatmak için belirli bir kullanıcı aracısına ihtiyaç duyar.",
"EXPORT_PLAYLIST": "Oynatma listesini m3u olarak dışa aktar",
"SERVER_URL": "Sunucu URL'si",
"USERNAME": "Kullanıcı Adı",
"PASSWORD": "Parola",
"MAC_ADDRESS": "MAC Adresi",
"PLAYLIST_EXPORT_SUCCESS": "Oynatma listesi başarıyla dışa aktarıldı.",
"PLAYLIST_EXPORT_ERROR": "Hata: Oynatma listesi dışa aktarılamadı.",
"COPY_URL": "URL'yi panoya kopyala",
"URL_COPIED": "URL panoya kopyalandı"
},
"REMOVE_DIALOG": {
"TITLE": "Oynatma Listesini Kaldır",
"MESSAGE": "Bu oynatma listesini tamamen silmek istediğinizden emin misiniz?",
"SUCCESS": "Oynatma listesi başarıyla kaldırıldı",
"IN_PROGRESS": "Oynatma listesi kaldırılıyor...",
"DELETING_FAVORITES": "Favoriler kaldırılıyor...",
"DELETING_RECENT": "Son izlenenler kaldırılıyor...",
"DELETING_PROGRESS": "Oynatma ilerlemesi kaldırılıyor...",
"DELETING_DOWNLOADS": "İndirilenler kaldırılıyor...",
"DELETING_CONTENT": "Önbelleğe alınmış içerik kaldırılıyor...",
"DELETING_CATEGORIES": "Kategoriler kaldırılıyor...",
"DELETING_PLAYLIST": "Oynatma listesi kaydı kaldırılıyor..."
},
"UPDATED": "Güncellendi",
"REFRESH": "Oynatma listesini yenile",
"REFRESH_XTREAM": "Xtream oynatma listesini uzaktan yenile",
"REFRESH_XTREAM_DIALOG": {
"TITLE": "Xtream Oynatma Listesini Yenile",
"MESSAGE": "Bu işlem yerel Xtream önbelleğini siler ve kategoriler ile yayınları uzak sunucudan yeniden içe aktarır. Favoriler, son izlenen öğeler, gizli kategori görünürlüğü ve oynatma ilerlemesi, eşleşen içerik hâlâ varsa başarılı bir yeniden içe aktarmadan sonra geri yüklenir. Sunucuya ulaşılamazsa geri yükleme, daha sonraki başarılı bir yenilemeye kadar beklemede kalır. Devam edilsin mi?",
"STARTED": "Yenileme başladı. Oynatma listesine gidiliyor...",
"ERROR": "Oynatma listesi yenilenemedi. Lütfen tekrar deneyin.",
"IN_PROGRESS": "Oynatma listesi yenileniyor...",
"COLLECTING_DATA": "Favoriler ve son izlenenler toplanıyor...",
"DELETING_CONTENT": "Önbelleğe alınmış içerik kaldırılıyor...",
"DELETING_CATEGORIES": "Önbelleğe alınmış kategoriler kaldırılıyor..."
},
"GLOBAL_FAVORITES": "Tüm Favoriler",
"GLOBAL_FAVORITES_DESCRIPTION": "Tüm oynatma listelerinden oluşan otomatik favori liste",
"MY_PLAYLISTS": "Oynatma Listelerim",
"MY_PLAYLISTS_SUBTITLE": "tüm oynatma listeleri",
"PLAYLIST_UPDATE_SUCCESS": "Başarılı! Oynatma listesi başarıyla güncellendi.",
"PLAYLIST_UPDATE_ERROR": "Oynatma listesi ayrıntıları güncellenirken hata oluştu.",
"PLAYLIST_UPDATE_FILE_NOT_FOUND": "Oynatma listesi yenileme başarısız oldu. Yerel dosya artık kullanılabilir değil. Dosya yolunu kontrol edin veya oynatma listesini yeniden içe aktarın.",
"PLAYLIST_UPDATE_FILE_ACCESS_ERROR": "Oynatma listesi yenileme başarısız oldu. Uygulama artık yerel dosyaya erişemiyor.",
"AUTO_REFRESH_UPDATE_SUCCESS": "Başarılı! Oynatma listeleri başarıyla güncellendi",
"AUTO_REFRESH_UPDATE_PARTIAL": "Otomatik yenileme tamamlanmadı: {{total}} oynatma listesinden {{updated}} tanesi güncellendi, {{failed}} tanesi başarısız oldu.",
"AUTO_REFRESH_UPDATE_PARTIAL_WITH_SKIPPED": "Otomatik yenileme tamamlanmadı: {{total}} oynatma listesinden {{updated}} tanesi güncellendi, {{failed}} tanesi başarısız oldu, {{skipped}} tanesi atlandı (URL veya dosya yolu yok).",
"AUTO_REFRESH_UPDATE_FAILED": "Oynatma listelerinin otomatik yenilenmesi başarısız oldu. {{total}} oynatma listesinin hiçbiri güncellenemedi.",
"AUTO_REFRESH_UPDATE_SKIPPED": "Oynatma listesi otomatik yenileme: {{total}} listeden {{updated}} tanesi güncellendi, {{skipped}} tanesi atlandı (URL veya dosya yolu yok).",
"AUTO_REFRESH_ENABLED": "Otomatik yenileme etkin"
},
"ADD_PLAYLIST": {
"PICK_METHOD": "Nasıl bağlanmak istediğinizi seçin. Daha sonra yeniden adlandırabilirsiniz.",
"METHOD_URL_LABEL": "M3U URL",
"METHOD_URL_SUB": "Uzak bir .m3u veya .m3u8 bağlantısını yapıştırın",
"METHOD_FILE_LABEL": "M3U dosyası",
"METHOD_FILE_SUB": "Bilgisayarınızdan bir oynatma listesi yükleyin",
"METHOD_XTREAM_LABEL": "Xtream kimlik bilgileri",
"METHOD_XTREAM_SUB": "Sunucu, kullanıcı adı ve parola ile bağlanın",
"METHOD_STALKER_LABEL": "Stalker portalı",
"METHOD_STALKER_SUB": "MAC adresi ve portal URL'si",
"METHOD_TEXT_LABEL": "Ham m3u metni",
"METHOD_TEXT_SUB": "Tek seferlik listeler veya yalnızca metniniz olduğunda"
},
"FILE_UPLOAD": {
"DRAG_DROP": "Dosyaları buraya sürükleyip bırakın",
"OR": "veya",
"CHOOSE_PLAYLIST": "oynatma listesi seçin",
"BROWSE": "Dosyalara göz at",
"REJECTED": "Dosya reddedildi, desteklenmeyen dosya formatı {{filename}}.",
"OPEN_FAILED": "\"{{filename}}\" açılamadı."
},
"URL_UPLOAD": {
"PLAYLIST_URL": "Oynatma listesi URL'si (m3u, m3u8)",
"ADD_PLAYLIST": "Oynatma listesi ekle",
"CORS_NOTE": "Not: CORS sorunlarını önlemek için bu aşamada uygulama, halka açık bir CORS Proxy hizmeti kullanır. Güvenlik nedeniyle hassas verileri içeren bir oynatma listesini yüklemek istiyorsanız, daha güvenli bir seçenek olarak dosyadan içe aktarmayı deneyin.",
"ERROR_FETCH_FAILED": "Oynatma listesi alınamadı. Lütfen URL'yi kontrol edip tekrar deneyin.",
"ERROR_403": "Erişim reddedildi (403): Sunucu isteği reddetti. URL kimlik doğrulama gerektiriyor olabilir veya oynatma listesi kısıtlanmış olabilir.",
"ERROR_404": "Oynatma listesi bulunamadı (404): URL geçerli bir oynatma listesine işaret etmiyor. Lütfen URL'yi kontrol edin.",
"ERROR_401": "Yetkisiz (401): Bu oynatma listesine erişmek için kimlik doğrulama gerekiyor.",
"ERROR_INVALID_TLS": "Certificate for this playlist host is invalid.",
"ERROR_TLS_HOST_UNKNOWN": "Could not determine the playlist host. Please retry manually.",
"TRUST_TLS_HOST": "Trust host",
"TRUST_TLS_HOST_TITLE": "Trust invalid certificate?",
"TRUST_TLS_HOST_WARNING": "Only continue if you trust this playlist host. IPTVnator will allow invalid TLS certificates for this host, but other hosts still require valid certificates."
},
"TEXT_IMPORT": {
"LABEL": "m3u(8) oynatma listesini metin olarak girin",
"BUTTON_LABEL": "İçe Aktar"
},
"XTREAM_PLAYLIST": {
"TITLE": "Oynatma listesi başlığı",
"SERVER_URL": "Sunucu URL'si",
"USERNAME": "Kullanıcı Adı",
"PASSWORD": "Parola",
"ADD": "Ekle",
"TEST_CREDENTIALS": "Kimlik bilgilerini test et",
"URL_VALIDATION_ERROR": "Geçerli bir URL olmalıdır (örn. http://example.com veya https://example.com:4324)"
},
"STALKER_PORTAL": {
"TITLE": "Başlık",
"MAC_ADDRESS": "MAC Adresi",
"SERIAL_NUMBER": "Seri Numarası (İsteğe Bağlı)",
"SERIAL_NUMBER_HINT": "Sağlayıcıya zaten kayıtlıysa kullanın",
"DEVICE_ID_1": "Cihaz Kimliği 1 (İsteğe Bağlı)",
"DEVICE_ID_2": "Cihaz Kimliği 2 (İsteğe Bağlı)",
"DEVICE_ID_HINT": "64 onaltılık karakter - sağlayıcı belirli bir cihaz kimliği gerektiriyorsa kullanın",
"SIGNATURE_1": "İmza 1 (İsteğe Bağlı)",
"SIGNATURE_2": "İmza 2 (İsteğe Bağlı)",
"SIGNATURE_HINT": "64 onaltılık karakter - sağlayıcı cihaz doğrulama imzaları gerektiriyorsa kullanın",
"SERVER_URL": "Sunucu URL'si",
"URL_VALIDATION_ERROR": "Geçerli bir URL olmalıdır (örn. http://example.com/c veya https://example.com/stalker_portal/c)",
"ADD": "Ekle",
"VALIDATING": "Portal doğrulanıyor..."
},
"FILTER_BY_NAME": "İsime göre filtrele",
"FILTER_AND_SORT": "Filtrele ve Sırala",
"SORT_PLAYLISTS": "Oynatma listelerini sırala",
"SORT_OPTIONS": {
"NAME_ASC": "İsim (A-Z)",
"NAME_DESC": "İsim (Z-A)",
"NEWEST": "Eklenme tarihi (En yeni önce)",
"OLDEST": "Eklenme tarihi (En eski önce)",
"CUSTOM_ORDER": "Özel sıralama (Sürükle ve Bırak)"
},
"PLAYLIST_TYPES": {
"M3U": "M3U (yerel, url, metin)",
"XTREAM": "Xtream",
"STALKER": "Stalker"
},
"IMPORTED_AS_TEXT": "Metin olarak içe aktarıldı",
"PARSING_ERROR": "Hata: Ayrıştırma başarısız, geçerli bir oynatma listesi değil:"
},
"SETTINGS": {
"TITLE": "Ayarlar",
"GENERAL": "Genel",
"GENERAL_SUBTITLE": "Uygulamanın yapılandırmasını değiştirin",
"PLAYBACK": "Oynatma",
"EPG_SOURCES": "EPG kaynakları",
"EPG_SOURCES_DESCRIPTION": "EPG kaynağı olarak bir veya daha fazla URL ekleyin",
"PLAYLIST_EPG_SOURCES_AUTO_SELECTED": "{{enabled}} of {{detected}} detected playlist EPG sources were auto-selected for import.",
"EPG_EMPTY_TITLE": "No EPG sources yet",
"EPG_EMPTY_HINT": "Add a URL to fetch program data for your channels.",
"PREFER_UPLOADED_EPG_TITLE": "Xtream oynatma listeleri için yüklediğin EPG'yi tercih et",
"PREFER_UPLOADED_EPG_HINT": "Varsayılan olarak Xtream oynatma listeleri sağlayıcının yayın akışını kullanır ve yüklediğin EPG yalnızca eksik kanalları doldurur. Bunu açarak yüklediğin EPG'yi önce kullanabilirsin.",
"DATA": "Veri",
"BACKUP": "Backup",
"BACKUP_DESCRIPTION": "Export your library as a snapshot, or restore from a previous backup.",
"RESET": "Reset",
"RESET_DESCRIPTION": "Permanently delete content stored in this app.",
"ABOUT": "Hakkında",
"NAV_GENERAL": "Genel",
"NAV_PLAYBACK": "Oynatma",
"NAV_EPG": "EPG",
"NAV_REMOTE": "Uzaktan",
"NAV_DATA": "Veri",
"NAV_BACKUP": "Backup",
"NAV_RESET": "Reset",
"NAV_ABOUT": "Hakkında",
"NAV_TMDB": "Meta veri",
"TMDB": "Meta veri (TMDB)",
"TMDB_DESCRIPTION": "Film ve dizi ayrıntılarını The Movie Database verileriyle zenginleştirin",
"TMDB_ENABLE": "TMDB meta verisini etkinleştir",
"TMDB_ENABLE_DESCRIPTION": "Ayrıntı görünümlerinde konu özetlerini, oyuncu kadrosunu, puanları ve görselleri iyileştirir. Etkinleştirildiğinde film ve dizi adları TMDB'ye gönderilir.",
"TMDB_API_KEY": "TMDB API anahtarı (isteğe bağlı)",
"TMDB_API_KEY_DESCRIPTION": "Yerleşik anahtar yerine kendi TMDB API anahtarınızı kullanın",
"TMDB_GET_KEY_HINT": "themoviedb.org üzerinden ücretsiz API anahtarı alın",
"TMDB_TEST_BUTTON": "Anahtarı kontrol et",
"TMDB_TEST_SUCCESS": "Anahtar çalışıyor — TMDB başarıyla yanıt verdi",
"TMDB_TEST_FAILED": "Kontrol başarısız — TMDB anahtarı reddetti veya erişilemiyor",
"TMDB_CACHE": "Meta veri önbelleği",
"TMDB_CACHE_DESCRIPTION": "İndirilen meta veriler yerel olarak saklanır, böylece detay sayfaları anında açılır. Temizlediğinizde uygulama bir sonraki ziyarette yeni veri indirir.",
"TMDB_CACHE_SIZE": "{{entries}} kayıt · {{size}}",
"TMDB_CACHE_CLEAR": "Önbelleği temizle",
"TMDB_CACHE_ERROR": "Önbellek okunamadı — tekrar deneyin",
"TMDB_CREDITS": "Meta veri",
"TMDB_ATTRIBUTION": "Bu ürün TMDB API'sini kullanır ancak TMDB tarafından onaylanmamış veya sertifikalandırılmamıştır.",
"APP_PREFERENCES": "Uygulama tercihleri",
"HERO_EYEBROW": "Çalışma alanı ayarları",
"HERO_TITLE": "IPTVnator'un nasıl hissettirdiğini şekillendirin",
"HERO_DESCRIPTION": "Oynatma, rehberler, uzaktan erişim ve veri yönetimini geçerli çalışma alanı stilini kaybetmeden tek bir yerden ayarlayın.",
"SUBTITLE": "Uygulamanın yapılandırmasını değiştir",
"DESCRIPTION": "Lütfen aşağıdaki seçenekleri düzenleyin ve değişiklikleri kaydet düğmesiyle onaylayın",
"EPG_URL_LABEL": "EPG Url (*.xml veya *.xml.gz)",
"EPG_URL_PLACEHOLDER": "URL girin",
"REFRESH_EPG": "EPG'yi yenile",
"REFRESH_ALL_EPG": "Tümünü yenile",
"REMOVE_EPG_SOURCE": "EPG kaynağını kaldır",
"EPG_STATUS_FRESH": "Güncel",
"EPG_STATUS_STALE": "Yenilenmeli",
"EPG_STATUS_ERROR": "Başarısız",
"VIDEO_PLAYER_LABEL": "Video oynatıcı",
"PLAYER_HTML5": "HTML5 video oynatıcı",
"PLAYER_VIDEOJS": "Video.js oynatıcı",
"PLAYER_ARTPLAYER": "ArtPlayer",
"PLAYER_EMBEDDED_MPV": "Gömülü MPV (Deneysel)",
"PLAYER_MPV": "MPV oynatıcı",
"PLAYER_VLC": "VLC",
"VIDEO_PLAYER_PLACEHOLDER": "Bir seçenek belirleyin",
"VERSION": "Sürüm",
"SAVE_CHANGES": "Değişiklikleri kaydet",
"BACK_TO_HOME": "Ana sayfaya dön",
"NEW_VERSION_AVAILABLE": "Yeni bir sürüm mevcut",
"LATEST_VERSION": "En son sürümü kullanıyorsunuz",
"STREAM_FORMAT": "Yayın Formatı",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK": "MPV/VLC'yi çift tıklamayla başlat",
"OPEN_EXTERNAL_PLAYER_ON_DOUBLE_CLICK_DESCRIPTION": "MPV veya VLC oynatımı başlamadan önce kanal satırlarına çift tıklanmasını gerektirir.",
"LANGUAGE": "Dil",
"SETTINGS_SAVED": "Başarılı! Yapılandırma kaydedildi.",
"SETTINGS_SAVE_FAILED": "Ayarlar kaydedilemedi. Değişiklikleriniz uygulama yeniden başlatıldığında kaybolacak.",
"SETTINGS_LOAD_FAILED": "Kaydedilmiş ayarlar yüklenemedi — varsayılanlar gösteriliyor. Uygulamanın başka bir kopyasının çalışmadığından emin olun.",
"THEME": "Görünüm teması",
"SHOW_CAPTIONS": "Altyazıları göster",
"SHOW_DASHBOARD": "Panoyu göster",
"SHOW_DASHBOARD_DESCRIPTION": "Panoyu çalışma alanı kenar çubuğunda erişilebilir tutun. Devre dışı bırakıldığında, ilk çalışma alanı sayfası Kaynaklar olur.",
"ADD_EPG_SOURCE": "EPG kaynağı ekle",
"CLEAR_EPG_DATA": "EPG verilerini temizle",
"CLEAR_EPG_DIALOG": {
"TITLE": "EPG verilerini temizle",
"MESSAGE": "Tüm EPG (Elektronik Program Rehberi) verilerini veritabanından silmek istediğinizden emin misiniz? Bu işlem, önbelleğe alınmış tüm program çizelgelerini silecektir."
},
"EPG_DATA_CLEARED": "EPG verileri temizlendi",
"EPG_DATA_CLEAR_FAILED": "EPG verileri temizlenemedi. Lütfen tekrar deneyin.",
"CLEARING_EPG_DATA": "Temizleniyor…",
"EPG_URL_ERROR": "Girilen değer geçerli bir URL değildir",
"VIDEO_PLAYER_DESCRIPTION": "Varsayılan video oynatıcıyı seçin",
"WEB_PLAYER_SHARED_CONTROLS": "Web oynatıcıları için birleşik kontroller (deneysel)",
"WEB_PLAYER_SHARED_CONTROLS_DESCRIPTION": "HTML5, Video.js ve ArtPlayer'da IPTVnator'ın ortak kontrollerini kullan.",
"PLAYER_AMBIENT_MODE": "Ortam ışığı dolgusu",
"PLAYER_AMBIENT_MODE_DESCRIPTION": "Oynatıcının çevresindeki boşluğu siyah çubuklar yerine bulanık ve karartılmış bir poster ile doldurur.",
"PLAYER_UP_NEXT_RAIL": "Sıradaki bölümler paneli",
"PLAYER_UP_NEXT_RAIL_DESCRIPTION": "Geniş pencerelerde dizi oynatıcısını sola yaslar ve sıradaki bölümleri yanında gösterir.",
"STREAM_FORMAT_DESCRIPTION": "Varsayılan yayın formatını seçin (xtream)",
"LANGUAGE_DESCRIPTION": "Uygulamanın dilini seçin",
"THEME_DESCRIPTION": "Uygulamanın görünüm temasını seçin",
"COVER_SIZE": "Kapak boyutu",
"COVER_SIZE_DESCRIPTION": "Izgaralar ve raylar genelinde poster kartlarının boyutunu ayarlayın. Daha büyük boyutların TV'lerde görülmesi daha kolaydır; daha küçük boyutlar büyük monitörlerde satır başına daha fazla içerik gösterir.",
"COVER_SIZE_SMALL": "Küçük",
"COVER_SIZE_MEDIUM": "Orta",
"COVER_SIZE_LARGE": "Büyük",
"EPG_VIEW_MODE": "Rehber görünümü",
"EPG_VIEW_MODE_DESCRIPTION": "Oynatıcının altındaki program rehberinin nasıl gösterileceğini seçin: yatay bir zaman çizelgesi şeridi veya dikey, tek günlük bir liste.",
"EPG_VIEW_MODE_TIMELINE": "Zaman çizelgesi",
"EPG_VIEW_MODE_LIST": "Liste",
"STARTUP_BEHAVIOR": "Başlangıç görünümü",
"STARTUP_BEHAVIOR_DESCRIPTION": "IPTVnator'un ilk kullanılabilir çalışma alanı görünümünü mü açacağını yoksa bölüm düzeyindeki son görünümünüzü mü geri yükleyeceğini seçin. İlk görünüm, Pano etkinleştirildiğinde Pano, aksi takdirde Kaynaklar anlamına gelir.",
"STARTUP_BEHAVIOR_FIRST_VIEW": "İlk kullanılabilir görünüm",
"STARTUP_BEHAVIOR_RESTORE_LAST_VIEW": "Son görünümü geri yükle",
"SHOW_CAPTIONS_DESCRIPTION": "Varsayılan olarak altyazıları göster veya gizle",
"SHOW_EXTERNAL_PLAYBACK_BAR": "Şu an oynatılıyor altbilgisini göster",
"SHOW_EXTERNAL_PLAYBACK_BAR_DESCRIPTION": "Masaüstünde harici oynatıcı durum çubuğunu çalışma alanının altında görünür tutun.",
"STRIP_COUNTRY_PREFIX": "Kanal adlarından ülke ön eklerini kaldır",
"STRIP_COUNTRY_PREFIX_DESCRIPTION": "Uygulama genelinde canlı kanal adlarından \"US | \" veya \"UK - \" gibi ülke/grup ön eklerini kaldırır.",
"VERSION_DESCRIPTION": "Uygulamanın geçerli sürümü",
"ABOUT_SUBTITLE": "Sürüm, proje bağlantıları ve IPTVnator'u desteklemenin yolları.",
"EMBEDDED_MPV_NOTICE_TITLE": "Gömülü MPV çalışma zamanı",
"EMBEDDED_MPV_NOTICE_DESCRIPTION": "Masaüstü sürümleri, deneysel Gömülü MPV oynatıcı için paketlenmiş libmpv ve FFmpeg kitaplıklarını içerebilir. Kullanıcıların MPV uygulamasını ayrıca kurmasına gerek yoktur; harici MPV ve VLC, yedek oynatıcı olarak kullanılabilir kalır.",
"COMMUNITY": "Proje ve topluluk",
"COMMUNITY_DESCRIPTION": "Depoyu yıldızlayın, blogu okuyun veya güncellemeler ve yardım için Telegram grubuna katılın.",
"LINK_BLOG": "Blog",
"LINK_GITHUB_DESCRIPTION": "Depoyu yıldızlayın, sorun açın veya katkıda bulunun",
"LINK_BLOG_DESCRIPTION": "Sürüm notları, ipuçları ve proje güncellemeleri",
"LINK_TELEGRAM_DESCRIPTION": "Duyurular, topluluk ve destek",
"SUPPORT_TITLE": "Projeyi destekleyin",
"SUPPORT_DESCRIPTION": "IPTVnator ücretsiz ve açık kaynaklıdır, açıkta geliştirilir. Günlük rutininizin bir parçasıysa, küçük bir katkı projeyi canlı tutmaya ve sırada ne olduğunu şekillendirmeye yardımcı olur — teşekkürler.",
"SUPPORT_SPONSORS_HINT": "Yinelenen veya tek seferlik",
"SUPPORT_KOFI_HINT": "Bana bir kahve ısmarla",
"EPG_NOTE": "Şu anda EPG, yalnızca Electron tabanlı IPTVnator sürümünde kullanılabilir.",
"EPG_NOTE_URL_TEXT": "Buradan kontrol edin",
"IMPORT_EXPORT_DATA": "Oynatma listelerini içe/dışa aktar",
"IMPORT_EXPORT_DATA_DESCRIPTION": "Uygulama içi kullanılan JSON formatında oynatma listelerini dışa aktar veya içe aktar.",
"IMPORT_DATA": "İçe Aktar",
"EXPORT_DATA": "Dışa Aktar",
"EXPORTING_DATA": "Dışa aktarılıyor…",
"REMOVE_ALL": "Delete all playlists and playlist data",
"REMOVE_ALL_BUTTON": "Delete all",
"REMOVE_ALL_DESCRIPTION": "Delete every M3U, Xtream, and Stalker playlist stored in the app.",
"REMOVE_DIALOG": {
"TITLE": "Delete all playlists?",
"MESSAGE": "Delete all {{count}} playlists from the app. This also removes stored playlist data on this device.",
"SUMMARY_TITLE": "Playlist sources",
"CONSEQUENCES_TITLE": "This also removes",
"CONSEQUENCE_FAVORITES": "Favorites saved for these playlists",
"CONSEQUENCE_RECENTLY_VIEWED": "Recently viewed history",
"CONSEQUENCE_PLAYBACK": "Saved playback positions",
"CONSEQUENCE_DOWNLOADS": "Download history entries linked to those playlists",
"CONSEQUENCE_XTREAM_CACHE": "Imported Xtream categories and locally cached content",
"BACKUP_HINT": "Use Export first if you want a restorable backup before deleting.",
"CONFIRM": "Delete everything"
},
"IMPORT_ERROR": "İçe aktarma hatası, lütfen başka bir dosyayla deneyin.",
"PLAYLISTS_REMOVED": "All playlists and playlist data were removed.",
"MPV_PLAYER_PATH_LABEL": "mpv yolu",
"MPV_PLAYER_PATH_DESCRIPTION": "MPV yolunu ayarlayın. macOS’ta /Applications/mpv.app gibi MPV uygulama paketini veya yürütülebilir dosya yolunu kullanabilirsiniz.",
"MPV_COMPATIBLE_PLAYER_TIP": "IINA, macOS’ta MPV uyumlu bir oynatıcı olarak başlatılabilir; ancak /Applications/IINA.app/Contents/MacOS/iina-cli veya /Applications/IINA.app/Contents/MacOS/IINA gibi yürütülebilir dosya yolunu kullanın. IPTVnator kontrolleri, konum yoklama ve örnek yeniden kullanımı yalnızca MPV IPC için garanti edilir.",
"MPV_PLAYER_PATH": "mpv oynatıcı yolu",
"MPV_PLAYER_ARGUMENTS_LABEL": "mpv command-line arguments",
"MPV_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to mpv when IPTVnator starts a new external player process.",
"MPV_PLAYER_ARGUMENTS": "mpv arguments",
"MPV_REUSE_INSTANCE_LABEL": "MPV örneğini yeniden kullan",
"MPV_REUSE_INSTANCE_DESCRIPTION": "Etkinleştirildiğinde, farklı yayınları oynatmak için aynı MPV penceresi yeniden kullanılır. Devre dışı bırakıldığında, her yayın yeni bir MPV penceresinde açılır.",
"RECORDING_FOLDER_LABEL": "Kayıt klasörü",
"RECORDING_FOLDER_DESCRIPTION": "Yerleşik MPV'den canlı yayın kayıtlarının kaydedileceği klasör. Sistem İndirilenler klasörünü kullanmak için boş bırakın.",
"RECORDING_FOLDER": "Kayıt klasörü",
"RECORDING_FOLDER_SELECT": "Kayıt klasörü seç",
"VLC_PLAYER_PATH_LABEL": "VLC yolu",
"VLC_PLAYER_PATH_DESCRIPTION": "VLC yolunu ayarlayın. macOS’ta /Applications/VLC.app gibi VLC uygulama paketini veya yürütülebilir dosya yolunu kullanabilirsiniz.",
"VLC_PLAYER_PATH": "VLC oynatıcı yolu",
"VLC_PLAYER_ARGUMENTS_LABEL": "VLC command-line arguments",
"VLC_PLAYER_ARGUMENTS_DESCRIPTION": "Optional arguments passed to VLC when IPTVnator starts a new external player process.",
"VLC_PLAYER_ARGUMENTS": "VLC arguments",
"EXTERNAL_PLAYER_ARGUMENTS_HINT": "Use one argument per line. These values are applied before stream-specific IPTVnator options.",
"VLC_REUSE_INSTANCE_LABEL": "VLC örneğini yeniden kullan",
"VLC_REUSE_INSTANCE_DESCRIPTION": "Etkinleştirildiğinde, farklı yayınları oynatmak için aynı VLC penceresi yeniden kullanılır. Devre dışı bırakıldığında, her yayın yeni bir VLC penceresinde açılır.",
"REMOTE_CONTROL": "Uzaktan kontrol",
"REMOTE_CONTROL_DESCRIPTION": "Uygulamanın uzaktan kontrolünü etkinleştirin.",
"REMOTE_CONTROL_PORT": "Uzaktan kontrol portu",
"REMOTE_CONTROL_PORT_DESCRIPTION": "Uygulamanın uzaktan kontrolü için kullanılan port. Varsayılan olarak port 8765 kullanılır.",
"REMOTE_CONTROL_URL": "Uzaktan kontrol URL'si",
"REMOTE_CONTROL_URL_DESCRIPTION": "Aynı ağdaki başka bir cihazdan uzaktan kontrole erişin.",
"SHOW_QR_CODE": "QR kodunu göster",
"STATUS_ENABLED": "Etkin",
"STATUS_DISABLED": "Devre dışı",
"SOURCE_SINGULAR": "kaynak",
"SOURCE_PLURAL": "kaynak",
"PORT_RANGE_LABEL": "Port (1-65535)",
"PORT_REQUIRED": "Port gereklidir",
"PORT_MIN": "Port en az 1 olmalıdır",
"PORT_MAX": "Port en fazla 65535 olmalıdır",
"PORT_PATTERN": "Sadece rakamlara izin verilir",
"REMOVE_ALL_IN_PROGRESS": "Deleting playlist data…",
"REMOVE_ALL_PROGRESS": "Deleting playlist data… {{current}} / {{total}}",
"PLAYLISTS_REMOVE_FAILED": "Could not delete playlists. Please try again.",
"NAV_DASHBOARD": "Dashboard",
"DASHBOARD": "Dashboard",
"DASHBOARD_SUBTITLE": "Choose which dashboard rails are shown in the workspace.",
"DASHBOARD_HERO": "Hero banner",
"DASHBOARD_HERO_DESCRIPTION": "Show the large top banner for your most recent item.",
"DASHBOARD_RAIL_CONTINUE": "Continue Watching",
"DASHBOARD_RAIL_CONTINUE_DESCRIPTION": "Show recently watched movies and series as cover cards.",
"DASHBOARD_RAIL_LIVE_FAVORITES": "Live now on your favorites",
"DASHBOARD_RAIL_LIVE_FAVORITES_DESCRIPTION": "Show favorite live channels with current EPG progress.",
"DASHBOARD_RAIL_RECENT_LIVE": "Recently watched live TV",
"DASHBOARD_RAIL_RECENT_LIVE_DESCRIPTION": "Show live channels you opened recently with current EPG progress.",
"DASHBOARD_RAIL_FAVORITE_VOD": "Favorite movies & series",
"DASHBOARD_RAIL_FAVORITE_VOD_DESCRIPTION": "Show favorited movies and series as cover cards.",
"DASHBOARD_RAIL_SOURCES": "Recently used sources",
"DASHBOARD_RAIL_SOURCES_DESCRIPTION": "Show shortcuts to your most recently used playlists and portals.",
"DASHBOARD_RAIL_TMDB_TRENDING": "Bu hafta trend olanlar (TMDB)",
"DASHBOARD_RAIL_TMDB_TRENDING_DESCRIPTION": "TMDB'nin haftalık trend film ve dizilerini kitaplıklarınızla eşleştirerek gösterir. TMDB meta verilerinin etkin olması gerekir.",
"DASHBOARD_RAIL_XTREAM_ADDED": "Recently added on Xtream",
"DASHBOARD_RAIL_XTREAM_ADDED_DESCRIPTION": "Show the newest Xtream movies, series, and channels.",
"APP_UPDATE_TITLE": "Application update",
"APP_UPDATE_DESCRIPTION": "Check GitHub Releases for desktop app updates.",
"APP_UPDATE_IDLE": "Ready to check for updates",
"APP_UPDATE_CHECKING": "Checking for updates…",
"APP_UPDATE_AVAILABLE": "Version {{version}} is available",
"APP_UPDATE_NOT_AVAILABLE": "You are using the latest version",
"APP_UPDATE_DOWNLOADING": "Downloading version {{version}}…",
"APP_UPDATE_DOWNLOADED": "Version {{version}} is ready to install",
"APP_UPDATE_MANUAL_AVAILABLE": "Version {{version}} is available for manual installation",
"APP_UPDATE_ERROR": "Update check failed",
"APP_UPDATE_CHECK": "Check again",
"APP_UPDATE_DOWNLOAD": "Update",
"APP_UPDATE_INSTALL": "Restart application",
"APP_UPDATE_OPEN_RELEASE": "Open GitHub release",
"APP_UPDATE_RELEASE_NOTES": "What's new",
"EMBEDDED_MPV_FRAME_COPY": "Embedded MPV: frame-copy motoru (deneysel)",
"EMBEDDED_MPV_FRAME_COPY_DESCRIPTION": "Video uygulama tuvaline çizilir, böylece menüler ve iletişim kutuları üzerinde görüntülenir.",
"EMBEDDED_MPV_FRAME_COPY_RESTART": "Uygulama yeniden başlatıldıktan sonra geçerli olur.",
"VOD_AUTO_FAILOVER": "Oynatma hatasında kaynağı otomatik değiştir",
"VOD_AUTO_FAILOVER_DESCRIPTION": "Bir film oynatılamadığında ve aynı film başka bir oynatma listesinde de varsa, otomatik olarak o listeye geçilir ve oynatma konumu korunur. Varsayılan olarak kapalıdır; başka bir kaynakta dublaj farklı olabileceği için geçiş her zaman bildirilir."
},
"THEMES": {
"DARK_THEME": "Koyu",
"LIGHT_THEME": "Açık",
"SYSTEM_THEME": "Sistem"
},
"AUDIO_PLAYER": {
"PLAY": "Oynat",
"PAUSE": "Duraklat",
"PREVIOUS_STATION": "Önceki istasyon",
"NEXT_STATION": "Sonraki istasyon",
"MUTE": "Sesi kapat",
"UNMUTE": "Sesi aç",
"VOLUME": "Ses seviyesi"
},
"EMBEDDED_MPV": {
"PLAYER": {
"BACK": "Geri",
"RETRY": "Tekrar dene",
"STALLED": "Bu yayının başlaması beklenenden uzun sürüyor.",
"PLAYBACK_FAILED": "Gömülü MPV oynatması başarısız oldu.",
"CHECKING_SUPPORT": "Gömülü MPV desteği denetleniyor...",
"NOT_AVAILABLE": "Gömülü MPV bu ortamda kullanılamıyor.",
"LOADING_STREAM": "Yayın yükleniyor…",
"PLAY": "Oynat",
"PAUSE": "Duraklat",
"PREVIOUS_EPISODE": "Önceki bölüm",
"NEXT_EPISODE": "Sonraki bölüm",
"BACK_10_SECONDS": "10 saniye geri",
"FORWARD_10_SECONDS": "10 saniye ileri",
"LIVE_STREAM": "Canlı yayın",
"PLAYBACK_POSITION": "Oynatma konumu",
"MUTE": "Sesi kapat",
"UNMUTE": "Sesi aç",
"MUTED": "Ses kapalı",
"VOLUME": "Ses seviyesi",
"VOLUME_LABEL": "Ses seviyesi %{{percent}}",
"AUDIO_TRACKS": "Ses parçaları",
"SUBTITLES": "Altyazılar",
"SUBTITLES_OFF": "Kapalı",
"TRACK_DEFAULT": "Varsayılan",
"AUDIO_TRACK_FALLBACK": "Ses {{index}}",
"SUBTITLE_TRACK_FALLBACK": "Altyazı {{index}}",
"PLAYBACK_SPEED": "Oynatma hızı",
"SPEED_TOOLTIP": "Hız: {{speed}}",
"ASPECT_RATIO": "En boy oranı",
"ASPECT_TOOLTIP": "En boy oranı: {{aspect}}",
"ASPECT_DEFAULT": "Varsayılan",
"START_RECORDING": "Kaydı başlat",
"STOP_RECORDING": "Kaydı durdur",
"RECORDING": "Kaydediliyor",
"RECORDING_SAVED": "Kayıt kaydedildi",
"RECORDING_FAILED": "Kayıt başarısız oldu",
"RECORDING_FAILED_TO_START": "Kayıt başlatılamadı.",
"RECORDING_FAILED_TO_STOP": "Kayıt durdurulamadı.",
"SAVED_TO": "{{path}} konumuna kaydedildi",
"ENTER_PICTURE_IN_PICTURE": "Resim içinde resim modunu aç",
"EXIT_PICTURE_IN_PICTURE": "Resim içinde resim modundan çık",
"ENTER_FULLSCREEN": "Tam ekrana geç",
"EXIT_FULLSCREEN": "Tam ekrandan çık"
}
},
"PLAYBACK_DIAGNOSTICS": {
"UNSUPPORTED_CONTAINER": {
"TITLE": "This stream container is likely unsupported by the browser player",
"DESCRIPTION": "The browser player may not decode this container inline. Copy the stream URL and open it in an external player such as MPV, VLC, or IINA for broader codec support."
},
"UNSUPPORTED_CODEC": {
"TITLE": "This stream codec is likely unsupported by the browser player",
"DESCRIPTION": "The stream advertises codecs that the browser player may not decode. Copy the stream URL and open it in an external player such as MPV, VLC, or IINA."
},
"MEDIA_DECODE_ERROR": {
"TITLE": "The browser player could not decode this stream",
"DESCRIPTION": "Playback started but the media pipeline failed while decoding. An external player may handle this stream better."
},
"NETWORK_ERROR": {
"TITLE": "The stream could not be loaded",
"DESCRIPTION": "The player reported a network or provider loading error. Check the stream URL, headers, or provider availability."
},
"BROWSER_ACCESS_ERROR": {
"TITLE": "The browser player could not access this stream",
"DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. MPV or VLC can often bypass browser-player access limits.",
"PWA_DESCRIPTION": "The request may be blocked by CORS, mixed content, or provider header/proxy restrictions. The PWA cannot launch external players directly; use the Copy stream URL action and open it manually in MPV, VLC, IINA, or another player."
},
"DRM_OR_ENCRYPTION": {
"TITLE": "This stream may be encrypted or DRM-protected",
"DESCRIPTION": "The browser player cannot continue with the reported encryption/key error. External playback may still fail if provider DRM is required."
},
"UNKNOWN_PLAYBACK_ERROR": {
"TITLE": "The browser player reported a playback error",
"DESCRIPTION": "The stream failed in the built-in player, but the error did not identify a specific codec, container, or network cause."
},
"NATIVE_FALLBACK_TITLE": "Open it in a native player instead",
"INLINE_FAILURE_TITLE": "Copy the URL or try again",
"ACTION_OPEN_MPV": "Open in MPV",
"ACTION_OPEN_VLC": "Open in VLC",
"ACTION_OPEN_MPV_HINT": "configured player",
"ACTION_OPEN_VLC_HINT": "configured player",
"ACTION_COPY_URL": "Copy URL",
"ACTION_RETRY": "Retry",
"DETAILS_SUMMARY": "Technical details",
"CODEC_HINT": "Detected {{codecs}}. Chromium/Electron browser players often have limited support for these codecs.",
"DETAIL_CODE": "Diagnostic code",
"DETAIL_PLAYER": "Player",
"DETAIL_SOURCE": "Source",
"DETAIL_CONTAINER": "Container",
"DETAIL_MIME_TYPE": "MIME type",
"DETAIL_VIDEO_CODECS": "Video codecs",
"DETAIL_AUDIO_CODECS": "Audio codecs",
"DETAIL_NATIVE_ERROR_CODE": "Native error code",
"DETAIL_NATIVE_ERROR_MESSAGE": "Native error message",
"DETAIL_ERROR_DETAILS": "Error details"
},
"LAYOUT": {
"HIDE_CHANNELS_LIST": "Kanal listesini gizle",
"SHOW_CHANNELS_LIST": "Kanal listesini göster",
"TOGGLE_SIDEBAR_TOOLTIP": "Kanal listesini aç/kapat (⌘/Ctrl+B)"
},
"CHANNELS": {
"UPLOAD_OR_SELECT_OTHER_PLAYLIST": "Dosya yükle veya başka bir oynatma listesi seçin",
"ALL_CHANNELS": "Tüm kanallar",
"GROUPS": "Gruplar",
"MANAGE_GROUPS": "Grupları yönet",
"MANAGE_GROUPS_HINT": "Grupları tekrar göstermek için grupları yönet seçeneğini kullanın.",
"NO_VISIBLE_GROUPS": "Görünür grup yok",
"SEARCH_GROUPS": "Grupları ara",
"UNGROUPED": "Grupsuz",
"UNNAMED_CHANNEL": "İsimsiz kanal",
"FAVORITES": "Favoriler",
"NO_FAVORITES": "Henüz favori yok",
"USE_STAR_TO_FAVORITE": "Lütfen favorilere kanal eklemek için 'yıldız' simgesini kullanın.",
"REMOVE_FAVORITE": "Favorilerden kaldır",
"ADD_FAVORITE": "Favorilere ekle",
"SHOW_DETAILS": "Kanal ayrıntılarını göster",
"MAP_EPG": "EPG kanalı eşle",
"FAVORITES_UPDATED": "Favoriler güncellendi!",
"SELECT_CHANNEL_PLAYBACK": "Lütfen oynatmaya başlamak için bir kanal seçin",
"SORT_BY": "Sırala",
"SORT_BY_DATE": "Eklenme tarihine göre sırala (Önce En Yeni)",
"SORT_BY_RATING": "Puana göre sırala",
"SORT_BY_NAME": "İsme göre sırala (A-Z)",
"LOAD_MORE": "Daha fazla yükle",
"REMAINING": "kalan",
"LOADING": "Kanallar yükleniyor...",
"LOADING_MORE": "Daha fazla yükleniyor...",
"NO_SEARCH_RESULTS": "Kanal bulunamadı",
"NO_CHANNELS_FOUND": "Kanal bulunamadı",
"NO_GROUPS_FOUND": "Grup bulunamadı",
"TRY_DIFFERENT_SEARCH": "Farklı bir arama terimi deneyin",
"DETAILS_DIALOG": {
"TITLE": "Kanal ayrıntıları",
"DESCRIPTION": "Oynatma listesi dosyasından ayrıştırılan salt okunur üst veri.",
"SUMMARY": "Özet",
"TVG": "TVG üst verisi",
"CATCHUP": "Catchup",
"HTTP": "HTTP başlıkları",
"NAME": "Ad",
"CHANNEL_ID": "Kanal ID",
"STREAM_URL": "Yayın URL'si",
"GROUP": "Grup",
"RADIO": "Radyo",
"CATCHUP_AVAILABLE": "Catchup mevcut",
"WINDOW": "Arşiv penceresi",
"PLAYBACK_SUPPORTED": "Oynatma destekleniyor",
"EPG_PARAMS": "EPG parametreleri",
"TIMESHIFT": "Timeshift",
"TVG_ID": "TVG ID",
"TVG_NAME": "TVG adı",
"TVG_URL": "TVG URL",
"TVG_LOGO": "TVG logosu",
"TVG_REC": "TVG rec",
"CATCHUP_TYPE": "Catchup türü",
"CATCHUP_SOURCE": "Catchup kaynağı",
"CATCHUP_DAYS": "Catchup günü",
"HTTP_ORIGIN": "Origin",
"HTTP_REFERRER": "Referrer",
"HTTP_USER_AGENT": "User-Agent",
"EMPTY": "Sağlanmadı",
"NOT_AVAILABLE": "Mevcut değil",
"DAYS_ONE": "1 gün",
"DAYS_OTHER": "{{count}} gün"
},
"GROUP_MANAGEMENT": {
"TITLE": "Grupları yönet",
"SELECTED": "Seçildi",
"SELECT_ALL": "Tümünü seç",
"DESELECT_ALL": "Tümünü kaldır",
"SEARCH_PLACEHOLDER": "Grupları ara...",
"NO_RESULTS": "Eşleşen grup bulunamadı",
"SAVE": "Kaydet"
}
},
"TOP_MENU": {
"OPEN_MULTI_EPG": "Çoklu EPG görünümünü aç"
},
"EPG": {
"NEXT_DAY": "Sonraki gün",
"PREVIOUS_DAY": "Önceki gün",
"ZOOM_IN": "Yakınlaştır",
"ZOOM_OUT": "Uzaklaştır",
"FILTER_CHANNELS": "Kanalları filtrele",
"CLOSE_FILTER": "Filtreyi kapat",
"SEARCH_PROGRAMS": "Programları ara",
"CLOSE_PROGRAM_SEARCH": "Program aramayı kapat",
"LIVE_STREAM": "Canlı yayın",
"ARCHIVE_PLAYBACK_UNAVAILABLE_TITLE": "Arşiv oynatma kullanılamıyor",
"ARCHIVE_PLAYBACK_UNAVAILABLE_DESCRIPTION": "Bu sağlayıcı kanal için yayın akışı geçmişini sunar, ancak oynatılabilir arşiv/Catchup oynatma sunmaz.",
"EPG_NOT_AVAILABLE_CHANNEL_DESCRIPTION": "Lütfen uygulamanın ayarlarında EPG listesi URL'sini ekleyin veya değiştirin.",
"FETCH_EPG": "EPG verileri alınıyor...",
"ERROR": "Üzgünüz, EPG yüklenemedi.",
"DOWNLOAD_SUCCESS": "EPG başarıyla indirildi.",
"NO_PROGRAM_INFO": "Program bilgisi mevcut değil",
"PROGRAM_DIALOG": {
"PROGRAM_DETAILS": "TV programı hakkında",
"TITLE": "Başlık",
"CATEGORY": "Kategori",
"PARENTAL_RATING_SYSTEM": "Televizyon içeriği derecelendirme sistemi",
"DESCRIPTION": "Açıklama",
"LANGUAGE": "Dil",
"SHOW_PROGRAM_DETAILS": "Bu program hakkında detayları göster",
"EPISODE": "Bölüm",
"DURATION": "Süre"
},
"FETCH_SUCCESS": "EPG verileri başarıyla alındı",
"IMPORT_PROGRESS": "EPG İçe Aktarımı",
"CHANNELS": "kanallar",
"PROGRAMS": "programlar",
"LOADING": "Yükleniyor",
"QUEUED": "Sırada",
"TOTAL_CHANNELS": "Toplam kanal",
"TOTAL_PROGRAMS": "Toplam program",
"UP_TO_DATE": "EPG güncel, senkronizasyona gerek yok",
"RETRY": "Tekrar Dene",
"ALLOW_PRIVATE_SOURCE": "Allow source",
"ALLOW_PRIVATE_SOURCE_TITLE": "Allow private-network EPG source?",
"ALLOW_PRIVATE_SOURCE_WARNING": "Only allow this if you trust the EPG source. IPTVnator will let this exact EPG URL connect to private or local network addresses.",
"TRUST_TLS_HOST": "Trust host",
"TRUST_TLS_HOST_TITLE": "Trust invalid certificate?",
"TRUST_TLS_HOST_WARNING": "Only continue if you trust this host. IPTVnator will allow invalid TLS certificates for this host, but other hosts still require valid certificates.",
"CURRENT_PROGRAM": "Current program",
"ARCHIVE_PLAYBACK": "Arşiv oynatma",
"COLLAPSE_PANEL": "Collapse EPG panel",
"EXPAND_PANEL": "Expand EPG panel",
"TIMELINE": {
"TODAY": "Bugün",
"NOW": "Şimdi",
"JUMP_NOW": "Şimdiye atla",
"RETURN_TO_LIVE": "Canlı yayına dön",
"ON_NOW": "Şimdi yayında",
"MINUTES_LEFT": "dk kaldı",
"FROM_ARCHIVE": "Arşivden",
"WATCH": "İzle",
"WATCH_LIVE": "Canlı izle",
"WATCH_FROM_START": "Baştan izle",
"ARCHIVE_UNAVAILABLE_NOTE": "Oynatma kullanılamıyor — bu sağlayıcı bir arşiv sunmuyor.",
"CATCHUP_FAILED": "Bu program için arşiv oynatma başlatılamadı",
"SCHEDULE_ONLY_NOTICE": "Yalnızca program çizelgesi. Bu sağlayıcı program geçmişini sunar ancak catch-up sunmaz — geçmiş programlar oynatılamaz.",
"EMPTY_DAY_TITLE": "Bu gün için program yok",
"EMPTY_DAY_DESCRIPTION": "Seçilen güne ait program çizelgesi henüz gelmedi. Diğer günlerde veri var.",
"JUMP_TODAY": "Bugüne git",
"JUMP_NEAREST": "Programı olan en yakın gün",
"CHANNEL_UNMAPPED_TITLE": "Bu kanal için program yok",
"CHANNEL_UNMAPPED_DESCRIPTION": "Bu kanal EPG kaynağıyla eşleştirilmedi (tvg-id bulunamadı). Diğer kanallar etkilenmez.",
"PROVIDER_NO_EPG_TITLE": "Sağlayıcı bir rehber sunmuyor",
"PROVIDER_NO_EPG_DESCRIPTION": "Bu kaynak hiçbir kanal için TV rehberi sunmuyor. Bir program çizelgesine ihtiyacınız varsa sağlayıcınıza sorun.",
"M3U_SETUP_TITLE": "Bir program rehberi bağlayın",
"M3U_SETUP_DESCRIPTION": "M3U oynatma listeleri harici bir EPG kaynağı (XMLTV) gerektirir. Bir url-tvg bağlantısı ekleyin; program çizelgesi burada görünecektir.",
"OPEN_EPG_SETTINGS": "EPG ayarlarını aç",
"ERROR_TITLE": "Rehber yüklenemedi",
"ERROR_DESCRIPTION": "EPG kaynağı yanıt vermedi. Bağlantınızı kontrol edin veya kaynağı yenileyin.",
"RETRY": "Tekrar dene",
"MINUTES_SHORT": "dk",
"ZOOM": "Yakınlaştırma",
"ZOOM_DAY": "Gün özeti",
"ZOOM_HOURS": "Saatlik",
"ZOOM_DETAIL": "Detaylı",
"GROUP_SHORT": "kısa program",
"GROUP_EXPAND": "göster ›"
}
},
"EPG_MAPPING_DIALOG": {
"TITLE": "EPG Kanalı Eşle",
"SUBTITLE": "\"{{channelName}}\" için EPG kanalını elle seçin",
"SEARCH_PLACEHOLDER": "EPG kanallarında ara...",
"CURRENT_MAPPING": "Geçerli eşleme",
"NO_MAPPING": "EPG eşlemesi ayarlanmadı",
"SAVE": "Eşlemeyi kaydet",
"REMOVE": "Eşlemeyi kaldır",
"CLOSE": "Kapat",
"NO_RESULTS": "EPG kanalı bulunamadı",
"SEARCH_HINT": "Aramak için en az {{min}} karakter yazın",
"SAVED": "EPG eşlemesi kaydedildi",
"REMOVED": "EPG eşlemesi kaldırıldı",
"SAVE_FAILED": "EPG eşlemesi güncellenemedi"
},
"LANGUAGES": {
"ARABIC": "العربية",
"MOROCCAN_ARABIC": "العربية (اللهجة المغربية)",
"ENGLISH": "English",
"KOREAN": "한국어",
"RUSSIAN": "Русский",
"GERMAN": "Deutsch",
"SPANISH": "Español",
"CHINESE": "简体中文",
"TRADITIONAL_CHINESE": "繁體中文",
"FRENCH": "Français",
"ITALIAN": "Italiano",
"TURKISH": "Türkçe",
"JAPANESE": "日本語",
"DUTCH": "Nederlands",
"BELARUSIAN": "Беларуская",
"POLISH": "Polski",
"PORTUGUESE": "Português",
"GREEK": "Ελληνικά",
"HUNGARIAN": "Magyar"
},
"YES": "Evet",
"NO": "Hayır",
"MENU": {
"OPEN": "Menüyü aç",
"SUPPORT_ARIA": "IPTVnator'u GitHub'da destekleyin",
"SETTINGS_ARIA": "Ayarları aç",
"BUG_REPORT": "Hata bildir",
"WHAT_IS_NEW": "Yeni ne var?",
"ABOUT": "Hakkında",
"ABOUT_ARIA": "Uygulama hakkında",
"SUPPORT_PROJECT": "Projeyi destekleyin"
},
"ABOUT": {
"TITLE": "IPTVnator Hakkında",
"DESCRIPTION": "Çoklu özellikler sunan, m3u ve m3u8 oynatma listeleri, favoriler, TV rehberi, TV arşivi/geri dönük izleme ve daha fazlasını destekleyen çok platformlu IPTV oynatıcı uygulaması.",
"VERSION": "Sürüm",
"GITHUB_TOOLTIP": "IPTVnator GitHub deposu",
"BUY_ME_A_COFFEE_TOOLTIP": "Bana bir kahve alın",
"TWITTER_TOOLTIP": "Twitter'da paylaş"
},
"UPDATE_AVAILABLE": "Güncelleme mevcut",
"REFRESH": "Yenile",
"XTREAM": {
"PLAY": "Oynat",
"OPENING_PLAYER": "Oynatıcı açılıyor...",
"NAME": "İsim",
"RELEASE_DATE": "Çıkış tarihi",
"GENRE": "Tür",
"COUNTRY": "Ülke",
"ACTORS": "Oyuncular",
"DIRECTOR": "Yönetmen",
"SIMILAR": "Benzerler",
"CREW_JOB_DIRECTOR": "Yönetmen",
"CREW_JOB_CREATOR": "Yaratıcı",
"ACTOR_FILMOGRAPHY": "Filmografi",
"ACTOR_FILTER_ALL": "Tümü",
"ACTOR_SCOPE_THIS_PORTAL": "Bu portal",
"ACTOR_SCOPE_ALL_PORTALS": "Tüm portallar",
"ACTOR_NO_RESULTS": "Bu filtre için sonuç bulunamadı",
"ACTOR_IN_LIBRARY": "Kitaplığınızda mevcut",
"ACTOR_SEARCH_IN_PORTAL": "Portalda ara",
"ACTOR_BORN": "Doğum:",
"ACTOR_DIED": "Vefat",
"SERIES_STATUS_RETURNING": "Devam ediyor",
"SERIES_STATUS_PLANNED": "Planlandı",
"SERIES_STATUS_IN_PRODUCTION": "Yapım aşamasında",
"SERIES_STATUS_ENDED": "Tamamlandı",
"SERIES_STATUS_CANCELED": "İptal edildi",
"SERIES_STATUS_PILOT": "Pilot",
"DURATION": "Süre",
"CAST": "Oyuncular",
"EPISODE_RUN_TIME": "Bölüm süresi",
"RATING": "Değerlendirme",
"IMDB_RATING": "IMDB derecelendirmesi",
"KINOPOISK_RATING": "Kinopoisk derecelendirmesi",
"DESCRIPTION": "Açıklama",
"YEAR": "Yıl",
"AGE": "Yaş",
"YOUTUBE_TRAILER": "YouTube fragmanı",
"EPISODE": "Bölüm",
"RESUME": "Devam Et",
"RESTART": "Baştan Başla",
"CONTINUE_WATCHING": "İzlemeye Devam Et",
"PLAY_FIRST_EPISODE": "İlk bölümü oynat",
"PLAY_EPISODE": "{{episode}}. bölümü oynat",
"RESUME_EPISODE": "Bölüme devam et",
"PLAY_NEXT_EPISODE": "Sonraki bölümü oynat",
"SERIES_WATCHED": "Dizi izlendi",
"WATCHED": "İzlendi",
"IN_PROGRESS": "Devam Ediyor",
"MARK_WATCHED": "İzlendi Olarak İşaretle",
"MARK_UNWATCHED": "İzlenmedi Olarak İşaretle",
"EPISODES_WATCHED": "{{total}} bölümden {{count}} tanesi izlendi",
"DETAIL_FALLBACK": {
"STATUS": "Portal üst verisi mevcut değil",
"NOTE": "Bu portal tarafından genişletilmiş üst veri sağlanmadı.",
"STREAM_ID": "Yayın ID",
"CATEGORY": "Kategori",
"CONTAINER": "Format",
"SOURCE_TITLE": "Kaynak başlığı"
},
"ACCOUNT_INFO": {
"TITLE": "Hesap Bilgileri",
"USER_INFO": "Kullanıcı Bilgileri",
"STATUS": "Durum",
"USERNAME": "Kullanıcı Adı",
"ACTIVE_CONNECTIONS": "Aktif Bağlantılar",
"CREATED": "Oluşturuldu",
"EXPIRES": "Sona Eriyor",
"TRIAL_ACCOUNT": "Deneme Hesabı",
"YES": "Evet",
"NO": "Hayır",
"ALLOWED_FORMATS": "İzin Verilen Formatlar",
"SERVER_INFO": "Sunucu Bilgileri",
"LIVE_TV": "Canlı TV",
"MOVIES": "Filmler",
"TV_SERIES": "TV Dizileri",
"URL": "URL",
"PROTOCOL": "Protokol",
"TIMEZONE": "Saat Dilimi",
"SERVER_TIME": "Sunucu Saati",
"PORTS": "Portlar",
"HTTP_PORT": "HTTP",
"HTTPS_PORT": "HTTPS",
"RTMP_PORT": "RTMP"
},
"CATEGORY_MANAGEMENT": {
"TITLE": "Kategorileri Yönet",
"LOADING": "Kategoriler yükleniyor...",
"SELECTED": "Seçildi",
"SELECT_ALL": "Tümünü Seç",
"DESELECT_ALL": "Tümünü Kaldır",
"SEARCH_PLACEHOLDER": "Kategorileri ara...",
"NO_RESULTS": "Eşleşen kategori bulunamadı",
"NO_CATEGORIES": "Kategori mevcut değil",
"SAVE": "Kaydet"
}
},
"PORTALS": {
"ALL_CATEGORIES": "Tüm kategoriler",
"MY_FAVORITES": "Favorilerim",
"ADD_TO_FAVORITES": "Favorilere ekle",
"REMOVE_FROM_FAVORITES": "Favorilerden kaldır",
"REMOVED_FROM_FAVORITES": "Favorilerden kaldırıldı",
"ADDED_TO_FAVORITES": "Favorilere eklendi",
"FAVORITES": "Favoriler",
"RECENTLY_VIEWED": "Son izlenenler",
"THIS_PLAYLIST": "Bu oynatma listesi",
"ALL_PLAYLISTS": "Tüm oynatma listeleri",
"LIVE_TV": "Canlı TV",
"MOVIES": "Filmler",
"SERIES": "Diziler",
"SEARCH": "Ara",
"NO_FAVORITES": "Henüz favori yok",
"NO_RECENT_ITEMS": "Son izlenen öğe yok",
"REMOVE_FROM_RECENT": "Son izlenenlerden kaldır",
"BROWSE_ALL_MOVIES": "Tüm filmlere göz at",
"BROWSE_ALL_SERIES": "Tüm dizilere göz at",
"BROWSE_ALL_LIVE": "Tüm canlı TV'ye göz at",
"SIDEBAR": {
"MAIN": "Ana Menü",
"MOVIES": "Filmler",
"LIVE_TV": "Canlı TV",
"SERIES": "Diziler",
"LIBRARY": "Kütüphane",
"SEARCH": "Ara",
"RECENT": "Son İzlenenler",
"RECENTLY_ADDED": "Yeni Eklenenler",
"FAVORITES": "Favoriler"
},
"ERROR_VIEW": {
"IMPORT_CANCELLED": {
"TITLE": "İçe aktarma iptal edildi",
"DESCRIPTION": "Oynatma listesi içe aktarımı iptal edildi. Bu kaynağı yüklemeye devam etmek istediğinizde tekrar deneyin."
},
"ACCOUNT_EXPIRED": {
"TITLE": "Hesap süresi doldu",
"DESCRIPTION": "Bu hesabın süresi doldu. Lütfen oynatma listesi ayarlarındaki kimlik bilgilerini kontrol edin veya kaldırın."
},
"ACCOUNT_INACTIVE": {
"TITLE": "Hesap etkin değil",
"DESCRIPTION": "Bu hesap etkin değil. Lütfen oynatma listesi ayarlarındaki kimlik bilgilerini kontrol edin veya kaldırın."
},
"UNAUTHORIZED": {
"TITLE": "Yetkisiz",
"DESCRIPTION": "Lütfen oynatma listesi ayarlarındaki kimlik bilgilerini kontrol edin veya kaldırın."
},
"NOT_FOUND": {
"TITLE": "Portal bulunamadı",
"DESCRIPTION": "Lütfen oynatma listesi ayarlarındaki kimlik bilgilerini ve portal url'sini kontrol edin veya kaldırın."
},
"PORTAL_UNAVAILABLE": {
"TITLE": "Portal kullanılamıyor",
"DESCRIPTION": "Portala ulaşılamadı. Sunucu URL'sini kontrol edin veya kaynak tekrar kullanılabilir olduğunda yeniden deneyin."
},
"EMPTY_CATEGORY": {
"TITLE": "Boş kategori",
"DESCRIPTION": "Bu kategoride içerik yok, lütfen başka bir kategori seçin veya içerik türünü değiştirin"
},
"UNKNOWN_ERROR": {
"TITLE": "Bir şeyler ters gitti",
"DESCRIPTION": "Bilinmeyen hata, lütfen daha sonra tekrar deneyin"
},
"NO_CATEGORY_SELECTED": {
"TITLE": "Kategori seçilmedi",
"DESCRIPTION": "İçeriği görmek için lütfen bir kategori seçin"
},
"PLAYLIST_SETTINGS": "Oynatma Listesi Ayarları",
"HOME": "Ana Sayfa",
"DELETE": "Sil"
},
"EMPTY_LIST_VIEW": {
"TITLE": "Sonuç bulunamadı",
"NO_SEARCH_RESULTS": "Arama sonuçları yok, lütfen arama talebinizi değiştirin"
},
"SEARCH_VIEW": {
"INITIAL_TITLE": "İçerik ara",
"INITIAL_DESCRIPTION": "Canlı TV, filmler ve diziler arasında arama yapmak için en az 3 karakter girin.",
"RESULTS_FOUND": "{{count}} sonuç bulundu",
"NO_RESULTS_FOR": "\"{{term}}\" için sonuç bulunamadı",
"GROUP_BY_PLAYLIST": "Sonuçları oynatma listesine göre grupla",
"VISIBLE_CATEGORIES_ONLY": "Sadece görünür kategoriler",
"GLOBAL_SEARCH_SOURCE_NOTE": "Global search includes results from Xtream and M3U playlists.",
"GLOBAL_INITIAL_DESCRIPTION": "Enter at least 2 characters to search across Xtream and M3U playlists.",
"LOADING_MORE_RESULTS": "Loading more results..."
},
"STREAM_URL_COPIED": "Yayın URL'si panoya kopyalandı",
"COPY_STREAM_URL": "Yayın URL'sini kopyala",
"NOW_PLAYING": "Şimdi oynatılıyor",
"UP_NEXT": "Sıradaki",
"CLOSE_PLAYER": "Oynatıcıyı kapat",
"ABOUT": "Hakkında",
"SEASONS_AND_EPISODES": "Sezonlar ve Bölümler",
"SEASON_TAB": "{{number}}. Sezon",
"BACK_TO_PLAYING_EPISODE": "Oynatılan bölüme dön",
"GRID_VIEW": "Izgara görünümü",
"LIST_VIEW": "Liste görünümü",
"NO_EPISODES_AVAILABLE": "Bölüm mevcut değil",
"NO_EPISODES_AVAILABLE_MESSAGE": "Bu dizi sağlayıcınız tarafından listeleniyor, ancak oynatılabilecek sezon veya bölüm bulunmuyor.",
"SEASON_EMPTY": "Bu sezon boş",
"SEASON_EMPTY_MESSAGE": "Bu sezon sağlayıcınız tarafından listeleniyor, ancak oynatılabilir bölüm bulunmuyor.",
"EPISODE_INFO": "Bölüm ayrıntıları",
"SELECT_CATEGORY": "Bir kategori seçin",
"PAGE": "Sayfa",
"CONTENT_NOT_AVAILABLE": "Bu içerik bu sunucuda mevcut değil",
"PLAYBACK_ERROR": "Oynatma URL'si alınamadı",
"LOADING_SEASONS": "Sezonlar yükleniyor...",
"LOADING_ALL_CHANNELS": "Tüm kanal listesi yükleniyor…",
"REFRESH_CHANNEL_LIST": "Kanal listesini yenile",
"NO_CHANNELS_IN_CATEGORY": "Bu kategoride kanal yok",
"ITEM": "öğe",
"ITEMS": "öğe",
"NO_EPISODES": "Bölüm mevcut değil",
"ALL_RADIO": "Tüm radyolar",
"MULTI_SOURCE": {
"SOURCES": "Kaynaklar",
"SOURCE": "Kaynak",
"SOURCES_FOR": "“{{title}}” için kaynaklar",
"OPEN_SOURCES": "Alternatif kaynakları göster",
"MATCHED_BY_TMDB": "TMDB ile eşleştirildi",
"MATCHED_BY_TITLE": "Başlığa göre eşleştirildi",
"MATCH_BY_TITLE": "başlığa göre",
"PLAYING": "Oynatılıyor",
"CURRENT": "Geçerli",
"PLAY_FROM_SOURCE": "Bu kaynaktan oynat",
"PIN_SOURCE": "Bunu ana kaynak yap",
"UNPIN_SOURCE": "Bu kaynağın sabitlemesini kaldır",
"CHECK": "kontrol et",
"CHECK_TOOLTIP": "Kullanılabilirliği kontrol et",
"PROBE_OK": "kullanılabilir",
"PROBE_FAIL": "kullanılamıyor",
"PROBE_CHECKING": "kontrol ediliyor",
"AUTO_FAILOVER": "Hatada otomatik geçiş",
"PLAYING_FROM": "Şuradan oynatılıyor:",
"SWITCH_TITLE": "Kaynak kullanılamıyor — {{playlist}} kaynağına geçiliyor",
"SWITCH_DETAIL": "{{timecode}} konumundan devam ediliyor",
"SWITCH_AUDIO_WARNING": "dublaj farklı olabilir",
"SWITCHED_TO": "{{playlist}} kaynağına geçildi",
"UNDO": "Geri al",
"CHOOSE_OTHER": "Başkasını seç",
"TRY_ANOTHER_SOURCE": "Başka bir kaynak dene",
"ERROR_HAS_ALTERNATIVES": "{{playlist}} bu yayını sunmuyor. Bu film diğer oynatma listelerinizde de var:",
"RESUME_KEPT": "oynatma konumu korunacak · {{timecode}}",
"ALL_SOURCES_TRIED": "Tüm kaynaklar denendi.",
"ALSO_FOUND_IN_ONE": "— bir oynatma listesinde daha bulundu",
"ALSO_FOUND_IN_OTHER": "— {{count}} oynatma listesinde daha bulundu",
"CHECK_ALL": "tümünü kontrol et",
"FILTER_PLACEHOLDER": "Oynatma listesine göre filtrele",
"NO_MATCHES": "Bu filtreyle eşleşen oynatma listesi yok",
"COPIES": "Bu oynatma listesinde {{count}} kopya",
"MORE_SOURCES": "Kaynak listesinde +{{count}} daha"
}
},
"INFORMATION": "Bilgi",
"DOWNLOADS": {
"TITLE": "İndirilenler",
"DOWNLOAD": "İndir",
"PLAY_LOCAL": "Yerel Oynat",
"EMPTY": "Henüz indirme yok",
"EMPTY_HINT": "Çevrimdışı izlemek için film ve dizileri indirin",
"NO_PLAYLISTS_TITLE": "Add a playlist to start downloading",
"NO_PLAYLISTS_BODY": "Once you add a playlist, you can save movies and episodes here for offline playback.",
"NO_ITEMS_BODY": "Use the download button on any movie or episode to save it for offline playback.",
"NO_FILTER_MATCH": "No downloads match the current filter",
"NO_FILTER_MATCH_HINT": "Try a different search term",
"NOT_AVAILABLE": "İndirme mevcut değil",
"DESKTOP_ONLY": "Bu özellik sadece masaüstü uygulamasında mevcuttur",
"NO_FOLDER": "İndirme klasörü seçilmedi",
"CHANGE_FOLDER": "Klasörü Değiştir",
"CLEAR_COMPLETED": "Tamamlananları Temizle",
"CLEAR_COMPLETED_DIALOG": {
"TITLE": "Tamamlanan indirmeleri temizle",
"MESSAGE": "Bu işlem, geçerli indirmeler görünümünden tamamlanmış, başarısız olmuş ve iptal edilmiş kayıtları kaldırır. Diske önceden indirilmiş dosyaları silmez."
},
"SOURCE_PLAYLIST_MISSING": "Bu indirme, kaynak oynatma listesi kaldırıldığı için kütüphanede artık açılamıyor.",
"FILE_NOT_FOUND": "Bu indirilen dosya artık diskte mevcut değil.",
"FILE_ACTION_ERROR": "Dosya işlemi tamamlanamadı.",
"ACTION_FAILED": "İşlem başarısız oldu",
"PAUSE": "Duraklat",
"RESUME": "Devam et",
"CANCEL": "İptal",
"RETRY": "Tekrar Dene",
"REMOVE": "Kaldır",
"PLAY": "Oynat",
"REVEAL": "Klasörde Göster",
"COPY_URL": "İndirme URL'sini kopyala",
"URL_COPIED": "İndirme URL'si panoya kopyalandı",
"URL_COPY_FAILED": "URL panoya kopyalanamadı",
"STATUS": {
"QUEUED": "Sırada",
"DOWNLOADING": "İndiriliyor",
"COMPLETED": "Tamamlandı",
"FAILED": "Başarısız",
"CANCELED": "İptal Edildi",
"PAUSED": "Duraklatıldı"
}
},
"REMOTE_CONTROL": {
"TITLE": "Uzaktan kontrol",
"SUBTITLE": "IPTV oynatıcınızı uzaktan kontrol edin",
"UP_CHANNEL": "Kanal yukarı",
"DOWN_CHANNEL": "Kanal aşağı",
"HEADER": "IPTVNator Uzaktan Kontrol",
"FOOTER": "IPTVNator Uzaktan Kontrol"
},
"WORKSPACE": {
"FAVORITES_ONLY": "Yalnızca favoriler",
"SORT_CUSTOM": "Özel sıralama",
"SORT_DATE_DESC": "Eklenme tarihi (En yeni önce)",
"SORT_DATE_ASC": "Eklenme tarihi (En eski önce)",
"SORT_NAME_ASC": "İsim A-Z",
"SORT_NAME_DESC": "İsim Z-A",
"SORT_TOP_RATED": "Top rated",
"SORT_LOWEST_RATED": "Lowest rated first",
"REFINE_ACTION": "Refine",
"REFINE_SORT_SECTION": "Sort by",
"REFINE_RATING_SECTION": "Minimum rating",
"REFINE_CLEAR_RATING": "Clear {{value}}.0 and higher rating filter",
"FILTER_RATING": "Filter by rating",
"FILTER_RATING_ANY": "Any rating",
"FILTER_RATING_MIN": "{{value}}.0 and higher",
"SORT_LABEL": "Sırala: ",
"SORT_SERVER": "Sunucu sıralaması",
"CATEGORY_SORT_ARIA": "Kategorileri sırala",
"DASHBOARD": {
"CONTINUE_WATCHING": "İzlemeye Devam Et",
"LIVE_FAVORITES": "Favorilerinizde şu an canlı",
"LIVE_RECENT": "Canlı TV ile devam et",
"REMAINING_SECONDS": "{{seconds}} sn kaldı",
"REMAINING_MINUTES": "{{minutes}} dk kaldı",
"REMAINING_HOURS": "{{hours}} sa kaldı",
"REMAINING_HOURS_MINUTES": "{{hours}} sa {{minutes}} dk kaldı",
"PERCENT_WATCHED": "{{value}}% izlendi",
"UNTITLED_SOURCE": "Adsız kaynak",
"GLOBAL_FAVORITES": "Tüm Favoriler",
"REMOVE_FROM_HISTORY": "Geçmişten kaldır",
"RECENT_SOURCES": "Son Kullanılan Kaynaklar",
"MANAGE_ALL": "Tümünü yönet",
"SEE_ALL": "Tümünü gör",
"SEE_ALL_COUNT": "{{count}} öğenin tümünü gör",
"RECENTLY_WATCHED": "Son İzlenenler",
"XTREAM_RECENTLY_ADDED": "Xtream'de Yeni Eklenenler",
"TMDB_TRENDING": "Bu hafta trend olanlar",
"NO_HISTORY": "Bu filtre için öğe bulunamadı.",
"NO_HISTORY_HINT": "Oynatma listelerinizden içerik oynatmaya başladığınızda izleme geçmişiniz burada görünecektir.",
"XTREAM": "Xtream",
"STALKER": "Stalker",
"M3U": "M3U",
"STALKER_PORTAL": "Stalker Portalı",
"PROVIDER": "Sağlayıcı",
"TYPE_LIVE": "Canlı",
"TYPE_MOVIE": "Film",
"TYPE_SERIES": "Dizi",
"SEASON_EPISODE_BADGE": "S{{season}}·B{{episode}}",
"NOT_YET_SYNCED": "Henüz eşitlenmedi",
"RECENTLY_WATCHED_LIVE_TV": "Recently watched live TV",
"FAVORITE_MOVIES_AND_SERIES": "Favorite movies & series"
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"RAIL_DASHBOARD": "Pano",
"OPEN_DASHBOARD": "Panoyu aç",
"RAIL_SOURCES": "Kaynaklar",
"OPEN_SOURCES": "Kaynakları aç",
"RAIL_GLOBAL_FAVORITES": "Favoriler",
"RAIL_GLOBAL_RECENT": "Son izlenenler",
"RAIL_GLOBAL_SEARCH": "Global search",
"RAIL_SETTINGS": "Ayarlar",
"OPEN_SETTINGS": "Ayarları aç",
"RAIL_LIBRARY": "Kütüphane",
"RAIL_MOVIES": "Filmler",
"RAIL_LIVE": "Canlı TV",
"RAIL_SERIES": "Diziler",
"RAIL_RECENTLY_ADDED": "Yeni eklenenler",
"RAIL_SEARCH": "Gelişmiş arama",
"RAIL_RECENT": "Son izlenenler",
"RAIL_FAVORITES": "Favoriler",
"RAIL_DOWNLOADS": "İndirilenler",
"EXTERNAL_PLAYBACK_CLOSE": "Close player",
"RAIL_ALL_CHANNELS": "Tüm kanallar",
"RAIL_GROUPS": "Gruplar",
"RAIL_CONTEXT_ACTIONS": "Bölüm işlemleri",
"SEARCH_LABEL": "Ara",
"SEARCH_SOURCES_PLACEHOLDER": "Kaynaklarda ara (tüm oynatma listeleri)...",
"SEARCH_GLOBAL_PLACEHOLDER": "Search all playlists...",
"SEARCH_PLAYLIST_PLACEHOLDER": "Bu oynatma listesinde ara...",
"SEARCH_SECTION_PLACEHOLDER": "Bu bölümde ara...",
"FILTER_SECTION_PLACEHOLDER": "Bu bölümü filtrele...",
"SEARCH_STATUS_LOADED_ONLY": "Yalnızca yüklenmiş kanallar",
"COMMAND_PALETTE_SHORTCUT": "Komut paleti (Ctrl/Cmd+K)",
"OPEN_COMMAND_PALETTE": "Komut paletini aç",
"ADD_PLAYLIST_ALL": "Oynatma listesi ekle",
"ADD_PLAYLIST": "Oynatma listesi ekle",
"OPEN_DOWNLOADS": "İndirilenleri aç",
"OPEN_CONTEXT_ACTIONS": "Bölüm işlemlerini aç",
"NO_SECTION_ACTIONS": "Bu bölüm için işlem yok",
"PLAYLIST_INFO": "Oynatma listesi bilgisi",
"ACCOUNT_INFO": "Hesap bilgisi",
"REFRESH_PLAYLIST": "Oynatma listesini yenile",
"CLEAR_COMPLETED_DOWNLOADS_ALL": "Tamamlanan indirmeleri temizle (tüm oynatma listeleri)",
"CLEAR_COMPLETED_DOWNLOADS_ALL_ARIA": "Tüm oynatma listeleri için tamamlanan indirmeleri temizle",
"CLEAR_COMPLETED_DOWNLOADS_THIS_PLAYLIST": "Tamamlanan indirmeleri temizle (bu oynatma listesi)",
"CLEAR_COMPLETED_DOWNLOADS_THIS_PLAYLIST_ARIA": "Bu oynatma listesi için tamamlanan indirmeleri temizle",
"CLEAR_RECENTLY_VIEWED_SECTION": "Son izlenenleri temizle",
"CLEAR_RECENTLY_VIEWED_SECTION_ARIA": "Bu bölüm için son izlenenleri temizle",
"CLEAR_RECENTLY_VIEWED_TYPE": "Son izlenen {{type}} öğelerini temizle",
"CLEAR_RECENTLY_VIEWED_DIALOG_TITLE": "Son izlenen {{type}} öğeleri temizlensin mi?",
"CLEAR_RECENTLY_VIEWED_DIALOG_MESSAGE_PLAYLIST": "Bu oynatma listesindeki tüm {{type}} öğeleri son izlenenler geçmişinizden kaldırılacaktır. Bu işlem geri alınamaz.",
"CLEAR_RECENTLY_VIEWED_DIALOG_MESSAGE_ALL": "Tüm oynatma listelerindeki tüm {{type}} öğeleri son izlenenler geçmişinizden kaldırılacaktır. Bu işlem geri alınamaz.",
"CLEAR_FAVORITES_TYPE": "{{type}} favorilerini temizle",
"CLEAR_FAVORITES_DIALOG_TITLE": "Tüm {{type}} favorileri kaldırılsın mı?",
"CLEAR_FAVORITES_DIALOG_MESSAGE_PLAYLIST": "Bu oynatma listesindeki tüm {{type}} favoriler kaldırılacaktır. Bu işlem geri alınamaz.",
"CLEAR_FAVORITES_DIALOG_MESSAGE_ALL": "Tüm oynatma listelerindeki tüm {{type}} favoriler kaldırılacaktır. Bu işlem geri alınamaz.",
"XTREAM_CODE": "Xtream Kodu",
"STALKER_PORTAL": "Stalker Portalı",
"CHANNELS_COUNT": "{{count}} kanal",
"NO_SOURCES_AVAILABLE": "Kullanılabilir kaynak yok",
"ONE_SOURCE_AVAILABLE": "1 kaynak kullanılabilir",
"SOURCES_AVAILABLE": "{{count}} kaynak kullanılabilir",
"GO_TO_DASHBOARD": "Back to dashboard",
"GO_TO_SOURCES": "Go to Sources",
"DROP_OVERLAY_TITLE": "Drop to add playlist",
"DROP_OVERLAY_BODY": "Release an .m3u or .m3u8 file anywhere in the workspace to import it.",
"DROP_OVERLAY_REJECTED_BODY": "Please drop an .m3u or .m3u8 file. Press Esc to dismiss.",
"DROP_OVERLAY_REJECTED_UNSUPPORTED": "Unsupported file type",
"DROP_OVERLAY_REJECTED_EMPTY": "That file is empty",
"DROP_OVERLAY_REJECTED_READ_ERROR": "Could not read that file",
"DROP_IMPORT_SUCCESS": "Importing \"{{title}}\"…",
"XTREAM_IMPORT_TITLE": "Oynatma listesi eşitleniyor",
"XTREAM_IMPORT_REMOTE_BADGE": "Uzak kaynak",
"XTREAM_IMPORT_LOCAL_BADGE": "Yerel kütüphane",
"XTREAM_IMPORT_DETAIL_REMOTE": "Sağlayıcı yanıtı bekleniyor.",
"XTREAM_IMPORT_DETAIL_LOCAL": "Bir sonraki ziyaret için daha hızlı oynatma listesi geçişi hazırlanıyor.",
"XTREAM_IMPORT_PREPARING": "Yerel kütüphane hazırlanıyor...",
"XTREAM_IMPORT_LOADING": "Kaynaktan oynatma listesi verileri alınıyor...",
"XTREAM_IMPORT_SAVING": "Oynatma listesi verileri yerel olarak kaydediliyor...",
"XTREAM_IMPORT_RESTORING_FAVORITES": "Yerel kütüphaneden favoriler geri yükleniyor...",
"XTREAM_IMPORT_RESTORING_RECENT": "Yerel kütüphaneden son izlenenler geri yükleniyor...",
"XTREAM_IMPORT_PROGRESS": "{{type}} içe aktarıldı: {{current}} / {{total}}",
"XTREAM_IMPORT_CANCEL": "Eşitlemeyi durdur",
"XTREAM_IMPORT_CANCELLING": "Eşitleme durduruluyor...",
"COMMANDS": {
"GLOBAL_SEARCH_LABEL": "Search all playlists",
"GLOBAL_SEARCH_DESCRIPTION": "Open global search",
"PLAYLIST_SEARCH_LABEL": "Bu oynatma listesinde gelişmiş arama",
"PLAYLIST_SEARCH_DESCRIPTION": "Gelişmiş oynatma listesi aramasını aç",
"OPEN_GLOBAL_FAVORITES_LABEL": "Tüm favorileri aç",
"OPEN_GLOBAL_FAVORITES_DESCRIPTION": "Birleştirilmiş favorilere git",
"OPEN_DOWNLOADS_LABEL": "İndirilenleri aç",
"OPEN_DOWNLOADS_DESCRIPTION": "İndirilenler görünümüne git",
"OPEN_GLOBAL_RECENT_LABEL": "Son izlenenleri aç",
"OPEN_GLOBAL_RECENT_DESCRIPTION": "Genel son izlenenler sayfasını aç",
"OPEN_SETTINGS_LABEL": "Ayarları aç",
"OPEN_SETTINGS_DESCRIPTION": "Uygulama ayarlarına git",
"OPEN_SOURCES_LABEL": "Kaynakları yönet",
"OPEN_SOURCES_DESCRIPTION": "Kaynak yönetimine git",
"OPEN_DASHBOARD_LABEL": "Panoyu aç",
"OPEN_DASHBOARD_DESCRIPTION": "Panoya git",
"ADD_PLAYLIST_LABEL": "Yeni kaynak ekle",
"ADD_PLAYLIST_DESCRIPTION": "Oynatma listesi ekle iletişim kutusunu aç",
"ADD_PLAYLIST_M3U_LABEL": "Add M3U playlist",
"ADD_PLAYLIST_M3U_DESCRIPTION": "Open add dialog on the M3U file or URL tab",
"ADD_PLAYLIST_XTREAM_LABEL": "Add Xtream Codes playlist",
"ADD_PLAYLIST_XTREAM_DESCRIPTION": "Open add dialog on the Xtream Codes tab",
"ADD_PLAYLIST_STALKER_LABEL": "Add Stalker portal",
"ADD_PLAYLIST_STALKER_DESCRIPTION": "Open add dialog on the Stalker Portal tab",
"REFRESH_PLAYLIST_LABEL": "Oynatma listesini yenile",
"REFRESH_PLAYLIST_DESCRIPTION": "Verileri uzak kaynaktan yeniden eşitle",
"PLAYLIST_INFO_LABEL": "Oynatma listesi ayrıntıları",
"PLAYLIST_INFO_DESCRIPTION": "Geçerli oynatma listesi bilgilerini göster",
"ACCOUNT_INFO_LABEL": "Hesap bilgisi",
"ACCOUNT_INFO_DESCRIPTION": "Xtream sunucu hesabı ayrıntılarını göster",
"OPEN_VIEW_DESCRIPTION": "{{view}} görünümüne git",
"CLEAR_CURRENT_VIEW_DESCRIPTION": "Bu görünümdeki görünür tüm {{type}} öğelerini temizle",
"OPEN_MULTI_EPG_DESCRIPTION": "Çoklu kanal EPG görünümünü aç",
"GO_TO_VOD_LABEL": "Filmler",
"GO_TO_VOD_DESCRIPTION": "Filmler bölümüne atla",
"GO_TO_LIVE_LABEL": "Canlı TV",
"GO_TO_LIVE_DESCRIPTION": "Canlı TV bölümüne atla",
"GO_TO_SERIES_LABEL": "Diziler",
"GO_TO_SERIES_DESCRIPTION": "Diziler bölümüne atla",
"SWITCH_PLAYER_LABEL": "Oynatıcıyı {{name}} olarak değiştir",
"SWITCH_PLAYER_DESCRIPTION": "{{name}} oynatıcısını etkin video oynatıcı olarak ayarla",
"SWITCH_PLAYER_FEEDBACK": "Oynatıcı {{name}} olarak ayarlandı"
},
"XTREAM_REFRESH_TITLE": "Oynatma listesi yenileniyor",
"XTREAM_REFRESH_COLLECTING_DATA": "Kitaplık verileriniz korunuyor...",
"XTREAM_REFRESH_DELETING_CONTENT": "Önbelleğe alınmış yayınlar kaldırılıyor...",
"XTREAM_REFRESH_DELETING_CATEGORIES": "Önbelleğe alınmış kategoriler kaldırılıyor...",
"XTREAM_REFRESH_DETAIL_LOCAL": "Favoriler, izleme geçmişi, gizli kategoriler ve oynatma ilerlemesi eşitlemeden sonra geri yüklenmek üzere hazırlanıyor.",
"XTREAM_REFRESH_PROGRESS": "İşlenen yerel kayıtlar: {{current}} / {{total}}",
"RAIL_RADIO": "Radyo"
},
"SOURCES": {
"TITLE": "Kaynaklar",
"SUBTITLE": "Oynatma listelerini filtrele ve sırala",
"TYPE": "Tür",
"ALL": "Tümü",
"M3U_PLAYLISTS": "M3U Oynatma Listeleri",
"XTREAM_PLAYLISTS": "Xtream Oynatma Listeleri",
"STALKER_PLAYLISTS": "Stalker Oynatma Listeleri",
"ALL_PLAYLISTS": "Tüm Oynatma Listeleri",
"PLAYLIST_COUNT_ONE": "1 oynatma listesi",
"PLAYLIST_COUNT_OTHER": "{{count}} oynatma listesi"
},
"CONTEXT": {
"CATEGORIES": "Kategoriler",
"MOVIE_CATEGORIES": "Film Kategorileri",
"SERIES_CATEGORIES": "Dizi Kategorileri",
"LIVE_CATEGORIES": "Canlı Kategoriler",
"XTREAM_SYNCING_MOVIES": "Film kategorileri eşitleniyor...",
"XTREAM_SYNCING_SERIES": "Dizi kategorileri eşitleniyor...",
"XTREAM_SYNCING_LIVE": "Canlı kategoriler eşitleniyor...",
"XTREAM_SOURCE": "Xtream kaynağı",
"STALKER_PORTAL": "Stalker portalı",
"MANAGE_CATEGORIES": "Kategorileri yönet",
"LOAD_CATEGORIES_ERROR": "Kategoriler yüklenemedi",
"LOAD_CATEGORIES_ERROR_HINT": "Lütfen tekrar deneyin veya portal ayarlarını doğrulayın.",
"FILTER_BY_TYPE": "Türe göre filtrele",
"ITEM_COUNT_ONE": "1 öğe",
"ITEM_COUNT_OTHER": "{{count}} öğe",
"SEARCH": "Ara",
"SEARCH_CATEGORIES": "Kategorileri ara",
"RADIO_CATEGORIES": "Radyo kategorileri"
},
"COMMAND_PALETTE": {
"TITLE": "Komut paleti",
"SUBTITLE": "Genel ve bağlamsal işlemler",
"SEARCH_PLACEHOLDER": "Komutları filtrelemek için yazın",
"SEARCH_LABEL": "Komutları filtrele",
"EMPTY": "Komut bulunamadı",
"GROUP_GLOBAL": "Genel işlemler",
"GROUP_PLAYLIST": "Bu oynatma listesi",
"GROUP_VIEW": "Geçerli görünüm",
"GROUP_SECTION": "Bu bölüm",
"GROUP_RECENT": "Son kullanılanlar"
},
"SHORTCUTS": {
"TITLE": "Keyboard shortcuts",
"SUBTITLE": "Workspace, navigation, playback, and dialog shortcuts available in IPTVnator.",
"OPEN_TOOLTIP": "Keyboard shortcuts (?)",
"OPEN_ARIA": "Open keyboard shortcuts",
"PLATFORM": {
"MAC": "macOS layout",
"OTHER": "Windows/Linux layout"
},
"GROUPS": {
"GLOBAL": "Global",
"NAVIGATION": "Navigation",
"PLAYBACK": "Playback",
"DIALOGS": "Dialogs and lists"
},
"ITEMS": {
"OPEN_KEYBOARD_SHORTCUTS": "Open this shortcuts list",
"OPEN_COMMAND_PALETTE": "Open command palette",
"OPEN_GLOBAL_SEARCH": "Open global search",
"OPEN_RECENTLY_VIEWED": "Open recently viewed",
"SUBMIT_SHELL_SEARCH": "Submit workspace search",
"TOGGLE_SIDEBAR": "Toggle sidebar",
"M3U_CHANNEL_NUMBER": "Select M3U channel by number",
"PLAY_PAUSE": "Play or pause embedded MPV playback",
"TOGGLE_FULLSCREEN": "Toggle embedded MPV fullscreen",
"SEEK": "Seek embedded MPV playback by 5 seconds",
"ADJUST_VOLUME": "Adjust volume by 5%",
"MUTE_AUDIO": "Mute audio",
"CLOSE_PLAYER_POPOVERS": "Close embedded MPV popovers",
"COMMAND_PALETTE_NAVIGATION": "Move command palette selection",
"COMMAND_PALETTE_RUN": "Run selected command palette item",
"CLOSE_DIALOGS": "Close dialog or dismiss overlay",
"DOWNLOADS_OPEN_ITEM": "Open focused download item in the library"
}
},
"GLOBAL_FAVORITES": {
"TITLE": "Tüm Favoriler",
"SUBTITLE": "Tüm oynatma listelerinden canlı TV",
"SEARCH_PLACEHOLDER": "Favorilerde ara...",
"NO_RESULTS": "Sonuç bulunamadı",
"NO_RESULTS_HINT": "Farklı bir arama terimi deneyin",
"EMPTY_TITLE": "Henüz canlı TV favorisi yok",
"EMPTY_HINT": "M3U, Xtream veya Stalker oynatma listelerinden canlı kanalları yıldızlayarak burada görüntüleyin.",
"EMPTY_PLAYER": "İzlemeye başlamak için bir kanal seçin",
"NO_PLAYLISTS_TITLE": "No favorites to show",
"NO_PLAYLISTS_BODY": "Add a playlist to start collecting favorites from live channels, movies, and series.",
"NO_ITEMS_TITLE": "No favorites yet",
"NO_ITEMS_BODY": "Tap the star icon on any channel, movie, or series to save it here.",
"UNKNOWN_CHANNEL": "Bilinmeyen"
},
"GLOBAL_RECENT": {
"NO_PLAYLISTS_TITLE": "Nothing watched yet",
"NO_PLAYLISTS_BODY": "Add a playlist and your watching history will appear here.",
"NO_ITEMS_TITLE": "Nothing watched yet",
"NO_ITEMS_BODY": "Start watching anything from your library and it will show up here."
}
}
}