feat(downloads): track live-TV recordings in the download manager (#1452)

* feat(downloads): track live-TV recordings in the download manager

Embedded MPV recordings were written to disk and forgotten: no list, no
reveal/play, no missing-file handling, and the channel/EPG context was lost
the moment the recording stopped. Recordings now live beside downloads:

- New `recordings` table (no unique index, no playlist FK — recordings
  survive source deletion; playlist name stored via playlistDisplayLabel).
- EmbeddedMpvRecordingTracker persists the lifecycle: start/stop hooks plus
  a session-snapshot observer for implicit stops (stream-replacement
  auto-stop, frame-copy helper crash, session error/close); startup repair
  turns rows a hard kill left behind into playable `interrupted` partials.
- Channel/EPG metadata is captured at recording START in all four live
  hosts (M3U, Xtream, Stalker ITV, unified live tab); a clean stop triggers
  renderer-side enrichment with every program overlapping the recorded
  window, keyed by target path — covering recordings that span a program
  boundary. Provider EPG never reaches SQLite, so post-hoc lookup is
  impossible by design.
- Own RECORDINGS_* IPC surface + RECORDINGS_UPDATE_EVENT ping and a
  separate supportsRecordings capability gate (the supportsDownloads
  allowlist is all-or-nothing and stays untouched). Reveal/play shell IPCs
  are gated on the recordings table, so the renderer-supplied recording
  directory stays a write-location preference, not a shell-access grant.
- Manager UI: `recording` filter chip, "Recording now" queue section (REC
  pulse, elapsed, live file size — no percentage, the length is unknown),
  16:9 channel-logo Recordings library, Needs attention with Remove only,
  focused detail at /workspace/downloads/recording/:recordingId.

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

* fix(downloads): close the stop-enrichment race and repair player stubs

Greptile spotted a real ordering bug: the stop IPC returns as soon as mpv
acknowledges, while the recording row's terminal-state update is still queued
in the tracker. The renderer answers that snapshot with stop enrichment, whose
handler only accepts a terminal row — so the covered-program metadata could be
silently dropped with "Recording not found".

- EmbeddedMpvRecordingTracker.whenSettled() exposes the serialized write
  chain; RECORDINGS_UPDATE_PROGRAMS awaits it before the terminal-row lookup.
  Regression covered from both sides: the handler must not touch the database
  until the barrier resolves, and the barrier must imply a committed row.

CI also caught spec stubs that had not learned the new player inputs (my local
run-many had been an Nx cache hit, so the failures only surfaced in CI):

- Teach the `app-web-player-view` and `app-embedded-mpv-player` stubs the
  `recordingMetadata` input and `recordingStopped` output across the m3u,
  Xtream, Stalker, unified-live-tab and web-player-view specs.
- The races spec now asserts the metadata argument explicitly instead of
  matching a two-argument call.
- Extract the Stalker and unified-live-tab spec stubs into sibling
  `*.spec-stubs.ts` files (the pattern ui/playback already uses) so both specs
  stay under the 1200-line test limit without shaving assertions.

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

* test(downloads): make the recordings events spec a module

The spec deliberately has no static imports — every dependency is swapped
through jest.doMock before the harness's dynamic import — which also made it a
TS script rather than a module, so its top-level `registeredHandlers` landed in
the global scope and collided with the same-named const in stream-probe.spec.ts
(TS2451). Local per-project runs compile the specs separately and stayed green;
only the Tier A coverage suite builds them into one program, so CI caught it.

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

* fix(downloads): address Codex review on recording lifecycle

Four findings from the Codex review, all real:

- P1: `addon.stopRecording()` only dispatches — native-view uses
  `mpv_set_property_async`, frame-copy writes a helper command — so
  finalizing inside the stop hook could stat a file mpv had not flushed and
  even unlink bytes still being written. The tracker now treats the hook as a
  request and finalizes on the acknowledged inactive snapshot, with a 10 s
  bound so a lost acknowledgement cannot strand the row. Only a recording
  that never went active has its empty reservation removed. Stop enrichment
  follows through `whenFinalized(targetPath)` (bounded) instead of merely
  draining the write queue.
- Live file size: `file_size_bytes` is written at finalization only, so the
  manager's 15 s refresh reported nothing while recording. Active rows are
  now decorated with a current `fs.stat` size.
- Manager-initiated Stop bypassed both player stop paths, so recordings
  spanning program boundaries kept only the start-time program.
  `EmbeddedMpvPlayerComponent` now owns the active→inactive edge and emits
  `recordingStopped` for every trigger; the adapter and legacy toggle no
  longer emit it themselves.
- Startup recovery could terminate a row another live instance was still
  writing under IPTVNATOR_ALLOW_MULTIPLE_INSTANCES. Rows carry `owner_pid`
  and recovery skips those whose owner process is alive.

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

* fix(downloads): derive the enrichment wait from the stop fallback

Greptile caught the seam my previous fix left: the enrichment barrier waited
5 s while the tracker's acknowledgement fallback only finalizes at 10 s, so a
stop mpv never confirms let the terminal-row lookup expire early and drop the
covered programs with no retry — precisely the case the fallback exists for.
The wait is now derived from the acknowledgement bound (fallback + 1 s), with
a regression test that fails if the two ever drift apart again.

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

* fix(downloads): address the second Codex pass on recordings

Four more findings, all real:

- P1 (macOS native-view): `StopRecording` clears `recordingActive` *before*
  dispatching the async property set and restores it if the request is
  rejected, so the first inactive snapshot is optimistic, not an
  acknowledgement — the tracker could finalize (and stat) a file mpv was
  still writing, and a rejected stop would leave the row `completed` while
  recording continued. An inactive snapshot now has to survive a 1.5 s settle
  window (three poll cycles); a revived recording cancels the pending
  finalization.
- Removing a failed row unlinked its path unconditionally, which takes the
  file of a newer recording that reused the freed name within the same
  timestamp second. The cleanup now runs only while no other row claims it.
- The All chip and the header's active badge ignored recordings, so a manager
  holding only recordings read "All 0" and an active recording never showed
  up in the badge.
- Switching channels auto-stops the recording, but by the time the host
  handled the stop its `activeChannel`/EPG already described the NEW channel,
  so the old recording was enriched with the wrong schedule (and an unrelated
  program could be promoted to its title). The stop event now carries the EPG
  key captured while the recording was active and every host compares it
  before enriching.

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

* fix(downloads): close the persistence race and two recording UX gaps

- Greptile P1: the enrichment deadline (fallback + 1 s) still raced the
  terminal write — if the tracker queue or the UPDATE took longer than the
  remaining margin, `whenFinalized` returned while the row was still
  `recording` and the one-shot enrichment was dropped. The deadline now
  bounds only the wait for mpv; `finalize()` removes the entry synchronously,
  so once it has started the wait follows the write itself.
- Codex: `RECORDINGS_STOP` ignored `owner_pid`. Session ids restart per
  process, so under IPTVNATOR_ALLOW_MULTIPLE_INSTANCES stopping another
  instance's row could stop an unrelated local recording. Foreign rows are
  now refused.
- Codex: the In progress chip counted active recordings while its filter
  deliberately hid them, so clicking it showed "no matches". Active
  recordings now belong to that filter — a chip whose count disagrees with
  its page is a lie.

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

* refactor(downloads): drop the enrichment barrier instead of tuning it

Three review rounds circled the same class: synchronizing mpv's asynchronous
stop acknowledgement with a one-shot program enrichment. Each fix moved the
deadline (5 s → fallback+1 s → wait-on-the-write) without removing the reason
a deadline existed at all — the handler insisted on a *terminal* row.

It never needed one. `openSync('wx')` makes the reserved path exclusive while
a recording owns it, so the newest row for that path IS the recording that was
stopped, and `finalize()` writes only status/end time/size and never
`programs_json`. Enrichment and finalization are therefore order-independent:

- `RECORDINGS_UPDATE_PROGRAMS` matches the newest row for the path in any
  status and awaits only the tracker's write queue, which exists solely to
  guarantee the INSERT committed (a recording stopped milliseconds after it
  started).
- `whenFinalized`, its deadline constant, and the per-entry finalized promise
  are gone; the tracker keeps only the settle window and fallback that make
  *finalization* itself correct.

No behavior is lost and the whole timing class disappears with the code.

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

* fix(downloads): bind recording finalization to its entry and shield live rows from startup repair

Two races from the Codex review:

- Tracker timers finalized by reusable session id, so a stop followed by an
  immediate restart on the same session let the old settle timer finalize
  the NEW row (marked completed while mpv kept writing) and strand the old
  row in 'recording'. Finalization is now bound to the exact open entry,
  and replacing a session's entry arms the old entry's settle timer so an
  unobserved stop still finalizes it.

- reconcileStaleRecordings() runs after the renderer is interactive; a
  recording started during bootstrap has ownerPid === process.pid and was
  repaired to interrupted/failed mid-write. Recovery now skips rows the
  tracker reports as actively tracked (activeRowIds()).

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

* fix(downloads): harden recording startup repair against recycled pids and stale renderer lists

Second Codex pass on the recovery path:

- A live ownerPid alone no longer shields a row: after a crash the OS can
  recycle the pid for an unrelated process, which would park the row in
  'recording' with no instance able to finalize it. Recovery now also
  checks (best-effort, ps/tasklist) that the process looks like an
  IPTVnator/Electron instance; an unreadable name stays conservative and
  keeps the skip.

- The renderer loads before the repair pass runs and may already hold the
  pre-repair list with a stale Stop affordance; recovery now broadcasts
  one RECORDINGS_UPDATE_EVENT after changing any rows.

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

* fix(downloads): defer teardown finalization behind the flush window and bound the live-size stat

Third Codex pass:

- A synthetic error/closed snapshot from disposeSession() arrives while
  the frame-copy helper may still be flushing (0.5 s quit grace + 2 s
  SIGTERM grace before SIGKILL). Finalizing there statted a file mid-write
  — short captures became terminal 'failed', longer rows persisted a
  truncated size, and startup recovery could repair neither. The tracker
  now defers that finalization behind a 2.5 s flush window; the row stays
  'recording' (repairable) meanwhile, and an already-acknowledged stop's
  settle timer keeps its 'completed' verdict instead of being relabelled
  'interrupted'.

- The active row's live file size used a bare await stat(): one stat
  hanging on a dead network filesystem wedged every RECORDINGS_GET_LIST.
  The probe now mirrors the availability probe's contract — in-flight
  coalescing plus a 1 s deadline degrading to no size.

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

* fix(downloads): unmask recycled recording owners, guard the PWA recording route, and unblock file probes

Fourth Codex pass:

- Recycled-pid discrimination no longer stops at the process-name family
  check (any Electron app could shield the row): a live holder must also
  not provably have started after the recording did (ps -o etime= /
  PowerShell StartTime). A pid frees only when its previous owner dies, so
  a recycled pid's holder is always younger than the recording; unreadable
  evidence stays conservative.

- /workspace/downloads/recording/:recordingId gets a supportsRecordings
  capability guard redirecting the PWA to the manager — RecordingsService
  never becomes authoritative there, so the detail rendered a permanently
  blank workspace.

- Finalization and startup repair stat through a bounded async probe (3 s
  deadline, ENOENT/ENOTDIR as the only proof of absence) instead of
  main-thread statSync: a dead network mount no longer freezes the main
  thread or the tracker queue, repair leaves unjudgeable rows recoverable,
  and finalization keeps the requested status with an unknown size rather
  than branding a likely-good file failed. The 0-byte reservation unlink
  is fire-and-forget for the same reason.

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

* fix(downloads): keep inconclusive recording probes out of Needs attention and bound repair batches

Fifth Codex pass:

- Recording list decoration now uses the bounded availability variant that
  preserves 'unknown': a timed-out or permission-errored probe is not
  proof of absence, so a good recording on a slow mount no longer lands in
  Needs attention with its Play/Reveal hidden.
  ElectronRecordingItem.fileAvailability widens accordingly; consumers
  already gate on === 'missing'.

- Startup repair probes its whole batch concurrently, so main.ts awaits
  roughly one 3 s deadline instead of one per stale row.

Cross-process ping propagation under IPTVNATOR_ALLOW_MULTIPLE_INSTANCES
stays out of scope (debug-only flag, same single-window design as
DOWNLOADS_UPDATE_EVENT) — rationale left on the review thread.

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

* fix(downloads): fix duration rounding at hour boundaries and bound owner-process probes

Sixth Codex pass:

- The recording duration formatter rounded minutes after flooring hours,
  so 59:45 read '60 min' and 1:59:45 read '1 h 60 min'. One shared
  recordingDurationLabel() now rounds the total minutes before splitting
  (both the detail page and the library card used a duplicated copy).

- Startup repair's synchronous ps/tasklist/PowerShell ownership probes get
  a 2 s spawn timeout and are memoized per unique pid, so a batch of rows
  from one crashed instance costs at most one name query and one
  start-time query, and a hung process query degrades to the conservative
  fallback instead of blocking the main thread.

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

* fix(downloads): return to the manager through history from the recording detail

Seventh Codex pass (single finding): with a validated returnUrl the manager
is already the previous history entry, so Back now uses Location.back()
instead of pushing a third entry that made the browser Back button reopen
the detail; router navigation remains the fallback for direct links —
matching the offline-detail navigation.

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

* fix(downloads): bound removal cleanup and shell gates, date interrupted rows by file mtime

Eighth Codex pass:

- RECORDINGS_REMOVE no longer awaits an unbounded unlink of a failed
  row's leftover reservation: cleanup is raced against the 1 s deadline,
  so a hung network unlink cannot keep the Remove action busy — the row
  deletion is what matters.

- Reveal/Play swap the synchronous lstat gate for the bounded async
  availability probe: a dead mount no longer blocks the main process, and
  only PROVEN absence refuses the action — an inconclusive probe lets the
  shell try and answer honestly.

- Startup repair dates an interrupted row's endedAt from the captured
  file's mtime (mpv's last write) instead of the repair time, so an
  overnight shutdown no longer inflates a five-minute capture into an
  hours-long recording; the repair-time fallback remains when mtime is
  unreadable.

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

* fix(downloads): keep recording-start program metadata fresh across EPG boundaries

Ninth Codex pass (single finding): the unified live tab's
recordingMetadata computed cached its Date.now() verdict — starting a
recording after an EPG boundary snapshotted the previous show. It now
tracks the existing 30 s progress tick. The Stalker live layout's
currentProgram had the same memoization (feeding recording metadata, the
EPG panel summary, and external-player metadata); it gains a 30 s clock
tick with interval cleanup in ngOnDestroy.

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

* fix(downloads): re-select the Xtream current program against the 30 s tick at recording start

Tenth Codex pass (single finding): the Xtream live layout's recording
snapshot read withEpg().currentEpgItem, a computed whose Date.now()
verdict stays cached until epgItems changes — a recording started after
an EPG boundary snapshotted the previous show. The selection logic is
extracted as the pure findCurrentEpgItem(items, nowMs), the store
computed delegates to it unchanged, and recordingMetadata re-selects
with the layout's existing 30 s currentTimeMs tick.

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

* fix(downloads): scope stop enrichment to the exact recorded list item

Eleventh Codex pass (single finding): the stop-enrichment guard compared
only the EPG key, which is not unique for M3U items — two list entries
sharing a tvgId (or the display-name fallback) could hand the first
item's recording the second item's schedule after a switch-triggered
auto-stop. RecordingStartMetadata/RecordingStoppedEvent gain an opaque
sourceItemKey (unified tab: item.uid; M3U player: channel.id), captured
while the recording is active exactly like the EPG key, carried through
the player's stop edge, and compared by the hosts before enriching.
Xtream/Stalker keys are already playlist+id-scoped and need no extra key.

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

* fix(downloads): derive the M3U start-snapshot program from the active channel's schedule

Twelfth Codex pass (single finding): the M3U recording snapshot read the
NgRx currentEpgProgram, which retains its last value across a channel
switch and through EPG gaps (the mirror effect only dispatches when a
program exists) — a recording started on a channel with no airing
program could persist the previous channel's title, which stop
enrichment deliberately never overwrites. The snapshot now derives the
program from the active channel's own schedule against the existing 30 s
clock, and an EPG gap snapshots no program.

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

* fix(downloads): keep finalizing rows in the recovery ledger and guard the repair update

Thirteenth Codex pass (single finding): finalize() removes an entry from
the open map before its queued terminal update commits, so
activeRowIds() briefly omitted a row still persisted as 'recording' —
startup recovery overlapping a clean stop could relabel it interrupted,
after which the tracker's status-guarded update could not restore
'completed'. Finalizing entries now stay in a dedicated ledger until the
update settles, and the repair UPDATE itself is guarded on
status='recording' as a second belt against a finalization that commits
between recovery's SELECT and its write.

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

* fix(downloads): register update listeners before the initial list load

Fourteenth Codex pass (single finding): RecordingsService awaited its
initial RECORDINGS_GET_LIST before subscribing to the update ping — a
recording transition during that request pinged into the void while the
response still reflected the pre-transition state, and recording pings
are rare enough that nothing self-healed until the 15 s poll (armed only
once an active row is visible). The listener now registers first so the
load-state coalescing queues the trailing refresh. DownloadsService had
the same latent window and gets the same reorder.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: 4gray <fourgray@proton.me>
This commit is contained in:
authored and GitHub committed 2026-08-23 08:05:25 +02:00
1 parent 50871e581f
commit 5b2eb515d1
105 files changed
+8329 -299

No files matched your search

+33
View File
@@ -15,6 +15,10 @@ describe('app routes', () => {
runtime: { isElectron: boolean },
router: { parseUrl: (url: string) => unknown }
) => unknown;
let resolveRecordingsCapabilityRoute: (
runtime: { supportsRecordings: boolean },
router: { parseUrl: (url: string) => unknown }
) => unknown;
beforeAll(async () => {
jest.unstable_mockModule(
@@ -55,6 +59,8 @@ describe('app routes', () => {
workspaceChildren = workspaceRoute?.children ?? [];
resolveElectronOnlyGlobalSearchRoute =
appRoutes.resolveElectronOnlyGlobalSearchRoute;
resolveRecordingsCapabilityRoute =
appRoutes.resolveRecordingsCapabilityRoute;
});
afterEach(() => {
@@ -176,6 +182,33 @@ describe('app routes', () => {
expect(result).toBe(redirectTree);
});
it('guards the focused recording detail behind the recordings capability', async () => {
const recordingRoute = workspaceChildren.find(
(route) => route.path === 'downloads/recording/:recordingId'
);
expect(recordingRoute?.canActivate).toHaveLength(1);
});
it('redirects the recording detail to the manager without the capability', async () => {
const redirectTree = { url: '/workspace/downloads' };
const parseUrl = jest.fn().mockReturnValue(redirectTree);
const result = resolveRecordingsCapabilityRoute(
{ supportsRecordings: false },
{ parseUrl }
);
expect(parseUrl).toHaveBeenCalledWith('/workspace/downloads');
expect(result).toBe(redirectTree);
expect(
resolveRecordingsCapabilityRoute(
{ supportsRecordings: true },
{ parseUrl }
)
).toBe(true);
});
it('uses a dynamic redirect for the default /workspace child route', async () => {
const workspaceDefaultRoute = workspaceChildren.find(
(route) => route.path === ''
+30
View File
@@ -33,6 +33,28 @@ const electronOnlyGlobalSearchGuard = () => {
);
};
/**
* The focused recording detail depends on `RecordingsService`, whose list
* never becomes authoritative without the recordings capability — the PWA
* would render a permanently blank workspace instead of the not-found
* redirect. Send it to the manager, which owns its own unavailable state.
*/
export function resolveRecordingsCapabilityRoute(
runtime: Pick<RuntimeCapabilitiesService, 'supportsRecordings'>,
router: Pick<Router, 'parseUrl'>
) {
return runtime.supportsRecordings
? true
: router.parseUrl('/workspace/downloads');
}
const recordingsCapabilityGuard = () => {
return resolveRecordingsCapabilityRoute(
inject(RuntimeCapabilitiesService),
inject(Router)
);
};
export const routes: Routes = [
{
path: '',
@@ -112,6 +134,14 @@ export const routes: Routes = [
(c) => c.GlobalSearchResultsComponent
),
},
{
path: 'downloads/recording/:recordingId',
canActivate: [recordingsCapabilityGuard],
loadComponent: () =>
import('@iptvnator/portal/downloads/feature').then(
(c) => c.RecordingDetailComponent
),
},
{
path: 'downloads/:downloadId',
loadComponent: () =>
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "فشل",
"CANCELED": "ملغى",
"PAUSED": "متوقف مؤقتًا",
"FILE_MISSING": "الملف مفقود"
"FILE_MISSING": "الملف مفقود",
"RECORDING": "جارٍ التسجيل",
"INTERRUPTED": "انقطع"
},
"ACTIVE_COUNT": "{{count}} نشط",
"TRACKED_DOWNLOADS": "التنزيلات المتتبعة",
@@ -1211,7 +1213,22 @@
"ALL": "الكل",
"MOVIES": "الأفلام",
"SERIES": "المسلسلات",
"IN_PROGRESS": "قيد التقدم"
"IN_PROGRESS": "قيد التقدم",
"RECORDINGS": "التسجيلات"
},
"RECORDING_NOW": "جارٍ التسجيل الآن",
"RECORDINGS": "التسجيلات",
"RECORDING_BADGE": "جارٍ التسجيل",
"STOP_RECORDING": "إيقاف التسجيل",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "إزالة التسجيل من مدير التنزيلات؟",
"MESSAGE": "يؤدي هذا إلى إزالة الإدخال من IPTVnator. ويبقى الملف المسجّل على القرص."
},
"RECORDING_DETAIL": {
"PROGRAMS": "البرامج المسجّلة",
"RECORDED_FROM": "تم التسجيل من {{playlist}}",
"MISSING_BODY": "لم يعد ملف التسجيل موجودًا على القرص. لا يمكن إعادة تسجيل البث المباشر، لكن يمكنك إزالة الإدخال من مدير التنزيلات.",
"FAILED_BODY": "فشل هذا التسجيل ولم ينتج عنه أي ملف قابل للتشغيل."
},
"REMOVE_FROM_MANAGER": "إزالة من مدير التنزيلات",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "فشل",
"CANCELED": "تلغى",
"PAUSED": "موقوف مؤقتاً",
"FILE_MISSING": "الملف ناقص"
"FILE_MISSING": "الملف ناقص",
"RECORDING": "جاري التسجيل",
"INTERRUPTED": "تقطع"
},
"ACTIVE_COUNT": "{{count}} نشيط",
"TRACKED_DOWNLOADS": "التحميلات المتتبعة",
@@ -1211,7 +1213,22 @@
"ALL": "الكل",
"MOVIES": "الأفلام",
"SERIES": "السلاسل",
"IN_PROGRESS": "كيتم التحميل"
"IN_PROGRESS": "كيتم التحميل",
"RECORDINGS": "التسجيلات"
},
"RECORDING_NOW": "كيتسجل دابا",
"RECORDINGS": "التسجيلات",
"RECORDING_BADGE": "تسجيل",
"STOP_RECORDING": "وقّف التسجيل",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "نحيدو التسجيل من مدير التحميلات؟",
"MESSAGE": "هاد العملية كتحيد هاد العنصر من IPTVnator. الملف المسجل كيبقى على القرص."
},
"RECORDING_DETAIL": {
"PROGRAMS": "البرامج المسجلة",
"RECORDED_FROM": "تسجل من {{playlist}}",
"MISSING_BODY": "ملف التسجيل ما بقاش على القرص. البث المباشر ما يمكنش يتعاود تسجيلو، ولكن تقدر تحيد هاد العنصر من مدير التحميلات.",
"FAILED_BODY": "هاد التسجيل فشل وما عطى حتى ملف يقدر يتشغل."
},
"REMOVE_FROM_MANAGER": "حيد من مسير التحميلات",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Няўдала",
"CANCELED": "Скасавана",
"PAUSED": "На паўзе",
"FILE_MISSING": "Файл адсутнічае"
"FILE_MISSING": "Файл адсутнічае",
"RECORDING": "Запісваецца",
"INTERRUPTED": "Перарвана"
},
"ACTIVE_COUNT": "{{count}} актыўныя",
"TRACKED_DOWNLOADS": "Адсочваныя спампоўкі",
@@ -1211,7 +1213,22 @@
"ALL": "Усе",
"MOVIES": "Фільмы",
"SERIES": "Серыялы",
"IN_PROGRESS": "Выконваецца"
"IN_PROGRESS": "Выконваецца",
"RECORDINGS": "Запісы"
},
"RECORDING_NOW": "Зараз запісваецца",
"RECORDINGS": "Запісы",
"RECORDING_BADGE": "Ідзе запіс",
"STOP_RECORDING": "Спыніць запіс",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Выдаліць запіс з менеджара?",
"MESSAGE": "Гэта выдаліць запіс з IPTVnator. Запісаны файл застанецца на дыску."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Запісаныя перадачы",
"RECORDED_FROM": "Запісана з {{playlist}}",
"MISSING_BODY": "Файл запісу больш недаступны на дыску. Прамую трансляцыю нельга запісаць паўторна, але вы можаце выдаліць запіс з менеджара.",
"FAILED_BODY": "Гэты запіс не ўдаўся, таму файла для прайгравання няма."
},
"REMOVE_FROM_MANAGER": "Выдаліць з менеджара спамповак",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Fehlgeschlagen",
"CANCELED": "Abgebrochen",
"PAUSED": "Pausiert",
"FILE_MISSING": "Datei fehlt"
"FILE_MISSING": "Datei fehlt",
"RECORDING": "Wird aufgenommen",
"INTERRUPTED": "Unterbrochen"
},
"ACTIVE_COUNT": "{{count}} aktiv",
"TRACKED_DOWNLOADS": "Überwachte Downloads",
@@ -1211,7 +1213,22 @@
"ALL": "Alle",
"MOVIES": "Filme",
"SERIES": "Serien",
"IN_PROGRESS": "In Bearbeitung"
"IN_PROGRESS": "In Bearbeitung",
"RECORDINGS": "Aufnahmen"
},
"RECORDING_NOW": "Aufnahme läuft",
"RECORDINGS": "Aufnahmen",
"RECORDING_BADGE": "Aufnahme",
"STOP_RECORDING": "Aufnahme stoppen",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Aufnahme aus dem Manager entfernen?",
"MESSAGE": "Dadurch wird der Eintrag aus IPTVnator entfernt. Die aufgenommene Datei bleibt auf der Festplatte erhalten."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Aufgenommene Sendungen",
"RECORDED_FROM": "Aufgenommen aus {{playlist}}",
"MISSING_BODY": "Die Aufnahmedatei ist nicht mehr auf der Festplatte vorhanden. Eine Live-Übertragung kann nicht erneut aufgenommen werden, aber Sie können den Eintrag aus dem Manager entfernen.",
"FAILED_BODY": "Diese Aufnahme ist fehlgeschlagen und hat keine abspielbare Datei erzeugt."
},
"REMOVE_FROM_MANAGER": "Aus dem Download-Manager entfernen",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Απέτυχε",
"CANCELED": "Ακυρώθηκε",
"PAUSED": "Σε παύση",
"FILE_MISSING": "Λείπει το αρχείο"
"FILE_MISSING": "Λείπει το αρχείο",
"RECORDING": "Εγγραφή",
"INTERRUPTED": "Διακόπηκε"
},
"ACTIVE_COUNT": "{{count}} ενεργά",
"TRACKED_DOWNLOADS": "Παρακολουθούμενες λήψεις",
@@ -1211,7 +1213,22 @@
"ALL": "Όλα",
"MOVIES": "Ταινίες",
"SERIES": "Σειρές",
"IN_PROGRESS": "Σε εξέλιξη"
"IN_PROGRESS": "Σε εξέλιξη",
"RECORDINGS": "Εγγραφές"
},
"RECORDING_NOW": "Εγγραφή σε εξέλιξη",
"RECORDINGS": "Εγγραφές",
"RECORDING_BADGE": "Εγγραφή",
"STOP_RECORDING": "Διακοπή εγγραφής",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Αφαίρεση εγγραφής από τη διαχείριση;",
"MESSAGE": "Αυτό αφαιρεί την καταχώριση από το IPTVnator. Το αρχείο εγγραφής παραμένει στον δίσκο."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Εγγεγραμμένα προγράμματα",
"RECORDED_FROM": "Εγγράφηκε από {{playlist}}",
"MISSING_BODY": "Το αρχείο εγγραφής δεν βρίσκεται πλέον στον δίσκο. Μια ζωντανή μετάδοση δεν μπορεί να εγγραφεί ξανά, αλλά μπορείτε να αφαιρέσετε την καταχώριση από τη διαχείριση.",
"FAILED_BODY": "Αυτή η εγγραφή απέτυχε και δεν δημιούργησε αρχείο που να μπορεί να αναπαραχθεί."
},
"REMOVE_FROM_MANAGER": "Αφαίρεση από τον διαχειριστή λήψεων",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Failed",
"CANCELED": "Canceled",
"PAUSED": "Paused",
"FILE_MISSING": "File missing"
"FILE_MISSING": "File missing",
"RECORDING": "Recording",
"INTERRUPTED": "Interrupted"
},
"ACTIVE_COUNT": "{{count}} active",
"TRACKED_DOWNLOADS": "Tracked downloads",
@@ -1211,7 +1213,22 @@
"ALL": "All",
"MOVIES": "Movies",
"SERIES": "Series",
"IN_PROGRESS": "In progress"
"IN_PROGRESS": "In progress",
"RECORDINGS": "Recordings"
},
"RECORDING_NOW": "Recording now",
"RECORDINGS": "Recordings",
"RECORDING_BADGE": "Recording",
"STOP_RECORDING": "Stop recording",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Remove recording from manager?",
"MESSAGE": "This removes the entry from IPTVnator. The recorded file remains on disk."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Recorded programs",
"RECORDED_FROM": "Recorded from {{playlist}}",
"MISSING_BODY": "The recording file is no longer on disk. A live broadcast cannot be re-recorded, but you can remove the entry from the manager.",
"FAILED_BODY": "This recording failed and produced no playable file."
},
"REMOVE_FROM_MANAGER": "Remove from manager",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Fallido",
"CANCELED": "Cancelado",
"PAUSED": "En pausa",
"FILE_MISSING": "Falta el archivo"
"FILE_MISSING": "Falta el archivo",
"RECORDING": "Grabando",
"INTERRUPTED": "Interrumpido"
},
"ACTIVE_COUNT": "{{count}} activas",
"TRACKED_DOWNLOADS": "Descargas registradas",
@@ -1211,7 +1213,22 @@
"ALL": "Todas",
"MOVIES": "Películas",
"SERIES": "Series de TV",
"IN_PROGRESS": "En curso"
"IN_PROGRESS": "En curso",
"RECORDINGS": "Grabaciones"
},
"RECORDING_NOW": "Grabando ahora",
"RECORDINGS": "Grabaciones",
"RECORDING_BADGE": "Grabando",
"STOP_RECORDING": "Detener grabación",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "¿Quitar la grabación del gestor?",
"MESSAGE": "Esto elimina la entrada de IPTVnator. El archivo grabado permanece en el disco."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Programas grabados",
"RECORDED_FROM": "Grabado desde {{playlist}}",
"MISSING_BODY": "El archivo de la grabación ya no está en el disco. Una emisión en vivo no se puede volver a grabar, pero puedes quitar la entrada del gestor.",
"FAILED_BODY": "Esta grabación ha fallado y no ha generado ningún archivo reproducible."
},
"REMOVE_FROM_MANAGER": "Quitar del administrador de descargas",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Échec",
"CANCELED": "Annulé",
"PAUSED": "En pause",
"FILE_MISSING": "Fichier manquant"
"FILE_MISSING": "Fichier manquant",
"RECORDING": "Enregistrement",
"INTERRUPTED": "Interrompu"
},
"ACTIVE_COUNT": "{{count}} actifs",
"TRACKED_DOWNLOADS": "Téléchargements suivis",
@@ -1211,7 +1213,22 @@
"ALL": "Tous",
"MOVIES": "Films",
"SERIES": "Séries",
"IN_PROGRESS": "En cours"
"IN_PROGRESS": "En cours",
"RECORDINGS": "Enregistrements"
},
"RECORDING_NOW": "Enregistrement en cours",
"RECORDINGS": "Enregistrements",
"RECORDING_BADGE": "Enregistrement",
"STOP_RECORDING": "Arrêter l'enregistrement",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Retirer l'enregistrement du gestionnaire ?",
"MESSAGE": "Cela retire l'entrée d'IPTVnator. Le fichier enregistré reste sur le disque."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Programmes enregistrés",
"RECORDED_FROM": "Enregistré depuis {{playlist}}",
"MISSING_BODY": "Le fichier d'enregistrement n'est plus sur le disque. Une diffusion en direct ne peut pas être réenregistrée, mais vous pouvez retirer l'entrée du gestionnaire.",
"FAILED_BODY": "Cet enregistrement a échoué et n'a produit aucun fichier lisible."
},
"REMOVE_FROM_MANAGER": "Retirer du gestionnaire de téléchargements",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Sikertelen",
"CANCELED": "Megszakítva",
"PAUSED": "Paused",
"FILE_MISSING": "Hiányzik a fájl"
"FILE_MISSING": "Hiányzik a fájl",
"RECORDING": "Felvétel folyamatban",
"INTERRUPTED": "Megszakadt"
},
"ACTIVE_COUNT": "{{count}} folyamatban",
"TRACKED_DOWNLOADS": "Letöltési feladatok",
@@ -1211,7 +1213,22 @@
"ALL": "Összes",
"MOVIES": "Filmek",
"SERIES": "Sorozatok",
"IN_PROGRESS": "Folyamatban"
"IN_PROGRESS": "Folyamatban",
"RECORDINGS": "Felvételek"
},
"RECORDING_NOW": "Folyamatban lévő felvételek",
"RECORDINGS": "Felvételek",
"RECORDING_BADGE": "Felvétel",
"STOP_RECORDING": "Felvétel leállítása",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Eltávolítja a felvételt a kezelőből?",
"MESSAGE": "A művelet eltávolítja a bejegyzést az IPTVnator alkalmazásból. A felvett fájl a lemezen marad."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Felvett műsorok",
"RECORDED_FROM": "Felvétel forrása: {{playlist}}",
"MISSING_BODY": "A felvett fájl már nem érhető el a lemezen. Az élő adás nem vehető fel újra, de a bejegyzést eltávolíthatja a kezelőből.",
"FAILED_BODY": "Ez a felvétel meghiúsult, és nem jött létre lejátszható fájl."
},
"REMOVE_FROM_MANAGER": "Eltávolítás a letöltéskezelőből",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Non riuscito",
"CANCELED": "Annullato",
"PAUSED": "In pausa",
"FILE_MISSING": "File mancante"
"FILE_MISSING": "File mancante",
"RECORDING": "In registrazione",
"INTERRUPTED": "Interrotto"
},
"ACTIVE_COUNT": "{{count}} attivi",
"TRACKED_DOWNLOADS": "Download tracciati",
@@ -1211,7 +1213,22 @@
"ALL": "Tutti",
"MOVIES": "Film",
"SERIES": "Serie",
"IN_PROGRESS": "In corso"
"IN_PROGRESS": "In corso",
"RECORDINGS": "Registrazioni"
},
"RECORDING_NOW": "Registrazione in corso",
"RECORDINGS": "Registrazioni",
"RECORDING_BADGE": "In registrazione",
"STOP_RECORDING": "Interrompi registrazione",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Rimuovere la registrazione dal gestore?",
"MESSAGE": "L'elemento viene rimosso da IPTVnator. Il file registrato rimane su disco."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Programmi registrati",
"RECORDED_FROM": "Registrato da {{playlist}}",
"MISSING_BODY": "Il file della registrazione non è più disponibile su disco. Una trasmissione in diretta non può essere registrata di nuovo, ma è possibile rimuovere l'elemento dal gestore.",
"FAILED_BODY": "Questa registrazione non è riuscita e non ha prodotto alcun file riproducibile."
},
"REMOVE_FROM_MANAGER": "Rimuovi dal gestore download",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "失敗",
"CANCELED": "キャンセル済み",
"PAUSED": "一時停止中",
"FILE_MISSING": "ファイルが見つかりません"
"FILE_MISSING": "ファイルが見つかりません",
"RECORDING": "録画中",
"INTERRUPTED": "中断"
},
"ACTIVE_COUNT": "{{count}} 件が進行中",
"TRACKED_DOWNLOADS": "管理中のダウンロード",
@@ -1211,7 +1213,22 @@
"ALL": "すべて",
"MOVIES": "映画",
"SERIES": "シリーズ",
"IN_PROGRESS": "進行中"
"IN_PROGRESS": "進行中",
"RECORDINGS": "録画"
},
"RECORDING_NOW": "現在録画中",
"RECORDINGS": "録画",
"RECORDING_BADGE": "録画",
"STOP_RECORDING": "録画を停止",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "録画をマネージャーから削除しますか?",
"MESSAGE": "IPTVnatorから項目を削除します。録画ファイルはディスクに残ります。"
},
"RECORDING_DETAIL": {
"PROGRAMS": "録画された番組",
"RECORDED_FROM": "{{playlist}}から録画",
"MISSING_BODY": "録画ファイルはディスク上で見つかりませんでした。ライブ放送は再録画できませんが、項目をマネージャーから削除できます。",
"FAILED_BODY": "この録画は失敗し、再生可能なファイルは作成されませんでした。"
},
"REMOVE_FROM_MANAGER": "マネージャーから削除",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "실패",
"CANCELED": "취소됨",
"PAUSED": "일시정지됨",
"FILE_MISSING": "파일이 없습니다"
"FILE_MISSING": "파일이 없습니다",
"RECORDING": "녹화 중",
"INTERRUPTED": "중단됨"
},
"ACTIVE_COUNT": "{{count}}개 진행 중",
"TRACKED_DOWNLOADS": "관리 중인 다운로드",
@@ -1211,7 +1213,22 @@
"ALL": "전체",
"MOVIES": "영화",
"SERIES": "시리즈",
"IN_PROGRESS": "진행 중"
"IN_PROGRESS": "진행 중",
"RECORDINGS": "녹화"
},
"RECORDING_NOW": "지금 녹화 중",
"RECORDINGS": "녹화",
"RECORDING_BADGE": "녹화 중",
"STOP_RECORDING": "녹화 중지",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "관리 목록에서 녹화를 제거하시겠습니까?",
"MESSAGE": "IPTVnator에서 항목을 제거합니다. 녹화된 파일은 디스크에 그대로 남아 있습니다."
},
"RECORDING_DETAIL": {
"PROGRAMS": "녹화된 프로그램",
"RECORDED_FROM": "{{playlist}}에서 녹화됨",
"MISSING_BODY": "녹화 파일이 더 이상 디스크에 없습니다. 라이브 방송은 다시 녹화할 수 없지만, 관리 목록에서 항목을 제거할 수 있습니다.",
"FAILED_BODY": "이 녹화는 실패하여 재생 가능한 파일이 생성되지 않았습니다."
},
"REMOVE_FROM_MANAGER": "다운로드 관리자에서 제거",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Mislukt",
"CANCELED": "Geannuleerd",
"PAUSED": "Gepauzeerd",
"FILE_MISSING": "Bestand ontbreekt"
"FILE_MISSING": "Bestand ontbreekt",
"RECORDING": "Opnemen",
"INTERRUPTED": "Onderbroken"
},
"ACTIVE_COUNT": "{{count}} actief",
"TRACKED_DOWNLOADS": "Bijgehouden downloads",
@@ -1211,7 +1213,22 @@
"ALL": "Alles",
"MOVIES": "Films",
"SERIES": "Reeksen",
"IN_PROGRESS": "Bezig"
"IN_PROGRESS": "Bezig",
"RECORDINGS": "Opnames"
},
"RECORDING_NOW": "Nu aan het opnemen",
"RECORDINGS": "Opnames",
"RECORDING_BADGE": "Opname",
"STOP_RECORDING": "Opname stoppen",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Opname uit downloadbeheer verwijderen?",
"MESSAGE": "Hiermee wordt het item uit IPTVnator verwijderd. Het opgenomen bestand blijft op de schijf staan."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Opgenomen programma's",
"RECORDED_FROM": "Opgenomen van {{playlist}}",
"MISSING_BODY": "Het opnamebestand staat niet meer op de schijf. Een live-uitzending kan niet opnieuw worden opgenomen, maar je kunt het item uit downloadbeheer verwijderen.",
"FAILED_BODY": "Deze opname is mislukt en heeft geen afspeelbaar bestand opgeleverd."
},
"REMOVE_FROM_MANAGER": "Uit de downloadbeheerder verwijderen",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Niepowodzenie",
"CANCELED": "Anulowane",
"PAUSED": "Wstrzymane",
"FILE_MISSING": "Brak pliku"
"FILE_MISSING": "Brak pliku",
"RECORDING": "Nagrywanie",
"INTERRUPTED": "Przerwane"
},
"ACTIVE_COUNT": "{{count}} aktywnych",
"TRACKED_DOWNLOADS": "Śledzone pobrania",
@@ -1211,7 +1213,22 @@
"ALL": "Wszystkie",
"MOVIES": "Filmy",
"SERIES": "Seriale",
"IN_PROGRESS": "W toku"
"IN_PROGRESS": "W toku",
"RECORDINGS": "Nagrania"
},
"RECORDING_NOW": "Trwa nagrywanie",
"RECORDINGS": "Nagrania",
"RECORDING_BADGE": "Nagrywanie",
"STOP_RECORDING": "Zatrzymaj nagrywanie",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Usunąć nagranie z menedżera?",
"MESSAGE": "Spowoduje to usunięcie wpisu z IPTVnator. Nagrany plik pozostanie na dysku."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Nagrane programy",
"RECORDED_FROM": "Nagrano z {{playlist}}",
"MISSING_BODY": "Plik nagrania nie znajduje się już na dysku. Transmisji na żywo nie można nagrać ponownie, ale wpis można usunąć z menedżera.",
"FAILED_BODY": "To nagrywanie nie powiodło się i nie powstał plik możliwy do odtworzenia."
},
"REMOVE_FROM_MANAGER": "Usuń z menedżera pobierania",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Falhou",
"CANCELED": "Cancelado",
"PAUSED": "Pausado",
"FILE_MISSING": "Arquivo ausente"
"FILE_MISSING": "Arquivo ausente",
"RECORDING": "Gravando",
"INTERRUPTED": "Interrompida"
},
"ACTIVE_COUNT": "{{count}} ativos",
"TRACKED_DOWNLOADS": "Downloads acompanhados",
@@ -1211,7 +1213,22 @@
"ALL": "Todos",
"MOVIES": "Filmes",
"SERIES": "Séries",
"IN_PROGRESS": "Em andamento"
"IN_PROGRESS": "Em andamento",
"RECORDINGS": "Gravações"
},
"RECORDING_NOW": "Gravando agora",
"RECORDINGS": "Gravações",
"RECORDING_BADGE": "Gravação",
"STOP_RECORDING": "Parar gravação",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Remover gravação do gerenciador?",
"MESSAGE": "Isso remove a entrada do IPTVnator. O arquivo gravado permanece no disco."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Programas gravados",
"RECORDED_FROM": "Gravado a partir de {{playlist}}",
"MISSING_BODY": "O arquivo da gravação não está mais no disco. Uma transmissão ao vivo não pode ser gravada novamente, mas você pode remover a entrada do gerenciador.",
"FAILED_BODY": "Esta gravação falhou e não gerou nenhum arquivo reproduzível."
},
"REMOVE_FROM_MANAGER": "Remover do gerenciador",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Ошибка",
"CANCELED": "Отменено",
"PAUSED": "На паузе",
"FILE_MISSING": "Файл отсутствует"
"FILE_MISSING": "Файл отсутствует",
"RECORDING": "Идёт запись",
"INTERRUPTED": "Прервано"
},
"ACTIVE_COUNT": "{{count}} активно",
"TRACKED_DOWNLOADS": "Отслеживаемые загрузки",
@@ -1211,7 +1213,22 @@
"ALL": "Все",
"MOVIES": "Фильмы",
"SERIES": "Сериалы",
"IN_PROGRESS": "В процессе"
"IN_PROGRESS": "В процессе",
"RECORDINGS": "Записи"
},
"RECORDING_NOW": "Записываются сейчас",
"RECORDINGS": "Записи",
"RECORDING_BADGE": "Запись",
"STOP_RECORDING": "Остановить запись",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Убрать запись из менеджера?",
"MESSAGE": "Запись исчезнет из IPTVnator, но записанный файл останется на диске."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Записанные передачи",
"RECORDED_FROM": "Записано из {{playlist}}",
"MISSING_BODY": "Файл записи больше недоступен на диске. Прямой эфир нельзя записать заново, но запись можно убрать из менеджера.",
"FAILED_BODY": "Эта запись завершилась ошибкой, и файл для воспроизведения не был создан."
},
"REMOVE_FROM_MANAGER": "Удалить из менеджера загрузок",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "Başarısız",
"CANCELED": "İptal Edildi",
"PAUSED": "Duraklatıldı",
"FILE_MISSING": "Dosya eksik"
"FILE_MISSING": "Dosya eksik",
"RECORDING": "Kaydediliyor",
"INTERRUPTED": "Kesildi"
},
"ACTIVE_COUNT": "{{count}} etkin",
"TRACKED_DOWNLOADS": "İzlenen indirmeler",
@@ -1211,7 +1213,22 @@
"ALL": "Tümü",
"MOVIES": "Filmler",
"SERIES": "Diziler",
"IN_PROGRESS": "Devam ediyor"
"IN_PROGRESS": "Devam ediyor",
"RECORDINGS": "Kayıtlar"
},
"RECORDING_NOW": "Şu anda kaydediliyor",
"RECORDINGS": "Kayıtlar",
"RECORDING_BADGE": "Kaydediliyor",
"STOP_RECORDING": "Kaydı durdur",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "Kayıt yöneticiden kaldırılsın mı?",
"MESSAGE": "Bu işlem, kaydı IPTVnator'dan kaldırır. Kaydedilen dosya diskte kalır."
},
"RECORDING_DETAIL": {
"PROGRAMS": "Kaydedilen programlar",
"RECORDED_FROM": "{{playlist}} kaynağından kaydedildi",
"MISSING_BODY": "Kayıt dosyası artık diskte mevcut değil. Canlı yayın yeniden kaydedilemez, ancak kaydı yöneticiden kaldırabilirsiniz.",
"FAILED_BODY": "Bu kayıt başarısız oldu ve oynatılabilir bir dosya oluşmadı."
},
"REMOVE_FROM_MANAGER": "İndirme yöneticisinden kaldır",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "失败",
"CANCELED": "已取消",
"PAUSED": "已暂停",
"FILE_MISSING": "文件缺失"
"FILE_MISSING": "文件缺失",
"RECORDING": "录制中",
"INTERRUPTED": "已中断"
},
"ACTIVE_COUNT": "{{count}} 个进行中",
"TRACKED_DOWNLOADS": "下载任务",
@@ -1211,7 +1213,22 @@
"ALL": "全部",
"MOVIES": "电影",
"SERIES": "剧集",
"IN_PROGRESS": "进行中"
"IN_PROGRESS": "进行中",
"RECORDINGS": "录制"
},
"RECORDING_NOW": "正在录制",
"RECORDINGS": "录制内容",
"RECORDING_BADGE": "录制中",
"STOP_RECORDING": "停止录制",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "从管理器中移除录制?",
"MESSAGE": "此操作将从 IPTVnator 中移除该条目。录制文件仍保留在磁盘上。"
},
"RECORDING_DETAIL": {
"PROGRAMS": "已录制的节目",
"RECORDED_FROM": "录制自 {{playlist}}",
"MISSING_BODY": "录制文件已不在磁盘上。直播无法重新录制,但您可以从管理器中移除该条目。",
"FAILED_BODY": "此次录制失败,未生成可播放的文件。"
},
"REMOVE_FROM_MANAGER": "从管理器中移除",
"REMOVE_COMPLETED_DIALOG": {
+19 -2
View File
@@ -1196,7 +1196,9 @@
"FAILED": "失敗",
"CANCELED": "已取消",
"PAUSED": "已暫停",
"FILE_MISSING": "檔案遺失"
"FILE_MISSING": "檔案遺失",
"RECORDING": "錄製中",
"INTERRUPTED": "已中斷"
},
"ACTIVE_COUNT": "{{count}} 個進行中",
"TRACKED_DOWNLOADS": "追蹤的下載",
@@ -1211,7 +1213,22 @@
"ALL": "全部",
"MOVIES": "電影",
"SERIES": "影集",
"IN_PROGRESS": "進行中"
"IN_PROGRESS": "進行中",
"RECORDINGS": "錄製檔"
},
"RECORDING_NOW": "正在錄製",
"RECORDINGS": "錄製檔",
"RECORDING_BADGE": "錄製中",
"STOP_RECORDING": "停止錄製",
"REMOVE_RECORDING_DIALOG": {
"TITLE": "從管理員中移除錄製檔?",
"MESSAGE": "這會從 IPTVnator 移除此項目。錄製的檔案仍會保留在磁碟上。"
},
"RECORDING_DETAIL": {
"PROGRAMS": "已錄製的節目",
"RECORDED_FROM": "錄製自 {{playlist}}",
"MISSING_BODY": "錄製檔已不在磁碟上。直播節目無法重新錄製,但您可以從管理員中移除此項目。",
"FAILED_BODY": "此次錄製失敗,未產生可播放的檔案。"
},
"REMOVE_FROM_MANAGER": "從管理器移除",
"REMOVE_COMPLETED_DIALOG": {