Compare commits

..

157 Commits

Author SHA1 Message Date
KoalaDev 9f02a47d98 feat(website): add KoalaSync vs Watch2Gether comparison page 2026-07-08 16:20:44 +02:00
KoalaDev c3827fa4b7 refactor: remove cloud storage providers from domains blacklist 2026-07-08 16:04:00 +02:00
KoalaDev 61ba32addf refactor: remove localhost from domains blacklist 2026-07-08 16:03:53 +02:00
KoalaDev 140e2c0d00 chore: clarify privacy and legal notice wording 2026-07-07 12:57:58 +02:00
KoalaDev cc53494cc0 Merge pull request #25 from Shik3i/landingpage-redesign
Landing page redesign and polish
2026-07-07 07:10:24 +02:00
KoalaDev fd3c3e6cdb fix: satisfy landing page resize observer lint 2026-07-07 06:28:34 +02:00
KoalaDev 7d6ca9fd68 fix: finish responsive landing page polish 2026-07-07 06:21:36 +02:00
KoalaDev 84526cf8e9 fix: complete website seo localization 2026-07-07 02:38:28 +02:00
KoalaDev c16bf30ad5 fix: persist theme and lighten demo mockup 2026-07-07 02:28:45 +02:00
KoalaDev e559b41c93 fix: polish light mode landing page 2026-07-07 02:12:40 +02:00
KoalaDev a9687219e0 fix: normalize mobile platform logos 2026-07-07 02:07:15 +02:00
KoalaDev dfe0b42252 fix: hide hero github cta on mobile 2026-07-07 02:05:31 +02:00
KoalaDev d29c842e96 fix: compact getting started section 2026-07-07 02:02:44 +02:00
KoalaDev 07839a907b fix: refine landing page responsive layout 2026-07-07 02:00:37 +02:00
KoalaDev 78efb04b6a feat: refine landing page demo experience 2026-07-07 01:49:25 +02:00
KoalaDev e093a8afae docs: refresh onboarding and examples 2026-07-06 17:31:24 +02:00
KoalaDev b8484f77d6 content: swap privacy tile for the unique Volume Leveling feature
The combined section's third feature tile was Privacy, which is already
covered by the hero trust line and the comparison table. Replace it with
Volume Leveling / Audio Compressor, a genuinely differentiating feature not
shown anywhere else on the page. Shorten its description to match the other
tiles (all 15 locales). The three tiles are now: 1-click invites, volume
leveling, universal HTML5 support.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 16:59:09 +02:00
KoalaDev 8d7b7f68f3 copy: shorten the Universal HTML5 Support feature description
Trim the long three-sentence blurb to a tight two-liner (platform list plus
the "only sync signals, never the video" reassurance) so it matches the other
tiles in the combined section. Applied to all 15 locales.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 16:51:14 +02:00
KoalaDev 2848a4450c fix: guarantee the three hero CTAs stay on one line
The headline column in the old narrow two-column range (769-1023px) was too
tight to fit the full "Add to X" label plus two more buttons, so GitHub wrapped
to a second row. The hero is now single-column below 1024px (headline gets the
full width, demo stacks under it, matching the demo's own breakpoint) and
two-column at >=1024px where the column is wide enough. GitHub button label
shortened to "GitHub" for extra margin. Verified one row at 769/900/1024/1280
and the mobile stack at 375px.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 16:47:37 +02:00
KoalaDev 4c519a81e0 a11y: hero demo passes WCAG contrast (Lighthouse 4x100 desktop)
- Remove the confusing, space-hungry "Episode Lobby" card from the mockup
- Give the decorative demo mockup a contrast-safe palette: accent shown as
  text uses a lighter indigo, and buttons with white text on a colour get
  darker shades, so every walkthrough state meets WCAG AA
- Drop the scroll-reveal fade from the above-the-fold hero demo; the fade
  briefly rendered its text at partial opacity, which axe caught as failing
  contrast on ~1 in 3 audits
- Mark the demo as decorative for assistive tech (aria-hidden + removed from
  tab order); mouse interaction is unchanged
- Darken the footer support pill so white text meets contrast

Desktop Lighthouse: 100/100/100/100 (stable across runs). Mobile: 100
a11y / best-practices / SEO; performance ~87 (throttled LCP, separate work).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 16:30:37 +02:00
KoalaDev db70828c82 feat: browser-aware CTAs, tidier hero demo, comparison guides link
- Hero CTAs: the install buttons are compact (just the browser name) so all
  three fit one line; JS detects the visitor's browser and expands that one
  to the full "Add to X" call to action. Unknown browsers stay compact.
- Hero demo: caption now sits directly under the two windows (absolutely
  positioned to the card cascade) instead of far below the tall scene. The
  scene keeps a fixed height that already fits the open popup, so the
  vertically centered hero never jumps when the popup toggles.
- The background window now also shows a (non-interactive) extension icon,
  making it clear both browsers have KoalaSync installed.
- Escape now also closes the demo popup.
- Added a link from the comparison table to the fuller alternatives/guides
  hub, localized in all 15 languages.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 16:03:48 +02:00
KoalaDev fd0ad310ce fix: hero demo popup closes on any outside click, add close button
Outside-click handling was scoped to the demo scene, so clicks elsewhere
on the page left the popup open and it felt unclosable. A document-level
listener now collapses it from anywhere, like a real extension popup,
and the popup header gained an explicit close button next to the version
link (hidden in the static mobile mockup).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 03:23:15 +02:00
KoalaDev 917afd795e fix: bottom-anchor hero demo popup instead of oversizing the scene
Reserving the full 600px open-popup height pushed the H1 down on short
viewports. The scene now stays at 500px (below the left column height,
so it never drives the grid row) and the extension popup is anchored to
the scene bottom, growing upwards on open. Layout stays pixel-stable
when the walkthrough toggles the popup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 03:10:18 +02:00
KoalaDev da681e9e3f fix: reserve open-popup height for hero demo scene
The scene animated its own height when the walkthrough toggled the
extension popup; with the vertically centered hero grid this re-centered
the left column and shifted everything below on mid-size screens. The
scene now always reserves the 600px open state, so toggling the popup
never reflows the page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 21:56:37 +02:00
KoalaDev 37df4b267c feat: landing page redesign
- New hero headline "Turn any video into a watch party" with animated
  gradient highlight, sharper subtitle and quiet trust line under the CTAs
- Changelog badge redesigned as the koala's speech bubble (version and
  release date baked in at build time, no version.json fetch anymore)
- Light/dark theme toggle in the header (dark default, pre-paint via
  lang-init, full light theme across sections)
- Use cases and "Why KoalaSync" merged into one section with the three
  strongest feature tiles; question-mark koala moved to the FAQ
- FAQ and self-hosting setup collapsed by default via shared
  section-collapse component (hash deep links auto-open them)
- Compat/juggler section more compact
- og:image switched to the 1280x640 repository OpenGraph card (landing
  and join page), generated during build
- "watch party" keyword added to meta title/description in all 15 locales
- All em-dashes removed from user-facing copy

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 21:50:45 +02:00
KoalaDev 793248053c Merge pull request #24 from Shik3i/chore/email-koalastuff
chore: migrate contact emails to koalastuff.net
2026-07-03 23:50:05 +02:00
Timo (KoalaDev) 1aa95079cf chore: migrate all contact emails from koalamail.rocks to koalastuff.net 2026-07-03 23:44:04 +02:00
KoalaDev 0976e765c3 Merge pull request #23 from Kaia-Alenia/fix-cors-env
docs: Update Vix test status to full in TESTED_SERVICES.md
2026-07-03 18:51:25 +02:00
Kaia-Alenia ee2c284cb4 Merge remote-tracking branch 'origin/main' into fix-cors-env
# Conflicts:
#	docs/TESTED_SERVICES.md
2026-07-03 10:23:54 -06:00
Kaia-Alenia 883807afd7 docs: Update Vix test status to full in TESTED_SERVICES.md 2026-07-03 09:38:38 -06:00
KoalaDev 46b2004f78 Merge pull request #22 from Shik3i/landingpage-ui-overhaul
Landing page UI overhaul: new extension mockup, interactive demo, 3-step guide, hero fixes
2026-07-03 14:36:00 +02:00
Timo 3fd02246ab feat: Add llms.txt for AI crawler support
- Create website/llms.txt following llmstxt.org spec (H1, blockquote,
  H2 sections with link lists, Optional section)
- Include llms.txt in website build pipeline (generic files copy)
- Sections: Features, Download, Technical, Optional (alternatives,
  sitemap, privacy, imprint, join)
2026-07-03 14:26:15 +02:00
Timo 14c9ab1d5e fix: Ensure hero heading has minimum distance from fixed nav bar across all languages
- Change .hero from align-items:center to flex-start so content starts from top
- Increase padding-top in short-viewport media query (<=920px height) to
  clamp(7.5rem, 10vh, 9rem) from clamp(3.75rem, 6vh, 5.5rem) so it always
  clears the ~96px nav bar
- Fix pre-existing lint errors: remove unused variables demoFinishedByStory,
  firstTake; add website/www to eslint ignores
2026-07-03 14:02:47 +02:00
Timo 0127d62a49 Tighten hero demo layout 2026-07-03 13:09:11 +02:00
KoalaDev 2d94cc10e4 website: Fix popup not collapsing after demo walkthrough completes
Remove firstTake && !demoFinishedByStory guard from takeOver handler
so clicks outside the extension mockup always collapse the popup,
regardless of whether the automated walkthrough finished.
2026-07-03 12:43:59 +02:00
KoalaDev c5c140d31c website: Link label elements to their select elements via for/id attributes in mockups for better accessibility 2026-07-03 12:14:35 +02:00
KoalaDev 5a53abc636 website: Adjust extension-mockup wrapper height to 600px to display the leave room button without scrolling 2026-07-03 12:13:34 +02:00
KoalaDev f0328fb865 website: Fall back to static extension mockup on medium screens (768px to 1024px) to prevent overlapping in narrower columns 2026-07-03 12:11:30 +02:00
KoalaDev e1e18aa26c website: Completely hide hero extension mockup wrapper on all stacked viewport sizes (768px and below) 2026-07-03 12:09:16 +02:00
KoalaDev cece816e00 website: Completely hide hero extension demo mockup wrapper on mobile screens (480px and below) 2026-07-03 11:57:33 +02:00
KoalaDev 9690868e33 website: Add smooth FAQ accordion spring animation with rotation icon transition 2026-07-03 11:35:00 +02:00
KoalaDev cb6d7eb98c website: Reformat Step 2 illustration as a realistic vertical extension popup mockup in the Sync tab 2026-07-03 11:32:51 +02:00
KoalaDev ab6356c5ad website: Replace long series name with shorter alternatives and randomize season 1 episode numbers dynamically 2026-07-03 11:30:04 +02:00
KoalaDev 5460ac3a01 website: Add micro-animations including scroll progress bar, nav logo entrance spin, and button shine sweeps 2026-07-03 11:26:45 +02:00
KoalaDev 60c80ad7fb website: Synchronize and randomize mockup video titles, and fix Step 2 illustration to use realistic select video dropdown 2026-07-03 11:25:12 +02:00
KoalaDev c8df8b6d08 website: Redesign Step 2 browser mockup as video player + select dropdown, and redesign Step 3 mockups to display matching forest movie 2026-07-03 11:22:10 +02:00
KoalaDev 49712a7151 website: Bring lower browser mockup to foreground on hover 2026-07-03 11:20:28 +02:00
KoalaDev 23aa0c8a15 website: Fix Host Control mockup to use the actual 'Only I can control playback' toggle switch 2026-07-03 11:15:32 +02:00
KoalaDev ae3b837bb4 website: Add Host Control Mode interactive mockup to Bento Card 1 and update translations in all 15 locales 2026-07-03 11:12:23 +02:00
KoalaDev 14ab6b2d50 website: Restructure 3-step guide flowchart, swap illustrations, and rewrite translations in all 15 locales 2026-07-03 11:09:25 +02:00
KoalaDev 82b90d86a9 website: Rename mockup usernames to ChillCat and HappyDog across HTML, JS, and all 15 locales 2026-07-03 11:07:03 +02:00
KoalaDev 537c8f3c60 website: Visual polish of 3-step guide and description updates in 13 locales 2026-07-03 11:05:45 +02:00
KoalaDev 6b81668e16 website: Fix mockup animation sync/pausing and improve 3-step guide copy 2026-07-03 11:01:57 +02:00
KoalaDev d6a23f2eb5 website: Hero layout and mock walkthrough visual optimizations 2026-07-03 10:57:23 +02:00
KoalaDev 573847486b fix: hero CTAs invisible until scroll, seek animation skip, layout overhaul
- Remove data-reveal from hero h1/subtitle/mascot/cta-group/version-badge
  (critical above-fold content must never start hidden)
- Change IntersectionObserver rootMargin -150px to -30px, threshold 0.1 to 0.05
  (CTAs at ~530px were past the observer zone on laptops)
- Hero layout: align-items flex-start + padding-top 7.5rem for content at top
- Cards 62cqw to 70cqw, -3% offsets for dramatic overlap cascade
- Film animation: replace stick-figure walker with cinematic night scene
  (7 twinkling stars, glowing moon with drift, parallax mountains, firefly)
- Seek now restarts all film child animations via .demo-reset class toggle
  (was incorrectly resetting parent only — no visible jump)
- Add .demo-seek-sweep overlay + brightness flash during seek
- Video select: placeholder option ('Choose a video tab...') until story picks
- RATE 8 to 1 for realtime playback
- Version badge moved below download buttons
- All 15 locale files: add DEMO_SELECT_PLACEHOLDER key
2026-07-03 10:42:09 +02:00
KoalaDev e5a4032710 work in progress: UI overhaul updates, new logo, and localization translations 2026-07-03 10:01:15 +02:00
KoalaDev 44d452a82f docs: update and categorize all entries in docs/README.md 2026-07-02 22:58:19 +02:00
KoalaDev 82d0bd4eb8 docs: correct Netflix compatibility status from N/A to X 2026-07-02 22:54:32 +02:00
KoalaDev d3ca2a2378 docs: update tested services compatibility table and contribution guide 2026-07-02 22:51:08 +02:00
KoalaDev b7622a8347 Update feature_request.md 2026-07-02 18:30:37 +02:00
Timo 3a733934c5 fix(lint): add website/**/*.cjs files to Node globals configuration 2026-07-02 18:01:11 +02:00
Timo 25ad3e8d59 feat(website): add cross-platform indexnow submission script 2026-07-02 17:39:17 +02:00
Timo 223c3949d2 feat(website): auto-copy google and indexnow verification files during build 2026-07-02 17:38:15 +02:00
Timo 3392f0b3a7 docs: tighten v2.5.3 changelog wording 2026-07-02 17:16:15 +02:00
GitHub Action 72ea5d1ee9 chore(release): update versions to v2.5.3 [skip ci] 2026-07-02 14:59:12 +00:00
Timo 869c64171b Fix Disney+ force sync, seek, and HCM regressions for v2.5.3
The v2.5.2 Disney+ page-API integration leaked blob-relative <video>
time into force sync, seeks, and heartbeats when the page-API bridge
had no fresh data, so force sync on Disney+ appeared broken.

- getSyncCurrentTime/getSyncDuration now refuse native values on Disney+
  (return null/0) so stale bridge data degrades to a clean no-op instead
  of broadcasting garbage to peers. The get_current_time handler and
  episode/lobby/hcmIsLive paths are routed through the same accessor.
- Validate FORCE_SYNC_PREPARE/SEEK payloads as finite before relaying;
  the internal coercion no longer treats '' as 0.
- Stop double-routing FORCE_SYNC_PREPARE from the popup path (the
  generic popup route now covers only play/pause/seek).
- popup force-sync: exclude null/empty peer times from the jump-to-others
  median (Number(null)===0 was dragging the target to 0), guard against
  NaN end-to-end, clear the dangling reset timer on failure, and retry
  without re-injecting when the content script responds but the Disney+
  bridge has not yet delivered a finite time.
- hcmIsLive skips the native-duration live signal on Disney+ only,
  preserving YouTube/Twitch Infinity-duration live detection.

Disney-specific logic remains strictly gated to disneyplus.com; no
Netflix/YouTube/Twitch/generic path is affected.
2026-07-02 16:57:48 +02:00
GitHub Action 6ccaf45f8e chore(release): update versions to v2.5.2 [skip ci] 2026-07-02 13:34:11 +00:00
Timo 1b7be23b29 Document v2.5.2 release 2026-07-02 15:33:47 +02:00
KoalaDev 9ec0e9dd4e Merge pull request #19 from Shik3i/codex/netflix-seek-fix
[codex] Fix Netflix and Disney+ seek handling
2026-07-02 15:29:55 +02:00
Timo 9ac8c28442 Remove Disney+ DOM scraping fallback 2026-07-02 15:08:54 +02:00
Timo 076157fef1 Sync Disney+ via the page media player API (precise time + seek)
Disney+'s <video> is blob-relative (unusable as an absolute clock) and its
scrubber aria-value freezes during playback, so DOM scraping lagged and the
+/-10s button seek could neither reach far targets nor land precisely. The
real player hangs off the <disney-web-player> custom element as
`.mediaPlayer`, exposing seek(ms) and timeline.info (playhead/duration ms).

Since the isolated content world can't read that page object, route it
through the existing MAIN-world page-API bridge (as Netflix already does):

- page-api-seek-overrides.js: register a 'disney' provider.
- background.js installPageApiSeekBridge: seek Disney via mediaPlayer.seek(),
  and post the exact playhead/duration (seconds) to the content world every
  250ms. Both are gated on provider === 'disney'; Netflix path unchanged.
- content.js: cache the pushed playhead, prefer it in getDisneyPlusTimeline
  (DOM scraping stays as fallback), and check the page-API seek first in
  seekVideo. Outcome is identical for Netflix and generic sites.

Verified live on Disney+: reported time matches the player exactly and
seek lands within ~1s of the target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:56:02 +02:00
Timo 236da46f5d Lift the 120s cap on Disney+ relative-seek so large seeks reach target
Disney+ has no absolute seek on its blob <video>, so absolute seeks are
translated into repeated clicks of the player's relative skip button. The
click count was capped at 12 (=120s at 10s/step), so any seek further than
two minutes away silently landed short — breaking absolute-position sync
(e.g. syncing to 5:00 from far away never arrived).

Derive the step size from the matched button's own label (10s default,
some UIs use 5/15/30) and issue enough clicks to cover the full delta,
bounded at a sane maximum.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:45:12 +02:00
Timo 2fbcbc4f83 Harden Disney+ time fallback against <video> element recreation
When the control overlay (and its live time readout) is briefly absent,
the old fallback recomputed the timeline from the raw blob-relative
video.currentTime via a cached scale/start. Disney recreates the <video>
element on some play/pause transitions, which resets currentTime, so that
math produced a wildly wrong time ("loses the time" after a pause).

The native clock advances 1:1 with real playback, so instead anchor the
last live UI position to video.currentTime and extrapolate by the elapsed
native delta. If the delta is negative or implausibly large (element
recreated / seek), freeze at the last known position rather than emitting
a garbage time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:38:26 +02:00
Timo 8fe1c6dfbc Fix laggy Disney+ time by preferring the live "time remaining" indicator
Disney+ throttles the scrubber slider's aria-valuenow, so it can lag real
playback by several seconds (and its value snaps back when the control
overlay reappears on pause). The player's "time remaining" indicator,
however, updates live.

getDisneyPlusUiTimeline() now captures any element whose class/testid
mentions "remain", parses its clock text, and — when a reliable duration
is known — derives current = duration - remaining and prefers it over the
laggy slider value. Falls back to the previous behavior when no remaining
indicator is present.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:26:53 +02:00
Timo 51da169d69 Remove temporary Media Session interceptor and Video Event Log diagnostics
Clean up the exploratory Disney+ diagnostics now that the Shadow DOM
timeline/seek integration is in place:

- Remove Proposal 1 (Media Session interceptor): the background.js
  interceptor injection and the content.js message listener capturing
  __koalaMediaSessionCapture, plus the now-unused mediaSessionPosition
  reporting in content.js and popup.js.
- Remove Proposal 3 (Video Event Log): videoEventsLog, logVideoEvent(),
  the per-video event logging listeners, and the popup.html/popup.js UI.
- Keep Proposal 2 (DOM timestamp scraper) for ongoing diagnostics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:18:50 +02:00
Timo a9f5c0b9bf TEMPORARY DIAGNOSTICS: Integrate querySelectorAllShadow in Disney+ specific functions 2026-07-02 13:45:36 +02:00
Timo 705fbc146b TEMPORARY DIAGNOSTICS: Upgrade DOM scraper to support Shadow DOM recursion 2026-07-02 13:42:25 +02:00
Timo 09d35f3e48 TEMPORARY DIAGNOSTICS: Implement Proposal 2 and 3 logs in Dev tools tab 2026-07-02 13:35:08 +02:00
Timo 32994aaaa5 Support dynamic Disney+ timeline offset/scale caching for accurate seek sync 2026-07-02 13:30:20 +02:00
Timo bea0f6925c Implement cached duration fallback and simulate keyboard events fallback for Disney+ seeks when controls are hidden 2026-07-02 13:27:53 +02:00
Timo 71fa6e7315 Support absolute seeks on Disney+ by automatically translating them to relative jumps 2026-07-02 13:25:11 +02:00
Timo 54b806d270 Fix Media Session listener scoping by moving it to root IIFE level in content.js 2026-07-02 13:23:14 +02:00
Timo 066d2c9407 Implement temporary Media Session interceptor diagnostics for Disney+ and restore Netflix sessionId lookup guard 2026-07-02 13:19:47 +02:00
Timo ce522e2ab7 Safeguard Netflix player session ID lookup in background.js 2026-07-02 13:01:14 +02:00
Timo 9cbeb661d6 Save baseline state with Disney+ seek adjustments and build identifier 2026-07-02 13:00:41 +02:00
Timo beda924b65 Fix DEV_SIMULATE_REMOTE_SEEK targetTime routing in background.js and add build identifier 2026-07-02 12:57:58 +02:00
KoalaDev 01762b0c2f Normalize shared tab titles 2026-07-02 10:20:25 +02:00
KoalaDev 0af22998c8 Handle target tab navigation reinjection 2026-07-02 10:02:31 +02:00
KoalaDev 52265e84eb Generalize page API seek overrides 2026-07-02 09:57:57 +02:00
KoalaDev 57cd071d58 Add hidden remote seek test controls 2026-07-02 09:50:39 +02:00
KoalaDev 8a21fbe07f Fix Netflix seek handling 2026-07-02 09:43:11 +02:00
KoalaDev 7781d418ba Merge pull request #17 from Shik3i/feat/protocol-rate-limit-vitest
feat: Protocol spec, rate limiting, vitest, and documentation
2026-07-02 00:17:01 +02:00
Timo c022f6f490 fix: harden leave room rate limiting 2026-07-02 00:14:55 +02:00
Timo 9fe83f89b2 fix: make protocol docs match implementation 2026-07-02 00:04:49 +02:00
Timo e51e8ae436 fix: move relay changelog notes to v2.5.1 2026-07-01 23:55:42 +02:00
Timo e74abd00f7 fix: keep linux optional deps in lockfile 2026-07-01 23:51:52 +02:00
Timo dab7368537 fix: repair PR verification and docs cleanup 2026-07-01 23:46:23 +02:00
Skrockle 463f871958 feat: Add protocol spec, rate limiting, vitest framework, and documentation
## Added
- **WebSocket Protocol Specification** (docs/PROTOCOL.md) - Complete reference
  for all 20+ events with payload schemas, rate limits, and edge cases
- **LEAVE_ROOM Rate Limiting** - 10 requests/socket/minute to prevent abuse
- **Vitest Testing Framework** - Modern test setup with coverage reporting
- **Host Control Mode Documentation** - Consolidated EN documentation
  (docs/host-control-mode.md) with references to internal implementation

## Changed
- **Graceful Shutdown** - Socket.IO clients properly disconnected during
  server shutdown (server/index.js)
- **CI Workflow** - Added test step with continue-on-error for gradual rollout
- **README** - Added protocol documentation link
- **CHANGELOG** - Updated with all new features and improvements

## Moved
- Internal documentation moved to docs/internal/ for better organization
- Host Control Mode docs consolidated from 4 files to 1 comprehensive guide

## Technical Details
- Protocol spec: 495 lines, covers all events from shared/constants.js
- Rate limiter: Follows existing pattern (checkAuthRate, checkEventRate)
- Vitest: 6 tests for rate limiter, all passing
- Documentation: Host Control Mode doc includes edge cases, testing checklist,
  architecture decisions
2026-07-01 23:27:30 +02:00
GitHub Action 1f5196e1e6 chore(release): update versions to v2.5.1 [skip ci] 2026-07-01 17:48:39 +00:00
Timo 003741a63f docs: add devops.md detailing release workflow 2026-07-01 19:47:31 +02:00
KoalaDev e644ab33cd Merge pull request #15 from Shik3i/codex/issue-13-title-privacy
feat(extension): add title privacy controls and settings
2026-07-01 19:43:46 +02:00
Timo 9006ebbc67 docs: remove temporary branch planning document 2026-07-01 19:40:57 +02:00
Timo e40017f053 Move privacy settings under username 2026-07-01 19:26:25 +02:00
Timo fa7eea54d8 Split tab and media title privacy controls 2026-07-01 19:21:38 +02:00
KoalaDev 04d2360814 Remove plan for splitting large JavaScript files
Removed the plan to split large JavaScript files into smaller modules due to prioritization changes.
2026-07-01 19:16:42 +02:00
Timo 29aba936ce Clarify tab and media title privacy setting 2026-07-01 18:58:39 +02:00
Timo 4bae88e107 Fix title privacy popup import 2026-07-01 18:50:09 +02:00
Skrockle 3e4b97b11a Fix: Remove redundant TITLE_PRIVACY_MODES array in popup.js
- Remove duplicate local TITLE_PRIVACY_MODES array definition
- Use Object.values(TITLE_PRIVACY_MODES).includes() for validation instead
- Keeps single source of truth in title-privacy.js

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-01 18:37:25 +02:00
Timo e555906553 fix(extension): harden title privacy edge cases 2026-06-29 13:43:10 +02:00
Timo 4f1335242c feat(extension): add shared title privacy modes 2026-06-29 13:35:34 +02:00
GitHub Action e683466bf0 chore(release): update versions to v2.5.0 [skip ci] 2026-06-29 10:58:06 +00:00
KoalaDev 788a3331d3 Merge pull request #14 from Shik3i/feature/host-control-mode
Prepare v2.5.0 host control release
2026-06-29 12:55:57 +02:00
Timo 2d9b7bc099 Ignore Claude local files 2026-06-29 12:52:22 +02:00
Timo 717d37c693 Prepare v2.5.0 website release notes 2026-06-29 12:50:49 +02:00
KoalaDev 1f48e9df47 fix(popup): polish custom-server, host-control card & co-host UX
- Manual Connect: split the server picker into its own inset panel,
  visually separated from the Room ID / Password fields (they were
  welded together in one flat list).
- Custom server URL: stop auto-prepending ws:// — default to wss://
  for real hosts, ws:// only for localhost/127.0.0.1 (matches what
  the background already enforces; the displayed URL no longer lies).
- Host Control card: replace the raw native checkbox with the proper
  .toggle-switch slider used in Options; move the toggle row from a
  <label> to a <div> to avoid an illegally nested label.
- Co-host discoverability: owner-only hint in the Host Control card
  pointing to the participant list (new NOTICE_COHOST_HINT across all
  15 locales). Shown only when actionable: owner + co-host-capable
  relay + host-only active.
- Peer row: group right-side badges/actions into one container so they
  no longer scatter via space-between or collide with long names;
  restyle the give/revoke button (padding, weight, hover fill, tooltip).
- Gate promote/demote (and role badges) on host-only mode being ON,
  in addition to the existing owner-privilege check — roles are moot
  in 'everyone' mode, so no badge/button noise there.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 02:43:28 +02:00
KoalaDev adab684ed0 perf+fix: debounce seq persistence + role changes, null-clear stale peer state
- server: SET_PEER_ROLE now uses the same per-room 500ms debounce as
  SET_CONTROL_MODE (M-4), preventing a buggy/malicious host from
  thrashing the room's UI with rapid promote/demote bursts.
- server: PEER_STATUS relay treats explicit null as 'clear' for
  tabTitle/mediaTitle/currentTime, so the tab-close heartbeat actually
  zeroes out stale state on other peers instead of being silently
  preserved by the clamp fallback.
- background: _persistLastSeq is now trailing-debounced (500ms),
  cutting storage.session IPC writes from one-per-relayed-event to
  one-per-quiet-window in active rooms.
- locales: add missing TOAST_ID_REGENERATED to all 14 non-English
  locales (was blocking locale coverage test on main).
- test: H-1 force-sync demote test now waits out the debounce window
  to reflect real-world UI timing (a host cannot promote, run a
  force-sync, and demote inside 500ms).
2026-06-29 01:22:41 +02:00
KoalaDev e23f9ab226 fix: wire up Regenerate Peer ID button + remove arbitrary MAX_CONTROLLERS cap
Two pre-existing issues surfaced during audit follow-up:

1. popup.js bound elements.regenId but never attached a click handler —
   the 'Regenerate Peer ID' button has been dead UI since it was added
   (verified across full git history). Wired it up to send REGENERATE_ID;
   the background handler already did the right thing (rotate peerId,
   persist, force reconnect). The button is functional (regen ID on
   duplicate-identity errors), not privacy-theater — username is already
   user-changeable in settings, so it stays out of scope.

2. MAX_CONTROLLERS=10 was arbitrary. Payload math (25 controllers ×
   16-char IDs ≈ 500 bytes) is well under the 4KB maxHttpBufferSize,
   controllers.has() is O(1) on a Set, and 'just use everyone mode' is
   not a real answer when a host wants 12 controllers + 3 guests in a
   15-person room. Removed the cap entirely; the only real upper bound
   is MAX_PEERS_PER_ROOM (25).

Added TOAST_ID_REGENERATED to en.json — the i18n fallback merge
(Object.assign({}, enDict, targetDict)) covers the other 14 locales
automatically with English until they're translated.
2026-06-28 14:03:59 +02:00
KoalaDev 77203fe8fd fix(extension): drop unused roomPassword field from GET_STATUS response
Defense-in-depth: the field was always undefined in practice (no code
path ever sets currentRoom.password — verified across the full git
history), and no GET_STATUS caller in the codebase reads it. But if a
future change ever populated currentRoom.password, any caller of
GET_STATUS would receive it in cleartext.

Threat surface today is narrow (externally_connectable is not set, no
content script sends GET_STATUS, only the popup uses it), but the field
has no reason to exist — removing it eliminates the future-leak vector
for free.

Backward compatible: no caller reads this field, so removing it is a no-op
for every existing code path.
2026-06-28 08:35:05 +02:00
KoalaDev 5379778a20 fix(extension): widen peerId generation to 16 hex chars (64-bit)
8 hex chars (32-bit) gives ~7% collision probability at the relay's
25k-peer capacity and ~1% at 10k peers. A same-room collision silently
triggers the dedup path and kicks the older session with a confusing
'Another session with this ID joined' error.

16 hex chars (64-bit) drops collision probability to ~1e-10 even at one
million peers. The server's JOIN_ROOM sanitizer already clamps peerId to
16 chars, so no server change is needed. Fully backward compatible:

- Existing clients keep their persisted 8-char peerId (only newly
  generated IDs change).
- Mixed 8-char + 16-char ID rooms work — both fit the server's 16-char
  clamp.
- No protocol change on the wire.

Birthday-paradox reference (computed, not estimated):
  32-bit @ 10k peers:  1.16%
  32-bit @ 25k peers:  7.02%
  64-bit @ 25k peers:  ~0.017e-9%
2026-06-28 08:29:59 +02:00
KoalaDev 9fc58dca4b docs(assets): add ready-to-paste marketing copy kit (1-sentence, 3-sentence, full)
Three drop-in lengths for product listings, launch pages, and directory
submissions where the same pitch gets re-typed repeatedly. Bonus taglines
for hero headlines and social bios. All claims verified against the
codebase and docs/TESTED_SERVICES.md.
2026-06-28 06:02:55 +02:00
KoalaDev 17ac5c0a6f fix(host-control-mode): adversarial audit — force-sync stall, desync/lobby, gate parity, BC tests
Audit fixes (each verified against the actual code path):

H-1 (server): track force-sync initiator on PREPARE; let the demoted
initiator's EXECUTE through the host-only gate so mid-sync demotion no
longer strands the whole room paused. Clear on EXECUTE/peer-leave.

M-1 (background): episode-lobby gate now uses !amController() for parity
with CONTENT_EVENT and server gates — co-hosts can drive the room and
initiate lobbies, not just the owner.

M-2/M-3 (popup/content/background): forceSyncReset respects hcmGuestLocked;
desynced guest skips EPISODE_LOBBY so they don't get frozen in pause after
lobby completion, and checkEpisodeLobbyCompletion excludes desynced peers
from the required count so they don't block the lobby.

M-4 (background): getHostSyncTarget clamps extrapolation to 2x heartbeat
interval so a stale host heartbeat can't snap the guest tens of seconds
past the host's real position.

L-1..L-4 (server/content/background/popup): clarify dedup comment re:
network-blip window, enforce desync invariant on SW-restore, add
forceSyncBtn guest-locked backstop, refresh badge text in place.

Backward compatibility (verified by BC-1..BC-4 regression tests):
- Old client ↔ new server: server adds fields only, never requires; old
  heartbeats stripped of desynced; host-only enforced server-side even
  when the client has no awareness.
- New client ↔ old server: empty capabilities → host-control UI hidden,
  all gates default to everyone, behavior byte-identical to pre-HCM.
- Mixed rooms: every pre-HCM event type relays cleanly in both directions.
2026-06-28 05:54:50 +02:00
KoalaDev 1ec2396c41 feat(co-host): popup UI — promote/demote, controller badges, controller-aware lock
- updateHostControlUI takes a state object now; role badge shows Host / Controller
  / Guest; remote-control lock uses amController (a co-host can drive, so their
  buttons aren't locked).
- Peer list: Host/Controller badges and, for the owner, a per-peer Give control /
  Revoke button (SET_PEER_ROLE). Shown only when the feature is active and the room
  is host-only or the viewer is the owner — no role noise for guests in normal rooms.
  Hidden entirely against a relay without the co-host capability.
- i18n: BADGE_CONTROLLER, BTN_GIVE_CONTROL, BTN_REVOKE_CONTROL across all 15 locales.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 03:45:00 +02:00
KoalaDev 7c65fe80cb feat(co-host): client gates use controller-set membership (background + content)
- background: track controllers[] from ROOM_DATA/CONTROL_MODE (restore + reset on
  teardown); add amController() (owner or co-host). Both gates now key on controller
  membership (sender: !amController(); receiver: senderId not in controllers); desync
  invariant uses amController. Thread controllers/amController/coHostSupported through
  GET_STATUS/GET_CONTROL_MODE/CONTROL_MODE. New SET_PEER_ROLE message (owner→server).
- content: gate on amController instead of amHost (a controller is not a gated guest);
  reconcile/HOST_BLOCKED updated accordingly. Drop now-unused hcmAmHost.

Single-host behavior unchanged (owner is always the sole controller until they promote).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 03:41:23 +02:00
KoalaDev a326087ca5 feat(co-host): server-side multi-controller (owner promotes peers to drive)
Generalizes host-only from a single host to a controller SET. The owner (creator)
can promote/demote peers; in host-only mode the relay gate allows any controller,
not just the owner. Single-host is now the special case controllers={owner}, so
existing behavior/tests are unchanged.

- shared: CAPABILITIES.CO_HOST enabled; new SET_PEER_ROLE event; CONTROL_MODE
  payload carries controllers.
- server: room.controllers Set (always incl. owner), advertised in ROOM_DATA;
  gate checks controllers.has(peerId); SET_PEER_ROLE handler (owner-only, target
  must be present, MAX_CONTROLLERS=10 cap, re-sync on reject); controlModePayload
  helper for consistent emits; owner-leave resets controllers + falls back to
  everyone, co-host-leave broadcasts the updated list (dedup/reconnect guarded).
- WS test: promote→drive, plain guest still gated, non-owner can't promote,
  demote→re-gated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 03:36:22 +02:00
KoalaDev de518ac205 docs+test: move KNOWN_LIMITATIONS to docs/, add everyone-mode regression test, co-host plan
- Move KNOWN_LIMITATIONS.md → docs/KNOWN_LIMITATIONS.md (+ fix SECURITY.md link).
- WS test: in default 'everyone' mode a non-host guest can still drive
  play/pause/seek/force-sync/episode-lobby — proves host-control OFF == unchanged
  behavior.
- docs/host-control-mode-COHOST-PLAN.md: plan for multi-controller (owner grants
  drive rights to several co-hosts), built on the capabilities hook; covers the
  gate generalization, roles, backwards-compat, edge cases, and a separate
  large-room (510-peer) scaling track.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 03:16:28 +02:00
KoalaDev ce3cf0ef89 docs: add KNOWN_LIMITATIONS.md with threat model + NOFIX entries
Records the accepted, out-of-threat-model decisions that reviewers/agents keep
re-reporting as bugs: unauthenticated peerId (host impersonation), non-constant-
time room-password compare, public server token, enumerable room IDs, and
pause-revert-not-prevent. Spells out the threat model (ephemeral, account-less
rooms of invited participants watching a video) so the "NOFIX" rationale is clear,
and distinguishes these from genuinely-deferred items (EC-10 host grace, classifier
tuning). Cross-referenced from SECURITY.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 02:53:11 +02:00
KoalaDev 9626948f29 security(server): warn at startup when SERVER_SALT is unset
Room-password hashing falls back to a built-in salt that is public in the repo
when SERVER_SALT is not configured. Low risk (the hash never leaves the server),
but operators should set a unique salt — warn at startup, mirroring the existing
ADMIN_METRICS_TOKEN warning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 02:37:37 +02:00
KoalaDev dfa0a3d03e fix(host-control-mode): audit findings — host dedup demotion, episode-lobby freeze, +cleanups
Findings recovered from the multi-agent audit (verification phase was cut off by a
usage limit; verified inline against the code):

HIGH:
- Host was demoted on peerId dedup (fast reconnect / second tab): the dedup path
  removes the old socket and the kicked socket's 'disconnect' both ran the
  host-leave fallback before the same peerId re-joined → room silently unlocked on
  every host network blip. Now skip the fallback while a join for that peerId is in
  flight (peerJoinLocks). Regression test added. (A long real disconnect still
  falls back — that's the deferred host-grace EC-10.)
- Episode auto-advance froze a gated guest: in host-only the guest's EPISODE_LOBBY
  is dropped server-side, but the guest still self-paused (PAUSE_FOR_LOBBY) waiting
  for readies that never came → 60s freeze. A host-only guest now skips creating a
  lobby (the host drives episode sync).

LOW / cleanup:
- GET_HCM_STRINGS could return a raw key name ("HCM_DIALOG_TITLE") if the locale
  dictionary failed to load (getMessage returns the key on miss) → omit it so
  content keeps its English fallback.
- hcmReset now also clears the snap-back cooldown + buffering grace, so a stale
  cooldown can't swallow the first snap-back after a room/host change.
- Popup play/pause labels reset on lock too (not just unlock), so a button can't
  freeze on "Playing…" when host-only activates mid-click.
- Fix a stale comment (heartbeat now carries 'desynced').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 02:25:11 +02:00
KoalaDev 02c41776b8 fix(host-control-mode): re-query host pos when captured target is unusable
Audit follow-up on the snap-back paths. The immediate involuntary snap used the
captured target directly; if it was null/invalid (host position not known yet,
e.g. before the first host heartbeat) hcmSnapBackToHost no-ops and the guest is
left silently paused with no dialog and no retry. The deferred and "Stay in sync"
paths already re-query with retry — make the immediate path fall back to the same
when the captured target isn't usable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 06:21:27 +02:00
KoalaDev b32e2fa8cc feat(host-control-mode): buffer-aware deferred snap-back (#3)
Implements the snap-back robustly instead of deferring it to device testing —
since the set of video sites is unbounded, "measure which players misbehave, then
fix" doesn't scale; the snap-back must be safe on any player.

On an involuntary pause/seek, if the player isn't ready to play (readyState<3 or
seeking) we no longer snap immediately (which fights the buffer: seek → re-buffer
→ pause → … = stutter). Instead hcmDeferredSnapBack polls until the player can
play (8s cap) and then snaps ONCE to the host's re-queried current position. A
player can't play while buffering anyway, so waiting is also more correct, not
just safer. Ready players still snap immediately (no regression).

Guards: aborts if the user goes solo or is no longer a gated guest; single
pending poll (no stacking); cleared in hcmReset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 06:18:22 +02:00
KoalaDev 9963da2ebc feat(host-control-mode): explicit server capabilities for feature detection
Replace the implicit "hostPeerId present ⇒ feature supported" heuristic with an
explicit capabilities list the relay advertises in ROOM_DATA. Cleaner, self-
documenting, and the extensible hook for the planned co-host feature (owner
promotes guests to extra controllers) — a future 'co-host' capability + events
slot in without a protocol bump.

- shared: CAPABILITIES { HOST_CONTROL }.
- server: SERVER_CAPABILITIES advertised in ROOM_DATA.
- background: track serverCapabilities (empty against older relay), serverSupports(),
  thread hostControlSupported through GET_STATUS / GET_CONTROL_MODE / CONTROL_MODE.
- popup: gate the host-control card on the explicit capability instead of hostPeerId.

Backwards-compatible both ways: old relay omits the field → feature stays hidden
(no errors); old client ignores the field. WS test asserts ROOM_DATA advertises
the capability. Adds docs/host-control-mode-TESTING.md (beta-server setup, wss
caveat, two-new-clients note, verification checklist).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 04:48:45 +02:00
KoalaDev 9feafab617 ci: publish beta relay Docker image for branch testing
Adds a separate workflow that builds the server image and pushes it to GHCR under
non-production tags (beta, branch slug, sha-<commit>, and an optional custom tag
via manual dispatch) so the host-control branch can be deployed to a staging /
backup server and used as a custom server — without ever touching ':latest' that
the official relay tracks (flavor: latest=false).

Runs on push to feature/host-control-mode and on workflow_dispatch. Remove once
the feature is merged and released normally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 01:42:56 +02:00
KoalaDev d26b95985d fix(host-control-mode): only show host-control card when meaningful
The card was rendered in every room with a "Guest" badge by default — confusing
in normal rooms, and outright misleading against a relay that doesn't yet support
host control (older server omits hostPeerId, so amHost is always false and nobody
is ever recognized as host).

Now the card shows only when it's meaningful: the host always sees it (to toggle
host-only), a guest sees it only while host-only is active, and it's hidden in a
normal "everyone" room and whenever the server doesn't advertise hostPeerId
(feature unavailable). Thread hostPeerId through updateHostControlUI to detect
server support.

Note: the feature still requires the relay to run the host-control server code;
this just makes the client degrade cleanly instead of showing a stray "Guest".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 01:29:00 +02:00
KoalaDev bb030a9737 feat(host-control-mode): localize the in-page dialog & badge
The host-control desync dialog and the "watching on your own" badge were
hardcoded English — content.js has no i18n loader. Background does, so it now
resolves the strings (GET_HCM_STRINGS: loadLocale + getMessage) and content
fetches them on init with English fallbacks, re-rendering the badge if it was
already shown. The dialog body is now generic (dropped the paused/jumped verb)
to keep translation clean across languages.

Adds 6 keys (HCM_DIALOG_TITLE/BODY/STAY/SOLO, HCM_BADGE_SOLO/RESYNC) translated
across all 15 popup locales.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 16:26:31 +02:00
KoalaDev 95ff460856 fix(host-control-mode): hold desync invariant + stop Solo-badge flicker
Two issues surfaced by a holistic audit of the combined desync-persist +
reconcile-on-init changes:

1. Stale desync outside gated-guest state. hcmDesynced is persisted, but neither
   ROOM_DATA nor CONTROL_MODE cleared it when the local peer became host or the
   room switched to 'everyone'. Combined with reconcile-on-init, a desynced guest
   promoted to host (host-leave fallback) while content was reloading could
   re-adopt desynced=true — self-labelling "Solo" to all peers and ignoring host
   commands in 'everyone' mode. Enforce the invariant centrally
   (hcmEnforceDesyncInvariant: desynced ⟹ host-only && !host) on every role/mode
   change, and guard the content reconcile to only adopt when actually a gated guest.

2. Solo-badge flicker. The host-side PEER_STATUS handler set peer.desynced
   unconditionally, but the background-driven keepAlive heartbeat omits the field —
   so every ~30s it clobbered desynced to false, flickering the badge. Only update
   when present (matching the sibling fields), and include desynced in the
   background heartbeat so both heartbeat sources are consistent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 16:06:04 +02:00
KoalaDev 96e2207cf3 fix(host-control-mode): retry host-sync on dialog "Stay in sync" too
The Resync badge already retried REQUEST_HOST_SYNC when the host's state wasn't
known yet, but the dialog's "Stay in sync" path did a single no-retry request —
so a stay tap right after the host paused (before a heartbeat propagated) could
silently leave the guest paused/out of sync. Extracted the retry loop into a
shared hcmRequestHostSyncWithRetry() and used it in both paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 15:52:49 +02:00
KoalaDev 2928393b4c fix(host-control-mode): reconcile desync state on content reinjection
Fixes a split-brain where a guest who chose "watch on my own" and then reloaded
the video page would appear stuck as "Solo" to the host while actually being
back in sync.

Root cause: background persists hcmDesynced (survives SW restart) and attaches
it to content-driven heartbeats, but a freshly injected content script started
with hcmDesynced=false and GET_CONTROL_MODE never returned the desync state — so
content re-applied host commands (synced) while background kept relaying
desynced=true to the host.

Fix: GET_CONTROL_MODE now also returns desynced; content adopts it on init
(re-shows the badge) and also adopts hostPeerId so later host-change resets work.
Desync now survives a page reload consistently on both sides, as intended.

Verified the full path: background.js heartbeat (1555 omits / 2018 attaches),
reset paths, CONTENT_BOOT and keepAlive-disconnect (neither reset it), content
sendHeartbeat on inject.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 15:45:33 +02:00
KoalaDev 5863991bfb fix(host-control-mode): remove redundant CONTENT_EVENT guard, persist desync in storage, block stale tab HCM_DESYNC_STATE
Original code at L1957 already validates sender.tab tabId against
currentTabId before calling processEvent() — added guard was
duplicate. HCM_DESYNC_STATE had no such guard; added with same
sender.tab && currentTabId pattern (popup-safe via undefined
short-circuit).

hcmDesynced now persisted in session storage:
- Restored in ensureState()
- Set on HCM_DESYNC_STATE change
- Reset to false in all 3 teardown storage.sets
Survives SW restarts; cleared on leave/switch/idle-room
2026-06-26 13:23:24 +02:00
KoalaDev 1eca3fa5bb fix(host-control-mode): guard against stale tab events, persist desync across SW restart
- CONTENT_EVENT now rejected from non-selected tabs (popup exempted via sender.tab check)
- HCM_DESYNC_STATE same guard
- hcmDesynced persisted in session storage: restored on SW restart, cleared on teardown
2026-06-26 13:18:09 +02:00
KoalaDev 58af258fd3 fix(host-control-mode): harden gates, close UX races, debounce toggles, surface desync state
Bis auf peerId-Auth (akzeptiert) alle Audit-Findings behoben:
- Snap-back spielt nicht mehr blind ab, wenn Host-Status unbekannt
- Teardown-Pfade broadcasten jetzt CONTROL_MODE → kein verwaistes Badge/Dialog
- Dialog-Timer bei Ersetzung sauber gecleart
- Server re-synchronisiert Sender bei nicht-Host-Reject
- Popup-Toggle revertiert bei Server-Ablehnung
- Desync-Zweig sendet keine Phantom-ACKs mehr an Host
- Toggle-Debouncing serverseitig (500ms pro Raum)
- Button-Text auf Unlock zurückgesetzt
- Live-Erkennung defensiver, Resync-Retry bei fehlendem Target, Badge-Retry

Neu: Host sieht 'Solo'-Badge für desyncte Gäste via PEER_STATUS-Heartbeat
(16 Sprachen, neuer i18n-Key BADGE_DESYNCED/TOOLTIP_PEER_DESYNCED)

Tests: H-5-Reject-Unicast, M-4-Debounce, Desync-Heartbeat-Relay
2026-06-26 13:09:34 +02:00
KoalaDev 69f8f4cfd7 fix(extension): trust HOST_BLOCKED as authoritative (join race, EC-5)
hcmHandleBlocked no longer re-checks local control mode before acting. Background
only sends HOST_BLOCKED to a gated guest, so the message itself is authoritative —
adopt host-only/guest role from it. Fixes the join race where a HOST_BLOCKED
arriving before the CONTROL_MODE broadcast was silently ignored.

Also corrects the EC-4 note: "let catch-up re-sync" is invalid (sync is
event-driven, no continuous catch-up); the right fix is a buffer-aware deferred
snap-back.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 10:01:04 +02:00
KoalaDev af9cf34f0b fix(extension): pre-test self-audit fixes for host control mode
- popup: lock remote-control buttons (Play/Pause/SYNC) for guests in host-only
  mode so clicks don't silently get gated and leave the button stuck; backstop
  guards in the handlers and the 2.5s safety reset respects the lock.
- content: rebuild the desync dialog + resync badge with the DOM API inside a
  Shadow DOM. innerHTML inline style="" attributes are stripped by strict
  style-src CSP (Netflix/YouTube/Disney+); CSSOM .style is CSP-safe and the
  shadow root isolates from page CSS.
- content: detect live-DVR in hcmIsLive() via a sliding seekable window
  (seekable.start(0) > 1), not just duration === Infinity (EC-15).

Records remaining audit items (snap-back thrash, join race, dialog i18n) in the
edge-case log for the device-testing pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 09:54:45 +02:00
KoalaDev b1ea280f0b docs: mark host control mode implementation status
All five layers implemented; automated checks green. Records what still needs
real-device testing (EC matrix) and what was deferred by decision.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 04:15:25 +02:00
KoalaDev 9006570bd1 feat(extension): host control toggle + role badge in popup (step 5)
Final wiring of host control mode — the user-facing UI:

- Active-room section gains a Host Control card: a Host/Guest role badge, a
  host-only toggle ("Only I can control playback") shown to the host, and a
  guest notice when the room is host-only.
- Toggle sends SET_CONTROL_MODE; UI refreshes from the server's CONTROL_MODE
  broadcast (with optimistic revert on failure). Card updates on GET_STATUS,
  CONTROL_MODE, and reconnect.
- i18n: 6 new keys (LABEL_HOST_CONTROL[_TOOLTIP], LABEL_HOST_ONLY_TOGGLE,
  NOTICE_HOST_CONTROLS, BADGE_HOST, BADGE_GUEST) translated across all 15
  popup locales.

Completes the feature end to end (server + background + content + popup).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 04:14:55 +02:00
KoalaDev f9dc9d4017 feat(extension): host control mode guest UX in content script (step 4)
Guest-side handling when a room is in host-only mode:

- Intent classifier (EC-9): tells a deliberate guest pause/seek from an
  involuntary one (buffering/seeking/tab-refocus/no recent gesture) via
  readyState, video.seeking, a 'waiting' buffering-grace window, the existing
  visibility grace, and own-tracked keydown/pointerdown gestures.
- HOST_BLOCKED handler: live streams are left alone (EC-15); involuntary events
  snap back silently; deliberate events show a Teleparty-style choice dialog
  (Stay in sync / Watch on my own), defaulting to stay after 8s (EC-18).
- Snap-back reuses tryMediaAction + suppress, with a cooldown to avoid
  pause/play fight loops (EC-4).
- Desync ("watch on my own") mode: persistent resync badge, ignores host
  commands while solo (still ACKs so force-sync doesn't stall), and auto-clears
  on episode change (EC-12). Resync pulls the host's extrapolated position.
- background: GET_CONTROL_MODE (init fetch) + REQUEST_HOST_SYNC (resync target).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 04:10:15 +02:00
KoalaDev 7b37d783b5 feat(extension): host control mode wiring in background (step 3)
Background service worker now understands room control mode (no UI yet):

- Track controlMode + hostPeerId + amHost(); adopt from ROOM_DATA, update on
  CONTROL_MODE, restore from session storage, reset on leave/room-switch.
- Receiver-side gate: ignore room-moving events from non-host peers in host-only
  mode (backstop for old/buggy/modified clients past the server gate).
- Sender-side gate: a guest's play/pause/seek/force-sync is not broadcast;
  instead a HOST_BLOCKED message is sent to the content tab with an extrapolated
  host sync target (hook for step 4's snap-back + desync dialog).
- broadcastControlMode() notifies popup + content tab; GET_STATUS exposes
  controlMode/hostPeerId/amHost; new SET_CONTROL_MODE message lets the host toggle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 04:04:54 +02:00
KoalaDev 9f4be58172 feat(server): host control mode — room state, gate, toggle & host fallback
Step 2 of host control mode (server-side, fully testable without UI):

- Room now tracks hostPeerId (= first joiner) and controlMode ('everyone' default).
- ROOM_DATA carries hostPeerId + controlMode so clients know their role on join.
- SET_CONTROL_MODE handler: host-only authority check, broadcasts CONTROL_MODE.
- Relay gate: in host-only mode, drop room-moving events (play/pause/seek/
  force-sync/episode-lobby) from non-host guests — robust chokepoint that kills
  spam regardless of client. Heartbeats/ACKs/episode-ready still pass.
- removePeerFromRoom: if the host leaves, fall back to 'everyone' and reassign
  host to the earliest remaining peer (v1: immediate, no grace period).

Extends the WS integration test suite to cover all of the above.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 23:30:57 +02:00
KoalaDev a968f708b3 feat(protocol): add host control mode events & constants
Step 1 of host control mode. Adds the protocol surface only — no behavior yet:
- EVENTS.SET_CONTROL_MODE (client->server: host sets mode)
- EVENTS.CONTROL_MODE   (server->client: mode changed)
- CONTROL_MODES { EVERYONE, HOST_ONLY }

Purely additive; old clients ignore the new events. Propagated to
extension/shared via build-extension. Also records the audited design
decisions in the edge-case log.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 23:27:19 +02:00
KoalaDev 2995afe970 docs: add host control mode branch overview, plan & edge-case log
Planning docs for the optional per-room Host Control Mode (Teleparty-style
host-only playback control) requested via GitHub issue.

- host-control-mode-EDGECASES.md: canonical branch entry-point — goals, scope,
  trust model, architecture summary, 21 edge cases, test matrix, open decisions.
- host-control-mode-plan.md: layered implementation plan with concrete code hooks
  (server room state, three-layer gate, snap-back, guest desync flow, UI/i18n).

Temporary working docs for this branch; to be folded in or removed before merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 23:22:26 +02:00
KoalaDev 68027a21d5 fix: use absolute /version.json path to work on all page depths
The old logic only handled en='version.json' and non-en='../version.json',
which broke on /alternatives/ and /de/alternatives/ pages.
Absolute path works for root, /de/, /alternatives/, /de/alternatives/ etc.
2026-06-25 04:29:24 +02:00
KoalaDev 4aa6f6d7a6 fix: replace deprecated apple-mobile-web-app-capable with mobile-web-app-capable
Chrome 79+ shows deprecation warning for the apple- prefix.
Main template already uses the correct name; alternatives templates
were missed.
2026-06-25 04:28:41 +02:00
KoalaDev 7602bfdb21 fix: preserve language in cross-links, add og:locale + Organization schema to alternatives
- Fix cross-links on teleparty↔screen-sharing pages: use
  {{LANG_PREFIX}} so German page links stay German, etc.
- Add og:locale meta tag with correct locale code per language
  (e.g. en_US, de_DE, fr_FR, ja_JP, zh_CN, uk_UA)
- Add Organization schema.org JSON-LD block alongside
  existing BreadcrumbList on all 3 alternatives templates
2026-06-25 04:21:02 +02:00
KoalaDev 3ccb967a45 fix(l10n): add missing zh/uk language dropdown options to alternatives pages
- Add 中文 (Chinese) and Українська (Ukrainian) to the language
  selector in index, teleparty, and screen-sharing templates
- Previously only 13 of 15 languages were listed in the dropdown
2026-06-25 04:08:06 +02:00
KoalaDev fe970de39a fix(seo): add missing sitemap locale entries, swap KoalaSearching→RemoteProf on alternatives index
- Add 39 missing language-specific sitemap entries for fr/es/it/.../uk
  alternatives pages (index, teleparty, screen-sharing)
- Replace KoalaSearching image with RemoteProf (professor koala)
  on the Guides & Comparisons overview page
- Update og:image, twitter:image, and img src accordingly
2026-06-25 03:50:00 +02:00
Timo e795339924 Add ALT_* translations for ja, ko, zh + rebuild assets
Replaced English fallback with proper Japanese, Korean, and Chinese
translations for all 27 ALT_* website locale keys. All 15 locale
files now fully translated; npm run verify and build pass cleanly.
2026-06-25 03:00:02 +02:00
KoalaDev 06b5ffbd0b feat(alternatives): add Teleparty & Screen Sharing comparison pages with i18n
- Add article-style comparison pages for Teleparty and Screen Sharing
- Replace comparison tables with honest prose sections
- Add 15-language hreflang alternates and sitemap entries
- Add English locale fallback for untranslated languages
- Replace placeholder Draft copy with real English and German text
- Remove old Draft locale keys from all 13 non-EN/DE locales
- Rebuild outputs with zero TODO/Draft/noindex issues
2026-06-25 00:12:04 +02:00
GitHub Action 24cac52674 chore(release): update versions to v2.4.6 [skip ci] 2026-06-23 16:36:23 +00:00
108 changed files with 14834 additions and 2475 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
name: 🚀 Feature Request
about: Suggest a new feature or enhancement for KoalaSync
title: ''
labels: enhancement
labels: feature
assignees: ''
---
+75
View File
@@ -0,0 +1,75 @@
name: Beta Server Image
# Publishes the relay server as a Docker image under NON-production tags so a
# feature branch can be deployed to a staging/backup server and used as a custom
# server, without ever touching the ':latest' tag the official relay tracks.
#
# Tags produced (on ghcr.io/<owner>/<repo>):
# - beta moving channel pointer to the newest build
# - <branch-slug> e.g. feature-host-control-mode
# - sha-<short-commit> immutable, pin to an exact build
# - <custom> only on manual run, e.g. hostcontrol01
# Never ':latest' (flavor: latest=false).
#
# Remove this workflow once the feature is merged & released the normal way.
on:
push:
branches:
- feature/host-control-mode
workflow_dispatch:
inputs:
tag:
description: 'Extra immutable tag for this build (e.g. hostcontrol01). Optional.'
required: false
default: ''
concurrency:
group: beta-image-${{ github.ref }}
cancel-in-progress: true
jobs:
build-beta-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository }}
# Never publish ':latest' from a beta build.
flavor: |
latest=false
tags: |
type=raw,value=beta
type=ref,event=branch
type=sha,prefix=sha-
type=raw,value=${{ github.event.inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag != '' }}
- name: Build and push Docker image
uses: docker/build-push-action@v7
with:
context: .
file: server/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
+1
View File
@@ -27,6 +27,7 @@ Thumbs.db
# IDEs
.vscode/
.idea/
.claude/
*.swp
*.swo
+1 -1
View File
@@ -59,7 +59,7 @@ representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the project maintainer at **koaladev@koalamail.rocks**.
reported to the project maintainer at **koalasync@koalastuff.net**.
All complaints will be reviewed and investigated promptly and fairly.
All project maintainers are obligated to respect the privacy and security of the
+153
View File
@@ -0,0 +1,153 @@
> **TEMPORÄR:** Diese Datei dient nur der gemeinsamen Planung des Landingpage-UI-Overhauls
> auf dem Branch `landingpage-ui-overhaul`. Sie ist **kein** Teil des finalen Features und
> **muss vor dem Öffnen des PRs wieder gelöscht werden**.
# Landingpage UI Overhaul — Hero Mockup
## Ausgangslage
Die Landingpage (`website/template.html`, `website/style.css`, `website/app.js`, kompiliert
via `website/build.cjs` nach `website/www/`) ist bereits sehr gut, soll aber **statisch und
backend-frei** bleiben. Einziger Fokus dieses Overhauls: das rechte Hero-Mockup
(`.hero-mockup-wrapper` / `.extension-mockup`, `template.html:304-579`).
**Heutiger Zustand:** Ein 1:1-Nachbau des Extension-Popups mit 4 Tabs (Room / Sync /
Settings / Status). Tab-Wechsel nur per Klick (`app.js:391-407`), keine Animationen, kein
Auto-Play, kein Bezug zu den eigentlichen Video-Tabs, die synchronisiert werden.
## Ziel
Ein dynamischeres, platzoptimiertes Hero-Visual, das das Kernversprechen von KoalaSync
("zwei Browser-Tabs synchron abspielen") direkt zeigt — mit echten Animationen, einem
automatischen Durchlauf beim ersten Sichtbarwerden und voller manueller Bedienbarkeit
danach. Auf Mobile (Bildschirmgröße) bleibt exakt das heutige, statische Popup-Mockup
erhalten — kein neues Mobile-Design nötig, kein zusätzlicher Wartungsaufwand dort.
## Getroffene Entscheidungen (aus Rückfrage-Runde)
1. **Layout:** Gestapelt/Layered — zwei Browser-Tab-Karten leicht versetzt im Hintergrund
(Tiefeneffekt via `transform`/`box-shadow`), Extension-Popup öffnet sich als Overlay
davor/darüber.
2. **Tab-Inhalt:** Abstraktes Video-Mock — Gradient-Placeholder als "Video", Play-Button,
Fortschrittsbalken, Timestamp. Kein echtes Videomaterial, keine Marken/Logos, im
bestehenden minimalistischen `mock-*`-Stil.
3. **Auto-Play-Verhalten:** Einmaliger Durchlauf beim Sichtbarwerden (IntersectionObserver),
danach reiner manueller Modus. Kein Endlos-Loop im Hintergrund.
4. **Popup-Umfang:** Alle 4 Tabs (Room/Sync/Settings/Status) bleiben inhaltlich unverändert
— nur in die neue Animations-/Overlay-Logik eingebettet, kein Content-Kahlschlag.
## Vorgeschlagenes Storyboard (einmaliger Auto-Durchlauf)
1. **Ausgangszustand:** Zwei Tab-Karten ("Tab A" / "Tab B") sichtbar, Video-Mocks pausiert,
Fortschrittsbalken bei 0. Popup geschlossen, nur ein kleiner Launcher-Button sichtbar.
2. Popup öffnet sich (scale/slide-in) vor den Tab-Karten.
3. Simulierter Klick auf "Play" im Sync-Tab → beide Tab-Karten starten gleichzeitig
(Fortschrittsbalken laufen synchron), begleitet von einem kurzen "Sync-Pulse"
(Glow/Connection-Line) zwischen den beiden Karten.
4. Nach ein paar Sekunden: simulierter Klick auf "Pause" → beide Tabs pausieren synchron.
5. Popup schließt sich, Endzustand bleibt stehen (beide Tabs pausiert, synchron,
"Synced"-Indikator dezent sichtbar) — kein Reset auf Ausgangszustand.
6. **Danach/parallel volle manuelle Steuerung:**
- Klick auf eine Tab-Karte togglet deren eigenes Play/Pause *unabhängig* — spielt eine
Karte allein, laufen die Fortschrittsbalken bewusst auseinander ("out of sync"), um den
Schmerzpunkt zu illustrieren, den KoalaSync löst.
- Klick auf Play/Pause im Popup synchronisiert immer beide Tabs.
- Popup lässt sich jederzeit manuell öffnen/schließen, Tabs (Room/Sync/Settings/Status)
bleiben wie heute klickbar.
- Jede manuelle Interaktion während des Auto-Durchlaufs bricht diesen sofort ab und
übergibt die volle Kontrolle.
## Technischer Ansatz (Vorschlag)
- **Keine neuen Dependencies** — reines Vanilla JS + CSS, passend zur "Zero-Dependency"-
Philosophie des Projekts (siehe `website/README.md`).
- **Eine "Director"-Funktion** in `app.js`, die exakt dieselben Click-Handler wie die
manuelle Bedienung aufruft (kein doppelter Code-Pfad zwischen Auto- und Manual-Modus).
- **IntersectionObserver** triggert den einmaligen Durchlauf beim ersten Sichtbarwerden.
- **`prefers-reduced-motion`**: Auto-Durchlauf und Cursor-Ripple-Indikatoren werden
übersprungen, Endzustand wird direkt gesetzt (Pattern existiert bereits in
`style.css:2915-2939`).
- **Kein DOM-Duplikat für Mobile:** Das Popup-Markup bleibt eine einzige Quelle. Der neue
`.hero-mockup-dynamic`-Wrapper enthält die Tab-Karten + das Popup; unterhalb des
bestehenden Breakpoints (768px, siehe `style.css:1913`) werden die Tab-Karten und
Animationen per CSS ausgeblendet, das Popup fällt optisch auf das heutige, statische
Erscheinungsbild zurück, JS initialisiert dort keine Animationen. Vermeidet doppelte
i18n-Pflege der ~270 Zeilen Popup-Markup.
- **i18n-Vorsicht:** 15 Locale-Dateien unter `website/locales/*.json`. Neue sichtbare Texte
(Tab-Labels etc.) nach Möglichkeit vermeiden oder wie bestehende Mock-Inhalte
(z. B. "CoolUsername", "Stranger Things - S4E1") als nicht-lokalisierte Platzhalter
behandeln, um Übersetzungsaufwand in 15 Sprachen zu vermeiden. Neue `{{MOCK_XX}}`-Keys nur
falls wirklich nötig, dann mit Übersetzung in allen 15 Dateien.
- **Build:** Änderungen nur in den Source-Dateien (`template.html`, `style.css`, `app.js`),
niemals in `website/www/**` (generiert via `website/build.cjs`).
## Detailentscheidungen (bei der Umsetzung getroffen)
- [x] Breakpoint Mobile-Fallback: bestehende 768px übernommen (`matchMedia('(min-width: 769px)')`
im JS, `@media (max-width: 768px)` im CSS).
- [x] Auto-Durchlauf-Timing: Start ~1,4s nach 45% Sichtbarkeit (IntersectionObserver), dann
Cursor → Launcher → Sync-Tab → Play (läuft ~3s synchron) → Pause → Popup schließt.
Gesamtdauer ca. 10s.
- [x] Tab-Karten haben User-Badges passend zur Peer-Liste im Popup: "CoolUsername" (indigo)
und "KoalaPC" (grün), gleicher Episodentitel "Stranger Things - S4E1".
- [x] Echter animierter Ghost-Mauszeiger (SVG-Cursor mit Klick-Ripple), kein reiner
Ripple-Indikator — verkauft den Durchlauf deutlich besser.
- [x] Keine externe Referenz — eigener Stil, konsistent mit dem bestehenden `mock-*`-Design.
- [x] ~~Drift-Konzept~~ **Verworfen nach Feedback** — es stellte die Extension falsch dar
(als bräuchte sie das offene Popup). Ersetzt durch das **Broadcast-Modell**: Play/
Pause/Seek auf *irgendeinem* Video wird automatisch zum Peer gespiegelt (Puls wandert
über die Verbindungslinie, Peer folgt ~260ms später, Engine gleicht die Zeit an). Der
Status-Chip bleibt grün "In sync" und blitzt bei jedem Broadcast kurz das Event auf
("▶ CoolUsername", "❚❚ KoalaPC", "» …" für Seek). Das Popup ist nur die Fernbedienung
obendrauf, nie Voraussetzung.
- [x] Rework 2 nach Feedback: Kein reservierter Popup-Platz mehr; KoalaSync-Icon als
Extension-Button in der Toolbar des vorderen Fensters; Fortschrittsbalken klickbar
(Seek wird mitgesynct, Scrub-Thumb bei Hover).
- [x] Rework 3 nach Feedback: Fenster-Kaskade statt starker Überlappung — das **rechte**
Fenster (CoolUsername, mit dem Toolbar-Icon oben rechts) liegt im Vordergrund, das
linke (KoalaPC) versetzt dahinter unten links. Das Popup öffnet **rechts außen**
(right: 0, verankert unterm Icon), die beiden Tabs weichen dabei animiert nach links
aus und bleiben sichtbar. Verbindungslinie komplett entfernt (Sync-Feedback = Puls-
Ring auf beiden Karten + Event-Chip). Peer-Latenz von 260ms auf 90ms gesenkt — wirkt
quasi instant.
- [x] Rework 4 nach Feedback: **Story-Walkthrough in 5 Akten** beim ersten Sichtbarwerden —
(1) Extension über das Toolbar-Icon öffnen, (2) "Raum erstellen" (eigener Pre-Room-
Screen im Popup, `#demo-room-empty`), (3) Invite-Link fliegt animiert vom Popup zum
zweiten Browserfenster (Toast "Raum beigetreten", KoalaPC erscheint in der Peer-Liste,
"IN SYNC"-Chip blendet erst jetzt ein), (4) beide wählen den Video-Tab (Select-Flash im
Popup + Toast am zweiten Fenster), (5) Play für alle → Popup schließt → Pause vom
anderen Tab, Seek mit Nachziehen, Play. Danach voll manuell. Jede Interaktion während
der Story bricht ab und springt via idempotentem `finishDemo()` sofort in den End-
zustand (Raum verbunden, Chip an, Hint an; Popup schließt bei Klicks außerhalb);
der Nutzer-Klick wirkt anschließend normal. 4 neue Locale-Keys (DEMO_NO_ROOM,
DEMO_CREATE_ROOM, DEMO_JOINED, DEMO_TAB_SELECTED) in allen 15 Sprachen.
- [x] Abspiel-Animation: abstrakter **Strichfigur-Kurzfilm** pro Video (SVG: Hügel-
Silhouetten, laufende Figur mit schwingenden Armen/Beinen, hüpfender Ball) — läuft
nur bei `playing`, Pause friert beide Filme im selben Frame ein
(`animation-play-state`), was die Synchronität sofort sichtbar macht.
## Umsetzungsstatus
- [x] `website/template.html`: Hero-Mockup in `.hero-demo-scene` eingebettet (Chip, 2 Tab-
Karten, Verbindungslinie, Launcher, Ghost-Cursor, Hint); Popup-Inhalt unverändert,
nur IDs für Play/Pause/Force-Sync ergänzt.
- [x] `website/style.css`: Demo-Styles mit Container-Queries (cqw + px-Fallbacks),
Mobile-Fallback, `prefers-reduced-motion`-Support.
- [x] `website/app.js`: `initHeroDemo()` — Playback-Uhr (rAF, 8x Zeitraffer), Broadcast-
Logik (Play/Pause/Seek → Peer folgt), manuelle Steuerung, einmaliger Auto-Durchlauf
(Play in Tab A → Seek in Tab B → Pause in Tab B → Popup öffnen/Play/schließen); jede
Interaktion bricht den Durchlauf sofort ab. Auto- und Manuell-Modus nutzen dieselben
Click-Handler. Launcher-Klicks sind vom Karten-Play/Pause entkoppelt (closest-Guard).
- [x] `website/locales/*.json`: 3 neue Keys (`DEMO_SYNC`, `DEMO_DRIFT`, `DEMO_HINT`) in
allen 15 Sprachen.
- [x] `eslint.config.mjs`: `requestAnimationFrame`/`cancelAnimationFrame` zu den Globals.
- [x] Verifiziert im Browser-Preview: Auto-Durchlauf, Drift-Szenario, Chip-Rettungsweg,
Re-Sync, Mobile-Fallback (statisches Popup wie vorher), keine Konsolenfehler,
Lint sauber, Build ohne offene Platzhalter.
- [ ] `.claude/launch.json` zeigt auf einen Session-Scratchpad-Server — vor dem PR prüfen,
ob die Datei ins Repo soll (ggf. löschen oder auf dauerhaften Pfad umstellen).
## Scope / Nicht-Ziele
- Kein Backend, keine echten Videos/Assets, keine neuen npm-Dependencies.
- Kein Umbau des restlichen Landingpage-Contents (Features, Compat-Section, Footer etc.) —
ausschließlich das Hero-Mockup.
- Mobile-Ansicht bekommt **kein** neues Design, bleibt 1:1 der heutige Stand.
+18 -11
View File
@@ -6,7 +6,7 @@
<p align="center">
<a href="https://github.com/Shik3i/KoalaSync/actions/workflows/release.yml"><img src="https://github.com/Shik3i/KoalaSync/actions/workflows/release.yml/badge.svg" alt="Release Status"></a>
<a href="https://github.com/Shik3i/KoalaSync/releases"><img src="https://img.shields.io/badge/Release-v2.4.6-blue?logo=github" alt="GitHub release"></a>
<a href="https://github.com/Shik3i/KoalaSync/releases"><img src="https://img.shields.io/badge/Release-v2.5.3-blue?logo=github" alt="GitHub release"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue" alt="License"></a>
<a href="https://addons.mozilla.org/de/firefox/addon/koalasync/"><img src="https://img.shields.io/badge/Firefox-Download-orange?logo=firefoxbrowser&logoColor=white" alt="Firefox Add-on"></a>
<a href="https://chromewebstore.google.com/detail/koalasync/obbnmkmlaaddodakcbdljknjpagklifc"><img src="https://img.shields.io/badge/Chrome-Download-blue?logo=googlechrome&logoColor=white" alt="Chrome Extension"></a>
@@ -14,7 +14,7 @@
<p align="center"><i>KoalaSync is a lightweight Browser Extension and Relay Server for synchronized video playback on almost any website with a video element—YouTube, Twitch, Netflix, Emby, Jellyfin, and beyond. Built with a focus on <b>Data Sovereignty</b> and <b>Performance</b>.</i></p>
<p align="center"><a href="docs/CHANGELOG.md"><b>New v2.4.6 Release!</b> — See what's changed</a></p>
<p align="center"><a href="docs/CHANGELOG.md"><b>New v2.5.3 Release!</b> — See what's changed</a></p>
### 🌟 Why KoalaSync?
@@ -29,6 +29,7 @@
- **Global Synchronization**: Synchronize Play, Pause, and Seeking on any website with a `<video>` tag.
- **Episode Auto-Sync**: Perfectly sync series binges. All peers wait until everyone has loaded the next episode before starting together.
- **Host Control & Co-Hosts**: Room hosts can lock playback control to trusted controllers while guests keep watching in sync.
- **Smart Matching**: Automatically highlights tabs containing matching video titles.
- **Dual Heartbeat Architecture**: Robust session tracking that prevents ghost rooms and stale connections.
- **Efficient Relay**: Minimal overhead WebSocket message forwarding.
@@ -58,7 +59,7 @@ The easiest and safest way to install KoalaSync is directly through the official
### 🌐 Localization & Translations
Both the official KoalaSync website and the **v2.0 Browser Extension** feature full dynamic localization:
Both the official KoalaSync website and the browser extension feature dynamic localization:
- **Available Languages**: Support is included for 15 languages: English (`en`), German (`de`), French (`fr`), Spanish (`es`), Portuguese (Brazil) (`pt-BR`), Russian (`ru`), Italian (`it`), Polish (`pl`), Turkish (`tr`), Dutch (`nl`), Japanese (`ja`), Korean (`ko`), Chinese (Simplified) (`zh`), Ukrainian (`uk`), and European Portuguese (`pt`).
- **Real-Time Extension Localization**: Inside the extension Settings panel, users can swap languages instantly. The entire interface, notifications, Empty States, and onboarding guides re-translate dynamically in real-time.
- **Contributing**: We welcome community translations for both the website and the extension! Please refer directly to the [TRANSLATION.md](docs/TRANSLATION.md) guide for step-by-step instructions on how to audit, refine, or add new languages.
@@ -86,16 +87,20 @@ npm run build:extension
The compiled artifacts will be available in the `dist/` directory.
#### For Self-Hosting (Docker)
Deploy your own private relay server using our official image:
For local development or a simple private relay, use the root Compose file:
```bash
# Pull the latest image
docker pull ghcr.io/shik3i/koalasync:latest
# Or use our example compose file
cp examples/docker-compose.caddy.example.yml docker-compose.yml
docker-compose up -d
cp server/.env.example server/.env
# Edit server/.env and set SERVER_SALT to a unique random value:
# openssl rand -base64 32
docker compose up -d --build
```
The server will be available at `ws://localhost:3000`. See [Docker network compose](examples/docker-compose.caddy.example.yml) or [Static IP compose](examples/docker-compose.ip.example.yml) for ready-to-use Docker Compose files.
The local relay will be available at `ws://localhost:3000`.
For production, use the official image and one of the ready-to-edit examples:
- [Docker network compose](examples/docker-compose.caddy.example.yml): for a Caddy or reverse-proxy network. This file does not publish `localhost:3000`; Caddy routes traffic to the container.
- [Static IP compose](examples/docker-compose.ip.example.yml): for a pre-existing Docker network with a fixed container IP.
In every real deployment, set a unique `SERVER_SALT`. Without it the relay still starts, but it warns that room-password hashes are using the public fallback salt from the repository.
To connect your extension to a self-hosted server, open the popup → **Room** tab → select **Custom Server** → enter your server's WebSocket URL (e.g., `ws://localhost:3000`).
@@ -131,8 +136,10 @@ gh attestation verify dist/koalasync-chrome.zip \
- **[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)**: Our community standards and expectations.
- **[HOW_IT_WORKS.md](docs/HOW_IT_WORKS.md)**: Step-by-step walkthrough of the complete user flow.
- **[ARCHITECTURE.md](docs/ARCHITECTURE.md)**: Deep-dive into the two-phase sync and heartbeat logic.
- **[PROTOCOL.md](docs/PROTOCOL.md)**: WebSocket protocol specification and event reference.
- **[ROADMAP.md](docs/ROADMAP.md)**: Planned features, backlog, and rejected ideas.
- **[SECURITY.md](SECURITY.md)**: Disclosure policy and security practices.
- **[AI_INIT.md](docs/AI_INIT.md)**: Maintainer and agent onboarding notes for safe code changes.
- **[Caddyfile.example](examples/Caddyfile.example)**: Production Caddy configuration for website and relay.
---
+7 -1
View File
@@ -44,7 +44,7 @@ The following components are within scope for security reports:
Instead, email the project maintainer privately:
**`koalasync_admin@koalamail.rocks`**
**`koalasync@koalastuff.net`**
Encrypt sensitive findings with our PGP key (available on request).
@@ -82,6 +82,12 @@ KoalaSync's security is grounded in its architecture:
If you find a way to bypass any of these protections, we want to know about it.
> [!NOTE]
> Some frequently-reported "issues" are **intentional and out of scope** for our
> threat model (ephemeral, account-less rooms of invited participants) — e.g. an
> unauthenticated `peerId` or non-constant-time room-password compare. Before
> reporting, please read **[`docs/KNOWN_LIMITATIONS.md`](docs/KNOWN_LIMITATIONS.md)**.
---
## Responsible Disclosure
+66
View File
@@ -0,0 +1,66 @@
# KoalaSync — Marketing Copy Kit
Ready-to-paste copy for product listings, launch pages, directory submissions, and anywhere else you keep re-typing the same pitch. Three lengths, one consistent message. Pick the one that fits the field limit.
---
## 1. One-Sentence Pitch
> KoalaSync is a privacy-first browser extension that synchronizes video playback across almost any website so you can watch with friends in real time — no accounts, no tracking, and your video never passes through anyone's server but the original site's.
**Shorter alternative** (for tight tagline fields):
> Private, universal watch parties on any website — no accounts, no tracking, no media proxying.
---
## 2. Three-Sentence Overview
> KoalaSync is a lightweight browser extension that keeps you and your friends perfectly in sync on YouTube, Netflix, Twitch, Prime Video, Jellyfin, Emby, and almost any other site with an HTML5 video player — press play once and everyone stays together. It's built privacy-first: no accounts, no telemetry, and the official relay server runs entirely in volatile RAM with zero persistence, so nothing about your sessions is ever stored. Open source under the MIT license and fully self-hostable with a single Docker command, KoalaSync is a transparent watch-party tool that works everywhere and respects your data sovereignty.
---
## 3. Full Description
### Watch together — on any site, on your terms.
Counting down "3, 2, 1, play" over voice chat doesn't scale past two people. KoalaSync fixes that with a tiny browser extension that synchronizes play, pause, and seeking across everyone in the room, on almost any website with a `<video>` element. Create a room, share a link, press play — that's it.
### What makes KoalaSync different
Most watch-party tools fall into one of two traps: they only work on a short allowlist of sites (site-specific extensions that need a separate build for every platform), or they route your video through their own player and servers. KoalaSync was built around three principles that break that mold.
**Universal by design.** If the site has an HTML5 `<video>` element, KoalaSync can usually sync it. YouTube, Netflix, Twitch, Prime Video, Disney+, Jellyfin, Emby, and countless niche sites work out of the box — no per-site integration to wait for, no extension swap when your friends want to switch services.
**Your video never touches our servers.** KoalaSync only relays tiny timing messages — play, pause, seek position, readiness — over a hand-rolled WebSocket protocol. The actual video keeps streaming directly from the original site to each viewer's browser. KoalaSync never proxies, transcodes, uploads, or redistributes a single frame, which also means there is no legal gray zone around redistribution.
**Privacy is the default, not an upgrade.** No accounts, no emails, no telemetry, no analytics, no behavior profiling. The official relay server runs entirely in volatile RAM and keeps zero persistent state — when the room closes, the data is gone. Pick a nickname or let KoalaSync generate one for you and you're in.
### Built for people who actually want to read the code
KoalaSync is MIT-licensed open source, built by a solo developer. Audit it, fork it, change it. The extension is dependency-free with a direct Socket.IO wire implementation — no opaque libraries, no framework bloat, no surprise third-party SDKs. Want full sovereignty? Self-host your own relay with a single Docker command and keep all watch-party coordination traffic inside your own infrastructure. The official public relay is there when you don't care, self-hosting is there when you do.
### Little touches you'll notice
- **Episode Auto-Sync** pauses the room when someone loads the next episode and resumes only when everyone is ready — no spoilers, no one left behind on the previous cliffhanger.
- **Smart Audio Compressor** tames the modern "whisper dialogue, deafening explosion" mix with one click. Three presets or full manual control over threshold, ratio, attack, and release.
- **One-click invite links** auto-configure the server and room for your friends — they just click the link and they're in. No fumbling with server URLs or room IDs.
- **Dual-heartbeat architecture** kills ghost rooms and stale connections before they desync your session.
- **15 languages** fully translated and switchable in real time from the settings panel — English, German, French, Spanish, Portuguese (Brazil + European), Russian, Italian, Polish, Turkish, Dutch, Japanese, Korean, Chinese, Ukrainian.
### Install and start in under a minute
Install KoalaSync from the Chrome Web Store or Firefox Add-ons, click "Create Room," share the invite link, and pick a video. The official relay is ready out of the box — no setup required unless you want to self-host.
- Website: https://sync.koalastuff.net
- GitHub: https://github.com/Shik3i/KoalaSync
---
## Bonus: Taglines (for hero headlines, social bios, meta descriptions)
- Watch together. Anywhere. Privately.
- The watch-party tool that works on every site and tracks none of them.
- Sync play, pause, and seek on any video — no accounts, no logs, no lock-in.
- Self-hostable, open-source watch parties for the post-"3, 2, 1, play" era.
- Press play once. Stay together anywhere.
+23 -15
View File
@@ -7,7 +7,7 @@ Welcome to the KoalaSync project. This file is the primary entry point for any d
> - **Font Stack**: Use a modern system font stack (e.g., -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif) to maintain a premium look without external dependencies. Prohibit the use of `@import` or `<link>` for external font services.
>
> [!IMPORTANT]
> **Caveman Communication Protocol**: To conserve context window and tokens, AI agents MUST communicate with the user in **caveman language** (broken, short German/English hybrid caveman style, e.g., "Ich Antigravity. Ich machen Git pull. Code fertig.") at all times. This rule is highly strict and must NOT be broken under any circumstances unless the user explicitly requests to drop it.
> **Communication Standard**: Be concise, concrete, and professional. Match the user's language when practical, explain risky changes before making them, and report exactly what was changed and verified. Do not use joke protocols or intentionally broken language in contributor-facing work.
---
@@ -23,26 +23,28 @@ KoalaSync is a specialized tool for **synchronized video playback** across multi
- `website/`: **Landing Page** & Invitation Bridge (Marketing, Tutorials, and Downloads).
- **`build.cjs`**: Zero-dependency static site compiler. Translates `template.html` + `locales/*.json``www/`. Also minifies CSS/JS automatically.
- **`www/` is auto-generated**: Never edit files in `www/` directly. Always edit source files (`template.html`, `style.css`, `app.js`, `lang-init.js`, `locales/*.json`) and run `node website/build.cjs` to regenerate. CSS/JS are output as `.min.*` files — a built-in cleanup step removes stale artifacts on each build.
- `shared/`: **Single Source of Truth** for protocol constants and event names.
- `shared/`: **Single Source of Truth** for protocol constants, event names, blacklist data, and generated usernames.
- `scripts/`: Development utilities (e.g., `build-extension.cjs`).
- `docker-compose.yml`: Root-level orchestration for the relay server.
> [!IMPORTANT]
> **Single Source of Truth**: `shared/constants.js` and `shared/blacklist.js` are the master files. They must be synchronized to the `extension/shared/` directory using `node scripts/build-extension.cjs`.
> **Single Source of Truth**: `shared/constants.js`, `shared/blacklist.js`, and `shared/names.js` are the master shared files. They must be synchronized to the `extension/shared/` directory using `node scripts/build-extension.cjs`.
> - **Extension Modules** (`background.js`, `popup.js`) import directly from `./shared/constants.js`.
> - **Content Scripts** (`content.js`) use a **marker-injected synchronous copy** of the constants. The build script automatically replaces the marked blocks — no manual mirroring needed.
## 3. Mandatory Reading
Before touching any code, you MUST read the following documents in order:
1. [ARCHITECTURE.md](ARCHITECTURE.md) Detailed communication flows, Dual Heartbeat, and two-phase sync protocol.
2. [extension/README.md](../extension/README.md) Extension components, tab structure, and loading process.
3. [SYNC_GUIDE.md](SYNC_GUIDE.md) Protocol constants and synchronization requirements.
1. [../README.md](../README.md) User, developer, and self-hosting overview.
2. [README.md](README.md) Documentation map by role.
3. [ARCHITECTURE.md](ARCHITECTURE.md) Detailed communication flows, Dual Heartbeat, and two-phase sync protocol.
4. [../extension/README.md](../extension/README.md) Extension components, tab structure, and loading process.
5. [SYNC_GUIDE.md](SYNC_GUIDE.md) Protocol constants and synchronization requirements.
## 4. The "Vanilla JS Mirror" Pattern
To avoid boot-time race conditions in Manifest V3 without a bundler, the following architectural trade-off is enforced:
- **Synchronous Execution**: `content.js` MUST execute synchronously to catch early media events.
- **Automated Injection**: The build script (`node scripts/build-extension.cjs`) automatically injects `EVENTS` and `HEARTBEAT_INTERVAL` into `content.js` using marker-based replacement (see `../scripts/README.md` for marker details).
- **Maintenance**: After modifying `shared/constants.js`, simply run the build script. No manual mirroring is required.
- **Automated Injection**: The build script (`node scripts/build-extension.cjs`) automatically injects `EVENTS`, `HEARTBEAT_INTERVAL`, and episode utility functions into `content.js` using marker-based replacement (see `../scripts/README.md` for marker details).
- **Maintenance**: After modifying any root `shared/` file, run the build script. No manual mirroring is required.
## 5. File Responsibility Map
@@ -52,13 +54,16 @@ To avoid boot-time race conditions in Manifest V3 without a bundler, the followi
| `content.js` | Video element detection, media control, event origin detection (loop prevention) |
| `popup.js` | UI rendering, user input handling, peer display, invitation link generation |
| `bridge.js` | Landing page ↔ extension communication for invitation join flow |
| `episode-utils.js` | Shared episode parsing, imported by background and injected into content |
| `title-privacy.js` | Tab/media title privacy normalization and sanitization |
| `page-api-seek-overrides.js` | Page-level seek bridge for site-specific player APIs |
| `server/index.js` | Room management, message relay, rate limiting, authentication, peer lifecycle |
## 6. Design Guidelines
The popup UI follows a strict design system. Do not modify these variables or the layout structure without explicit approval.
- **Font**: System font stack. **MANDATORY**: No external CDNs or Google Fonts to ensure 100% privacy.
- **Popup Width**: Fixed at `320px`.
- **Tab Structure**: Must maintain the **Room**, **Sync**, **Settings**, and **Dev** tabs.
- **Tab Structure**: Must maintain the visible **Room**, **Sync**, **Settings**, and **Status** tabs. The hidden **Dev** tab is available only for developer diagnostics.
- **CSS Variables**:
| Variable | Value | Purpose |
| :--- | :--- | :--- |
@@ -72,6 +77,7 @@ The popup UI follows a strict design system. Do not modify these variables or th
The following features are critical and must not be removed or fundamentally altered:
- **Two-Phase Force Sync**: The `Prepare``ACK``Execute` flow ensures all peers are buffered before playback resumes.
- **Episode Auto-Sync**: Ensures series binges stay perfectly synced. A lobby initiates during title transitions, freezing peers until everyone is ready.
- **Host Control Mode**: In `host-only` rooms, only the host and promoted controllers may initiate room-moving playback events. Both client-side UX and server-side gates must remain intact.
- **Dual Heartbeat**:
- **Background Heartbeat (1m)**: Ensures session persistence even without a video element.
- **Content Heartbeat (15s)**: Transmits current video metadata (time, title).
@@ -80,8 +86,9 @@ The following features are critical and must not be removed or fundamentally alt
- **Platform Specifics**: Specialized click-logic for YouTube (`.ytp-play-button`) and Twitch.
- **pollSeekReady()**: Polling mechanism that checks `video.readyState` before acknowledging sync.
- **SW Keep-alive**: Use of `chrome.alarms` to prevent the Manifest V3 Service Worker from suspending.
- **Diagnostics**: The "Dev" tab provides real-time access to the underlying `<video>` state for troubleshooting.
- **Diagnostics**: The visible **Status** tab provides real-time access to connection state, ping, logs, history, and underlying `<video>` state for troubleshooting.
- **Persistence**: `peerId` and `username` must be stored to remain stable across sessions.
- **Title Privacy**: Do not accidentally reintroduce tab/media title sharing when users disabled or reduced it.
- **Room ID Format**: Room IDs are restricted to `[a-zA-Z0-9-]` only (alphanumeric + hyphens). This is enforced server-side.
## 8. Technical Constraints
@@ -89,7 +96,8 @@ The following features are critical and must not be removed or fundamentally alt
- **Manual Protocol**: `background.js` implements a subset of the Socket.IO wire protocol natively.
- **Server Transport**: Restricted to `websocket` only. Polling is disabled.
- **Docker Context**: The Docker build must run from the **Repo Root**.
- **Manifest Settings**: `run_at` must remain `document_idle`, and `all_frames` must remain `false`.
- **Content Script Scope**: `bridge.js` is the only static content script in the manifest and runs on `https://sync.koalastuff.net/*` at `document_start`. Video control scripts are injected only into the selected tab via `chrome.scripting`; do not add broad persistent video content-script matches.
- **Self-Hosting Salt**: Production/self-hosted relay examples must include a unique `SERVER_SALT` so room-password hashes are not derived with the public fallback salt.
- **Strict Backward & Forward Compatibility (Store Delay Rule)**: Browser extensions are distributed through stores (e.g., Chrome Web Store, Firefox Add-ons) which can take up to 2 weeks to approve updates. Therefore, the server MUST NOT reject older extension clients unless a critical protocol version bump is explicitly authorized, and new extension versions MUST remain fully operational when connected to older servers (e.g., by silently falling back if a new feature is not supported). This is a core architectural requirement.
## 9. Security & Deployment
@@ -117,7 +125,7 @@ Before starting any task, committing, or pushing, you **MUST** run `git pull --r
> **AI AGENTS MUST FOLLOW THIS EXACT SEQUENCE WHEN RELEASING A NEW VERSION OR TAGGING.**
>
> **🚫 NO MANUAL VERSION BUMPING**: You MUST **NEVER** manually modify the version strings in `package.json`, `extension/manifest.base.json`, or `website/version.json`. The GitHub Actions CI pipeline automatically extracts the version from the git tag (e.g. `v2.0.5` -> `2.0.5`), injects it into all target files, and commits the updates back to `main` with `[skip ci]`. Manual bumps will cause merge conflicts and build failures.
> - **Website Versioning**: **NEVER** manually modify the version fallback strings in `website/index.html`. The website dynamically fetches the latest version and release date from `website/version.json` at runtime using `website/app.js`. Manual bumps in the HTML file are completely redundant and should be avoided.
> - **Website Versioning**: **NEVER** manually modify generated version strings in `website/www/`. The website build injects version data from `website/version.json` into generated output.
1. **MANDATORY SYNTAX & LINT CHECKS**: Before staging, committing, or pushing any changes, you **MUST** run both checks on every modified JavaScript file:
- **Syntax Validation**: Run `node -c` on every single modified JavaScript file (e.g., `node -c extension/background.js` and `node -c extension/content.js`). **NEVER** commit or push code that fails this check.
- **ESLint Validation**: Run `npm run lint` (or `npx eslint .`). The output must show **zero errors and zero warnings**. ESLint is configured to catch undefined variables, unused vars, unreachable code, and other semantic issues. **NEVER** commit or push code that fails this check.
@@ -143,16 +151,16 @@ Before starting any task, committing, or pushing, you **MUST** run `git pull --r
### Making Website Changes
1. Edit source files in `website/` (`template.html`, `style.css`, `app.js`, `lang-init.js`, or `locales/*.json`).
2. Run the compiler: `node website/build.cjs`. This generates the multilingual pages in `www/` and minifies CSS/JS.
3. Verify the output: `node --check website/www/app.js && node --check website/www/lang-init.js`.
3. Verify the output: `node --check website/www/app.min.js && node --check website/www/lang-init.min.js`.
4. Test locally: `npx serve website/www` or `python3 -m http.server 8080 -d website/www`.
5. Commit both source changes and the updated `www/` output.
### Testing Locally
1. Run the build script: `node scripts/build-extension.cjs`.
2. Load `dist/chrome/` as an "Unpacked Extension" in Chrome (or `dist/firefox/` in Firefox).
3. Start the server from the root: `docker-compose up --build`.
3. Start the server from the root: `docker compose up --build`.
4. Use **different browser profiles** or vendors to test multi-peer logic.
5. Use the **Dev tab** to verify real-time video element metadata.
5. Use the **Status tab** to verify real-time connection state, logs, and video element metadata.
### Locking Old Versions
1. Update `MIN_VERSION` in the server's `.env` file to the minimum acceptable version.
+47
View File
@@ -4,6 +4,53 @@ All notable changes to the KoalaSync browser extension and relay server.
---
## Unreleased
---
## [v2.5.3] — 2026-07-02
### Fixed
- **Extension: Disney+ force sync and seeking** — Fixed force sync on Disney+ failing or jumping to wrong positions. The extension now relies solely on accurate player-API time and fails cleanly when it isn't available yet, instead of falling back to unreliable raw video data.
- **Extension: Force-sync accuracy** — When syncing to the group, peers whose current position isn't known yet (e.g. a Disney+ peer that just loaded) no longer pull the sync target toward the start of the video.
- **Extension: Disney+ Host Control Mode** — Regular Disney+ content is no longer misclassified as a live stream, which had silently disabled the desync dialog and snap-back for guests in host-controlled rooms. YouTube and Twitch live detection is unchanged.
- **Extension: Disney+ episode auto-sync** — Episode transitions and the "waiting for peers" lobby flow now work reliably on Disney+ again.
---
## [v2.5.2] — 2026-07-02
### Added
- **Extension: Privacy title controls** - Advanced users can now disable sending browser tab titles separately from media titles. Media titles can still be sent in full, reduced to detected episode identifiers such as `S01E04`, or hidden entirely. Defaults remain full titles for backwards compatibility.
- **Relay: Cleaner restart handling** — Connected clients are now disconnected explicitly during relay shutdown so reconnects recover more predictably.
- **Relay: Stronger abuse protection** — Rapid room-leave spam is now rate-limited.
- **Extension: Hidden remote seek diagnostics** — KoalaDev can use the hidden Dev tab to simulate remote seeks and inspect precise native/page-API timing while debugging playback integrations.
### Changed
- **Extension: Shared page-API seek bridge** — Netflix and Disney+ now use a common page-level seek bridge so private player APIs can be invoked from the page context while the default HTML5 path stays unchanged.
- **Build: Release build timestamp** — Extension builds now inject a build timestamp into the hidden Dev tab for easier local package verification.
### Fixed
- **Extension: Disney+ precise sync** — Disney+ now reads time and seeks through the real page media-player API, and the temporary DOM timeline/button scraping fallback has been removed.
- **Extension: Netflix seek reliability** — Netflix seeking keeps using the page player API with a safer session lookup path.
- **Extension: Tab-title counter cleanup** — Leading browser notification counters such as `(14)` or `[7]` are removed from shared tab titles and matching logic without changing the existing privacy controls.
- **Extension: Tab navigation reinjection** — Reinjecting the content script after selected-tab navigation now uses the same page-API-aware injection path.
---
## [v2.5.0] — 2026-06-29
### Added
- **Extension + Relay: Host Control Mode** — Room owners can now switch a room between open playback control and host-controlled playback. In host-only mode, guests stay synchronized but their local play, pause, and seek actions are not rebroadcast to the room.
- **Backward-compatible Host Control rollout** — The extension only shows Host Control when the connected relay supports it, so users on older self-hosted servers do not see controls that cannot work yet.
- **Extension: Clear host and guest states** — The popup shows the current control mode, host status, peer roles, and localized guest guidance so participants understand when playback is controlled by the host.
- **Website: FAQ clarification for streaming access** — The landing page and FAQ structured data now state clearly that KoalaSync does not stream, host, share, or bypass access to video content. Every participant watches locally and needs their own access to services such as Netflix.
### Changed
- **Playback sync now follows the room's control setting** — When Host Control is enabled, only the host can drive room-wide playback changes; guests can still watch in sync without accidentally changing playback for everyone.
---
## [v2.4.6] — 2026-06-23
### Fixed
+2 -2
View File
@@ -156,7 +156,7 @@ While in a room, two heartbeats keep the session alive:
| Heartbeat | Interval | Source | Purpose |
|:----------|:---------|:-------|:--------|
| **Background** | 30 seconds | `background.js` | While connected, signals "I'm still connected" and triggers automatic reconnect (500ms base, max 5s). No heartbeats fire when idle (lazy connect). |
| **Content** | 15 seconds | `content.js` | Sends video metadata: `currentTime`, `mediaTitle`, `playbackState`, `volume`, `muted` |
| **Content** | 15 seconds | `content.js` | Sends video metadata: `currentTime`, privacy-filtered `mediaTitle`, `playbackState`, `volume`, `muted` |
- **Server Reaper**: Every 2 minutes, the server checks for peers with no activity for 5+ minutes and disconnects them ("dead peer pruning").
- **Room Cleanup**: Empty rooms are deleted immediately. Inactive rooms are pruned after 2 hours.
@@ -184,7 +184,7 @@ When a user clicks **"Leave"** or closes their browser:
When watching a series and an episode ends:
1. `content.js` monitors the [Media Session API](https://developer.mozilla.org/en-US/docs/Web/API/Media_Session_API) for title changes.
2. When a new title is detected, the peer broadcasts `EPISODE_LOBBY` with the expected new title.
2. When a new title is detected, the peer broadcasts `EPISODE_LOBBY` with the expected title after applying the local media title privacy setting. In episode-only mode this is an identifier such as `S01E04`; when media titles are not sent, the client does not create a new episode lobby.
3. All peers' videos freeze. The UI shows an "Episode Lobby" card with peer readiness status.
4. Each peer's `content.js` polls for the new title to appear in the `<video>` element's metadata.
5. Once a peer detects the matching title, they send `EPISODE_READY`.
+103
View File
@@ -0,0 +1,103 @@
# Known Limitations & Accepted Non-Issues (NOFIX)
This file records design decisions that are **intentional** and **out of scope** for
KoalaSync's threat model. They are frequently re-discovered by reviewers, linters, and
AI agents and re-reported as "bugs" or "vulnerabilities." They are not. Please read the
threat model below before flagging any of them.
> **For reviewers & agents:** Do **not** re-report the NOFIX items below. They are
> deliberate. If you think the *threat model itself* should change (e.g. KoalaSync
> starts handling accounts, payments, or sensitive data), raise **that** discussion
> first — these items only stand because the threat model holds.
---
## Threat model
KoalaSync is a **privacy-preserving watch-party sync tool**, not a security product.
- **Rooms are ephemeral.** They exist for a few hours and are auto-reaped. There are no
accounts, no persistent storage, no money, and no sensitive content on the relay.
- **The relay is a dumb, stateless message bus.** It forwards play/pause/seek between
peers who *chose* to watch together and joined via an invite link shared out-of-band.
- **Participants are invited.** Anyone in a room was let in. The social contract is
"we're watching a video together," not "mutually distrusting parties."
### What we DO defend against
- **Accidental disruption** — the entire point of Host Control Mode.
- **Spam / DoS** that degrades the relay for everyone — rate limits, 4 KB payload cap,
server-side gating, lazy-cached health responses.
- **Resource exhaustion / memory leaks** — bounded maps, periodic cleanup, room/peer reaping.
- **Crashes from malformed input** — strict sanitization and clamping of every field.
- **Genuine boundary breaches** — admin-metrics auth (constant-time), CORS, WSS upgrade,
invite-hash isolation, strict CSP. Reports here are very welcome (see `SECURITY.md`).
### What we explicitly DO NOT defend against
A **determined participant who modifies their own client to misbehave inside a room they
were invited to.** The worst they achieve is sending playback commands or seizing the
"host" role in a temporary room they could already disrupt by other means. That is a
**social** problem, solved socially: kick them, or start a new room. Engineering real
identity/auth to prevent it would destroy the account-less, frictionless, privacy-first
design — a bad trade for an ad-hoc movie night.
---
## NOFIX entries
### NOFIX-1 — `peerId` is unauthenticated; a crafted client can impersonate or seize the host
**Flag:** `peerId` is client-asserted and broadcast to every peer (in `ROOM_DATA` /
`PEER_STATUS`). A modified client can join with the host's `peerId`, dedupe-kick the real
host, and become host — controlling or locking `host-only` mode.
**Why NOFIX:** Requires a *modified client* + an *invited* participant + a `peerId` that is
only meaningful inside that one *temporary* room. The payoff is sending play/pause or
locking a room the attacker is already in — pure trolling, instantly reversible (kick /
new room). Cryptographic per-user identity is wildly disproportionate for an ad-hoc,
account-less, ephemeral watch party. **Out of threat model.**
Do **not** "fix" with accounts, signed peerIds, or per-user tokens — that breaks the
core design.
### NOFIX-2 — Room-password comparison is not constant-time
**Flag:** room password hashes are compared with `!==` (`server/index.js`), so the compare
is theoretically timing-attackable.
**Why NOFIX:** The compared value is an **HMAC-SHA256 hash that never leaves the server**
an attacker cannot observe it to mount a timing attack. Even a hypothetical success only
lets someone join a *temporary* room to send playback commands. Not worth defending.
(The admin-metrics bearer token — a real boundary — **does** use `crypto.timingSafeEqual`.
That is the line we actually guard.)
### NOFIX-3 — `OFFICIAL_SERVER_TOKEN` is public in the repo
**Flag:** the connection token in `shared/constants.js` is committed, so anyone can connect.
**Why NOFIX:** It is a **coarse filter** to keep random scanners off the relay, **not
authentication**. The relay is a public message bus by design; rate limits and per-room
behavior are the real protections.
### NOFIX-4 — Room IDs are enumerable via `GET_ROOMS`
**Flag:** any connected client can list all room IDs (and whether each has a password).
**Why NOFIX:** This is the intended **"Public Rooms"** feature. Rooms wanting privacy set a
password; listing the IDs of password-less rooms only lets someone join a watch party —
the same as being handed the invite link.
### NOFIX-5 — A pause/seek can only be reverted, not prevented
**Flag:** in `host-only` mode a guest's pause still fires locally before the extension can
react, so there is a brief flicker before snap-back.
**Why NOFIX:** A content script cannot intercept a `<video>` event before the element
acts. Reacting (snap-back) is the only option and is by design; the ~½s flicker is
acceptable. Not a bug.
---
## Not NOFIX — just deferred (may be revisited)
These are *not* accepted-forever; they are scoped out of v1 and tracked separately
(see the host-control-mode design docs in `docs/`):
- **Host grace on a long disconnect (EC-10).** A brief reconnect/second-tab keeps the host
(handled), but a long real disconnect still falls back to `everyone`. A ~30s host-reserve
grace could be added later.
- **Intent-classifier / snap-back tuning.** Thresholds are first-pass; real-device testing
may adjust them.
+1 -1
View File
@@ -8,7 +8,7 @@ KoalaSync is designed with a **Security-First & Volatile** architecture. This me
## 1. Data Processing (In-Memory Only)
KoalaSync does not use a database. All active session data exists only in the server's RAM and is purged immediately when no longer needed.
- **Session Data**: To synchronize playback, the server must temporarily hold your `peerId`, `username`, and the `title` of the video you are watching. Additionally, playback metadata (`mediaTitle`, `playbackState`, `currentTime`, `volume`, `muted`) is held per peer for the duration of the session. All of this is deleted as soon as you leave the room.
- **Session Data**: To synchronize playback, the server must temporarily hold your `peerId` and `username`. By default, KoalaSync also shares the selected tab title and media title with the room so peers can identify matching videos and coordinate episode transitions. Privacy Settings let you disable sending the tab title separately, and choose whether media titles are sent in full, reduced to a detected episode identifier (for example `S01E04`), or not sent. Playback metadata (`playbackState`, `currentTime`, `volume`, `muted`) is held per peer for the duration of the session. All of this is deleted as soon as you leave the room.
- **Room Passwords**: If you set a room password, it is stored only as an in-memory **keyed SHA-256 HMAC hash**. The server receives the plaintext password only during join validation, never stores it, and keeps only the hash for the short room lifetime.
- **Routing Maps**: The server maintains ephemeral lookup tables (`socketToRoom`, `peerToSocket`) to route messages between peers. These contain only transport identifiers and are purged on disconnect.
+369
View File
@@ -0,0 +1,369 @@
# WebSocket Protocol Reference
This document describes the relay behavior implemented by `server/index.js` and
the event names defined in `shared/constants.js`.
## Transport
- The relay uses Socket.IO v4 events over WebSocket.
- Long-polling is disabled (`transports: ['websocket']`, `allowUpgrades: false`).
- Messages are Socket.IO event packets whose payload is an event name plus an
object payload.
- The relay caps incoming Socket.IO message size at 4 KB.
## Connection Handshake
The Socket.IO handshake must include:
- `token`: must match `OFFICIAL_SERVER_TOKEN`.
- `version`: optional app version. If present, it must be a valid semver-like
string and not older than `MIN_VERSION` (default `1.0.0`).
If the token is invalid, the relay emits `error` and disconnects the socket.
If `version` is invalid or too old, the relay emits `error` and disconnects the
socket.
After the socket is connected, `join_room` must include `protocolVersion`.
It must equal `PROTOCOL_VERSION` exactly. A mismatch emits `error` and rejects
the join attempt; it does not currently disconnect the socket.
## Room Join
### `join_room` (client -> server)
Payload:
```json
{
"roomId": "string, sanitized to [A-Za-z0-9-], max 64",
"peerId": "string, max 16",
"username": "string, max 30",
"password": "string, max 128, optional",
"tabTitle": "string, max 100, optional",
"mediaTitle": "string, max 100, optional",
"protocolVersion": "string, max 16"
}
```
Behavior:
- Creates the room if it does not exist and capacity allows it.
- The first peer becomes `hostPeerId`.
- Rooms may have an optional password hash.
- Joining with a duplicate `peerId` disconnects the previous socket for that peer.
- Joining the same room with the same socket and peer is ignored as a no-op.
- Switching rooms removes the socket from the old room first.
On success, the joining socket receives `room_data`.
Other room members receive `peer_status` with `status: "joined"`.
### `room_data` (server -> client)
Payload:
```json
{
"roomId": "string",
"peers": ["peer state objects"],
"activeLobby": "object or null",
"hostPeerId": "string or null",
"controlMode": "everyone | host-only",
"controllers": ["peerId"],
"capabilities": ["host-control", "co-host"]
}
```
`room_data` is sent to the joining socket. It is not the general broadcast used
for every later room update.
## Room Leave
### `leave_room` (client -> server)
Payload: none.
Behavior:
- Rate-limited to 10 events per socket per minute.
- If the socket is mapped to a room, the relay removes it from that room.
- Remaining room members receive `peer_status` with `status: "left"` when the
peer is no longer represented by another socket.
- Empty rooms are deleted.
- If the host leaves and peers remain, the relay assigns the next peer as host,
falls back to `controlMode: "everyone"`, resets controllers to the new host,
and broadcasts `control_mode`.
Exceeding the `leave_room` limit is logged and the socket is disconnected.
## Relayed Room Events
The relay accepts and sanitizes these events, then emits the same event to other
peers in the room:
- `play`
- `pause`
- `seek`
- `peer_status`
- `force_sync_prepare`
- `force_sync_ack`
- `force_sync_execute`
- `episode_lobby`
- `episode_ready`
- `episode_lobby_cancel`
Relayed payload fields are sanitized and may include:
```json
{
"senderId": "peerId of sender",
"seq": "number",
"currentTime": "number 0..86400 or null",
"targetTime": "number 0..86400",
"playbackState": "playing | paused",
"username": "string, max 30",
"tabTitle": "string, max 100 or null",
"mediaTitle": "string, max 100 or null",
"volume": "number 0..1",
"muted": "boolean",
"desynced": "boolean",
"peerId": "sender peerId",
"status": "string, max 16",
"expectedTitle": "string, max 100",
"title": "string, max 100",
"actionTimestamp": "number"
}
```
Undefined fields are removed before relay. Raw client payloads are not forwarded.
## Media Control
### `play`, `pause`, `seek`
These are room-moving actions. In `host-only` mode, the relay drops them unless
the sender is a controller.
Common payload fields:
- `currentTime` for `play`/`pause`.
- `targetTime` for `seek`.
- `seq` and `actionTimestamp` when the extension needs stale-command or ACK
handling.
The content script applies additional client-side filtering for noisy native
player events before it sends these events.
## Peer Status
### `peer_status`
Used for heartbeats and peer state updates. The extension sends it every
`HEARTBEAT_INTERVAL` while syncing is active.
Typical fields:
- `peerId`
- `username`
- `tabTitle`
- `mediaTitle`
- `playbackState`
- `currentTime`
- `volume`
- `muted`
- `desynced`
- `status`
The relay stores sanitized peer state and relays the sanitized update to other
peers.
## Force Sync
Force sync coordination is implemented primarily in the extension. The relay
sanitizes and relays the events.
### `force_sync_prepare`
Payload includes `targetTime`. The initiator waits for ACKs or for
`FORCE_SYNC_TIMEOUT` before sending `force_sync_execute`.
In `host-only` mode, only controllers may initiate it.
### `force_sync_ack`
The extension sends ACKs with peer identity and sequence data. The relay relays
them with the same sanitized relay envelope as other room events, including
`senderId`.
### `force_sync_execute`
Payload includes `targetTime`. In `host-only` mode, only controllers may send it.
The relay also allows a matching initiator's execute event after that initiator
started the prepare step, even if their controller state changed before execute.
## Episode Lobby
Episode lobby coordination is implemented primarily in the extension. The relay
tracks enough state to include `activeLobby` in `room_data` for later joiners.
### `episode_lobby`
Payload uses `expectedTitle`. The relay creates `activeLobby` when this field is
present and no lobby is already active.
In `host-only` mode, only controllers may initiate it.
### `episode_ready`
Payload may include `title`. The relay adds the sender to the active lobby's
ready list when a lobby exists.
### `episode_lobby_cancel`
Clears the active lobby and is relayed to peers. In `host-only` mode, only
controllers may initiate it.
## Host Control Mode
### `set_control_mode` (client -> server)
Payload:
```json
{
"controlMode": "everyone | host-only"
}
```
Only the room host may change the mode. Non-host attempts are ignored and the
sender receives the current `control_mode` snapshot.
Mode changes are debounced per room with `CONTROL_MODE_MIN_INTERVAL_MS` (500 ms).
### `set_peer_role` (client -> server)
Payload:
```json
{
"peerId": "string, max 16",
"controller": "boolean"
}
```
Only the room host may promote or demote controllers. The host cannot demote
themself. Role changes use the same 500 ms per-room debounce as mode changes.
### `control_mode` (server -> client)
Payload:
```json
{
"controlMode": "everyone | host-only",
"hostPeerId": "string or null",
"controllers": ["peerId"]
}
```
Sent when mode or controller state changes, when host migration changes room
authority, and when unauthorized role/mode attempts need to resync the sender.
## Room List
### `get_rooms` (client -> server)
Payload: none.
No admin token is required for this Socket.IO event.
Limits:
- Counts against the per-socket event limit.
- Also has a 10 second per-socket cooldown.
### `room_list` (server -> client)
Payload:
```json
{
"rooms": [
{
"id": "room id",
"peerCount": 2,
"hasPassword": false
}
]
}
```
## Ping, Pong, and ACK
### `ping`
Payload:
```json
{
"t": 1234567890,
"target": "peerId, optional"
}
```
If `target` is omitted, the relay responds to the sender with `pong`.
If `target` is another peer in the same room, the relay sends `ping` to that peer
with `{ "t": ..., "sender": "senderPeerId" }`.
### `pong`
Payload:
```json
{
"t": 1234567890,
"target": "peerId, optional"
}
```
If `target` is a peer in the same room, the relay sends `pong` to that peer with
`{ "t": ... }`.
### `event_ack`
Client payload:
```json
{
"targetId": "peerId",
"actionTimestamp": 1234567890
}
```
If sender and target are still in the same room, the relay emits:
```json
{
"senderId": "sender peerId",
"actionTimestamp": 1234567890
}
```
## Rate Limits
- Connections: 10 per IP per minute; excess connections are disconnected.
- Relayed/events: 50 per socket per 10 seconds; excess disconnects the socket.
- `get_rooms`: 10 second cooldown per socket plus the event limit.
- `leave_room`: 10 per socket per minute; excess disconnects the socket.
- Invalid room passwords: tracked per IP and room. Five recent failures block
more password attempts for that room until the failure window ages out.
- HTTP health and admin-metrics endpoints have their own rate limits outside this
Socket.IO protocol.
## Capabilities
`room_data.capabilities` advertises server-backed features:
- `host-control`
- `co-host`
Clients should treat a missing or unknown capabilities list as unsupported.
+37 -5
View File
@@ -1,8 +1,40 @@
# Technical Documentation
This directory contains deep-dives into the KoalaSync protocol and architecture.
This directory contains deep-dives into the KoalaSync protocol, architecture, roadmap, and operational guidelines.
- [HOW_IT_WORKS.md](HOW_IT_WORKS.md): Step-by-step walkthrough of every user flow, from room creation to synchronized playback. Ideal for store reviewers and manual testers.
- [ARCHITECTURE.md](ARCHITECTURE.md): Communication flows, Dual Heartbeat, and Sync logic.
- [SYNC_GUIDE.md](SYNC_GUIDE.md): Protocol constants and sync requirements.
- [TRANSLATION.md](TRANSLATION.md): Translation and localization guide for the extension and website.
## Start Here by Role
- **Users and reviewers**: Start with [HOW_IT_WORKS.md](HOW_IT_WORKS.md), then [PRIVACY.md](PRIVACY.md) and [TESTED_SERVICES.md](TESTED_SERVICES.md).
- **Self-hosters**: Start with the root [README.md](../README.md), then [devops.md](devops.md), [PROTOCOL.md](PROTOCOL.md), and the examples in `../examples/`.
- **Contributors**: Start with [../CONTRIBUTING.md](../CONTRIBUTING.md), then [ARCHITECTURE.md](ARCHITECTURE.md), [SYNC_GUIDE.md](SYNC_GUIDE.md), and the README for the subdirectory you are editing.
- **AI agents**: Start with [AI_INIT.md](AI_INIT.md), then read the relevant subdirectory README before changing files.
- **Security reviewers**: Start with [SECURITY.md](../SECURITY.md), [KNOWN_LIMITATIONS.md](KNOWN_LIMITATIONS.md), [PRIVACY.md](PRIVACY.md), and [PROTOCOL.md](PROTOCOL.md).
## 🏗️ Core Architecture & Design
- **[ARCHITECTURE.md](ARCHITECTURE.md)**: Overview of the communication flows, Dual Heartbeat architecture, and synchronization logic.
- **[HOW_IT_WORKS.md](HOW_IT_WORKS.md)**: Step-by-step walkthrough of every user flow, from room creation to synchronized playback. Ideal for store reviewers and manual testers.
- **[host-control-mode.md](host-control-mode.md)**: Design, requirements, and edge cases of the Host Control feature.
- **[AI_INIT.md](AI_INIT.md)**: Maintainer and AI-agent onboarding: non-negotiables, workflow order, and safety checks.
## 📡 Protocol & Synchronization
- **[PROTOCOL.md](PROTOCOL.md)**: Low-level message format and payload descriptions for the KoalaSync sync protocol.
- **[SYNC_GUIDE.md](SYNC_GUIDE.md)**: Guide on keeping protocol constants synchronized across the workspace.
## 📋 Compatibility, Roadmap & Contribution
- **[TESTED_SERVICES.md](TESTED_SERVICES.md)**: Status of compatibility with major streaming services and contribution guidelines for testing new platforms.
- **[KNOWN_LIMITATIONS.md](KNOWN_LIMITATIONS.md)**: Threat model and accepted design limitations (NOFIX entries) for security audits.
- **[PRIVACY.md](PRIVACY.md)**: Privacy model and data-handling policy for users, reviewers, and contributors.
- **[ROADMAP.md](ROADMAP.md)**: Planned features, backlog items, and rejected proposals.
- **[TRANSLATION.md](TRANSLATION.md)**: Guide for native speakers to contribute and audit dynamic extension/website translations.
## 🚀 DevOps & Releases
- **[devops.md](devops.md)**: Guide on the automated tag-based release pipeline.
- **[CHANGELOG.md](CHANGELOG.md)**: Detailed history of releases and changes.
---
*For high-level project information and developer setup instructions, refer to the root [README.md](../README.md).*
+16 -10
View File
@@ -30,15 +30,6 @@
*Prioritized for upcoming phases.*
### 1. Split large JavaScript files into smaller modules
- **Priority:** P1
- **Category:** Maintainability / AI Context Optimization
- **Background:** Core files like `background.js` and `popup.js` have grown large and exceed 800 lines. This makes manual debugging harder and wastes context window space for AI models.
- **Planned solution:**
- Structurally split logic into separate focused modules (e.g., UI Renderer, Message Router, Storage Manager, Socket Client).
- Use ES modules for clean separation and better reusability.
### 2. Invite link with target URL for auto-redirect
- **Priority:** P2
@@ -69,6 +60,22 @@
- **Legal/moderation:** Unclear what moderation requirements would apply if users can exchange chat messages. Could be relevant depending on jurisdiction.
- **Status:** Under evaluation, may come later.
### Cross-frame video detection and control
- **Priority:** P3
- **Category:** Compatibility / Embedded Players
- **Background:** KoalaSync currently injects on demand into the selected tab's top frame. This works for normal top-frame players, including current Emby/Jellyfin usage, but does not cover cases where the real `<video>` lives inside a cross-origin iframe or an `about:blank`/`srcdoc` player frame.
- **Possible approach:** Add an opt-in frame bridge where child frames announce detected videos to the top frame, and the top frame routes remote play/pause/seek commands to the active child video.
- **Status:** Future compatibility work, not needed for current Emby behavior.
### Local extension E2E smoke tests
- **Priority:** P2
- **Category:** Testing / Release Confidence
- **Background:** The release verification covers unit tests, server integration, syntax, lint, audits, and builds, but it does not currently run a real browser extension flow. A small local E2E smoke suite would catch regressions in content-script injection, tab navigation reinjection, remote seek handling, and iframe player support.
- **Possible approach:** Add a separate local-only Playwright smoke command that loads the unpacked extension, opens two controlled video pages, and verifies play/pause/seek through the actual extension path. Keep it outside `npm run verify` until it is stable enough for CI.
- **Status:** Backlog, recommended before larger content-script or frame-bridge changes.
---
## ❌ Rejected
@@ -79,4 +86,3 @@
|---|---|
| *(none yet)* | |
+7 -5
View File
@@ -10,8 +10,9 @@ You MUST run the build script in any of the following scenarios:
1. **After a fresh `git clone` or `git pull`** (as the synced files are ignored by git).
2. **After modifying** `shared/constants.js`.
3. **After modifying** `shared/blacklist.js`.
4. **Before committing** changes to the repository if any protocol-related files were touched.
5. **Before deploying** the server or releasing the extension.
4. **After modifying** `shared/names.js`.
5. **Before committing** changes to the repository if any shared protocol or extension-mirrored files were touched.
6. **Before deploying** the server or releasing the extension.
## How to sync
@@ -24,10 +25,11 @@ npm run build:extension
## What does it do?
The build script performs the following actions:
1. Synchronizes protocol constants by copying `shared/constants.js`, `shared/blacklist.js`, and `shared/README.md` into `extension/shared/`.
1. Synchronizes shared files by copying `shared/constants.js`, `shared/blacklist.js`, `shared/names.js`, and `shared/README.md` into `extension/shared/`.
2. Injects `EVENTS`, `HEARTBEAT_INTERVAL`, and `episode-utils.js` functions (`extractEpisodeId`, `sameEpisode`) into `content.js` via marker-based replacement.
3. Compiles browser-specific manifest files.
4. Packages the final ready-to-publish extension artifacts for Chrome and Firefox into the `dist/` directory.
3. Injects browser-specific uninstall URL constants into `background.js` and a build timestamp into `popup.html`.
4. Compiles browser-specific manifest files.
5. Packages the final ready-to-publish extension artifacts for Chrome and Firefox into the `dist/` directory.
## Protocol Versioning
The system enforces a strict `protocolVersion` check during the `JOIN_ROOM` handshake.
+44 -24
View File
@@ -1,38 +1,58 @@
# Tested Streaming Services
# 🎬 Tested Streaming Services & Compatibility
This document tracks which streaming platforms and media servers have been tested with KoalaSync.
This document tracks which streaming platforms and media servers are supported by the KoalaSync extension.
| Service | Sync Works | Media Title | Episode Auto-Sync | Notes |
|---------|:----------:|:-----------:|:-----------------:|-------|
| **YouTube** | ✅ Full | ✅ Full | ❌ | Individual videos, not episodes — no episode auto-sync. |
| **Twitch** | ✅ Full | ✅ Full | ❌ | Individual streams/VODs, not episodes — no episode auto-sync. |
| **Netflix** | ✅ Full | ❌ | ❌ | No media title exposed. |
| **Emby** | ✅ Full | ✅ Full | ✅ Full | Best-in-class support. |
| **Jellyfin** | ✅ Full | ✅ Full | ✅ Full | — |
| **Plex** | Not tested | Not tested | Not tested | — |
| **Disney+** | ✅ Full | ⚠️ Partial | ❌ | Series title only (e.g. "The Simpsons"), no episode info. |
| **Prime Video** | ✅ Full | ✅ Full | ❌ | — |
| **HBO Max / Max** | Not tested | Not tested | Not tested | — |
| **Crunchyroll** | Not tested | Not tested | Not tested | — |
| **Vimeo** | Not tested | Not tested | Not tested | — |
| **Dailymotion** | Not tested | Not tested | Not tested | — |
| **ARD / ZDF Mediathek** | Not tested | Not tested | Not tested | — |
> [!TIP]
> **Contributions are highly welcome!** 🤝 Anyone can easily update this list. If you have tested a streaming service (whether it works, has issues, or is not yet listed), please help the project by submitting a quick Pull Request. See the [How to Contribute](#how-to-contribute) guide below!
## Legend
---
## Compatibility Matrix
| Service | Sync Works | Media Title | Episode Auto-Sync | Last Tested | Tested By | Extension Version | Notes |
| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :--- |
| **YouTube** | ✅ Full | ✅ Full | ❌ N/A | — | — | — | Individual videos, not episodes. |
| **Twitch** | ✅ Full | ✅ Full | ❌ N/A | — | — | — | Individual streams/VODs. |
| **Netflix** | ✅ Full | ❌ | ❌ | — | — | — | No media title exposed. |
| **Emby** | ✅ Full | ✅ Full | ✅ Full | — | — | — | Best-in-class support. |
| **Jellyfin** | ✅ Full | ✅ Full | ✅ Full | — | — | — | — |
| **Plex** | Not tested | Not tested | Not tested | — | — | — | — |
| **Disney+** | ✅ Full | ⚠️ Partial | ❌ | — | — | — | Series title only (e.g. "The Simpsons"), no episode info. |
| **Prime Video** | ✅ Full | ✅ Full | ❌ | — | — | — | — |
| **HBO Max / Max** | Not tested | Not tested | Not tested | — | — | — | — |
| **Crunchyroll** | Not tested | Not tested | Not tested | — | — | — | — |
| **Vimeo** | Not tested | Not tested | Not tested | — | — | — | — |
| **Dailymotion** | Not tested | Not tested | Not tested | — | — | — | — |
| **ARD / ZDF Mediathek** | Not tested | Not tested | Not tested | — | — | — | — |
| **Vix** | ✅ Full | ✅ Full | ✅ Full | — | — | — | Everything works correctly. |
### Legend
| Symbol | Meaning |
|--------|---------|
| :---: | :--- |
| ✅ Full | Works without limitations. |
| ⚠️ Partial | Works with caveats (see Notes). |
| ❌ N/A | Not applicable or not supported. |
| ❌ | Not supported / does not work. |
| ❌ N/A | Not applicable (feature does not exist on the platform). |
| **Not tested** | Has not been tested yet. |
---
## How to Contribute
Tested a service that's not listed? Found different behavior than documented?
Updating this compatibility list is quick and easy! You don't need deep coding skills to contribute:
1. Test KoalaSync on the service with two browser profiles
2. Use the extension's **Dev tab** to check `readyState`, `currentTime`, and media title
3. Open a GitHub issue or PR updating this table
1. **Fork the Repository**: Click the **Fork** button at the top of the [KoalaSync GitHub Repository](https://github.com/Shik3i/KoalaSync).
2. **Edit this File**: Open [docs/TESTED_SERVICES.md](TESTED_SERVICES.md) in your fork's browser editor (or clone it locally) and update the table with your testing details.
3. **Commit & Push**: Commit your changes with a clear message (e.g., `docs: update Netflix compatibility status`).
4. **Create a Pull Request**: Submit the Pull Request (PR) from your fork to our `main` branch.
> [!NOTE]
> **Reporting Problems:** If you notice a bug or partial support on a service, please open a [GitHub Issue](https://github.com/Shik3i/KoalaSync/issues) describing the problem, and link it in the **Notes** column of the table.
>
> _If you are unsure how to create/link an issue, don't worry! Simply submit the PR anyway, and the maintainers will gladly create and link the issue for you._
---
## Technical Background
+51
View File
@@ -0,0 +1,51 @@
# DevOps Release Workflow
This document describes the deployment and release process for KoalaSync.
## Tag-Based Releases
KoalaSync uses a fully automated release pipeline triggered by Git tags.
> [!IMPORTANT]
> **DO NOT** manually bump the version numbers in any files (such as `package.json`, `manifest.base.json`, `shared/constants.js`, etc.) before creating a release.
> Bumping versions manually is redundant, leads to conflicts, and is completely handled by the CI/CD pipeline.
### How it Works
When you push a Git tag matching `v*` (e.g., `v2.5.1`), the GitHub Actions release workflow (`.github/workflows/release.yml`) is triggered. The workflow performs the following actions:
1. **Extracts the version** from the tag (e.g., `2.5.1` from `v2.5.1`).
2. **Injects the version** automatically into the following files:
- `extension/manifest.base.json`
- `shared/constants.js` (updates `APP_VERSION`)
- `package.json`
- `website/version.json`
- `website/template.html` (updates `softwareVersion` schema)
- `README.md` (updates badge and announcement banner)
- `website/sitemap.xml` (updates `lastmod` dates)
3. **Commits and pushes** these version updates back to the `main` branch automatically with the commit message `chore(release): update versions to vX.X.X [skip ci]`.
4. **Builds the extension** for both Chrome and Firefox and publishes the zipped archives.
5. **Builds the website** and uploads website artifacts.
6. **Builds and publishes** the Docker image for the relay server to the GitHub Container Registry (`ghcr.io`).
---
## Steps to Deploy a New Release
To release a new version (e.g., `v2.5.1`), follow these steps:
1. Make sure your local repository is synced on `main`:
```bash
git checkout main
git pull origin main
```
2. Create a local Git tag:
```bash
git tag v2.5.1
```
3. Push the tag to GitHub:
```bash
git push origin v2.5.1
```
The release pipeline will take care of the rest! You can monitor the progress under the **Actions** tab of the GitHub repository.
+151
View File
@@ -0,0 +1,151 @@
# Host Control Mode
This document describes the Host Control Mode implementation in the relay and
extension. It only covers behavior implemented in the current codebase.
## Modes
### `everyone`
- Default room mode.
- Any peer may send room-moving playback events.
### `host-only`
- Only controllers may send room-moving playback events.
- The host is always a controller.
- The host can promote additional peers to controllers.
- Guests can keep watching locally in solo/desynced mode, but their local actions
still do not drive the shared room.
Room-moving events are:
- `play`
- `pause`
- `seek`
- `force_sync_prepare`
- `force_sync_execute`
- `episode_lobby`
- `episode_lobby_cancel`
Heartbeats, force-sync ACKs, episode-ready events, ping/pong, and command ACKs
remain allowed for guests.
## Server State
Rooms store Host Control state in memory:
```js
{
hostPeerId,
controlMode,
controllers,
lastControlModeChangeAt,
lastRoleChangeAt,
forceSyncInitiator
}
```
`controllers` is a `Set` on the server and is serialized as an array in
`room_data` and `control_mode`.
State is not persisted across relay restarts.
## Authority Rules
### Changing mode
Only `hostPeerId` may send `set_control_mode`.
Valid values:
- `everyone`
- `host-only`
Invalid values are ignored. Non-host attempts are ignored and the sender receives
the current `control_mode` snapshot so optimistic UI can revert.
Mode changes are debounced per room for 500 ms.
### Promoting and demoting controllers
Only `hostPeerId` may send `set_peer_role`.
The host cannot demote themself. No-op role changes are ignored. Role changes are
debounced per room for 500 ms.
### Host leaving
When the host leaves and peers remain:
- the next peer becomes `hostPeerId`;
- `controlMode` falls back to `everyone`;
- `controllers` is reset to the new host;
- the relay broadcasts `control_mode`.
When a non-host controller leaves, the relay removes that peer from
`controllers` and broadcasts `control_mode`.
## Enforcement
The implementation has two enforcement points:
- The extension background script blocks local guest attempts in `host-only` and
sends `HOST_BLOCKED` to the content script for local UX.
- The relay drops room-moving events from non-controllers in `host-only`, so old
or modified clients cannot drive the room.
The relay is the authority for room-wide effects.
## Guest UX
When a guest action is blocked locally, the content script classifies it:
- deliberate user action: show the host-control dialog;
- likely involuntary player action (buffering, tab refocus, no recent gesture):
silently snap back when safe;
- live/DVR stream: degrade without forcing snap-back.
The dialog offers:
- stay in sync: resync to the host;
- watch on my own: enter solo/desynced mode.
In solo/desynced mode:
- the guest can control their local video;
- host room commands are ignored locally, except force-sync preparation is ACKed
so the host's flow can continue;
- the guest can resync to the host.
The extension reports `desynced` in peer status so the host UI can show that a
guest is watching solo.
## Force Sync Edge Case
The relay tracks `forceSyncInitiator` after a controller sends
`force_sync_prepare`.
This allows that same initiator's `force_sync_execute` through even if their
controller role changes before execute arrives. Without this, a demotion in the
middle of a force-sync flow could leave peers waiting after prepare.
The relay clears `forceSyncInitiator` after execute or when the initiator leaves.
## Capabilities
The relay advertises Host Control support in `room_data.capabilities`:
- `host-control`
- `co-host`
The extension hides or disables matching UI when capabilities are missing.
## Related Events
See [PROTOCOL.md](PROTOCOL.md) for payloads and relay behavior for:
- `set_control_mode`
- `control_mode`
- `set_peer_role`
- host-only gated relay events
+4 -2
View File
@@ -1,6 +1,6 @@
export default [
{
ignores: ["dist/**", "node_modules/**", "scratch/**"]
ignores: ["coverage/**", "dist/**", "node_modules/**", "scratch/**", "website/www/**"]
},
{
languageOptions: {
@@ -18,6 +18,8 @@ export default [
setInterval: "readonly",
clearTimeout: "readonly",
clearInterval: "readonly",
requestAnimationFrame: "readonly",
cancelAnimationFrame: "readonly",
fetch: "readonly",
CustomEvent: "readonly",
MutationObserver: "readonly",
@@ -58,7 +60,7 @@ export default [
}
},
{
files: ["server/**/*.js", "scripts/**/*.js", "scripts/**/*.cjs", "website/build.cjs"],
files: ["server/**/*.js", "scripts/**/*.js", "scripts/**/*.cjs", "website/build.cjs", "website/**/*.cjs"],
languageOptions: {
globals: {
require: "readonly",
@@ -9,6 +9,7 @@ services:
- MIN_VERSION=1.0.0 # Minimum client version allowed to connect
- MAX_ROOMS=100 # Maximum number of rooms that can exist
- MAX_PEERS_PER_ROOM=25 # Maximum number of peers allowed per room
- SERVER_SALT=CHANGE_ME_GENERATE_WITH_OPENSSL_RAND_BASE64_32 # Required: unique random salt for room-password hashes
- ADMIN_METRICS_TOKEN= # Optional: 32+ char random token for aggregate-only /health metrics
pids_limit: 2048 # Limits the container to 2048 process IDs for safety
networks: # Attaches the service to the networks listed below
+1
View File
@@ -11,6 +11,7 @@ services:
- MIN_VERSION=1.0.0 # Minimum client version allowed to connect
- MAX_ROOMS=100 # Maximum number of rooms that can exist
- MAX_PEERS_PER_ROOM=25 # Maximum number of peers allowed per room
- SERVER_SALT=CHANGE_ME_GENERATE_WITH_OPENSSL_RAND_BASE64_32 # Required: unique random salt for room-password hashes
- ADMIN_METRICS_TOKEN= # Optional: 32+ char random token for aggregate-only /health metrics
pids_limit: 2048 # Limits the container to 2048 process IDs for safety
networks: # Attaches the service to the networks listed below
+86 -32
View File
@@ -1,52 +1,106 @@
# KoalaSync Browser Extension
A Manifest V3 Browser Extension (Chrome & Firefox) for synchronized video playback across any website.
This directory contains the Manifest V3 browser extension for Chrome and Firefox. It owns the popup UI, background service worker, content-script video control, invitation bridge, audio processing, and all browser-local settings.
## Where You Are
- `manifest.base.json` is the source manifest. The build script creates browser-specific `manifest.json` files in `dist/chrome/` and `dist/firefox/`.
- `background.js` is the long-lived coordinator: WebSocket client, room state, host-control authority, heartbeat, reconnects, tab selection, and content-script injection.
- `content.js` runs in the selected video tab. It detects video state, applies remote play/pause/seek, handles episode transitions, and applies local audio processing.
- `popup.html` and `popup.js` implement the visible extension UI.
- `extension/shared/` is generated by `npm run build:extension` from the root `shared/` directory. Do not edit it directly.
## Key Features
- **Manifest V3**: Optimized Service Worker architecture with session persistence.
- **Pure Vanilla JS**: No external dependencies or heavy libraries.
- **Smart Peer IDs**: Hexadecimal IDs combined with customizable Usernames for easy identification.
- **On-Demand Connection**: The service worker only maintains a WebSocket connection while you're in a room. No persistent background connections — privacy-first architecture. Based on `connectIntent` flag that gates all reconnect attempts.
- **Live Diagnostics**: Built-in "Dev" tab for real-time video state debugging (ReadyState, CurrentTime, etc.).
- **Dynamic i18n (Multi-Language)**: Fully localized in 13 languages (`en`, `de`, `fr`, `es`, `it`, `pl`, `tr`, `nl`, `ja`, `ko`, `pt-BR`, `pt`, `ru`) with auto-detected fallback and dynamic on-the-fly language selectors.
- **Manifest V3**: Service-worker architecture with session persistence and explicit keep-alive handling.
- **Pure Vanilla JS**: No extension runtime dependencies and no bundler inside `extension/`.
- **On-Demand Connection**: The service worker connects only while the user intends to be in a room.
- **Host Control & Co-Hosts**: Hosts can switch a room into `host-only` mode and grant controller rights to trusted peers.
- **Episode Auto-Sync**: Title/episode changes can open a lobby so peers resume together once everyone is ready.
- **Smart Matching & Title Privacy**: Matching video tabs are highlighted, while tab/media title sharing can be reduced or disabled.
- **Audio Processing**: Optional local compressor settings live in the dedicated audio options page.
- **Status Diagnostics**: The Status tab exposes connection state, ping, video debug data, action history, and copyable logs.
- **Dynamic i18n**: 15 languages are supported: `en`, `de`, `fr`, `es`, `it`, `nl`, `pl`, `pt`, `pt-BR`, `tr`, `ru`, `ja`, `ko`, `zh`, and `uk`.
## Tab Overview
1. **Room**: Manage connections, view active peers, and share invitation links.
2. **Sync**: Control video playback (Play/Pause/Force Sync) and view recent activity.
3. **Settings**: Customize your Username, toggle domain-based Noise Filtering, and switch the App Language.
4. **Dev**: Monitor connection status and view real-time video element metadata for debugging.
1. **Room**: Select official/custom server, create or join rooms, view peers, share invite links, and manage Host Control when supported by the relay.
2. **Sync**: Select the video tab, send play/pause/seek/force-sync actions, and view episode lobby state.
3. **Settings**: Configure username, title sharing, noise filtering, auto episode sync, notifications, language, and audio options.
4. **Status**: Inspect connection state, latency, video debug info, history, and logs for bug reports.
5. **Dev**: Hidden developer-only controls shown for the `KoalaDev` username.
## Privacy & Permissions
KoalaSync requires `<all_urls>` permission to detect and interact with video elements (`<video>`) on websites.
- **No Browsing History**: We do not track or store your browsing history.
- **State Management**: Sensitive data (Room Passwords) is stored locally using `chrome.storage`.
- **Zero Telemetry**: No analytics or external tracking scripts.
- **Zero Runtime Dependencies**: The extension is built with pure Vanilla JS and contains no external libraries or tracking scripts, ensuring performance and privacy.
KoalaSync requires `<all_urls>` host permission so it can detect and control `<video>` elements on arbitrary sites.
- No browsing history is collected or uploaded.
- Room credentials and user settings are stored locally with `chrome.storage`.
- No analytics, external scripts, external fonts, or tracking libraries are used by the extension.
- Audio processing is local to the selected tab.
- Title privacy controls decide whether tab/media titles are shared with room peers.
## Installation
1. **Prepare Extension**: From the repository root, run:
```bash
npm run build:extension
```
2. Open Chrome and go to `chrome://extensions/`.
3. Enable **Developer mode** (top right).
4. Click **Load unpacked** and select the `dist/chrome` folder.
From the repository root:
```bash
npm install
npm run build:extension
```
Then load the generated bundle:
- Chrome/Chromium: open `chrome://extensions/`, enable Developer Mode, and load `dist/chrome`.
- Firefox: open `about:debugging`, choose **This Firefox**, and load `dist/firefox/manifest.json`.
## Development
If you modify `shared/constants.js`, you must synchronize the changes by running the build script from the root:
Run the build whenever shared protocol files or extension packaging inputs change:
```bash
npm run build:extension
```
This ensures that the `extension/shared` folder is updated with the latest protocol constants.
The build copies `shared/constants.js`, `shared/blacklist.js`, `shared/names.js`, and `shared/README.md` into `extension/shared/`, injects synchronous constants into `content.js`, generates browser manifests, and creates zip artifacts in `dist/`.
Useful focused checks from the repository root:
```bash
node -c extension/background.js
node -c extension/content.js
node -c extension/popup.js
node scripts/test-episode-utils.mjs
node scripts/test-title-privacy.mjs
node scripts/test-audio-settings.mjs
node scripts/test-locales.cjs
```
For the full suite, run:
```bash
npm run verify
```
## Do Not Break
- Keep `content.js` synchronous and IIFE-based; it cannot import ES modules directly.
- Keep the injection markers used by `scripts/build-extension.cjs`.
- Keep protocol names in `shared/constants.js` as the source of truth.
- Keep extension runtime dependencies at zero unless the project explicitly decides to introduce a bundler.
- Keep all extension assets self-hosted.
## Module Structure
| File | Purpose |
|---|---|
| `background.js` | Service worker: message routing, tab listeners, startup |
| `content.js` | Video detection, audio processing, episode transition (IIFE) |
| `popup.js` | Popup UI: join/create, tabs, status, settings |
| `bridge.js` | Landing page bridge (injected into sync.koalastuff.net) |
| `episode-utils.js` | Shared `extractEpisodeId()` / `sameEpisode()` — used by background.js, injected into content.js at build time |
| `i18n.js` | Translation loader |
| `shared/` | Constants, blacklist, name generator |
| `background.js` | Service worker: WebSocket protocol, room state, host control, tab/content routing, reconnects |
| `content.js` | Video detection/control, audio processing, episode transition, host-only guest behavior |
| `popup.js` | Popup UI: room join/create, tabs, settings, peer list, status, diagnostics |
| `popup.html` | Popup markup, tabs, onboarding, status/debug surfaces |
| `bridge.js` | Invitation bridge injected into `sync.koalastuff.net` |
| `episode-utils.js` | Shared episode-title parser imported by background and injected into content at build time |
| `title-privacy.js` | Tab/media title privacy modes and sanitization helpers |
| `audio-options.html` / `audio-options.js` / `audio-options.css` | Dedicated local audio-processing settings page |
| `page-api-seek-overrides.js` | Page-level seek bridge for site-specific player APIs |
| `modules/tab-manager.js` | Tab lifecycle helper used by the background service worker |
| `i18n.js` | Dynamic locale loader and DOM translation helper |
| `locales/` | Runtime popup translations |
| `_locales/` | Browser-store manifest translations |
| `shared/` | Generated mirror of root shared constants, blacklist, names, and README |
+640 -48
View File
File diff suppressed because it is too large Load Diff
+1638 -1120
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* KoalaSync Episode Title Utilities
* Single source of truth — synced to content.js by build-extension.js.
* Single source of truth — synced to content.js by build-extension.cjs.
* Keep in sync with the injection block in content.js!
*/
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Teile diesen Link mit Freunden, damit sie beitreten können",
"LABEL_PEERS_IN_ROOM": "Teilnehmer im Raum",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Andere Benutzer, die derzeit mit diesem Raum verbunden sind",
"LABEL_HOST_CONTROL": "Host-Steuerung",
"LABEL_HOST_CONTROL_TOOLTIP": "Festlegen, wer die Wiedergabe in diesem Raum steuern darf",
"LABEL_HOST_ONLY_TOGGLE": "Nur ich darf die Wiedergabe steuern",
"NOTICE_HOST_CONTROLS": "Der Host steuert die Wiedergabe für alle.",
"NOTICE_COHOST_HINT": "Tipp: Gib einzelnen Teilnehmern unten in der Teilnehmerliste die Steuerung.",
"BADGE_HOST": "Host",
"BADGE_GUEST": "Gast",
"BADGE_CONTROLLER": "Controller",
"BTN_GIVE_CONTROL": "Steuerung geben",
"BTN_REVOKE_CONTROL": "Entziehen",
"BADGE_DESYNCED": "Solo",
"TOOLTIP_PEER_DESYNCED": "Schaut alleine — ignoriert die Befehle des Hosts",
"HCM_DIALOG_TITLE": "KoalaSync · Der Host steuert diesen Raum",
"HCM_DIALOG_BODY": "Nur der Host kann die Wiedergabe in diesem Raum steuern. Weiter gemeinsam schauen oder allein weiterschauen?",
"HCM_DIALOG_STAY": "Synchron bleiben",
"HCM_DIALOG_SOLO": "Allein weiterschauen",
"HCM_BADGE_SOLO": "Du schaust allein",
"HCM_BADGE_RESYNC": "Neu sync",
"NO_PEERS_CONNECTED": "Keine Teilnehmer verbunden",
"BTN_LEAVE_ROOM": "Raum verlassen",
"LABEL_SELECT_VIDEO": "Video auswählen",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Generiere deine interne ID neu und verbinde dich erneut",
"REGEN_ID_DESC": "Verwende dies, wenn du Fehler wegen doppelter Identität siehst.",
"REGEN_ID_OTHER_ISSUE": "Anderes Problem? Öffne ein GitHub Issue",
"TOAST_ID_REGENERATED": "Identität neu generiert — Verbindung wird wiederhergestellt…",
"LABEL_CONN_STATUS": "Verbindungsstatus",
"LABEL_CONN_STATUS_TOOLTIP": "Aktueller WebSocket-Verbindungsstatus",
"CONN_STATUS_DISCONNECTED": "Getrennt",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Demnächst",
"BTN_RESTART_TOUR": "Einführung neu starten",
"BTN_RESTART_TOUR_TOOLTIP": "Startet die Einführung für neue Benutzer erneut",
"HINT_SELECT_VIDEO": "Wähle hier dein Video aus!"
"HINT_SELECT_VIDEO": "Wähle hier dein Video aus!",
"OPTION_TITLE_PRIVACY_FULL": "Voll senden",
"OPTION_TITLE_PRIVACY_EPISODE": "Nur Episoden-ID senden",
"OPTION_TITLE_PRIVACY_HIDDEN": "Nicht senden",
"LABEL_PRIVACY_SETTINGS": "Privatsphäre-Einstellungen",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Lege fest, welche Tab- und Medientitel an den Raum gesendet werden.",
"LABEL_SEND_TAB_TITLE": "Tab-Titel senden",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Sendet den Titel des ausgewählten Browser-Tabs an den Raum.",
"LABEL_MEDIA_TITLE_PRIVACY": "Medientitel",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Lege fest, wie Medientitel an den Raum gesendet werden."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Share this link with friends so they can join",
"LABEL_PEERS_IN_ROOM": "Peers in Room",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Other users currently connected to this room",
"LABEL_HOST_CONTROL": "Host Control",
"LABEL_HOST_CONTROL_TOOLTIP": "Restrict who can control playback in this room",
"LABEL_HOST_ONLY_TOGGLE": "Only I can control playback",
"NOTICE_HOST_CONTROLS": "The host controls playback for everyone.",
"NOTICE_COHOST_HINT": "Tip: grant control to individual viewers in the participant list below.",
"BADGE_HOST": "Host",
"BADGE_GUEST": "Guest",
"BADGE_CONTROLLER": "Controller",
"BTN_GIVE_CONTROL": "Give control",
"BTN_REVOKE_CONTROL": "Revoke",
"BADGE_DESYNCED": "Solo",
"TOOLTIP_PEER_DESYNCED": "Watching on their own — host's commands are ignored",
"HCM_DIALOG_TITLE": "KoalaSync · Host controls this room",
"HCM_DIALOG_BODY": "Only the host can control playback in this room. Keep watching together, or watch on your own?",
"HCM_DIALOG_STAY": "Stay in sync",
"HCM_DIALOG_SOLO": "Watch on my own",
"HCM_BADGE_SOLO": "Watching on your own",
"HCM_BADGE_RESYNC": "Resync",
"NO_PEERS_CONNECTED": "No peers connected",
"BTN_LEAVE_ROOM": "Leave Room",
"LABEL_SELECT_VIDEO": "Select Video",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Regenerate your internal ID and reconnect",
"REGEN_ID_DESC": "Use this if you see \"Duplicate Identity\" errors.",
"REGEN_ID_OTHER_ISSUE": "Other issue? Open a GitHub Issue",
"TOAST_ID_REGENERATED": "Identity regenerated — reconnecting…",
"LABEL_CONN_STATUS": "Connection Status",
"LABEL_CONN_STATUS_TOOLTIP": "Current WebSocket connection state",
"CONN_STATUS_DISCONNECTED": "Disconnected",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Coming soon",
"BTN_RESTART_TOUR": "Restart Tutorial",
"BTN_RESTART_TOUR_TOOLTIP": "Restart the onboarding tutorial",
"HINT_SELECT_VIDEO": "Select your video here!"
"HINT_SELECT_VIDEO": "Select your video here!",
"OPTION_TITLE_PRIVACY_FULL": "Send full title",
"OPTION_TITLE_PRIVACY_EPISODE": "Send episode ID",
"OPTION_TITLE_PRIVACY_HIDDEN": "Do not send",
"LABEL_PRIVACY_SETTINGS": "Privacy Settings",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Choose which tab and media titles are sent to the room.",
"LABEL_SEND_TAB_TITLE": "Send tab title",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Send the selected browser tab title to the room.",
"LABEL_MEDIA_TITLE_PRIVACY": "Media title",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Choose how media titles are sent to the room."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Comparte este enlace con amigos para que se unan",
"LABEL_PEERS_IN_ROOM": "Participantes en la sala",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Otros usuarios conectados actualmente a esta sala",
"LABEL_HOST_CONTROL": "Control del anfitrión",
"LABEL_HOST_CONTROL_TOOLTIP": "Restringir quién puede controlar la reproducción en esta sala",
"LABEL_HOST_ONLY_TOGGLE": "Solo yo puedo controlar la reproducción",
"NOTICE_HOST_CONTROLS": "El anfitrión controla la reproducción para todos.",
"NOTICE_COHOST_HINT": "Consejo: concede el control a usuarios concretos en la lista de participantes de abajo.",
"BADGE_HOST": "Anfitrión",
"BADGE_GUEST": "Invitado",
"BADGE_CONTROLLER": "Controlador",
"BTN_GIVE_CONTROL": "Dar control",
"BTN_REVOKE_CONTROL": "Quitar",
"BADGE_DESYNCED": "Solo",
"TOOLTIP_PEER_DESYNCED": "Viendo por su cuenta — ignora los comandos del anfitrión",
"HCM_DIALOG_TITLE": "KoalaSync · El anfitrión controla esta sala",
"HCM_DIALOG_BODY": "Solo el anfitrión puede controlar la reproducción en esta sala. ¿Seguir viendo juntos o ver por tu cuenta?",
"HCM_DIALOG_STAY": "Seguir sincronizado",
"HCM_DIALOG_SOLO": "Ver por mi cuenta",
"HCM_BADGE_SOLO": "Viendo por tu cuenta",
"HCM_BADGE_RESYNC": "Resincronizar",
"NO_PEERS_CONNECTED": "Sin participantes conectados",
"BTN_LEAVE_ROOM": "Salir de la sala",
"LABEL_SELECT_VIDEO": "Seleccionar video",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Regenerar tu ID interno y volver a conectarte",
"REGEN_ID_DESC": "Usa esto si ves errores de 'Identidad duplicada'.",
"REGEN_ID_OTHER_ISSUE": "¿Tienes otro problema? Abre un reporte en GitHub",
"TOAST_ID_REGENERATED": "Identidad regenerada — reconectando…",
"LABEL_CONN_STATUS": "Estado de la conexión",
"LABEL_CONN_STATUS_TOOLTIP": "Estado actual de la conexión WebSocket",
"CONN_STATUS_DISCONNECTED": "Desconectado",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Próximamente",
"BTN_RESTART_TOUR": "Reiniciar tutorial",
"BTN_RESTART_TOUR_TOOLTIP": "Reiniciar el tutorial de inicio",
"HINT_SELECT_VIDEO": "¡Selecciona tu video aquí!"
"HINT_SELECT_VIDEO": "¡Selecciona tu video aquí!",
"OPTION_TITLE_PRIVACY_FULL": "Enviar título completo",
"OPTION_TITLE_PRIVACY_EPISODE": "Enviar ID de episodio",
"OPTION_TITLE_PRIVACY_HIDDEN": "No enviar",
"LABEL_PRIVACY_SETTINGS": "Ajustes de privacidad",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Elige qué títulos de pestaña y multimedia se envían a la sala.",
"LABEL_SEND_TAB_TITLE": "Enviar título de pestaña",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Envía a la sala el título de la pestaña del navegador seleccionada.",
"LABEL_MEDIA_TITLE_PRIVACY": "Título multimedia",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Elige cómo se envían los títulos multimedia a la sala."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Partagez ce lien avec vos amis pour qu'ils vous rejoignent",
"LABEL_PEERS_IN_ROOM": "Membres dans le salon",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Autres utilisateurs connectés à ce salon",
"LABEL_HOST_CONTROL": "Contrôle de l'hôte",
"LABEL_HOST_CONTROL_TOOLTIP": "Définir qui peut contrôler la lecture dans ce salon",
"LABEL_HOST_ONLY_TOGGLE": "Moi seul peux contrôler la lecture",
"NOTICE_HOST_CONTROLS": "L'hôte contrôle la lecture pour tout le monde.",
"NOTICE_COHOST_HINT": "Astuce : donnez le contrôle à des participants précis dans la liste ci-dessous.",
"BADGE_HOST": "Hôte",
"BADGE_GUEST": "Invité",
"BADGE_CONTROLLER": "Contrôleur",
"BTN_GIVE_CONTROL": "Donner le contrôle",
"BTN_REVOKE_CONTROL": "Retirer",
"BADGE_DESYNCED": "Solo",
"TOOLTIP_PEER_DESYNCED": "Regarde de son côté — ignore les commandes de l'hôte",
"HCM_DIALOG_TITLE": "KoalaSync · L'hôte contrôle ce salon",
"HCM_DIALOG_BODY": "Seul l'hôte peut contrôler la lecture dans ce salon. Continuer à regarder ensemble, ou regarder de votre côté ?",
"HCM_DIALOG_STAY": "Rester synchronisé",
"HCM_DIALOG_SOLO": "Regarder de mon côté",
"HCM_BADGE_SOLO": "Vous regardez seul",
"HCM_BADGE_RESYNC": "Resynchroniser",
"NO_PEERS_CONNECTED": "Aucun membre connecté",
"BTN_LEAVE_ROOM": "Quitter le salon",
"LABEL_SELECT_VIDEO": "Choisir une vidéo",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Régénérer votre identifiant interne et vous reconnecter",
"REGEN_ID_DESC": "Utilisez cette option si vous rencontrez des erreurs de 'Double identité'.",
"REGEN_ID_OTHER_ISSUE": "Autre problème? Ouvrez un Issue GitHub",
"TOAST_ID_REGENERATED": "Identité régénérée — reconnexion…",
"LABEL_CONN_STATUS": "Statut de la connexion",
"LABEL_CONN_STATUS_TOOLTIP": "Statut actuel de la connexion WebSocket",
"CONN_STATUS_DISCONNECTED": "Déconnecté",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Bientôt disponible",
"BTN_RESTART_TOUR": "Redémarrer le tutoriel",
"BTN_RESTART_TOUR_TOOLTIP": "Recommencer le tutoriel d'intégration",
"HINT_SELECT_VIDEO": "Sélectionnez votre vidéo ici !"
"HINT_SELECT_VIDEO": "Sélectionnez votre vidéo ici !",
"OPTION_TITLE_PRIVACY_FULL": "Envoyer le titre complet",
"OPTION_TITLE_PRIVACY_EPISODE": "Envoyer lidentifiant d’épisode",
"OPTION_TITLE_PRIVACY_HIDDEN": "Ne pas envoyer",
"LABEL_PRIVACY_SETTINGS": "Paramètres de confidentialité",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Choisissez quels titres donglet et de média sont envoyés au salon.",
"LABEL_SEND_TAB_TITLE": "Envoyer le titre de longlet",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Envoie au salon le titre de longlet de navigateur sélectionné.",
"LABEL_MEDIA_TITLE_PRIVACY": "Titre du média",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Choisissez comment les titres des médias sont envoyés au salon."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Condividi questo link per invitare i tuoi amici",
"LABEL_PEERS_IN_ROOM": "Partecipanti",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Utenti connessi a questa stanza",
"LABEL_HOST_CONTROL": "Controllo host",
"LABEL_HOST_CONTROL_TOOLTIP": "Limita chi può controllare la riproduzione in questa stanza",
"LABEL_HOST_ONLY_TOGGLE": "Solo io posso controllare la riproduzione",
"NOTICE_HOST_CONTROLS": "L'host controlla la riproduzione per tutti.",
"NOTICE_COHOST_HINT": "Suggerimento: concedi il controllo ai singoli partecipanti nell'elenco qui sotto.",
"BADGE_HOST": "Host",
"BADGE_GUEST": "Ospite",
"BADGE_CONTROLLER": "Controller",
"BTN_GIVE_CONTROL": "Dai il controllo",
"BTN_REVOKE_CONTROL": "Revoca",
"BADGE_DESYNCED": "Solo",
"TOOLTIP_PEER_DESYNCED": "Guarda per conto proprio — ignora i comandi dell'host",
"HCM_DIALOG_TITLE": "KoalaSync · L'host controlla questa stanza",
"HCM_DIALOG_BODY": "Solo l'host può controllare la riproduzione in questa stanza. Continuare a guardare insieme o guardare per conto tuo?",
"HCM_DIALOG_STAY": "Resta sincronizzato",
"HCM_DIALOG_SOLO": "Guarda per conto mio",
"HCM_BADGE_SOLO": "Stai guardando da solo",
"HCM_BADGE_RESYNC": "Risincronizza",
"NO_PEERS_CONNECTED": "Nessun partecipante connesso",
"BTN_LEAVE_ROOM": "Lascia Stanza",
"LABEL_SELECT_VIDEO": "Seleziona Video",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Genera un nuovo ID interno e riconnettiti",
"REGEN_ID_DESC": "Usa questa opzione se riscontri errori di 'Identità Duplicata'.",
"REGEN_ID_OTHER_ISSUE": "Hai altri problemi? Apri una segnalazione su GitHub",
"TOAST_ID_REGENERATED": "Identità rigenerata — riconnessione…",
"LABEL_CONN_STATUS": "Stato Connessione",
"LABEL_CONN_STATUS_TOOLTIP": "Stato attuale della connessione",
"CONN_STATUS_DISCONNECTED": "Disconnesso",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Prossimamente",
"BTN_RESTART_TOUR": "Riavvia Tutorial",
"BTN_RESTART_TOUR_TOOLTIP": "Riavvia il tutorial introduttivo",
"HINT_SELECT_VIDEO": "Scegli il tuo video qui!"
"HINT_SELECT_VIDEO": "Scegli il tuo video qui!",
"OPTION_TITLE_PRIVACY_FULL": "Invia titolo completo",
"OPTION_TITLE_PRIVACY_EPISODE": "Invia ID episodio",
"OPTION_TITLE_PRIVACY_HIDDEN": "Non inviare",
"LABEL_PRIVACY_SETTINGS": "Impostazioni privacy",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Scegli quali titoli di scheda e contenuto multimediale vengono inviati alla stanza.",
"LABEL_SEND_TAB_TITLE": "Invia titolo scheda",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Invia alla stanza il titolo della scheda del browser selezionata.",
"LABEL_MEDIA_TITLE_PRIVACY": "Titolo multimediale",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Scegli come inviare alla stanza i titoli multimediali."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "友達とこのリンクを共有して参加してもらいましょう",
"LABEL_PEERS_IN_ROOM": "ルーム内のメンバー",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "現在このルームに接続している他のユーザー",
"LABEL_HOST_CONTROL": "ホスト操作",
"LABEL_HOST_CONTROL_TOOLTIP": "このルームで再生を操作できる人を制限します",
"LABEL_HOST_ONLY_TOGGLE": "再生を操作できるのは自分だけ",
"NOTICE_HOST_CONTROLS": "ホストが全員の再生を操作します。",
"NOTICE_COHOST_HINT": "ヒント: 下の参加者リストで個別の参加者に操作権限を付与できます。",
"BADGE_HOST": "ホスト",
"BADGE_GUEST": "ゲスト",
"BADGE_CONTROLLER": "操作権あり",
"BTN_GIVE_CONTROL": "操作権を付与",
"BTN_REVOKE_CONTROL": "取り消す",
"BADGE_DESYNCED": "ソロ",
"TOOLTIP_PEER_DESYNCED": "単独で視聴中 — ホストのコマンドを無視しています",
"HCM_DIALOG_TITLE": "KoalaSync · ホストがこのルームを操作中",
"HCM_DIALOG_BODY": "このルームではホストのみが再生を操作できます。一緒に視聴を続けますか、それとも自分だけで視聴しますか?",
"HCM_DIALOG_STAY": "同期を維持",
"HCM_DIALOG_SOLO": "自分だけで視聴",
"HCM_BADGE_SOLO": "自分だけで視聴中",
"HCM_BADGE_RESYNC": "再同期",
"NO_PEERS_CONNECTED": "接続しているメンバーはいません",
"BTN_LEAVE_ROOM": "ルームを退室",
"LABEL_SELECT_VIDEO": "ビデオを選択",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "内部IDを再生成して再接続します",
"REGEN_ID_DESC": "「Duplicate Identity」(ID重複)エラーが表示される場合に使用します。",
"REGEN_ID_OTHER_ISSUE": "他の問題?GitHub Issueを開く",
"TOAST_ID_REGENERATED": "IDを再生成しました — 再接続中…",
"LABEL_CONN_STATUS": "接続状態",
"LABEL_CONN_STATUS_TOOLTIP": "現在のWebSocket接続状態",
"CONN_STATUS_DISCONNECTED": "切断されました",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "近日公開",
"BTN_RESTART_TOUR": "チュートリアルを再起動",
"BTN_RESTART_TOUR_TOOLTIP": "オンボーディングチュートリアルを再起動する",
"HINT_SELECT_VIDEO": "ここで動画を選択してください!"
"HINT_SELECT_VIDEO": "ここで動画を選択してください!",
"OPTION_TITLE_PRIVACY_FULL": "完全なタイトルを送信",
"OPTION_TITLE_PRIVACY_EPISODE": "エピソードIDを送信",
"OPTION_TITLE_PRIVACY_HIDDEN": "送信しない",
"LABEL_PRIVACY_SETTINGS": "プライバシー設定",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "ルームに送信するタブタイトルとメディアタイトルを選択します。",
"LABEL_SEND_TAB_TITLE": "タブタイトルを送信",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "選択中のブラウザータブのタイトルをルームに送信します。",
"LABEL_MEDIA_TITLE_PRIVACY": "メディアタイトル",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "メディアタイトルをルームに送信する方法を選択します。"
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "친구들이 참여할 수 있도록 이 링크를 공유하세요",
"LABEL_PEERS_IN_ROOM": "방의 참여자",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "현재 이 방에 연결되어 있는 다른 사용자들",
"LABEL_HOST_CONTROL": "호스트 제어",
"LABEL_HOST_CONTROL_TOOLTIP": "이 방에서 재생을 제어할 수 있는 사람 제한",
"LABEL_HOST_ONLY_TOGGLE": "나만 재생을 제어할 수 있음",
"NOTICE_HOST_CONTROLS": "호스트가 모두의 재생을 제어합니다.",
"NOTICE_COHOST_HINT": "팁: 아래 참가자 목록에서 개별 참가자에게 제어 권한을 부여하세요.",
"BADGE_HOST": "호스트",
"BADGE_GUEST": "게스트",
"BADGE_CONTROLLER": "제어자",
"BTN_GIVE_CONTROL": "제어 권한 부여",
"BTN_REVOKE_CONTROL": "취소",
"BADGE_DESYNCED": "단독",
"TOOLTIP_PEER_DESYNCED": "혼자 시청 중 — 호스트 명령을 무시합니다",
"HCM_DIALOG_TITLE": "KoalaSync · 호스트가 이 방을 제어합니다",
"HCM_DIALOG_BODY": "이 방에서는 호스트만 재생을 제어할 수 있습니다. 계속 함께 보시겠어요, 아니면 혼자 보시겠어요?",
"HCM_DIALOG_STAY": "동기화 유지",
"HCM_DIALOG_SOLO": "혼자 보기",
"HCM_BADGE_SOLO": "혼자 시청 중",
"HCM_BADGE_RESYNC": "다시 동기화",
"NO_PEERS_CONNECTED": "연결된 참여자가 없습니다",
"BTN_LEAVE_ROOM": "방 나가기",
"LABEL_SELECT_VIDEO": "비디오 선택",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "내부 ID를 재생성하고 다시 연결합니다",
"REGEN_ID_DESC": "\"중복된 ID\" 오류가 발생할 경우에 사용하세요.",
"REGEN_ID_OTHER_ISSUE": "다른 문제? GitHub Issue 열기",
"TOAST_ID_REGENERATED": "ID가 재생성되었습니다 — 다시 연결 중…",
"LABEL_CONN_STATUS": "연결 상태",
"LABEL_CONN_STATUS_TOOLTIP": "현재 WebSocket 연결 상태",
"CONN_STATUS_DISCONNECTED": "연결 끊김",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "출시 예정",
"BTN_RESTART_TOUR": "튜토리얼 다시 시작",
"BTN_RESTART_TOUR_TOOLTIP": "온보딩 튜토리얼을 다시 시작합니다",
"HINT_SELECT_VIDEO": "여기에서 비디오를 선택하세요!"
"HINT_SELECT_VIDEO": "여기에서 비디오를 선택하세요!",
"OPTION_TITLE_PRIVACY_FULL": "전체 제목 보내기",
"OPTION_TITLE_PRIVACY_EPISODE": "에피소드 ID 보내기",
"OPTION_TITLE_PRIVACY_HIDDEN": "보내지 않음",
"LABEL_PRIVACY_SETTINGS": "개인정보 설정",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "방에 보낼 탭 제목과 미디어 제목을 선택합니다.",
"LABEL_SEND_TAB_TITLE": "탭 제목 보내기",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "선택한 브라우저 탭 제목을 방에 보냅니다.",
"LABEL_MEDIA_TITLE_PRIVACY": "미디어 제목",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "미디어 제목을 방에 보내는 방식을 선택합니다."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Deel deze link met vrienden zodat ze kunnen deelnemen",
"LABEL_PEERS_IN_ROOM": "Deelnemers in kamer",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Andere gebruikers die momenteel zijn verbonden met deze kamer",
"LABEL_HOST_CONTROL": "Hostbeheer",
"LABEL_HOST_CONTROL_TOOLTIP": "Bepaal wie de weergave in deze kamer mag bedienen",
"LABEL_HOST_ONLY_TOGGLE": "Alleen ik mag de weergave bedienen",
"NOTICE_HOST_CONTROLS": "De host bedient de weergave voor iedereen.",
"NOTICE_COHOST_HINT": "Tip: geef afzonderlijke deelnemers de besturing in de lijst hieronder.",
"BADGE_HOST": "Host",
"BADGE_GUEST": "Gast",
"BADGE_CONTROLLER": "Bediener",
"BTN_GIVE_CONTROL": "Bediening geven",
"BTN_REVOKE_CONTROL": "Intrekken",
"BADGE_DESYNCED": "Solo",
"TOOLTIP_PEER_DESYNCED": "Zelfstandig aan het kijken — negeert de commando's van de host",
"HCM_DIALOG_TITLE": "KoalaSync · De host bedient deze kamer",
"HCM_DIALOG_BODY": "Alleen de host kan de weergave in deze kamer bedienen. Samen blijven kijken of zelf verder kijken?",
"HCM_DIALOG_STAY": "Gesynchroniseerd blijven",
"HCM_DIALOG_SOLO": "Zelf verder kijken",
"HCM_BADGE_SOLO": "Je kijkt zelfstandig",
"HCM_BADGE_RESYNC": "Opnieuw sync",
"NO_PEERS_CONNECTED": "Geen deelnemers verbonden",
"BTN_LEAVE_ROOM": "Kamer verlaten",
"LABEL_SELECT_VIDEO": "Selecteer video",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Genereer uw interne ID opnieuw en maak opnieuw verbinding",
"REGEN_ID_DESC": "Gebruik dit als u fouten ziet over 'Duplicate Identity'.",
"REGEN_ID_OTHER_ISSUE": "Ander probleem? Open een GitHub Issue",
"TOAST_ID_REGENERATED": "Identiteit opnieuw gegenereerd — opnieuw verbinden…",
"LABEL_CONN_STATUS": "Verbindingsstatus",
"LABEL_CONN_STATUS_TOOLTIP": "Huidige WebSocket-verbindingsstatus",
"CONN_STATUS_DISCONNECTED": "Verbinding verbroken",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Binnenkort beschikbaar",
"BTN_RESTART_TOUR": "Tutorial opnieuw starten",
"BTN_RESTART_TOUR_TOOLTIP": "Start de introductie-tutorial opnieuw",
"HINT_SELECT_VIDEO": "Selecteer hier je video!"
"HINT_SELECT_VIDEO": "Selecteer hier je video!",
"OPTION_TITLE_PRIVACY_FULL": "Volledige titel verzenden",
"OPTION_TITLE_PRIVACY_EPISODE": "Afleverings-ID verzenden",
"OPTION_TITLE_PRIVACY_HIDDEN": "Niet verzenden",
"LABEL_PRIVACY_SETTINGS": "Privacy-instellingen",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Kies welke tab- en mediatitels naar de kamer worden gestuurd.",
"LABEL_SEND_TAB_TITLE": "Tabtitel verzenden",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Stuurt de titel van het geselecteerde browsertabblad naar de kamer.",
"LABEL_MEDIA_TITLE_PRIVACY": "Mediatitel",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Kies hoe mediatitels naar de kamer worden gestuurd."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Udostępnij ten link znajomym, aby mogli dołączyć",
"LABEL_PEERS_IN_ROOM": "Uczestnicy w pokoju",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Inni użytkownicy aktualnie połączeni z tym pokojem",
"LABEL_HOST_CONTROL": "Kontrola hosta",
"LABEL_HOST_CONTROL_TOOLTIP": "Określ, kto może sterować odtwarzaniem w tym pokoju",
"LABEL_HOST_ONLY_TOGGLE": "Tylko ja mogę sterować odtwarzaniem",
"NOTICE_HOST_CONTROLS": "Host steruje odtwarzaniem dla wszystkich.",
"NOTICE_COHOST_HINT": "Wskazówka: przyznaj sterowanie wybranym uczestnikom na liście poniżej.",
"BADGE_HOST": "Host",
"BADGE_GUEST": "Gość",
"BADGE_CONTROLLER": "Sterujący",
"BTN_GIVE_CONTROL": "Daj sterowanie",
"BTN_REVOKE_CONTROL": "Odbierz",
"BADGE_DESYNCED": "Solo",
"TOOLTIP_PEER_DESYNCED": "Ogląda samodzielnie — ignoruje polecenia hosta",
"HCM_DIALOG_TITLE": "KoalaSync · Host steruje tym pokojem",
"HCM_DIALOG_BODY": "Tylko host może sterować odtwarzaniem w tym pokoju. Oglądać dalej razem czy oglądać samodzielnie?",
"HCM_DIALOG_STAY": "Pozostań zsynchronizowany",
"HCM_DIALOG_SOLO": "Oglądaj samodzielnie",
"HCM_BADGE_SOLO": "Oglądasz samodzielnie",
"HCM_BADGE_RESYNC": "Zsynchronizuj",
"NO_PEERS_CONNECTED": "Brak połączonych uczestników",
"BTN_LEAVE_ROOM": "Opuść pokój",
"LABEL_SELECT_VIDEO": "Wybierz wideo",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Wygeneruj ponownie swój identyfikator i połącz się ponownie",
"REGEN_ID_DESC": "Użyj tego, jeśli widzisz błędy o zduplikowanej tożsamości.",
"REGEN_ID_OTHER_ISSUE": "Inny problem? Otwórz Issue na GitHub",
"TOAST_ID_REGENERATED": "Tożsamość wygenerowana ponownie — ponowne łączenie…",
"LABEL_CONN_STATUS": "Status połączenia",
"LABEL_CONN_STATUS_TOOLTIP": "Bieżący stan połączenia WebSocket",
"CONN_STATUS_DISCONNECTED": "Rozłączono",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Wkrótce",
"BTN_RESTART_TOUR": "Uruchom samouczek ponownie",
"BTN_RESTART_TOUR_TOOLTIP": "Uruchom ponownie samouczek powitalny",
"HINT_SELECT_VIDEO": "Wybierz swoje wideo tutaj!"
"HINT_SELECT_VIDEO": "Wybierz swoje wideo tutaj!",
"OPTION_TITLE_PRIVACY_FULL": "Wysyłaj pełny tytuł",
"OPTION_TITLE_PRIVACY_EPISODE": "Wysyłaj ID odcinka",
"OPTION_TITLE_PRIVACY_HIDDEN": "Nie wysyłaj",
"LABEL_PRIVACY_SETTINGS": "Ustawienia prywatności",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Wybierz, które tytuły kart i multimediów są wysyłane do pokoju.",
"LABEL_SEND_TAB_TITLE": "Wysyłaj tytuł karty",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Wysyła do pokoju tytuł wybranej karty przeglądarki.",
"LABEL_MEDIA_TITLE_PRIVACY": "Tytuł multimediów",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Wybierz, jak tytuły multimediów są wysyłane do pokoju."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Compartilhe este link com seus amigos para que eles entrem",
"LABEL_PEERS_IN_ROOM": "Participantes",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Outros usuários conectados a esta sala",
"LABEL_HOST_CONTROL": "Controle do anfitrião",
"LABEL_HOST_CONTROL_TOOLTIP": "Restringir quem pode controlar a reprodução nesta sala",
"LABEL_HOST_ONLY_TOGGLE": "Somente eu posso controlar a reprodução",
"NOTICE_HOST_CONTROLS": "O anfitrião controla a reprodução para todos.",
"NOTICE_COHOST_HINT": "Dica: conceda o controle a participantes específicos na lista abaixo.",
"BADGE_HOST": "Anfitrião",
"BADGE_GUEST": "Convidado",
"BADGE_CONTROLLER": "Controlador",
"BTN_GIVE_CONTROL": "Dar controle",
"BTN_REVOKE_CONTROL": "Revogar",
"BADGE_DESYNCED": "Solo",
"TOOLTIP_PEER_DESYNCED": "Assistindo por conta própria — ignora os comandos do anfitrião",
"HCM_DIALOG_TITLE": "KoalaSync · O anfitrião controla esta sala",
"HCM_DIALOG_BODY": "Somente o anfitrião pode controlar a reprodução nesta sala. Continuar assistindo juntos ou assistir por conta própria?",
"HCM_DIALOG_STAY": "Manter sincronizado",
"HCM_DIALOG_SOLO": "Assistir por conta própria",
"HCM_BADGE_SOLO": "Assistindo por conta própria",
"HCM_BADGE_RESYNC": "Ressincronizar",
"NO_PEERS_CONNECTED": "Sem participantes conectados",
"BTN_LEAVE_ROOM": "Sair da sala",
"LABEL_SELECT_VIDEO": "Selecionar vídeo",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Gerar um novo ID interno e conectar novamente",
"REGEN_ID_DESC": "Use esta opção se aparecer o erro de 'Identidade duplicada'.",
"REGEN_ID_OTHER_ISSUE": "Outro problema? Abra um relatório no GitHub",
"TOAST_ID_REGENERATED": "Identidade regenerada — reconectando…",
"LABEL_CONN_STATUS": "Status da conexão",
"LABEL_CONN_STATUS_TOOLTIP": "Status atual da conexão WebSocket",
"CONN_STATUS_DISCONNECTED": "Desconectado",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Em breve",
"BTN_RESTART_TOUR": "Reiniciar tutorial",
"BTN_RESTART_TOUR_TOOLTIP": "Reiniciar o tutorial de introdução",
"HINT_SELECT_VIDEO": "Selecione seu vídeo aqui!"
"HINT_SELECT_VIDEO": "Selecione seu vídeo aqui!",
"OPTION_TITLE_PRIVACY_FULL": "Enviar título completo",
"OPTION_TITLE_PRIVACY_EPISODE": "Enviar ID do episódio",
"OPTION_TITLE_PRIVACY_HIDDEN": "Não enviar",
"LABEL_PRIVACY_SETTINGS": "Configurações de privacidade",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Escolha quais títulos de aba e mídia são enviados para a sala.",
"LABEL_SEND_TAB_TITLE": "Enviar título da aba",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Envia para a sala o título da aba selecionada do navegador.",
"LABEL_MEDIA_TITLE_PRIVACY": "Título da mídia",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Escolha como os títulos de mídia são enviados para a sala."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Partilhe este link com os amigos para que possam entrar",
"LABEL_PEERS_IN_ROOM": "Participantes na Sala",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Utilizadores atualmente ligados a esta sala",
"LABEL_HOST_CONTROL": "Controlo do anfitrião",
"LABEL_HOST_CONTROL_TOOLTIP": "Restringir quem pode controlar a reprodução nesta sala",
"LABEL_HOST_ONLY_TOGGLE": "Só eu posso controlar a reprodução",
"NOTICE_HOST_CONTROLS": "O anfitrião controla a reprodução para todos.",
"NOTICE_COHOST_HINT": "Dica: conceda o controlo a participantes específicos na lista abaixo.",
"BADGE_HOST": "Anfitrião",
"BADGE_GUEST": "Convidado",
"BADGE_CONTROLLER": "Controlador",
"BTN_GIVE_CONTROL": "Dar controlo",
"BTN_REVOKE_CONTROL": "Revogar",
"BADGE_DESYNCED": "Solo",
"TOOLTIP_PEER_DESYNCED": "A ver por conta própria — ignora os comandos do anfitrião",
"HCM_DIALOG_TITLE": "KoalaSync · O anfitrião controla esta sala",
"HCM_DIALOG_BODY": "Só o anfitrião pode controlar a reprodução nesta sala. Continuar a ver em conjunto ou ver por conta própria?",
"HCM_DIALOG_STAY": "Manter sincronizado",
"HCM_DIALOG_SOLO": "Ver por minha conta",
"HCM_BADGE_SOLO": "A ver por tua conta",
"HCM_BADGE_RESYNC": "Ressincronizar",
"NO_PEERS_CONNECTED": "Nenhum participante ligado",
"BTN_LEAVE_ROOM": "Sair da Sala",
"LABEL_SELECT_VIDEO": "Selecionar Vídeo",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Regerar o seu ID interno e voltar a ligar",
"REGEN_ID_DESC": "Use isto se encontrar erros de 'Identidade Duplicada'.",
"REGEN_ID_OTHER_ISSUE": "Outro problema? Abra um relatório no GitHub",
"TOAST_ID_REGENERATED": "Identidade regenerada — a voltar a ligar…",
"LABEL_CONN_STATUS": "Estado da Ligação",
"LABEL_CONN_STATUS_TOOLTIP": "Estado atual da ligação",
"CONN_STATUS_DISCONNECTED": "Desligado",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Em breve",
"BTN_RESTART_TOUR": "Reiniciar Tutorial",
"BTN_RESTART_TOUR_TOOLTIP": "Reiniciar o tutorial introdutório",
"HINT_SELECT_VIDEO": "Selecione o seu vídeo aqui!"
"HINT_SELECT_VIDEO": "Selecione o seu vídeo aqui!",
"OPTION_TITLE_PRIVACY_FULL": "Enviar título completo",
"OPTION_TITLE_PRIVACY_EPISODE": "Enviar ID do episódio",
"OPTION_TITLE_PRIVACY_HIDDEN": "Não enviar",
"LABEL_PRIVACY_SETTINGS": "Definições de privacidade",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Escolhe que títulos do separador e do conteúdo multimédia são enviados para a sala.",
"LABEL_SEND_TAB_TITLE": "Enviar título do separador",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Envia para a sala o título do separador do navegador selecionado.",
"LABEL_MEDIA_TITLE_PRIVACY": "Título multimédia",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Escolhe como os títulos multimédia são enviados para a sala."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Поделитесь этой ссылкой с друзьями, чтобы они вошли",
"LABEL_PEERS_IN_ROOM": "Участники в комнате",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Другие пользователи, подключенные к этой комнате",
"LABEL_HOST_CONTROL": "Управление ведущим",
"LABEL_HOST_CONTROL_TOOLTIP": "Ограничить, кто может управлять воспроизведением в этой комнате",
"LABEL_HOST_ONLY_TOGGLE": "Только я могу управлять воспроизведением",
"NOTICE_HOST_CONTROLS": "Ведущий управляет воспроизведением для всех.",
"NOTICE_COHOST_HINT": "Совет: предоставьте управление отдельным участникам в списке ниже.",
"BADGE_HOST": "Ведущий",
"BADGE_GUEST": "Гость",
"BADGE_CONTROLLER": "Управляющий",
"BTN_GIVE_CONTROL": "Дать управление",
"BTN_REVOKE_CONTROL": "Отозвать",
"BADGE_DESYNCED": "Соло",
"TOOLTIP_PEER_DESYNCED": "Смотрит отдельно — игнорирует команды ведущего",
"HCM_DIALOG_TITLE": "KoalaSync · Комнатой управляет ведущий",
"HCM_DIALOG_BODY": "Управлять воспроизведением в этой комнате может только ведущий. Продолжить смотреть вместе или смотреть самостоятельно?",
"HCM_DIALOG_STAY": "Оставаться синхронно",
"HCM_DIALOG_SOLO": "Смотреть самостоятельно",
"HCM_BADGE_SOLO": "Вы смотрите самостоятельно",
"HCM_BADGE_RESYNC": "Синхронизировать",
"NO_PEERS_CONNECTED": "Нет подключенных участников",
"BTN_LEAVE_ROOM": "Выйти из комнаты",
"LABEL_SELECT_VIDEO": "Выбрать видео",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Сбросить ваш внутренний идентификатор и переподключиться",
"REGEN_ID_DESC": "Используйте при появлении ошибок дублирования идентификации.",
"REGEN_ID_OTHER_ISSUE": "Другая проблема? Откройте Issue на GitHub",
"TOAST_ID_REGENERATED": "Идентичность перегенерирована — переподключение…",
"LABEL_CONN_STATUS": "Статус подключения",
"LABEL_CONN_STATUS_TOOLTIP": "Текущий статус WebSocket-соединения с сервером",
"CONN_STATUS_DISCONNECTED": "Отключено",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Скоро",
"BTN_RESTART_TOUR": "Перезапустить обучение",
"BTN_RESTART_TOUR_TOOLTIP": "Запустить приветственное руководство заново",
"HINT_SELECT_VIDEO": "Выберите ваше видео здесь!"
"HINT_SELECT_VIDEO": "Выберите ваше видео здесь!",
"OPTION_TITLE_PRIVACY_FULL": "Отправлять полное название",
"OPTION_TITLE_PRIVACY_EPISODE": "Отправлять ID эпизода",
"OPTION_TITLE_PRIVACY_HIDDEN": "Не отправлять",
"LABEL_PRIVACY_SETTINGS": "Настройки приватности",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Выберите, какие названия вкладки и медиа отправляются в комнату.",
"LABEL_SEND_TAB_TITLE": "Отправлять название вкладки",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Отправляет в комнату название выбранной вкладки браузера.",
"LABEL_MEDIA_TITLE_PRIVACY": "Название медиа",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Выберите, как названия медиа отправляются в комнату."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Katılabilmeleri için bu bağlantıyı arkadaşlarınızla paylaşın",
"LABEL_PEERS_IN_ROOM": "Odadaki Kişiler",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Şu anda bu odaya bağlı olan diğer kullanıcılar",
"LABEL_HOST_CONTROL": "Sunucu Kontrolü",
"LABEL_HOST_CONTROL_TOOLTIP": "Bu odada oynatmayı kimin kontrol edebileceğini belirleyin",
"LABEL_HOST_ONLY_TOGGLE": "Oynatmayı yalnızca ben kontrol edebilirim",
"NOTICE_HOST_CONTROLS": "Oynatmayı herkes için sunucu kontrol eder.",
"NOTICE_COHOST_HINT": "İpucu: aşağıdaki katılımcı listesinde tek tek katılımcılara kontrol verin.",
"BADGE_HOST": "Sunucu",
"BADGE_GUEST": "Misafir",
"BADGE_CONTROLLER": "Kontrolcü",
"BTN_GIVE_CONTROL": "Kontrol ver",
"BTN_REVOKE_CONTROL": "Geri al",
"BADGE_DESYNCED": "Solo",
"TOOLTIP_PEER_DESYNCED": "Tek başına izliyor — sunucu komutlarını yok sayar",
"HCM_DIALOG_TITLE": "KoalaSync · Bu odayı sunucu kontrol ediyor",
"HCM_DIALOG_BODY": "Bu odada oynatmayı yalnızca sunucu kontrol edebilir. Birlikte izlemeye devam mı, yoksa kendi başına mı izlemek istiyorsun?",
"HCM_DIALOG_STAY": "Senkron kal",
"HCM_DIALOG_SOLO": "Kendi başıma izle",
"HCM_BADGE_SOLO": "Kendi başına izliyorsun",
"HCM_BADGE_RESYNC": "Yeniden senkronize et",
"NO_PEERS_CONNECTED": "Bağlı kimse yok",
"BTN_LEAVE_ROOM": "Odadan Ayrıl",
"LABEL_SELECT_VIDEO": "Video Seç",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Dahili kimliğinizi yeniden oluşturun ve tekrar bağlanın",
"REGEN_ID_DESC": "\"Duplicate Identity\" (Mükerrer Kimlik) hataları görüyorsanız bunu kullanın.",
"REGEN_ID_OTHER_ISSUE": "Başka bir sorun? GitHub Issue açın",
"TOAST_ID_REGENERATED": "Kimlik yeniden oluşturuldu — yeniden bağlanılıyor…",
"LABEL_CONN_STATUS": "Bağlantı Durumu",
"LABEL_CONN_STATUS_TOOLTIP": "Mevcut WebSocket bağlantı durumu",
"CONN_STATUS_DISCONNECTED": "Bağlantı Kesildi",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Çok yakında",
"BTN_RESTART_TOUR": "Eğitimi Yeniden Başlat",
"BTN_RESTART_TOUR_TOOLTIP": "Tanıtım eğitimini yeniden başlat",
"HINT_SELECT_VIDEO": "Videonuzu buradan seçin!"
"HINT_SELECT_VIDEO": "Videonuzu buradan seçin!",
"OPTION_TITLE_PRIVACY_FULL": "Tam başlığı gönder",
"OPTION_TITLE_PRIVACY_EPISODE": "Bölüm IDsini gönder",
"OPTION_TITLE_PRIVACY_HIDDEN": "Gönderme",
"LABEL_PRIVACY_SETTINGS": "Gizlilik ayarları",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Odaya hangi sekme ve medya başlıklarının gönderileceğini seçin.",
"LABEL_SEND_TAB_TITLE": "Sekme başlığını gönder",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Seçili tarayıcı sekmesinin başlığını odaya gönderir.",
"LABEL_MEDIA_TITLE_PRIVACY": "Medya başlığı",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Medya başlıklarının odaya nasıl gönderileceğini seçin."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "Поділіться цим посиланням з друзями, щоб вони могли приєднатися",
"LABEL_PEERS_IN_ROOM": "Учасники в кімнаті",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "Інші користувачі зараз підключені до цієї кімнати",
"LABEL_HOST_CONTROL": "Керування ведучим",
"LABEL_HOST_CONTROL_TOOLTIP": "Обмежити, хто може керувати відтворенням у цій кімнаті",
"LABEL_HOST_ONLY_TOGGLE": "Лише я можу керувати відтворенням",
"NOTICE_HOST_CONTROLS": "Ведучий керує відтворенням для всіх.",
"NOTICE_COHOST_HINT": "Порада: надайте керування окремим учасникам у списку нижче.",
"BADGE_HOST": "Ведучий",
"BADGE_GUEST": "Гість",
"BADGE_CONTROLLER": "Керуючий",
"BTN_GIVE_CONTROL": "Дати керування",
"BTN_REVOKE_CONTROL": "Відкликати",
"BADGE_DESYNCED": "Соло",
"TOOLTIP_PEER_DESYNCED": "Дивиться окремо — ігнорує команди ведучого",
"HCM_DIALOG_TITLE": "KoalaSync · Кімнатою керує ведучий",
"HCM_DIALOG_BODY": "Керувати відтворенням у цій кімнаті може лише ведучий. Продовжити дивитися разом чи дивитися самостійно?",
"HCM_DIALOG_STAY": "Залишатися синхронно",
"HCM_DIALOG_SOLO": "Дивитися самостійно",
"HCM_BADGE_SOLO": "Ви дивитеся самостійно",
"HCM_BADGE_RESYNC": "Синхронізувати",
"NO_PEERS_CONNECTED": "Немає підключених однорангових пристроїв",
"BTN_LEAVE_ROOM": "Вийти з кімнати",
"LABEL_SELECT_VIDEO": "Виберіть Відео",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "Повторно згенеруйте свій внутрішній ідентифікатор і повторно підключіться",
"REGEN_ID_DESC": "Використовуйте це, якщо ви бачите помилку \"Дублікати ідентифікатора\".",
"REGEN_ID_OTHER_ISSUE": "Інша проблема? Відкрийте випуск GitHub",
"TOAST_ID_REGENERATED": "Ідентичність перегенеровано — повторне підключення…",
"LABEL_CONN_STATUS": "Статус підключення",
"LABEL_CONN_STATUS_TOOLTIP": "Поточний стан підключення WebSocket",
"CONN_STATUS_DISCONNECTED": "Відключено",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "Скоро буде",
"BTN_RESTART_TOUR": "Перезапустіть підручник",
"BTN_RESTART_TOUR_TOOLTIP": "Перезапустіть навчальний посібник із адаптації",
"HINT_SELECT_VIDEO": "Виберіть своє відео тут!"
"HINT_SELECT_VIDEO": "Виберіть своє відео тут!",
"OPTION_TITLE_PRIVACY_FULL": "Надсилати повну назву",
"OPTION_TITLE_PRIVACY_EPISODE": "Надсилати ID епізоду",
"OPTION_TITLE_PRIVACY_HIDDEN": "Не надсилати",
"LABEL_PRIVACY_SETTINGS": "Налаштування приватності",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "Виберіть, які назви вкладки й медіа надсилати до кімнати.",
"LABEL_SEND_TAB_TITLE": "Надсилати назву вкладки",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "Надсилає до кімнати назву вибраної вкладки браузера.",
"LABEL_MEDIA_TITLE_PRIVACY": "Назва медіа",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Виберіть, як назви медіа надсилати до кімнати."
}
+29 -1
View File
@@ -44,6 +44,24 @@
"LABEL_INVITE_LINK_TOOLTIP": "与朋友分享此链接,以便他们可以加入",
"LABEL_PEERS_IN_ROOM": "房间里的同伴",
"LABEL_PEERS_IN_ROOM_TOOLTIP": "当前连接到该房间的其他用户",
"LABEL_HOST_CONTROL": "主持人控制",
"LABEL_HOST_CONTROL_TOOLTIP": "限制谁可以在此房间中控制播放",
"LABEL_HOST_ONLY_TOGGLE": "仅我可以控制播放",
"NOTICE_HOST_CONTROLS": "由主持人为所有人控制播放。",
"NOTICE_COHOST_HINT": "提示:在下方的参与者列表中将控制权授予单个参与者。",
"BADGE_HOST": "主持人",
"BADGE_GUEST": "访客",
"BADGE_CONTROLLER": "控制者",
"BTN_GIVE_CONTROL": "授予控制权",
"BTN_REVOKE_CONTROL": "撤销",
"BADGE_DESYNCED": "单独",
"TOOLTIP_PEER_DESYNCED": "单独观看中 — 忽略主持人的命令",
"HCM_DIALOG_TITLE": "KoalaSync · 主持人控制本房间",
"HCM_DIALOG_BODY": "本房间只有主持人可以控制播放。继续一起观看,还是自己单独观看?",
"HCM_DIALOG_STAY": "保持同步",
"HCM_DIALOG_SOLO": "自己单独观看",
"HCM_BADGE_SOLO": "正在单独观看",
"HCM_BADGE_RESYNC": "重新同步",
"NO_PEERS_CONNECTED": "没有对等点连接",
"BTN_LEAVE_ROOM": "离开房间",
"LABEL_SELECT_VIDEO": "选择视频",
@@ -92,6 +110,7 @@
"BTN_REGEN_ID_TOOLTIP": "重新生成您的内部 ID 并重新连接",
"REGEN_ID_DESC": "如果您看到“重复身份”错误,请使用此选项。",
"REGEN_ID_OTHER_ISSUE": "其他问题?打开 GitHub 问题",
"TOAST_ID_REGENERATED": "身份已重新生成 — 正在重新连接…",
"LABEL_CONN_STATUS": "连接状态",
"LABEL_CONN_STATUS_TOOLTIP": "当前WebSocket连接状态",
"CONN_STATUS_DISCONNECTED": "已断开连接",
@@ -210,5 +229,14 @@
"AUDIO_COMING_SOON": "即将推出",
"BTN_RESTART_TOUR": "重启教程",
"BTN_RESTART_TOUR_TOOLTIP": "重新启动入门教程",
"HINT_SELECT_VIDEO": "在这里选择您的视频!"
"HINT_SELECT_VIDEO": "在这里选择您的视频!",
"OPTION_TITLE_PRIVACY_FULL": "发送完整标题",
"OPTION_TITLE_PRIVACY_EPISODE": "发送剧集 ID",
"OPTION_TITLE_PRIVACY_HIDDEN": "不发送",
"LABEL_PRIVACY_SETTINGS": "隐私设置",
"LABEL_PRIVACY_SETTINGS_TOOLTIP": "选择要发送到房间的标签页标题和媒体标题。",
"LABEL_SEND_TAB_TITLE": "发送标签页标题",
"LABEL_SEND_TAB_TITLE_TOOLTIP": "将所选浏览器标签页的标题发送到房间。",
"LABEL_MEDIA_TITLE_PRIVACY": "媒体标题",
"LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "选择媒体标题发送到房间的方式。"
}
+1 -1
View File
@@ -2,7 +2,7 @@
"manifest_version": 3,
"default_locale": "en",
"name": "KoalaSync",
"version": "2.4.6",
"version": "2.5.3",
"description": "Synchronize video playback on YouTube, Netflix, Emby, Jellyfin, and any HTML5 site in real-time with friends.",
"permissions": [
"storage",
+2 -4
View File
@@ -12,6 +12,7 @@ export function initTabManager({
getSettings,
emit,
applyAudioSettingsToTab,
injectContentScript,
ensureState,
EVENTS
}) {
@@ -83,10 +84,7 @@ export function initTabManager({
await ensureState();
const curTabId = getCurrentTabId();
if (curTabId && tabId === parseInt(curTabId) && changeInfo.status === 'complete') {
chrome.scripting.executeScript({
target: { tabId },
files: ['content.js']
})
injectContentScript(tabId)
.then(() => applyAudioSettingsToTab(tabId))
.catch(() => {});
}
+36
View File
@@ -0,0 +1,36 @@
(function(root) {
const PAGE_API_SEEK_FIXES = [
{
name: 'netflix-page-api-seek',
urls: ['netflix.com'],
provider: 'netflix'
},
{
name: 'disney-page-api-seek',
urls: ['disneyplus.com'],
provider: 'disney'
}
];
function normalizeHost(input) {
try {
return new URL(input).hostname.toLowerCase();
} catch (_e) {
return String(input || '').toLowerCase();
}
}
function matchesDomain(host, domain) {
const normalizedDomain = normalizeHost(domain);
return normalizedDomain && (host === normalizedDomain || host.endsWith(`.${normalizedDomain}`));
}
root.KOALA_PAGE_API_SEEK_FIXES = PAGE_API_SEEK_FIXES;
root.KOALA_PAGE_API_SEEK_PROVIDERS = PAGE_API_SEEK_FIXES;
root.koalaFindPageApiSeekProvider = (input) => {
const host = normalizeHost(input);
return PAGE_API_SEEK_FIXES.find(entry =>
Array.isArray(entry.urls) && entry.urls.some(url => matchesDomain(host, url))
) || null;
};
})(globalThis);
+58 -6
View File
@@ -391,6 +391,7 @@
<button class="tab-btn" data-tab="tab-sync" data-i18n="TAB_SYNC" data-i18n-title="TAB_SYNC_TOOLTIP" title="Video sync controls and remote actions">Sync</button>
<button class="tab-btn" data-tab="tab-settings" data-i18n="TAB_SETTINGS" data-i18n-title="TAB_SETTINGS_TOOLTIP" title="Extension preferences">Settings</button>
<button class="tab-btn" data-tab="tab-dev" data-i18n="TAB_STATUS" data-i18n-title="TAB_STATUS_TOOLTIP" title="Advanced Diagnostics & Logs">Status</button>
<button id="devToolsTabBtn" class="tab-btn" data-tab="tab-devtools" style="display:none;">Dev</button>
</div>
<!-- Room Tab -->
@@ -402,14 +403,18 @@
<details style="margin-bottom: 12px; background: var(--card); border: 1px solid #334155; border-radius: 8px; padding: 8px;">
<summary style="font-size: 11px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; cursor: pointer; outline: none;" data-i18n="MANUAL_CONNECT_HEADER">Manual Connect / Advanced</summary>
<div style="margin-top: 12px;">
<div class="form-group">
<label title="Select which KoalaSync server to use" data-i18n="LABEL_SERVER">Server</label>
<div style="display:flex; gap:4px; margin-bottom:8px;">
<button id="serverOfficial" class="tab-btn active" style="flex:1; padding:6px; font-size:11px;" data-i18n="BTN_SERVER_OFFICIAL" data-i18n-title="BTN_SERVER_OFFICIAL_TOOLTIP" title="Use the official reliable server">Official</button>
<button id="serverCustom" class="tab-btn" style="flex:1; padding:6px; font-size:11px;" data-i18n="BTN_SERVER_CUSTOM" data-i18n-title="BTN_SERVER_CUSTOM_TOOLTIP" title="Connect to your own self-hosted server">Custom</button>
<!-- Server selection — its own concern, kept visually separate from the room fields. -->
<div style="background: var(--bg); border: 1px solid #334155; border-radius: 8px; padding: 10px; margin-bottom: 14px;">
<div class="form-group" style="margin-bottom:0;">
<label title="Select which KoalaSync server to use" data-i18n="LABEL_SERVER">Server</label>
<div style="display:flex; gap:4px; margin-bottom:0;">
<button id="serverOfficial" class="tab-btn active" style="flex:1; padding:6px; font-size:11px;" data-i18n="BTN_SERVER_OFFICIAL" data-i18n-title="BTN_SERVER_OFFICIAL_TOOLTIP" title="Use the official reliable server">Official</button>
<button id="serverCustom" class="tab-btn" style="flex:1; padding:6px; font-size:11px;" data-i18n="BTN_SERVER_CUSTOM" data-i18n-title="BTN_SERVER_CUSTOM_TOOLTIP" title="Connect to your own self-hosted server">Custom</button>
</div>
<input type="text" id="serverUrl" data-i18n-placeholder="PLACEHOLDER_SERVER_URL" placeholder="wss://your-server:3000" style="display:none; margin-top:8px;">
</div>
<input type="text" id="serverUrl" data-i18n-placeholder="PLACEHOLDER_SERVER_URL" placeholder="wss://your-server:3000" style="display:none;">
</div>
<!-- Room credentials — distinct group below the server picker. -->
<div class="form-group">
<label title="The unique identifier for your sync room" data-i18n="LABEL_ROOM_ID" data-i18n-title="LABEL_ROOM_ID_TOOLTIP">Room ID</label>
<input type="text" id="roomId" data-i18n-placeholder="PLACEHOLDER_ROOM_ID" data-i18n-title="PLACEHOLDER_ROOM_ID_TOOLTIP" placeholder="Enter Room ID" title="The unique ID of the room you want to join">
@@ -450,6 +455,22 @@
</div>
</div>
<div id="hostControlCard" class="info-card" style="margin-bottom: 20px; display:none;">
<div style="display:flex; justify-content:space-between; align-items:center;">
<label style="margin:0;" title="Restrict who can control playback in this room" data-i18n="LABEL_HOST_CONTROL" data-i18n-title="LABEL_HOST_CONTROL_TOOLTIP">Host Control</label>
<span id="hostRoleBadge" style="font-size:10px; font-weight:700; text-transform:uppercase; padding:2px 8px; border-radius:8px; background:var(--accent); color:#fff;"></span>
</div>
<div id="hostControlToggleRow" style="display:none; align-items:center; justify-content:space-between; gap:12px; margin-top:12px;">
<span style="font-size:12px; color:var(--text); line-height:1.3;" data-i18n="LABEL_HOST_ONLY_TOGGLE">Only I can control playback</span>
<label class="toggle-switch" style="flex:none;">
<input type="checkbox" id="hostControlToggle">
<span class="slider"></span>
</label>
</div>
<div id="hostControlCohostHint" style="display:none; margin-top:10px; padding-top:10px; border-top:1px solid #334155; font-size:11px; color:var(--text-muted); line-height:1.4;" data-i18n="NOTICE_COHOST_HINT">Tip: grant control to individual viewers in the participant list below.</div>
<div id="hostControlGuestNote" style="display:none; margin-top:10px; font-size:12px; color:var(--text-muted);" data-i18n="NOTICE_HOST_CONTROLS">The host controls playback for everyone.</div>
</div>
<div style="margin-bottom: 20px;">
<label title="Other users currently connected to this room" data-i18n="LABEL_PEERS_IN_ROOM" data-i18n-title="LABEL_PEERS_IN_ROOM_TOOLTIP">Peers in Room</label>
<div id="peerList" class="info-card">
@@ -530,6 +551,25 @@
<input type="text" id="username" data-i18n-placeholder="PLACEHOLDER_USERNAME" placeholder="Anonymous Koala" maxlength="20" style="width: 150px;">
</div>
<details class="form-group" style="background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid #334155;">
<summary style="cursor: pointer; font-size: 12px; font-weight: 700; color: var(--text);" title="Choose which titles are sent to the room." data-i18n="LABEL_PRIVACY_SETTINGS" data-i18n-title="LABEL_PRIVACY_SETTINGS_TOOLTIP">Privacy Settings</summary>
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px;">
<label style="margin-bottom: 0; cursor: help;" title="Send the selected browser tab title to the room." data-i18n="LABEL_SEND_TAB_TITLE" data-i18n-title="LABEL_SEND_TAB_TITLE_TOOLTIP">Send tab title</label>
<label class="toggle-switch">
<input type="checkbox" id="sendTabTitle" checked>
<span class="slider"></span>
</label>
</div>
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px;">
<label style="margin-bottom: 0; cursor: help;" title="Choose how media titles are sent to the room." data-i18n="LABEL_MEDIA_TITLE_PRIVACY" data-i18n-title="LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP">Media title</label>
<select id="mediaTitlePrivacyMode" style="width: 165px; background: var(--bg); border: 1px solid #334155; color: white; padding: 6px 10px; border-radius: 8px; font-size: 12px; cursor: pointer; outline: none; font-family: inherit;">
<option value="full" data-i18n="OPTION_TITLE_PRIVACY_FULL">Send full title</option>
<option value="episode" data-i18n="OPTION_TITLE_PRIVACY_EPISODE">Send episode ID</option>
<option value="hidden" data-i18n="OPTION_TITLE_PRIVACY_HIDDEN">Do not send</option>
</select>
</div>
</details>
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid #334155;">
<label style="margin-bottom: 0; cursor: help;" title="Filters out non-video tabs and unrelated domains to keep the list clean" data-i18n="LABEL_HIDE_CLUTTER" data-i18n-title="LABEL_HIDE_CLUTTER_TOOLTIP">Hide Clutter Tabs</label>
<label class="toggle-switch">
@@ -653,6 +693,18 @@
</div>
</div>
<div id="tab-devtools" class="tab-content">
<label>Remote Seek</label>
<div class="info-card" style="display:flex; gap:8px; margin-bottom:15px;">
<button id="remoteSeekBack" class="secondary" style="flex:1; font-size:12px;">-30s</button>
<button id="remoteSeekForward" class="secondary" style="flex:1; font-size:12px;">+30s</button>
</div>
<button id="remoteSeekFiveMin" class="secondary" style="width:100%; font-size:12px; margin-bottom:15px;">Seek 5:00</button>
<div style="font-size:10px; color:var(--text-muted); text-align:center; margin-top:10px; opacity:0.7;">
Build: <span id="buildIdentifier">__BUILD_TIMESTAMP__</span>
</div>
</div>
<script src="popup.js" type="module"></script>
<!-- Onboarding Overlay -->
+329 -34
View File
@@ -2,6 +2,7 @@ import { EVENTS, OFFICIAL_LANDING_PAGE_URL, SUPPORT_URL, getReviewUrl } from './
import { BLACKLIST_DOMAINS } from './shared/blacklist.js';
import { getAvatarForName, generateUsername, USERNAME_ADJECTIVES, USERNAME_NOUNS } from './shared/names.js';
import { loadLocale, translateDOM, getMessage, getSystemLanguage } from './i18n.js';
import { TITLE_PRIVACY_MODES, normalizeSendTabTitle, normalizeTabTitle } from './title-privacy.js';
const elements = {
@@ -41,6 +42,12 @@ const elements = {
retryBtn: document.getElementById('retryBtn'),
sectionJoin: document.getElementById('section-join'),
sectionActive: document.getElementById('section-active'),
hostControlCard: document.getElementById('hostControlCard'),
hostRoleBadge: document.getElementById('hostRoleBadge'),
hostControlToggleRow: document.getElementById('hostControlToggleRow'),
hostControlToggle: document.getElementById('hostControlToggle'),
hostControlGuestNote: document.getElementById('hostControlGuestNote'),
hostControlCohostHint: document.getElementById('hostControlCohostHint'),
activeRoomId: document.getElementById('activeRoomId'),
activeServer: document.getElementById('activeServer'),
peerListSync: document.getElementById('peerListSync'),
@@ -48,6 +55,8 @@ const elements = {
playBtn: document.getElementById('playBtn'),
pauseBtn: document.getElementById('pauseBtn'),
autoSyncNextEpisode: document.getElementById('autoSyncNextEpisode'),
sendTabTitle: document.getElementById('sendTabTitle'),
mediaTitlePrivacyMode: document.getElementById('mediaTitlePrivacyMode'),
episodeLobbyCard: document.getElementById('episodeLobbyCard'),
lobbyTitle: document.getElementById('lobbyTitle'),
lobbyPeerStatus: document.getElementById('lobbyPeerStatus'),
@@ -62,7 +71,11 @@ const elements = {
settingsVersion: document.getElementById('settingsVersion'),
devSupportLink: document.getElementById('devSupportLink'),
devReviewLink: document.getElementById('devReviewLink'),
syncTabCopyInvite: document.getElementById('syncTabCopyInvite')
syncTabCopyInvite: document.getElementById('syncTabCopyInvite'),
devToolsTabBtn: document.getElementById('devToolsTabBtn'),
remoteSeekBack: document.getElementById('remoteSeekBack'),
remoteSeekForward: document.getElementById('remoteSeekForward'),
remoteSeekFiveMin: document.getElementById('remoteSeekFiveMin')
};
let localPeerId = null;
@@ -79,6 +92,19 @@ let errorToken = 0;
let forceSyncDone = false;
let connectionErrorTimer = null;
let pendingConnectionErrorMsg = null;
function devToolsEnabled() {
return elements.username && elements.username.value.trim() === 'KoalaDev';
}
function syncDevToolsVisibility() {
if (!elements.devToolsTabBtn) return;
const enabled = devToolsEnabled();
elements.devToolsTabBtn.style.display = enabled ? '' : 'none';
if (!enabled && document.getElementById('tab-devtools')?.classList.contains('active')) {
document.querySelector('.tab-btn[data-tab="tab-settings"]')?.click();
}
}
let roomListRefreshTimer = null;
let roomListRefreshInterval = null;
const ROOM_LIST_REFRESH_COOLDOWN_MS = 11000;
@@ -178,7 +204,7 @@ function setRoomRefreshCooldown() {
async function init() {
// Local-only by design — settings and room credentials never come from
// storage.sync (only onboardingComplete + dismissedHints live there).
const localData = await chrome.storage.local.get(['serverUrl', 'useCustomServer', 'roomId', 'password', 'username', 'filterNoise', 'autoSyncNextEpisode', 'forceSyncMode', 'browserNotifications', 'autoCopyInvite', 'locale', 'audioSettings', 'activeTab']);
const localData = await chrome.storage.local.get(['serverUrl', 'useCustomServer', 'roomId', 'password', 'username', 'filterNoise', 'autoSyncNextEpisode', 'sendTabTitle', 'mediaTitlePrivacyMode', 'titlePrivacyMode', 'forceSyncMode', 'browserNotifications', 'autoCopyInvite', 'locale', 'audioSettings', 'activeTab']);
let activeLang = localData.locale;
if (!activeLang) {
@@ -201,8 +227,13 @@ async function init() {
elements.roomId.value = localData.roomId || '';
elements.password.value = localData.password || '';
elements.username.value = username;
syncDevToolsVisibility();
if (elements.filterNoise) elements.filterNoise.checked = localData.filterNoise !== false;
if (elements.autoSyncNextEpisode) elements.autoSyncNextEpisode.checked = localData.autoSyncNextEpisode !== false;
const legacyTitlePrivacyMode = Object.values(TITLE_PRIVACY_MODES).includes(localData.titlePrivacyMode) ? localData.titlePrivacyMode : TITLE_PRIVACY_MODES.FULL;
const mediaTitlePrivacyMode = Object.values(TITLE_PRIVACY_MODES).includes(localData.mediaTitlePrivacyMode) ? localData.mediaTitlePrivacyMode : legacyTitlePrivacyMode;
if (elements.sendTabTitle) elements.sendTabTitle.checked = normalizeSendTabTitle(localData.sendTabTitle, legacyTitlePrivacyMode);
if (elements.mediaTitlePrivacyMode) elements.mediaTitlePrivacyMode.value = mediaTitlePrivacyMode;
if (elements.forceSyncMode) elements.forceSyncMode.value = localData.forceSyncMode || 'jump-to-others';
if (elements.browserNotifications) elements.browserNotifications.checked = localData.browserNotifications === true;
if (elements.autoCopyInvite) elements.autoCopyInvite.checked = localData.autoCopyInvite !== false;
@@ -244,6 +275,7 @@ async function init() {
updatePingDisplay(res.ping);
updatePeerList(res.peers);
lastKnownPeers = res.peers || [];
updateHostControlUI({ controlMode: res.controlMode, amHost: res.amHost, amController: res.amController, controllers: res.controllers, hostPeerId: res.hostPeerId, hostControlSupported: res.hostControlSupported, coHostSupported: res.coHostSupported, inRoom: res.status === 'connected' });
if (res.lastActionState) updateLastActionUI(res.lastActionState, res.peers);
// If user has a room configured but background is not connected (disconnected or idle),
@@ -263,13 +295,13 @@ async function init() {
const syncTabBtn = document.querySelector('.tab-btn[data-tab="tab-sync"]');
if (syncTabBtn) syncTabBtn.click();
showSelectVideoHint();
} else if (localData.activeTab) {
} else if (localData.activeTab && (localData.activeTab !== 'tab-devtools' || devToolsEnabled())) {
const btn = document.querySelector(`.tab-btn[data-tab="${localData.activeTab}"]`);
if (btn) btn.click();
}
} else {
await populateTabs();
if (localData.activeTab) {
if (localData.activeTab && (localData.activeTab !== 'tab-devtools' || devToolsEnabled())) {
const btn = document.querySelector(`.tab-btn[data-tab="${localData.activeTab}"]`);
if (btn) btn.click();
}
@@ -303,6 +335,98 @@ function toggleUIState(inRoom) {
if (syncInactive) syncInactive.style.display = inRoom ? 'none' : 'block';
}
// --- Host Control Mode UI ---
// True when we're a guest in a host-only room → remote-control buttons are locked.
let hcmGuestLocked = false;
// Co-Host state mirrored for the peer-list renderer (promote/demote + role badges).
let hcmAmOwner = false;
let hcmControllers = [];
let hcmCoHostSupported = false;
let hcmOwnerPeerId = null;
let hcmRoomIsHostOnly = false;
function updateHostControlUI(state) {
const s = state || {};
const controlMode = s.controlMode;
const amHost = !!s.amHost;
const amController = !!s.amController;
const hostOnly = controlMode === 'host-only';
// Stash for the peer-list renderer, then force it to re-render with new roles.
hcmAmOwner = amHost;
hcmControllers = Array.isArray(s.controllers) ? s.controllers : [];
hcmCoHostSupported = !!s.coHostSupported;
hcmOwnerPeerId = s.hostPeerId || null;
hcmRoomIsHostOnly = hostOnly;
lastPeersJson = '';
if (activePeers) updatePeerList(activePeers);
const card = elements.hostControlCard;
if (!card) return;
// Explicit capability advertised by the relay. Against an older relay it's
// false/absent → feature unavailable, so hide the card instead of misleading UI.
const serverSupportsHostControl = !!s.hostControlSupported;
// Show the card only when meaningful: owner always (to manage); a non-controller
// guest only while host-only is active. A guest in a normal room sees nothing.
const show = s.inRoom && serverSupportsHostControl && (amHost || hostOnly);
if (!show) {
card.style.display = 'none';
hcmGuestLocked = false;
setRemoteControlsLocked(false);
return;
}
card.style.display = 'block';
if (elements.hostRoleBadge) {
const role = amHost ? (getMessage('BADGE_HOST') || 'Host')
: amController ? (getMessage('BADGE_CONTROLLER') || 'Controller')
: (getMessage('BADGE_GUEST') || 'Guest');
elements.hostRoleBadge.textContent = role;
elements.hostRoleBadge.style.background = (amHost || amController) ? 'var(--accent)' : 'var(--text-muted)';
}
if (elements.hostControlToggleRow) elements.hostControlToggleRow.style.display = amHost ? 'flex' : 'none';
if (elements.hostControlToggle) elements.hostControlToggle.checked = hostOnly;
if (elements.hostControlGuestNote) elements.hostControlGuestNote.style.display = (!amController && hostOnly) ? 'block' : 'none';
// Owner-only discoverability hint: co-hosting is granted down in the peer list,
// which isn't obvious from this card. Only show it when it's actionable — owner,
// relay supports co-host, and the room is locked to host-only (the buttons only
// render in that mode).
if (elements.hostControlCohostHint) elements.hostControlCohostHint.style.display = (amHost && !!s.coHostSupported && hostOnly) ? 'block' : 'none';
// Only a non-controller is locked out of the remote controls.
hcmGuestLocked = (!amController && hostOnly);
setRemoteControlsLocked(hcmGuestLocked);
}
function setRemoteControlsLocked(locked) {
[elements.playBtn, elements.pauseBtn, elements.forceSyncBtn].forEach(btn => {
if (!btn) return;
btn.disabled = locked;
btn.style.opacity = locked ? '0.5' : '';
btn.style.cursor = locked ? 'not-allowed' : '';
btn.title = locked ? (getMessage('NOTICE_HOST_CONTROLS') || 'The host controls playback for everyone.') : '';
});
// Always restore the default labels. The action handlers leave the text in a
// transitional state ("Playing..." / "Pausing...") and the 2.5s safety reset
// skips the refresh while guest-locked — so reset on BOTH transitions: on lock
// (host enabled host-only just as the guest clicked → don't freeze "Playing...")
// and on unlock (L-1).
if (elements.playBtn) elements.playBtn.textContent = getMessage('BTN_PLAY') || 'Play';
if (elements.pauseBtn) elements.pauseBtn.textContent = getMessage('BTN_PAUSE') || 'Pause';
}
if (elements.hostControlToggle) {
elements.hostControlToggle.addEventListener('change', () => {
const mode = elements.hostControlToggle.checked ? 'host-only' : 'everyone';
chrome.runtime.sendMessage({ type: 'SET_CONTROL_MODE', controlMode: mode }, (res) => {
// Server broadcasts CONTROL_MODE back to refresh the UI; revert on failure.
if (chrome.runtime.lastError || !res || res.status !== 'ok') {
elements.hostControlToggle.checked = (mode === 'everyone');
}
});
});
}
function updateUI(roomId, password, useCustomServer = false, serverUrl = '') {
const inRoom = !!roomId;
toggleUIState(inRoom);
@@ -577,6 +701,11 @@ function updatePeerList(peers) {
header.appendChild(nameSpan);
// Right-side badges + actions, kept in one group so they sit together
// on the right instead of being scattered by the header's space-between.
const rightGroup = document.createElement('div');
rightGroup.style.cssText = 'display:flex; align-items:center; gap:6px; flex-shrink:0;';
// Volume Icon (Top Right)
if (p.volume !== undefined && p.volume !== null) {
const volIcon = document.createElement('div');
@@ -590,9 +719,57 @@ function updatePeerList(peers) {
const you = document.createElement('span');
you.style.cssText = 'font-size:10px; color:var(--accent); font-weight:bold;';
you.textContent = getMessage('LABEL_YOU') || 'YOU';
header.appendChild(you);
rightGroup.appendChild(you);
}
// Host Control Mode: show "Solo" badge for peers watching on their own.
if (typeof p === 'object' && p.desynced) {
const solo = document.createElement('span');
solo.style.cssText = 'font-size:10px; color:#fff; background:#b45309; padding:2px 6px; border-radius:6px; font-weight:600;';
const soloText = getMessage('BADGE_DESYNCED') || 'Solo';
solo.textContent = soloText;
solo.title = getMessage('TOOLTIP_PEER_DESYNCED') || soloText;
rightGroup.appendChild(solo);
}
// Co-Host: role badges + the owner's promote/demote control. Only meaningful
// once the room is locked to host-only — in 'everyone' mode anyone can already
// control, so roles are moot and we show nothing (no badge/button noise).
// The promote/demote button additionally requires owner privileges (below).
const showRoles = !!hcmOwnerPeerId && hcmRoomIsHostOnly;
if (showRoles) {
const isOwner = pId === hcmOwnerPeerId;
const isController = !isOwner && hcmControllers.includes(pId);
if (isOwner || isController) {
const roleBadge = document.createElement('span');
roleBadge.style.cssText = 'font-size:10px; color:#fff; background:var(--accent); padding:2px 6px; border-radius:6px; font-weight:600;';
roleBadge.textContent = isOwner ? (getMessage('BADGE_HOST') || 'Host') : (getMessage('BADGE_CONTROLLER') || 'Controller');
rightGroup.appendChild(roleBadge);
}
// Owner can promote/demote any non-owner peer.
if (hcmAmOwner && hcmCoHostSupported && !isOwner) {
const btn = document.createElement('button');
const revoke = isController;
btn.style.cssText = `font-size:10px; padding:3px 9px; border-radius:6px; cursor:pointer; white-space:nowrap; font-weight:600; transition:background .15s,color .15s; border:1px solid ${revoke ? 'var(--text-muted)' : 'var(--accent)'}; background:transparent; color:${revoke ? 'var(--text-muted)' : 'var(--accent)'};`;
btn.textContent = revoke ? (getMessage('BTN_REVOKE_CONTROL') || 'Revoke') : (getMessage('BTN_GIVE_CONTROL') || 'Give control');
btn.title = revoke ? (getMessage('BTN_REVOKE_CONTROL') || 'Revoke') : (getMessage('BTN_GIVE_CONTROL') || 'Give control');
btn.addEventListener('mouseenter', () => {
btn.style.background = revoke ? 'var(--text-muted)' : 'var(--accent)';
btn.style.color = '#fff';
});
btn.addEventListener('mouseleave', () => {
btn.style.background = 'transparent';
btn.style.color = revoke ? 'var(--text-muted)' : 'var(--accent)';
});
btn.addEventListener('click', () => {
chrome.runtime.sendMessage({ type: 'SET_PEER_ROLE', peerId: pId, controller: !isController }, () => {});
});
rightGroup.appendChild(btn);
}
}
if (rightGroup.childNodes.length) header.appendChild(rightGroup);
peerItem.appendChild(header);
// Media Info
@@ -744,7 +921,7 @@ async function populateTabs(providedPeers = null, providedTargetTabId = null) {
// Smart Matching Logic — exclude own tabTitle to prevent self-match (computed once)
const cleanTitle = (rawTitle) => {
if (!rawTitle) return '';
return rawTitle
return (normalizeTabTitle(rawTitle) || '')
.replace(/(?:\s*[-\|•]\s*(?:YouTube|Twitch|Jellyfin|Emby|Netflix|Vimeo|Dailymotion).*)$/i, '')
.replace(/^(?:Netflix|Twitch|YouTube|Emby|Jellyfin)\s*[-\|•]\s*/i, '')
.trim();
@@ -1070,6 +1247,22 @@ elements.autoSyncNextEpisode.addEventListener('change', () => {
chrome.storage.local.set({ autoSyncNextEpisode: elements.autoSyncNextEpisode.checked });
});
if (elements.sendTabTitle) {
elements.sendTabTitle.addEventListener('change', () => {
chrome.storage.local.set({ sendTabTitle: elements.sendTabTitle.checked }, () => {
chrome.runtime.sendMessage({ type: 'TITLE_PRIVACY_CHANGED' }).catch(() => {});
});
});
}
if (elements.mediaTitlePrivacyMode) {
elements.mediaTitlePrivacyMode.addEventListener('change', () => {
chrome.storage.local.set({ mediaTitlePrivacyMode: elements.mediaTitlePrivacyMode.value }, () => {
chrome.runtime.sendMessage({ type: 'TITLE_PRIVACY_CHANGED' }).catch(() => {});
});
});
}
elements.browserNotifications.addEventListener('change', () => {
chrome.storage.local.set({ browserNotifications: elements.browserNotifications.checked });
});
@@ -1100,8 +1293,10 @@ elements.serverUrl.addEventListener('input', () => {
chrome.storage.local.set({ serverUrl: elements.serverUrl.value });
});
elements.username.addEventListener('input', syncDevToolsVisibility);
elements.username.addEventListener('change', () => {
chrome.storage.local.set({ username: elements.username.value });
syncDevToolsVisibility();
});
if (elements.langSelector) {
@@ -1145,7 +1340,11 @@ if (elements.langSelector) {
elements.serverUrl.addEventListener('change', () => {
let url = elements.serverUrl.value.trim();
if (url && !url.includes('://')) {
url = 'ws://' + url;
// Default to secure wss:// — plain ws:// is only valid for a local relay
// (and the background rejects/upgrades ws:// for any remote host anyway).
const host = url.split('/')[0].split(':')[0].toLowerCase();
const isLocal = host === 'localhost' || host === '127.0.0.1';
url = (isLocal ? 'ws://' : 'wss://') + url;
elements.serverUrl.value = url;
chrome.storage.local.set({ serverUrl: url });
}
@@ -1262,7 +1461,7 @@ elements.joinBtn.addEventListener('click', async () => {
}
if (useCustom && serverUrl) {
try {
const urlToCheck = serverUrl.includes('://') ? serverUrl : 'ws://' + serverUrl;
const urlToCheck = serverUrl.includes('://') ? serverUrl : 'wss://' + serverUrl;
new URL(urlToCheck);
} catch (_e) {
showError(getMessage('ERR_INVALID_SERVER_URL'));
@@ -1366,6 +1565,7 @@ elements.targetTab.addEventListener('change', () => {
});
elements.forceSyncBtn.addEventListener('click', async () => {
if (hcmGuestLocked) return; // guest in host-only room — backstop (M-2/L-3)
if (elements.forceSyncBtn.disabled) return;
const originalText = elements.forceSyncBtn.textContent;
@@ -1388,8 +1588,9 @@ elements.forceSyncBtn.addEventListener('click', async () => {
}
const peers = status.peers || [];
const otherTimes = peers
.filter(p => typeof p === 'object' && p.peerId !== localPeerId && p.currentTime != null && !isNaN(p.currentTime))
.map(p => p.currentTime);
.filter(p => typeof p === 'object' && p.peerId !== localPeerId && p.currentTime != null && p.currentTime !== '')
.map(p => Number(p.currentTime))
.filter(Number.isFinite);
if (otherTimes.length === 0) {
showError(getMessage('ERR_NO_PEERS_TIME'));
@@ -1407,7 +1608,10 @@ elements.forceSyncBtn.addEventListener('click', async () => {
const peerCount = (status.peers || []).filter(p => (typeof p === 'object' ? p.peerId : p) !== localPeerId).length;
const syncTimeoutMs = peerCount === 0 ? 3000 : 12000;
const forceSyncReset = () => {
if (!forceSyncDone) {
// Don't unlock a button that's locked because we became a guest mid-flight —
// hcmGuestLocked is the source of truth for the lock state, and the next
// CONTROL_MODE update restores the correct label.
if (!forceSyncDone && !hcmGuestLocked) {
elements.forceSyncBtn.disabled = false;
elements.forceSyncBtn.textContent = originalText;
}
@@ -1415,38 +1619,57 @@ elements.forceSyncBtn.addEventListener('click', async () => {
forceSyncResetTimer = setTimeout(forceSyncReset, syncTimeoutMs);
const tabId = parseInt(status.targetTabId);
const failForceSyncTime = () => {
if (forceSyncResetTimer) { clearTimeout(forceSyncResetTimer); forceSyncResetTimer = null; }
showError(getMessage('ERR_NO_VIDEO_TAB'));
forceSyncDone = true;
elements.forceSyncBtn.disabled = false;
elements.forceSyncBtn.textContent = originalText;
};
const sendForceSync = (time) => {
if (time === null || time === undefined) {
failForceSyncTime();
return;
}
const target = Number(time);
if (!Number.isFinite(target)) {
failForceSyncTime();
return;
}
chrome.runtime.sendMessage({
type: 'CONTENT_EVENT',
action: EVENTS.FORCE_SYNC_PREPARE,
payload: { targetTime: parseFloat(time) }
payload: { targetTime: target }
});
};
if (mode === 'jump-to-me') {
const retryQueryTime = () => {
chrome.tabs.sendMessage(tabId, { action: 'get_current_time' }, (retryResponse) => {
if (chrome.runtime.lastError || !retryResponse || !Number.isFinite(retryResponse.currentTime)) {
failForceSyncTime();
return;
}
sendForceSync(retryResponse.currentTime);
});
};
chrome.tabs.sendMessage(tabId, { action: 'get_current_time' }, (response) => {
if (chrome.runtime.lastError || !response || response.currentTime === undefined) {
chrome.scripting.executeScript({
target: { tabId },
files: ['content.js']
}).then(() => {
setTimeout(() => {
chrome.tabs.sendMessage(tabId, { action: 'get_current_time' }, (retryResponse) => {
if (chrome.runtime.lastError) return;
if (retryResponse && retryResponse.currentTime !== undefined) {
sendForceSync(retryResponse.currentTime);
}
});
}, 500);
}).catch(() => {
showError(getMessage('ERR_NO_VIDEO_TAB'));
forceSyncDone = true;
elements.forceSyncBtn.disabled = false;
elements.forceSyncBtn.textContent = originalText;
if (Number.isFinite(response?.currentTime)) {
sendForceSync(response.currentTime);
return;
}
if (chrome.runtime.lastError || !response) {
chrome.runtime.sendMessage({ type: 'INJECT_CONTENT_SCRIPT', tabId }, (injectResponse) => {
if (chrome.runtime.lastError || !injectResponse || injectResponse.status !== 'ok') {
failForceSyncTime();
return;
}
setTimeout(retryQueryTime, 500);
});
return;
}
sendForceSync(response.currentTime);
setTimeout(retryQueryTime, 500);
});
} else {
sendForceSync(targetTime);
@@ -1454,6 +1677,7 @@ elements.forceSyncBtn.addEventListener('click', async () => {
});
elements.playBtn.addEventListener('click', () => {
if (hcmGuestLocked) return; // guest in host-only room — backstop
if (!elements.targetTab.value) {
showToast(getMessage('ERR_SELECT_VIDEO'), 'warning');
return;
@@ -1472,7 +1696,7 @@ elements.playBtn.addEventListener('click', () => {
});
// Safety reset: restore button after 2.5s in case no peers respond
setTimeout(() => {
if (elements.playBtn.disabled) {
if (elements.playBtn.disabled && !hcmGuestLocked) {
elements.playBtn.textContent = getMessage('BTN_PLAY');
elements.playBtn.disabled = false;
}
@@ -1480,6 +1704,7 @@ elements.playBtn.addEventListener('click', () => {
});
elements.pauseBtn.addEventListener('click', () => {
if (hcmGuestLocked) return; // guest in host-only room — backstop
if (!elements.targetTab.value) {
showToast(getMessage('ERR_SELECT_VIDEO'), 'warning');
return;
@@ -1498,19 +1723,54 @@ elements.pauseBtn.addEventListener('click', () => {
});
// Safety reset: restore button after 2.5s in case no peers respond
setTimeout(() => {
if (elements.pauseBtn.disabled) {
if (elements.pauseBtn.disabled && !hcmGuestLocked) {
elements.pauseBtn.textContent = getMessage('BTN_PAUSE');
elements.pauseBtn.disabled = false;
}
}, 2500);
});
function simulateRemoteSeek({ delta = null, targetTime = null }) {
chrome.runtime.sendMessage({ type: 'DEV_SIMULATE_REMOTE_SEEK', delta, targetTime }, (res) => {
if (chrome.runtime.lastError || !res || res.status !== 'ok') {
showToast(res?.message || 'Remote seek failed', 'error');
return;
}
showToast(`Remote seek -> ${formatTime(res.targetTime)}`, 'success', 1200);
refreshDebugInfo();
});
}
if (elements.remoteSeekBack) {
elements.remoteSeekBack.addEventListener('click', () => simulateRemoteSeek({ delta: -30 }));
}
if (elements.remoteSeekForward) {
elements.remoteSeekForward.addEventListener('click', () => simulateRemoteSeek({ delta: 30 }));
}
if (elements.remoteSeekFiveMin) {
elements.remoteSeekFiveMin.addEventListener('click', () => simulateRemoteSeek({ targetTime: 300 }));
}
elements.clearLogs.addEventListener('click', () => {
chrome.runtime.sendMessage({ type: 'CLEAR_LOGS' }, () => {
elements.logList.innerHTML = '';
});
});
if (elements.regenId) {
elements.regenId.addEventListener('click', () => {
elements.regenId.disabled = true;
chrome.runtime.sendMessage({ type: 'REGENERATE_ID' }, (res) => {
elements.regenId.disabled = false;
if (chrome.runtime.lastError || !res || !res.peerId) {
showToast(getMessage('TOAST_ID_REGENERATED') || 'Failed to regenerate identity', 'error');
return;
}
showToast(getMessage('TOAST_ID_REGENERATED') || 'Identity regenerated — reconnecting…', 'success', 3000);
});
});
}
elements.copyInvite.addEventListener('click', () => {
navigator.clipboard.writeText(elements.inviteLink.value).then(() => {
const original = elements.copyInvite.textContent;
@@ -1656,6 +1916,9 @@ chrome.runtime.onMessage.addListener((msg) => {
} else if (msg.type === 'PEER_UPDATE') {
updatePeerList(msg.peers);
if (msg.peers) detectPeerChanges(msg.peers);
} else if (msg.type === 'CONTROL_MODE') {
const inRoom = elements.sectionActive && elements.sectionActive.style.display === 'block';
updateHostControlUI({ controlMode: msg.controlMode, amHost: msg.amHost, amController: msg.amController, controllers: msg.controllers, hostPeerId: msg.hostPeerId, hostControlSupported: msg.hostControlSupported, coHostSupported: msg.coHostSupported, inRoom });
} else if (msg.type === 'CONNECTION_STATUS') {
if (msg.status === 'connected' || msg.status === 'disconnected') {
if (joinBtnTimeout) { clearTimeout(joinBtnTimeout); joinBtnTimeout = null; }
@@ -1673,6 +1936,7 @@ chrome.runtime.onMessage.addListener((msg) => {
if (res.peers) updatePeerList(res.peers);
if (res.lastActionState) updateLastActionUI(res.lastActionState, res.peers);
updatePingDisplay(res.ping);
updateHostControlUI({ controlMode: res.controlMode, amHost: res.amHost, amController: res.amController, controllers: res.controllers, hostPeerId: res.hostPeerId, hostControlSupported: res.hostControlSupported, coHostSupported: res.coHostSupported, inRoom: true });
});
}
if (msg.status === 'disconnected') {
@@ -1844,6 +2108,26 @@ elements.copyLogs.addEventListener('click', () => {
lines.push(`- **ReadyState:** ${readyOk ? '\u2705' : '\u26A0\uFE0F'} ${safe(vs.readyState, '?')} (${readyLabel})`);
lines.push(`- **Network:** ${safe(vs.networkState, '?')} (${netLabel})`);
lines.push(`- **Buffered:** ${safe(vs.buffered, '?')}`);
if (vs.nativeCurrentTime != null || vs.nativeDuration != null) {
lines.push(`- **Native Time:** ${safe(vs.nativeCurrentTime, '?')}s / ${safe(vs.nativeDuration, '?')}s`);
}
if (vs.siteQuirk) {
const quirk = vs.siteQuirk;
const label = quirk.name || quirk.key || 'site';
if (quirk.timeline) {
lines.push(`- **${label} Timeline:** ${safe(quirk.timeline.current, '?')}s / ${safe(quirk.timeline.duration, '?')}s`);
}
const candidates = Array.isArray(quirk.timelineCandidates) ? quirk.timelineCandidates : [];
if (candidates.length > 0) {
lines.push(`- **${label} Timeline Candidates:**`);
candidates.forEach(c => lines.push(` - ${safe(c.source, '?')}: ${safe(c.current, '?')}s / ${safe(c.duration, '?')}s`));
}
const buttons = Array.isArray(quirk.seekButtons) ? quirk.seekButtons : [];
if (buttons.length > 0) {
lines.push(`- **${label} Buttons:**`);
buttons.forEach(label => lines.push(` - ${label}`));
}
}
lines.push(`- **Dimensions:** ${safe(vs.videoWidth, '?')}x${safe(vs.videoHeight, '?')}${dimOk ? '' : ' \u26A0\uFE0F 0x0'}`);
lines.push(`- **Muted:** ${safe(vs.muted, '?')} | **Volume:** ${safe(vs.volume, '?')} | **Speed:** ${safe(vs.playbackRate, '?')}x`);
lines.push(`- **Seeking:** ${safe(vs.seeking, '?')} | **Ended:** ${safe(vs.ended, '?')} | **Loop:** ${safe(vs.loop, '?')}`);
@@ -2017,7 +2301,18 @@ function refreshDebugInfo() {
addField('Network', `${state.networkState} (${state.networkStateLabel || '?'})`,
state.networkState === 1 ? '#22c55e' : state.networkState === 3 ? '#ef4444' : 'var(--text-muted)');
addField('Buffered', state.buffered || '?');
if (state.nativeCurrentTime != null || state.nativeDuration != null) {
addField('Native Time', `${state.nativeCurrentTime ?? '?'}s / ${state.nativeDuration ?? '?'}s`);
}
if (state.siteQuirk) {
const quirk = state.siteQuirk;
const label = quirk.name || quirk.key || 'Site';
if (quirk.timeline) {
addField(`${label} Timeline`, `${quirk.timeline.current ?? '?'}s / ${quirk.timeline.duration ?? '?'}s`);
}
const buttons = Array.isArray(quirk.seekButtons) ? quirk.seekButtons.slice(0, 4).join(' | ') : '';
if (buttons) addField(`${label} Buttons`, buttons);
}
addSection('Properties');
addField('Seeking', String(state.seeking));
addField('Ended', String(state.ended));
+51
View File
@@ -0,0 +1,51 @@
import { extractEpisodeId } from './episode-utils.js';
export const TITLE_PRIVACY_MODES = Object.freeze({
FULL: 'full',
EPISODE: 'episode',
HIDDEN: 'hidden'
});
export function normalizeTitlePrivacyMode(mode) {
return Object.values(TITLE_PRIVACY_MODES).includes(mode)
? mode
: TITLE_PRIVACY_MODES.FULL;
}
export function normalizeSendTabTitle(sendTabTitle, legacyMode = TITLE_PRIVACY_MODES.FULL) {
if (typeof sendTabTitle === 'boolean') return sendTabTitle;
return normalizeTitlePrivacyMode(legacyMode) === TITLE_PRIVACY_MODES.FULL;
}
export function normalizeTabTitle(title) {
if (typeof title !== 'string') return null;
const normalized = title.replace(/^\s*(?:\(\d{1,2}\)|\[\d{1,2}\])\s+/, '').trim();
return normalized.length > 0 ? normalized : null;
}
export function sanitizeTabTitle(title, sendTabTitle) {
if (!sendTabTitle) return null;
return normalizeTabTitle(title);
}
export function sanitizeSharedTitle(title, mode) {
const normalizedMode = normalizeTitlePrivacyMode(mode);
if (normalizedMode === TITLE_PRIVACY_MODES.HIDDEN) return null;
if (typeof title !== 'string' || title.length === 0) return null;
if (normalizedMode === TITLE_PRIVACY_MODES.EPISODE) {
return extractEpisodeId(title) || null;
}
return title;
}
export function applyTitlePrivacyToPayload(payload, mode, keys = ['mediaTitle', 'expectedTitle', 'title']) {
const source = payload && typeof payload === 'object' ? payload : {};
const next = { ...source };
keys.forEach(key => {
if (Object.prototype.hasOwnProperty.call(next, key)) {
next[key] = sanitizeSharedTitle(next[key], mode);
}
});
return next;
}
+1496 -6
View File
File diff suppressed because it is too large Load Diff
+8 -3
View File
@@ -1,22 +1,27 @@
{
"name": "koalasync",
"version": "2.4.6",
"version": "2.5.3",
"description": "KoalaSync Build Scripts",
"private": true,
"type": "module",
"scripts": {
"build:extension": "node scripts/build-extension.cjs",
"indexnow": "node website/submit-indexnow.cjs",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"test": "node scripts/verify-release.mjs",
"test": "npm run verify",
"test:unit": "vitest run",
"verify": "node scripts/verify-release.mjs"
},
"devDependencies": {
"@playwright/test": "^1.61.1",
"@vitest/coverage-v8": "^4.1.9",
"archiver": "^7.0.1",
"esbuild": "^0.28.1",
"eslint": "^10.4.0",
"fs-extra": "^11.2.0",
"sharp": "^0.34.5",
"svgo": "^4.0.1"
"svgo": "^4.0.1",
"vitest": "^4.1.9"
}
}
+78 -18
View File
@@ -1,33 +1,93 @@
# Development Scripts
This directory contains utility scripts for the KoalaSync development workflow.
This directory contains build, synchronization, and verification scripts for the KoalaSync workspace. Run all commands from the repository root unless a script says otherwise.
## Main Commands
```bash
npm run build:extension
npm run verify
npm run lint
npm run test:unit
```
- `npm run build:extension` runs `scripts/build-extension.cjs`.
- `npm run verify` runs the full release-safety suite in `scripts/verify-release.mjs`.
- `npm run lint` runs ESLint across the repository.
- `npm run test:unit` runs Vitest tests.
## build-extension.cjs
The primary build tool for KoalaSync. This Node.js script automates two critical tasks:
The primary extension build tool performs these steps:
1. **Protocol Synchronization**: Copies the "Single Source of Truth" constants (`shared/constants.js`) and the domain blacklist (`shared/blacklist.js`) from the root `/shared` directory into the `extension/shared/` directory.
2. **Content Script Injection**: Injects protocol constants directly into `content.js` using marker-based replacement. This is necessary because `content.js` executes synchronously and cannot use ES module imports.
3. **Artifact Generation**: Compiles the extension into browser-specific bundles for Chrome and Firefox, located in the `dist/` directory.
### Usage
From the **repository root**, run:
1. Recreates `dist/`.
2. Copies `shared/constants.js`, `shared/blacklist.js`, `shared/names.js`, and `shared/README.md` into `extension/shared/`.
3. Injects synchronous shared values into `content.js`.
4. Injects browser-specific uninstall URL constants into `background.js`.
5. Injects the build timestamp into `popup.html`.
6. Generates browser-specific manifests for Chrome and Firefox.
7. Creates `dist/koalasync-chrome.zip` and `dist/koalasync-firefox.zip`.
Usage:
```bash
node scripts/build-extension.cjs
# or
npm run build:extension
```
### Why this script exists
KoalaSync uses **Vanilla JS** in the extension to maintain zero runtime dependencies and maximum privacy. Since we don't use a bundler (like Webpack or Vite) inside the extension, this script serves as our lightweight "pre-build" step to ensure that the protocol constants remain synchronized between the extension and the relay server.
## Injection Markers
### Content Injection Markers
The build script uses marker comments/placeholders. Missing markers are a hard build failure so release artifacts cannot silently contain stale protocol data.
The build script uses marker comments in `content.js` to locate and replace constant blocks:
| Target | Marker / Placeholder | Injected Value | Source |
|:---|:---|:---|:---|
| `content.js` | `SHARED_EVENTS_INJECT_START` / `END` | Full `EVENTS` object | `shared/constants.js` |
| `content.js` | `SHARED_HEARTBEAT_INJECT_START` / `END` | `HEARTBEAT_INTERVAL` | `shared/constants.js` |
| `content.js` | `SHARED_EPISODE_UTILS_INJECT_START` / `END` | `extractEpisodeId()` and `sameEpisode()` | `extension/episode-utils.js` |
| `background.js` | `UNINSTALL_URL_INJECT_START` / `END` | Uninstall URL and browser type | `scripts/build-extension.cjs` |
| `popup.html` | `__BUILD_TIMESTAMP__` | UTC build timestamp | Build time |
| Marker Pair | Injected Value | Source |
|:---|:---|:---|
| `SHARED_EVENTS_INJECT_START` / `END` | The full `EVENTS` object | `shared/constants.js` |
| `SHARED_HEARTBEAT_INJECT_START` / `END` | `HEARTBEAT_INTERVAL` value | `shared/constants.js` |
Do not remove or rename these markers without updating the build script and tests.
> **⚠️ Do NOT remove or modify these marker comments in `content.js`.** They are required for the build script to function. If the markers are missing, the build will fail with a clear error message.
## Verification Suite
`scripts/verify-release.mjs` is the best single command before release, PR review, or handoff:
```bash
npm run verify
```
It currently runs:
- Vitest unit tests.
- Server ops, route, WebSocket, and rate-limiter checks.
- Episode parser, title privacy, audio settings, popup cooldown, names, and content-video-finder checks.
- JavaScript syntax checks for server and extension entry points.
- Extension and website locale coverage checks.
- ESLint.
- Production `npm audit` checks for root and server dependencies.
- Extension build and website build.
## Focused Scripts
| Script | Purpose |
|:---|:---|
| `test-server-ops.mjs` | Health payload and admin metrics helpers |
| `test-server-routes.mjs` | HTTP health routes, caching, and admin metrics access |
| `test-server-ws.mjs` | Socket.IO relay integration, including host-control behavior |
| `test-rate-limiter.mjs` | Rate-limiter map and cooldown behavior |
| `test-episode-utils.mjs` | Episode-title extraction and comparison |
| `test-title-privacy.mjs` | Tab/media title privacy sanitization |
| `test-audio-settings.mjs` | Audio settings defaults and normalization |
| `test-popup-refresh-cooldown.mjs` | Popup refresh throttling behavior |
| `test-names.mjs` | Generated username format and coverage |
| `test-content-video-finder.cjs` | Content-script video selection helpers |
| `test-locales.cjs` | Extension runtime and browser-store locale coverage |
| `test-website-locales.mjs` | Website locale coverage |
## Do Not Break
- Keep scripts runnable from the repository root.
- Keep build output under `dist/` and generated website output under `website/www/`.
- Keep shared protocol sync automated; do not add manual copy steps.
- Treat warnings in verification scripts as release blockers unless the script explicitly documents them as informational.
+28 -27
View File
@@ -36,6 +36,13 @@ console.log('✓ constants.js, blacklist.js, names.js, and README.md synced to e
// Read the base manifest
const baseManifest = JSON.parse(fs.readFileSync(baseManifestPath, 'utf8'));
function replaceRequiredBlock(content, pattern, replacement, description) {
if (!pattern.test(content)) {
throw new Error(`CRITICAL: ${description} markers not found. Aborting build to prevent stale artifacts.`);
}
return content.replace(pattern, replacement);
}
// Helper to copy files, ignoring manifest.json and manifest.base.json
// Also injects shared constants into content.js
function copyExtensionFiles(targetDir, browserName) {
@@ -79,11 +86,7 @@ function copyExtensionFiles(targetDir, browserName) {
const ePattern = new RegExp(`${eStart}[\\s\\S]+?${eEnd}`);
const eRep = `${eStart}\n // This block is automatically updated by /scripts/build-extension.cjs\n const EVENTS = ${eventsObject};\n ${eEnd}`;
if (ePattern.test(content)) {
content = content.replace(ePattern, eRep);
} else {
console.warn('⚠️ WARNING: Event markers not found in content.js');
}
content = replaceRequiredBlock(content, ePattern, eRep, 'Event injection');
// 2. Inject Heartbeat
const hStart = '// --- SHARED_HEARTBEAT_INJECT_START ---';
@@ -91,30 +94,23 @@ function copyExtensionFiles(targetDir, browserName) {
const hPattern = new RegExp(`${hStart}[\\s\\S]+?${hEnd}`);
const hRep = `${hStart}\n const HEARTBEAT_INTERVAL_VAL = ${heartbeatVal};\n ${hEnd}`;
if (hPattern.test(content)) {
content = content.replace(hPattern, hRep);
} else {
console.warn('⚠️ WARNING: Heartbeat markers not found in content.js');
}
content = replaceRequiredBlock(content, hPattern, hRep, 'Heartbeat injection');
// 3. Inject Episode Utils
const euStart = '// --- SHARED_EPISODE_UTILS_INJECT_START ---';
const euEnd = '// --- SHARED_EPISODE_UTILS_INJECT_END ---';
const euPattern = new RegExp(euStart.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '[\\s\\S]+?' + euEnd.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
const euPath = path.join(rootDir, 'extension', 'episode-utils.js');
if (fs.existsSync(euPath)) {
const euContent = fs.readFileSync(euPath, 'utf8');
const stripped = euContent
.replace(/^\/\*\*[\s\S]*?\*\/\s*/m, '')
.replace(/export function /g, 'function ')
.trim();
const euRep = `${euStart}\n // This block is automatically updated by /scripts/build-extension.cjs\n${stripped.split('\n').map(l => ' ' + l).join('\n')}\n ${euEnd}`;
if (euPattern.test(content)) {
content = content.replace(euPattern, euRep);
} else {
console.warn('⚠ WARNING: Episode utils markers not found in content.js');
}
if (!fs.existsSync(euPath)) {
throw new Error(`CRITICAL: Episode utils source missing: ${euPath}. Aborting build.`);
}
const euContent = fs.readFileSync(euPath, 'utf8');
const stripped = euContent
.replace(/^\/\*\*[\s\S]*?\*\/\s*/m, '')
.replace(/export function /g, 'function ')
.trim();
const euRep = `${euStart}\n // This block is automatically updated by /scripts/build-extension.cjs\n${stripped.split('\n').map(l => ' ' + l).join('\n')}\n ${euEnd}`;
content = replaceRequiredBlock(content, euPattern, euRep, 'Episode utils injection');
fs.writeFileSync(destPath, content);
console.log('✓ Injected shared constants into content.js');
@@ -132,14 +128,19 @@ function copyExtensionFiles(targetDir, browserName) {
uRep += ` const BROWSER_TYPE = "${browserName}";\n`;
uRep += ` ${uEnd}`;
if (uPattern.test(content)) {
content = content.replace(uPattern, uRep);
} else {
console.warn('⚠️ WARNING: Uninstall URL markers not found in background.js');
}
content = replaceRequiredBlock(content, uPattern, uRep, 'Uninstall URL injection');
fs.writeFileSync(destPath, content);
console.log(`✓ Injected uninstall URL constants for ${browserName} into background.js`);
} else if (item === 'popup.html') {
let content = fs.readFileSync(srcPath, 'utf8');
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC';
if (!content.includes('__BUILD_TIMESTAMP__')) {
throw new Error('CRITICAL: Build timestamp placeholder not found in popup.html. Aborting build.');
}
content = content.replace(/__BUILD_TIMESTAMP__/g, timestamp);
fs.writeFileSync(destPath, content);
console.log(`✓ Injected build timestamp into popup.html: ${timestamp}`);
} else {
fs.copyFileSync(srcPath, destPath);
}
+90 -1
View File
@@ -19,6 +19,14 @@ function extractFunction(name, text) {
throw new Error(`${name} body did not terminate`);
}
function makeSeekable(ranges = []) {
return {
length: ranges.length,
start(i) { return ranges[i][0]; },
end(i) { return ranges[i][1]; }
};
}
function makeVideo(name, width, height, options = {}) {
return {
name,
@@ -28,7 +36,9 @@ function makeVideo(name, width, height, options = {}) {
offsetWidth: width,
offsetHeight: height,
muted: options.muted ?? true,
duration: options.duration ?? 0
duration: options.duration ?? 0,
currentTime: options.currentTime ?? 0,
seekable: options.seekable ?? makeSeekable()
};
}
@@ -61,4 +71,83 @@ assert.strictEqual(
'findVideo should score Shadow DOM videos together with light DOM videos'
);
function makeDocument(nodes = []) {
return {
querySelectorAll() { return nodes; }
};
}
function loadTimelineFns(hostname, document = makeDocument(), pageApiTime = null) {
const disneyPageApiTime = pageApiTime
? `let disneyPageApiTime = { position: ${pageApiTime.position}, duration: ${pageApiTime.duration}, at: Date.now() - ${pageApiTime.ageMs || 0} };`
: 'let disneyPageApiTime = null;';
return Function('window', 'document', [
disneyPageApiTime,
extractFunction('hostMatchesUrl', source),
extractFunction('matchesPlayerUrls', source),
extractFunction('isDisneyPlusHost', source),
extractFunction('getDisneyPlusTimeline', source),
extractFunction('getSiteQuirkAdapters', source),
extractFunction('getActiveSiteQuirk', source),
extractFunction('getSiteQuirkTimeline', source),
extractFunction('getSiteQuirkDebug', source),
extractFunction('getSyncCurrentTime', source),
extractFunction('getSyncDuration', source),
extractFunction('toNativeSeekTime', source),
'return { getActiveSiteQuirk, getSyncCurrentTime, getSyncDuration, toNativeSeekTime };'
].join('\n'))({ location: { hostname } }, document);
}
function loadPlayerFixFns(hostname) {
return Function('window', [
extractFunction('hostMatchesUrl', source),
extractFunction('matchesPlayerUrls', source),
extractFunction('getPlayerActionFixes', source),
extractFunction('getActivePlayerActionFix', source),
'return { getPlayerActionFixes, getActivePlayerActionFix };'
].join('\n'))({ location: { hostname } });
}
const disneyFns = loadTimelineFns('www.disneyplus.com', makeDocument(), {
position: 9,
duration: 10800
});
assert.equal(disneyFns.getActiveSiteQuirk().name, 'disneyplus-page-api');
assert.deepEqual(disneyFns.getActiveSiteQuirk().urls, ['disneyplus.com']);
const disneyVideo = makeVideo('disney-offset', 1920, 1080, {
currentTime: 29,
duration: 0,
seekable: makeSeekable([[0, 32400]])
});
assert.equal(disneyFns.getSyncCurrentTime(disneyVideo), 9);
assert.equal(disneyFns.getSyncDuration(disneyVideo), 10800);
assert.equal(disneyFns.toNativeSeekTime(disneyVideo, 39), 39);
assert.equal(disneyFns.getSyncCurrentTime(makeVideo('disney-native-broken', 1920, 1080, {
currentTime: Number.NaN,
duration: 0
})), 9);
const disneyNoPageApiFns = loadTimelineFns('www.disneyplus.com');
const disneyOffsetVideo = makeVideo('disney-offset', 1920, 1080, {
currentTime: 29,
duration: 0,
seekable: makeSeekable([[20, 10820]])
});
assert.equal(disneyNoPageApiFns.getSyncCurrentTime(disneyOffsetVideo), null);
assert.equal(disneyNoPageApiFns.getSyncDuration(disneyOffsetVideo), 0);
assert.equal(disneyNoPageApiFns.toNativeSeekTime(disneyOffsetVideo, 39), 39);
const genericFns = loadTimelineFns('example.com');
assert.equal(genericFns.getActiveSiteQuirk(), null);
assert.equal(genericFns.getSyncCurrentTime(disneyVideo), 29);
assert.equal(genericFns.getSyncDuration(disneyVideo), 0);
assert.equal(genericFns.toNativeSeekTime(disneyVideo, 39), 39);
const twitchFixFns = loadPlayerFixFns('player.twitch.tv');
assert.equal(twitchFixFns.getActivePlayerActionFix().name, 'twitch-player-buttons');
assert.deepEqual(twitchFixFns.getActivePlayerActionFix().urls, ['twitch.tv']);
const genericFixFns = loadPlayerFixFns('example.com');
assert.equal(genericFixFns.getActivePlayerActionFix(), null);
console.log('content video finder tests passed');
+20 -3
View File
@@ -4,6 +4,7 @@ import {
checkEventRate,
checkHealthRate,
checkAdminMetricsAuthRate,
checkLeaveRoomRate,
checkAuthRate,
recordAuthFailure,
clearRateLimitMaps,
@@ -13,11 +14,13 @@ import {
healthCounts,
adminMetricsAuthCounts,
roomListCooldowns,
leaveRoomCounts,
rateLimitDenied,
startRateLimitCleanup,
stopRateLimitCleanup,
CONNECTION_RATE_LIMIT,
EVENT_RATE_LIMIT
EVENT_RATE_LIMIT,
LEAVE_ROOM_RATE_LIMIT
} from '../server/rate-limiter.js';
// Helper: mock io for cleanup
@@ -26,7 +29,7 @@ const mockIo = { sockets: { sockets: new Map() } };
// Reset state before each test group
function reset() {
clearRateLimitMaps();
Object.assign(rateLimitDenied, { connections: 0, events: 0, health: 0, adminMetricsAuth: 0, roomList: 0 });
Object.assign(rateLimitDenied, { connections: 0, events: 0, health: 0, adminMetricsAuth: 0, roomList: 0, leaveRoom: 0 });
stopRateLimitCleanup();
}
@@ -52,6 +55,16 @@ assert.equal(rateLimitDenied.events, 1);
reset();
assert.equal(checkEventRate('sock2'), true, 'separate socket independent');
// --- checkLeaveRoomRate ---
reset();
assert.equal(checkLeaveRoomRate('sock-leave-1'), true, 'first leave-room event allowed');
for (let i = 0; i < LEAVE_ROOM_RATE_LIMIT - 1; i++) checkLeaveRoomRate('sock-leave-1');
assert.equal(checkLeaveRoomRate('sock-leave-1'), false, `leave-room beyond ${LEAVE_ROOM_RATE_LIMIT}/window blocked`);
assert.equal(rateLimitDenied.leaveRoom, 1);
reset();
assert.equal(checkLeaveRoomRate('sock-leave-2'), true, 'separate leave-room socket independent');
// --- checkHealthRate ---
reset();
assert.equal(checkHealthRate('1.2.3.4'), true, 'first health check allowed');
@@ -91,12 +104,14 @@ eventCounts.set('sock1', { count: 1, resetTime: Date.now() + 10000 });
healthCounts.set('ip2', { count: 1, resetTime: Date.now() + 60000 });
adminMetricsAuthCounts.set('ip3', { count: 1, resetTime: Date.now() + 60000 });
roomListCooldowns.set('sock2', Date.now());
leaveRoomCounts.set('sock3', { count: 1, resetTime: Date.now() + 60000 });
clearRateLimitMaps();
assert.equal(connectionCounts.size, 0, 'connectionCounts cleared');
assert.equal(eventCounts.size, 0, 'eventCounts cleared');
assert.equal(healthCounts.size, 0, 'healthCounts cleared');
assert.equal(adminMetricsAuthCounts.size, 0, 'adminMetricsAuthCounts cleared');
assert.equal(roomListCooldowns.size, 0, 'roomListCooldowns cleared');
assert.equal(leaveRoomCounts.size, 0, 'leaveRoomCounts cleared');
// --- startRateLimitCleanup / stopRateLimitCleanup ---
reset();
@@ -108,7 +123,9 @@ assert.ok(true, 'cleanup start/stop does not throw');
// --- rateLimitDenied reset ---
reset();
rateLimitDenied.connections = 5;
Object.assign(rateLimitDenied, { connections: 0, events: 0, health: 0, adminMetricsAuth: 0, roomList: 0 });
rateLimitDenied.leaveRoom = 5;
Object.assign(rateLimitDenied, { connections: 0, events: 0, health: 0, adminMetricsAuth: 0, roomList: 0, leaveRoom: 0 });
assert.equal(rateLimitDenied.connections, 0, 'denial counter resettable');
assert.equal(rateLimitDenied.leaveRoom, 0, 'leave-room denial counter resettable');
console.log('rate-limiter tests passed');
+5 -4
View File
@@ -52,7 +52,7 @@ const basicHealth = buildHealthPayload({
now: 1234,
uptime: 99,
memoryUsage: () => ({ rss: 10, heapUsed: 5, heapTotal: 8 }),
rateLimitSizes: { connections: 1, events: 2, health: 3, adminMetricsAuth: 4, authFailures: 5, roomList: 6 }
rateLimitSizes: { connections: 1, events: 2, health: 3, adminMetricsAuth: 4, authFailures: 5, roomList: 6, leaveRoom: 7 }
});
assert.deepEqual(
@@ -68,7 +68,8 @@ const adminHealth = buildHealthPayload({
now: 1234,
uptime: 99,
memoryUsage: () => ({ rss: 10, heapUsed: 5, heapTotal: 8 }),
rateLimitSizes: { connections: 1, events: 2, health: 3, adminMetricsAuth: 4, authFailures: 5, roomList: 6 }
rateLimitSizes: { connections: 1, events: 2, health: 3, adminMetricsAuth: 4, authFailures: 5, roomList: 6, leaveRoom: 7 },
rateLimitDenied: { leaveRoom: 8 }
});
assert.equal(adminHealth.peers, 5, 'admin metrics should include aggregate peer count');
@@ -79,8 +80,8 @@ assert.deepEqual(adminHealth.memory, { rss: 10, heapUsed: 5, heapTotal: 8 }, 'ad
assert.deepEqual(
adminHealth.rateLimits,
{
trackedClients: { connections: 1, events: 2, health: 3, adminMetricsAuth: 4, authFailures: 5, roomList: 6 },
denied: { connections: 0, events: 0, health: 0, adminMetricsAuth: 0, roomList: 0 }
trackedClients: { connections: 1, events: 2, health: 3, adminMetricsAuth: 4, authFailures: 5, roomList: 6, leaveRoom: 7 },
denied: { connections: 0, events: 0, health: 0, adminMetricsAuth: 0, roomList: 0, leaveRoom: 8 }
},
'admin metrics should expose rate-limit tracking and denial counts'
);
+316 -1
View File
@@ -3,6 +3,7 @@ import http from 'node:http';
import { createRequire } from 'node:module';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import { connectionCounts, clearRateLimitMaps } from '../server/rate-limiter.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const require = createRequire(path.join(__dirname, '..', 'server', 'package.json'));
@@ -23,6 +24,9 @@ function a(ws) { if (ws._m.length) { const r=ws._m.shift(); return r.startsWith(
async function w(ws, evt, ms=3000) { const st=Date.now(); while(Date.now()-st<ms) { for(let i=0;i<ws._m.length;i++){const r=ws._m[i];ws._m.splice(i,1);if(r.startsWith('42')){try{const[e]=JSON.parse(r.substring(2));if(e===evt)return e}catch{/* skip */}}} await new Promise(r=>setTimeout(r,50));} throw Error(`wait:${evt}`); }
async function j(ws, rid, pid, pw=null) { s(ws,'join_room',{roomId:rid,peerId:pid,password:pw,protocolVersion:'1.0.0'}); assert.equal((await a(ws))[0],'room_data'); }
function close() { clients.forEach(w=>{try{w.close()}catch{/* ignore */}}); clients.length=0; }
// Test suite opens >10 connections/min — clear the IP connection counter so the
// connection rate limiter doesn't mask test failures (test-only, never at runtime).
function resetConnectionRate() { connectionCounts.clear(); clearRateLimitMaps(); }
try {
process.env.ADMIN_METRICS_TOKEN = 'ws-integration-test-32chars-minimum!';
@@ -57,6 +61,317 @@ try {
s(p1,'leave_room',{}); const [ev,d]=await a(p2); assert.equal(ev,'peer_status');assert.equal(d.status,'left');
close();
resetConnectionRate();
// --- Capabilities: ROOM_DATA advertises server features for client detection ---
const capClient = await c();
s(capClient, 'join_room', { roomId: 'cap-'+Date.now(), peerId: 'capp', protocolVersion: '1.0.0' });
const [capEv, capData] = await a(capClient);
assert.equal(capEv, 'room_data');
assert.ok(Array.isArray(capData.capabilities) && capData.capabilities.includes('host-control'),
'ROOM_DATA advertises the host-control capability');
close();
resetConnectionRate();
// --- Default 'everyone' mode does NOT gate anyone (host-control OFF = unchanged) ---
// Confirms that with host-only off, a non-host guest can still drive every
// room-moving event exactly like before the feature existed.
const erid = 'every-'+Date.now();
const e1 = await c(), e2 = await c(); // e1 = creator/host, e2 = guest
await j(e1, erid, 'ehost'); await j(e2, erid, 'eguest'); e1._m.length = e2._m.length = 0;
s(e2,'play',{currentTime:1}); await w(e1,'play');
s(e2,'pause',{currentTime:2}); await w(e1,'pause');
s(e2,'seek',{currentTime:3}); await w(e1,'seek');
s(e2,'force_sync_prepare',{targetTime:0}); await w(e1,'force_sync_prepare');
s(e2,'episode_lobby',{expectedTitle:'S1E1'}); await w(e1,'episode_lobby');
// (reaching here without a wait timeout == nothing was gated)
close();
resetConnectionRate();
// --- Host Control Mode ---
const hrid = 'host-'+Date.now();
const h1 = await c(), h2 = await c(); // h1 = host (first joiner), h2 = guest
await j(h1, hrid, 'host1'); await j(h2, hrid, 'guest1'); h1._m.length = h2._m.length = 0;
// Host enables host-only -> both peers get the control_mode broadcast
s(h1,'set_control_mode',{controlMode:'host-only'});
await w(h1,'control_mode'); await w(h2,'control_mode');
h1._m.length = h2._m.length = 0;
// Guest's room-moving event (pause) is dropped -> host must NOT receive it
s(h2,'pause',{currentTime:5});
let guestPauseDropped = false; try { await w(h1,'pause',600); } catch { guestPauseDropped = true; }
assert.ok(guestPauseDropped, 'guest pause dropped in host-only');
// Host's own pause still relays to the guest
s(h1,'pause',{currentTime:7}); await w(h2,'pause');
h1._m.length = h2._m.length = 0;
// desynced flag is relayed through PEER_STATUS heartbeats so the host's UI
// can show "Solo" for guests watching on their own.
s(h2,'peer_status',{status:'heartbeat',desynced:true,currentTime:42,playbackState:'playing'});
let hbData = null; const hbStart = Date.now();
while (Date.now()-hbStart < 600 && !hbData) {
for (let i=0;i<h1._m.length;i++){ const r=h1._m[i]; if(r.startsWith('42')){ const [e,dd]=JSON.parse(r.substring(2)); if(e==='peer_status'){ h1._m.splice(i,1); hbData=dd; break; } } }
await new Promise(r=>setTimeout(r,30));
}
assert.ok(hbData && hbData.desynced === true, 'desynced=true relayed in heartbeat');
h1._m.length = h2._m.length = 0;
// Guest cannot change the control mode -> host must NOT receive a broadcast.
// The rejected sender gets a unicast of the *actual* state so any optimistic
// UI reverts (H-5); assert both halves.
s(h2,'set_control_mode',{controlMode:'everyone'});
let guestSetBlocked = false; try { await w(h1,'control_mode',600); } catch { guestSetBlocked = true; }
assert.ok(guestSetBlocked, 'non-host cannot set control mode');
let rejectSync = null; const rsStart = Date.now();
while (Date.now()-rsStart < 600 && !rejectSync) {
for (let i=0;i<h2._m.length;i++){ const r=h2._m[i]; if(r.startsWith('42')){ const [e,dd]=JSON.parse(r.substring(2)); if(e==='control_mode'){ h2._m.splice(i,1); rejectSync=dd; break; } } }
await new Promise(r=>setTimeout(r,30));
}
assert.ok(rejectSync && rejectSync.controlMode==='host-only' && rejectSync.hostPeerId==='host1',
'rejected sender is re-synced to actual state');
h1._m.length = h2._m.length = 0;
// Host leaves -> room falls back to 'everyone' and reassigns host to the guest
s(h1,'leave_room',{});
let fb = null; const fbStart = Date.now();
while (Date.now()-fbStart < 2000 && !fb) {
for (let i=0;i<h2._m.length;i++){ const r=h2._m[i]; if(r.startsWith('42')){ const [e,dd]=JSON.parse(r.substring(2)); if(e==='control_mode'){ h2._m.splice(i,1); fb=dd; break; } } }
await new Promise(r=>setTimeout(r,50));
}
assert.ok(fb && fb.controlMode==='everyone' && fb.hostPeerId==='guest1', 'host leave -> fallback everyone + new host');
close();
// --- M-4: rapid control-mode toggles are debounced per-room ---
const drid = 'debounce-'+Date.now();
const db1 = await c(), db2 = await c();
await j(db1, drid, 'dhost'); await j(db2, drid, 'dguest'); db1._m.length = db2._m.length = 0;
// First toggle (everyone → host-only) goes through.
s(db1,'set_control_mode',{controlMode:'host-only'});
await w(db1,'control_mode'); await w(db2,'control_mode');
db1._m.length = db2._m.length = 0;
// Immediate second toggle (host-only → everyone) should be debounced:
// broadcast goes to neither peer, but sender gets a re-sync unicast.
s(db1,'set_control_mode',{controlMode:'everyone'});
let dGuestGotIt = false; try { await w(db2,'control_mode',600); } catch { dGuestGotIt = true; }
assert.ok(dGuestGotIt, 'rapid control-mode toggle is debounced (no broadcast)');
let dSenderResync = null; const dsStart = Date.now();
while (Date.now()-dsStart < 600 && !dSenderResync) {
for (let i=0;i<db1._m.length;i++){ const r=db1._m[i]; if(r.startsWith('42')){ const [e,dd]=JSON.parse(r.substring(2)); if(e==='control_mode'){ db1._m.splice(i,1); dSenderResync=dd; break; } } }
await new Promise(r=>setTimeout(r,30));
}
assert.ok(dSenderResync && dSenderResync.controlMode==='host-only',
'debounced toggle re-syncs sender to actual state');
close();
resetConnectionRate();
// --- Host role survives peerId dedup (reconnect / second tab) ---
const hdrid = 'dedup-host-'+Date.now();
const hd1 = await c(), hd2 = await c();
await j(hd1, hdrid, 'dhost'); await j(hd2, hdrid, 'dguest'); hd1._m.length = hd2._m.length = 0;
s(hd1,'set_control_mode',{controlMode:'host-only'});
await w(hd1,'control_mode'); await w(hd2,'control_mode');
hd1._m.length = hd2._m.length = 0;
// The host's peerId re-joins on a fresh socket → server dedupes the old socket.
// This must NOT demote the host or reset the mode (a network blip / second tab).
const hd3 = await c();
s(hd3,'join_room',{roomId:hdrid,peerId:'dhost',protocolVersion:'1.0.0'});
const hdrd = await a(hd3);
assert.equal(hdrd[0],'room_data');
assert.ok(hdrd[1].controlMode === 'host-only' && hdrd[1].hostPeerId === 'dhost',
'host role + host-only mode survive peerId dedup (reconnect/second tab)');
close();
resetConnectionRate();
// --- Co-Host: owner promotes a guest to controller (can drive); demote re-gates ---
const crid = 'cohost-'+Date.now();
const co1 = await c(), co2 = await c(), co3 = await c(); // owner / to-promote / stays guest
await j(co1, crid, 'owner'); await j(co2, crid, 'cohost'); await j(co3, crid, 'guestx');
co1._m.length = co2._m.length = co3._m.length = 0;
s(co1,'set_control_mode',{controlMode:'host-only'});
await w(co1,'control_mode'); await w(co2,'control_mode'); await w(co3,'control_mode');
co1._m.length = co2._m.length = co3._m.length = 0;
// before promotion the co-host is gated
s(co2,'pause',{currentTime:1});
let coGatedBefore=false; try { await w(co1,'pause',500); } catch { coGatedBefore=true; }
assert.ok(coGatedBefore, 'co-host gated before promotion');
// owner promotes co-host → controllers broadcast includes owner + cohost
s(co1,'set_peer_role',{peerId:'cohost',controller:true});
let promo=null; const pps=Date.now();
while(Date.now()-pps<800 && !promo){ for(let i=0;i<co2._m.length;i++){const r=co2._m[i];if(r.startsWith('42')){const[e,dd]=JSON.parse(r.substring(2));if(e==='control_mode'){co2._m.splice(i,1);promo=dd;break;}}} await new Promise(r=>setTimeout(r,30)); }
assert.ok(promo && Array.isArray(promo.controllers) && promo.controllers.includes('cohost') && promo.controllers.includes('owner'),
'promotion broadcasts controllers (owner + cohost)');
co1._m.length = co2._m.length = co3._m.length = 0;
// promoted co-host can now drive; a plain guest still cannot
s(co2,'pause',{currentTime:2}); await w(co1,'pause');
s(co3,'play',{currentTime:3});
let plainGuestGated=false; try { await w(co1,'play',500); } catch { plainGuestGated=true; }
assert.ok(plainGuestGated, 'plain guest still gated after a co-host is promoted');
co1._m.length = co2._m.length = co3._m.length = 0;
// a non-owner (the co-host) cannot promote anyone
s(co2,'set_peer_role',{peerId:'guestx',controller:true});
let nonOwnerBlocked=false; try { await w(co3,'control_mode',500); } catch { nonOwnerBlocked=true; }
assert.ok(nonOwnerBlocked, 'non-owner cannot promote (no room broadcast)');
co1._m.length = co2._m.length = co3._m.length = 0;
// owner demotes the co-host → gated again
s(co1,'set_peer_role',{peerId:'cohost',controller:false});
await w(co2,'control_mode');
co1._m.length = co2._m.length = co3._m.length = 0;
s(co2,'seek',{currentTime:4});
let coGatedAfter=false; try { await w(co1,'seek',500); } catch { coGatedAfter=true; }
assert.ok(coGatedAfter, 'demoted co-host is gated again');
close();
resetConnectionRate();
// --- H-1: a demoted co-host's FORCE_SYNC_EXECUTE still relays when they
// initiated the in-flight PREPARE. Without the initiator exemption, the
// EXECUTE would be dropped by the host-only gate and every peer would be
// left stuck paused. ---
const h1rid = 'h1-'+Date.now();
const ho = await c(), hc = await c(), hg = await c(); // owner / co-host / guest
await j(ho, h1rid, 'owner'); await j(hc, h1rid, 'cohost'); await j(hg, h1rid, 'guest');
ho._m.length = hc._m.length = hg._m.length = 0;
s(ho,'set_control_mode',{controlMode:'host-only'});
await w(ho,'control_mode'); await w(hc,'control_mode'); await w(hg,'control_mode');
ho._m.length = hc._m.length = hg._m.length = 0;
// owner promotes co-host; co-host initiates force sync
s(ho,'set_peer_role',{peerId:'cohost',controller:true});
await w(hc,'control_mode');
ho._m.length = hc._m.length = hg._m.length = 0;
s(hc,'force_sync_prepare',{targetTime:0});
await w(ho,'force_sync_prepare'); await w(hg,'force_sync_prepare');
ho._m.length = hc._m.length = hg._m.length = 0;
// owner demotes the co-host mid-flight — the EXECUTE must still go through.
// Wait out the per-room role-change debounce (M-4) so this demote broadcasts:
// in real usage the host can't promote, run a force-sync, and demote inside 500ms.
await new Promise(r => setTimeout(r, 550));
s(ho,'set_peer_role',{peerId:'cohost',controller:false});
await w(hc,'control_mode');
ho._m.length = hc._m.length = hg._m.length = 0;
s(hc,'force_sync_execute',{});
await w(ho,'force_sync_execute'); await w(hg,'force_sync_execute');
// After the EXECUTE, the initiator slot is cleared: a fresh EXECUTE from the
// (now plain guest) co-host is gated again, confirming the exemption is scoped.
ho._m.length = hc._m.length = hg._m.length = 0;
s(hc,'force_sync_execute',{});
let reGated=false; try { await w(ho,'force_sync_execute',500); } catch { reGated=true; }
assert.ok(reGated, 'initiator exemption is cleared after the EXECUTE relayes');
close();
resetConnectionRate();
// --- A guest's stray EXECUTE (no matching PREPARE they initiated) is still gated ---
const grid = 'h1b-'+Date.now();
const go = await c(), gg = await c();
await j(go, grid, 'own'); await j(gg, grid, 'gst');
go._m.length = gg._m.length = 0;
s(go,'set_control_mode',{controlMode:'host-only'});
await w(go,'control_mode'); await w(gg,'control_mode');
go._m.length = gg._m.length = 0;
s(gg,'force_sync_execute',{});
let uninitGated=false; try { await w(go,'force_sync_execute',500); } catch { uninitGated=true; }
assert.ok(uninitGated, 'guest FORCE_SYNC_EXECUTE without a matching PREPARE is gated');
close();
resetConnectionRate();
// =====================================================================
// BACKWARD COMPATIBILITY — old clients (pre-HCM build) against new server.
// These tests simulate an old client by deliberately omitting fields the
// new feature added (desynced in heartbeats, capabilities expectation)
// and by ignoring CONTROL_MODE broadcasts. The wire format for existing
// events must stay byte-compatible: the relay must accept old payloads
// and must not inject new fields old clients would misread.
// =====================================================================
// --- BC-1: Old-client heartbeat (no `desynced` field) is accepted and
// relayed without injecting `desynced`. Old clients never sent the
// field; the relay must strip it from the wire so we don't surprise
// them with unexpected keys. ---
const bcrid = 'bc-'+Date.now();
const bco = await c(), bcn = await c(); // bco = "old", bcn = "new"
await j(bco, bcrid, 'oldp'); await j(bcn, bcrid, 'newp');
bco._m.length = bcn._m.length = 0;
// Old-client heartbeat: every field an old build would send, NO `desynced`.
s(bco,'peer_status',{
status:'heartbeat', username:'old', tabTitle:'t', mediaTitle:'m',
playbackState:'playing', currentTime:42, volume:0.5, muted:false
});
let bcRelay = null; const bcStart = Date.now();
while (Date.now()-bcStart < 800 && !bcRelay) {
for (let i=0;i<bcn._m.length;i++){ const r=bcn._m[i]; if(r.startsWith('42')){ const [e,dd]=JSON.parse(r.substring(2)); if(e==='peer_status'){ bcn._m.splice(i,1); bcRelay=dd; break; } } }
await new Promise(r=>setTimeout(r,30));
}
assert.ok(bcRelay, 'old-style heartbeat relayed');
assert.ok(bcRelay.desynced === undefined, 'old-client heartbeat has no desynced on the wire (stripped)');
assert.equal(bcRelay.currentTime, 42, 'old-client currentTime preserved');
assert.equal(bcRelay.senderId, 'oldp', 'old-client senderId preserved');
close();
resetConnectionRate();
// --- BC-2: Old client in a host-only room — server still gates its events
// even though the client has no awareness of the mode. This is the
// key guarantee for mixed rooms during rollout: an old client can't
// drive a host-only room just because it ignores CONTROL_MODE. ---
const hmrid = 'hcmix-'+Date.now();
const hmo = await c(), hmg = await c(); // hmo = host (new), hmg = "old" guest
await j(hmo, hmrid, 'hmixhost'); await j(hmg, hmrid, 'hmixold');
hmo._m.length = hmg._m.length = 0;
s(hmo,'set_control_mode',{controlMode:'host-only'});
await w(hmo,'control_mode'); await w(hmg,'control_mode'); // old client's socket still receives it; old client would ignore
hmo._m.length = hmg._m.length = 0;
// "Old" guest tries to drive — server must drop. Host must NOT receive it.
s(hmg,'pause',{currentTime:5});
let oldGated=false; try { await w(hmo,'pause',600); } catch { oldGated=true; }
assert.ok(oldGated, 'old-client pause dropped in host-only (server enforces regardless of client awareness)');
// Host's own command still relays to the old client's socket — old client
// applies it via its existing PLAY/PAUSE handler.
s(hmo,'pause',{currentTime:7}); await w(hmg,'pause');
close();
resetConnectionRate();
// --- BC-3: Mixed room with old + new client in 'everyone' mode — every
// event flows identically to pre-HCM. Confirms no regression in the
// default-mode relay path that could fragment a rolling-update room. ---
const mxrid = 'mix-'+Date.now();
const mxo = await c(), mxn = await c(); // mxo = old, mxn = new
await j(mxo, mxrid, 'oldmx'); await j(mxn, mxrid, 'newmx');
mxo._m.length = mxn._m.length = 0;
// Old → new
s(mxo,'play',{currentTime:1}); await w(mxn,'play');
s(mxo,'seek',{currentTime:99}); await w(mxn,'seek');
s(mxo,'force_sync_prepare',{targetTime:5}); await w(mxn,'force_sync_prepare');
s(mxo,'episode_lobby',{expectedTitle:'S1E1'}); await w(mxn,'episode_lobby');
// New → old
mxo._m.length = mxn._m.length = 0;
s(mxn,'pause',{currentTime:2}); await w(mxo,'pause');
s(mxn,'seek',{currentTime:50}); await w(mxo,'seek');
s(mxn,'force_sync_execute',{}); await w(mxo,'force_sync_execute');
s(mxn,'episode_lobby_cancel',{}); await w(mxo,'episode_lobby_cancel');
close();
resetConnectionRate();
// --- BC-4: New-client heartbeat WITH `desynced` does not break an old
// client's receive path. The field is appended but old clients ignore
// unknown keys — verify the relay preserves every pre-HCM field and
// only adds `desynced`. ---
const b4rid = 'bc4-'+Date.now();
const b4old = await c(), b4new = await c();
await j(b4old, b4rid, 'b4o'); await j(b4new, b4rid, 'b4n');
b4old._m.length = b4new._m.length = 0;
s(b4new,'peer_status',{status:'heartbeat', desynced:true, currentTime:7, playbackState:'paused', username:'newp'});
let b4Relay = null; const b4Start = Date.now();
while (Date.now()-b4Start < 800 && !b4Relay) {
for (let i=0;i<b4old._m.length;i++){ const r=b4old._m[i]; if(r.startsWith('42')){ const [e,dd]=JSON.parse(r.substring(2)); if(e==='peer_status'){ b4old._m.splice(i,1); b4Relay=dd; break; } } }
await new Promise(r=>setTimeout(r,30));
}
assert.ok(b4Relay, 'new-client heartbeat relayed to old client');
assert.equal(b4Relay.desynced, true, 'desynced preserved for new recipients');
assert.equal(b4Relay.currentTime, 7, 'old fields preserved on the same relay');
assert.equal(b4Relay.senderId, 'b4n', 'senderId preserved');
close();
resetConnectionRate();
// --- Password room ---
const prid = 'pw-'+Date.now();
@@ -93,7 +408,7 @@ try {
let d=''; res.on('data',c=>d+=c); res.on('end',()=>r([res.statusCode,JSON.parse(d)])); }));
assert.equal(st,200); assert.equal(body.status,'online');
console.log('All 13 WebSocket integration tests passed');
console.log('All WebSocket integration tests passed (incl. host control mode)');
} catch(e) {
console.error('FAILED:', e.message);
process.exitCode=1;
+82
View File
@@ -0,0 +1,82 @@
import assert from 'node:assert/strict';
import {
TITLE_PRIVACY_MODES,
applyTitlePrivacyToPayload,
normalizeSendTabTitle,
normalizeTabTitle,
normalizeTitlePrivacyMode,
sanitizeSharedTitle,
sanitizeTabTitle
} from '../extension/title-privacy.js';
assert.equal(normalizeTitlePrivacyMode(undefined), TITLE_PRIVACY_MODES.FULL);
assert.equal(normalizeTitlePrivacyMode('unknown'), TITLE_PRIVACY_MODES.FULL);
assert.equal(normalizeTitlePrivacyMode(TITLE_PRIVACY_MODES.HIDDEN), TITLE_PRIVACY_MODES.HIDDEN);
assert.equal(normalizeSendTabTitle(undefined, TITLE_PRIVACY_MODES.FULL), true);
assert.equal(normalizeSendTabTitle(undefined, TITLE_PRIVACY_MODES.EPISODE), false);
assert.equal(normalizeSendTabTitle(true, TITLE_PRIVACY_MODES.HIDDEN), true);
assert.equal(normalizeSendTabTitle(false, TITLE_PRIVACY_MODES.FULL), false);
assert.equal(normalizeTabTitle('(12) Testvideo - YouTube'), 'Testvideo - YouTube');
assert.equal(normalizeTabTitle('[7] Testvideo - YouTube'), 'Testvideo - YouTube');
assert.equal(normalizeTabTitle('(500) Days of Summer'), '(500) Days of Summer');
assert.equal(normalizeTabTitle(' '), null);
assert.equal(sanitizeTabTitle('Private Tab', true), 'Private Tab');
assert.equal(sanitizeTabTitle('(12) Private Tab', true), 'Private Tab');
assert.equal(sanitizeTabTitle('Private Tab', false), null);
assert.equal(sanitizeTabTitle('', true), null);
assert.equal(sanitizeSharedTitle('Example Movie', 'full'), 'Example Movie');
assert.equal(sanitizeSharedTitle('', 'full'), null);
assert.equal(sanitizeSharedTitle(null, 'full'), null);
assert.equal(sanitizeSharedTitle('Show Name - S01/E04 - Title', 'episode'), 'S01E04');
assert.equal(sanitizeSharedTitle('Folge 7 - Private Server', 'episode'), 'EP007');
assert.equal(sanitizeSharedTitle('Example Movie', 'episode'), null);
assert.equal(sanitizeSharedTitle('Show Name - S01E04', 'hidden'), null);
assert.equal(sanitizeSharedTitle('Private Tab Title', 'hidden'), null);
assert.deepEqual(
applyTitlePrivacyToPayload({
tabTitle: 'Private Jellyfin - S01E04',
mediaTitle: 'Show Name - S01E04',
currentTime: 42
}, 'episode'),
{
tabTitle: 'Private Jellyfin - S01E04',
mediaTitle: 'S01E04',
currentTime: 42
},
'media privacy must not rewrite tabTitle'
);
assert.deepEqual(
applyTitlePrivacyToPayload({
tabTitle: 'Private Jellyfin - S01E04',
status: 'heartbeat'
}, 'episode'),
{
tabTitle: 'Private Jellyfin - S01E04',
status: 'heartbeat'
},
'media privacy must not rewrite tabTitle or add absent media keys'
);
assert.deepEqual(
applyTitlePrivacyToPayload({
tabTitle: 'Private Tab',
mediaTitle: 'Private Media',
expectedTitle: 'S01E04',
title: 'S01E04'
}, 'hidden'),
{
tabTitle: 'Private Tab',
mediaTitle: null,
expectedTitle: null,
title: null
},
'hidden media privacy must not clear tabTitle'
);
console.log('title-privacy tests passed');
+2
View File
@@ -7,12 +7,14 @@ import path from 'node:path';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const checks = [
['vitest unit tests', 'npm', ['run', 'test:unit']],
['server ops', 'node', ['scripts/test-server-ops.mjs']],
['server routes', 'node', ['scripts/test-server-routes.mjs'], {
env: { ADMIN_METRICS_TOKEN: 'verify-admin-token-with-more-than-32-chars' }
}],
['rate-limiter unit tests', 'node', ['scripts/test-rate-limiter.mjs']],
['episode-utils unit tests', 'node', ['scripts/test-episode-utils.mjs']],
['title privacy unit tests', 'node', ['scripts/test-title-privacy.mjs']],
['server WebSocket integration', 'node', ['scripts/test-server-ws.mjs']],
['names generator', 'node', ['scripts/test-names.mjs']],
['content video finder', 'node', ['scripts/test-content-video-finder.cjs']],
+5
View File
@@ -2,6 +2,11 @@ PORT="3000"
MIN_VERSION="1.0.0"
MAX_ROOMS="1000"
MAX_PEERS_PER_ROOM="25"
# Required for production/self-hosting: unique random salt used for room-password hashes.
# Generate one with: openssl rand -base64 32
SERVER_SALT=""
# Optional: enables aggregate-only admin metrics on /health with Authorization: Bearer <token>
# Use a long random token, 32+ characters recommended.
ADMIN_METRICS_TOKEN=""
+27
View File
@@ -16,6 +16,11 @@ PORT="3000"
MAX_ROOMS="1000"
MAX_PEERS_PER_ROOM="25"
MIN_VERSION="1.0.0"
# Required for production/self-hosting: unique random salt used for room-password hashes.
# Generate one with: openssl rand -base64 32
SERVER_SALT=""
# Optional: enables aggregate-only admin metrics on /health with Authorization: Bearer <token>
# Use a long random token, 32+ characters recommended.
ADMIN_METRICS_TOKEN=""
@@ -24,6 +29,19 @@ ADMIN_METRICS_TOKEN=""
DEBUG_LOGGING="0"
```
### Room Password Salt
`SERVER_SALT` is used to HMAC room passwords before they are stored in RAM. The relay has a built-in fallback so development keeps working, but that fallback is public in the repository and the server logs a startup warning when it is used.
For every real self-hosted deployment, set a unique random value:
```bash
openssl rand -base64 32
```
Then add it to `server/.env` or your Compose environment:
```bash
SERVER_SALT=replace-with-a-long-random-salt
```
### Health & Metrics
`GET /` and `GET /health` are IP-rate-limited to 10 requests per minute per client IP. These health-style responses are cached server-side for 60 seconds and refreshed lazily on request. By default `/health` returns only basic service status, uptime, room count, connection count, and a timestamp.
@@ -63,11 +81,20 @@ npm install
npm start
```
### Verification
The server is covered by the root verification suite. From the repository root, run:
```bash
npm run verify
```
For focused server checks, see `scripts/test-server-ops.mjs`, `scripts/test-server-routes.mjs`, `scripts/test-server-ws.mjs`, and `scripts/test-rate-limiter.mjs`.
## Security
- **Rate Limiting**: IP-based connection limits and socket-based event limits.
- **Health Endpoint Throttle**: `GET /` and `GET /health` are limited to 10 requests per minute per IP, with 60-second lazy server-side response caching and stricter throttling for wrong admin bearer attempts.
- **Room Discovery Throttle**: Room-list refreshes are rate-limited server-side to one request every 10 seconds per socket.
- **Token Handshake**: Requires a valid token defined in the root `shared/constants.js`.
- **Password Hash Salt**: Set `SERVER_SALT` in every deployment so room-password hashes are not derived with the public fallback salt.
- **Single Source of Truth**: The server imports constants directly from the root `shared/` directory.
- **In-Memory**: Rooms are automatically pruned after 2 hours of inactivity.
- **Reverse Proxy Boundary**: The server trusts one reverse proxy hop for client IP detection. Keep the Node port private/firewalled so clients can only reach it through Caddy or another trusted proxy.
+237 -16
View File
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'url';
import { Server } from 'socket.io';
import crypto from 'crypto';
import dotenv from 'dotenv';
import { EVENTS, OFFICIAL_SERVER_TOKEN, PROTOCOL_VERSION } from '../shared/constants.js';
import { EVENTS, OFFICIAL_SERVER_TOKEN, PROTOCOL_VERSION, CONTROL_MODES, CAPABILITIES } from '../shared/constants.js';
import {
buildHealthPayload,
checkCooldown,
@@ -22,6 +22,7 @@ import {
healthCounts,
adminMetricsAuthCounts,
roomListCooldowns,
leaveRoomCounts,
rateLimitDenied,
checkAuthRate,
recordAuthFailure,
@@ -29,6 +30,7 @@ import {
checkEventRate,
checkHealthRate,
checkAdminMetricsAuthRate,
checkLeaveRoomRate,
startRateLimitCleanup,
stopRateLimitCleanup,
clearRateLimitMaps
@@ -64,6 +66,10 @@ if (!isAdminMetricsTokenStrong(ADMIN_METRICS_TOKEN)) {
console.warn('[SECURITY] ADMIN_METRICS_TOKEN is set but shorter than 32 characters. Use a long random token.');
}
if (!process.env.SERVER_SALT) {
console.warn('[SECURITY] SERVER_SALT is not set — using the built-in default salt (public in the repo). Set a unique SERVER_SALT so room-password hashes are not computed with a known salt.');
}
export const app = express();
app.set('trust proxy', 1); // For real client IP through reverse proxy
@@ -110,7 +116,8 @@ app.get('/health', (req, res) => {
health: healthCounts.size,
adminMetricsAuth: adminMetricsAuthCounts.size,
authFailures: failedAuthAttempts.size,
roomList: roomListCooldowns.size
roomList: roomListCooldowns.size,
leaveRoom: leaveRoomCounts.size
},
rateLimitDenied
})
@@ -148,6 +155,39 @@ const peerToSocket = new Map(); // peerId -> socketId (Global lookup)
const roomCreationLocks = new Map(); // roomId -> Promise (prevents race on room creation)
const peerJoinLocks = new Map(); // peerId -> Promise (prevents race on same peerId joins)
// Host Control Mode: events a non-host guest may NOT initiate while a room is in
// 'host-only' mode (they would move/disrupt everyone). Reactions like FORCE_SYNC_ACK,
// EPISODE_READY, PEER_STATUS heartbeats remain allowed for all peers.
const HOST_ONLY_GATED_EVENTS = new Set([
EVENTS.PLAY,
EVENTS.PAUSE,
EVENTS.SEEK,
EVENTS.FORCE_SYNC_PREPARE,
EVENTS.FORCE_SYNC_EXECUTE,
EVENTS.EPISODE_LOBBY,
EVENTS.EPISODE_LOBBY_CANCEL
]);
// Features this relay supports, advertised to clients in ROOM_DATA so they can
// enable matching UI/behavior only when the server actually backs it. Append a
// flag here when a new server-gated feature ships (e.g. co-host promotion).
const SERVER_CAPABILITIES = [CAPABILITIES.HOST_CONTROL, CAPABILITIES.CO_HOST];
// M-4: minimum interval between CONTROL_MODE changes per room. Stops a rapidly
// toggling host from thrashing every guest's UI (locked/unlocked/locked...) and
// from generating one broadcast per toggle across all peers.
const CONTROL_MODE_MIN_INTERVAL_MS = 500;
// Canonical control-mode/role snapshot sent to clients. controllers always includes
// the owner (hostPeerId). Built in one place so every emit stays consistent.
function controlModePayload(room) {
return {
controlMode: room.controlMode,
hostPeerId: room.hostPeerId,
controllers: room.controllers ? Array.from(room.controllers) : (room.hostPeerId ? [room.hostPeerId] : [])
};
}
function log(type, message, details = '') {
const debugLogging = process.env.DEBUG_LOGGING === '1';
const isVerbose = type === 'CONN' || type === 'ROOM' || type === 'DEDUPE' || type === 'CORS' || type === 'ACKDROP';
@@ -201,6 +241,42 @@ function removePeerFromRoom(socketId, roomId, reason) {
}
}
// 3.6. Host Control Mode: if the host left (and isn't still connected via another
// socket), fall back to 'everyone' so the room never gets stuck locked, and
// reassign host to the earliest remaining peer so the feature stays usable.
// (v1: immediate fallback, no grace period — see host-control-mode docs.)
// Skip while a join for this peerId is in flight (peerJoinLocks holds it):
// that's the reconnect / second-tab case where the same peerId is being
// re-added right after the dedupe kicked the old socket. Demoting there
// would silently unlock the room on every second-tab open.
// NOTE: this does *not* cover a true network blip where the old socket's
// 'disconnect' fires before the new socket acquires its join lock — in that
// window peerJoinLocks is empty, so the fallback fires. Documented v1
// limitation (no host grace period); see KNOWN_LIMITATIONS.md.
const peerRejoining = peerJoinLocks.has(peerId);
const peerGone = !isPeerStillConnected && !peerRejoining;
if (peerGone && room.controllers && room.peers.size > 0) {
const wasController = room.controllers.has(peerId);
room.controllers.delete(peerId);
// H-1: a leaving initiator strands the room's force-sync — release the
// slot so a future controller's PREPARE can take over cleanly.
if (room.forceSyncInitiator === peerId) room.forceSyncInitiator = null;
if (room.hostPeerId === peerId) {
// Owner left → reassign owner + fall back to 'everyone' so the room is
// never stuck locked, and reset the controller set to just the new owner.
const nextPeerData = room.peerData.values().next().value;
room.hostPeerId = nextPeerData ? nextPeerData.peerId : null;
room.controlMode = CONTROL_MODES.EVERYONE;
room.controllers = new Set(room.hostPeerId ? [room.hostPeerId] : []);
io.to(roomId).emit(EVENTS.CONTROL_MODE, controlModePayload(room));
log('ROOM', `Owner left room ${roomId.substring(0, 3)}*** — fell back to 'everyone', new owner: ${room.hostPeerId}`);
} else if (wasController) {
// A co-host left → keep the mode, just broadcast the updated controller list.
io.to(roomId).emit(EVENTS.CONTROL_MODE, controlModePayload(room));
log('ROOM', `Controller ${peerId} left room ${roomId.substring(0, 3)}***`);
}
}
// 4. Delete empty room
if (room.peers.size === 0) {
rooms.delete(roomId);
@@ -333,7 +409,19 @@ io.on('connection', (socket) => {
peers: new Set(),
peerIds: new Map(),
peerData: new Map(),
lastActivity: Date.now()
lastActivity: Date.now(),
// Host Control Mode: creator (first joiner) is the host/owner.
hostPeerId: peerId,
controlMode: CONTROL_MODES.EVERYONE,
lastControlModeChangeAt: 0, // M-4: per-room debounce for control-mode toggles
lastRoleChangeAt: 0, // M-4: per-room debounce for role promote/demote
// Co-Host: peers allowed to drive in 'host-only'. Always includes the owner.
controllers: new Set([peerId]),
// H-1: peerId of the in-flight force-sync initiator. Lets a demoted
// controller's FORCE_SYNC_EXECUTE through the host-only gate — without
// it, demoting a co-host mid-force-sync would drop their EXECUTE and
// leave every peer stuck paused.
forceSyncInitiator: null
};
rooms.set(roomId, room);
createdByMe = true;
@@ -415,10 +503,14 @@ io.on('connection', (socket) => {
peerToSocket.set(peerId, socket.id);
socket.to(roomId).emit(EVENTS.PEER_STATUS, { peerId, username: username || null, tabTitle: tabTitle || null, mediaTitle: mediaTitle || null, status: 'joined' });
socket.emit(EVENTS.ROOM_DATA, {
roomId,
socket.emit(EVENTS.ROOM_DATA, {
roomId,
peers: Array.from(room.peers).map(sid => room.peerData.get(sid)),
activeLobby: room.activeLobby || null
activeLobby: room.activeLobby || null,
hostPeerId: room.hostPeerId || null,
controlMode: room.controlMode || CONTROL_MODES.EVERYONE,
controllers: room.controllers ? Array.from(room.controllers) : [],
capabilities: SERVER_CAPABILITIES
});
log('ROOM', `Peer ${peerId} joined: ${roomId.substring(0, 3)}***`);
} finally {
@@ -458,7 +550,35 @@ io.on('connection', (socket) => {
const room = rooms.get(mapping.roomId);
if (room) {
room.lastActivity = Date.now();
// --- Host Control Mode gate ---
// In 'host-only' mode, drop room-moving events from anyone who is not
// a controller (the owner + any promoted co-hosts). Robust chokepoint:
// independent of client behavior, kills spam. Heartbeats/ACKs pass.
//
// H-1 exception: a demoted co-host's FORCE_SYNC_EXECUTE still has to
// land — otherwise their already-relayed PREPARE would leave the whole
// room stuck paused. Track the in-flight initiator on PREPARE and let
// their matching EXECUTE through regardless of current controllers set.
if (eventName === EVENTS.FORCE_SYNC_PREPARE &&
room.controlMode === CONTROL_MODES.HOST_ONLY &&
room.controllers && room.controllers.has(mapping.peerId)) {
room.forceSyncInitiator = mapping.peerId;
}
const isOwnForceSyncExecute = eventName === EVENTS.FORCE_SYNC_EXECUTE &&
room.forceSyncInitiator && mapping.peerId === room.forceSyncInitiator;
if (!isOwnForceSyncExecute &&
room.controlMode === CONTROL_MODES.HOST_ONLY &&
!(room.controllers && room.controllers.has(mapping.peerId)) &&
HOST_ONLY_GATED_EVENTS.has(eventName)) {
log('ROOM', `Dropped ${eventName} from guest ${mapping.peerId} in host-only room ${mapping.roomId.substring(0, 3)}***`);
return;
}
// Clear initiator tracking once the EXECUTE has been relayed.
if (eventName === EVENTS.FORCE_SYNC_EXECUTE && room.forceSyncInitiator) {
room.forceSyncInitiator = null;
}
// --- S-2 & S-3: Sanitize ALL relay fields (strings, numbers, booleans) ---
const clamp = (val, max) => typeof val === 'string' ? val.substring(0, max) : undefined;
const clampNum = (val, min, max) => typeof val === 'number' && Number.isFinite(val) ? Math.max(min, Math.min(max, val)) : undefined;
@@ -469,12 +589,13 @@ io.on('connection', (socket) => {
room.peerData.set(socket.id, {
...existing,
username: data.username !== undefined ? (clamp(data.username, 30) ?? existing.username) : existing.username,
tabTitle: data.tabTitle !== undefined ? (clamp(data.tabTitle, 100) ?? existing.tabTitle) : existing.tabTitle,
mediaTitle: data.mediaTitle !== undefined ? (clamp(data.mediaTitle, 100) ?? existing.mediaTitle) : existing.mediaTitle,
tabTitle: data.tabTitle === null ? null : (data.tabTitle !== undefined ? (clamp(data.tabTitle, 100) ?? existing.tabTitle) : existing.tabTitle),
mediaTitle: data.mediaTitle === null ? null : (data.mediaTitle !== undefined ? (clamp(data.mediaTitle, 100) ?? existing.mediaTitle) : existing.mediaTitle),
playbackState: data.playbackState !== undefined ? (validState(data.playbackState) ?? existing.playbackState) : existing.playbackState,
currentTime: data.currentTime !== undefined ? (clampNum(data.currentTime, 0, 86400) ?? existing.currentTime) : existing.currentTime,
currentTime: data.currentTime === null ? null : (data.currentTime !== undefined ? (clampNum(data.currentTime, 0, 86400) ?? existing.currentTime) : existing.currentTime),
volume: data.volume !== undefined ? (clampNum(data.volume, 0, 1) ?? existing.volume) : existing.volume,
muted: data.muted !== undefined ? (validBool(data.muted) ?? existing.muted) : existing.muted,
desynced: data.desynced !== undefined ? (validBool(data.desynced) === true) : (existing.desynced || false),
lastSeen: Date.now()
});
@@ -482,14 +603,15 @@ io.on('connection', (socket) => {
const relayPayload = {
senderId: mapping.peerId,
seq: clampNum(data.seq, 0, Number.MAX_SAFE_INTEGER),
currentTime: clampNum(data.currentTime, 0, 86400),
currentTime: data.currentTime === null ? null : clampNum(data.currentTime, 0, 86400),
targetTime: clampNum(data.targetTime, 0, 86400),
playbackState: validState(data.playbackState),
username: clamp(data.username, 30),
tabTitle: clamp(data.tabTitle, 100),
mediaTitle: clamp(data.mediaTitle, 100),
tabTitle: data.tabTitle === null ? null : clamp(data.tabTitle, 100),
mediaTitle: data.mediaTitle === null ? null : clamp(data.mediaTitle, 100),
volume: clampNum(data.volume, 0, 1),
muted: validBool(data.muted),
desynced: validBool(data.desynced),
peerId: mapping.peerId,
status: typeof data.status === 'string' ? data.status.substring(0, 16) : undefined,
expectedTitle: clamp(data.expectedTitle, 100),
@@ -542,6 +664,11 @@ io.on('connection', (socket) => {
});
socket.on(EVENTS.LEAVE_ROOM, () => {
if (!checkLeaveRoomRate(socket.id)) {
log('SECURITY', `LEAVE_ROOM rate limit exceeded for socket: ${socket.id}`);
socket.disconnect(true);
return;
}
try {
const mapping = socketToRoom.get(socket.id);
if (mapping) {
@@ -553,6 +680,97 @@ io.on('connection', (socket) => {
}
});
socket.on(EVENTS.SET_CONTROL_MODE, (data) => {
if (!checkEventRate(socket.id)) {
log('SECURITY', `Event rate limit exceeded for socket (SET_CONTROL_MODE): ${socket.id}`);
socket.disconnect(true);
return;
}
if (!data || typeof data !== 'object') return;
const mode = data.controlMode;
if (mode !== CONTROL_MODES.EVERYONE && mode !== CONTROL_MODES.HOST_ONLY) return;
const mapping = socketToRoom.get(socket.id);
if (!mapping) return;
const room = rooms.get(mapping.roomId);
if (!room) return;
// Only the owner may change the control mode.
if (mapping.peerId !== room.hostPeerId) {
log('AUTH', `Non-host ${mapping.peerId} tried to set control mode in ${mapping.roomId.substring(0, 3)}***`);
// Re-sync the sender to the actual room state so any optimistic UI
// (e.g. popup toggle) reverts — covers stale-local-amHost races (H-5).
socket.emit(EVENTS.CONTROL_MODE, controlModePayload(room));
return;
}
if (room.controlMode === mode) return; // no-op, ignore (UI debounce backstop)
// M-4: per-room debounce — reject toggles faster than CONTROL_MODE_MIN_INTERVAL_MS.
// The host still gets the final state via the next legit change; this just
// kills the spam vector.
const now = Date.now();
if (now - room.lastControlModeChangeAt < CONTROL_MODE_MIN_INTERVAL_MS) {
log('ROOM', `Control mode toggle debounced in ${mapping.roomId.substring(0, 3)}***`);
// Re-sync the sender so any optimistic UI matches the actual (unchanged) state.
socket.emit(EVENTS.CONTROL_MODE, controlModePayload(room));
return;
}
room.controlMode = mode;
room.lastControlModeChangeAt = now;
room.lastActivity = now;
io.to(mapping.roomId).emit(EVENTS.CONTROL_MODE, controlModePayload(room));
log('ROOM', `Control mode set to '${mode}' by host in room ${mapping.roomId.substring(0, 3)}***`);
});
socket.on(EVENTS.SET_PEER_ROLE, (data) => {
if (!checkEventRate(socket.id)) {
log('SECURITY', `Event rate limit exceeded for socket (SET_PEER_ROLE): ${socket.id}`);
socket.disconnect(true);
return;
}
if (!data || typeof data !== 'object') return;
const targetPeerId = typeof data.peerId === 'string' ? data.peerId.substring(0, 16) : null;
const makeController = data.controller === true;
if (!targetPeerId) return;
const mapping = socketToRoom.get(socket.id);
if (!mapping) return;
const room = rooms.get(mapping.roomId);
if (!room || !room.controllers) return;
// Only the owner may promote/demote controllers.
if (mapping.peerId !== room.hostPeerId) {
log('AUTH', `Non-owner ${mapping.peerId} tried to set peer role in ${mapping.roomId.substring(0, 3)}***`);
socket.emit(EVENTS.CONTROL_MODE, controlModePayload(room));
return;
}
// The owner is always a controller and cannot be demoted.
if (targetPeerId === room.hostPeerId) return;
// Target must be a peer currently in the room.
const targetPresent = Array.from(room.peerData.values()).some(d => d.peerId === targetPeerId);
if (!targetPresent) return;
const now = Date.now();
if (now - (room.lastRoleChangeAt || 0) < CONTROL_MODE_MIN_INTERVAL_MS) {
log('ROOM', `Role change debounced in ${mapping.roomId.substring(0, 3)}***`);
socket.emit(EVENTS.CONTROL_MODE, controlModePayload(room));
return;
}
if (makeController) {
if (room.controllers.has(targetPeerId)) return; // no-op
room.controllers.add(targetPeerId);
} else {
if (!room.controllers.has(targetPeerId)) return; // no-op
room.controllers.delete(targetPeerId);
}
room.lastRoleChangeAt = now;
room.lastActivity = now;
io.to(mapping.roomId).emit(EVENTS.CONTROL_MODE, controlModePayload(room));
log('ROOM', `Peer ${targetPeerId} ${makeController ? 'promoted to' : 'demoted from'} controller in ${mapping.roomId.substring(0, 3)}***`);
});
socket.on(EVENTS.EVENT_ACK, (data) => {
if (!checkEventRate(socket.id)) {
log('SECURITY', `Event rate limit exceeded for socket (ACK): ${socket.id}`);
@@ -629,6 +847,7 @@ io.on('connection', (socket) => {
socket.on('disconnect', () => {
eventCounts.delete(socket.id);
roomListCooldowns.delete(socket.id);
leaveRoomCounts.delete(socket.id);
const mapping = socketToRoom.get(socket.id);
if (mapping) {
try {
@@ -709,12 +928,14 @@ function gracefulShutdown(signal) {
log('SERVER', `${signal} received — starting graceful shutdown...`);
// 1. Notify all connected clients so they can display a meaningful message
io.emit(EVENTS.ERROR, { message: 'Server is restarting. Reconnecting automatically...' });
// 2. Stop accepting new HTTP connections
// 2. Gracefully disconnect all Socket.IO clients
io.disconnectSockets(true);
// 3. Stop accepting new HTTP connections
httpServer.close(() => {
log('SERVER', 'HTTP server closed. Exiting.');
process.exit(0);
});
// 3. Safety net: force-exit after 5s if connections don't drain
// 4. Safety net: force-exit after 5s if connections don't drain
setTimeout(() => {
log('SERVER', 'Force-exit after timeout.');
process.exit(1);
@@ -733,7 +954,7 @@ export async function stopServerForTests() {
healthResponseCache.clear();
io.removeAllListeners();
io.disconnectSockets(true);
Object.assign(rateLimitDenied, { connections: 0, events: 0, health: 0, adminMetricsAuth: 0, roomList: 0 });
Object.assign(rateLimitDenied, { connections: 0, events: 0, health: 0, adminMetricsAuth: 0, roomList: 0, leaveRoom: 0 });
if (!httpServer.listening) return;
await new Promise((resolve, reject) => {
httpServer.close((err) => err ? reject(err) : resolve());
+4 -2
View File
@@ -97,14 +97,16 @@ export function buildHealthPayload({
health: rateLimitSizes.health || 0,
adminMetricsAuth: rateLimitSizes.adminMetricsAuth || 0,
authFailures: rateLimitSizes.authFailures || 0,
roomList: rateLimitSizes.roomList || 0
roomList: rateLimitSizes.roomList || 0,
leaveRoom: rateLimitSizes.leaveRoom || 0
},
denied: {
connections: rateLimitDenied.connections || 0,
events: rateLimitDenied.events || 0,
health: rateLimitDenied.health || 0,
adminMetricsAuth: rateLimitDenied.adminMetricsAuth || 0,
roomList: rateLimitDenied.roomList || 0
roomList: rateLimitDenied.roomList || 0,
leaveRoom: rateLimitDenied.leaveRoom || 0
}
},
memory: {
+26 -1
View File
@@ -14,6 +14,8 @@ export const EVENT_RATE_LIMIT = 50; // max relayed events per soc
export const EVENT_RATE_WINDOW_MS = 10000; // 10 seconds
export const HEALTH_RATE_WINDOW_MS = 60000; // 1 minute
export const ADMIN_METRICS_AUTH_WINDOW_MS = 60000; // 1 minute
export const LEAVE_ROOM_RATE_LIMIT = 10; // max LEAVE_ROOM events per socket per window
export const LEAVE_ROOM_RATE_WINDOW_MS = 60000; // 1 minute
export const connectionCounts = new Map(); // ip -> { count, resetTime }
export const failedAuthAttempts = new Map(); // Map<IP+RoomID, {count, lastAttempt}>
@@ -21,13 +23,15 @@ export const eventCounts = new Map(); // socketId -> { count, resetTime }
export const healthCounts = new Map(); // ip -> { count, resetTime }
export const adminMetricsAuthCounts = new Map(); // ip -> { count, resetTime }
export const roomListCooldowns = new Map(); // socketId -> last allowed timestamp
export const leaveRoomCounts = new Map(); // socketId -> { count, resetTime }
export const rateLimitDenied = {
connections: 0,
events: 0,
health: 0,
adminMetricsAuth: 0,
roomList: 0
roomList: 0,
leaveRoom: 0
};
let authCleanupId = null;
@@ -122,6 +126,20 @@ export function checkAdminMetricsAuthRate(ip) {
return false;
}
export function checkLeaveRoomRate(socketId) {
const now = Date.now();
const entry = leaveRoomCounts.get(socketId) || { count: 0, resetTime: now + LEAVE_ROOM_RATE_WINDOW_MS };
if (now > entry.resetTime) {
entry.count = 0;
entry.resetTime = now + LEAVE_ROOM_RATE_WINDOW_MS;
}
entry.count++;
leaveRoomCounts.set(socketId, entry);
if (entry.count <= LEAVE_ROOM_RATE_LIMIT) return true;
rateLimitDenied.leaveRoom++;
return false;
}
export function startRateLimitCleanup(io) {
if (authCleanupId !== null || rateLimitCleanupId !== null) return; // guard double-start
// Clean up old auth failure records (every 15 minutes)
@@ -145,6 +163,11 @@ export function startRateLimitCleanup(io) {
eventCounts.delete(socketId);
}
}
for (const [socketId, entry] of leaveRoomCounts.entries()) {
if (now > entry.resetTime || !io.sockets.sockets.has(socketId)) {
leaveRoomCounts.delete(socketId);
}
}
for (const [ip, entry] of healthCounts.entries()) {
if (now > entry.resetTime) healthCounts.delete(ip);
}
@@ -166,7 +189,9 @@ export function clearRateLimitMaps() {
connectionCounts.clear();
failedAuthAttempts.clear();
eventCounts.clear();
healthCounts.clear();
adminMetricsAuthCounts.clear();
roomListCooldowns.clear();
leaveRoomCounts.clear();
}
+107
View File
@@ -0,0 +1,107 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import {
checkLeaveRoomRate,
LEAVE_ROOM_RATE_LIMIT,
LEAVE_ROOM_RATE_WINDOW_MS,
rateLimitDenied,
leaveRoomCounts,
clearRateLimitMaps
} from './rate-limiter.js';
describe('LEAVE_ROOM Rate Limiter', () => {
const testSocketId = 'test-socket-123';
beforeEach(() => {
clearRateLimitMaps();
rateLimitDenied.leaveRoom = 0;
});
afterEach(() => {
clearRateLimitMaps();
});
it('should allow LEAVE_ROOM within limit', () => {
// Test within the rate limit
for (let i = 0; i < LEAVE_ROOM_RATE_LIMIT; i++) {
const result = checkLeaveRoomRate(testSocketId);
expect(result).toBe(true);
}
expect(rateLimitDenied.leaveRoom).toBe(0);
});
it('should block LEAVE_ROOM when exceeding limit', () => {
// Fill up to the limit
for (let i = 0; i < LEAVE_ROOM_RATE_LIMIT; i++) {
checkLeaveRoomRate(testSocketId);
}
// Next request should be blocked
const result = checkLeaveRoomRate(testSocketId);
expect(result).toBe(false);
expect(rateLimitDenied.leaveRoom).toBe(1);
});
it('should reset count after window expires', () => {
// Fill up to the limit
for (let i = 0; i < LEAVE_ROOM_RATE_LIMIT; i++) {
checkLeaveRoomRate(testSocketId);
}
// Verify we're at the limit
let result = checkLeaveRoomRate(testSocketId);
expect(result).toBe(false);
// Fast-forward time beyond the rate limit window
const entry = leaveRoomCounts.get(testSocketId);
entry.resetTime = Date.now() - LEAVE_ROOM_RATE_WINDOW_MS - 1000;
leaveRoomCounts.set(testSocketId, entry);
// Next request should be allowed again
result = checkLeaveRoomRate(testSocketId);
expect(result).toBe(true);
});
it('should handle multiple sockets independently', () => {
const socketId2 = 'test-socket-456';
// Fill up first socket
for (let i = 0; i < LEAVE_ROOM_RATE_LIMIT; i++) {
checkLeaveRoomRate(testSocketId);
}
// Second socket should still be allowed
const result = checkLeaveRoomRate(socketId2);
expect(result).toBe(true);
// First socket should be blocked
const result2 = checkLeaveRoomRate(testSocketId);
expect(result2).toBe(false);
});
it('should increment rateLimitDenied counter on block', () => {
for (let i = 0; i < LEAVE_ROOM_RATE_LIMIT; i++) {
checkLeaveRoomRate(testSocketId);
}
checkLeaveRoomRate(testSocketId);
expect(rateLimitDenied.leaveRoom).toBe(1);
checkLeaveRoomRate(testSocketId);
expect(rateLimitDenied.leaveRoom).toBe(2);
});
it('should be cleared by the shared reset helper', () => {
checkLeaveRoomRate(testSocketId);
expect(leaveRoomCounts.size).toBe(1);
clearRateLimitMaps();
expect(leaveRoomCounts.size).toBe(0);
});
});
describe('Rate Limit Constants', () => {
it('should have correct rate limit values', () => {
expect(LEAVE_ROOM_RATE_LIMIT).toBe(10);
expect(LEAVE_ROOM_RATE_WINDOW_MS).toBe(60000); // 1 minute
});
});
+61 -21
View File
@@ -1,33 +1,73 @@
# KoalaSync Shared Constants
This directory contains constants and protocol definitions used by both the extension and the server.
This directory is the source of truth for protocol constants and shared browser/server data.
## Where You Are
- `constants.js`: protocol version, app version, official URLs/token, event names, control modes, capabilities, and timing constants.
- `blacklist.js`: domains hidden by the popup's clutter filter.
- `names.js`: generated username parts and helper used by the extension.
- `extension/shared/`: generated mirror created by `npm run build:extension`; do not edit that mirror directly.
## Syncing with the Extension
> [!IMPORTANT]
> Every time this directory is modified, you must run `node scripts/build-extension.cjs` to keep the extension's copy up to date.
Because Browser Extensions (Manifest V3) cannot load files outside their root directory, all files in this directory must be copied to `extension/shared/` whenever they are modified. The build script handles this automatically.
> [!IMPORTANT]
> After modifying any file in this directory, run `npm run build:extension` from the repository root.
Browser extensions cannot import files outside their own root directory, so the build script copies shared files into `extension/shared/` and injects selected constants into `content.js`.
## Security & Versioning Constants
- `OFFICIAL_SERVER_TOKEN`: A 32-byte hex token required to connect to the official relay server.
- `APP_VERSION`: The current version of the extension. Automatically injected from the git tag during CI release builds.
- `OFFICIAL_SERVER_URL`: The default endpoint for the official KoalaSync relay.
- `PROTOCOL_VERSION`: exact protocol version required during `join_room`.
- `APP_VERSION`: extension/app version, injected from release tags by CI.
- `OFFICIAL_SERVER_URL`: default public relay endpoint.
- `OFFICIAL_LANDING_PAGE_URL`: public website and invitation bridge origin.
- `OFFICIAL_SERVER_TOKEN`: public client token used to reject non-KoalaSync Socket.IO clients. It is not a secret; see `docs/KNOWN_LIMITATIONS.md`.
- `SUPPORT_URL` and `GITHUB_URL`: public project links used by the extension UI.
## Control Modes & Capabilities
- `CONTROL_MODES.EVERYONE`: default room mode; any peer may control playback.
- `CONTROL_MODES.HOST_ONLY`: only the host and promoted controllers may move playback.
- `CAPABILITIES.HOST_CONTROL`: relay supports host-only room authority.
- `CAPABILITIES.CO_HOST`: relay supports promoted controller peers.
Clients should enable capability-gated UI only when the relay advertises the matching flag in `room_data.capabilities`.
## Protocol Events
For the complete and current event list, see the `EVENTS` object in [`constants.js`](constants.js). Key events include:
For the complete event list, read the `EVENTS` object in [`constants.js`](constants.js). Current event groups:
| Event | Direction | Purpose |
|:------|:----------|:--------|
| `JOIN_ROOM` | Client Server | Request to join a room with credentials |
| `LEAVE_ROOM` | Client Server | Leave the current room |
| `ROOM_DATA` | Server Client | Current room state (peers list) |
|:---|:---|:---|
| `JOIN_ROOM` | Client -> Server | Join or create a room with credentials and protocol version |
| `LEAVE_ROOM` | Client -> Server | Leave the current room |
| `ROOM_DATA` | Server -> Client | Initial room snapshot, peers, host state, capabilities |
| `ERROR` | Server -> Client | Connection, auth, capacity, or protocol error |
| `SET_CONTROL_MODE` | Client -> Server | Host switches between `everyone` and `host-only` |
| `CONTROL_MODE` | Server -> Client | Host/control-mode/controller snapshot changed |
| `SET_PEER_ROLE` | Client -> Server | Host promotes or demotes a co-host/controller |
| `PLAY` / `PAUSE` / `SEEK` | Bidirectional relay | Media control commands |
| `PEER_STATUS` | Bidirectional relay | Heartbeat or join/leave notification |
| `FORCE_SYNC_PREPARE` | Bidirectional relay | Phase 1: Pause & seek to target time |
| `FORCE_SYNC_ACK` | Bidirectional relay | Phase 1 confirmation: peer is buffered |
| `FORCE_SYNC_EXECUTE` | Bidirectional relay | Phase 2: Resume playback simultaneously |
| `EVENT_ACK` | Server Client | Delivery confirmation for UI feedback |
| `EPISODE_LOBBY` | Bidirectional relay | Episode transition: waiting for all peers |
| `EPISODE_READY` | Bidirectional relay | Episode confirmation: peer has loaded |
| `GET_ROOMS` / `ROOM_LIST` | Client ↔ Server | Room discovery |
| `ERROR` | Server → Client | Error message |
| `PEER_STATUS` | Bidirectional relay | Heartbeat and current media state |
| `FORCE_SYNC_PREPARE` | Bidirectional relay | Phase 1: pause and seek peers to a target time |
| `FORCE_SYNC_ACK` | Bidirectional relay | Phase 1 confirmation that a peer is ready |
| `FORCE_SYNC_EXECUTE` | Bidirectional relay | Phase 2: resume playback together |
| `EVENT_ACK` | Server -> Client | Delivery confirmation for UI feedback |
| `EPISODE_LOBBY` | Bidirectional relay | Episode transition lobby started |
| `EPISODE_READY` | Bidirectional relay | Peer has loaded the episode and is ready |
| `EPISODE_LOBBY_CANCEL` | Bidirectional relay | Active episode lobby cancelled |
| `GET_ROOMS` / `ROOM_LIST` | Client <-> Server | Room discovery with server-side cooldown |
| `PING` / `PONG` | Client <-> Server/Peer | Server RTT and peer latency checks |
## Timing Constants
- `HEARTBEAT_INTERVAL`: content heartbeat interval in milliseconds.
- `FORCE_SYNC_TIMEOUT`: max wait for force-sync ACKs.
- `EPISODE_LOBBY_TIMEOUT`: max wait for episode-lobby readiness.
## Do Not Break
- Do not rename events without updating `docs/PROTOCOL.md`, `server/index.js`, `extension/background.js`, and the build-injected `content.js` path.
- Do not edit `extension/shared/` directly.
- Keep new server-gated features behind `CAPABILITIES` so old clients and old relays degrade cleanly.
- Run `npm run build:extension` and `npm run verify` after protocol changes.
-5
View File
@@ -53,10 +53,6 @@ export const BLACKLIST_DOMAINS = [
'docs.google.com',
'sheets.google.com',
'slides.google.com',
'drive.google.com',
'dropbox.com',
'onedrive.live.com',
'icloud.com',
// Messengers
'web.whatsapp.com',
@@ -117,7 +113,6 @@ export const BLACKLIST_DOMAINS = [
'status.koalastuff.net',
'sync.koalastuff.net',
'timer.koalastuff.net',
'localhost',
'zoom.us',
'teams.microsoft.com',
'meet.google.com',
+24 -1
View File
@@ -7,7 +7,7 @@
*/
export const PROTOCOL_VERSION = "1.0.0";
export const APP_VERSION = "2.4.6";
export const APP_VERSION = "2.5.3";
export const OFFICIAL_SERVER_URL = 'wss://syncserver.koalastuff.net';
export const OFFICIAL_LANDING_PAGE_URL = 'https://sync.koalastuff.net';
@@ -33,6 +33,11 @@ export const EVENTS = {
ROOM_DATA: "room_data", // Server -> Client: current room state
ERROR: "error",
// Host Control Mode
SET_CONTROL_MODE: "set_control_mode", // Client -> Server: host sets room control mode ('everyone' | 'host-only')
CONTROL_MODE: "control_mode", // Server -> Client: control mode/role changed { controlMode, hostPeerId, controllers }
SET_PEER_ROLE: "set_peer_role", // Client -> Server: owner promotes/demotes a peer { peerId, controller }
// Media Control
PLAY: "play",
PAUSE: "pause",
@@ -57,6 +62,24 @@ export const EVENTS = {
PONG: "pong" // server responds with same { t } for client RTT calculation
};
// Room control modes (Host Control Mode feature).
// NOTE: content.js does not import this module — it uses the string literals
// 'everyone' / 'host-only' directly. Keep these values in sync there.
export const CONTROL_MODES = {
EVERYONE: 'everyone', // default: anyone can play/pause/seek for the room
HOST_ONLY: 'host-only' // only the host drives the room
};
// Server feature capabilities, advertised to clients in ROOM_DATA. Lets a client
// detect what the relay actually supports instead of inferring it from the
// presence of a data field — so new server features degrade cleanly on older
// relays (unknown/absent list → feature treated as unavailable) and old clients
// simply ignore the field. Add a flag here as each server-gated feature lands.
export const CAPABILITIES = {
HOST_CONTROL: 'host-control',
CO_HOST: 'co-host' // owner promotes guests to additional controllers
};
export const HEARTBEAT_INTERVAL = 15000; // 15s
export const FORCE_SYNC_TIMEOUT = 8500; // 8.5s timeout for force sync ACKs (must be > content.js poll timeout of 8s)
export const EPISODE_LOBBY_TIMEOUT = 60000; // 60s timeout for episode lobby
+24
View File
@@ -0,0 +1,24 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: false,
environment: 'node',
include: [
'server/**/*.test.js',
'server/**/*.test.mjs',
'shared/**/*.test.js',
'shared/**/*.test.mjs'
],
coverage: {
provider: 'v8',
reporter: ['text', 'lcov'],
include: ['server/**/*.js', 'shared/**/*.js'],
exclude: [
'**/node_modules/**',
'**/scripts/**',
'**/extension/**'
]
}
}
});
@@ -0,0 +1 @@
4650678bb36b488d8f64343a67f1b931
+68 -41
View File
@@ -1,74 +1,101 @@
# KoalaSync Website & Invitation Bridge
This directory contains the KoalaSync website. It serves a dual purpose: it is both the **marketing landing page** and the **technical bridge** for joining synchronized rooms.
This directory contains the static KoalaSync website. It is both the public marketing/onboarding site and the browser-side invitation bridge for joining sync rooms.
## Where You Are
- `template.html`, `style.css`, `app.js`, `lang-init.js`, and `locales/*.json` are source files.
- `build.cjs` compiles the static site into `website/www/`.
- `website/www/` is generated output. Do not edit files there directly.
- `join.html` handles room-invite links and communicates with the extension through `bridge.js`.
- `llms.txt` gives crawlers and AI tools a compact project overview.
- `alternatives/` contains comparison pages for users evaluating KoalaSync against other watch-party approaches.
## Core Roles
### 1. Marketing & Onboarding
Provides a premium, multi-language (EN/DE/FR/ES/PT-BR/RU/IT/PL/TR/NL/JA/KO/PT) overview of features, setup instructions, and direct links to the extension stores.
### 2. The Invitation Bridge (`join.html`)
The website handles incoming invitation links. When a user clicks a link like `sync.koalastuff.net/join.html#join:roomID:pass`, the website:
- **Detects the Extension**: Verifies if KoalaSync is installed via the `bridge.js` content script.
- **Privacy-First Handshake**: The room credentials (ID/Password) are stored in the **URL Hash (#)**. This ensures the sensitive credentials **never reach the web server** and are processed entirely within the user's browser.
- **Auto-Join**: If the extension is detected, it automatically triggers the join flow without requiring user input.
The site explains KoalaSync, links to browser stores, documents self-hosting, and provides localized user-facing copy in 15 languages:
`en`, `de`, `fr`, `es`, `it`, `nl`, `pl`, `pt`, `pt-BR`, `tr`, `ru`, `ja`, `ko`, `zh`, and `uk`.
### 2. Invitation Bridge (`join.html`)
When a user opens an invitation such as `https://sync.koalastuff.net/join.html#join:roomID:pass`, the page:
- detects whether the KoalaSync extension is installed via the extension's `bridge.js` content script,
- keeps room credentials inside the URL hash so they are not sent to the web server,
- asks the extension to join the target room automatically when possible.
## Architecture
The website is 100% **Static HTML, CSS, and JS**.
- **Static i18n Compiler**: The site uses a lightweight, zero-dependency Node.js compiler (`build.cjs`) to parse dictionary files inside `/locales/` against a single source-of-truth template (`template.html`), outputting the fully deployable static folder to `/www/`.
- **Build-time Minification**: `build.cjs` automatically minifies CSS and JS during compilation using a built-in state-machine tokenizer (no npm dependencies). Source files are written unminified (`style.css`, `app.js`, `lang-init.js`) — always edit source files, never the generated `.min.*` files in `www/`.
- **Zero Backend**: No Node.js, PHP, or databases are required to host the compiled website.
- **Zero Tracking**: All assets (fonts, icons) are self-hosted to prevent third-party tracking.
- **Responsive**: Fully optimized for mobile with a native-feel hamburger menu.
The website is 100% static HTML, CSS, and JavaScript.
- **Static i18n compiler**: `build.cjs` combines `template.html` with dictionaries in `locales/`.
- **Build-time minification**: Source CSS/JS stays readable; generated output uses `.min.css` and `.min.js`.
- **Zero backend**: The compiled site can be hosted by any static file server.
- **Zero external assets**: Fonts, icons, scripts, and images must remain self-hosted.
- **Generated SEO/runtime files**: `version.json`, sitemap, robots, clean URLs, localized pages, and minified assets are copied or generated into `www/`.
## Local Development & Compilation
From the repository root:
```bash
node website/build.cjs
npx serve -l 5000 website/www
```
Then open:
- `http://localhost:5000/` for the default page.
- `http://localhost:5000/de/` for a localized page.
- `http://localhost:5000/join.html#join:test-room:test-pass` to test the invitation bridge path.
Focused verification:
```bash
node scripts/test-website-locales.mjs
node --check website/www/app.min.js
node --check website/www/lang-init.min.js
```
Full verification:
```bash
npm run verify
```
## Hosting with Caddy
Caddy is the recommended web server. It provides automatic HTTPS and high-performance static file serving.
### Recommended Caddyfile
For a more comprehensive configuration that includes the Relay Server reverse proxy, see the root [Caddyfile.example](../examples/Caddyfile.example).
Caddy is the recommended production server because it handles HTTPS and static file serving cleanly. For a complete website plus relay reverse-proxy setup, use the root [Caddyfile.example](../examples/Caddyfile.example).
Minimal static-site block:
```caddy
sync.koalastuff.net {
root * /var/www/koalasync/website/www
try_files {path} {path}.html {path}/
file_server
encode zstd gzip
# Static Caching for high-performance PageSpeed (1 year with validation)
@static {
file
path *.ico *.css *.js *.png *.svg *.webp
path *.ico *.css *.js *.png *.svg *.webp *.avif
}
header @static Cache-Control "public, max-age=31536000, must-revalidate"
# Security Headers & Content Security Policy (CSP)
header {
# Strict Content Security Policy (restricts scripts and connections to self, forbids frames)
Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; object-src 'none'; frame-ancestors 'none';"
# Prevent FLoC tracking
Permissions-Policy interest-cohort=()
# Security best practices
Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; object-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none';"
Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()"
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy no-referrer-when-downgrade
X-Frame-Options SAMEORIGIN
Referrer-Policy strict-origin-when-cross-origin
}
}
```
## Local Development & Compilation
## Do Not Break
1. Run the compilation script from the repository root to generate the `/website/www` folder:
```bash
node website/build.cjs
```
2. Serve the compiled `/www` directory using any local development server:
```bash
npx serve website/www
```
3. To test the invitation flow locally, navigate to `http://localhost:5000/join.html#join:test-room:test-pass`.
> [!IMPORTANT]
> **Never edit files inside `website/www/` directly.** This directory is fully auto-generated by `build.cjs`. Always edit source files (`template.html`, `style.css`, `app.js`, `lang-init.js`, locale files in `locales/`) and re-run `node website/build.cjs` to apply changes. CSS and JS are output as `style.min.css`, `app.min.js`, and `lang-init.min.js` — the `.min.*` naming makes it visually obvious these are build artifacts. Editing minified files in `www/` will result in lost changes on the next build.
- Do not edit `website/www/` manually; rebuild it from sources.
- Do not add external CDNs, fonts, analytics, or third-party scripts.
- Keep invite credentials in the URL hash, not query parameters.
- Keep locale files synchronized with `website/build.cjs` and `scripts/test-website-locales.mjs`.
- Commit source changes and regenerated `www/` output together when website output changes.
+212
View File
@@ -0,0 +1,212 @@
<!DOCTYPE html>
<html lang="{{LANG_CODE}}" class="lang-{{LANG_CODE}}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ALT_INDEX_TITLE}}</title>
<link rel="preload" href="{{ASSET_PATH}}style.min.css" as="style">
<link rel="stylesheet" href="{{ASSET_PATH}}style.min.css">
<link rel="icon" type="image/webp" href="{{ASSET_PATH}}assets/NewLogoIcon_64.webp">
<link rel="canonical" href="https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives">
<link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives">
<link rel="alternate" hreflang="de" href="https://sync.koalastuff.net/de/alternatives">
<link rel="alternate" hreflang="fr" href="https://sync.koalastuff.net/fr/alternatives">
<link rel="alternate" hreflang="es" href="https://sync.koalastuff.net/es/alternatives">
<link rel="alternate" hreflang="pt-br" href="https://sync.koalastuff.net/pt-BR/alternatives">
<link rel="alternate" hreflang="ru" href="https://sync.koalastuff.net/ru/alternatives">
<link rel="alternate" hreflang="it" href="https://sync.koalastuff.net/it/alternatives">
<link rel="alternate" hreflang="pl" href="https://sync.koalastuff.net/pl/alternatives">
<link rel="alternate" hreflang="tr" href="https://sync.koalastuff.net/tr/alternatives">
<link rel="alternate" hreflang="nl" href="https://sync.koalastuff.net/nl/alternatives">
<link rel="alternate" hreflang="ja" href="https://sync.koalastuff.net/ja/alternatives">
<link rel="alternate" hreflang="ko" href="https://sync.koalastuff.net/ko/alternatives">
<link rel="alternate" hreflang="zh" href="https://sync.koalastuff.net/zh/alternatives">
<link rel="alternate" hreflang="uk" href="https://sync.koalastuff.net/uk/alternatives">
<link rel="alternate" hreflang="pt" href="https://sync.koalastuff.net/pt/alternatives">
<link rel="alternate" hreflang="x-default" href="https://sync.koalastuff.net/alternatives">
<meta name="description" content="{{ALT_INDEX_META_DESC}}">
<meta name="robots" content="index, follow">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives">
<meta property="og:title" content="{{ALT_INDEX_TITLE}}">
<meta property="og:description" content="{{ALT_INDEX_META_DESC}}">
<meta property="og:image" content="https://sync.koalastuff.net/assets/RemoteProf.webp">
<meta property="og:locale" content="{{OG_LOCALE}}">
<!-- Twitter / X -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ALT_INDEX_TITLE}}">
<meta name="twitter:description" content="{{ALT_INDEX_META_DESC}}">
<meta name="twitter:image" content="https://sync.koalastuff.net/assets/RemoteProf.webp">
<!-- Structured Data (Schema.org) for Breadcrumbs -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://sync.koalastuff.net/{{LANG_PREFIX}}"
},
{
"@type": "ListItem",
"position": 2,
"name": "Alternatives",
"item": "https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives"
}
]
}
</script>
<!-- Organization Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "KoalaSync",
"url": "https://sync.koalastuff.net/",
"logo": "https://sync.koalastuff.net/assets/NewLogoIcon_128.webp",
"sameAs": [
"https://github.com/Shik3i/KoalaSync",
"https://mastodon.social/@koalastuff"
]
}
</script>
<!-- Mobile Browser Theme Styling -->
<meta name="theme-color" content="#0f172a">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<script src="{{ASSET_PATH}}lang-init.min.js"></script>
</head>
<body>
<div class="bg-blobs">
<div class="blob blob-1"></div>
<div class="blob blob-2"></div>
<div class="blob blob-3"></div>
</div>
<nav>
<div class="container nav-content">
<a href="{{ASSET_PATH}}" class="logo-area" style="text-decoration: none;">
<img src="{{ASSET_PATH}}assets/NewLogoIcon.webp" alt="KoalaSync Logo" width="40" height="40">
<span>KoalaSync</span>
</a>
<div class="nav-links">
<a href="{{ASSET_PATH}}" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="width: 16px; height: 16px; display: block;"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
<span>Home</span>
</a>
<a href="https://github.com/Shik3i/KoalaSync" target="_blank" rel="noopener" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
GitHub
</a>
</div>
<div class="nav-right">
<div class="lang-select-container">
<select class="lang-dropdown" aria-label="Select Language">
<option value="en">🇬🇧 English</option>
<option value="de">🇩🇪 Deutsch</option>
<option value="fr">🇫🇷 Français</option>
<option value="es">🇪🇸 Español</option>
<option value="pt-BR">🇧🇷 Português (Brasil)</option>
<option value="ru">🇷🇺 Русский</option>
<option value="it">🇮🇹 Italiano</option>
<option value="pl">🇵🇱 Polski</option>
<option value="tr">🇹🇷 Türkçe</option>
<option value="nl">🇳🇱 Nederlands</option>
<option value="ja">🇯🇵 日本語</option>
<option value="ko">🇰🇷 한국어</option>
<option value="zh">🇨🇳 中文</option>
<option value="uk">🇺🇦 Українська</option>
<option value="pt">🇵🇹 Português (Portugal)</option>
</select>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="chevron-icon"><polyline points="6 9 12 15 18 9"/></svg>
</div>
<button class="hamburger" aria-label="Menu" aria-expanded="false"></button>
</div>
</div>
</nav>
<main class="legal-content">
<div class="legal-card" data-reveal style="padding: 2.5rem 2rem;">
<div style="display: flex; justify-content: center;">
<img src="{{ASSET_PATH}}assets/RemoteProf.webp" alt="Koala teaching and explaining concepts" class="legal-mascot" width="180" height="180">
</div>
<h1>{{ALT_INDEX_HEADING}}</h1>
<p style="text-align: center; font-size: 0.95rem; line-height: 1.6; max-width: 600px; margin: 0 auto 2rem auto; color: var(--text-muted);">
{{ALT_INDEX_INTRO}}
</p>
<!-- Cards Grid Layout -->
<div class="guides-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1.5rem; margin-top: 2rem; margin-bottom: 2rem;">
<!-- Teleparty Card -->
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives/teleparty" class="guide-card" style="text-decoration: none; background: rgba(255, 255, 255, 0.03); border: 1px solid var(--glass-border); border-radius: 12px; padding: 1.5rem; display: flex; flex-direction: column; justify-content: space-between;">
<div>
<h3 style="margin-top: 0; margin-bottom: 0.75rem; color: #fff; font-size: 1.25rem;">{{ALT_TELEPARTY_TITLE}}</h3>
<p style="font-size: 0.9rem; line-height: 1.5; color: var(--text-muted); margin-bottom: 1.5rem;">{{ALT_TELEPARTY_META_DESC}}</p>
</div>
<div class="btn btn-primary" style="align-self: flex-start; padding: 0.5rem 1rem; font-size: 0.85rem; border-radius: 6px;">
{{ALT_INDEX_READ_COMPARISON}}
</div>
</a>
<!-- Watch2Gether Card -->
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives/watch2gether" class="guide-card" style="text-decoration: none; background: rgba(255, 255, 255, 0.03); border: 1px solid var(--glass-border); border-radius: 12px; padding: 1.5rem; display: flex; flex-direction: column; justify-content: space-between;">
<div>
<h3 style="margin-top: 0; margin-bottom: 0.75rem; color: #fff; font-size: 1.25rem;">{{ALT_W2G_TITLE}}</h3>
<p style="font-size: 0.9rem; line-height: 1.5; color: var(--text-muted); margin-bottom: 1.5rem;">{{ALT_W2G_META_DESC}}</p>
</div>
<div class="btn btn-primary" style="align-self: flex-start; padding: 0.5rem 1rem; font-size: 0.85rem; border-radius: 6px;">
{{ALT_INDEX_READ_COMPARISON}}
</div>
</a>
<!-- Screen Sharing Card -->
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives/screen-sharing" class="guide-card" style="text-decoration: none; background: rgba(255, 255, 255, 0.03); border: 1px solid var(--glass-border); border-radius: 12px; padding: 1.5rem; display: flex; flex-direction: column; justify-content: space-between;">
<div>
<h3 style="margin-top: 0; margin-bottom: 0.75rem; color: #fff; font-size: 1.25rem;">{{ALT_SCREEN_TITLE}}</h3>
<p style="font-size: 0.9rem; line-height: 1.5; color: var(--text-muted); margin-bottom: 1.5rem;">{{ALT_SCREEN_META_DESC}}</p>
</div>
<div class="btn btn-primary" style="align-self: flex-start; padding: 0.5rem 1rem; font-size: 0.85rem; border-radius: 6px;">
{{ALT_INDEX_READ_GUIDE}}
</div>
</a>
</div>
<!-- CTA block -->
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; margin-top: 3rem; padding-top: 2rem; border-top: 1px solid var(--glass-border);">
<a href="{{ASSET_PATH}}" class="btn btn-primary" style="margin-bottom: 1.5rem;">{{ALT_TRY_KOALASYNC}}</a>
</div>
</div>
</main>
<footer>
<div class="container">
<p>&copy; 2026 KoalaSync. {{FOOTER_MIT}}</p>
<p style="font-size: 0.8rem; margin-top: 0.5rem;">{{FOOTER_RAM}}</p>
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<a href="{{ASSET_PATH}}{{FOOTER_LEGAL_LINK}}">{{FOOTER_LEGAL}}</a>
<a href="{{ASSET_PATH}}{{FOOTER_PRIVACY_LINK}}">{{FOOTER_PRIVACY}}</a>
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives">{{ALT_INDEX_FOOTER_LINK}}</a>
<a href="https://mastodon.social/@koalastuff" rel="me noopener" target="_blank" style="display: inline-flex; align-items: center; gap: 4px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="14" height="14" aria-hidden="true" style="display: block;"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
Mastodon
</a>
</div>
</div>
</footer>
<script src="{{ASSET_PATH}}app.min.js"></script>
</body>
</html>
+218
View File
@@ -0,0 +1,218 @@
<!DOCTYPE html>
<html lang="{{LANG_CODE}}" class="lang-{{LANG_CODE}}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ALT_SCREEN_TITLE}}</title>
<link rel="preload" href="{{ASSET_PATH}}style.min.css" as="style">
<link rel="stylesheet" href="{{ASSET_PATH}}style.min.css">
<link rel="icon" type="image/webp" href="{{ASSET_PATH}}assets/NewLogoIcon_64.webp">
<link rel="canonical" href="https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives/screen-sharing">
<link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/screen-sharing">
<link rel="alternate" hreflang="de" href="https://sync.koalastuff.net/de/alternatives/screen-sharing">
<link rel="alternate" hreflang="fr" href="https://sync.koalastuff.net/fr/alternatives/screen-sharing">
<link rel="alternate" hreflang="es" href="https://sync.koalastuff.net/es/alternatives/screen-sharing">
<link rel="alternate" hreflang="pt-br" href="https://sync.koalastuff.net/pt-BR/alternatives/screen-sharing">
<link rel="alternate" hreflang="ru" href="https://sync.koalastuff.net/ru/alternatives/screen-sharing">
<link rel="alternate" hreflang="it" href="https://sync.koalastuff.net/it/alternatives/screen-sharing">
<link rel="alternate" hreflang="pl" href="https://sync.koalastuff.net/pl/alternatives/screen-sharing">
<link rel="alternate" hreflang="tr" href="https://sync.koalastuff.net/tr/alternatives/screen-sharing">
<link rel="alternate" hreflang="nl" href="https://sync.koalastuff.net/nl/alternatives/screen-sharing">
<link rel="alternate" hreflang="ja" href="https://sync.koalastuff.net/ja/alternatives/screen-sharing">
<link rel="alternate" hreflang="ko" href="https://sync.koalastuff.net/ko/alternatives/screen-sharing">
<link rel="alternate" hreflang="zh" href="https://sync.koalastuff.net/zh/alternatives/screen-sharing">
<link rel="alternate" hreflang="uk" href="https://sync.koalastuff.net/uk/alternatives/screen-sharing">
<link rel="alternate" hreflang="pt" href="https://sync.koalastuff.net/pt/alternatives/screen-sharing">
<link rel="alternate" hreflang="x-default" href="https://sync.koalastuff.net/alternatives/screen-sharing">
<meta name="description" content="{{ALT_SCREEN_META_DESC}}">
<meta name="robots" content="index, follow">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="article">
<meta property="og:url" content="https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives/screen-sharing">
<meta property="og:title" content="{{ALT_SCREEN_TITLE}}">
<meta property="og:description" content="{{ALT_SCREEN_META_DESC}}">
<meta property="og:image" content="https://sync.koalastuff.net/assets/PopcornFriends.webp">
<meta property="og:locale" content="{{OG_LOCALE}}">
<!-- Twitter / X -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ALT_SCREEN_TITLE}}">
<meta name="twitter:description" content="{{ALT_SCREEN_META_DESC}}">
<meta name="twitter:image" content="https://sync.koalastuff.net/assets/PopcornFriends.webp">
<!-- Structured Data (Schema.org) for Breadcrumbs -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://sync.koalastuff.net/{{LANG_PREFIX}}"
},
{
"@type": "ListItem",
"position": 2,
"name": "Alternatives",
"item": "https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives"
},
{
"@type": "ListItem",
"position": 3,
"name": "KoalaSync vs Screen Sharing",
"item": "https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives/screen-sharing"
}
]
}
</script>
<!-- Organization Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "KoalaSync",
"url": "https://sync.koalastuff.net/",
"logo": "https://sync.koalastuff.net/assets/NewLogoIcon_128.webp",
"sameAs": [
"https://github.com/Shik3i/KoalaSync",
"https://mastodon.social/@koalastuff"
]
}
</script>
<!-- Mobile Browser Theme Styling -->
<meta name="theme-color" content="#0f172a">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<script src="{{ASSET_PATH}}lang-init.min.js"></script>
</head>
<body>
<div class="bg-blobs">
<div class="blob blob-1"></div>
<div class="blob blob-2"></div>
<div class="blob blob-3"></div>
</div>
<nav>
<div class="container nav-content">
<a href="{{ASSET_PATH}}" class="logo-area" style="text-decoration: none;">
<img src="{{ASSET_PATH}}assets/NewLogoIcon.webp" alt="KoalaSync Logo" width="40" height="40">
<span>KoalaSync</span>
</a>
<div class="nav-links">
<a href="{{ASSET_PATH}}" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="width: 16px; height: 16px; display: block;"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
<span>Home</span>
</a>
<a href="https://github.com/Shik3i/KoalaSync" target="_blank" rel="noopener" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
GitHub
</a>
</div>
<div class="nav-right">
<div class="lang-select-container">
<select class="lang-dropdown" aria-label="Select Language">
<option value="en">🇬🇧 English</option>
<option value="de">🇩🇪 Deutsch</option>
<option value="fr">🇫🇷 Français</option>
<option value="es">🇪🇸 Español</option>
<option value="pt-BR">🇧🇷 Português (Brasil)</option>
<option value="ru">🇷🇺 Русский</option>
<option value="it">🇮🇹 Italiano</option>
<option value="pl">🇵🇱 Polski</option>
<option value="tr">🇹🇷 Türkçe</option>
<option value="nl">🇳🇱 Nederlands</option>
<option value="ja">🇯🇵 日本語</option>
<option value="ko">🇰🇷 한국어</option>
<option value="zh">🇨🇳 中文</option>
<option value="uk">🇺🇦 Українська</option>
<option value="pt">🇵🇹 Português (Portugal)</option>
</select>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="chevron-icon"><polyline points="6 9 12 15 18 9"/></svg>
</div>
<button class="hamburger" aria-label="Menu" aria-expanded="false"></button>
</div>
</div>
</nav>
<main class="legal-content">
<div class="legal-card" data-reveal style="padding: 2rem;">
<div style="display: flex; justify-content: center;">
<img src="{{ASSET_PATH}}assets/PopcornFriends.webp" alt="Koalas watching movie with popcorn" class="legal-mascot" width="180" height="180">
</div>
<h1>{{ALT_SCREEN_TITLE}}</h1>
<!-- Intro -->
<section>
<p><em>{{ALT_SCREEN_INTRO}}</em></p>
</section>
<!-- When screen sharing is enough -->
<section>
<h2>{{ALT_SCREEN_WHEN_ENOUGH_TITLE}}</h2>
<p>{{ALT_SCREEN_WHEN_ENOUGH_BODY}}</p>
</section>
<!-- Where screen sharing gets annoying -->
<section>
<h2>{{ALT_SCREEN_WHEN_ANNOYING_TITLE}}</h2>
<p>{{ALT_SCREEN_WHEN_ANNOYING_BODY}}</p>
</section>
<!-- Why KoalaSync can feel cleaner -->
<section>
<h2>{{ALT_SCREEN_WHY_CLEANER_TITLE}}</h2>
<p>{{ALT_SCREEN_WHY_CLEANER_BODY}}</p>
</section>
<!-- Honest limitations -->
<section>
<h2>{{ALT_SCREEN_LIMITS_TITLE}}</h2>
<p>{{ALT_SCREEN_LIMITS_BODY}}</p>
</section>
<!-- Summary -->
<section>
<h2>{{ALT_SCREEN_SUMMARY_TITLE}}</h2>
<p>{{ALT_SCREEN_SUMMARY_BODY}}</p>
</section>
<!-- CTA block -->
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; margin-top: 3rem; padding-top: 2rem; border-top: 1px solid var(--glass-border);">
<a href="{{ASSET_PATH}}" class="btn btn-primary" style="margin-bottom: 1.5rem;">{{ALT_TRY_KOALASYNC}}</a>
<p style="font-size: 0.9rem; text-align: center; display: flex; flex-direction: column; gap: 0.5rem;">
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives/watch2gether" style="color: var(--accent); text-decoration: none;">{{ALT_W2G_TITLE}}</a>
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives/teleparty" style="color: var(--accent); text-decoration: none;">{{ALT_TELEPARTY_TITLE}}</a>
</p>
</div>
</div>
</main>
<footer>
<div class="container">
<p>&copy; 2026 KoalaSync. {{FOOTER_MIT}}</p>
<p style="font-size: 0.8rem; margin-top: 0.5rem;">{{FOOTER_RAM}}</p>
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<a href="{{ASSET_PATH}}{{FOOTER_LEGAL_LINK}}">{{FOOTER_LEGAL}}</a>
<a href="{{ASSET_PATH}}{{FOOTER_PRIVACY_LINK}}">{{FOOTER_PRIVACY}}</a>
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives"><span><span lang="en">Guides & comparisons</span><span lang="de">Vergleiche & Anleitungen</span></span></a>
<a href="https://mastodon.social/@koalastuff" rel="me noopener" target="_blank" style="display: inline-flex; align-items: center; gap: 4px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="14" height="14" aria-hidden="true" style="display: block;"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
Mastodon
</a>
</div>
</div>
</footer>
<script src="{{ASSET_PATH}}app.min.js"></script>
</body>
</html>
+218
View File
@@ -0,0 +1,218 @@
<!DOCTYPE html>
<html lang="{{LANG_CODE}}" class="lang-{{LANG_CODE}}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ALT_TELEPARTY_TITLE}}</title>
<link rel="preload" href="{{ASSET_PATH}}style.min.css" as="style">
<link rel="stylesheet" href="{{ASSET_PATH}}style.min.css">
<link rel="icon" type="image/webp" href="{{ASSET_PATH}}assets/NewLogoIcon_64.webp">
<link rel="canonical" href="https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives/teleparty">
<link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/teleparty">
<link rel="alternate" hreflang="de" href="https://sync.koalastuff.net/de/alternatives/teleparty">
<link rel="alternate" hreflang="fr" href="https://sync.koalastuff.net/fr/alternatives/teleparty">
<link rel="alternate" hreflang="es" href="https://sync.koalastuff.net/es/alternatives/teleparty">
<link rel="alternate" hreflang="pt-br" href="https://sync.koalastuff.net/pt-BR/alternatives/teleparty">
<link rel="alternate" hreflang="ru" href="https://sync.koalastuff.net/ru/alternatives/teleparty">
<link rel="alternate" hreflang="it" href="https://sync.koalastuff.net/it/alternatives/teleparty">
<link rel="alternate" hreflang="pl" href="https://sync.koalastuff.net/pl/alternatives/teleparty">
<link rel="alternate" hreflang="tr" href="https://sync.koalastuff.net/tr/alternatives/teleparty">
<link rel="alternate" hreflang="nl" href="https://sync.koalastuff.net/nl/alternatives/teleparty">
<link rel="alternate" hreflang="ja" href="https://sync.koalastuff.net/ja/alternatives/teleparty">
<link rel="alternate" hreflang="ko" href="https://sync.koalastuff.net/ko/alternatives/teleparty">
<link rel="alternate" hreflang="zh" href="https://sync.koalastuff.net/zh/alternatives/teleparty">
<link rel="alternate" hreflang="uk" href="https://sync.koalastuff.net/uk/alternatives/teleparty">
<link rel="alternate" hreflang="pt" href="https://sync.koalastuff.net/pt/alternatives/teleparty">
<link rel="alternate" hreflang="x-default" href="https://sync.koalastuff.net/alternatives/teleparty">
<meta name="description" content="{{ALT_TELEPARTY_META_DESC}}">
<meta name="robots" content="index, follow">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="article">
<meta property="og:url" content="https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives/teleparty">
<meta property="og:title" content="{{ALT_TELEPARTY_TITLE}}">
<meta property="og:description" content="{{ALT_TELEPARTY_META_DESC}}">
<meta property="og:image" content="https://sync.koalastuff.net/assets/ProContraKoala.webp">
<meta property="og:locale" content="{{OG_LOCALE}}">
<!-- Twitter / X -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ALT_TELEPARTY_TITLE}}">
<meta name="twitter:description" content="{{ALT_TELEPARTY_META_DESC}}">
<meta name="twitter:image" content="https://sync.koalastuff.net/assets/ProContraKoala.webp">
<!-- Structured Data (Schema.org) for Breadcrumbs -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://sync.koalastuff.net/{{LANG_PREFIX}}"
},
{
"@type": "ListItem",
"position": 2,
"name": "Alternatives",
"item": "https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives"
},
{
"@type": "ListItem",
"position": 3,
"name": "KoalaSync vs Teleparty",
"item": "https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives/teleparty"
}
]
}
</script>
<!-- Organization Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "KoalaSync",
"url": "https://sync.koalastuff.net/",
"logo": "https://sync.koalastuff.net/assets/NewLogoIcon_128.webp",
"sameAs": [
"https://github.com/Shik3i/KoalaSync",
"https://mastodon.social/@koalastuff"
]
}
</script>
<!-- Mobile Browser Theme Styling -->
<meta name="theme-color" content="#0f172a">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<script src="{{ASSET_PATH}}lang-init.min.js"></script>
</head>
<body>
<div class="bg-blobs">
<div class="blob blob-1"></div>
<div class="blob blob-2"></div>
<div class="blob blob-3"></div>
</div>
<nav>
<div class="container nav-content">
<a href="{{ASSET_PATH}}" class="logo-area" style="text-decoration: none;">
<img src="{{ASSET_PATH}}assets/NewLogoIcon.webp" alt="KoalaSync Logo" width="40" height="40">
<span>KoalaSync</span>
</a>
<div class="nav-links">
<a href="{{ASSET_PATH}}" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="width: 16px; height: 16px; display: block;"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
<span>Home</span>
</a>
<a href="https://github.com/Shik3i/KoalaSync" target="_blank" rel="noopener" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
GitHub
</a>
</div>
<div class="nav-right">
<div class="lang-select-container">
<select class="lang-dropdown" aria-label="Select Language">
<option value="en">🇬🇧 English</option>
<option value="de">🇩🇪 Deutsch</option>
<option value="fr">🇫🇷 Français</option>
<option value="es">🇪🇸 Español</option>
<option value="pt-BR">🇧🇷 Português (Brasil)</option>
<option value="ru">🇷🇺 Русский</option>
<option value="it">🇮🇹 Italiano</option>
<option value="pl">🇵🇱 Polski</option>
<option value="tr">🇹🇷 Türkçe</option>
<option value="nl">🇳🇱 Nederlands</option>
<option value="ja">🇯🇵 日本語</option>
<option value="ko">🇰🇷 한국어</option>
<option value="zh">🇨🇳 中文</option>
<option value="uk">🇺🇦 Українська</option>
<option value="pt">🇵🇹 Português (Portugal)</option>
</select>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="chevron-icon"><polyline points="6 9 12 15 18 9"/></svg>
</div>
<button class="hamburger" aria-label="Menu" aria-expanded="false"></button>
</div>
</div>
</nav>
<main class="legal-content">
<div class="legal-card" data-reveal style="padding: 2rem;">
<div style="display: flex; justify-content: center;">
<img src="{{ASSET_PATH}}assets/ProContraKoala.webp" alt="Koala weighing pros and cons" class="legal-mascot" width="180" height="180">
</div>
<h1>{{ALT_TELEPARTY_TITLE}}</h1>
<!-- Intro -->
<section>
<p><em>{{ALT_TELEPARTY_INTRO}}</em></p>
</section>
<!-- When Teleparty may still make sense -->
<section>
<h2>{{ALT_TELEPARTY_WHEN_TELE_TITLE}}</h2>
<p>{{ALT_TELEPARTY_WHEN_TELE_BODY}}</p>
</section>
<!-- When KoalaSync may fit better -->
<section>
<h2>{{ALT_TELEPARTY_WHEN_KOALA_TITLE}}</h2>
<p>{{ALT_TELEPARTY_WHEN_KOALA_BODY}}</p>
</section>
<!-- The main difference -->
<section>
<h2>{{ALT_TELEPARTY_MAIN_DIFF_TITLE}}</h2>
<p>{{ALT_TELEPARTY_MAIN_DIFF_BODY}}</p>
</section>
<!-- Honest limitations -->
<section>
<h2>{{ALT_TELEPARTY_LIMITS_TITLE}}</h2>
<p>{{ALT_TELEPARTY_LIMITS_BODY}}</p>
</section>
<!-- Summary -->
<section>
<h2>{{ALT_TELEPARTY_SUMMARY_TITLE}}</h2>
<p>{{ALT_TELEPARTY_SUMMARY_BODY}}</p>
</section>
<!-- CTA block -->
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; margin-top: 3rem; padding-top: 2rem; border-top: 1px solid var(--glass-border);">
<a href="{{ASSET_PATH}}" class="btn btn-primary" style="margin-bottom: 1.5rem;">{{ALT_TRY_KOALASYNC}}</a>
<p style="font-size: 0.9rem; text-align: center; display: flex; flex-direction: column; gap: 0.5rem;">
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives/watch2gether" style="color: var(--accent); text-decoration: none;">{{ALT_W2G_TITLE}}</a>
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives/screen-sharing" style="color: var(--accent); text-decoration: none;">{{ALT_SCREEN_TITLE}}</a>
</p>
</div>
</div>
</main>
<footer>
<div class="container">
<p>&copy; 2026 KoalaSync. {{FOOTER_MIT}}</p>
<p style="font-size: 0.8rem; margin-top: 0.5rem;">{{FOOTER_RAM}}</p>
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<a href="{{ASSET_PATH}}{{FOOTER_LEGAL_LINK}}">{{FOOTER_LEGAL}}</a>
<a href="{{ASSET_PATH}}{{FOOTER_PRIVACY_LINK}}">{{FOOTER_PRIVACY}}</a>
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives"><span><span lang="en">Guides & comparisons</span><span lang="de">Vergleiche & Anleitungen</span></span></a>
<a href="https://mastodon.social/@koalastuff" rel="me noopener" target="_blank" style="display: inline-flex; align-items: center; gap: 4px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="14" height="14" aria-hidden="true" style="display: block;"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
Mastodon
</a>
</div>
</div>
</footer>
<script src="{{ASSET_PATH}}app.min.js"></script>
</body>
</html>
+218
View File
@@ -0,0 +1,218 @@
<!DOCTYPE html>
<html lang="{{LANG_CODE}}" class="lang-{{LANG_CODE}}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ALT_W2G_TITLE}}</title>
<link rel="preload" href="{{ASSET_PATH}}style.min.css" as="style">
<link rel="stylesheet" href="{{ASSET_PATH}}style.min.css">
<link rel="icon" type="image/webp" href="{{ASSET_PATH}}assets/NewLogoIcon_64.webp">
<link rel="canonical" href="https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives/watch2gether">
<link rel="alternate" hreflang="en" href="https://sync.koalastuff.net/alternatives/watch2gether">
<link rel="alternate" hreflang="de" href="https://sync.koalastuff.net/de/alternatives/watch2gether">
<link rel="alternate" hreflang="fr" href="https://sync.koalastuff.net/fr/alternatives/watch2gether">
<link rel="alternate" hreflang="es" href="https://sync.koalastuff.net/es/alternatives/watch2gether">
<link rel="alternate" hreflang="pt-br" href="https://sync.koalastuff.net/pt-BR/alternatives/watch2gether">
<link rel="alternate" hreflang="ru" href="https://sync.koalastuff.net/ru/alternatives/watch2gether">
<link rel="alternate" hreflang="it" href="https://sync.koalastuff.net/it/alternatives/watch2gether">
<link rel="alternate" hreflang="pl" href="https://sync.koalastuff.net/pl/alternatives/watch2gether">
<link rel="alternate" hreflang="tr" href="https://sync.koalastuff.net/tr/alternatives/watch2gether">
<link rel="alternate" hreflang="nl" href="https://sync.koalastuff.net/nl/alternatives/watch2gether">
<link rel="alternate" hreflang="ja" href="https://sync.koalastuff.net/ja/alternatives/watch2gether">
<link rel="alternate" hreflang="ko" href="https://sync.koalastuff.net/ko/alternatives/watch2gether">
<link rel="alternate" hreflang="zh" href="https://sync.koalastuff.net/zh/alternatives/watch2gether">
<link rel="alternate" hreflang="uk" href="https://sync.koalastuff.net/uk/alternatives/watch2gether">
<link rel="alternate" hreflang="pt" href="https://sync.koalastuff.net/pt/alternatives/watch2gether">
<link rel="alternate" hreflang="x-default" href="https://sync.koalastuff.net/alternatives/watch2gether">
<meta name="description" content="{{ALT_W2G_META_DESC}}">
<meta name="robots" content="index, follow">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="article">
<meta property="og:url" content="https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives/watch2gether">
<meta property="og:title" content="{{ALT_W2G_TITLE}}">
<meta property="og:description" content="{{ALT_W2G_META_DESC}}">
<meta property="og:image" content="https://sync.koalastuff.net/assets/ProContraKoala.webp">
<meta property="og:locale" content="{{OG_LOCALE}}">
<!-- Twitter / X -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ALT_W2G_TITLE}}">
<meta name="twitter:description" content="{{ALT_W2G_META_DESC}}">
<meta name="twitter:image" content="https://sync.koalastuff.net/assets/ProContraKoala.webp">
<!-- Structured Data (Schema.org) for Breadcrumbs -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://sync.koalastuff.net/{{LANG_PREFIX}}"
},
{
"@type": "ListItem",
"position": 2,
"name": "Alternatives",
"item": "https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives"
},
{
"@type": "ListItem",
"position": 3,
"name": "KoalaSync vs Watch2Gether",
"item": "https://sync.koalastuff.net/{{LANG_PREFIX}}alternatives/watch2gether"
}
]
}
</script>
<!-- Organization Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "KoalaSync",
"url": "https://sync.koalastuff.net/",
"logo": "https://sync.koalastuff.net/assets/NewLogoIcon_128.webp",
"sameAs": [
"https://github.com/Shik3i/KoalaSync",
"https://mastodon.social/@koalastuff"
]
}
</script>
<!-- Mobile Browser Theme Styling -->
<meta name="theme-color" content="#0f172a">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<script src="{{ASSET_PATH}}lang-init.min.js"></script>
</head>
<body>
<div class="bg-blobs">
<div class="blob blob-1"></div>
<div class="blob blob-2"></div>
<div class="blob blob-3"></div>
</div>
<nav>
<div class="container nav-content">
<a href="{{ASSET_PATH}}" class="logo-area" style="text-decoration: none;">
<img src="{{ASSET_PATH}}assets/NewLogoIcon.webp" alt="KoalaSync Logo" width="40" height="40">
<span>KoalaSync</span>
</a>
<div class="nav-links">
<a href="{{ASSET_PATH}}" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="width: 16px; height: 16px; display: block;"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
<span>Home</span>
</a>
<a href="https://github.com/Shik3i/KoalaSync" target="_blank" rel="noopener" style="display: inline-flex; align-items: center; gap: 6px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" aria-hidden="true" style="display: block;"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
GitHub
</a>
</div>
<div class="nav-right">
<div class="lang-select-container">
<select class="lang-dropdown" aria-label="Select Language">
<option value="en">🇬🇧 English</option>
<option value="de">🇩🇪 Deutsch</option>
<option value="fr">🇫🇷 Français</option>
<option value="es">🇪🇸 Español</option>
<option value="pt-BR">🇧🇷 Português (Brasil)</option>
<option value="ru">🇷🇺 Русский</option>
<option value="it">🇮🇹 Italiano</option>
<option value="pl">🇵🇱 Polski</option>
<option value="tr">🇹🇷 Türkçe</option>
<option value="nl">🇳🇱 Nederlands</option>
<option value="ja">🇯🇵 日本語</option>
<option value="ko">🇰🇷 한국어</option>
<option value="zh">🇨🇳 中文</option>
<option value="uk">🇺🇦 Українська</option>
<option value="pt">🇵🇹 Português (Portugal)</option>
</select>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="chevron-icon"><polyline points="6 9 12 15 18 9"/></svg>
</div>
<button class="hamburger" aria-label="Menu" aria-expanded="false"></button>
</div>
</div>
</nav>
<main class="legal-content">
<div class="legal-card" data-reveal style="padding: 2rem;">
<div style="display: flex; justify-content: center;">
<img src="{{ASSET_PATH}}assets/ProContraKoala.webp" alt="Koala weighing pros and cons" class="legal-mascot" width="180" height="180">
</div>
<h1>{{ALT_W2G_TITLE}}</h1>
<!-- Intro -->
<section>
<p><em>{{ALT_W2G_INTRO}}</em></p>
</section>
<!-- When Watch2Gether may still make sense -->
<section>
<h2>{{ALT_W2G_WHEN_W2G_TITLE}}</h2>
<p>{{ALT_W2G_WHEN_W2G_BODY}}</p>
</section>
<!-- When KoalaSync may fit better -->
<section>
<h2>{{ALT_W2G_WHEN_KOALA_TITLE}}</h2>
<p>{{ALT_W2G_WHEN_KOALA_BODY}}</p>
</section>
<!-- The main difference -->
<section>
<h2>{{ALT_W2G_MAIN_DIFF_TITLE}}</h2>
<p>{{ALT_W2G_MAIN_DIFF_BODY}}</p>
</section>
<!-- Honest limitations -->
<section>
<h2>{{ALT_W2G_LIMITS_TITLE}}</h2>
<p>{{ALT_W2G_LIMITS_BODY}}</p>
</section>
<!-- Summary -->
<section>
<h2>{{ALT_W2G_SUMMARY_TITLE}}</h2>
<p>{{ALT_W2G_SUMMARY_BODY}}</p>
</section>
<!-- CTA block -->
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; margin-top: 3rem; padding-top: 2rem; border-top: 1px solid var(--glass-border);">
<a href="{{ASSET_PATH}}" class="btn btn-primary" style="margin-bottom: 1.5rem;">{{ALT_TRY_KOALASYNC}}</a>
<p style="font-size: 0.9rem; text-align: center; display: flex; flex-direction: column; gap: 0.5rem;">
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives/teleparty" style="color: var(--accent); text-decoration: none;">{{ALT_TELEPARTY_TITLE}}</a>
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives/screen-sharing" style="color: var(--accent); text-decoration: none;">{{ALT_SCREEN_TITLE}}</a>
</p>
</div>
</div>
</main>
<footer>
<div class="container">
<p>&copy; 2026 KoalaSync. {{FOOTER_MIT}}</p>
<p style="font-size: 0.8rem; margin-top: 0.5rem;">{{FOOTER_RAM}}</p>
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<a href="{{ASSET_PATH}}{{FOOTER_LEGAL_LINK}}">{{FOOTER_LEGAL}}</a>
<a href="{{ASSET_PATH}}{{FOOTER_PRIVACY_LINK}}">{{FOOTER_PRIVACY}}</a>
<a href="{{ASSET_PATH}}{{LANG_PREFIX}}alternatives"><span><span lang="en">Guides & comparisons</span><span lang="de">Vergleiche & Anleitungen</span></span></a>
<a href="https://mastodon.social/@koalastuff" rel="me noopener" target="_blank" style="display: inline-flex; align-items: center; gap: 4px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="14" height="14" aria-hidden="true" style="display: block;"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
Mastodon
</a>
</div>
</div>
</footer>
<script src="{{ASSET_PATH}}app.min.js"></script>
</body>
</html>
+881 -66
View File
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1041px" height="565px" viewBox="0 0 1041 565" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<!-- Generated by Pixelmator Pro 2.2 -->
<g id="Layer_1">
<g id="group">
<g id="group-1">
<g id="group-2">
<path id="Path" d="M735.8 365.7 C721.4 369 683.5 370.9 683.5 370.9 L678.7 385.9 C678.7 385.9 697.6 384.3 711.4 385.7 711.4 385.7 715.9 385.2 716.4 390.8 716.6 396 716 401.6 716 401.6 716 401.6 715.7 405 710.9 405.8 705.7 406.7 670.1 408 670.1 408 L664.3 427.5 C664.3 427.5 662.2 432 667 430.7 671.5 429.5 708.8 422.5 713.7 423.5 718.9 424.8 724.7 431.7 723 438.1 721 445.9 683.8 469.7 661.1 468 661.1 468 649.2 468.8 639.1 452.7 629.7 437.4 642.7 408.3 642.7 408.3 642.7 408.3 636.8 394.7 641.1 390.2 641.1 390.2 643.7 387.9 651.1 387.3 L660.2 368.4 C660.2 368.4 649.8 369.1 643.6 361.5 637.8 354.2 637.4 350.9 641.8 348.9 646.5 346.6 689.8 338.7 719.6 339.7 719.6 339.7 730 338.7 738.9 356.7 738.8 356.7 743.2 364 735.8 365.7 Z M623.7 438.3 C619.9 447.3 609.8 456.9 597.3 450.9 584.9 444.9 565.2 404.6 565.2 404.6 565.2 404.6 557.7 389.6 556.3 389.9 556.3 389.9 554.7 387 553.7 403.4 552.7 419.8 553.9 451.7 547.4 456.7 541.2 461.7 533.7 459.7 529.8 453.8 526.3 448 524.8 434.2 526.7 410 529 385.8 534.6 360 541.8 351.9 549 343.9 554.8 349.7 557 351.8 557 351.8 566.6 360.5 582.5 386.1 L585.3 390.8 C585.3 390.8 599.7 415 601.2 414.9 601.2 414.9 602.4 416 603.4 415.2 604.9 414.8 604.3 407 604.3 407 604.3 407 601.3 380.7 588.2 336.1 588.2 336.1 586.2 330.5 587.6 325.3 588.9 320 594.2 322.5 594.2 322.5 594.2 322.5 614.6 332.7 624.4 365.9 634.1 399.4 627.5 429.3 623.7 438.3 Z M523.5 353 C521.8 356.4 520.8 361.3 512.2 362.6 512.2 362.6 429.9 368.2 426 374 426 374 423.1 377.4 427.6 378.4 432.1 379.3 450.7 381.8 459.7 382.3 469.3 382.4 501.7 382.7 513.3 397.2 513.3 397.2 520.2 404.1 519.9 419.7 519.6 435.7 516.8 441.3 510.6 447.1 504.1 452.5 448.3 477.5 412.3 439.1 412.3 439.1 395.7 420.6 418 406.6 418 406.6 434.1 396.9 475 408.3 475 408.3 487.4 412.8 486.8 417.3 486.1 422.1 476.6 427.2 462.8 426.9 449.4 426.5 439.6 420.1 441.5 421.1 443.3 421.8 427.1 413.3 422.1 419.1 417.1 424.4 418.3 427.7 423.2 431 435.7 438.1 484 435.6 498.4 419.6 498.4 419.6 504.1 413.1 495.4 407.8 486.7 402.8 461.8 399.8 452.1 399.3 442.8 398.8 408.2 399.4 403.2 390.2 403.2 390.2 398.2 384 403.7 366.4 409.5 348 449.8 340.9 467.2 339.3 467.2 339.3 515.1 337.6 523.9 347.4 523.8 347.4 525 349.7 523.5 353 Z M387.5 460.9 C381.7 465.2 369.4 463.3 365.9 458.5 362.4 454.2 361.2 437.1 361.9 410.3 362.6 383.2 363.2 349.6 369 344.3 375.2 338.9 379 343.6 381.4 347.3 384 350.9 387.1 354.9 387.8 363.4 388.4 371.9 390.4 416.5 390.4 416.5 390.4 416.5 393 456.7 387.5 460.9 Z M400 317.1 C383.1 322.7 371.5 320.8 361.7 316.6 357.4 324.1 354.9 326.4 351.6 326.9 346.8 327.4 342.5 319.7 341.7 317.2 340.9 315.3 338.6 312.1 341.4 304.5 331.8 295.9 331.1 284.3 332.7 276.5 335.1 267.5 351.3 233.3 400.6 229.3 400.6 229.3 424.7 227.5 428.8 240.4 L429.5 240.4 C429.5 240.4 452.9 240.5 452.4 261.3 452.1 282.2 426.4 308.2 400 317.1 Z M354 270.8 C349 278.8 348.8 283.6 351.1 286.9 356.8 278.2 367.2 264.5 382.5 254.1 370.7 255.1 360.8 260.2 354 270.8 Z M422.1 257.4 C406.6 259.7 382.6 280.5 371.2 297.5 388.7 300.7 419.6 299.5 433.3 271.6 433.2 271.6 439.8 254.3 422.1 257.4 Z M842.9 418.5 C833.6 434.7 807.5 468.5 772.7 460.6 761.2 488.5 751.6 516.6 746.1 558.8 746.1 558.8 744.9 567 738.1 564.1 731.4 561.7 720.2 550.5 718 535 715.6 514.6 724.7 480.1 743.2 440.6 737.8 431.8 734.1 419.2 737.3 401.3 737.3 401.3 742 368.1 775.3 338.1 775.3 338.1 779.3 334.6 781.6 335.7 784.2 336.8 783 347.6 780.9 352.8 778.8 358 763.9 383.8 763.9 383.8 763.9 383.8 754.6 401.2 757.2 414.9 774.7 388 814.5 333.7 839.2 350.8 847.5 356.7 851.3 369.6 851.3 383.5 851.2 395.8 848.3 408.8 842.9 418.5 Z M835.7 375.9 C835.7 375.9 834.3 365.2 823.9 377 814.9 386.9 798.7 405.6 785.6 430.9 799.3 429.4 812.5 421.9 816.5 418.1 823 412.3 838.1 396.7 835.7 375.9 Z M350.2 389.5 C348.3 413.7 339 454.4 273.1 474.5 229.6 487.6 188.5 481.3 166.1 475.6 165.6 484.5 164.6 488.3 163.2 489.8 161.3 491.7 147.1 499.9 139.3 488.3 135.8 482.8 134 472.8 133 463.9 82.6 440.7 59.4 407.3 58.5 405.8 57.4 404.7 45.9 392.7 57.4 378 68.2 364.7 103.5 351.4 135.3 346 136.4 318.8 139.6 298.3 143.4 288.9 148 278 153.8 287.8 158.8 295.2 163 300.7 165.5 324.4 165.7 343.3 186.5 342.3 198.8 343.8 222 348 252.2 353.5 272.4 368.9 270.6 386.4 269.3 403.6 253.5 410.7 247.5 411.2 241.2 411.7 231.4 407.2 231.4 407.2 224.7 404 230.9 401.2 239 397.7 247.8 393.4 245.8 389 245.8 389 242.5 379.4 203.3 372.7 164.3 372.7 164.1 394.2 165.2 429.9 165.7 450.7 193 455.9 213.4 454.9 213.4 454.9 213.4 454.9 313 452.1 316 388.5 319.1 324.8 216.7 263.7 141 244.3 65.4 224.5 22.6 238.3 18.9 240.2 14.9 242.2 18.6 242.8 18.6 242.8 18.6 242.8 22.7 243.4 29.8 245.8 37.3 248.2 31.5 252.1 31.5 252.1 18.6 256.2 4.1 253.6 1.3 247.7 -1.5 241.8 3.2 236.5 8.6 228.9 14 220.9 19.9 221.2 19.9 221.2 113.4 188.8 227.3 247.4 227.3 247.4 334 301.5 352.2 364.9 350.2 389.5 Z M68 386.2 C57.4 391.4 64.7 398.9 64.7 398.9 84.6 420.3 109.1 433.7 132.4 442 135.1 405.1 134.7 392.1 135 373.5 98.6 376 77.6 381.8 68 386.2 Z" fill="#01147c" fill-opacity="1" stroke="none"/>
</g>
</g>
<g id="group-3">
<g id="group-4">
<g id="group-5">
<path id="Path-1" d="M1040.9 378.6 L1040.9 391.8 C1040.9 394.7 1038.6 397 1035.7 397 L972.8 397 C972.8 400.3 972.9 403.2 972.9 405.9 972.9 425.4 972.1 441.3 970.2 459.2 969.9 461.9 967.7 463.9 965.1 463.9 L951.5 463.9 C950.1 463.9 948.8 463.3 947.9 462.3 947 461.3 946.5 459.9 946.7 458.5 948.6 440.7 949.5 425 949.5 405.9 949.5 403.1 949.5 400.2 949.4 397 L887.2 397 C884.3 397 882 394.7 882 391.8 L882 378.6 C882 375.7 884.3 373.4 887.2 373.4 L948.5 373.4 C947.2 351.9 944.6 331.2 940.4 310.2 940.2 308.9 940.5 307.6 941.3 306.6 942.1 305.6 943.3 305 944.6 305 L959.3 305 C961.6 305 963.5 306.6 964 308.9 968.1 330.6 970.7 351.7 972 373.4 L1035.7 373.4 C1038.5 373.4 1040.9 375.8 1040.9 378.6 Z" fill="#01147c" fill-opacity="1" stroke="none"/>
</g>
</g>
</g>
<g id="group-6">
<g id="group-7">
<path id="Path-2" d="M200.2 204.3 L200.1 204.3 M199.4 204.4 C199.1 204.4 198.8 204.3 198.5 204.3 198.8 204.4 199.1 204.4 199.4 204.4 L199.7 204.4 C199.6 204.4 199.5 204.4 199.4 204.4 Z M199.4 204.4 C199.1 204.4 198.8 204.3 198.5 204.3 198.8 204.4 199.1 204.4 199.4 204.4 L199.7 204.4 C199.6 204.4 199.5 204.4 199.4 204.4 Z" fill="none" stroke="none"/>
<defs>
<radialGradient id="radial-gradient" gradientUnits="userSpaceOnUse" cx="942.524" cy="279.896" r="760.124" fx="942.524" fy="279.896">
<stop offset="0.007" stop-color="#021192" stop-opacity="1"/>
<stop offset="0.03" stop-color="#021096" stop-opacity="1"/>
<stop offset="0.057" stop-color="#010cb4" stop-opacity="1"/>
<stop offset="0.084" stop-color="#0008ce" stop-opacity="1"/>
<stop offset="0.111" stop-color="#0006d7" stop-opacity="1"/>
<stop offset="0.138" stop-color="#0004e1" stop-opacity="1"/>
<stop offset="0.165" stop-color="#0001fa" stop-opacity="1"/>
<stop offset="0.191" stop-color="#0000fe" stop-opacity="1"/>
<stop offset="0.216" stop-color="#0f1eff" stop-opacity="1"/>
<stop offset="1" stop-color="#00ffff" stop-opacity="0"/>
</radialGradient>
</defs>
<path id="Path-3" d="M955.3 273.9 C922.8 194 867.9 125.9 796.5 76.9 723.4 26.8 637.7 0.3 548.7 0.3 401.5 0.3 264.9 73.4 183.4 195.9 182.5 197.2 182.3 198.9 182.8 200.4 183.3 202 184.5 203.1 186 203.6 L197.4 207.5 C198.1 207.7 198.8 207.8 199.4 207.8 201.5 207.8 203.5 206.7 204.7 205 242.1 150 292.7 104.3 351.1 72.7 411.4 40.1 479.7 22.8 548.6 22.8 631.9 22.8 712.2 47.4 781 93.8 848.1 139.1 900.2 202.4 931.7 276.7 932.6 278.9 934.8 280.4 937.2 280.4 L950.8 280.4 C952.4 280.4 953.9 279.6 954.7 278.3 955.7 277 955.9 275.4 955.3 273.9 Z M199.4 204.4 C199.1 204.4 198.8 204.3 198.5 204.2 198.8 204.3 199.1 204.4 199.4 204.4 L199.6 204.4 C199.6 204.4 199.5 204.4 199.4 204.4 Z M934.4 278.6 C934.7 278.8 935 279 935.3 279.1 935 278.9 934.7 278.8 934.4 278.6 Z" fill-opacity="1" fill="url(#radial-gradient)" stroke="none"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

+132 -13
View File
@@ -117,6 +117,18 @@ async function compile() {
}
console.log(' ✓ Favicons/touch icons successfully synced to website/assets/');
// Social link preview image (og:image / twitter:image), same artwork as the
// GitHub repository OpenGraph card. PNG kept for maximum scraper support.
const ogSrc = path.join(websiteDir, '..', 'assets', 'StoreAssets', 'RepositoryOpenGraph.png');
if (fs.existsSync(ogSrc)) {
await sharp(ogSrc)
.png({ quality: 80, palette: true })
.toFile(path.join(targetAssetsDir, 'og-image.png'));
console.log(' ✓ og-image.png generated from RepositoryOpenGraph.png');
} else {
console.warn(` ⚠️ Warning: ${ogSrc} not found. Skipping og-image generation.`);
}
// ── 1. Minify CSS/JS (must happen first so hashes go into HTML) ──
console.log('Minifying CSS/JS...');
const styleRaw = fs.readFileSync(path.join(websiteDir, 'style.css'), 'utf8');
@@ -174,6 +186,9 @@ async function compile() {
let compiled = templateContent;
compiled = compiled.replace(/\{\{ASSET_PATH\}\}/g, assetPath);
compiled = compiled.replace(/\{\{VERSION\}\}/g, buildVersion);
compiled = compiled.replace(/\{\{VERSION_DATE\}\}/g, versionJson.date || '');
const langPrefix = lang === 'en' ? '' : `${lang}/`;
compiled = compiled.replace(/\{\{LANG_PREFIX\}\}/g, langPrefix);
languages.forEach(l => {
compiled = compiled.replace(new RegExp(`\\{\\{SELECTED_${l.toUpperCase()}\\}\\}`, 'g'), l === lang ? 'selected' : '');
});
@@ -183,6 +198,48 @@ async function compile() {
return compiled;
}
// Read alternative page templates
const telepartyTemplatePath = path.join(websiteDir, 'alternatives/teleparty.html');
const screenSharingTemplatePath = path.join(websiteDir, 'alternatives/screen-sharing.html');
const watch2getherTemplatePath = path.join(websiteDir, 'alternatives/watch2gether.html');
const overviewTemplatePath = path.join(websiteDir, 'alternatives/index.html');
const hasTelepartyTemplate = fs.existsSync(telepartyTemplatePath);
const hasScreenSharingTemplate = fs.existsSync(screenSharingTemplatePath);
const hasWatch2getherTemplate = fs.existsSync(watch2getherTemplatePath);
const hasOverviewTemplate = fs.existsSync(overviewTemplatePath);
const telepartyTemplate = hasTelepartyTemplate ? fs.readFileSync(telepartyTemplatePath, 'utf8') : '';
const screenSharingTemplate = hasScreenSharingTemplate ? fs.readFileSync(screenSharingTemplatePath, 'utf8') : '';
const watch2getherTemplate = hasWatch2getherTemplate ? fs.readFileSync(watch2getherTemplatePath, 'utf8') : '';
const overviewTemplate = hasOverviewTemplate ? fs.readFileSync(overviewTemplatePath, 'utf8') : '';
// Preload English locale for fallback
const englishLocalePath = path.join(localesDir, 'en.json');
const englishLocale = JSON.parse(fs.readFileSync(englishLocalePath, 'utf8'));
function compileAlternativePage(templateContent, locale, lang, assetPath, langPrefix) {
let compiled = templateContent;
compiled = compiled.replace(/\{\{LANG_CODE\}\}/g, lang);
compiled = compiled.replace(/\{\{ASSET_PATH\}\}/g, assetPath);
compiled = compiled.replace(/\{\{LANG_PREFIX\}\}/g, langPrefix);
compiled = compiled.replace(/\{\{VERSION\}\}/g, buildVersion);
// og:locale mapping for Facebook
const ogLocales = {
en: 'en_US', de: 'de_DE', fr: 'fr_FR', es: 'es_ES',
'pt-BR': 'pt_BR', ru: 'ru_RU', it: 'it_IT', pl: 'pl_PL',
tr: 'tr_TR', nl: 'nl_NL', ja: 'ja_JP', ko: 'ko_KR',
zh: 'zh_CN', uk: 'uk_UA', pt: 'pt_PT'
};
compiled = compiled.replace(/\{\{OG_LOCALE\}\}/g, ogLocales[lang] || 'en_US');
// Merge locale with English fallback for keys not present in current locale
const mergedLocale = { ...englishLocale, ...locale };
for (const [key, value] of Object.entries(mergedLocale)) {
compiled = compiled.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value);
}
return compiled;
}
for (const lang of languages) {
const localePath = path.join(localesDir, `${lang}.json`);
if (!fs.existsSync(localePath)) { console.warn(`Warning: Locale ${lang} not found.`); continue; }
@@ -193,6 +250,32 @@ async function compile() {
const html = compilePage(locale, '', lang);
fs.writeFileSync(path.join(wwwDir, 'index.html'), html);
englishHtml[''] = html;
// Compile alternatives for English (assets resolved using '../' prefix)
if (hasTelepartyTemplate) {
const altDir = path.join(wwwDir, 'alternatives');
fs.mkdirSync(altDir, { recursive: true });
const tpCompiled = compileAlternativePage(telepartyTemplate, locale, lang, '../', '');
fs.writeFileSync(path.join(altDir, 'teleparty.html'), tpCompiled);
}
if (hasScreenSharingTemplate) {
const altDir = path.join(wwwDir, 'alternatives');
fs.mkdirSync(altDir, { recursive: true });
const ssCompiled = compileAlternativePage(screenSharingTemplate, locale, lang, '../', '');
fs.writeFileSync(path.join(altDir, 'screen-sharing.html'), ssCompiled);
}
if (hasWatch2getherTemplate) {
const altDir = path.join(wwwDir, 'alternatives');
fs.mkdirSync(altDir, { recursive: true });
const w2gCompiled = compileAlternativePage(watch2getherTemplate, locale, lang, '../', '');
fs.writeFileSync(path.join(altDir, 'watch2gether.html'), w2gCompiled);
}
if (hasOverviewTemplate) {
const altDir = path.join(wwwDir, 'alternatives');
fs.mkdirSync(altDir, { recursive: true });
const ovCompiled = compileAlternativePage(overviewTemplate, locale, lang, '../', '');
fs.writeFileSync(path.join(altDir, 'index.html'), ovCompiled);
}
} else {
console.log(`Compiling ${lang.toUpperCase()} (${lang}/index.html)...`);
const langDir = path.join(wwwDir, lang);
@@ -200,6 +283,32 @@ async function compile() {
const html = compilePage(locale, '../', lang);
fs.writeFileSync(path.join(langDir, 'index.html'), html);
englishHtml[lang] = html;
// Compile alternatives for other languages (assets resolved using '../../' prefix)
if (hasTelepartyTemplate) {
const langAltDir = path.join(langDir, 'alternatives');
fs.mkdirSync(langAltDir, { recursive: true });
const tpCompiled = compileAlternativePage(telepartyTemplate, locale, lang, '../../', lang + '/');
fs.writeFileSync(path.join(langAltDir, 'teleparty.html'), tpCompiled);
}
if (hasScreenSharingTemplate) {
const langAltDir = path.join(langDir, 'alternatives');
fs.mkdirSync(langAltDir, { recursive: true });
const ssCompiled = compileAlternativePage(screenSharingTemplate, locale, lang, '../../', lang + '/');
fs.writeFileSync(path.join(langAltDir, 'screen-sharing.html'), ssCompiled);
}
if (hasWatch2getherTemplate) {
const langAltDir = path.join(langDir, 'alternatives');
fs.mkdirSync(langAltDir, { recursive: true });
const w2gCompiled = compileAlternativePage(watch2getherTemplate, locale, lang, '../../', lang + '/');
fs.writeFileSync(path.join(langAltDir, 'watch2gether.html'), w2gCompiled);
}
if (hasOverviewTemplate) {
const langAltDir = path.join(langDir, 'alternatives');
fs.mkdirSync(langAltDir, { recursive: true });
const ovCompiled = compileAlternativePage(overviewTemplate, locale, lang, '../../', lang + '/');
fs.writeFileSync(path.join(langAltDir, 'index.html'), ovCompiled);
}
}
}
@@ -219,19 +328,37 @@ async function compile() {
const src = path.join(websiteDir, mapping.src);
const dest = path.join(wwwDir, mapping.dest);
if (fs.existsSync(src)) {
fs.mkdirSync(path.dirname(dest), { recursive: true });
fs.copyFileSync(src, dest);
console.log(` Copied: ${mapping.src}${mapping.dest}`);
}
}
// ── 5. Copy generic static files ──
const genericFiles = ['robots.txt', 'sitemap.xml', 'site.webmanifest', 'version.json'];
// ── 5. Copy generic static files and verification files ──
const genericFiles = ['robots.txt', 'sitemap.xml', 'site.webmanifest', 'version.json', 'llms.txt'];
for (const file of genericFiles) {
const src = path.join(websiteDir, file);
const dest = path.join(wwwDir, file);
if (fs.existsSync(src)) { fs.copyFileSync(src, dest); }
}
// Auto-copy Google verification files and IndexNow/txt key files from website source to www root
const websiteFiles = fs.readdirSync(websiteDir);
for (const file of websiteFiles) {
const filePath = path.join(websiteDir, file);
if (fs.statSync(filePath).isFile()) {
// Match google[hex/alphanumeric].html or any hex/uuid txt file (e.g. 4650678bb36b488d8f64343a67f1b931.txt)
const isGoogleVerification = /^google[a-zA-Z0-9_-]+\.html$/i.test(file);
const isIndexNowVerification = (/^[a-zA-Z0-9_-]{8,}\.txt$/i.test(file) || /^[a-f0-9-]{32,36}$/i.test(file)) && file !== 'robots.txt';
if (isGoogleVerification || isIndexNowVerification) {
const dest = path.join(wwwDir, file);
fs.copyFileSync(filePath, dest);
console.log(` Copied verification file: ${file}${file}`);
}
}
}
// ── 6. Copy assets ──
console.log('Copying assets...');
const srcAssets = path.join(websiteDir, 'assets');
@@ -278,23 +405,15 @@ async function compile() {
let html = fs.readFileSync(filePath, 'utf8');
// 8a. Replace hashed asset refs
html = html.replace(/href="(?:\.\.\/)?style\.min\.css"/g, (m) => {
const prefix = m.includes('../') ? '../' : '';
html = html.replace(/href="((?:\.\.\/)*)style\.min\.css"/g, (m, prefix) => {
return `href="${prefix}${styleName}"`;
});
html = html.replace(/src="(?:\.\.\/)?app\.min\.js"/g, (m) => {
const prefix = m.includes('../') ? '../' : '';
html = html.replace(/src="((?:\.\.\/)*)app\.min\.js"/g, (m, prefix) => {
return `src="${prefix}${appName}"`;
});
html = html.replace(/src="(?:\.\.\/)?lang-init\.min\.js"/g, (m) => {
const prefix = m.includes('../') ? '../' : '';
html = html.replace(/src="((?:\.\.\/)*)lang-init\.min\.js"/g, (m, prefix) => {
return `src="${prefix}${langName}"`;
});
// Also update preload directives
html = html.replace(/href="(?:\.\.\/)?style\.min\.css"/g, (m) => {
const prefix = m.includes('../') ? '../' : '';
return `href="${prefix}${styleName}"`;
});
// 8b. Inject AVIF <picture> wrappers
html = injectAvifPictures(html);
+77 -16
View File
@@ -90,14 +90,28 @@
</p>
<section>
<h2>1. Hosting & Logfiles</h2>
<h2>1. Kontakt</h2>
<p>
Diese Seite wird auf einem privaten Server gehostet. Zur Gewährleistung der Stabilität werden standardmäßige Server-Logs (IP, Browser, Zeit) erhoben, aber nicht mit Personen verknüpft und nach 7 Tagen automatisch gelöscht.
KoalaSync wird von Timo Schmidt betrieben.<br>
Für datenschutzbezogene Fragen: <span class="email-reveal" data-user="koalasync" data-domain="koalastuff.net" style="color: var(--accent); cursor: pointer; text-decoration: underline;">[E-Mail anzeigen]</span>
</p>
</section>
<section>
<h2>2. Keine Drittanbieter & Open Source</h2>
<h2>2. Website-Hosting & Access Logs</h2>
<p>
KoalaSync wird auf Infrastruktur der Hetzner Online GmbH, Industriestr. 25, 91710 Gunzenhausen, Deutschland, gehostet. Der Serverstandort ist Finnland und damit innerhalb der EU/des EWR. Mit Hetzner besteht ein Auftragsverarbeitungsvertrag gemäß Art. 28 DSGVO.
</p>
<p style="margin-top: 0.5rem;">
Beim Aufruf dieser Website können Standard-Access-Logs Ihre IP-Adresse, Browser/User-Agent und Zeitpunkt enthalten. Diese Logs werden ausschließlich für Sicherheit, Stabilität und Missbrauchsprävention verwendet und nach 7 Tagen automatisch gelöscht.
</p>
<p style="margin-top: 0.5rem;">
Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO, berechtigtes Interesse am sicheren Betrieb der Website, an Stabilität und Missbrauchsprävention.
</p>
</section>
<section>
<h2>3. Keine Drittanbieter & Open Source</h2>
<p>
KoalaSync verzichtet bewusst auf Analyse-Tools, Tracking-Cookies oder Werbenetzwerke. Wir laden keine Ressourcen von Drittanbietern (wie Google Fonts) nach, um Ihre Privatsphäre maximal zu schützen.
</p>
@@ -107,26 +121,41 @@
</section>
<section>
<h2>3. Relay-Server Architektur</h2>
<h2>4. Offizieller Relay-Server & Raumdaten</h2>
<p>
Unser Relay-Server arbeitet ausschließlich im Arbeitsspeicher (RAM). Nachrichten zwischen Teilnehmern werden nicht auf Festplatten gespeichert und sind flüchtig. Sobald ein Raum geschlossen wird, werden alle zugehörigen Metadaten sofort gelöscht. Wir tracken Sie nicht. Wir überwachen lediglich unseren Server auf Basis von aggregierten, anonymen, nicht-personenbezogenen System-Leistungsmetriken.
Der offizielle KoalaSync-Relay-Server führt keine persistenten Logs. Er speichert keine Raumhistorie, keinen Sync-Verlauf, keinen Wiedergabeverlauf, keine Raumnachrichten, keine Playback-Events, keine Tab-Titel und keine Medientitel auf Festplatte.
</p>
<p style="margin-top: 0.5rem;">
Zur Bereitstellung der Raumfunktion verarbeitet der Relay Raum- und Synchronisationsdaten temporär im Arbeitsspeicher, solange ein Raum aktiv ist. Dazu können Room-ID, Peer-ID, Anzeigename, Wiedergabestatus, Sync-Events und abhängig von den Titel-Privatsphäre-Einstellungen Tab-/Medientitel gehören. Diese Daten werden nur an Teilnehmer desselben Raums weitergeleitet und aus dem Arbeitsspeicher entfernt, wenn der Raum abläuft oder geschlossen wird.
</p>
<p style="margin-top: 0.5rem;">
Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO, berechtigtes Interesse an der Bereitstellung der gewünschten Synchronisationsfunktion, Sicherheit und Missbrauchsprävention.
</p>
<p style="margin-top: 0.5rem;">
Raum- und Peer-Zahlen sind aggregierte Betriebsmetriken zur Überwachung des Serverstatus.
</p>
</section>
<section>
<h2>4. Browser-Erweiterung (Extension)</h2>
<h2>5. Browser-Erweiterung & lokaler Speicher</h2>
<p>
Um die geräteübergreifende Synchronisation zu ermöglichen, erfasst die KoalaSync Browser-Erweiterung temporär Daten des aktuell aktiven Video-Tabs (z. B. Tab-Titel, Medien-Metadaten wie den Videotitel sowie den Wiedergabestatus). Diese Daten werden ausschließlich zur Synchronisation an die anderen Teilnehmer in Ihrem Raum gesendet. Es wird ausdrücklich <strong>kein allgemeiner Browserverlauf (Browsing History)</strong> ausgelesen, gespeichert oder übermittelt.
Zur Synchronisierung der Wiedergabe zwischen Teilnehmern desselben Raums erfasst die KoalaSync Browser-Erweiterung temporär Daten des aktuell aktiven Video-Tabs (z. B. Tab-Titel, Medien-Metadaten wie den Videotitel sowie den Wiedergabestatus). Tab- oder Medientitel können je nach Inhalt potenziell personenbezogen sein. Sie werden nur für die Synchronisationsfunktion verarbeitet, können über die Privatsphäre-Einstellungen der Erweiterung eingeschränkt oder deaktiviert werden, werden nur an Teilnehmer desselben Raums weitergeleitet und vom Relay nicht protokolliert oder dauerhaft gespeichert.
</p>
<p style="margin-top: 0.5rem;">
Die Erweiterung speichert nur für den Betrieb erforderliche Einstellungen lokal im Browser, z. B. Anzeigename, Server-URL, aktuelle Raumdaten, Sprache, Titel-Privatsphäre-Einstellungen, Benachrichtigungseinstellungen und Audio-Processing-Einstellungen. KoalaSync speichert keinen allgemeinen Browserverlauf und keinen persistenten Wiedergabeverlauf.
</p>
<p style="margin-top: 0.5rem;">
Die Erweiterung verbindet sich nur mit dem Relay-Server, während Sie sich aktiv in einem Raum befinden. Es wird keine dauerhafte Hintergrundverbindung aufrechterhalten, sodass Ihre IP-Adresse dem Server nicht offengelegt wird, wenn Sie die Erweiterung nicht nutzen.
</p>
</section>
<section>
<h2>5. Berechtigungen der Erweiterung</h2>
<h2>6. Berechtigungen der Erweiterung</h2>
<p>
Um ihre technischen Zweck zu erfüllen, benötigt die Browser-Erweiterung bestimmte Berechtigungen. Jede Berechtigung wird ausschließlich für die Kernfunktionalität genutzt:
Um ihren technischen Zweck zu erfüllen, benötigt die Browser-Erweiterung bestimmte Berechtigungen. Jede Berechtigung wird ausschließlich für die Kernfunktionalität genutzt:
</p>
<ul style="margin-left: 1.5rem; margin-top: 0.5rem; color: var(--text-muted); font-size: 0.9rem; list-style-type: disc; display: flex; flex-direction: column; gap: 0.35rem;">
<li><strong>storage</strong>: Erlaubt das lokale Speichern von Benutzernamen, Server-URLs und Raum-Zugangsdaten im Browser, damit Sie sich nicht jedes Mal neu anmelden müssen.</li>
<li><strong>storage</strong>: Ermöglicht die lokale Speicherung von Anzeigename, Server-URL, Raumdaten und Einstellungen im Browser, damit diese zwischen Sitzungen erhalten bleiben.</li>
<li><strong>tabs</strong>: Wird benötigt, um geöffnete Tabs im Dropdown der Erweiterung aufzulisten und deren Titel auszulesen, damit Sie bequem den richtigen Video-Tab auswählen können.</li>
<li><strong>scripting</strong>: Erforderlich, um das Synchronisationsskript (content.js) sicher in den von Ihnen ausgewählten Videotab zu injizieren.</li>
<li><strong>alarms</strong>: Verhindert, dass der Hintergrunddienst der Erweiterung (Service Worker) während einer aktiven Synchronisationssitzung vom Browser in den Ruhezustand versetzt wird.</li>
@@ -137,18 +166,49 @@
</section>
<section>
<h2>6. Brute-Force Schutz</h2>
<h2>7. Brute-Force & Rate-Limit-Schutz</h2>
<p>
Zur Sicherheit unserer Nutzer speichern wir fehlgeschlagene Login-Versuche (IP-Adresse und Raum-ID) für maximal 15 Minuten im RAM, um automatisierte Angriffe zu verhindern. Diese Daten werden danach rückstandslos gelöscht.
Zur Missbrauchsprävention können fehlgeschlagene Beitritts-/Login-Versuche die IP-Adresse und Room-ID für bis zu 15 Minuten im Arbeitsspeicher halten. Diese Daten werden nicht auf Festplatte geschrieben, nicht öffentlich zugänglich gemacht und automatisch gelöscht. Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO, berechtigtes Interesse an der Verhinderung von Brute-Force-Angriffen und Missbrauch.
</p>
</section>
<section>
<h2>7. Ihre Rechte</h2>
<h2>8. Empfänger & Dritte</h2>
<p>
Sie haben das Recht auf Auskunft, Berichtigung oder Löschung Ihrer Daten. Da wir jedoch keine personenbezogenen Daten dauerhaft speichern, ist eine Zuordnung zu Ihrer Person in der Regel technisch nicht möglich.
Technische Hosting- und Verbindungsdaten können durch Hetzner als Hosting-Anbieter im Auftrag des Betreibers verarbeitet werden. Raum-Synchronisationsdaten werden nur an Teilnehmer desselben Raums weitergeleitet. KoalaSync verwendet keine Analytics-Anbieter, Werbenetzwerke oder Tracking-Dienste.
</p>
</section>
<section>
<h2>9. Erforderliche & optionale Daten</h2>
<p>
Die Bereitstellung technischer Verbindungsdaten ist erforderlich, um die Website aufzurufen und den Relay zu nutzen. Raumdaten und Sync-Events sind erforderlich, um die Raumsynchronisation zu verwenden. Das Teilen von Tab-/Medientitel ist optional und kann über die Privatsphäre-Einstellungen der Erweiterung eingeschränkt werden.
</p>
</section>
<section>
<h2>10. Rechte der betroffenen Person</h2>
<p>
Nach der DSGVO können Ihnen Rechte auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung, Datenübertragbarkeit, Widerspruch sowie das Recht auf Beschwerde bei einer Aufsichtsbehörde zustehen.
</p>
<p style="margin-top: 0.5rem;">
Da KoalaSync keine Nutzerkonten verwendet und keine persistenten Raumlogs oder Wiedergabeverläufe speichert, sind viele Datenpunkte entweder kurzlebig oder nach ihrer Löschung keiner bestimmten Person mehr zuordenbar. KoalaSync erhebt oder speichert außerdem keine zusätzlichen Identifikationsdaten nur um Nutzer für Betroffenenanfragen zu identifizieren.
</p>
<p style="margin-top: 0.5rem;">Kontakt für Datenschutzanfragen: <span class="email-reveal" data-user="koalasync" data-domain="koalastuff.net" style="color: var(--accent); cursor: pointer; text-decoration: underline;">[E-Mail anzeigen]</span></p>
</section>
<section>
<h2>11. Kein Profiling & automatisierte Entscheidungsfindung</h2>
<p>
KoalaSync verwendet kein Profiling und keine automatisierte Entscheidungsfindung im Sinne von Art. 22 DSGVO.
</p>
</section>
<section>
<h2>12. Drittlandstransfers</h2>
<p>
Das reguläre Hosting befindet sich innerhalb der EU/des EWR. Eine regelmäßige Übermittlung personenbezogener Daten in Drittländer findet nicht statt.
</p>
<p>Kontakt bei Fragen: <span class="email-reveal" data-user="koalasync_datenschutz" data-domain="koalamail.rocks" style="color: var(--accent); cursor: pointer; text-decoration: underline;">[E-Mail anzeigen]</span></p>
</section>
</div>
</main>
@@ -156,10 +216,11 @@
<footer>
<div class="container">
<p>&copy; 2026 KoalaSync. Open Source unter der MIT-Lizenz.</p>
<p style="font-size: 0.8rem; margin-top: 0.5rem;">Keine Daten werden auf unseren Servern gespeichert. Reines RAM-basiertes Relay.</p>
<p style="font-size: 0.8rem; margin-top: 0.5rem;">Keine Relay-Logs. Kein Tracking. Website-Access-Logs werden nach 7 Tagen gelöscht.</p>
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<a href="impressum" style="color: var(--text-muted); text-decoration: none;">Impressum</a>
<a href="datenschutz" style="color: var(--text-muted); text-decoration: none;">Datenschutz</a>
<a href="alternatives/teleparty">Vergleiche & Anleitungen</a>
<a href="https://mastodon.social/@koalastuff" rel="me noopener" target="_blank" style="color: var(--text-muted); text-decoration: none; display: inline-flex; align-items: center; gap: 4px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="14" height="14" aria-hidden="true" style="display: block;"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
Mastodon
+2
View File
@@ -2,6 +2,8 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex, follow">
<title>Redirecting...</title>
<script>
(function() {
+1
View File
@@ -0,0 +1 @@
google-site-verification: google157cb8453d13ccdc.html
+29 -18
View File
@@ -84,57 +84,67 @@
<div style="display: flex; justify-content: center;">
<img src="../assets/KoalaImprintl.webp" alt="Niedlicher Koala, der das Impressum repräsentiert" class="legal-mascot" width="180" height="180">
</div>
<h1>Impressum</h1>
<h1>Impressum / Anbieterinformationen</h1>
<p style="text-align: center; text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.8rem; border-bottom: 1px solid var(--glass-border); padding-bottom: 1.5rem; margin-bottom: 2rem;">
Transparenz & Identifikation
Kontakt & Projektinformationen
</p>
<section>
<h2>Betreiber & Kontakt</h2>
<p>Timo (KoalaDev) Privatperson</p>
<p>Timo Schmidt</p>
<p style="margin-top: 0.5rem;">
E-Mail: <span class="email-reveal" data-user="koalasync_admin" data-domain="koalamail.rocks" style="color: var(--accent); cursor: pointer; text-decoration: underline;">[E-Mail anzeigen]</span>
E-Mail: <span class="email-reveal" data-user="koalasync" data-domain="koalastuff.net" style="color: var(--accent); cursor: pointer; text-decoration: underline;">[E-Mail anzeigen]</span>
</p>
<p style="margin-top: 0.75rem;">
<span style="opacity: 0.6;">🔒 Private Nachricht:</span>
<a href="https://mastodon.social/@koalastuff" rel="me noopener" target="_blank" style="color: var(--accent); text-decoration: none;">@koalastuff auf Mastodon</a>
</p>
<p style="margin-top: 0.5rem;">
<span style="opacity: 0.6;">🐛 Active Bedenken / Bug Reports:</span>
<span style="opacity: 0.6;">🐛 Fehlerberichte und Sicherheitsmeldungen:</span>
<a href="https://github.com/Shik3i/KoalaSync/issues" target="_blank" rel="noopener" style="color: var(--accent); text-decoration: none;">GitHub Issues</a>
</p>
</section>
<section style="opacity: 0.8;">
<h2>Privatprojekt-Hinweis</h2>
<section>
<h2>Projektinformationen</h2>
<p>
Diese Website ist ein rein privates Hobby-Projekt und dient keinen geschäftsmäßigen Zwecken.
Eine Impressumspflicht nach § 5 DDG (ehemals TMG) besteht daher nicht.
Diese Angaben erfolgen rein freiwillig zur Transparenz gegenüber der Community.
KoalaSync ist eine kostenlose und quelloffene Browser-Erweiterung. Der Quellcode ist auf GitHub unter der MIT-Lizenz verfügbar.
</p>
<p style="margin-top: 0.5rem;">
Informationen zum Datenschutz, einschließlich Hosting, Access Logs, Relay-Verhalten und Betroffenenrechten, finden Sie in der <a href="datenschutz" style="color: var(--accent); text-decoration: none;">Datenschutzerklärung</a>.
</p>
</section>
<section>
<h2>Haftung für Inhalte</h2>
<p>
Gemäß § 7 Abs.1 DDG sind wir für eigene Inhalte verantwortlich. Nach §§ 8 bis 10 DDG sind wir jedoch nicht verpflichtet,
übermittelte oder gespeicherte fremde Informationen zu überwachen.
Die Inhalte dieser Website wurden mit Sorgfalt erstellt. Ich kann jedoch nicht gewährleisten, dass alle Informationen jederzeit vollständig, richtig oder aktuell sind.
</p>
<p style="margin-top: 0.5rem;">
Als Diensteanbieter bin ich nach den allgemeinen Gesetzen für eigene Inhalte auf diesen Seiten verantwortlich.
</p>
</section>
<section>
<h2>Haftung für Links</h2>
<p>
Unser Angebot enthält Links zu externen Websites Dritter. Auf deren Inhalte haben wir keinen Einfluss und
können daher keine Gewähr für diese fremden Inhalte übernehmen.
Diese Website enthält Links zu externen Websites Dritter. Auf deren Inhalte habe ich keinen Einfluss. Für die Inhalte verlinkter Seiten ist der jeweilige Anbieter oder Betreiber verantwortlich.
</p>
<p style="margin-top: 0.5rem;">
Wenn mir rechtswidrige Inhalte auf verlinkten Seiten bekannt werden, entferne ich die entsprechenden Links.
</p>
</section>
<section>
<h2>Urheberrecht</h2>
<h2>Lizenz & Urheberrecht</h2>
<p>
Die durch die Seitenbetreiber erstellten Inhalte auf diesen Seiten unterliegen dem deutschen Urheberrecht.
Vervielfältigung, Bearbeitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung.
KoalaSync ist freie und quelloffene Software. Sofern nicht anders angegeben, steht der Quellcode von KoalaSync unter der MIT-Lizenz.
</p>
<p style="margin-top: 0.5rem;">
Die MIT-Lizenz erlaubt Nutzung, Vervielfältigung, Bearbeitung, Verbreitung, Unterlizenzierung und Verkauf der Software, sofern der Copyright-Hinweis und der Lizenztext entsprechend den Lizenzbedingungen erhalten bleiben.
</p>
<p style="margin-top: 0.5rem;">
Projektname, Logos, Grafiken, Website-Texte und andere Nicht-Code-Assets können separaten Rechten unterliegen, sofern sie nicht ausdrücklich von der Repository-Lizenz umfasst sind.
</p>
</section>
</div>
@@ -143,10 +153,11 @@
<footer>
<div class="container">
<p>&copy; 2026 KoalaSync. Open Source unter der MIT-Lizenz.</p>
<p style="font-size: 0.8rem; margin-top: 0.5rem;">Keine Daten werden auf unseren Servern gespeichert. Reines RAM-basiertes Relay.</p>
<p style="font-size: 0.8rem; margin-top: 0.5rem;">Keine Relay-Logs. Kein Tracking. Website-Access-Logs werden nach 7 Tagen gelöscht.</p>
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<a href="impressum" style="color: var(--text-muted); text-decoration: none;">Impressum</a>
<a href="datenschutz" style="color: var(--text-muted); text-decoration: none;">Datenschutz</a>
<a href="alternatives">Vergleiche & Anleitungen</a>
<a href="https://mastodon.social/@koalastuff" rel="me noopener" target="_blank" style="color: var(--text-muted); text-decoration: none; display: inline-flex; align-items: center; gap: 4px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="14" height="14" aria-hidden="true" style="display: block;"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
Mastodon
+2
View File
@@ -2,6 +2,8 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex, follow">
<title>Redirecting...</title>
<script>
(function() {
+28 -15
View File
@@ -86,53 +86,65 @@
</div>
<h1>Legal Notice</h1>
<p style="text-align: center; text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.8rem; border-bottom: 1px solid var(--glass-border); padding-bottom: 1.5rem; margin-bottom: 2rem;">
Transparency & Identification
Contact & Project Information
</p>
<section>
<h2>Operator & Contact</h2>
<p>Timo (KoalaDev) Private Individual</p>
<p>Timo Schmidt</p>
<p style="margin-top: 0.5rem;">
E-Mail: <span class="email-reveal" data-user="koalasync_admin" data-domain="koalamail.rocks" style="color: var(--accent); cursor: pointer; text-decoration: underline;">[Show Email]</span>
E-Mail: <span class="email-reveal" data-user="koalasync" data-domain="koalastuff.net" style="color: var(--accent); cursor: pointer; text-decoration: underline;">[Show Email]</span>
</p>
<p style="margin-top: 0.75rem;">
<span style="opacity: 0.6;">🔒 Private message:</span>
<a href="https://mastodon.social/@koalastuff" rel="me noopener" target="_blank" style="color: var(--accent); text-decoration: none;">@koalastuff on Mastodon</a>
</p>
<p style="margin-top: 0.5rem;">
<span style="opacity: 0.6;">🐛 Active concerns / Bug reports:</span>
<span style="opacity: 0.6;">🐛 Bug reports and security concerns:</span>
<a href="https://github.com/Shik3i/KoalaSync/issues" target="_blank" rel="noopener" style="color: var(--accent); text-decoration: none;">GitHub Issues</a>
</p>
</section>
<section style="opacity: 0.8;">
<h2>Private Project Notice</h2>
<section>
<h2>Project Information</h2>
<p>
This website is a purely private hobby project and does not serve any commercial purposes.
Therefore, there is no obligation to provide a legal notice according to § 5 DDG (formerly TMG).
This information is provided voluntarily for transparency towards the community.
KoalaSync is a free and open-source browser extension. The source code is available on GitHub under the MIT License.
</p>
<p style="margin-top: 0.5rem;">
For privacy-related information, including hosting, access logs, relay behavior, and data subject rights, please see the <a href="privacy" style="color: var(--accent); text-decoration: none;">Privacy Policy</a>.
</p>
</section>
<section>
<h2>Liability for Content</h2>
<p>
According to § 7 Abs.1 DDG we are responsible for our own content. According to §§ 8 to 10 DDG, however, we are not obligated to monitor transmitted or stored third-party information.
The contents of this website were created with care. However, I cannot guarantee that all information is always complete, accurate, or up to date.
</p>
<p style="margin-top: 0.5rem;">
As a service provider, I am responsible for my own content on these pages in accordance with the general laws.
</p>
</section>
<section>
<h2>Liability for Links</h2>
<p>
Our offer contains links to external third-party websites. We have no influence on their content and therefore cannot assume any liability for these external contents.
This website contains links to external third-party websites. I have no influence over the content of those websites. The respective provider or operator is responsible for the content of linked pages.
</p>
<p style="margin-top: 0.5rem;">
If I become aware of unlawful content on linked pages, I will remove the corresponding links.
</p>
</section>
<section>
<h2>Copyright</h2>
<h2>License & Copyright</h2>
<p>
The content and works created by the site operators on these pages are subject to German copyright law.
Duplication, processing, and any kind of exploitation outside the limits of copyright require written consent.
KoalaSync is free and open-source software. Unless otherwise stated, the KoalaSync source code is licensed under the MIT License.
</p>
<p style="margin-top: 0.5rem;">
The MIT License allows use, copying, modification, distribution, sublicensing, and sale of the software, provided that the copyright notice and license text are included where required by the license.
</p>
<p style="margin-top: 0.5rem;">
Project name, logos, graphics, website text, and other non-code assets may be subject to separate rights unless they are explicitly included under the repository license.
</p>
</section>
</div>
@@ -141,10 +153,11 @@
<footer>
<div class="container">
<p>&copy; 2026 KoalaSync. Open source under the MIT License.</p>
<p style="font-size: 0.8rem; margin-top: 0.5rem;">No data is stored on our servers. Pure RAM-based relay.</p>
<p style="font-size: 0.8rem; margin-top: 0.5rem;">No relay logs. No tracking. Website access logs are deleted after 7 days.</p>
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<a href="imprint" style="color: var(--text-muted); text-decoration: none;">Legal Notice</a>
<a href="privacy" style="color: var(--text-muted); text-decoration: none;">Privacy Policy</a>
<a href="alternatives">Guides & comparisons</a>
<a href="https://mastodon.social/@koalastuff" rel="me noopener" target="_blank" style="color: var(--text-muted); text-decoration: none; display: inline-flex; align-items: center; gap: 4px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="14" height="14" aria-hidden="true" style="display: block;"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
Mastodon
+7 -4
View File
@@ -9,6 +9,8 @@
<link rel="icon" type="image/png" sizes="16x16" href="assets/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="assets/favicon-32x32.png">
<link rel="apple-touch-icon" sizes="180x180" href="assets/icon-192x192.png">
<meta name="description" content="Join a KoalaSync room invitation and start watching videos in sync with friends. No account needed.">
<link rel="canonical" href="https://sync.koalastuff.net/join">
<meta name="robots" content="noindex">
<!-- Open Graph / Facebook -->
@@ -16,13 +18,13 @@
<meta property="og:url" content="https://sync.koalastuff.net/join">
<meta property="og:title" content="Join Room | KoalaSync">
<meta property="og:description" content="You've been invited to watch videos together in perfect sync with your friends on KoalaSync.">
<meta property="og:image" content="https://sync.koalastuff.net/assets/PlatformJuggler_New.webp">
<meta property="og:image" content="https://sync.koalastuff.net/assets/og-image.png">
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Join Room | KoalaSync">
<meta name="twitter:description" content="You've been invited to watch videos together in perfect sync with your friends on KoalaSync.">
<meta name="twitter:image" content="https://sync.koalastuff.net/assets/PlatformJuggler_New.webp">
<meta name="twitter:image" content="https://sync.koalastuff.net/assets/og-image.png">
<!-- Mobile Browser Theme Styling -->
<meta name="theme-color" content="#0f172a">
@@ -114,11 +116,12 @@
<footer>
<div class="container">
<p>&copy; 2026 KoalaSync. Open source under the MIT License.</p>
<p lang="en" style="font-size: 0.8rem; margin-top: 0.5rem;">No data is stored on our servers. Pure RAM-based relay.</p>
<p lang="de" style="font-size: 0.8rem; margin-top: 0.5rem;">Keine Daten werden auf unseren Servern gespeichert. Reines RAM-basiertes Relay.</p>
<p lang="en" style="font-size: 0.8rem; margin-top: 0.5rem;">No relay logs. No tracking. Website access logs are deleted after 7 days.</p>
<p lang="de" style="font-size: 0.8rem; margin-top: 0.5rem;">Keine Relay-Logs. Kein Tracking. Website-Access-Logs werden nach 7 Tagen gelöscht.</p>
<div style="margin-top: 1.5rem; font-size: 0.8rem; display: flex; justify-content: center; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<a href="impressum" style="color: var(--text-muted); text-decoration: none;"><span lang="en">Legal Notice</span><span lang="de">Impressum</span></a>
<a href="datenschutz" style="color: var(--text-muted); text-decoration: none;"><span lang="en">Privacy Policy</span><span lang="de">Datenschutz</span></a>
<a href="alternatives"><span lang="en">Guides & comparisons</span><span lang="de">Vergleiche & Anleitungen</span></a>
<a href="https://mastodon.social/@koalastuff" rel="me noopener" target="_blank" style="color: var(--text-muted); text-decoration: none; display: inline-flex; align-items: center; gap: 4px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="14" height="14" aria-hidden="true" style="display: block;"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
Mastodon
+7 -2
View File
@@ -18,6 +18,11 @@
}
};
// Apply saved theme before first paint (dark is the default, no class needed)
if (safeGetLocalStorage('koala_theme') === 'light') {
html.classList.add('theme-light');
}
// Mapping of browser language codes to KoalaSync locale directories
var langMap = {
'de': 'de',
@@ -97,8 +102,8 @@
if (isIndex) {
var titles = {
en: 'KoalaSync | Real-time Video Synchronization for Friends',
de: 'KoalaSync | Echtzeit-Video-Synchronisation für Freunde'
en: 'KoalaSync | Watch Party for Netflix, YouTube & Any Video',
de: 'KoalaSync | Watch Party für Netflix, YouTube & jedes Video'
};
document.title = titles[activeLang] || titles.en;
} else if (isJoin) {
+33
View File
@@ -0,0 +1,33 @@
# KoalaSync
> Watch Netflix, YouTube, Twitch & any HTML5 video in perfect sync with friends. Free, open-source browser extension for Chrome and Firefox. No sign-up needed.
KoalaSync is a privacy-first peer-to-peer video synchronization tool. It uses WebRTC for direct connections between peers — no video data ever passes through a server. The relay server is only used for signaling and optional room-based coordination. Self-hostable, MIT licensed.
## Features
- [Sync any HTML5 video](https://sync.koalastuff.net/): Works with Netflix, Emby, Jellyfin, YouTube, Twitch, Amazon Prime Video, Disney+, Crunchyroll, HBO Max and more
- [Host & co-host controls](https://sync.koalastuff.net/#features): Synchronized play, pause, seek, host control mode, co-host roles
- [Privacy-first](https://sync.koalastuff.net/#features): No accounts, no tracking, no data collection, peer-to-peer by design
- [Available in 15 languages](https://sync.koalastuff.net/): English, Deutsch, Français, Español, Português, Italiano, Nederlands, Polski, Türkçe, Русский, 日本語, 한국어, 中文, Українська
## Download
- [Chrome Web Store](https://chromewebstore.google.com/detail/koalasync/obbnmkmlaaddodakcbdljknjpagklifc): Install KoalaSync for Chrome, Edge, and other Chromium-based browsers
- [Firefox Add-ons](https://addons.mozilla.org/de/firefox/addon/koalasync/): Install KoalaSync for Firefox
- [GitHub Releases](https://github.com/Shik3i/KoalaSync/releases): Manual download and release notes
## Technical
- [Source code](https://github.com/Shik3i/KoalaSync): Full MIT-licensed source on GitHub (extension, server, website)
- [Changelog](https://github.com/Shik3i/KoalaSync/blob/main/docs/CHANGELOG.md): Version history and release notes
- [Self-hosting guide](https://sync.koalastuff.net/#self-hosting): Run your own relay server
- [Server](https://sync.koalastuff.net/): Node.js WebSocket relay, port 54000
## Optional
- [Alternatives hub](https://sync.koalastuff.net/alternatives/): Comparison with Teleparty, Scener, other sync tools
- [Privacy policy](https://sync.koalastuff.net/privacy.html): Data handling and privacy information
- [Imprint](https://sync.koalastuff.net/imprint.html): Legal notice
- [Sitemap](https://sync.koalastuff.net/sitemap.xml): Full site index
- [Join a room](https://sync.koalastuff.net/join.html): Direct room join page
+104 -34
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "de_DE",
"META_TITLE": "KoalaSync | Netflix, YouTube & jedes Video mit Freunden synchronisieren",
"META_DESCRIPTION": "Schaue Netflix, YouTube, Twitch und jedes HTML5-Video synchron mit Freunden. Kostenlose, quelloffene Browser-Erweiterung. Keine Anmeldung erforderlich.",
"SCHEMA_APP_DESC": "Schaue Netflix, YouTube, Twitch und jedes HTML5-Video synchron mit Freunden. Kostenlose, quelloffene Browser-Erweiterung. Keine Anmeldung erforderlich.",
"OG_TITLE": "KoalaSync | Netflix, Emby, Jellyfin & fast jedes Video mit Freunden synchronisieren",
"OG_DESCRIPTION": "Schaue Netflix, Emby, Jellyfin, YouTube, Twitch und fast jedes HTML5-Video perfekt synchronisiert. Quelloffene, datenschutzfreundliche Browser-Erweiterung für Chrome und Firefox.",
"TWITTER_TITLE": "KoalaSync | Netflix, Emby, Jellyfin & fast jedes Video mit Freunden synchronisieren Browser-Erweiterung",
"TWITTER_DESCRIPTION": "Schaue Netflix, Emby, Jellyfin, YouTube, Twitch und fast jedes HTML5-Video perfekt synchronisiert mit Freunden. Datenschutzfreundliche Browser-Erweiterung für Chrome und Firefox.",
"META_TITLE": "KoalaSync | Watch Party für Netflix, YouTube & jedes Video",
"META_DESCRIPTION": "Watch Party auf Netflix, YouTube, Twitch & HTML5-Videos. Open-Source-Erweiterung für Chrome & Firefox. Kein Konto, kein Tracking, keine Werbung.",
"SCHEMA_APP_DESC": "Watch Party auf Netflix, YouTube, Twitch & HTML5-Videos. Open-Source-Erweiterung für Chrome & Firefox. Kein Konto, kein Tracking, keine Werbung.",
"OG_TITLE": "KoalaSync | Watch Party für Netflix, Emby, Jellyfin & fast jedes Video",
"OG_DESCRIPTION": "Starte eine Watch Party auf Netflix, Emby, Jellyfin, YouTube, Twitch und fast jedem HTML5-Video. Open Source, datenschutzfreundlich und ohne Konto.",
"TWITTER_TITLE": "KoalaSync | Watch Party für Netflix, Emby, Jellyfin & fast jedes Video Browser-Erweiterung",
"TWITTER_DESCRIPTION": "Starte eine Watch Party auf Netflix, Emby, Jellyfin, YouTube, Twitch und fast jedem HTML5-Video. Open Source, datenschutzfreundlich und ohne Konto.",
"NAV_FEATURES": "Funktionen",
"NAV_HOW_IT_WORKS": "So funktioniert's",
"HERO_TITLE": "Gemeinsam schauen.<span class='hero-line2'>Perfekt synchron.</span>",
"HERO_SUBTITLE": "Dein Kino-Abend auf Distanz. Keine Lags, keine Anmeldung. Einfach Link teilen und zusammen schauen.",
"HERO_TITLE": "Mach jedes Video zur <span class='hero-highlight'>Watch Party</span>.",
"HERO_SUBTITLE": "Dein Kino-Abend auf Distanz, ohne Lags. Kein Account, kein Tracking, keine Werbung. Einfach Link teilen und gemeinsam Play drücken.",
"HERO_MASCOT_ALT": "Ein niedlicher Koala steht da und schaut nach unten auf die Download-Buttons",
"DEMO_SYNC": "Synchron",
"DEMO_HINT": "Live-Demo: Drück Play in einem der Tabs, KoalaSync hält beide synchron.",
"DEMO_NO_ROOM": "Noch nicht verbunden",
"DEMO_CREATE_ROOM": "Raum erstellen",
"DEMO_JOINED": "Raum beigetreten",
"DEMO_TAB_SELECTED": "Tab ausgewählt",
"DEMO_SELECT_PLACEHOLDER": "Video-Tab wählen…",
"MOBILE_DEMO_CTA": "So funktioniert's ansehen",
"MOBILE_DEMO_TITLE": "KoalaSync Live-Demo",
"MOBILE_DEMO_CLOSE": "Demo schließen",
"ADD_TO_CHROME": "Zu Chrome hinzufügen",
"ADD_TO_FIREFOX": "Zu Firefox hinzufügen",
"COMPAT_HEADING": "Funktioniert auf deinen Lieblingsplattformen",
@@ -35,22 +45,22 @@
"USE_CASE_3_DESC": "Überbrückt die Distanz und genießt gemeinsame Date-Nächte. Erlebt jeden Plot-Twist, lacht über dieselben Witze und teilt emotionale Momente zur selben Millisekunde.",
"WHY_TITLE": "Warum KoalaSync?",
"WHY_SUBTITLE": "Entwickelt für zuverlässigen Sync, Datenschutz und einfache Einrichtung.",
"FEATURE_1_TITLE": "Volle Kontrolle, in Echtzeit",
"FEATURE_1_DESC": "Einer pausiert, alle pausieren. Einer spult, alle folgen. Unser Zwei-Phasen-Synchronisationsprotokoll koordiniert die Wiedergabe aller Teilnehmer in Echtzeit.",
"FEATURE_1_TITLE": "Raum-Kontrolle & Sync",
"FEATURE_1_DESC": "Du bestimmst, wer die Wiedergabe steuert. Aktiviere die Host-Steuerung, damit nur du pausieren und spulen darfst, oder ernenne Co-Hosts in der Teilnehmerliste.",
"FEATURE_2_TITLE": "Grenzenloses Bingen",
"FEATURE_2_DESC": "Nächste Episode startet für jeden zeitgleich. KoalaSync erkennt den Episodenwechsel und pausiert, bis jeder Teilnehmer das neue Video fertig geladen hat.",
"FEATURE_3_TITLE": "Keine Accounts / Datenschutz",
"FEATURE_3_DESC": "Kein Login. Keine Daten. Einfach Link teilen und schauen. Der Server läuft flüchtig im RAM und löscht deinen Raum komplett nach dem Verlassen.",
"FEATURE_4_TITLE": "Universeller HTML5-Support",
"FEATURE_4_DESC": "Unterstützt YouTube, Twitch, Netflix, Jellyfin, Emby und fast jede beliebige Webseite mit einem HTML5-Video-Tag. Ideal auch für eigene Medienbibliotheken. Es werden keinerlei Videodaten an unsere Server gesendet — nur Synchronisations-Metadaten.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby und fast jede Seite mit HTML5-Video. Nur Sync-Signale verlassen deinen Browser, nie das Video.",
"FEATURE_5_TITLE": "Self-Hostable & Docker-Ready",
"FEATURE_5_DESC": "Unsere Server sind kostenlos und schnell, aber du kannst auch die volle Kontrolle übernehmen, wenn du willst. Starte dein eigenes privates Relay in Sekunden via Docker.",
"FEATURE_6_TITLE": "Direkte Einladungen & 1-Klick Beitritt",
"FEATURE_6_DESC": "Keine lästigen IPs oder Passwörter austauschen. Teile einfach einen Einladungslink mit deinen Freunden, um sie mit einem Klick in den Raum zu holen.",
"FEATURE_7_TITLE": "Lautstärke Angleichen / Audio Compressor",
"FEATURE_7_DESC": "Nie wieder am Lautstärkeregler spielen. Der eingebaute Audio Compressor verstärkt leise Dialoge und zähmt laute Explosionen. Funktioniert auf Netflix, Prime Video, Disney+, YouTube und jedem HTML5-Video. Komplett lokal — keine Audiodaten verlassen deinen Computer.",
"FEATURE_7_DESC": "Nie wieder am Lautstärkeregler spielen. Ein eingebauter Kompressor hebt leise Dialoge an und dämpft laute Explosionen, alles lokal im Browser verarbeitet.",
"FEATURE_8_TITLE": "Open Source (MIT-Lizenz)",
"FEATURE_8_DESC": "Der gesamte Quellcode ist auf GitHub öffentlich einsehbar. Keine Black Boxes, keine proprietären Teile 100 % Transparenz vom Relay-Server bis zur Browser-Erweiterung.",
"FEATURE_8_DESC": "Der gesamte Quellcode ist auf GitHub öffentlich einsehbar. Keine Black Boxes, keine proprietären Teile. 100 % Transparenz vom Relay-Server bis zur Browser-Erweiterung.",
"COMP_TITLE": "KoalaSync vs. Teleparty",
"COMP_SUBTITLE": "Erfahre, warum Open Source, Werbefreiheit und echter Datenschutz die bessere Wahl für gemeinsames Schauen sind.",
"COMP_COL_FEATURE": "Funktion",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Datenschutzerklärung und Tracker-Erfassung von Teleparty:",
"COMP_FOOTNOTE_4": "Funktioniert auf allen Seiten, die Skript-Injektionen in Standard-HTML5-Videoplayer erlauben. Seiten mit extrem strengen Content Security Policies (CSP), DRM-Kopierschutz oder stark verschachtelten Player-Wrappern (z. B. komplexe Shadow-DOMs) können die automatische Steuerung blockieren.",
"STEPS_TITLE": "Erste Schritte",
"STEP_1_TITLE": "Erweiterung installieren",
"STEP_1_DESC": "Füge KoalaSync aus dem Chrome Web Store, den Firefox Add-ons oder über die Entwickler-ZIP von GitHub zu deinem Browser hinzu.",
"STEP_1_ILLUS_DESC": "Privatsphäre-fokussierte Video-Synchronisation",
"STEP_1_ILLUS_DL_CHROME": "Download für Chrome",
"STEP_1_ILLUS_DL_FIREFOX": "Download für Firefox",
"STEP_1_ILLUS_ACTIVE": "Erweiterung aktiv",
"STEP_1_ILLUS_READY": "Bereit zum Mitschauen!",
"STEP_2_TITLE": "Raum erstellen",
"STEP_2_DESC": "Öffne die Erweiterung und klicke auf „+ Neuer Raum“. KoalaSync generiert automatisch eine sichere Raum-ID samt Passwort, tritt dem Raum bei und kopiert den Einladungslink in deine Zwischenablage.",
"STEP_1_TITLE": "Raum erstellen & teilen",
"STEP_1_DESC": "Öffne das Popup der Erweiterung, erstelle einen Raum mit einem Klick und teile den generierten Link mit deinen Freunden. Sie treten über die Einladungsbrücke sofort bei.",
"STEP_1_ILLUS_DESC": "Wähle einen Video-Tab",
"STEP_1_ILLUS_DL_CHROME": "YouTube-Tab verbinden",
"STEP_1_ILLUS_DL_FIREFOX": "Netflix-Tab verbinden",
"STEP_1_ILLUS_ACTIVE": "Tab verbunden",
"STEP_1_ILLUS_READY": "Bereit für synchrones Schauen!",
"STEP_2_TITLE": "Video-Tab auswählen",
"STEP_2_DESC": "Klicke auf den Tab deines Videos und wähle im Popup den Tab aus, den du synchronisieren möchtest. KoalaSync übernimmt ab jetzt die automatische Verbindung.",
"STEP_2_ILLUS_ROOM": "Raum",
"STEP_2_ILLUS_SYNC": "Sync",
"STEP_2_ILLUS_SETTINGS": "Optionen",
"STEP_2_ILLUS_CREATE": "+ Neuer Raum",
"STEP_2_ILLUS_MANUAL": "Manuell verbinden / Erweitert",
"STEP_2_ILLUS_COPIED": "Einladungslink kopiert!",
"STEP_3_TITLE": "Teilen & Synchronisieren",
"STEP_3_DESC": "Sende den Einladungslink an deine Freunde. Sobald sie beitreten, wähle deinen Video-Tab aus und genieße die synchronisierte Wiedergabe.",
"STEP_3_TITLE": "Synchron genießen",
"STEP_3_DESC": "Drücke auf Wiedergabe. Egal wer pausiert, spult oder startet: Alles läuft bei allen Beteiligten absolut zeitgleich und verzögerungsfrei ab.",
"STEP_3_ILLUS_IN_SYNC": "SYNCHRON",
"SELF_TITLE": "Für Self-Hoster",
"SELF_SUBTITLE": "Du traust dem Server nicht? Behalte die volle Datenhoheit. Richte deinen eigenen privaten Relay-Server in wenigen Minuten ein.",
@@ -116,19 +126,21 @@
"FAQ_TITLE": "Häufig gestellte Fragen",
"FAQ_SUBTITLE": "Alles, was du über synchrones Videoschauen mit Freunden wissen musst.",
"FAQ_Q1": "Kann ich Netflix, Emby oder Jellyfin synchron mit Freunden schauen?",
"FAQ_A1": "Ja! KoalaSync funktioniert mit Netflix, YouTube, Twitch und jeder Website mit einem HTML5-Videoplayer einschließlich Emby, Jellyfin und eigenen Medienservern. Installiere die Erweiterung, erstelle einen Raum, teile den Einladungslink und alle schauen perfekt synchron.",
"FAQ_A1": "Ja! KoalaSync funktioniert mit Netflix, YouTube, Twitch und jeder Website mit einem HTML5-Videoplayer, einschließlich Emby, Jellyfin und eigenen Medienservern. Installiere die Erweiterung, erstelle einen Raum, teile den Einladungslink und alle schauen perfekt synchron.",
"FAQ_Q_VIDEO_ACCESS": "Streamt KoalaSync das Video an meine Freunde?",
"FAQ_A_VIDEO_ACCESS": "Nein. KoalaSync streamt, hostet oder teilt keine Videoinhalte und umgeht keinen Zugang. Jede Person schaut das Video lokal im eigenen Browser und braucht selbst Zugriff auf die jeweilige Website oder den Streamingdienst, zum Beispiel ein eigenes Netflix-Konto. KoalaSync synchronisiert nur Aktionen wie Play, Pause und Spulen.",
"FAQ_Q2": "Ist KoalaSync eine kostenlose Alternative zu Teleparty?",
"FAQ_A2": "100 % kostenlos und Open Source unter der MIT-Lizenz. Keine Premium-Stufen, keine gesperrten Funktionen, keine Werbung. Anders als Teleparty läuft KoalaSyncs Relay-Server ausschließlich im RAM ohne Datensammlung und ohne Benutzerkonten.",
"FAQ_A2": "100 % kostenlos und Open Source unter der MIT-Lizenz. Keine Premium-Stufen, keine gesperrten Funktionen, keine Werbung. Anders als Teleparty läuft KoalaSyncs Relay-Server ausschließlich im RAM, ohne Datensammlung und ohne Benutzerkonten.",
"FAQ_Q3": "Funktioniert KoalaSync mit Emby, Jellyfin und eigenen Medienservern?",
"FAQ_A3": "Ja. KoalaSync erkennt jedes HTML5-Videoelement und funktioniert perfekt mit Emby, Jellyfin, Plex und beliebigen eigenen Streaming-Setups. Du kannst den KoalaSync Relay-Server auch via Docker selbst hosten.",
"FAQ_Q4": "Welche Browser und Plattformen unterstützen KoalaSync?",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave und alle Chromium-basierten Browser. Direkt aus dem Chrome Web Store oder den Firefox Add-ons installieren keine manuelle Konfiguration nötig.",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave und alle Chromium-basierten Browser. Direkt aus dem Chrome Web Store oder den Firefox Add-ons installieren, keine manuelle Konfiguration nötig.",
"FAQ_Q5": "Müssen alle meine Freunde die Erweiterung installieren?",
"FAQ_A5": "Ja, jeder Teilnehmer braucht KoalaSync aber ohne Anmeldung oder Kontoerstellung. Einfach auf den Einladungslink klicken, die Erweiterung installieren (falls nicht vorhanden) und du bist automatisch im Raum.",
"FAQ_A5": "Ja, jeder Teilnehmer braucht KoalaSync, aber ohne Anmeldung oder Kontoerstellung. Einfach auf den Einladungslink klicken, die Erweiterung installieren (falls nicht vorhanden) und du bist automatisch im Raum.",
"FAQ_Q6": "Sind meine Daten sicher? Was, wenn ich dem offiziellen Server nicht traue?",
"FAQ_A6": "Absolut sicher. Der KoalaSync Relay-Server läuft ausschließlich im RAM alle Sitzungsdaten werden sofort gelöscht, sobald du den Raum verlässt. Keine Datenbank, keine Protokolle, keine Analysen, keine Benutzerkonten. Wenn du volle Kontrolle möchtest: hoste deinen eigenen privaten Relay-Server via Docker in wenigen Minuten. Der gesamte Quellcode ist Open Source und auf GitHub einsehbar.",
"FAQ_A6": "Absolut sicher. Der KoalaSync Relay-Server läuft ausschließlich im RAM, alle Sitzungsdaten werden sofort gelöscht, sobald du den Raum verlässt. Keine Datenbank, keine Protokolle, keine Analysen, keine Benutzerkonten. Wenn du volle Kontrolle möchtest: hoste deinen eigenen privaten Relay-Server via Docker in wenigen Minuten. Der gesamte Quellcode ist Open Source und auf GitHub einsehbar.",
"FAQ_Q7": "Kann ich leise Dialoge und laute Explosionen beim Videoschauen im Browser ausgleichen?",
"FAQ_A7": "Ja. KoalaSync 2.2.0 enthält einen eingebauten Audio Compressor, der den Unterschied zwischen lauten und leisen Passagen reduziert. Er läuft lokal über die Web Audio API keine Audiodaten verlassen deinen Computer. Öffne das Popup, erstelle einen Raum, gehe zu Einstellungen → Audio Processing, schalte den Compressor ein und wähle ein Preset. Funktioniert auf Netflix, Prime Video, Disney+, YouTube und allen HTML5-Video-Seiten.",
"FAQ_A7": "Ja. KoalaSync 2.2.0 enthält einen eingebauten Audio Compressor, der den Unterschied zwischen lauten und leisen Passagen reduziert. Er läuft lokal über die Web Audio API, keine Audiodaten verlassen deinen Computer. Öffne das Popup, erstelle einen Raum, gehe zu Einstellungen → Audio Processing, schalte den Compressor ein und wähle ein Preset. Funktioniert auf Netflix, Prime Video, Disney+, YouTube und allen HTML5-Video-Seiten.",
"HOWTO_TITLE": "So synchronisierst du Videos mit Freunden mit KoalaSync",
"HOWTO_DESC": "Schaue Videos perfekt synchron mit Freunden auf YouTube, Netflix, Twitch und mehr. Keine Anmeldung, kein Tracking.",
"HOWTO_STEP_1_NAME": "Browser-Erweiterung installieren",
@@ -138,7 +150,7 @@
"HOWTO_STEP_3_NAME": "Teilen und synchronisieren",
"HOWTO_STEP_3_TEXT": "Sende den Einladungslink an deine Freunde. Wähle einen Video-Tab aus und genieße synchronisierte Wiedergabe in Echtzeit.",
"FOOTER_MIT": "Open Source unter der MIT-Lizenz.",
"FOOTER_RAM": "Keine Daten werden auf unseren Servern gespeichert. Reines RAM-basiertes Relay.",
"FOOTER_RAM": "Keine Relay-Logs. Kein Tracking. Website-Access-Logs werden nach 7 Tagen gelöscht.",
"FOOTER_LEGAL": "Impressum",
"FOOTER_PRIVACY": "Datenschutz",
"MOCK_01": "Aktiver Raum",
@@ -156,7 +168,7 @@
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SYNC",
"MOCK_14": "Zu anderen springen",
"MOCK_15": "Letzte Aktivität",
"MOCK_16": "Warten auf 1 Partner (KoalaPC)...",
"MOCK_16": "Warten auf 1 Partner (🐶 HappyDog)...",
"MOCK_17": "Überspringen & abspielen",
"MOCK_18": "Dein Benutzername",
"MOCK_19": "Noise-Filterung",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "de/impressum",
"FOOTER_PRIVACY_LINK": "de/datenschutz",
"FOOTER_DISCLAIMER": "KoalaSync ist nicht mit Netflix, Disney+, Amazon, YouTube, Twitch oder anderen Streaming-Plattformen verbunden, wird von diesen nicht unterstützt oder steht in keiner Beziehung zu diesen. Alle Markenrechte liegen bei ihren jeweiligen Inhabern.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - Vergleiche & Anleitungen",
"ALT_INDEX_META_DESC": "Entdecke ehrliche Vergleiche und detaillierte Leitfäden zwischen KoalaSync und anderen Watch-Party-Erweiterungen.",
"ALT_INDEX_HEADING": "Vergleiche & Anleitungen",
"ALT_INDEX_INTRO": "Wie schlägt sich KoalaSync im Vergleich zu anderen Watch-Party-Tools und Streaming-Lösungen? Lies unsere ehrlichen, technischen Vergleiche, um die passende Lösung für deine Filmabende zu finden.",
"ALT_INDEX_READ_COMPARISON": "Vergleich lesen →",
"ALT_INDEX_READ_GUIDE": "Anleitung lesen →",
"ALT_INDEX_FOOTER_LINK": "Vergleiche & Anleitungen",
"ALT_TELEPARTY_TITLE": "KoalaSync vs Teleparty: Welche Watch-Party-Lösung passt besser?",
"ALT_TELEPARTY_META_DESC": "Ein ehrlicher Vergleich von KoalaSync und Teleparty für Watch-Partys, eingebauten Chat, Mobile-Support, Browser-Video-Sync, selbst gehostete Medienserver und Open Source.",
"ALT_TELEPARTY_INTRO": "KoalaSync und Teleparty lösen beide dasselbe nervige Problem: Du willst mit anderen Leuten online etwas schauen, ohne ständig „3, 2, 1, Play“ sagen zu müssen. Beide synchronisieren Videowiedergabe, aber sie gehen ziemlich unterschiedlich an die Sache ran.<br><br>Teleparty ist sinnvoll, wenn du einen eingebauten Chat möchtest oder am Handy schauen willst. KoalaSync ist eher für Desktop-Browser-Watch-Partys gedacht, bei denen du sowieso schon mit Freunden in Discord, Teamspeak oder einem anderen Voice-Call bist und einfach nur die Wiedergabe synchron halten möchtest.<br><br>Der Hauptgrund, warum ich KoalaSync gebaut habe, war Flexibilität. Es soll mit fast jeder Website funktionieren, die einen normalen Browser-Videoplayer nutzt, nicht nur mit einer festen Liste an Plattformen. Dazu gehören auch selbst gehostete Setups wie Emby, Jellyfin und Plex, lokale oder private Domains und viele normale Videoseiten im Browser.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Wann Teleparty trotzdem sinnvoller sein kann",
"ALT_TELEPARTY_WHEN_TELE_BODY": "Teleparty kann trotzdem die bessere Wahl sein, wenn du den Chat direkt im Watch-Party-Tool haben möchtest. KoalaSync hat aktuell keinen eingebauten Chat. Es geht eher davon aus, dass du sowieso schon woanders mit deinen Freunden redest, zum Beispiel in Discord, Teamspeak, WhatsApp, Signal oder einem normalen Anruf.<br><br>Mobile ist der zweite große Unterschied. Teleparty hat eine App, während KoalaSync aktuell nur auf Desktop-Browsern läuft. Wenn du Netflix oder andere Inhalte am Handy oder Tablet synchron schauen möchtest, ist KoalaSync im Moment nicht das richtige Tool dafür.<br><br>Wenn dir also vor allem eingebauter Chat und Mobile-Support wichtig sind, passt Teleparty wahrscheinlich besser zu deinem Setup.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "Wann KoalaSync besser passt",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "KoalaSync passt besser, wenn du ein flexibles Sync-Tool für den Desktop-Browser suchst und nicht auf eine kurze Plattformliste beschränkt sein möchtest.<br><br>Es funktioniert mit fast jeder Website, die einen normalen Browser-Videoplayer nutzt. Das ist besonders nützlich für Emby, Jellyfin, Plex, private Medienserver, lokale Domains oder kleinere Videoseiten, die klassische Watch-Party-Tools meistens gar nicht beachten.<br><br>Außerdem ist KoalaSync kostenlos, Open Source, braucht keine Accounts und kann bei Bedarf selbst gehostet werden. Ich habe es gebaut, weil ich etwas Privacy-freundliches wollte, das mit meinem eigenen Emby-Setup funktioniert, nicht noch eine geschlossene Plattform nur für ein paar große Streaming-Seiten.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "Der wichtigste Unterschied",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "Teleparty ist eher ein Watch-Party-Produkt mit Chat und Mobile-Unterstützung. KoalaSync ist eher eine flexible Sync-Schicht für Browser-Video.<br><br>Das klingt erstmal nach einem kleinen Unterschied, ändert aber den Use Case ziemlich stark. Wenn du eine Watch-Party-App mit eingebautem Chat brauchst, ergibt Teleparty Sinn. Wenn du schon einen Voice-Call hast und nur möchtest, dass Play, Pause und Seek zwischen mehreren Browsern synchron bleiben, passt KoalaSync besser.",
"ALT_TELEPARTY_LIMITS_TITLE": "Ehrliche Grenzen",
"ALT_TELEPARTY_LIMITS_BODY": "KoalaSync ist nicht magisch und versucht auch nicht, seine Grenzen zu verstecken. Es funktioniert aktuell nur auf Desktop-Browsern. Es gibt keinen eingebauten Chat und keine Mobile-App.<br><br>Die Kompatibilität hängt außerdem vom Browser und vom jeweiligen Videoplayer ab. KoalaSync sollte mit fast jedem normalen Browser-Videoplayer funktionieren, aber Websites können ihre Player, DRM-Logik oder Browser-Einschränkungen jederzeit ändern.<br><br>KoalaSync ist nicht mit Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex oder anderen hier genannten Marken verbunden. Die Namen werden nur verwendet, um typische Anwendungsfälle verständlich zu beschreiben.",
"ALT_TELEPARTY_SUMMARY_TITLE": "Kurz gesagt",
"ALT_TELEPARTY_SUMMARY_BODY": "Wenn du eingebauten Chat oder Mobile-Support brauchst, passt Teleparty wahrscheinlich besser. Wenn du kostenlose, Open-Source-Desktop-Browser-Synchronisierung möchtest, die mit fast jedem normalen Videoplayer funktioniert, inklusive selbst gehosteter Medienserver wie Emby, Jellyfin und Plex, ist KoalaSync wahrscheinlich die passendere Wahl.",
"ALT_SCREEN_TITLE": "KoalaSync vs Bildschirmübertragung: Wann ist Video-Sync besser?",
"ALT_SCREEN_META_DESC": "Ein ehrlicher Vergleich von KoalaSync und Bildschirmübertragung für Filmabende, Wiedergabekontrolle, Videoqualität, Privatsphäre, Untertitel und gemeinsames Schauen im Browser.",
"ALT_SCREEN_INTRO": "Bildschirmübertragung funktioniert, und manchmal reicht das auch völlig. Eine Person teilt den Bildschirm, alle anderen schauen zu, fertig.<br><br>Bei längeren Filmabenden oder Serien kann das aber schnell nervig werden. Die Qualität hängt vom Upload, der CPU, dem Browser und der Komprimierung durch Discord, Teamspeak, Zoom oder andere Call-Apps ab. Außerdem kontrolliert nur eine Person wirklich die Wiedergabe.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "Wann Bildschirmübertragung reicht",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "Screen Sharing ist sinnvoll, wenn es möglichst schnell gehen soll. Wenn nur eine Person Zugriff auf das Video hat oder alle damit okay sind, dass eine Person Play, Pause und Spulen kontrolliert, ist es wahrscheinlich die einfachste Lösung.<br><br>Es reicht auch oft, wenn die Qualität nicht so wichtig ist, niemand eigene Untertitel oder Audio-Einstellungen braucht und die Person, die teilt, kein Problem damit hat, genau dieses Fenster oder diesen Bildschirm zu zeigen.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "Wo Bildschirmübertragung nervig wird",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "Der größte Nachteil ist, dass nicht jeder im eigenen Player schaut. Eine Person streamt den Bildschirm, alle anderen sehen nur diesen Stream.<br><br>Das kann schlechtere Bildqualität, komprimierten Ton, Verzögerung und weniger Kontrolle für alle anderen bedeuten. Untertitel und Audiospur sind außerdem genau das, was die teilende Person eingestellt hat.<br><br>Dazu kommt der Privacy-Aspekt. Wenn du Fenster oder Tabs wechselst, können andere kurz deine Browser-Tabs, Mails, Reddit, Chats oder andere private Dinge sehen. Man kann das mit einem separaten Fenster umgehen, aber man muss trotzdem daran denken.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "Warum KoalaSync sauberer sein kann",
"ALT_SCREEN_WHY_CLEANER_BODY": "KoalaSync überträgt nicht deinen Bildschirm. Jeder schaut das Video lokal im eigenen Browser, und KoalaSync hält nur Aktionen wie Play, Pause und Spulen zwischen allen Teilnehmern synchron.<br><br>Dadurch kann jeder lokal Vollbild nutzen, eigene Untertitel oder Audio-Einstellungen verwenden, sofern der Player das unterstützt, und der Voice-Call bleibt separat in Discord, Teamspeak oder einer anderen App.<br><br>Für Filmabende fühlt sich das oft deutlich sauberer an, als wenn eine Person den ganzen Bildschirm streamt. Besonders praktisch ist das bei browserbasierten Setups wie Emby, Jellyfin, Plex, lokalen Dateien im Browser, privaten Domains oder anderen Websites mit normalen Browser-Videoplayern.",
"ALT_SCREEN_LIMITS_TITLE": "Ehrliche Grenzen",
"ALT_SCREEN_LIMITS_BODY": "KoalaSync ersetzt keinen Voice-Call. Wenn ihr beim Schauen reden wollt, nutzt ihr weiterhin Discord, Teamspeak, Signal, WhatsApp oder was auch immer ihr normalerweise verwendet.<br><br>Außerdem braucht jede Person selbst Zugriff auf das Video oder die jeweilige Seite. KoalaSync umgeht keine Abos, Logins, DRM, regionale Einschränkungen oder Plattformregeln.<br><br>KoalaSync ist aktuell Desktop-Browser-only, hat keinen eingebauten Chat oder Voice-Chat und die Funktion hängt immer vom jeweiligen Browser und Videoplayer ab.",
"ALT_SCREEN_SUMMARY_TITLE": "Kurz gesagt",
"ALT_SCREEN_SUMMARY_BODY": "Bildschirmübertragung ist die einfachste Lösung, wenn es schnell gehen soll und Qualität, Privatsphäre oder individuelle Kontrolle nicht so wichtig sind. KoalaSync passt besser, wenn jeder lokal im eigenen Browser schauen soll und sich der Filmabend mehr wie wirklich gemeinsames Schauen anfühlen soll.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Welche Watch-Party-Lösung passt besser?",
"ALT_W2G_META_DESC": "Ein ehrlicher Vergleich von KoalaSync und Watch2Gether für Watch-Partys. Erfahre, wann KoalaSyncs Flexibilität unschlagbar ist und warum Watch2Gether für YouTube die Nase vorn hat.",
"ALT_W2G_INTRO": "KoalaSync und Watch2Gether lösen beide dasselbe Problem: Du willst mit Freunden online Videos schauen, ohne ständig manuell abgleichen zu müssen. Während Watch2Gether einer der ältesten und bekanntesten Dienste für gemeinsame Videoabende ist, geht KoalaSync einen ganz anderen, minimalistischen Weg. Welches Tool das richtige für dich ist, hängt stark davon ab, was und wo du schauen möchtest.",
"ALT_W2G_WHEN_W2G_TITLE": "Wann Watch2Gether die bessere Wahl ist",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether ist unschlagbar für <strong>YouTube-Watch-Partys</strong>. Dank integrierter Playlist und Suche kann jeder in der Lobby sofort neue Videos suchen, hinzufügen und abspielen. Alle schauen nahtlos das nächste Video zusammen.<br><br>Bei KoalaSync greift auf YouTube die Autoplay-Logik der Website: Nach Videoende wird jeder auf ein anderes Empfehlungsvideo geleitet. Unsere automatische Folgen-Fortsetzung („Episode Lobby“), die auf Netflix oder Disney+ super funktioniert, klappt auf YouTube wegen dieser dynamischen Vorschläge leider nicht. Für reine YouTube-Abende ist Watch2Gether daher die bessere Wahl.",
"ALT_W2G_WHEN_KOALA_TITLE": "Wann KoalaSync besser passt",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync glänzt bei <strong>echten Streaming-Diensten</strong> (Netflix, Prime Video, Disney+) und selbstgehosteten Medienservern (Jellyfin, Plex, Emby). Im Gegensatz zu Watch2Gether, das oft keine Premium-Inhalte abspielen kann, synchronisiert KoalaSync den echten Original-Player direkt auf der jeweiligen Website.<br><br>Es funktioniert auf fast jeder Seite mit einem normalen HTML5-Videoplayer. Du musst keinen virtuellen Raum auf einer Drittseite erstellen: Einfach die Extension aktivieren, mit Freunden verbinden und losschauen.",
"ALT_W2G_MAIN_DIFF_TITLE": "Der wichtigste Unterschied",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether ist eine <strong>eigenständige Website</strong> mit integriertem Chat, Playlist und Webcam-Support. KoalaSync ist eine <strong>minimalistische Erweiterung</strong>, die sich direkt in deine normalen Browser-Tabs einklinkt.<br><br>Suchst du ein All-in-One-Paket für YouTube mit Chat? Nimm Watch2Gether. Willst du Filme auf Netflix oder deinem eigenen Server ohne Ablenkung, ohne Umwege und in voller nativer Bildqualität (ohne Server-Komprimierung) genießen? Dann ist KoalaSync die sauberere Lösung.",
"ALT_W2G_LIMITS_TITLE": "Ehrliche Grenzen",
"ALT_W2G_LIMITS_BODY": "KoalaSync hat <strong>keine eigene Playlist oder Suche</strong> wie Watch2Gether. Auf YouTube führt das dazu, dass das automatische Abspielen der nächsten Videos nicht synchron läuft.<br><br>Zudem gibt es <strong>keinen integrierten Chat</strong> und KoalaSync läuft aktuell nur auf Desktop-Browsern. Es ist so gedacht, dass ihr ohnehin über Discord, Teamspeak oder andere Voice-Tools miteinander sprecht.",
"ALT_W2G_SUMMARY_TITLE": "Kurz gesagt",
"ALT_W2G_SUMMARY_BODY": "Für spontane YouTube-Sessions mit Playlists und integriertem Chat ist Watch2Gether die beste Wahl. Für echte Premium-Streaming-Plattformen (wie Netflix, Prime Video, Disney+), selbstgehostete Server (Jellyfin, Plex, Emby) und maximale Flexibilität im Browser ist KoalaSync das passendere und schlankere Tool.",
"ALT_TRY_KOALASYNC": "KoalaSync ausprobieren",
"MOCK_HOST_CONTROL_LABEL": "Host-Steuerung",
"MOCK_HOST_CONTROL_TOGGLE": "Nur ich darf die Wiedergabe steuern",
"LABEL_SELECT_VIDEO": "Video auswählen",
"OPTION_SELECT_TAB": "-- Wähle einen Tab --",
"THEME_TOGGLE_LABEL": "Hell-/Dunkelmodus umschalten",
"FAQ_SHOW_ALL": "Alle Fragen anzeigen",
"FAQ_HIDE_ALL": "Fragen ausblenden",
"SELF_SHOW_SETUP": "Setup-Anleitung anzeigen",
"SELF_HIDE_SETUP": "Setup-Anleitung ausblenden",
"HERO_TRUST": "Kostenlos · Open Source · Kein Account nötig",
"COMP_MORE_GUIDES": "Ausführlicher Vergleich und weitere Alternativen"
}
+104 -34
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "de/",
"LANG_TOGGLE_TEXT": "DE",
"OG_LOCALE": "en_US",
"META_TITLE": "KoalaSync | Sync Netflix, YouTube & Any Video with Friends",
"META_DESCRIPTION": "Watch Netflix, YouTube, Twitch & any HTML5 video in perfect sync with friends. Free, open-source browser extension for Chrome and Firefox. No sign-up needed.",
"SCHEMA_APP_DESC": "Watch Netflix, YouTube, Twitch & any HTML5 video in perfect sync with friends. Free, open-source browser extension for Chrome and Firefox. No sign-up needed.",
"OG_TITLE": "KoalaSync | Sync Netflix, Emby, Jellyfin & Almost Any Video with Friends",
"OG_DESCRIPTION": "Watch Netflix, Emby, Jellyfin, YouTube, Twitch and almost any HTML5 video in perfect sync. Privacy-first, open-source browser extension for Chrome & Firefox.",
"TWITTER_TITLE": "KoalaSync | Sync Netflix, Emby, Jellyfin & Almost Any Video with Friends Browser Extension",
"TWITTER_DESCRIPTION": "Watch Netflix, Emby, Jellyfin, YouTube, Twitch and almost any HTML5 video in perfect sync with friends. Privacy-first, open-source browser extension for Chrome and Firefox.",
"META_TITLE": "KoalaSync | Watch Party for Netflix, YouTube & Any Video",
"META_DESCRIPTION": "Host a watch party on Netflix, YouTube, Twitch & HTML5 video. Free open-source extension for Chrome & Firefox. No account, tracking, or ads.",
"SCHEMA_APP_DESC": "Host a watch party on Netflix, YouTube, Twitch & HTML5 video. Free open-source extension for Chrome & Firefox. No account, tracking, or ads.",
"OG_TITLE": "KoalaSync | Watch Party for Netflix, Emby, Jellyfin & Almost Any Video",
"OG_DESCRIPTION": "Host a watch party on Netflix, Emby, Jellyfin, YouTube, Twitch and almost any HTML5 video. Open source, privacy-first, and account-free.",
"TWITTER_TITLE": "KoalaSync | Watch Party for Netflix, Emby, Jellyfin & Almost Any Video Browser Extension",
"TWITTER_DESCRIPTION": "Host a watch party on Netflix, Emby, Jellyfin, YouTube, Twitch and almost any HTML5 video. Open source, privacy-first, and account-free.",
"NAV_FEATURES": "Features",
"NAV_HOW_IT_WORKS": "How it works",
"HERO_TITLE": "Watch Together.<span class='hero-line2'>Sync Perfectly.</span>",
"HERO_SUBTITLE": "Your remote movie night without lags. No registration, no data collection. Just share a link and watch together.",
"HERO_TITLE": "Turn any video into a <span class='hero-highlight'>watch party</span>.",
"HERO_SUBTITLE": "Your remote movie night without lags. No account, no tracking, no ads. Just share a link and press play together.",
"HERO_MASCOT_ALT": "A cute koala standing and looking down at the download buttons",
"DEMO_SYNC": "In sync",
"DEMO_HINT": "Live demo: press play in either tab and KoalaSync keeps both in sync.",
"DEMO_NO_ROOM": "Not connected yet",
"DEMO_CREATE_ROOM": "Create Room",
"DEMO_JOINED": "Room joined",
"DEMO_TAB_SELECTED": "Tab selected",
"DEMO_SELECT_PLACEHOLDER": "Choose a video tab…",
"MOBILE_DEMO_CTA": "Watch how it works",
"MOBILE_DEMO_TITLE": "KoalaSync Live Demo",
"MOBILE_DEMO_CLOSE": "Close demo",
"ADD_TO_CHROME": "Add to Chrome",
"ADD_TO_FIREFOX": "Add to Firefox",
"COMPAT_HEADING": "Works on your favorite platforms",
@@ -35,22 +45,22 @@
"USE_CASE_3_DESC": "Bridge the distance and enjoy date nights. Experience every plot twist, laugh at the same jokes, and share emotional moments at the exact same millisecond.",
"WHY_TITLE": "Why KoalaSync?",
"WHY_SUBTITLE": "Built for reliable sync, privacy-first design, and easy setup.",
"FEATURE_1_TITLE": "Full Control / Instant Sync",
"FEATURE_1_DESC": "One pauses, everyone pauses. One seeks, everyone follows. Our two-phase sync protocol coordinates playback in real time across all participants.",
"FEATURE_1_TITLE": "Host Control & Sync",
"FEATURE_1_DESC": "You decide who controls playback. Turn on Host Control to lock playback actions to yourself, or promote individual peers to co-hosts in the participant list.",
"FEATURE_2_TITLE": "Endless Binge-Watching",
"FEATURE_2_DESC": "Autoplay in sync. KoalaSync automatically detects episode transitions and holds playback until all peers have successfully loaded the next video.",
"FEATURE_3_TITLE": "Zero Accounts / Pure Privacy",
"FEATURE_3_DESC": "No registration, no tracking, no persistency footprints. The server runs entirely in ephemeral RAM and purges your room when you leave.",
"FEATURE_4_TITLE": "Universal HTML5 Support",
"FEATURE_4_DESC": "Works on YouTube, Twitch, Netflix, Jellyfin, Emby, and almost any standard webpage containing a HTML5 video element. Also compatible with custom self-hosted setups. No video data is ever sent to our servers — only synchronization metadata.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby and almost any page with an HTML5 video. Only sync signals leave your browser, never the video.",
"FEATURE_5_TITLE": "Self-Hostable & Docker-Ready",
"FEATURE_5_DESC": "Our official servers are free and fast, but you can take full ownership if you want to. Launch your own private relay server in seconds via Docker.",
"FEATURE_6_TITLE": "Instant Invites / 1-Click Join",
"FEATURE_6_DESC": "No IP addresses or passwords to exchange. Share a generated invite link with your friends to let them join your room automatically with a single click.",
"FEATURE_7_TITLE": "Volume Leveling / Audio Compressor",
"FEATURE_7_DESC": "No more riding the volume knob. Built-in audio compressor boosts quiet dialogue and tames loud explosions. Works on Netflix, Prime Video, Disney+, YouTube, and any HTML5 video. Completely local — no audio data leaves your computer.",
"FEATURE_7_DESC": "No more riding the volume knob. A built-in compressor lifts quiet dialogue and tames loud explosions, all processed locally in your browser.",
"FEATURE_8_TITLE": "Open Source (MIT License)",
"FEATURE_8_DESC": "The entire codebase is publicly auditable on GitHub. No black boxes, no proprietary blobs 100% transparency from the relay server to the browser extension.",
"FEATURE_8_DESC": "The entire codebase is publicly auditable on GitHub. No black boxes, no proprietary blobs. 100% transparency from the relay server to the browser extension.",
"COMP_TITLE": "KoalaSync vs. Teleparty",
"COMP_SUBTITLE": "See why open source, ad-free and privacy-first is the superior way to watch together.",
"COMP_COL_FEATURE": "Feature",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Teleparty privacy policies and tracking data collection:",
"COMP_FOOTNOTE_4": "Works on websites that allow script injections into standard HTML5 video tags. Websites with highly strict Content Security Policies (CSP), DRM copy protection, or heavily obfuscated player wrappers (like complex shadow DOMs) might restrict automated control or injection.",
"STEPS_TITLE": "Getting Started",
"STEP_1_TITLE": "Install Extension",
"STEP_1_DESC": "Add KoalaSync to your browser from the Chrome Web Store, Firefox Add-ons, or download the latest developer ZIP from GitHub.",
"STEP_1_ILLUS_DESC": "Privacy-first video synchronizer",
"STEP_1_ILLUS_DL_CHROME": "Download for Chrome",
"STEP_1_ILLUS_DL_FIREFOX": "Download for Firefox",
"STEP_1_ILLUS_ACTIVE": "Extension Active",
"STEP_1_ILLUS_READY": "Ready to watch together!",
"STEP_2_TITLE": "Create a Room",
"STEP_2_DESC": "Open the extension popup and click '+ Create New Room'. KoalaSync automatically generates a secure Room ID and Password, joins it, and copies the invite link to your clipboard.",
"STEP_1_TITLE": "Create & Share Room",
"STEP_1_DESC": "Open the extension popup, create a room with one click, and share the link with your friends. They join instantly via the invitation bridge.",
"STEP_1_ILLUS_DESC": "Select a tab to link",
"STEP_1_ILLUS_DL_CHROME": "Link YouTube Tab",
"STEP_1_ILLUS_DL_FIREFOX": "Link Netflix Tab",
"STEP_1_ILLUS_ACTIVE": "Tab Linked",
"STEP_1_ILLUS_READY": "Ready to sync playback!",
"STEP_2_TITLE": "Select Video Tab",
"STEP_2_DESC": "Go to your video tab and select the tab you want to synchronize in the popup. KoalaSync automatically links your playheads from here.",
"STEP_2_ILLUS_ROOM": "Room",
"STEP_2_ILLUS_SYNC": "Sync",
"STEP_2_ILLUS_SETTINGS": "Settings",
"STEP_2_ILLUS_CREATE": "+ Create New Room",
"STEP_2_ILLUS_MANUAL": "Manual Connect / Advanced",
"STEP_2_ILLUS_COPIED": "Invite link copied!",
"STEP_3_TITLE": "Share & Sync",
"STEP_3_DESC": "Send the invite link to your friends. Once they join, select your video tab and enjoy synchronized playback.",
"STEP_3_TITLE": "Enjoy Perfect Sync",
"STEP_3_DESC": "Press play. Whether someone pauses, seeks, or plays, everyone's playback stays perfectly in sync without any lag.",
"STEP_3_ILLUS_IN_SYNC": "IN SYNC",
"SELF_TITLE": "For Self-Hosters",
"SELF_SUBTITLE": "Don't trust our server? Maintain full data sovereignty. Deploy your own private relay server in minutes.",
@@ -116,19 +126,21 @@
"FAQ_TITLE": "Frequently Asked Questions",
"FAQ_SUBTITLE": "Everything you need to know about watching videos together in perfect sync.",
"FAQ_Q1": "Can I watch Netflix, Emby or Jellyfin together in sync with friends?",
"FAQ_A1": "Yes! KoalaSync works on Netflix, YouTube, Twitch and any website with an HTML5 video player including Emby, Jellyfin and self-hosted media servers. Install the extension, create a room, share the invite link, and everyone watches in perfect sync.",
"FAQ_A1": "Yes! KoalaSync works on Netflix, YouTube, Twitch and any website with an HTML5 video player, including Emby, Jellyfin and self-hosted media servers. Install the extension, create a room, share the invite link, and everyone watches in perfect sync.",
"FAQ_Q_VIDEO_ACCESS": "Does KoalaSync stream the video to my friends?",
"FAQ_A_VIDEO_ACCESS": "No. KoalaSync does not stream, host, share, or bypass access to video content. Everyone watches the video locally in their own browser and needs their own access to the website or streaming service, such as their own Netflix account. KoalaSync only synchronizes playback actions like play, pause, and seeking.",
"FAQ_Q2": "Is KoalaSync a free alternative to Teleparty?",
"FAQ_A2": "100% free and open source under the MIT License. No premium tiers, no locked features, no ads everything is included. Unlike Teleparty, KoalaSync's relay server is RAM-only with zero data collection or user accounts required.",
"FAQ_A2": "100% free and open source under the MIT License. No premium tiers, no locked features, no ads, everything is included. Unlike Teleparty, KoalaSync's relay server is RAM-only with zero data collection or user accounts required.",
"FAQ_Q3": "Does KoalaSync work with Emby, Jellyfin and self-hosted media servers?",
"FAQ_A3": "Yes. KoalaSync detects any standard HTML5 video element, which means it works perfectly with Emby, Jellyfin, Plex, and any custom self-hosted streaming setup. You can also self-host the KoalaSync relay server via Docker for complete data sovereignty.",
"FAQ_Q4": "Which browsers and platforms support KoalaSync?",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave, and all Chromium-based browsers. Install it directly from the Chrome Web Store or Firefox Add-ons no manual configuration needed.",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave, and all Chromium-based browsers. Install it directly from the Chrome Web Store or Firefox Add-ons, no manual configuration needed.",
"FAQ_Q5": "Do all my friends need the extension installed?",
"FAQ_A5": "Yes, each participant needs KoalaSync installed but there's no sign-up or account creation. Simply click the invitation link, install the extension if you haven't already, and you'll join the room automatically in seconds.",
"FAQ_A5": "Yes, each participant needs KoalaSync installed, but there's no sign-up or account creation. Simply click the invitation link, install the extension if you haven't already, and you'll join the room automatically in seconds.",
"FAQ_Q6": "Is my data safe? What if I don't trust the official server?",
"FAQ_A6": "Completely safe. KoalaSync's relay server is RAM-only all session data is permanently deleted the moment you leave the room. No database, no logging, no analytics, no accounts. And if you prefer full control: you can self-host your own private relay server via Docker in minutes. The entire codebase is open source and auditable on GitHub.",
"FAQ_A6": "Completely safe. KoalaSync's relay server is RAM-only, all session data is permanently deleted the moment you leave the room. No database, no logging, no analytics, no accounts. And if you prefer full control: you can self-host your own private relay server via Docker in minutes. The entire codebase is open source and auditable on GitHub.",
"FAQ_Q7": "Can I fix quiet dialogue and loud explosions when watching videos in the browser?",
"FAQ_A7": "Yes. KoalaSync 2.2.0 includes a built-in audio compressor that reduces the gap between quiet and loud parts of your video. It runs locally via the Web Audio API no audio data ever leaves your computer. Open the popup, create a room, go to Settings → Audio Processing, toggle the compressor on, and pick a preset. Works on Netflix, Prime Video, Disney+, YouTube, and any HTML5 video site.",
"FAQ_A7": "Yes. KoalaSync 2.2.0 includes a built-in audio compressor that reduces the gap between quiet and loud parts of your video. It runs locally via the Web Audio API, no audio data ever leaves your computer. Open the popup, create a room, go to Settings → Audio Processing, toggle the compressor on, and pick a preset. Works on Netflix, Prime Video, Disney+, YouTube, and any HTML5 video site.",
"HOWTO_TITLE": "How to synchronize videos with friends using KoalaSync",
"HOWTO_DESC": "Watch any video in perfect sync with friends across YouTube, Netflix, Twitch and more. No sign-up, no tracking.",
"HOWTO_STEP_1_NAME": "Install the browser extension",
@@ -138,7 +150,7 @@
"HOWTO_STEP_3_NAME": "Share and sync",
"HOWTO_STEP_3_TEXT": "Send the invite link to your friends. Pick a video tab and enjoy synchronized playback in real time.",
"FOOTER_MIT": "Open source under the MIT License.",
"FOOTER_RAM": "No data is stored on our servers. Pure RAM-based relay.",
"FOOTER_RAM": "No relay logs. No tracking. Website access logs are deleted after 7 days.",
"FOOTER_LEGAL": "Legal Notice",
"FOOTER_PRIVACY": "Privacy Policy",
"MOCK_01": "Active Room",
@@ -156,7 +168,7 @@
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SYNC",
"MOCK_14": "Jump to Others",
"MOCK_15": "Last Activity Status",
"MOCK_16": "Waiting for 1 peer (KoalaPC)...",
"MOCK_16": "Waiting for 1 peer (🐶 HappyDog)...",
"MOCK_17": "Skip & Play anyway",
"MOCK_18": "Your Username",
"MOCK_19": "Hide Clutter Tabs",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSync is not affiliated with, endorsed by, or associated with Netflix, Disney+, Amazon, YouTube, Twitch, or any other streaming platform. All trademarks are property of their respective owners.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - Alternatives & Comparisons",
"ALT_INDEX_META_DESC": "Explore honest comparisons and detailed guides comparing KoalaSync with other watch party extensions and streaming solutions.",
"ALT_INDEX_HEADING": "Guides & Comparisons",
"ALT_INDEX_INTRO": "How does KoalaSync stack up against other watch party tools and streaming solutions? Read our honest, technical comparisons to find the best fit for your remote movie nights.",
"ALT_INDEX_READ_COMPARISON": "Read comparison →",
"ALT_INDEX_READ_GUIDE": "Read guide →",
"ALT_INDEX_FOOTER_LINK": "Guides & comparisons",
"ALT_TELEPARTY_TITLE": "KoalaSync vs Teleparty: which watch party tool should you use?",
"ALT_TELEPARTY_META_DESC": "A practical comparison of KoalaSync and Teleparty for watch parties, built-in chat, mobile support, browser video sync, self-hosted media servers and open-source use.",
"ALT_TELEPARTY_INTRO": "KoalaSync and Teleparty both help with the same annoying problem: watching something together without doing the whole \"3, 2, 1, play\" routine. They just take a different approach.<br><br>Teleparty makes the most sense if you want a built-in chat or if you want to watch on mobile. KoalaSync is more for desktop browser watch parties where you are already talking on Discord, Teamspeak or another voice call and just want playback to stay in sync.<br><br>The main reason I built KoalaSync was flexibility. It is meant to work with almost any website that uses a normal browser video player, not just a fixed list of platforms. That includes self-hosted setups like Emby, Jellyfin and Plex, local/private domains, and a lot of normal video pages in the browser.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "When Teleparty may still make sense",
"ALT_TELEPARTY_WHEN_TELE_BODY": "Teleparty can still be the better choice if you want the chat to be part of the watch party tool itself. KoalaSync does not currently have a built-in chat. It assumes you are already talking somewhere else, like Discord, Teamspeak, WhatsApp, Signal or a normal call.<br><br>Mobile is the other big reason. Teleparty has an app, while KoalaSync currently only works on desktop browsers. If you want to sync Netflix on a phone or tablet, KoalaSync is not the right tool for that right now.<br><br>So if chat and mobile support are the things you care about most, Teleparty may simply fit your setup better.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "When KoalaSync may fit better",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "KoalaSync fits better if you want a flexible desktop browser sync tool instead of something tied to a short platform list.<br><br>It works with almost any website that uses a normal browser video player. That is especially useful for Emby, Jellyfin, Plex, private media servers, local domains, or smaller video sites that classic watch party tools usually ignore.<br><br>It is also free, open source, does not require accounts, and can be self-hosted if you want to run your own relay. I built it because I wanted something privacy-friendly that worked with my own Emby setup, not another closed platform just for a few big streaming sites.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "The main difference",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "Teleparty is more of a watch party product with chat and mobile support. KoalaSync is more of a flexible sync layer for browser video.<br><br>That sounds like a small difference, but it changes the use case a lot. If you need a full watch party app with chat inside it, Teleparty makes sense. If you already have voice chat and just want the video controls to stay synced across browsers, KoalaSync is the simpler fit.",
"ALT_TELEPARTY_LIMITS_TITLE": "Honest limitations",
"ALT_TELEPARTY_LIMITS_BODY": "KoalaSync is not magic and it is not trying to hide its limits. It currently works only on desktop browsers. It has no built-in chat and no mobile app.<br><br>Compatibility also depends on the browser and the video player. KoalaSync should work with almost any normal browser video player, but websites can change their players, DRM behavior or browser restrictions at any time.<br><br>KoalaSync is not affiliated with Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex or any other brand mentioned here. The names are only used to explain typical use cases.",
"ALT_TELEPARTY_SUMMARY_TITLE": "Summary",
"ALT_TELEPARTY_SUMMARY_BODY": "If you want built-in chat or mobile support, Teleparty may be the better fit. If you want free, open-source desktop browser sync that works with almost any normal video player, including self-hosted media servers like Emby, Jellyfin and Plex, KoalaSync is probably the better match.",
"ALT_SCREEN_TITLE": "KoalaSync vs screen sharing: when is video sync better?",
"ALT_SCREEN_META_DESC": "A practical comparison of KoalaSync and screen sharing for remote movie nights, playback control, video quality, privacy, subtitles and browser-based watching.",
"ALT_SCREEN_INTRO": "Screen sharing works, and sometimes it is honestly enough. One person shares their screen, everyone else watches, and you are done.<br><br>But for longer movie nights or watching episodes together, it can get annoying. The video quality depends on the sharers upload, CPU, browser, and whatever compression Discord, Teamspeak, Zoom or another call app applies. Also, only one person really controls the playback.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "When screen sharing is enough",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "Screen sharing is fine when you want the fastest possible setup. If only one person has access to the video, or everyone is okay with one person controlling play, pause and seeking, it may be the simplest option.<br><br>It also makes sense when quality is not a big deal, when nobody needs their own subtitles or audio settings, and when the person sharing is comfortable showing that browser window or screen.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "Where screen sharing gets annoying",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "The biggest downside is that not everyone is watching in their own player. One person streams their screen, and everyone else only sees that stream.<br><br>That can mean worse video quality, compressed audio, delayed playback, and less control for everyone else. Subtitles and audio tracks are also whatever the sharer picked.<br><br>There is also the privacy side. If you switch windows or tabs, people may briefly see your browser tabs, email, Reddit, chats or other private stuff. You can work around that with a separate window, but it is still something you have to think about.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "Why KoalaSync can feel cleaner",
"ALT_SCREEN_WHY_CLEANER_BODY": "KoalaSync does not stream your screen. Everyone watches the video locally in their own browser, and KoalaSync only keeps playback actions like play, pause and seeking in sync.<br><br>That means everyone can use fullscreen on their own device, use their own subtitle or audio settings if the player supports it, and keep the voice chat separate in Discord, Teamspeak or another call.<br><br>For movie nights, that can feel a lot cleaner than one person broadcasting their screen. It is especially useful for browser-based setups like Emby, Jellyfin, Plex, local files opened in the browser, private domains, or other websites with normal browser video players.",
"ALT_SCREEN_LIMITS_TITLE": "Honest limitations",
"ALT_SCREEN_LIMITS_BODY": "KoalaSync is not a replacement for a voice call. If you want to talk while watching, you still use Discord, Teamspeak, Signal, WhatsApp or whatever you normally use.<br><br>Everyone also needs access to the video or page on their own device. KoalaSync does not bypass subscriptions, logins, DRM, regional restrictions or platform rules.<br><br>It is currently desktop-browser only, has no built-in chat or voice feature, and compatibility always depends on the browser and the video player.",
"ALT_SCREEN_SUMMARY_TITLE": "Summary",
"ALT_SCREEN_SUMMARY_BODY": "Screen sharing is the simplest option when you just want something quick and do not care much about quality, privacy or individual control. KoalaSync is a better fit when everyone should watch locally in their own browser and the session should feel more like actually watching together.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "Try KoalaSync",
"MOCK_HOST_CONTROL_LABEL": "Host Control",
"MOCK_HOST_CONTROL_TOGGLE": "Only I can control playback",
"LABEL_SELECT_VIDEO": "Select Video",
"OPTION_SELECT_TAB": "-- Select a Tab --",
"THEME_TOGGLE_LABEL": "Toggle light/dark mode",
"FAQ_SHOW_ALL": "Show all questions",
"FAQ_HIDE_ALL": "Hide questions",
"SELF_SHOW_SETUP": "Show setup guide",
"SELF_HIDE_SETUP": "Hide setup guide",
"HERO_TRUST": "Free · Open source · No account needed",
"COMP_MORE_GUIDES": "Full comparison and more alternatives"
}
+96 -26
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "es_ES",
"META_TITLE": "KoalaSync | Sincroniza Netflix, YouTube y cualquier video con amigos",
"META_DESCRIPTION": "Mira Netflix, YouTube, Twitch y cualquier video HTML5 en perfecta sincronización con amigos. Extensión de navegador gratuita, de código abierto y sin necesidad de registro.",
"SCHEMA_APP_DESC": "Mira Netflix, YouTube, Twitch y cualquier video HTML5 en perfecta sincronización con amigos. Extensión de navegador gratuita, de código abierto y sin necesidad de registro.",
"OG_TITLE": "KoalaSync | Sincroniza Netflix, Emby, Jellyfin y casi cualquier video con amigos",
"OG_DESCRIPTION": "Mira Netflix, Emby, Jellyfin, YouTube, Twitch y casi cualquier video HTML5 en perfecta sincronización. Extensión de navegador de código abierto y respetuosa con la privacidad para Chrome y Firefox.",
"TWITTER_TITLE": "KoalaSync | Sincroniza Netflix, Emby, Jellyfin y casi cualquier video con amigos Extensión de navegador",
"TWITTER_DESCRIPTION": "Mira Netflix, Emby, Jellyfin, YouTube, Twitch y casi cualquier video HTML5 en perfecta sincronización con amigos. Extensión de navegador de código abierto y respetuosa con la privacidad para Chrome y Firefox.",
"META_TITLE": "KoalaSync | Watch party para Netflix, YouTube y s",
"META_DESCRIPTION": "Watch party en Netflix, YouTube, Twitch y cualquier vídeo HTML5. Extensión gratuita y open source para Chrome y Firefox. Sin cuenta, sin rastreo, sin anuncios.",
"SCHEMA_APP_DESC": "Watch party en Netflix, YouTube, Twitch y cualquier vídeo HTML5. Extensión gratuita y open source para Chrome y Firefox. Sin cuenta, sin rastreo, sin anuncios.",
"OG_TITLE": "KoalaSync | Watch party para Netflix, Emby, Jellyfin y casi cualquier vídeo",
"OG_DESCRIPTION": "Organiza una watch party en Netflix, Emby, Jellyfin, YouTube, Twitch y casi cualquier vídeo HTML5. Open source, privada y sin cuenta.",
"TWITTER_TITLE": "KoalaSync | Watch party para Netflix, Emby, Jellyfin y casi cualquier vídeo Extensión",
"TWITTER_DESCRIPTION": "Organiza una watch party en Netflix, Emby, Jellyfin, YouTube, Twitch y casi cualquier vídeo HTML5. Open source, privada y sin cuenta.",
"NAV_FEATURES": "Características",
"NAV_HOW_IT_WORKS": "Cómo funciona",
"HERO_TITLE": "Miren juntos.<span class='hero-line2'>Sincronización perfecta.</span>",
"HERO_SUBTITLE": "Tu noche de películas a distancia sin retrasos. Sin registros ni recopilación de datos. Solo comparte un enlace y miren juntos.",
"HERO_TITLE": "Convierte cualquier vídeo en una <span class='hero-highlight'>watch party</span>.",
"HERO_SUBTITLE": "Tu noche de cine a distancia, sin retrasos. Sin cuenta, sin rastreo, sin anuncios. Comparte un enlace y dale al play juntos.",
"HERO_MASCOT_ALT": "Un lindo koala de pie mirando hacia abajo a los botones de descarga",
"DEMO_SYNC": "Sincronizado",
"DEMO_HINT": "Demo en vivo: pulsa play en cualquiera de las pestañas y KoalaSync mantiene ambas sincronizadas.",
"DEMO_NO_ROOM": "Aún sin conexión",
"DEMO_CREATE_ROOM": "Crear sala",
"DEMO_JOINED": "Conectado a la sala",
"DEMO_TAB_SELECTED": "Pestaña seleccionada",
"DEMO_SELECT_PLACEHOLDER": "Elige una pestaña de video…",
"MOBILE_DEMO_CTA": "Ver cómo funciona",
"MOBILE_DEMO_TITLE": "Demo en vivo de KoalaSync",
"MOBILE_DEMO_CLOSE": "Cerrar demo",
"ADD_TO_CHROME": "Añadir a Chrome",
"ADD_TO_FIREFOX": "Añadir a Firefox",
"COMPAT_HEADING": "Funciona en tus plataformas favoritas",
@@ -35,20 +45,20 @@
"USE_CASE_3_DESC": "Reduce la distancia y disfruta de noches de citas. Vive cada giro de la trama, ríete de los mismos chistes y comparte momentos emotivos en el mismo milisegundo exacto.",
"WHY_TITLE": "¿Por qué KoalaSync?",
"WHY_SUBTITLE": "Diseñado para una sincronización confiable, con la privacidad como prioridad y una configuración sencilla.",
"FEATURE_1_TITLE": "Control total / Sincronización instantánea",
"FEATURE_1_DESC": "Si uno pausa, todos pausan. Si uno avanza, todos le siguen. Nuestro protocolo de sincronización en dos fases coordina la reproducción en tiempo real para todos los participantes.",
"FEATURE_1_TITLE": "Control del host y sincronización",
"FEATURE_1_DESC": "Tú decides quién controla la reproducción. Activa el control del anfitrión para que solo tú puedas pausar y avanzar, o asigna coanfitriones.",
"FEATURE_2_TITLE": "Maratones sin fin",
"FEATURE_2_DESC": "Reproducción automática sincronizada. KoalaSync detecta automáticamente los cambios de episodio y detiene la reproducción hasta que todos los participantes hayan cargado el siguiente video.",
"FEATURE_3_TITLE": "Sin cuentas / Privacidad absoluta",
"FEATURE_3_DESC": "Sin registros, sin seguimiento y sin dejar huella. El servidor funciona completamente en RAM efímera y elimina tu sala en cuanto te vas.",
"FEATURE_4_TITLE": "Soporte universal HTML5",
"FEATURE_4_DESC": "Funciona en YouTube, Twitch, Netflix, Jellyfin, Emby y casi cualquier página web estándar con un reproductor HTML5. También es compatible con configuraciones propias. Nunca enviamos datos de vídeo a nuestros servidores, solo metadatos de sincronización.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby y casi cualquier página con vídeo HTML5. Solo salen las señales de sincronización, nunca el vídeo.",
"FEATURE_5_TITLE": "Auto-alojable y listo para Docker",
"FEATURE_5_DESC": "Nuestros servidores oficiales son gratuitos y rápidos, pero puedes tomar el control total si lo deseas. Inicia tu propio servidor de retransmisión privado en segundos mediante Docker.",
"FEATURE_6_TITLE": "Invitaciones al instante / Unirse con un clic",
"FEATURE_6_DESC": "Sin intercambio de direcciones IP o contraseñas. Comparte un enlace de invitación generado para que tus amigos se unan automáticamente a tu sala con un solo clic.",
"FEATURE_7_TITLE": "Nivelación de volumen / Compresor de audio",
"FEATURE_7_DESC": "Se acabó el tener que ajustar el volumen constantemente. El compresor de audio integrado realza los diálogos bajos y suaviza las explosiones fuertes. Funciona en Netflix, Prime Video, Disney+, YouTube y cualquier video HTML5. Funciona de forma local: ningún dato de audio sale de tu equipo.",
"FEATURE_7_DESC": "Se acabó ajustar el volumen. Un compresor integrado sube los diálogos bajos y suaviza las explosiones fuertes, todo procesado localmente en tu navegador.",
"FEATURE_8_TITLE": "Código Abierto (Licencia MIT)",
"FEATURE_8_DESC": "Todo el código fuente es público y auditable en GitHub. Sin cajas negras ni software propietario: transparencia total desde el servidor de retransmisión hasta la extensión del navegador.",
"COMP_TITLE": "KoalaSync vs Teleparty",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Políticas de privacidad y recopilación de datos de Teleparty:",
"COMP_FOOTNOTE_4": "Funciona en sitios web que permiten inyecciones de scripts en etiquetas de video HTML5 estándar. Los sitios con políticas de seguridad (CSP) muy estrictas, protección DRM o reproductores ofuscados (como DOMs de sombra complejos) pueden restringir el control automático.",
"STEPS_TITLE": "Cómo empezar",
"STEP_1_TITLE": "Instalar la extensión",
"STEP_1_DESC": "Añade KoalaSync a tu navegador desde la Chrome Web Store, complementos de Firefox o descarga el archivo ZIP para desarrolladores desde GitHub.",
"STEP_1_ILLUS_DESC": "Sincronizador de video privado",
"STEP_1_ILLUS_DL_CHROME": "Descargar para Chrome",
"STEP_1_ILLUS_DL_FIREFOX": "Descargar para Firefox",
"STEP_1_ILLUS_ACTIVE": "Extensión activa",
"STEP_1_ILLUS_READY": "¡Listo para ver juntos!",
"STEP_2_TITLE": "Crear una sala",
"STEP_2_DESC": "Abre el menú de la extensión y haz clic en '+ Crear nueva sala'. KoalaSync generará automáticamente un ID de sala y una contraseña seguros, se conectará y copiará el enlace al portapapeles.",
"STEP_1_TITLE": "Crear y compartir sala",
"STEP_1_DESC": "Abre la extensión, crea una sala con un clic y comparte el enlace con tus amigos. Se unirán al instante a través del enlace de invitación.",
"STEP_1_ILLUS_DESC": "Elige una pestaña para vincular",
"STEP_1_ILLUS_DL_CHROME": "Vincular pestaña de YouTube",
"STEP_1_ILLUS_DL_FIREFOX": "Vincular pestaña de Netflix",
"STEP_1_ILLUS_ACTIVE": "Pestaña vinculada",
"STEP_1_ILLUS_READY": "Listo para ver en sincronía!",
"STEP_2_TITLE": "Seleccionar pestaña de vídeo",
"STEP_2_DESC": "Ve a la pestaña de tu vídeo y selecciónala en el menú de la extensión. KoalaSync vinculará vuestros reproductores automáticamente.",
"STEP_2_ILLUS_ROOM": "Sala",
"STEP_2_ILLUS_SYNC": "Sincro",
"STEP_2_ILLUS_SETTINGS": "Ajustes",
"STEP_2_ILLUS_CREATE": "+ Crear nueva sala",
"STEP_2_ILLUS_MANUAL": "Conexión manual / Avanzado",
"STEP_2_ILLUS_COPIED": "¡Enlace de invitación copiado!",
"STEP_3_TITLE": "Compartir y Sincronizar",
"STEP_3_DESC": "Envía el enlace de invitación a tus amigos. Una vez que se unan, selecciona tu pestaña de video y disfrutad de la reproducción sincronizada.",
"STEP_3_TITLE": "Disfrutar de la sincronización",
"STEP_3_DESC": "Pulsa reproducir. Cuando alguien pause, avance o reproduzca, el vídeo de todos se mantendrá en sincronía en tiempo real y sin retrasos.",
"STEP_3_ILLUS_IN_SYNC": "SINCRONIZADO",
"SELF_TITLE": "Para auto-alojadores",
"SELF_SUBTITLE": "¿No te fías de nuestro servidor? Mantén la soberanía total de tus datos. Despliega tu propio servidor de retransmisión en minutos.",
@@ -117,6 +127,8 @@
"FAQ_SUBTITLE": "Todo lo que necesitas saber para ver vídeos en perfecta sincronía.",
"FAQ_Q1": "¿Puedo ver Netflix, Emby o Jellyfin sincronizado con amigos?",
"FAQ_A1": "¡Sí! KoalaSync funciona en Netflix, YouTube, Twitch y cualquier web con reproductor HTML5, incluyendo Emby, Jellyfin y servidores multimedia propios. Instala la extensión, crea una sala, comparte el enlace y todos veréis lo mismo al mismo tiempo.",
"FAQ_Q_VIDEO_ACCESS": "¿KoalaSync transmite el video a mis amigos?",
"FAQ_A_VIDEO_ACCESS": "No. KoalaSync no transmite, aloja, comparte ni evita el acceso al contenido de video. Cada persona ve el video localmente en su propio navegador y necesita su propio acceso al sitio web o servicio de streaming, por ejemplo su propia cuenta de Netflix. KoalaSync solo sincroniza acciones de reproducción como reproducir, pausar y avanzar o retroceder.",
"FAQ_Q2": "¿Es KoalaSync una alternativa gratuita a Teleparty?",
"FAQ_A2": "Es 100% gratuito y de código abierto (Licencia MIT). Sin suscripciones, sin funciones bloqueadas y sin anuncios. A diferencia de Teleparty, el servidor de KoalaSync solo usa RAM: no hay bases de datos ni cuentas de usuario.",
"FAQ_Q3": "¿Funciona KoalaSync con Emby, Jellyfin y servidores propios?",
@@ -156,7 +168,7 @@
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SINCRO",
"MOCK_14": "Ir a la posición de otros",
"MOCK_15": "Estado de la última actividad",
"MOCK_16": "Esperando a 1 participante (KoalaPC)...",
"MOCK_16": "Esperando a 1 participante (🐶 HappyDog)...",
"MOCK_17": "Omitir y reproducir ya",
"MOCK_18": "Tu nombre de usuario",
"MOCK_19": "Ocultar pestañas sin video",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSync no está afiliado, respaldado ni asociado con Netflix, Disney+, Amazon, YouTube, Twitch ni ninguna otra plataforma de streaming. Todas las marcas comerciales pertenecen a sus respectivos dueños.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - Alternativas y comparativas",
"ALT_INDEX_META_DESC": "Explora comparativas honestas y guías detalladas entre KoalaSync y otras extensiones de watch party y soluciones de streaming.",
"ALT_INDEX_HEADING": "Guías y comparativas",
"ALT_INDEX_INTRO": "¿Cómo se compara KoalaSync con otras herramientas de watch party y soluciones de streaming? Lee nuestras comparativas honestas y técnicas para encontrar la mejor opción para tus noches de cine a distancia.",
"ALT_INDEX_READ_COMPARISON": "Leer comparativa →",
"ALT_INDEX_READ_GUIDE": "Leer guía →",
"ALT_INDEX_FOOTER_LINK": "Guías y comparativas",
"ALT_TELEPARTY_TITLE": "KoalaSync frente a Teleparty: ¿qué herramienta de visionado conjunto deberías usar?",
"ALT_TELEPARTY_META_DESC": "Una comparativa práctica entre KoalaSync y Teleparty para ver contenido en grupo, analizando aspectos como el chat integrado, la compatibilidad móvil, la sincronización de vídeo en el navegador, los servidores multimedia autohospedados y el uso de código abierto.",
"ALT_TELEPARTY_INTRO": "Tanto KoalaSync como Teleparty ayudan a resolver el mismo problema molesto: ver algo juntos sin tener que recurrir a la típica rutina de «3, 2, 1, ¡reproducir!». Simplemente adoptan enfoques diferentes.<br><br>Teleparty es la opción más lógica si buscas un chat integrado o si quieres ver contenido desde un dispositivo móvil. KoalaSync está más orientado a sesiones de visionado en navegadores de escritorio, donde ya te comunicas a través de Discord, Teamspeak u otra aplicación de voz y solo necesitas que la reproducción se mantenga sincronizada.<br><br>La razón principal por la que creé KoalaSync fue la flexibilidad. Está diseñado para funcionar con casi cualquier sitio web que utilice un reproductor de vídeo estándar de navegador, no solo con una lista fija de plataformas. Esto incluye configuraciones autohospedadas como Emby, Jellyfin y Plex, dominios locales o privados y muchas páginas de vídeo convencionales.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Cuándo puede seguir siendo útil Teleparty",
"ALT_TELEPARTY_WHEN_TELE_BODY": "Teleparty puede seguir siendo la mejor opción si prefieres que el chat forme parte de la propia herramienta de visionado. Actualmente, KoalaSync no cuenta con chat integrado; parte de la premisa de que ya te estás comunicando por otro medio, como Discord, Teamspeak, WhatsApp, Signal o una llamada convencional.<br><br>La compatibilidad móvil es otro factor clave. Teleparty dispone de aplicación propia, mientras que KoalaSync, por ahora, solo funciona en navegadores de escritorio. Si quieres sincronizar Netflix en un teléfono o tableta, KoalaSync no es la herramienta adecuada en este momento.<br><br>Por tanto, si el chat y la compatibilidad móvil son tus prioridades, es posible que Teleparty se adapte mejor a tus necesidades.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "Cuándo es mejor usar KoalaSync",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "KoalaSync es una mejor opción si buscas una herramienta flexible para sincronizar la reproducción de vídeo en navegadores de escritorio, en lugar de algo limitado a una lista reducida de plataformas.<br><br>Funciona con casi cualquier sitio web que utilice el reproductor de vídeo estándar del navegador. Esto resulta especialmente útil para Emby, Jellyfin, Plex, servidores multimedia privados, dominios locales o sitios de vídeo más pequeños que las herramientas convencionales de visionado compartido suelen ignorar.<br><br>Además, es gratuito, de código abierto, no requiere crear cuentas y permite el autohospedaje si deseas gestionar tu propio servidor de retransmisión (relay). Lo creé porque buscaba una solución respetuosa con la privacidad que funcionara con mi propia configuración de Emby, y no otra plataforma cerrada limitada a unos pocos grandes servicios de streaming.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "La diferencia principal",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "Teleparty está concebido principalmente como una herramienta para ver contenido en grupo, con funciones de chat y compatibilidad móvil. KoalaSync, en cambio, funciona más bien como una capa flexible de sincronización de vídeo para navegadores.<br><br>Puede parecer una diferencia menor, pero cambia considerablemente el caso de uso. Si necesitas una aplicación completa para ver contenido en grupo que incluya chat integrado, Teleparty es la opción lógica. Si ya dispones de chat de voz y solo quieres mantener sincronizados los controles de vídeo entre distintos navegadores, KoalaSync es la solución más sencilla.",
"ALT_TELEPARTY_LIMITS_TITLE": "Limitaciones reales",
"ALT_TELEPARTY_LIMITS_BODY": "KoalaSync no hace milagros ni pretende ocultar sus limitaciones. Actualmente, solo funciona en navegadores de escritorio; no cuenta con chat integrado ni aplicación móvil.<br><br>La compatibilidad también depende del navegador y del reproductor de vídeo utilizado. Aunque KoalaSync debería funcionar con casi cualquier reproductor estándar de navegador, los sitios web pueden modificar sus reproductores, el comportamiento del DRM o las restricciones del navegador en cualquier momento.<br><br>KoalaSync no tiene ninguna relación con Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex ni ninguna otra marca mencionada aquí. Dichos nombres se utilizan únicamente para ilustrar casos de uso habituales.",
"ALT_TELEPARTY_SUMMARY_TITLE": "Resumen",
"ALT_TELEPARTY_SUMMARY_BODY": "Si buscas chat integrado o compatibilidad con dispositivos móviles, Teleparty podría ser la mejor opción. Si prefieres una solución gratuita y de código abierto para sincronizar navegadores de escritorio que funcione con casi cualquier reproductor de vídeo convencional, incluidos servidores multimedia de alojamiento propio como Emby, Jellyfin y Plex, KoalaSync es probablemente la alternativa más adecuada.",
"ALT_SCREEN_TITLE": "KoalaSync frente a compartir pantalla: ¿cuándo es mejor la sincronización de vídeo?",
"ALT_SCREEN_META_DESC": "Una comparativa práctica entre KoalaSync y la función de compartir pantalla para noches de cine a distancia, analizando aspectos como el control de reproducción, la calidad de vídeo, la privacidad, los subtítulos y la visualización desde el navegador.",
"ALT_SCREEN_INTRO": "Compartir pantalla funciona y, sinceramente, a veces es suficiente. Una persona comparte su pantalla, las demás la ven y listo.<br><br>Sin embargo, para sesiones de cine más largas o para ver episodios en grupo, puede resultar molesto. La calidad del vídeo depende de la velocidad de subida, la CPU y el navegador de quien comparte, así como de la compresión que apliquen aplicaciones como Discord, Teamspeak, Zoom u otras plataformas de videollamada. Además, solo una persona tiene el control real de la reproducción.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "Cuándo basta con compartir pantalla",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "Compartir pantalla es una buena opción si buscas una configuración lo más rápida posible. Si solo una persona tiene acceso al vídeo o si a todos les parece bien que una sola persona controle la reproducción, la pausa y el avance o retroceso, puede que sea la opción más sencilla.<br><br>También tiene sentido cuando la calidad no es una prioridad, cuando nadie necesita sus propios subtítulos o ajustes de audio, y cuando la persona que comparte se siente cómoda mostrando esa ventana del navegador o su pantalla.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "Cuando compartir pantalla resulta molesto",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "La mayor desventaja es que no todos ven el contenido en su propio reproductor. Una persona transmite su pantalla y el resto solo ve esa transmisión.<br><br>Esto puede implicar una peor calidad de video, audio comprimido, retraso en la reproducción y menos control para los demás. Además, los subtítulos y las pistas de audio serán los que haya elegido la persona que comparte.<br><br>También está el aspecto de la privacidad. Si cambias de ventana o de pestaña, los demás podrían ver brevemente las pestañas de tu navegador, tu correo electrónico, Reddit, chats u otra información privada. Puedes evitar esto usando una ventana independiente, pero es algo que debes tener en cuenta.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "Por qué KoalaSync puede resultar más limpio",
"ALT_SCREEN_WHY_CLEANER_BODY": "KoalaSync no transmite tu pantalla. Cada persona ve el video localmente en su propio navegador, y KoalaSync solo sincroniza las acciones de reproducción, como iniciar, pausar y avanzar o retroceder.<br><br>Esto significa que todos pueden usar la pantalla completa en su propio dispositivo, utilizar sus propios ajustes de subtítulos o audio (si el reproductor lo permite) y mantener el chat de voz separado en Discord, Teamspeak o cualquier otra aplicación de llamadas.<br><br>Para las noches de cine, esto puede resultar mucho más limpio que tener a una persona transmitiendo su pantalla. Es especialmente útil para configuraciones basadas en navegador como Emby, Jellyfin, Plex, archivos locales abiertos en el navegador, dominios privados u otros sitios web que utilicen reproductores de video estándar del navegador.",
"ALT_SCREEN_LIMITS_TITLE": "Limitaciones reales",
"ALT_SCREEN_LIMITS_BODY": "KoalaSync no sustituye a una llamada de voz. Si quieres hablar mientras ves el contenido, puedes seguir usando Discord, Teamspeak, Signal, WhatsApp o cualquier otra herramienta que utilices habitualmente.<br><br>Además, cada participante necesita acceder al vídeo o a la página web desde su propio dispositivo. KoalaSync no elude suscripciones, inicios de sesión, DRM, restricciones regionales ni las normas de las plataformas.<br><br>Actualmente solo funciona en navegadores de escritorio, no incluye funciones de chat o voz, y la compatibilidad depende siempre del navegador y del reproductor de vídeo.",
"ALT_SCREEN_SUMMARY_TITLE": "Resumen",
"ALT_SCREEN_SUMMARY_BODY": "Compartir pantalla es la opción más sencilla cuando buscas algo rápido y no te preocupa demasiado la calidad, la privacidad o el control individual. KoalaSync es una mejor alternativa cuando cada persona debe ver el contenido localmente en su propio navegador y se busca una experiencia que realmente se asemeje a verlo juntos.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "Prueba KoalaSync",
"MOCK_HOST_CONTROL_LABEL": "Control del anfitrión",
"MOCK_HOST_CONTROL_TOGGLE": "Solo yo puedo controlar la reproducción",
"LABEL_SELECT_VIDEO": "Seleccionar video",
"OPTION_SELECT_TAB": "-- Selecciona una pestaña --",
"THEME_TOGGLE_LABEL": "Cambiar modo claro/oscuro",
"FAQ_SHOW_ALL": "Mostrar todas las preguntas",
"FAQ_HIDE_ALL": "Ocultar preguntas",
"SELF_SHOW_SETUP": "Mostrar guía de instalación",
"SELF_HIDE_SETUP": "Ocultar guía de instalación",
"HERO_TRUST": "Gratis · Código abierto · Sin cuenta",
"COMP_MORE_GUIDES": "Comparativa completa y más alternativas"
}
+101 -31
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "fr_FR",
"META_TITLE": "KoalaSync | Synchronisez Netflix, YouTube et n'importe quelle vidéo avec vos amis",
"META_DESCRIPTION": "Regardez Netflix, YouTube, Twitch et des vidéos HTML5 en synchro avec vos amis. Extension de navigateur gratuite et open-source. Aucune inscription requise.",
"SCHEMA_APP_DESC": "Regardez Netflix, YouTube, Twitch et des vidéos HTML5 en synchro avec vos amis. Extension de navigateur gratuite et open-source. Aucune inscription requise.",
"OG_TITLE": "KoalaSync | Synchronisez Netflix, Emby, Jellyfin et presque toutes les vidéos avec vos amis",
"OG_DESCRIPTION": "Regardez Netflix, Emby, Jellyfin, YouTube, Twitch et presque n'importe quelle vidéo HTML5 en parfaite synchronisation. Extension de navigateur open-source et respectueuse de la vie privée pour Chrome et Firefox.",
"TWITTER_TITLE": "KoalaSync | Synchronisez Netflix, Emby, Jellyfin et presque toutes les vidéos avec vos amis Extension de navigateur",
"TWITTER_DESCRIPTION": "Regardez Netflix, Emby, Jellyfin, YouTube, Twitch et presque n'importe quelle vidéo HTML5 en parfaite synchronisation avec vos amis. Extension de navigateur open-source et respectueuse de la vie privée pour Chrome et Firefox.",
"META_TITLE": "KoalaSync | Watch party pour Netflix, YouTube et plus",
"META_DESCRIPTION": "Organisez une watch party sur Netflix, YouTube, Twitch et toute vidéo HTML5. Extension libre et gratuite pour Chrome et Firefox. Sans compte, sans pistage, sans pub.",
"SCHEMA_APP_DESC": "Organisez une watch party sur Netflix, YouTube, Twitch et toute vidéo HTML5. Extension libre et gratuite pour Chrome et Firefox. Sans compte, sans pistage, sans pub.",
"OG_TITLE": "KoalaSync | Watch party pour Netflix, Emby, Jellyfin et presque toute vidéo",
"OG_DESCRIPTION": "Organisez une watch party sur Netflix, Emby, Jellyfin, YouTube, Twitch et presque toute vidéo HTML5. Open source, privé et sans compte.",
"TWITTER_TITLE": "KoalaSync | Watch party pour Netflix, Emby, Jellyfin et presque toute vidéo Extension",
"TWITTER_DESCRIPTION": "Organisez une watch party sur Netflix, Emby, Jellyfin, YouTube, Twitch et presque toute vidéo HTML5. Open source, privé et sans compte.",
"NAV_FEATURES": "Fonctionnalités",
"NAV_HOW_IT_WORKS": "Comment ça marche",
"HERO_TITLE": "Regardez ensemble.<span class='hero-line2'>Synchronisation parfaite.</span>",
"HERO_SUBTITLE": "Votre soirée cinéma à distance sans décalage. Pas d'inscription, pas de collecte de données. Partagez simplement un lien et regardez ensemble.",
"HERO_TITLE": "Transformez n'importe quelle vidéo en <span class='hero-highlight'>watch party</span>.",
"HERO_SUBTITLE": "Votre soirée ciné à distance, sans décalage. Pas de compte, pas de tracking, pas de pub. Partagez un lien et lancez la lecture ensemble.",
"HERO_MASCOT_ALT": "Un koala mignon debout et regardant vers le bas les boutons de téléchargement",
"DEMO_SYNC": "Synchronisé",
"DEMO_HINT": "Démo interactive : lancez la lecture dans un onglet, KoalaSync garde les deux synchronisés.",
"DEMO_NO_ROOM": "Pas encore connecté",
"DEMO_CREATE_ROOM": "Créer un salon",
"DEMO_JOINED": "Salon rejoint",
"DEMO_TAB_SELECTED": "Onglet sélectionné",
"DEMO_SELECT_PLACEHOLDER": "Choisir un onglet vidéo…",
"MOBILE_DEMO_CTA": "Voir comment ça marche",
"MOBILE_DEMO_TITLE": "Démo live KoalaSync",
"MOBILE_DEMO_CLOSE": "Fermer la démo",
"ADD_TO_CHROME": "Ajouter à Chrome",
"ADD_TO_FIREFOX": "Ajouter à Firefox",
"COMPAT_HEADING": "Fonctionne sur vos plateformes préférées",
@@ -35,20 +45,20 @@
"USE_CASE_3_DESC": "Comblez la distance et profitez de soirées en tête-à-tête. Vivez chaque rebondissement, riez des mêmes blagues et partagez des moments émotionnels à la milliseconde près.",
"WHY_TITLE": "Pourquoi KoalaSync ?",
"WHY_SUBTITLE": "Conçu pour une synchronisation fiable, le respect de la vie privée et une configuration facile.",
"FEATURE_1_TITLE": "Contrôle total / Synchro instantanée",
"FEATURE_1_DESC": "Un joueur met en pause, tout le monde met en pause. Un joueur avance, tout le monde suit. Notre protocole de synchronisation en deux phases coordonne la lecture en temps réel pour tous les participants.",
"FEATURE_1_TITLE": "Contrôle de l'hôte et synchronisation",
"FEATURE_1_DESC": "Vous décidez qui contrôle la lecture. Activez le contrôle de l'hôte pour être le seul à pouvoir mettre en pause et avancer, ou nommez des co-hôtes.",
"FEATURE_2_TITLE": "Binge-watching sans fin",
"FEATURE_2_DESC": "Lecture automatique synchronisée. KoalaSync détecte automatiquement les transitions d'épisodes et suspend la lecture jusqu'à ce que tous les participants aient chargé la vidéo suivante.",
"FEATURE_3_TITLE": "Aucun compte / Vie privée garantie",
"FEATURE_3_DESC": "Pas d'inscription, pas de suivi, pas de stockage de données. Le serveur fonctionne entièrement dans une RAM éphémère et supprime votre salon dès votre départ.",
"FEATURE_4_TITLE": "Support HTML5 universel",
"FEATURE_4_DESC": "Fonctionne sur YouTube, Twitch, Netflix, Jellyfin, Emby et presque n'importe quelle page web standard contenant un élément vidéo HTML5. Également compatible avec les installations personnalisées. Aucune donnée vidéo n'est envoyée à nos serveurs — seules les métadonnées de synchronisation.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby et presque toute page avec une vidéo HTML5. Seuls les signaux de sync quittent le navigateur, jamais la vidéo.",
"FEATURE_5_TITLE": "Hébergeable soi-même & prêt pour Docker",
"FEATURE_5_DESC": "Nos serveurs officiels sont gratuits et rapides, mais vous pouvez prendre le contrôle total si vous le souhaitez. Lancez votre propre serveur relais privé en quelques secondes via Docker.",
"FEATURE_6_TITLE": "Invitations instantanées / Rejoindre en 1 clic",
"FEATURE_6_DESC": "Pas d'adresses IP ou de mots de passe à échanger. Partagez un lien d'invitation généré pour permettre à vos amis de rejoindre automatiquement votre salon en un seul clic.",
"FEATURE_7_TITLE": "Égalisation du volume / Compresseur audio",
"FEATURE_7_DESC": "Fini de jouer avec le bouton du volume. Le compresseur audio intégré booste les dialogues discrets et adoucit les explosions. Fonctionne sur Netflix, Prime Video, Disney+, YouTube et toute vidéo HTML5. Entièrement local — aucune donnée audio ne quitte votre ordinateur.",
"FEATURE_7_DESC": "Fini de jouer avec le bouton du volume. Un compresseur intégré rehausse les dialogues discrets et adoucit les explosions, le tout traité localement.",
"FEATURE_8_TITLE": "Open Source (MIT License)",
"FEATURE_8_DESC": "Tout le code source est publiquement auditable sur GitHub. Pas de boite noire, pas de blob proprietaire - transparence totale du serveur relais a l'extension de navigateur.",
"COMP_TITLE": "KoalaSync vs Teleparty",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Politiques de confidentialité et collecte de données de suivi de Teleparty :",
"COMP_FOOTNOTE_4": "Fonctionne sur les sites web qui autorisent les injections de scripts dans les balises vidéo HTML5 standard. Les sites avec des politiques de sécurité du contenu (CSP) très strictes, une protection contre la copie DRM ou des conteneurs de lecteurs fortement obscurcis (comme des DOM fantômes complexes) peuvent restreindre le contrôle automatisé ou l'injection.",
"STEPS_TITLE": "Pour commencer",
"STEP_1_TITLE": "Installer l'extension",
"STEP_1_DESC": "Ajoutez KoalaSync à votre navigateur depuis le Chrome Web Store, les modules complémentaires de Firefox, ou téléchargez le dernier fichier ZIP développeur depuis GitHub.",
"STEP_1_ILLUS_DESC": "Synchronisateur vidéo respectueux de la vie privée",
"STEP_1_ILLUS_DL_CHROME": "Télécharger pour Chrome",
"STEP_1_ILLUS_DL_FIREFOX": "Télécharger pour Firefox",
"STEP_1_ILLUS_ACTIVE": "Extension active",
"STEP_1_ILLUS_READY": "Prêt à regarder ensemble !",
"STEP_2_TITLE": "Créer un salon",
"STEP_2_DESC": "Ouvrez la fenêtre de l'extension et cliquez sur '+ Créer un nouveau salon'. KoalaSync génère automatiquement un identifiant de salon et un mot de passe sécurisés, s'y connecte et copie le lien d'invitation dans votre presse-papiers.",
"STEP_1_TITLE": "Créer et partager un salon",
"STEP_1_DESC": "Ouvrez l'extension, créez un salon en un clic et partagez le lien avec vos amis. Ils rejoindront instantanément via le lien d'invitation.",
"STEP_1_ILLUS_DESC": "Choisir un onglet à lier",
"STEP_1_ILLUS_DL_CHROME": "Lier l'onglet YouTube",
"STEP_1_ILLUS_DL_FIREFOX": "Lier l'onglet Netflix",
"STEP_1_ILLUS_ACTIVE": "Onglet lié",
"STEP_1_ILLUS_READY": "Prêt à regarder en synchronisation !",
"STEP_2_TITLE": "Sélectionner l'onglet vidéo",
"STEP_2_DESC": "Allez sur l'onglet de votre vidéo et sélectionnez-le dans le menu de l'extension. KoalaSync liera vos lecteurs automatiquement.",
"STEP_2_ILLUS_ROOM": "Salon",
"STEP_2_ILLUS_SYNC": "Synchro",
"STEP_2_ILLUS_SETTINGS": "Options",
"STEP_2_ILLUS_CREATE": "+ Créer un nouveau salon",
"STEP_2_ILLUS_MANUAL": "Connexion manuelle / Avancé",
"STEP_2_ILLUS_COPIED": "Lien d'invitation copié !",
"STEP_3_TITLE": "Partager & Synchroniser",
"STEP_3_DESC": "Envoyez le lien d'invitation à vos amis. Dès qu'ils rejoignent, sélectionnez votre onglet vidéo et profitez de la lecture synchronisée.",
"STEP_3_TITLE": "Profiter de la synchronisation",
"STEP_3_DESC": "Lancez la lecture. Si quelqu'un met en pause, avance ou joue, la vidéo de tout le monde reste synchronisée en temps réel sans décalage.",
"STEP_3_ILLUS_IN_SYNC": "SYNCHRONISÉ",
"SELF_TITLE": "Pour l'auto-hébergement",
"SELF_SUBTITLE": "Vous ne faites pas confiance à notre serveur ? Conservez votre souveraineté totale sur les données. Déployez votre propre serveur relais en quelques minutes.",
@@ -116,19 +126,21 @@
"FAQ_TITLE": "Foire aux questions",
"FAQ_SUBTITLE": "Tout ce que vous devez savoir pour regarder des vidéos en parfaite synchronisation.",
"FAQ_Q1": "Puis-je regarder Netflix, Emby ou Jellyfin en synchronisation avec des amis ?",
"FAQ_A1": "Oui ! KoalaSync fonctionne avec Netflix, YouTube, Twitch et tout site avec un lecteur vidéo HTML5 y compris Emby, Jellyfin et les serveurs multimédia auto-hébergés. Installez l'extension, créez un salon, partagez le lien et regardez en parfaite synchronisation.",
"FAQ_A1": "Oui ! KoalaSync fonctionne avec Netflix, YouTube, Twitch et tout site avec un lecteur vidéo HTML5, y compris Emby, Jellyfin et les serveurs multimédia auto-hébergés. Installez l'extension, créez un salon, partagez le lien et regardez en parfaite synchronisation.",
"FAQ_Q_VIDEO_ACCESS": "KoalaSync diffuse-t-il la vidéo à mes amis ?",
"FAQ_A_VIDEO_ACCESS": "Non. KoalaSync ne diffuse, nhéberge, ne partage et ne contourne aucun accès aux contenus vidéo. Chaque personne regarde la vidéo localement dans son propre navigateur et doit disposer de son propre accès au site ou au service de streaming, par exemple son propre compte Netflix. KoalaSync synchronise uniquement les actions de lecture comme lecture, pause et recherche dans la vidéo.",
"FAQ_Q2": "KoalaSync est-il une alternative gratuite à Teleparty ?",
"FAQ_A2": "100 % gratuit et open source sous licence MIT. Aucun niveau premium, aucune fonctionnalité verrouillée, aucune publicité. Contrairement à Teleparty, le serveur relay de KoalaSync fonctionne uniquement en RAM sans collecte de données ni comptes utilisateur.",
"FAQ_A2": "100 % gratuit et open source sous licence MIT. Aucun niveau premium, aucune fonctionnalité verrouillée, aucune publicité. Contrairement à Teleparty, le serveur relay de KoalaSync fonctionne uniquement en RAM, sans collecte de données ni comptes utilisateur.",
"FAQ_Q3": "KoalaSync fonctionne-t-il avec Emby, Jellyfin et les serveurs multimédia auto-hébergés ?",
"FAQ_A3": "Oui. KoalaSync détecte tout élément vidéo HTML5 standard et fonctionne parfaitement avec Emby, Jellyfin, Plex et toute configuration de streaming personnalisée. Vous pouvez également auto-héberger le serveur relay via Docker.",
"FAQ_Q4": "Quels navigateurs et plateformes prennent en charge KoalaSync ?",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave et tous les navigateurs basés sur Chromium. Installez-le depuis le Chrome Web Store ou les modules complémentaires Firefox.",
"FAQ_Q5": "Tous mes amis doivent-ils installer l'extension ?",
"FAQ_A5": "Oui, chaque participant a besoin de KoalaSync mais sans inscription ni création de compte. Cliquez simplement sur le lien d'invitation, installez l'extension si nécessaire et vous rejoignez automatiquement le salon.",
"FAQ_A5": "Oui, chaque participant a besoin de KoalaSync, mais sans inscription ni création de compte. Cliquez simplement sur le lien d'invitation, installez l'extension si nécessaire et vous rejoignez automatiquement le salon.",
"FAQ_Q6": "Mes données sont-elles en sécurité ? Et si je ne fais pas confiance au serveur officiel ?",
"FAQ_A6": "Totalement sûr. Le serveur relay de KoalaSync fonctionne uniquement en RAM toutes les données de session sont supprimées dès que vous quittez le salon. Aucune base de données, aucun journal, aucune analyse, aucun compte. Si vous préférez un contrôle total : hébergez votre propre serveur relay privé via Docker en quelques minutes. Le code source est entièrement open source et vérifiable sur GitHub.",
"FAQ_A6": "Totalement sûr. Le serveur relay de KoalaSync fonctionne uniquement en RAM, toutes les données de session sont supprimées dès que vous quittez le salon. Aucune base de données, aucun journal, aucune analyse, aucun compte. Si vous préférez un contrôle total : hébergez votre propre serveur relay privé via Docker en quelques minutes. Le code source est entièrement open source et vérifiable sur GitHub.",
"FAQ_Q7": "Puis-je atténuer les dialogues discrets et les explosions fortes en regardant des vidéos dans le navigateur ?",
"FAQ_A7": "Oui. KoalaSync 2.2.0 inclut un compresseur audio intégré qui réduit l'écart entre les parties silencieuses et bruyantes de votre vidéo. Il fonctionne localement via l'API Web Audio aucune donnée audio ne quitte votre ordinateur. Ouvrez le popup, créez un salon, allez dans Paramètres → Audio Processing, activez le compresseur et choisissez un préréglage. Fonctionne sur Netflix, Prime Video, Disney+, YouTube et tout site vidéo HTML5.",
"FAQ_A7": "Oui. KoalaSync 2.2.0 inclut un compresseur audio intégré qui réduit l'écart entre les parties silencieuses et bruyantes de votre vidéo. Il fonctionne localement via l'API Web Audio, aucune donnée audio ne quitte votre ordinateur. Ouvrez le popup, créez un salon, allez dans Paramètres → Audio Processing, activez le compresseur et choisissez un préréglage. Fonctionne sur Netflix, Prime Video, Disney+, YouTube et tout site vidéo HTML5.",
"HOWTO_TITLE": "Comment synchroniser des vidéos avec des amis avec KoalaSync",
"HOWTO_DESC": "Regardez des vidéos en parfaite synchronisation avec vos amis sur YouTube, Netflix, Twitch et plus. Sans inscription, sans pistage.",
"HOWTO_STEP_1_NAME": "Installer l'extension",
@@ -155,7 +167,7 @@
"MOCK_12": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"14\" height=\"14\" aria-hidden=\"true\"><rect x=\"6\" y=\"4\" width=\"4\" height=\"16\"/><rect x=\"14\" y=\"4\" width=\"4\" height=\"16\"/></svg> Pause",
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SYNC",
"MOCK_14": "Dernière activité",
"MOCK_15": "En attente de 1 participant (KoalaPC)...",
"MOCK_15": "En attente de 1 participant (🐶 HappyDog)...",
"MOCK_16": "Passer & lire",
"MOCK_17": "Votre pseudo",
"MOCK_18": "Masquer les onglets inutiles",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSync n'est pas affilié à Netflix, Disney+, Amazon, YouTube, Twitch ou toute autre plateforme de streaming. Toutes les marques déposées appartiennent à leurs détenteurs respectifs.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - Alternatives et comparatifs",
"ALT_INDEX_META_DESC": "Explorez des comparatifs honnêtes et des guides détaillés entre KoalaSync et dautres extensions de watch party et solutions de streaming.",
"ALT_INDEX_HEADING": "Guides et comparatifs",
"ALT_INDEX_INTRO": "Comment KoalaSync se compare-t-il aux autres outils de watch party et solutions de streaming ? Lisez nos comparatifs honnêtes et techniques pour trouver la meilleure option pour vos soirées cinéma à distance.",
"ALT_INDEX_READ_COMPARISON": "Lire le comparatif →",
"ALT_INDEX_READ_GUIDE": "Lire le guide →",
"ALT_INDEX_FOOTER_LINK": "Guides et comparatifs",
"ALT_TELEPARTY_TITLE": "KoalaSync vs Teleparty : quel outil de visionnage en groupe choisir ?",
"ALT_TELEPARTY_META_DESC": "Une comparaison pratique entre KoalaSync et Teleparty pour le visionnage en groupe : chat intégré, compatibilité mobile, synchronisation vidéo via navigateur, serveurs multimédias auto-hébergés et nature open-source.",
"ALT_TELEPARTY_INTRO": "KoalaSync et Teleparty répondent tous deux au même problème agaçant : regarder quelque chose ensemble sans avoir à passer par le rituel du « 3, 2, 1, lecture ». Ils adoptent simplement des approches différentes.<br><br>Teleparty est la solution idéale si vous souhaitez un chat intégré ou si vous voulez regarder du contenu sur mobile. KoalaSync est davantage conçu pour le visionnage sur navigateur (PC/Mac) lorsque vous communiquez déjà via Discord, Teamspeak ou un autre service d'appel vocal, et que vous voulez simplement que la lecture reste synchronisée.<br><br>La raison principale pour laquelle j'ai créé KoalaSync est la flexibilité. L'outil est conçu pour fonctionner avec presque tous les sites web utilisant un lecteur vidéo standard, et non avec une liste fixe de plateformes. Cela inclut les installations auto-hébergées comme Emby, Jellyfin et Plex, les domaines locaux ou privés, ainsi que de nombreuses pages vidéo classiques sur navigateur.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Quand Teleparty reste une bonne option",
"ALT_TELEPARTY_WHEN_TELE_BODY": "Teleparty peut s'avérer être le meilleur choix si vous souhaitez que le chat soit directement intégré à l'outil de visionnage. KoalaSync ne propose pas de chat intégré pour le moment ; il part du principe que vous communiquez déjà par ailleurs, via Discord, Teamspeak, WhatsApp, Signal ou un appel classique.<br><br>L'aspect mobile constitue une autre raison majeure. Teleparty dispose d'une application, alors que KoalaSync ne fonctionne actuellement que sur les navigateurs de bureau. Si vous souhaitez synchroniser Netflix sur un téléphone ou une tablette, KoalaSync n'est pas l'outil adapté pour l'instant.<br><br>Ainsi, si le chat et la compatibilité mobile sont vos priorités, Teleparty pourrait tout simplement mieux convenir à votre configuration.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "Quand choisir KoalaSync",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "KoalaSync est plus adapté si vous recherchez un outil flexible de synchronisation vidéo pour navigateur de bureau, plutôt qu'une solution limitée à une courte liste de plateformes.<br><br>Il fonctionne avec presque tous les sites web utilisant un lecteur vidéo standard de navigateur. C'est particulièrement utile pour Emby, Jellyfin, Plex, les serveurs multimédias privés, les domaines locaux ou les sites vidéo plus modestes que les outils classiques de visionnage en groupe ignorent généralement.<br><br>De plus, c'est un outil gratuit et open source qui ne nécessite pas de compte et peut être auto-hébergé si vous souhaitez gérer votre propre relais. Je l'ai créé parce que je voulais une solution respectueuse de la vie privée compatible avec ma propre installation Emby, et non une plateforme fermée supplémentaire réservée à quelques grands sites de streaming.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "La différence principale",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "Teleparty est avant tout un outil de visionnage en groupe incluant une messagerie et une prise en charge mobile. KoalaSync se présente davantage comme une couche de synchronisation flexible pour la vidéo sur navigateur.<br><br>Cela peut sembler être un détail, mais cela change considérablement l'usage. Si vous avez besoin d'une application complète de visionnage en groupe avec messagerie intégrée, Teleparty est tout indiqué. Si vous disposez déjà d'un système de discussion vocale et que vous souhaitez simplement synchroniser les commandes vidéo entre différents navigateurs, KoalaSync est la solution la plus simple.",
"ALT_TELEPARTY_LIMITS_TITLE": "Limites en toute transparence",
"ALT_TELEPARTY_LIMITS_BODY": "KoalaSync n'est pas magique et ne cherche pas à dissimuler ses limites. Actuellement, il ne fonctionne que sur les navigateurs de bureau. Il ne dispose ni de messagerie intégrée ni d'application mobile.<br><br>La compatibilité dépend également du navigateur et du lecteur vidéo. KoalaSync devrait fonctionner avec presque tous les lecteurs vidéo standard, mais les sites web peuvent modifier leurs lecteurs, la gestion des DRM ou les restrictions du navigateur à tout moment.<br><br>KoalaSync n'est affilié ni à Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex, ni à aucune autre marque mentionnée ici. Ces noms sont utilisés uniquement pour illustrer des cas d'utilisation courants.",
"ALT_TELEPARTY_SUMMARY_TITLE": "Résumé",
"ALT_TELEPARTY_SUMMARY_BODY": "Si vous recherchez une fonction de chat intégrée ou une prise en charge mobile, Teleparty pourrait être la meilleure option. Si vous préférez une solution gratuite et open source de synchronisation via navigateur (sur ordinateur) compatible avec presque tous les lecteurs vidéo classiques, y compris les serveurs multimédias auto-hébergés comme Emby, Jellyfin et Plex, alors KoalaSync est probablement le choix idéal.",
"ALT_SCREEN_TITLE": "KoalaSync vs partage d'écran : quand la synchronisation vidéo est-elle préférable ?",
"ALT_SCREEN_META_DESC": "Une comparaison pratique entre KoalaSync et le partage d'écran pour les soirées cinéma à distance : contrôle de la lecture, qualité vidéo, confidentialité, sous-titres et visionnage via navigateur.",
"ALT_SCREEN_INTRO": "Le partage d'écran fonctionne et, honnêtement, cela suffit parfois. Une personne partage son écran, les autres regardent, et le tour est joué.<br><br>Cependant, pour des soirées cinéma plus longues ou pour regarder des épisodes ensemble, cela peut devenir contraignant. La qualité vidéo dépend de la vitesse d'envoi (upload), du processeur et du navigateur de la personne qui partage, ainsi que de la compression appliquée par Discord, Teamspeak, Zoom ou toute autre application d'appel. De plus, une seule personne contrôle réellement la lecture.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "Quand le partage d'écran suffit",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "Le partage d'écran est une bonne solution lorsque vous souhaitez une mise en place ultra-rapide. Si une seule personne a accès à la vidéo ou si tout le monde accepte qu'une seule personne contrôle la lecture, la pause et la navigation dans la vidéo, c'est peut-être l'option la plus simple.<br><br>C'est aussi une solution pertinente lorsque la qualité n'est pas une priorité, que personne n'a besoin de paramètres de sous-titres ou audio personnalisés, et que la personne qui partage l'écran est à l'aise à l'idée de montrer sa fenêtre de navigateur ou son écran.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "Les inconvénients du partage d'écran",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "Le principal inconvénient est que les participants ne regardent pas la vidéo via leur propre lecteur. Une personne diffuse son écran, et tous les autres ne voient que ce flux.<br><br>Cela peut entraîner une qualité vidéo moindre, une compression audio, un décalage de lecture et une perte de contrôle pour les autres spectateurs. De plus, les sous-titres et les pistes audio sont imposés par la personne qui partage.<br><br>Il y a aussi la question de la confidentialité. Si vous changez de fenêtre ou d'onglet, les autres peuvent brièvement voir vos onglets, vos e-mails, Reddit, vos discussions ou d'autres éléments privés. Vous pouvez contourner ce problème en utilisant une fenêtre séparée, mais c'est un aspect à prendre en compte.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "Pourquoi KoalaSync offre une expérience plus fluide",
"ALT_SCREEN_WHY_CLEANER_BODY": "KoalaSync ne diffuse pas votre écran. Chacun regarde la vidéo localement dans son propre navigateur, et KoalaSync se contente de synchroniser les actions de lecture telles que le lancement, la pause et la navigation dans la vidéo.<br><br>Ainsi, chacun peut passer en plein écran sur son appareil, utiliser ses propres paramètres de sous-titres ou audio (si le lecteur le permet) et gérer la discussion vocale séparément via Discord, Teamspeak ou un autre service d'appel.<br><br>Pour les soirées cinéma, cette approche est souvent plus agréable que la diffusion d'écran par une seule personne. C'est particulièrement utile pour les solutions basées sur un navigateur comme Emby, Jellyfin, Plex, les fichiers locaux ouverts dans le navigateur, les domaines privés ou tout autre site web utilisant des lecteurs vidéo standard.",
"ALT_SCREEN_LIMITS_TITLE": "Limites à connaître",
"ALT_SCREEN_LIMITS_BODY": "KoalaSync ne remplace pas un appel vocal. Si vous souhaitez discuter tout en regardant, vous continuez à utiliser Discord, Teamspeak, Signal, WhatsApp ou tout autre outil habituel.<br><br>Chacun doit également pouvoir accéder à la vidéo ou à la page depuis son propre appareil. KoalaSync ne contourne ni les abonnements, ni les identifiants, ni les DRM, ni les restrictions géographiques ou les règles des plateformes.<br><br>L'outil fonctionne actuellement uniquement sur les navigateurs de bureau, ne dispose pas de fonction de chat ou de voix intégrée, et la compatibilité dépend toujours du navigateur et du lecteur vidéo utilisé.",
"ALT_SCREEN_SUMMARY_TITLE": "Résumé",
"ALT_SCREEN_SUMMARY_BODY": "Le partage d'écran est la solution la plus simple si vous cherchez une méthode rapide sans accorder une grande importance à la qualité, à la confidentialité ou au contrôle individuel. KoalaSync est plus adapté lorsque chaque participant doit visionner le contenu localement dans son propre navigateur, pour une expérience se rapprochant davantage d'un véritable visionnage en commun.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "Essayer KoalaSync",
"MOCK_HOST_CONTROL_LABEL": "Contrôle de l'hôte",
"MOCK_HOST_CONTROL_TOGGLE": "Seul moi peut contrôler la lecture",
"LABEL_SELECT_VIDEO": "Choisir une vidéo",
"OPTION_SELECT_TAB": "-- Choisir un onglet --",
"THEME_TOGGLE_LABEL": "Basculer mode clair/sombre",
"FAQ_SHOW_ALL": "Afficher toutes les questions",
"FAQ_HIDE_ALL": "Masquer les questions",
"SELF_SHOW_SETUP": "Afficher le guide d'installation",
"SELF_HIDE_SETUP": "Masquer le guide d'installation",
"HERO_TRUST": "Gratuit · Open source · Sans compte",
"COMP_MORE_GUIDES": "Comparatif complet et autres alternatives"
}
+96 -26
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "it_IT",
"META_TITLE": "KoalaSync | Sincronizza Netflix, YouTube e qualsiasi video con i tuoi amici",
"META_DESCRIPTION": "Guarda Netflix, YouTube, Twitch e qualsiasi video HTML5 in perfetta sincronia. Estensione gratuita, open source e senza registrazione.",
"SCHEMA_APP_DESC": "Guarda Netflix, YouTube, Twitch e qualsiasi video HTML5 in perfetta sincronia. Estensione gratuita, open source e senza registrazione.",
"OG_TITLE": "KoalaSync | Sincronizza Netflix, Emby, Jellyfin e video web con i tuoi amici",
"OG_DESCRIPTION": "Guarda video in perfetta sincronia con i tuoi amici. Estensione open source per Chrome e Firefox incentrata sulla privacy.",
"TWITTER_TITLE": "KoalaSync | Sincronizza video in streaming con i tuoi amici",
"TWITTER_DESCRIPTION": "Guarda video insieme ai tuoi amici in perfetta sincronia. Open source e rispettoso della privacy.",
"META_TITLE": "KoalaSync | Watch party per Netflix, YouTube e ogni video",
"META_DESCRIPTION": "Watch party su Netflix, YouTube, Twitch e qualsiasi video HTML5. Estensione gratuita e open source per Chrome e Firefox. Senza account, tracking né pubblicità.",
"SCHEMA_APP_DESC": "Watch party su Netflix, YouTube, Twitch e qualsiasi video HTML5. Estensione gratuita e open source per Chrome e Firefox. Senza account, tracking né pubblicità.",
"OG_TITLE": "KoalaSync | Watch party per Netflix, Emby, Jellyfin e quasi ogni video",
"OG_DESCRIPTION": "Avvia una watch party su Netflix, Emby, Jellyfin, YouTube, Twitch e quasi ogni video HTML5. Open source, privata e senza account.",
"TWITTER_TITLE": "KoalaSync | Watch party per Netflix, Emby, Jellyfin e quasi ogni video Estensione",
"TWITTER_DESCRIPTION": "Avvia una watch party su Netflix, Emby, Jellyfin, YouTube, Twitch e quasi ogni video HTML5. Open source, privata e senza account.",
"NAV_FEATURES": "Funzionalità",
"NAV_HOW_IT_WORKS": "Come Funziona",
"HERO_TITLE": "Guardate Insieme.<span class='hero-line2'>Sincronia Perfetta.</span>",
"HERO_SUBTITLE": "La tua serata cinema a distanza senza ritardi. Nessun account, nessun dato raccolto. Condividi un link e guardate insieme.",
"HERO_TITLE": "Trasforma qualsiasi video in una <span class='hero-highlight'>watch party</span>.",
"HERO_SUBTITLE": "La tua serata film a distanza, senza lag. Niente account, niente tracciamento, niente pubblicità. Condividi un link e premete play insieme.",
"HERO_MASCOT_ALT": "Un simpatico koala che guarda i pulsanti di download",
"DEMO_SYNC": "Sincronizzato",
"DEMO_HINT": "Demo dal vivo: premi play in una delle schede e KoalaSync le mantiene sincronizzate.",
"DEMO_NO_ROOM": "Non ancora connesso",
"DEMO_CREATE_ROOM": "Crea stanza",
"DEMO_JOINED": "Entrato nella stanza",
"DEMO_TAB_SELECTED": "Scheda selezionata",
"DEMO_SELECT_PLACEHOLDER": "Scegli una scheda video…",
"MOBILE_DEMO_CTA": "Guarda come funziona",
"MOBILE_DEMO_TITLE": "Demo live di KoalaSync",
"MOBILE_DEMO_CLOSE": "Chiudi demo",
"ADD_TO_CHROME": "Aggiungi a Chrome",
"ADD_TO_FIREFOX": "Aggiungi a Firefox",
"COMPAT_HEADING": "Compatibile con le tue piattaforme preferite",
@@ -35,20 +45,20 @@
"USE_CASE_3_DESC": "Accorcia le distanze e goditi serate romantiche. Vivi ogni emozione e ridi alle stesse battute nello stesso identico istante.",
"WHY_TITLE": "Perché KoalaSync?",
"WHY_SUBTITLE": "Sincronizzazione affidabile, massima privacy e configurazione immediata.",
"FEATURE_1_TITLE": "Controllo Totale / Sincro Istantanea",
"FEATURE_1_DESC": "Se uno mette in pausa, tutti si fermano. Se uno salta in avanti, gli altri lo seguono. Tutto in tempo reale.",
"FEATURE_1_TITLE": "Controllo dell'host e sincronizzazione",
"FEATURE_1_DESC": "Decidi tu chi controlla la riproduzione. Attiva il controllo host per consentire solo a te di mettere in pausa e avanzare, o nomina co-host.",
"FEATURE_2_TITLE": "Maratone senza interruzioni",
"FEATURE_2_DESC": "Riproduzione automatica sincronizzata. KoalaSync attende che tutti abbiano caricato l'episodio successivo prima di farlo partire.",
"FEATURE_3_TITLE": "Senza Account / Privacy Totale",
"FEATURE_3_DESC": "Niente registrazioni o tracciamenti. Il server non salva nulla e cancella la tua stanza appena te ne vai.",
"FEATURE_4_TITLE": "Supporto HTML5 Universale",
"FEATURE_4_DESC": "Funziona su YouTube, Twitch, Netflix, Jellyfin, Emby e quasi ogni pagina web. Nessun dato video viene inviato ai nostri server.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby e quasi ogni pagina con video HTML5. Escono solo i segnali di sync, mai il video.",
"FEATURE_5_TITLE": "Ospitabile in Proprio (Docker)",
"FEATURE_5_DESC": "I nostri server sono veloci, ma puoi gestire il tuo server privato in pochi secondi tramite Docker.",
"FEATURE_6_TITLE": "Inviti Rapidi / Un clic per entrare",
"FEATURE_6_DESC": "Niente password complicate. Condividi il link generato e i tuoi amici entreranno automaticamente con un solo clic.",
"FEATURE_7_TITLE": "Volume Bilanciato / Compressore Audio",
"FEATURE_7_DESC": "Basta regolare il volume ogni minuto. Il compressore integrato esalta le voci e attenua i rumori forti. Tutto in locale.",
"FEATURE_7_DESC": "Basta armeggiare col volume. Un compressore integrato alza i dialoghi sommessi e attenua le esplosioni forti, tutto elaborato localmente nel browser.",
"FEATURE_8_TITLE": "Open Source (Licenza MIT)",
"FEATURE_8_DESC": "Tutto il codice è pubblico su GitHub. Trasparenza totale, senza scatole nere o software proprietario.",
"COMP_TITLE": "KoalaSync vs Teleparty",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Politiche sulla privacy di Teleparty:",
"COMP_FOOTNOTE_4": "Funziona su siti che permettono l'uso di script nei tag video HTML5. Alcuni siti con protezioni DRM molto rigide potrebbero limitare il controllo automatico.",
"STEPS_TITLE": "Inizia ora",
"STEP_1_TITLE": "Installa l'Estensione",
"STEP_1_DESC": "Aggiungi KoalaSync al tuo browser dal Chrome Web Store o dai componenti aggiuntivi di Firefox.",
"STEP_1_ILLUS_DESC": "Sincronizzatore video privato",
"STEP_1_ILLUS_DL_CHROME": "Scarica per Chrome",
"STEP_1_ILLUS_DL_FIREFOX": "Scarica per Firefox",
"STEP_1_ILLUS_ACTIVE": "Estensione Attiva",
"STEP_1_ILLUS_READY": "Pronti a guardare!",
"STEP_2_TITLE": "Crea una Stanza",
"STEP_2_DESC": "Apri l'estensione e clicca su '+ Crea Nuova Stanza'. Verrà generato un link di invito automaticamente.",
"STEP_1_TITLE": "Creare e condividere la stanza",
"STEP_1_DESC": "Apri l'estensione, crea una stanza con un clic e condividi il link con i tuoi amici. Si uniranno all'istante tramite il link di invito.",
"STEP_1_ILLUS_DESC": "Scegli una scheda da collegare",
"STEP_1_ILLUS_DL_CHROME": "Collega scheda YouTube",
"STEP_1_ILLUS_DL_FIREFOX": "Collega scheda Netflix",
"STEP_1_ILLUS_ACTIVE": "Scheda collegata",
"STEP_1_ILLUS_READY": "Pronto per guardare in sincro !",
"STEP_2_TITLE": "Selezionare la scheda del video",
"STEP_2_DESC": "Vai alla scheda del tuo video e selezionala nel menu dell'estensione. KoalaSync collegherà i vostri lettori in modo automatico.",
"STEP_2_ILLUS_ROOM": "Stanza",
"STEP_2_ILLUS_SYNC": "Sincro",
"STEP_2_ILLUS_SETTINGS": "Ajustes",
"STEP_2_ILLUS_CREATE": "+ Crea Nuova Stanza",
"STEP_2_ILLUS_MANUAL": "Manuale / Avanzato",
"STEP_2_ILLUS_COPIED": "Link copiato!",
"STEP_3_TITLE": "Condividi e Guarda",
"STEP_3_DESC": "Invia il link ai tuoi amici. Una volta connessi, scegli il video e goditi la riproduzione sincronizzata.",
"STEP_3_TITLE": "Godersi la sincronizzazione",
"STEP_3_DESC": "Avvia la riproduzione. Quando qualcuno mette in pausa, avanza o riproduce, il video di tutti rimarrà sincronizzato in tempo reale senza ritardi.",
"STEP_3_ILLUS_IN_SYNC": "SINCRONIZZATI",
"SELF_TITLE": "Per utenti esperti",
"SELF_SUBTITLE": "Preferisci gestire i tuoi dati? Avvia il tuo server privato in pochi minuti.",
@@ -117,6 +127,8 @@
"FAQ_SUBTITLE": "Tutto quello che c'è da sapere per guardare video in perfetta sincronia.",
"FAQ_Q1": "Posso guardare Netflix o YouTube in sincronia?",
"FAQ_A1": "Certamente! KoalaSync funziona su Netflix, YouTube, Twitch e qualsiasi sito con video HTML5. Basta installare l'estensione, creare una stanza e condividere il link.",
"FAQ_Q_VIDEO_ACCESS": "KoalaSync trasmette il video ai miei amici?",
"FAQ_A_VIDEO_ACCESS": "No. KoalaSync non trasmette, ospita, condivide o aggira laccesso ai contenuti video. Ogni persona guarda il video localmente nel proprio browser e deve avere il proprio accesso al sito o al servizio di streaming, ad esempio un proprio account Netflix. KoalaSync sincronizza solo le azioni di riproduzione come play, pausa e avanzamento.",
"FAQ_Q2": "KoalaSync è gratuito?",
"FAQ_A2": "Sì, al 100%. È open source con licenza MIT. Non ci sono funzioni a pagamento né pubblicità. A differenza di altre app, non raccogliamo dati utente.",
"FAQ_Q3": "Funziona con Emby o Jellyfin?",
@@ -156,7 +168,7 @@
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SYNC",
"MOCK_14": "Vai dagli altri",
"MOCK_15": "Ultima attività",
"MOCK_16": "In attesa di un amico (KoalaPC)...",
"MOCK_16": "In attesa di un amico (🐶 HappyDog)...",
"MOCK_17": "Salta l'attesa",
"MOCK_18": "Tuo Nome",
"MOCK_19": "Nascondi schede",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSync non è affiliato a Netflix, YouTube o altre piattaforme. I marchi appartengono ai rispettivi proprietari.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - Alternative e confronti",
"ALT_INDEX_META_DESC": "Scopri confronti onesti e guide dettagliate tra KoalaSync e altre estensioni per watch party e soluzioni di streaming.",
"ALT_INDEX_HEADING": "Guide e confronti",
"ALT_INDEX_INTRO": "Come si confronta KoalaSync con altri strumenti per watch party e soluzioni di streaming? Leggi i nostri confronti onesti e tecnici per trovare lopzione migliore per le tue serate film a distanza.",
"ALT_INDEX_READ_COMPARISON": "Leggi il confronto →",
"ALT_INDEX_READ_GUIDE": "Leggi la guida →",
"ALT_INDEX_FOOTER_LINK": "Guide e confronti",
"ALT_TELEPARTY_TITLE": "KoalaSync vs Teleparty: quale strumento scegliere per le watch party?",
"ALT_TELEPARTY_META_DESC": "Un confronto pratico tra KoalaSync e Teleparty per le watch party: chat integrata, supporto mobile, sincronizzazione video nel browser, server multimediali self-hosted e natura open-source.",
"ALT_TELEPARTY_INTRO": "Sia KoalaSync che Teleparty risolvono lo stesso fastidioso problema: guardare qualcosa insieme senza dover ricorrere alla classica procedura del '3, 2, 1, via'. Semplicemente, adottano approcci diversi.<br><br>Teleparty è la scelta ideale se cerchi una chat integrata o se vuoi guardare i contenuti da dispositivo mobile. KoalaSync è più indicato per le watch party su browser desktop, quando stai già comunicando tramite Discord, Teamspeak o un'altra chiamata vocale e vuoi semplicemente mantenere sincronizzata la riproduzione.<br><br>Il motivo principale per cui ho creato KoalaSync è la flessibilità. È progettato per funzionare con quasi tutti i siti web che utilizzano un normale lettore video del browser, non solo con una lista predefinita di piattaforme. Ciò include configurazioni self-hosted come Emby, Jellyfin e Plex, domini locali o privati e molte pagine web che ospitano video.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Quando Teleparty può ancora essere la scelta giusta",
"ALT_TELEPARTY_WHEN_TELE_BODY": "Teleparty può rivelarsi la scelta migliore se desideri che la chat sia parte integrante dello strumento stesso per la watch party. Attualmente KoalaSync non dispone di una chat integrata; presuppone che tu stia già comunicando altrove, ad esempio su Discord, Teamspeak, WhatsApp, Signal o tramite una normale chiamata.<br><br>Il supporto mobile è un altro fattore chiave. Teleparty offre un'app dedicata, mentre KoalaSync funziona al momento solo su browser desktop. Se vuoi sincronizzare Netflix su smartphone o tablet, KoalaSync non è lo strumento adatto in questo momento.<br><br>Quindi, se la chat e il supporto mobile sono gli aspetti a cui tieni di più, Teleparty potrebbe adattarsi meglio alle tue esigenze.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "Quando KoalaSync è la scelta ideale",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "KoalaSync è la soluzione ideale se cerchi uno strumento flessibile per sincronizzare la riproduzione video tra browser desktop, anziché una soluzione vincolata a una lista ristretta di piattaforme.<br><br>Funziona con quasi tutti i siti web che utilizzano i normali lettori video dei browser. È particolarmente utile per Emby, Jellyfin, Plex, server multimediali privati, domini locali o siti video minori che solitamente vengono ignorati dai classici strumenti per le sessioni di visione condivisa.<br><br>È inoltre gratuito, open source, non richiede la creazione di account e può essere ospitato autonomamente se desideri gestire il tuo relay. L'ho creato perché volevo una soluzione rispettosa della privacy che funzionasse con la mia configurazione Emby, non l'ennesima piattaforma chiusa limitata a pochi grandi servizi di streaming.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "La differenza principale",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "Teleparty è concepito principalmente come strumento per la visione condivisa, completo di chat e supporto mobile. KoalaSync, invece, agisce più come un livello flessibile di sincronizzazione per i video riprodotti nel browser.<br><br>Può sembrare una differenza da poco, ma cambia notevolmente le modalità d'uso. Se cerchi un'applicazione completa per la visione condivisa con chat integrata, Teleparty è la scelta giusta. Se invece disponi già di una chat vocale e vuoi semplicemente mantenere sincronizzati i controlli video tra diversi browser, KoalaSync è la soluzione più semplice ed efficace.",
"ALT_TELEPARTY_LIMITS_TITLE": "Limiti dichiarati apertamente",
"ALT_TELEPARTY_LIMITS_BODY": "KoalaSync non fa miracoli e non cerca di nascondere i propri limiti. Attualmente funziona solo su browser desktop; non dispone di chat integrata né di un'app per dispositivi mobili.<br><br>La compatibilità dipende anche dal browser e dal lettore video utilizzato. KoalaSync dovrebbe funzionare con quasi tutti i normali lettori video dei browser, ma i siti web possono modificare in qualsiasi momento i propri lettori, la gestione dei DRM o le restrizioni del browser.<br><br>KoalaSync non è affiliato a Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex o qualsiasi altro marchio qui menzionato. I nomi sono utilizzati esclusivamente per illustrare i tipici scenari di utilizzo.",
"ALT_TELEPARTY_SUMMARY_TITLE": "Riepilogo",
"ALT_TELEPARTY_SUMMARY_BODY": "Se cerchi una chat integrata o il supporto per dispositivi mobili, Teleparty potrebbe essere la scelta più adatta. Se invece preferisci una soluzione gratuita e open-source per la sincronizzazione tra browser desktop, compatibile con quasi tutti i comuni lettori video, inclusi i media server self-hosted come Emby, Jellyfin e Plex. KoalaSync è probabilmente l'opzione migliore.",
"ALT_SCREEN_TITLE": "KoalaSync vs condivisione schermo: quando è meglio la sincronizzazione video?",
"ALT_SCREEN_META_DESC": "Un confronto pratico tra KoalaSync e la condivisione dello schermo per serate film a distanza, analizzando controllo della riproduzione, qualità video, privacy, sottotitoli e visione tramite browser.",
"ALT_SCREEN_INTRO": "La condivisione dello schermo funziona e, a dire il vero, a volte è sufficiente. Una persona condivide il proprio schermo, le altre guardano e il gioco è fatto.<br><br>Tuttavia, per serate film più lunghe o per guardare insieme degli episodi, può diventare scomoda. La qualità video dipende dalla velocità di upload, dalla CPU e dal browser di chi condivide, oltre che dalla compressione applicata da app come Discord, Teamspeak, Zoom o altre piattaforme di chiamata. Inoltre, solo una persona ha il controllo effettivo della riproduzione.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "Quando la condivisione dello schermo è sufficiente",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "La condivisione dello schermo è una buona soluzione quando si cerca la configurazione più rapida possibile. Se solo una persona ha accesso al video o se a tutti va bene che una sola persona controlli la riproduzione, la pausa e lo scorrimento del video, potrebbe essere l'opzione più semplice.<br><br>È una soluzione valida anche quando la qualità non è una priorità assoluta, quando nessuno ha bisogno di impostazioni personalizzate per sottotitoli o audio e quando chi condivide lo schermo si sente a proprio agio nel mostrare quella specifica finestra del browser o l'intero schermo.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "Quando la condivisione schermo diventa scomoda",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "Lo svantaggio principale è che non tutti guardano il video tramite il proprio lettore. Una persona trasmette il proprio schermo e tutti gli altri vedono solo quel flusso video.<br><br>Ciò può comportare una qualità video inferiore, audio compresso, ritardi nella riproduzione e un controllo limitato per gli altri partecipanti. Anche i sottotitoli e le tracce audio sono quelli scelti da chi effettua la condivisione.<br><br>C'è poi l'aspetto della privacy. Se cambi finestra o scheda, gli altri potrebbero vedere brevemente le schede del tuo browser, le email, Reddit, le chat o altri contenuti privati. È possibile aggirare il problema usando una finestra separata, ma è comunque un aspetto da tenere a mente.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "Perché KoalaSync offre un'esperienza più pulita",
"ALT_SCREEN_WHY_CLEANER_BODY": "KoalaSync non trasmette il tuo schermo. Ognuno guarda il video localmente sul proprio browser e KoalaSync si limita a sincronizzare le azioni di riproduzione, come avvio, pausa e scorrimento.<br><br>Questo permette a ciascuno di utilizzare la modalità a schermo intero sul proprio dispositivo, di impostare i sottotitoli o l'audio secondo le proprie preferenze (se il lettore lo consente) e di mantenere separata la chat vocale su Discord, Teamspeak o altre piattaforme.<br><br>Per le serate film, questa soluzione risulta molto più ordinata rispetto alla condivisione dello schermo da parte di una sola persona. È particolarmente utile per configurazioni basate su browser come Emby, Jellyfin, Plex, file locali aperti nel browser, domini privati o altri siti web dotati di lettori video standard.",
"ALT_SCREEN_LIMITS_TITLE": "Limiti reali",
"ALT_SCREEN_LIMITS_BODY": "KoalaSync non sostituisce una chiamata vocale. Se volete parlare mentre guardate il contenuto, potete continuare a usare Discord, Teamspeak, Signal, WhatsApp o qualsiasi altro strumento abituale.<br><br>Ciascun partecipante deve inoltre poter accedere al video o alla pagina dal proprio dispositivo. KoalaSync non aggira abbonamenti, accessi, DRM, restrizioni geografiche o regole delle piattaforme.<br><br>Al momento è utilizzabile solo tramite browser desktop, non dispone di funzioni di chat o voce integrate e la compatibilità dipende sempre dal browser e dal lettore video.",
"ALT_SCREEN_SUMMARY_TITLE": "Riepilogo",
"ALT_SCREEN_SUMMARY_BODY": "La condivisione dello schermo è l'opzione più semplice quando si cerca una soluzione rapida e non si dà molta importanza alla qualità, alla privacy o al controllo individuale. KoalaSync è la scelta ideale quando tutti devono guardare il contenuto localmente sul proprio browser, offrendo un'esperienza che ricorda più da vicino la visione condivisa reale.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "Prova KoalaSync",
"MOCK_HOST_CONTROL_LABEL": "Controllo host",
"MOCK_HOST_CONTROL_TOGGLE": "Solo io posso controllare la riproduzione",
"LABEL_SELECT_VIDEO": "Seleziona Video",
"OPTION_SELECT_TAB": "-- Seleziona una Scheda --",
"THEME_TOGGLE_LABEL": "Attiva/disattiva tema chiaro/scuro",
"FAQ_SHOW_ALL": "Mostra tutte le domande",
"FAQ_HIDE_ALL": "Nascondi le domande",
"SELF_SHOW_SETUP": "Mostra la guida all'installazione",
"SELF_HIDE_SETUP": "Nascondi la guida",
"HERO_TRUST": "Gratuito · Open source · Senza account",
"COMP_MORE_GUIDES": "Confronto completo e altre alternative"
}
+96 -26
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "ja_JP",
"META_TITLE": "KoalaSync | NetflixYouTube、あらゆる動画を友達と同期視聴",
"META_DESCRIPTION": "Netflix、YouTube、Twitch、およびあらゆるHTML5動画を友達と完全に同期して視聴。ChromeおよびFirefoxの無料オープンソースブラウザ拡張機能。登録不要。",
"SCHEMA_APP_DESC": "Netflix、YouTube、Twitch、およびあらゆるHTML5動画を友達と完全に同期して視聴。ChromeおよびFirefoxの無料オープンソースブラウザ拡張機能。登録不要。",
"OG_TITLE": "KoalaSync | Netflix、Emby、Jellyfin、ほぼすべての動画を友達と同期",
"OG_DESCRIPTION": "Netflix、Emby、Jellyfin、YouTube、Twitch、ほぼすべてのHTML5動画を完全に同期して視聴。ChromeおよびFirefox用のプライバシー優先のオープンソースブラウザ拡張機能。",
"TWITTER_TITLE": "KoalaSync | Netflix、Emby、Jellyfin、ほぼすべての動画を友達と同期 ブラウザ拡張機能",
"TWITTER_DESCRIPTION": "Netflix、Emby、Jellyfin、YouTube、Twitch、ほぼすべてのHTML5動画を友達と完全に同期して視聴。ChromeおよびFirefox用のプライバシー優先のオープンソースブラウザ拡張機能。",
"META_TITLE": "KoalaSync | NetflixYouTube対応ウォッチパーティー拡張機能",
"META_DESCRIPTION": "Netflix、YouTube、TwitchなどあらゆるHTML5動画でウォッチパーティー。ChromeFirefox向けの無料オープンソース拡張機能。アカウント不要、トラッキングなし、広告なし。",
"SCHEMA_APP_DESC": "Netflix、YouTube、TwitchなどあらゆるHTML5動画でウォッチパーティー。ChromeFirefox向けの無料オープンソース拡張機能。アカウント不要、トラッキングなし、広告なし。",
"OG_TITLE": "KoalaSync | Netflix、Emby、Jellyfin、ほぼすべての動画でウォッチパーティー",
"OG_DESCRIPTION": "Netflix、Emby、Jellyfin、YouTube、Twitch、ほぼすべてのHTML5動画でウォッチパーティーを開催。オープンソース、プライバシー重視、アカウント不要。",
"TWITTER_TITLE": "KoalaSync | Netflix、Emby、Jellyfin、ほぼすべての動画でウォッチパーティー 拡張機能",
"TWITTER_DESCRIPTION": "Netflix、Emby、Jellyfin、YouTube、Twitch、ほぼすべてのHTML5動画でウォッチパーティーを開催。オープンソース、プライバシー重視、アカウント不要。",
"NAV_FEATURES": "機能",
"NAV_HOW_IT_WORKS": "仕組み",
"HERO_TITLE": "一緒に観よう。<span class='hero-line2'>完璧に同期。</span>",
"HERO_SUBTITLE": "遅延のないリモート映画鑑賞。登録不要、データ収集なし。リンクを共有するだけで、一緒に視聴できます。",
"HERO_TITLE": "どんな動画も<span class='hero-highlight'>ウォッチパーティー</span>に。",
"HERO_SUBTITLE": "離れていても一緒に映画ナイト。アカウント不要、トラッキングなし、広告なし。リンクを共有して、一緒に再生するだけ。",
"HERO_MASCOT_ALT": "ダウンロードボタンを見下ろしているかわいいコアラ",
"DEMO_SYNC": "同期中",
"DEMO_HINT": "ライブデモ:どちらかのタブで再生すると、KoalaSyncが両方を同期させます。",
"DEMO_NO_ROOM": "未接続",
"DEMO_CREATE_ROOM": "ルームを作成",
"DEMO_JOINED": "ルームに参加",
"DEMO_TAB_SELECTED": "タブを選択済み",
"DEMO_SELECT_PLACEHOLDER": "動画タブを選ぶ…",
"MOBILE_DEMO_CTA": "仕組みを見る",
"MOBILE_DEMO_TITLE": "KoalaSync ライブデモ",
"MOBILE_DEMO_CLOSE": "デモを閉じる",
"ADD_TO_CHROME": "Chromeに追加",
"ADD_TO_FIREFOX": "Firefoxに追加",
"COMPAT_HEADING": "お気に入りのプラットフォームで動作",
@@ -35,20 +45,20 @@
"USE_CASE_3_DESC": "距離を縮めてデートナイトを楽しみましょう。すべてのストーリー展開を体験し、同じジョックで笑い、感情的な瞬間をまったく同じミリ秒で共有します。",
"WHY_TITLE": "KoalaSyncを選ぶ理由",
"WHY_SUBTITLE": "信頼性の高い同期、プライバシー優先の設計、簡単なセットアップのために構築。",
"FEATURE_1_TITLE": "完全なコントロール / 即時同期",
"FEATURE_1_DESC": "一人が一時停止すれば、全員が一時停止。一人がシークすれば、全員が追従。2フェーズ同期プロトコルが、リアルタイムで全員の再生状況を連動させます。",
"FEATURE_1_TITLE": "ホスト制御と同期",
"FEATURE_1_DESC": "再生をコントロールする人を決定できます。ホスト操作を有効にして自分だけが一時停止やスキップを行えるようにするか、共同ホストを任命します。",
"FEATURE_2_TITLE": "終わらない連続視聴",
"FEATURE_2_DESC": "同期した自動再生。KoalaSyncはエピソードの切り替えを自動検知し、全員が次の動画を読み込むまで再生を待機します。",
"FEATURE_3_TITLE": "アカウント不要 / 純粋なプライバシー",
"FEATURE_3_DESC": "登録不要、追跡なし、履歴も残りません。サーバーは一時的なメモリ(RAM)上でのみ動作し、退出時にルームを完全に消去します。",
"FEATURE_4_TITLE": "ユニバーサルHTML5サポート",
"FEATURE_4_DESC": "YouTube、Twitch、Netflix、Jellyfin、Emby、およびHTML5ビデオ要素を含むほぼすべての標準的なWebページで動作します。独自のセルフホスト設定とも互換性があります。No video data is ever sent to our servers — only synchronization metadata.",
"FEATURE_4_DESC": "Netflix、YouTube、Twitch、Jellyfin、Emby、そしてHTML5動画があるほぼすべてのページに対応。ブラウザから出るのは同期信号だけで、動画自体は送信されません。",
"FEATURE_5_TITLE": "セルフホスト可能&Docker対応",
"FEATURE_5_DESC": "公式サーバーは無料で高速ですが、必要に応じて独自のサーバーを構築できます。Dockerを使用して、数秒でプライベートリレーサーバーを起動できます。",
"FEATURE_6_TITLE": "インスタント招待 / 1クリック参加",
"FEATURE_6_DESC": "IPアドレスやパスワードをやり取りする必要はありません。生成された招待リンクを友達に共有するだけで、1クリックで自動的にルームに参加できます。",
"FEATURE_7_TITLE": "音量均等化 / オーディオコンプレッサー",
"FEATURE_7_DESC": "音量つまみをいじる必要はもうありません。内蔵オーディオコンプレッサーが小さな会話を強調し、大きな爆発音を抑えます。Netflix、Prime Video、Disney+、YouTube、あらゆるHTML5ビデオで動作。完全にローカル処理 — オーディオデータがコンピュータから送信されることはありません。",
"FEATURE_7_DESC": "音量つまみをいじる必要はもうありません。内蔵コンプレッサーが小さな会話を持ち上げ、大きな爆発音を抑えます。すべてブラウザ内でローカル処理されます。",
"FEATURE_8_TITLE": "オープンソース(MITライセンス)",
"FEATURE_8_DESC": "すべてのコードベースはGitHubで公開されており、誰でも監査可能です。ブラックボックスや独自のバイナリデータ(blob)は一切なく、中継サーバーからブラウザ拡張機能まで100%の透明性を確保しています。",
"COMP_TITLE": "KoalaSync と Teleparty の比較",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Telepartyのプライバシーポリシーと追跡データの収集について:",
"COMP_FOOTNOTE_4": "標準的なHTML5ビデオタグへのスクリプトインジェクションを許可するウェブサイトで動作します。非常に厳格なコンテンツセキュリティポリシー(CSP)、DRMコピー保護、または高度に難読化されたプレーヤーラッパー(複雑なシャドウDOMなど)を持つウェブサイトでは、自動制御やインジェクションが制限される場合があります。",
"STEPS_TITLE": "はじめに",
"STEP_1_TITLE": "拡張機能をインストール",
"STEP_1_DESC": "Chrome ウェブストアや Firefox Add-ons から KoalaSync をブラウザに追加するか、GitHub から最新の開発者向け ZIP をダウンロードしてください。",
"STEP_1_ILLUS_DESC": "プライバシー優先のビデオ同期ツール",
"STEP_1_ILLUS_DL_CHROME": "Chrome版をダウンロード",
"STEP_1_ILLUS_DL_FIREFOX": "Firefox版をダウンロード",
"STEP_1_ILLUS_ACTIVE": "拡張機能はアクティブです",
"STEP_1_ILLUS_READY": "一緒に観る準備完了",
"STEP_2_TITLE": "ルームを作成",
"STEP_2_DESC": "拡張機能のポップアップを開き、「+ 新規ルーム作成」をクリックします。KoalaSync自動的に安全なルームIDとパスワードを生成し、接続し、招待リンクをクリップボードにコピーします。",
"STEP_1_TITLE": "ルーム作成と共有",
"STEP_1_DESC": "拡張機能を開き、ワンクリックでルームを作成してリンクを共有します。友達は招待リンクからすぐに参加できます。",
"STEP_1_ILLUS_DESC": "連携するタブを選択",
"STEP_1_ILLUS_DL_CHROME": "YouTubeタブを連携",
"STEP_1_ILLUS_DL_FIREFOX": "Netflixタブを連携",
"STEP_1_ILLUS_ACTIVE": "タブ連携完了",
"STEP_1_ILLUS_READY": "同期再生の準備完了 !",
"STEP_2_TITLE": "動画タブの選択",
"STEP_2_DESC": "動画のあるタブを開き、拡張機能のメニューでそのタブを選択します。KoalaSync自動的にお互いのプレイヤーを連携させます。",
"STEP_2_ILLUS_ROOM": "ルーム",
"STEP_2_ILLUS_SYNC": "同期",
"STEP_2_ILLUS_SETTINGS": "設定",
"STEP_2_ILLUS_CREATE": "+ 新規ルーム作成",
"STEP_2_ILLUS_MANUAL": "手動接続 / 詳細設定",
"STEP_2_ILLUS_COPIED": "招待リンクをコピーしました!",
"STEP_3_TITLE": "共有と同期",
"STEP_3_DESC": "招待リンクを友達に送信します。友達が参加したら、ビデオのタブを選択して、同期された再生を楽しんでください。",
"STEP_3_TITLE": "同期を楽しむ",
"STEP_3_DESC": "再生ボタンを押します。誰かが一時停止、早送り、または再生をしても、全員の動画がタイムラグなしで完璧に同期します。",
"STEP_3_ILLUS_IN_SYNC": "同期中",
"SELF_TITLE": "セルフホストする方向け",
"SELF_SUBTITLE": "公式サーバーを信頼しませんか?データの完全な所有権を維持しましょう。数分で独自のプライベートリレーサーバーを構築できます。",
@@ -117,6 +127,8 @@
"FAQ_SUBTITLE": "完璧に同期して動画を一緒に楽しむために必要なすべての情報。",
"FAQ_Q1": "友達と一緒にNetflix、Emby、Jellyfinを同期視聴できますか?",
"FAQ_A1": "はい!KoalaSyncはNetflix, YouTube, TwitchおよびHTML5ビデオプレーヤーを備えたあらゆるウェブサイト(Emby, Jellyfin, 独自のメディアサーバーを含む)で動作します。拡張機能をインストールし、ルームを作成して招待リンクを共有するだけで、全員で完全に同期して視聴できます。",
"FAQ_Q_VIDEO_ACCESS": "KoalaSync は友達に動画を配信しますか?",
"FAQ_A_VIDEO_ACCESS": "いいえ。KoalaSync は動画コンテンツを配信、ホスト、共有したり、アクセス制限を回避したりしません。各参加者は自分のブラウザで動画をローカルに視聴し、Netflix アカウントなど、そのウェブサイトやストリーミングサービスへの自分自身のアクセス権が必要です。KoalaSync は再生、一時停止、シークなどの再生操作だけを同期します。",
"FAQ_Q2": "KoalaSyncはTelepartyの無料の代替ツールですか?",
"FAQ_A2": "MITライセンスのもとで100%無料かつオープンソース。有料プラン、制限された機能、広告などは一切ありません。Telepartyとは異 nel、KoalaSyncのリレーサーバーはメモリ上のみで動作し、データ収集やユーザーアカウント作成は不要です。",
"FAQ_Q3": "KoalaSyncはEmby、Jellyfin、およびセルフホストのメディアサーバーで動作しますか?",
@@ -156,7 +168,7 @@
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SYNC",
"MOCK_14": "他の人に合わせる",
"MOCK_15": "最新のアクティビティ状況",
"MOCK_16": "1人のメンバー (KoalaPC) を待機中...",
"MOCK_16": "1人のメンバー (🐶 HappyDog) を待機中...",
"MOCK_17": "スキップして再生",
"MOCK_18": "ユーザー名",
"MOCK_19": "不要なタブを非表示",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSyncは、Netflix、Disney+、Amazon、YouTube、Twitch、またはその他のストリーミングプラットフォームと提携、承認、または関連付けられているものではありません。すべての商標はそれぞれの所有者に帰属します。",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - 代替サービスと比較",
"ALT_INDEX_META_DESC": "KoalaSync と他のウォッチパーティー拡張機能やストリーミング向けソリューションを、公平な比較と詳しいガイドで確認できます。",
"ALT_INDEX_HEADING": "ガイドと比較",
"ALT_INDEX_INTRO": "KoalaSync は他のウォッチパーティーツールやストリーミング向けソリューションと比べてどう違うのでしょうか。リモート映画鑑賞に合う選択肢を見つけるために、公平で技術的な比較をご覧ください。",
"ALT_INDEX_READ_COMPARISON": "比較を読む →",
"ALT_INDEX_READ_GUIDE": "ガイドを読む →",
"ALT_INDEX_FOOTER_LINK": "ガイドと比較",
"ALT_TELEPARTY_TITLE": "KoalaSync vs Teleparty:どちらのウォッチパーティツールを使うべき?",
"ALT_TELEPARTY_META_DESC": "ウォッチパーティ向けツール、KoalaSyncとTelepartyの実践的比較。チャット機能、モバイル対応、ブラウザでの動画同期、セルフホスト型メディアサーバー、オープンソース利用といった観点から解説します。",
"ALT_TELEPARTY_INTRO": "KoalaSyncとTelepartyは、どちらも「3、2、1、再生!」と声を掛け合う手間をかけずに、一緒に動画を楽しむという共通の課題を解決してくれます。ただ、そのアプローチは異なります。<br><br>チャット機能が組み込まれていることや、モバイル端末で視聴できることを重視するなら、Telepartyが最適です。一方、KoalaSyncは、DiscordやTeamSpeakなどで既に通話しながら、ブラウザ上で動画の再生タイミングだけを同期させたいという、デスクトップブラウザでの利用に適しています。<br><br>私がKoalaSyncを開発した主な理由は「柔軟性」です。特定のプラットフォームだけでなく、一般的なブラウザ用動画プレーヤーを使用するほぼすべてのウェブサイトで動作するように設計されています。これには、Emby、Jellyfin、Plexといったセルフホスト環境や、ローカル/プライベートドメイン、そしてブラウザ上の一般的な動画ページなどが含まれます。",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Telepartyが適しているケース",
"ALT_TELEPARTY_WHEN_TELE_BODY": "ウォッチパーティツール自体にチャット機能が組み込まれていることを望むなら、Telepartyの方が良い選択肢となるでしょう。KoalaSyncには現在、チャット機能が搭載されていません。Discord、TeamSpeak、WhatsApp、Signal、あるいは通常の通話など、別の手段で既に会話していることを前提としています。<br><br>もう一つの大きな理由はモバイル対応です。Telepartyにはアプリがありますが、KoalaSyncは現時点ではデスクトップブラウザでしか動作しません。スマートフォンやタブレットでNetflixを同期再生したい場合、今のところKoalaSyncは適していません。<br><br>つまり、チャット機能やモバイル対応を何よりも重視するのであれば、Telepartyの方があなたの利用スタイルに合っているかもしれません。",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "KoalaSyncが適している場合",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "特定のプラットフォームに縛られるツールではなく、柔軟性の高いデスクトップブラウザ用同期ツールをお探しなら、KoalaSyncが適しています。<br><br>一般的なブラウザの動画プレーヤーを使用するほぼすべてのウェブサイトで動作します。これは特に、Emby、Jellyfin、Plex、プライベートメディアサーバー、ローカルドメイン、あるいは従来のウォッチパーティツールが見落としがちな小規模な動画サイトなどで役立ちます。<br><br>また、無料かつオープンソースであり、アカウント登録も不要です。独自のリレーサーバーを運用したい場合は、セルフホストも可能です。私がこのツールを開発したのは、一部の大手ストリーミングサイト専用の閉鎖的なプラットフォームではなく、自分のEmby環境で動作し、プライバシーに配慮したツールが欲しかったからです。",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "主な違い",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "Telepartyは、チャットやモバイル対応を備えた「ウォッチパーティ(同時視聴)」向け製品です。一方、KoalaSyncは、ブラウザ上の動画再生を同期させるための、より柔軟な同期レイヤーと言えます。<br><br>些細な違いに聞こえるかもしれませんが、これによって利用シーンが大きく変わります。チャット機能が組み込まれた総合的なウォッチパーティアプリが必要なら、Telepartyが適しています。すでにボイスチャット環境があり、単にブラウザ間で動画の再生状態を同期させたいだけであれば、よりシンプルなKoalaSyncが適しています。",
"ALT_TELEPARTY_LIMITS_TITLE": "制限事項について",
"ALT_TELEPARTY_LIMITS_BODY": "KoalaSyncは魔法のツールではなく、その制限を隠そうともしていません。現時点ではデスクトップブラウザでのみ動作し、チャット機能やモバイルアプリは搭載されていません。<br><br>互換性はブラウザや動画プレーヤーにも依存します。KoalaSyncは一般的なブラウザの動画プレーヤーのほとんどで動作するはずですが、ウェブサイト側がプレーヤー、DRMの挙動、あるいはブラウザへの制限などをいつでも変更する可能性があります。<br><br>KoalaSyncは、Netflix、Disney+、Prime Video、YouTube、Teleparty、Emby、Jellyfin、Plex、その他ここで言及されているブランドとは一切関係ありません。これらの名称は、一般的な利用シーンを説明するために使用されているに過ぎません。",
"ALT_TELEPARTY_SUMMARY_TITLE": "概要",
"ALT_TELEPARTY_SUMMARY_BODY": "チャット機能やモバイル対応を重視するなら、Telepartyの方が適しているかもしれません。一方、無料かつオープンソースで、デスクトップブラウザでの同期が可能、さらにEmby、Jellyfin、Plexといったセルフホスト型メディアサーバーを含む、一般的な動画プレーヤーのほとんどで動作するツールをお探しなら、KoalaSyncの方が適しているでしょう。",
"ALT_SCREEN_TITLE": "KoalaSyncと画面共有:動画同期の方が優れているのはどんな時?",
"ALT_SCREEN_META_DESC": "リモートでの映画鑑賞会におけるKoalaSyncと画面共有の実践的な比較。再生制御、画質、プライバシー、字幕、ブラウザでの視聴といった観点から解説します。",
"ALT_SCREEN_INTRO": "画面共有は有効な手段であり、正直なところ、それで十分な場合もあります。一人が画面を共有し、他の全員がそれを見るだけで済みますから。<br><br>しかし、長時間の映画鑑賞やシリーズ作品を一緒に見るとなると、煩わしさを感じることもあります。画質は、共有者のアップロード速度、CPU、ブラウザ、そしてDiscord、TeamSpeak、Zoomなどの通話アプリによる圧縮処理に左右されます。また、再生を実際に操作できるのは一人のみです。",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "画面共有で十分な場合",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "とにかく手早く準備を済ませたいなら、画面共有で問題ありません。もし一人だけが動画にアクセスできる場合……あるいは、一人が再生・一時停止・シーク(再生位置の移動)を操作することに全員が納得しているなら、それが最もシンプルな選択肢かもしれません。<br><br>画質がそれほど重要ではなく、個別の字幕や音声設定を必要とする人がおらず、かつ共有者がブラウザウィンドウや画面をそのまま見せても構わない場合には、理にかなった方法と言えます。",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "画面共有が煩わしくなる場合",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "最大の欠点は、全員がそれぞれのプレイヤーで視聴するわけではないという点です。一人が画面をストリーミングし、他の全員はそのストリーム映像を見るだけになります。<br><br>その結果、画質の低下、音声の圧縮、再生の遅延が生じたり、他の参加者が操作できる範囲が狭まったりする可能性があります。字幕や音声トラックも、共有者が選択したものに固定されます。<br><br>プライバシーの問題もあります。ウィンドウやタブを切り替える際、ブラウザのタブ、メール、Reddit、チャット、その他の個人的な情報が一時的に見えてしまう可能性があります。別のウィンドウを使うことで回避はできますが、やはり考慮すべき点ではあります。",
"ALT_SCREEN_WHY_CLEANER_TITLE": "KoalaSyncがすっきり感じられる理由",
"ALT_SCREEN_WHY_CLEANER_BODY": "KoalaSyncは画面をストリーミングしません。全員がそれぞれのブラウザでローカルに動画を視聴し、KoalaSyncは再生・一時停止・シークといった操作の同期のみを行います。<br><br>つまり、全員が自分のデバイスでフルスクリーン表示を利用したり、プレイヤーが対応していれば個別の字幕や音声設定を使ったりできるほか、DiscordやTeamSpeakなどの通話ツールで音声チャットを別に行うことも可能です。<br><br>映画鑑賞会などでは、一人が画面を配信するよりも、はるかにすっきりとした体験になります。特に、Emby、Jellyfin、Plex、ブラウザで開いたローカルファイル、プライベートドメイン、あるいは一般的なブラウザ用動画プレイヤーを備えたウェブサイトなど、ブラウザベースの環境で利用する場合に非常に便利です。",
"ALT_SCREEN_LIMITS_TITLE": "正直なところ、制限事項も",
"ALT_SCREEN_LIMITS_BODY": "KoalaSyncは音声通話の代わりになるものではありません。視聴しながら会話をする場合は、Discord、TeamSpeak、Signal、WhatsAppなど、普段お使いのツールをそのまま利用します。<br><br>また、参加者全員がそれぞれのデバイスでその動画やページにアクセスできる必要があります。KoalaSyncは、サブスクリプション、ログイン、DRM(デジタル著作権管理)、地域制限、プラットフォームの利用規約などを回避するものではありません。<br><br>現在はデスクトップ版ブラウザのみに対応しており、チャットや音声通話機能は内蔵されていません。また、動作の可否は使用するブラウザや動画プレーヤーに依存します。",
"ALT_SCREEN_SUMMARY_TITLE": "概要",
"ALT_SCREEN_SUMMARY_BODY": "画質やプライバシー、個別の操作性をあまり気にせず、手軽に済ませたい場合は「画面共有」が最も簡単な選択肢です。一方、参加者それぞれが自身のブラウザで動画を再生し、実際に同じ空間で一緒に視聴しているような感覚を重視するなら、KoalaSyncの方が適しています。",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "KoalaSyncを試す",
"MOCK_HOST_CONTROL_LABEL": "ホスト操作",
"MOCK_HOST_CONTROL_TOGGLE": "自分だけが再生を操作できる",
"LABEL_SELECT_VIDEO": "ビデオを選択",
"OPTION_SELECT_TAB": "-- タブを選択してください --",
"THEME_TOGGLE_LABEL": "ライト/ダークモード切り替え",
"FAQ_SHOW_ALL": "すべての質問を表示",
"FAQ_HIDE_ALL": "質問を隠す",
"SELF_SHOW_SETUP": "セットアップガイドを表示",
"SELF_HIDE_SETUP": "ガイドを隠す",
"HERO_TRUST": "無料 · オープンソース · アカウント不要",
"COMP_MORE_GUIDES": "詳しい比較とその他の選択肢"
}
+96 -26
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "ko_KR",
"META_TITLE": "KoalaSync | Netflix, YouTube 및 모든 비디오를 친구와 실시간 동기화 시청",
"META_DESCRIPTION": "Netflix, YouTube, Twitch 및 모든 HTML5 비디오를 친구들과 완벽하게 동기화하여 시청하세요. Chrome 및 Firefox용 무료 오픈 소스 브라우저 확장 프로그램. 가입 필요 없음.",
"SCHEMA_APP_DESC": "Netflix, YouTube, Twitch 및 모든 HTML5 비디오를 친구들과 완벽하게 동기화하여 시청하세요. Chrome 및 Firefox용 무료 오픈 소스 브라우저 확장 프로그램. 가입 필요 없음.",
"OG_TITLE": "KoalaSync | Netflix, Emby, Jellyfin 및 거의 모든 비디오를 친구와 동기화 시청",
"OG_DESCRIPTION": "Netflix, Emby, Jellyfin, YouTube, Twitch 및 거의 모든 HTML5 비디오를 완벽하게 동기화하여 시청하세요. Chrome 및 Firefox용 개인정보 보호 중심 오픈 소스 브라우저 확장 프로그램.",
"TWITTER_TITLE": "KoalaSync | Netflix, Emby, Jellyfin 및 거의 모든 비디오를 친구와 동기화 시청 – 브라우저 확장 프로그램",
"TWITTER_DESCRIPTION": "Netflix, Emby, Jellyfin, YouTube, Twitch 및 거의 모든 HTML5 비디오를 친구들과 완벽하게 동기화하여 시청하세요. Chrome 및 Firefox용 개인정보 보호 중심 오픈 소스 브라우저 확장 프로그램.",
"META_TITLE": "KoalaSync | 넷플릭스·유튜브 워치 파티 확장 프로그램",
"META_DESCRIPTION": "넷플릭스, 유튜브, 트위치 등 모든 HTML5 영상에서 워치 파티. 크롬·파이어폭스용 무료 오픈소스 확장 프로그램. 계정 없음, 추적 없음, 광고 없음.",
"SCHEMA_APP_DESC": "넷플릭스, 유튜브, 트위치 등 모든 HTML5 영상에서 워치 파티. 크롬·파이어폭스용 무료 오픈소스 확장 프로그램. 계정 없음, 추적 없음, 광고 없음.",
"OG_TITLE": "KoalaSync | Netflix, Emby, Jellyfin 및 거의 모든 영상 워치 파티",
"OG_DESCRIPTION": "Netflix, Emby, Jellyfin, YouTube, Twitch 및 거의 모든 HTML5 영상에서 워치 파티를 열어보세요. 오픈소스, 개인정보 보호 중심, 계정 불필요.",
"TWITTER_TITLE": "KoalaSync | Netflix, Emby, Jellyfin 및 거의 모든 영상 워치 파티 – 브라우저 확장 프로그램",
"TWITTER_DESCRIPTION": "Netflix, Emby, Jellyfin, YouTube, Twitch 및 거의 모든 HTML5 영상에서 워치 파티를 열어보세요. 오픈소스, 개인정보 보호 중심, 계정 불필요.",
"NAV_FEATURES": "특징",
"NAV_HOW_IT_WORKS": "작동 방식",
"HERO_TITLE": "함께 시청하세요.<span class='hero-line2'>완벽하게 동기화됩니다.</span>",
"HERO_SUBTITLE": "지연 없는 원격 영화의 밤. 회원가입도, 데이터 수집도 없습니다. 링크 공유하고 함께 시청하세요.",
"HERO_TITLE": "어떤 영상이든 <span class='hero-highlight'>워치 파티</span>로.",
"HERO_SUBTITLE": "지연 없는 원격 영화의 밤. 계정 없음, 추적 없음, 광고 없음. 링크 공유하고 함께 재생하세요.",
"HERO_MASCOT_ALT": "다운로드 버튼을 내려다보고 서 있는 귀여운 코알라",
"DEMO_SYNC": "동기화됨",
"DEMO_HINT": "라이브 데모: 아무 탭에서나 재생을 누르면 KoalaSync가 두 탭을 동기화합니다.",
"DEMO_NO_ROOM": "아직 연결되지 않음",
"DEMO_CREATE_ROOM": "방 만들기",
"DEMO_JOINED": "방 참여됨",
"DEMO_TAB_SELECTED": "탭 선택됨",
"DEMO_SELECT_PLACEHOLDER": "동영상 탭 선택…",
"MOBILE_DEMO_CTA": "작동 방식 보기",
"MOBILE_DEMO_TITLE": "KoalaSync 라이브 데모",
"MOBILE_DEMO_CLOSE": "데모 닫기",
"ADD_TO_CHROME": "Chrome에 추가",
"ADD_TO_FIREFOX": "Firefox에 추가",
"COMPAT_HEADING": "가장 좋아하는 플랫폼에서 작동합니다",
@@ -35,20 +45,20 @@
"USE_CASE_3_DESC": "거리의 한계를 넘어 데이트를 즐겨보세요. 모든 줄거리의 반전을 경험하고, 같은 농담에 웃으며, 감동적인 순간을 정확히 같은 밀리초에 공유하세요.",
"WHY_TITLE": "왜 KoalaSync인가요?",
"WHY_SUBTITLE": "신뢰할 수 있는 동기화, 개인정보 보호 중심 설계, 쉬운 설정을 위해 구축되었습니다.",
"FEATURE_1_TITLE": "완벽한 제어 / 즉시 동기화",
"FEATURE_1_DESC": "한 명이 일시정지하면 모두가 일시정지됩니다. 한 명이 탐색하면 모두가 따라갑니다. 당사의 2단계 동기화 프로토콜은 실시간으로 모든 참여자의 비디오 재생을 조율합니다.",
"FEATURE_1_TITLE": "방장 제어 동기화",
"FEATURE_1_DESC": "누가 재생을 제어할지 직접 결정하세요. 호스트 제어를 활성화하여 자신만 일시중지 및 탐색을 수행하거나 공동 호스트를 지정합니다.",
"FEATURE_2_TITLE": "끊임없는 몰아보기",
"FEATURE_2_DESC": "동기화된 자동 재생. KoalaSync는 에피소드 전환을 자동으로 감지하고 모든 참여자가 다음 비디오를 로드할 때까지 재생을 대기시킵니다.",
"FEATURE_3_TITLE": "계정 없음 / 완전한 개인정보 보호",
"FEATURE_3_DESC": "회원가입 없음, 추적 없음, 흔적을 남기지 않습니다. 서버는 휘발성 RAM에서만 완전히 실행되며 방을 나가면 영구히 삭제됩니다.",
"FEATURE_4_TITLE": "범용 HTML5 지원",
"FEATURE_4_DESC": "YouTube, Twitch, Netflix, Jellyfin, Emby HTML5 비디오 요소가 포함된 거의 모든 페이지에서 작동합니다. 사용자 정의 자체 호스팅 설정과도 호환됩니다. 비디오 데이터는 당사 서버로 절대 전송되지 않으며, 동기화 메타데이터만 전송됩니다.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby HTML5 영상이 있는 거의 모든 페이지에서 작동합니다. 브라우저를 떠나는 것은 동기화 신호뿐, 영상은 아닙니다.",
"FEATURE_5_TITLE": "자체 호스팅 가능 및 Docker 지원",
"FEATURE_5_DESC": "공식 서버는 무료이며 빠르지만, 원하는 경우 직접 호스팅할 수도 있습니다. Docker를 통해 몇 초 만에 나만의 개인 릴레이 서버를 구축해 보세요.",
"FEATURE_6_TITLE": "즉시 초대 / 클릭 한 번으로 참여",
"FEATURE_6_DESC": "IP 주소나 비밀번호를 교환할 필요가 없습니다. 생성된 초대 링크를 친구들과 공유하면 클릭 한 번으로 방에 자동으로 참여할 수 있습니다.",
"FEATURE_7_TITLE": "볼륨 레벨링 / 오디오 압축기",
"FEATURE_7_DESC": "더 이상 볼륨 노브를 계속 조정하지 마세요. 내장 오디오 압축기가 조용한 대증폭하고 큰 폭발음을 완화합니다. Netflix, Prime Video, Disney+, YouTube 및 모든 HTML5 비디오에서 작동합니다. 완전히 로컬에서 처리 — 오디오 데이터가 컴퓨터를 떠나지 않습니다.",
"FEATURE_7_DESC": "더 이상 볼륨을 조절할 필요 없습니다. 내장 압축기가 조용한 대키우고 큰 폭발음을 완화하며, 모든 처리는 브라우저에서 로컬로 이루어집니다.",
"FEATURE_8_TITLE": "오픈 소스 (MIT 라이선스)",
"FEATURE_8_DESC": "모든 코드베이스는 GitHub에서 공개적으로 검토할 수 있습니다. 블랙박스나 독점적인 블롭(blob)이 전혀 없으며, 릴레이 서버부터 브라우저 확장 프로그램까지 100% 투명성을 보장합니다.",
"COMP_TITLE": "KoalaSync 대 Teleparty",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Teleparty 개인정보 처리방침 및 추적 데이터 수집 정보:",
"COMP_FOOTNOTE_4": "표준 HTML5 비디오 태그에 스크립트 삽입을 허용하는 웹사이트에서 작동합니다. 콘텐츠 보안 정책(CSP)이 매우 엄격하거나, DRM 복사 방지가 적용되었거나, 플레이어 래퍼가 복잡하게 난독화된 웹사이트(예: 복잡한 섀도 DOM)에서는 자동 제어 또는 스크립트 삽입이 제한될 수 있습니다.",
"STEPS_TITLE": "시작하기",
"STEP_1_TITLE": "확장 프로그램 설치",
"STEP_1_DESC": "Chrome 웹 스토어, Firefox 부가 기능에서 KoalaSync를 브라우저에 추가하거나, GitHub에서 최신 개발자용 ZIP 파일을 다운로드하세요.",
"STEP_1_ILLUS_DESC": "개인정보 보호 중심의 비디오 동기화 프로그램",
"STEP_1_ILLUS_DL_CHROME": "Chrome용 다운로드",
"STEP_1_ILLUS_DL_FIREFOX": "Firefox용 다운로드",
"STEP_1_ILLUS_ACTIVE": "확장 프로그램 활성화됨",
"STEP_1_ILLUS_READY": "함께 시청 준비가 되었습니다!",
"STEP_2_TITLE": "방 만들기",
"STEP_2_DESC": "확장 프로그램 팝업을 열고 '+ 새 방 만들기'를 클릭하세요. KoalaSync가 안전한 방 ID와 비밀번호를 자동으로 생성하고, 참여한 후 초대 링크를 클립보드에 복사합니다.",
"STEP_1_TITLE": "방 만들기 및 공유",
"STEP_1_DESC": "확장 프로그램을 열고 클릭 한 번으로 방을 만든 후 링크를 공유하세요. 친구들이 초대 링크를 통해 바로 참여합니다.",
"STEP_1_ILLUS_DESC": "연결할 탭 선택",
"STEP_1_ILLUS_DL_CHROME": "YouTube 탭 연결",
"STEP_1_ILLUS_DL_FIREFOX": "Netflix 탭 연결",
"STEP_1_ILLUS_ACTIVE": "탭 연결됨",
"STEP_1_ILLUS_READY": "동기화 시청 준비 완료 !",
"STEP_2_TITLE": "동영상 탭 선택",
"STEP_2_DESC": "동영상이 열려 있는 탭으로 이동하여 확장 프로그램 메뉴에서 해당 탭을 선택하세요. KoalaSync가 플레이어를 자동으로 연결합니다.",
"STEP_2_ILLUS_ROOM": "방",
"STEP_2_ILLUS_SYNC": "동기화",
"STEP_2_ILLUS_SETTINGS": "설정",
"STEP_2_ILLUS_CREATE": "+ 새 방 만들기",
"STEP_2_ILLUS_MANUAL": "수동 연결 / 고급",
"STEP_2_ILLUS_COPIED": "초대 링크가 복사되었습니다!",
"STEP_3_TITLE": "공유 및 동기화",
"STEP_3_DESC": "친구들에게 초대 링크를 보내세요. 친구들이 참여하면 비디오 탭을 선택하고 동기화된 재생을 즐기세요.",
"STEP_3_TITLE": "동기화 즐기기",
"STEP_3_DESC": "재생을 누르세요. 누군가 일시중지, 탐색 또는 재생을 하더라도 모든 사람의 영상이 지연 없이 실시간으로 동기화됩니다.",
"STEP_3_ILLUS_IN_SYNC": "동기화됨",
"SELF_TITLE": "자체 호스팅 사용자를 위한 안내",
"SELF_SUBTITLE": "공식 서버를 신뢰하기 어려우신가요? 완벽한 데이터 주권을 유지하세요. 몇 분 만에 나만의 개인 릴레이 서버를 구축할 수 있습니다.",
@@ -117,6 +127,8 @@
"FAQ_SUBTITLE": "완벽한 동기화로 비디오를 함께 시청하는 방법에 대해 알아야 할 모든 것.",
"FAQ_Q1": "친구들과 Netflix, Emby 또는 Jellyfin을 함께 동기화하여 시청할 수 있나요?",
"FAQ_A1": "네! KoalaSync는 Netflix, YouTube, Twitch 및 HTML5 비디오 플레이어가 있는 모든 웹사이트(Emby, Jellyfin 및 자체 호스팅 미디어 서버 포함)에서 작동합니다. 확장 프로그램을 설치하고 방을 만든 후 초대 링크를 공유하면 모두가 완벽하게 동기화되어 시청할 수 있습니다.",
"FAQ_Q_VIDEO_ACCESS": "KoalaSync가 친구들에게 동영상을 스트리밍하나요?",
"FAQ_A_VIDEO_ACCESS": "아니요. KoalaSync는 동영상 콘텐츠를 스트리밍, 호스팅, 공유하거나 접근 권한을 우회하지 않습니다. 모든 참가자는 각자의 브라우저에서 동영상을 로컬로 시청하며, Netflix 계정처럼 해당 웹사이트나 스트리밍 서비스에 대한 본인만의 접근 권한이 필요합니다. KoalaSync는 재생, 일시정지, 탐색 같은 재생 동작만 동기화합니다.",
"FAQ_Q2": "KoalaSync는 Teleparty의 무료 대체 서비스인가요?",
"FAQ_A2": "MIT 라이선스에 따라 100% 무료이며 오픈 소스입니다. 프리미ウム 등급도, 제한된 기능도, 광고도 없습니다. 모든 것이 포함되어 있습니다. Teleparty와 달리 KoalaSync의 릴레이 서버는 가입이나 개인정보 수집 없이 RAM에서만 실행됩니다.",
"FAQ_Q3": "KoalaSync는 Emby, Jellyfin 및 자체 호스팅 미디어 서버에서 작동하나요?",
@@ -156,7 +168,7 @@
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SYNC",
"MOCK_14": "다른 사람에게 이동",
"MOCK_15": "최근 활동 상태",
"MOCK_16": "참여자 1명 (KoalaPC) 대기 중...",
"MOCK_16": "참여자 1명 (🐶 HappyDog) 대기 중...",
"MOCK_17": "건너뛰고 재생",
"MOCK_18": "사용자 이름",
"MOCK_19": "복잡한 탭 숨기기",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSync는 Netflix, Disney+, Amazon, YouTube, Twitch 또는 기타 스트리밍 플랫폼과 제휴, 보증 또는 연관되어 있지 않습니다. 모든 상표는 해당 소유자의 재산입니다.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - 대안 및 비교",
"ALT_INDEX_META_DESC": "KoalaSync와 다른 워치 파티 확장 프로그램 및 스트리밍 솔루션을 솔직한 비교와 자세한 가이드로 살펴보세요.",
"ALT_INDEX_HEADING": "가이드 및 비교",
"ALT_INDEX_INTRO": "KoalaSync는 다른 워치 파티 도구와 스트리밍 솔루션에 비해 어떤가요? 원격 영화 감상에 가장 잘 맞는 선택지를 찾을 수 있도록 솔직하고 기술적인 비교를 읽어보세요.",
"ALT_INDEX_READ_COMPARISON": "비교 읽기 →",
"ALT_INDEX_READ_GUIDE": "가이드 읽기 →",
"ALT_INDEX_FOOTER_LINK": "가이드 및 비교",
"ALT_TELEPARTY_TITLE": "KoalaSync 대 Teleparty: 어떤 '워치 파티(watch party)' 도구를 선택해야 할까요?",
"ALT_TELEPARTY_META_DESC": "워치 파티를 위한 KoalaSync와 Teleparty의 실용적 비교: 내장 채팅, 모바일 지원, 브라우저 영상 동기화, 자체 호스팅 미디어 서버 및 오픈 소스 활용 여부 등을 다룹니다.",
"ALT_TELEPARTY_INTRO": "KoalaSync와 Teleparty는 모두 '3, 2, 1, 재생' 같은 번거로운 절차 없이 함께 영상을 시청할 수 있게 해준다는 점에서 같은 문제를 해결해 줍니다. 다만 접근 방식에는 차이가 있습니다.<br><br>내장 채팅 기능이 필요하거나 모바일에서 시청하고 싶다면 Teleparty가 가장 합리적인 선택입니다. 반면 KoalaSync는 Discord, Teamspeak 또는 기타 음성 통화 서비스를 통해 이미 대화를 나누고 있으면서, 단순히 영상 재생만 동기화하고 싶은 데스크톱 브라우저 기반의 워치 파티에 더 적합합니다.<br><br>제가 KoalaSync를 개발한 주된 이유는 유연성 때문입니다. 특정 플랫폼 목록에만 국한되지 않고, 일반적인 브라우저 비디오 플레이어를 사용하는 거의 모든 웹사이트에서 작동하도록 설계되었습니다. 여기에는 Emby, Jellyfin, Plex와 같은 자체 호스팅 환경, 로컬/비공개 도메인, 그리고 브라우저상의 수많은 일반 동영상 페이지가 포함됩니다.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Teleparty가 여전히 좋은 선택이 될 수 있는 경우",
"ALT_TELEPARTY_WHEN_TELE_BODY": "워치 파티 도구 자체에 채팅 기능이 포함되기를 원한다면 Teleparty가 더 나은 선택일 수 있습니다. KoalaSync에는 현재 내장 채팅 기능이 없습니다. Discord, Teamspeak, WhatsApp, Signal 또는 일반 통화 등 다른 수단을 통해 이미 대화하고 있다는 것을 전제로 하기 때문입니다.<br><br>모바일 지원 여부도 중요한 이유 중 하나입니다. Teleparty는 전용 앱을 제공하지만, KoalaSync는 현재 데스크톱 브라우저에서만 작동합니다. 스마트폰이나 태블릿에서 Netflix 영상을 동기화하고 싶다면, 현재로서는 KoalaSync가 적합한 도구가 아닙니다.<br><br>따라서 채팅과 모바일 지원이 가장 중요한 요소라면, Teleparty가 여러분의 환경에 더 잘 맞을 것입니다.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "KoalaSync가 더 적합한 경우",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "지원 플랫폼이 제한적인 도구 대신 유연한 데스크톱 브라우저 동기화 도구를 원하신다면 KoalaSync가 더 나은 선택입니다.<br><br>일반적인 브라우저 비디오 플레이어를 사용하는 거의 모든 웹사이트에서 작동합니다. 이는 특히 Emby, Jellyfin, Plex, 개인 미디어 서버, 로컬 도메인 또는 기존 '워치 파티(watch party)' 도구들이 흔히 지원하지 않는 소규모 동영상 사이트에서 유용합니다.<br><br>또한 무료이자 오픈 소스이며, 계정 생성도 필요하지 않습니다. 직접 릴레이 서버를 운영하고 싶다면 자체 호스팅도 가능합니다. 저는 소수의 대형 스트리밍 사이트만을 위한 폐쇄적인 플랫폼이 아니라, 제 Emby 환경에서도 잘 작동하면서 개인정보 보호를 중시하는 도구를 만들고 싶어 이 프로그램을 개발했습니다.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "주요 차이점",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "Teleparty는 채팅과 모바일 지원 기능을 갖춘 '워치 파티' 서비스에 가깝습니다. 반면 KoalaSync는 브라우저 비디오를 위한 유연한 동기화 도구(레이어)에 가깝습니다.<br><br>사소한 차이처럼 들릴 수 있지만, 실제 사용 방식에는 큰 차이가 있습니다. 채팅 기능이 포함된 종합적인 워치 파티 앱이 필요하다면 Teleparty가 적합합니다. 하지만 이미 음성 채팅 수단이 있고 단순히 브라우저 간 비디오 재생 상태만 동기화하고 싶다면, 더 간편한 KoalaSync가 알맞은 선택입니다.",
"ALT_TELEPARTY_LIMITS_TITLE": "솔직하게 밝히는 한계점",
"ALT_TELEPARTY_LIMITS_BODY": "KoalaSync는 마법 같은 도구가 아니며, 그 한계를 숨기지도 않습니다. 현재 데스크톱 브라우저에서만 작동하며, 자체 채팅 기능이나 모바일 앱은 없습니다.<br><br>호환성은 브라우저와 비디오 플레이어의 영향을 받습니다. KoalaSync는 일반적인 브라우저 비디오 플레이어라면 대부분 작동하지만, 웹사이트 측에서 플레이어, DRM 방식, 또는 브라우저 관련 제한 사항을 언제든지 변경할 수 있습니다.<br><br>KoalaSync는 Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex 또는 여기에 언급된 다른 어떤 브랜드와도 제휴 관계가 아닙니다. 해당 명칭들은 단지 일반적인 사용 사례를 설명하기 위해 사용되었습니다.",
"ALT_TELEPARTY_SUMMARY_TITLE": "요약",
"ALT_TELEPARTY_SUMMARY_BODY": "내장 채팅이나 모바일 지원 기능이 필요하다면 Teleparty가 더 적합할 수 있습니다. 반면, Emby, Jellyfin, Plex와 같은 자체 호스팅 미디어 서버를 포함해 거의 모든 일반적인 동영상 플레이어와 호환되는 무료 오픈 소스 데스크톱 브라우저 동기화 도구를 원하신다면 KoalaSync가 더 나은 선택일 것입니다.",
"ALT_SCREEN_TITLE": "KoalaSync 대 화면 공유: 동영상 동기화가 더 나은 경우는 언제일까요?",
"ALT_SCREEN_META_DESC": "원격 영화 감상, 재생 제어, 화질, 개인정보 보호, 자막 및 브라우저 기반 시청 측면에서 KoalaSync와 화면 공유를 실용적으로 비교해 봅니다.",
"ALT_SCREEN_INTRO": "화면 공유도 훌륭한 방법이며, 솔직히 때로는 그것만으로도 충분합니다. 한 사람이 화면을 공유하고 나머지는 시청하기만 하면 되니까요.<br><br>하지만 긴 영화를 보거나 여러 에피소드를 함께 시청할 때는 다소 번거로울 수 있습니다. 화질은 화면을 공유하는 사람의 업로드 속도, CPU, 브라우저, 그리고 Discord, Teamspeak, Zoom 등의 통화 앱이 적용하는 압축 방식에 따라 달라집니다. 또한, 재생을 실제로 제어할 수 있는 사람도 한 명뿐입니다.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "화면 공유로 충분한 경우",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "가장 빠르게 준비를 마치고 싶을 때는 화면 공유가 좋은 선택입니다. 만약 한 사람만 동영상에 접근할 수 있거나...\"또는 한 사람이 재생, 일시 정지, 탐색(seek)을 제어하는 방식에 모두가 동의한다면, 이것이 가장 간단한 선택지가 될 수 있습니다.<br><br>또한 화질이 크게 중요하지 않거나, 개별적인 자막이나 오디오 설정이 필요 없으며, 화면을 공유하는 사람이 자신의 브라우저 창이나 화면을 보여주는 데 거부감이 없는 경우에도 합리적인 방법입니다.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "화면 공유가 번거로울 수 있는 상황",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "가장 큰 단점은 각자의 플레이어로 영상을 시청하는 것이 아니라는 점입니다. 한 사람이 자신의 화면을 스트리밍하면, 다른 사람들은 그 스트리밍 화면만 보게 됩니다.<br><br>이로 인해 화질 저하, 오디오 압축, 재생 지연이 발생할 수 있으며, 다른 사람들의 제어 권한도 제한됩니다. 자막이나 오디오 트랙 또한 화면 공유자가 선택한 설정으로 고정됩니다.<br><br>개인정보 보호 문제도 있습니다. 창이나 탭을 전환할 때 브라우저 탭, 이메일, Reddit, 채팅 내용 등 사적인 정보가 일시적으로 노출될 수 있습니다. 별도의 창을 사용하여 이를 피할 수는 있지만, 여전히 고려해야 할 사항입니다.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "KoalaSync가 더 깔끔하게 느껴지는 이유",
"ALT_SCREEN_WHY_CLEANER_BODY": "KoalaSync는 화면을 직접 스트리밍하지 않습니다. 각자 자신의 브라우저에서 영상을 시청하며, KoalaSync는 재생, 일시 정지, 탐색과 같은 재생 관련 동작만 동기화합니다.<br><br>따라서 각자 자신의 기기에서 전체 화면으로 시청하거나, 플레이어가 지원하는 경우 개별 자막 및 오디오 설정을 사용할 수 있으며, 음성 채팅은 Discord, Teamspeak 또는 기타 통화 서비스를 통해 별도로 진행할 수 있습니다.<br><br>영화 감상 모임의 경우, 한 사람이 화면을 송출하는 방식보다 훨씬 깔끔한 경험을 제공합니다. 특히 Emby, Jellyfin, Plex와 같은 브라우저 기반 서비스나 브라우저에서 연 로컬 파일, 비공개 도메인, 또는 일반적인 브라우저 비디오 플레이어를 사용하는 웹사이트에서 활용하기에 매우 유용합니다.",
"ALT_SCREEN_LIMITS_TITLE": "솔직한 한계점",
"ALT_SCREEN_LIMITS_BODY": "KoalaSync는 음성 통화를 대체하는 서비스가 아닙니다. 영상을 보면서 대화하고 싶다면 평소처럼 Discord, Teamspeak, Signal, WhatsApp 등을 그대로 사용하면 됩니다.<br><br>또한 모든 참가자가 각자의 기기에서 해당 영상이나 페이지에 접속할 수 있어야 합니다. KoalaSync는 구독, 로그인, DRM, 지역 제한 또는 플랫폼 규정을 우회하지 않습니다.<br><br>현재 데스크톱 브라우저에서만 지원되며, 자체 채팅이나 음성 기능은 없습니다. 호환성은 항상 브라우저 및 동영상 플레이어의 영향을 받습니다.",
"ALT_SCREEN_SUMMARY_TITLE": "요약",
"ALT_SCREEN_SUMMARY_BODY": "화질, 개인정보 보호, 개별 제어 기능 등을 크게 신경 쓰지 않고 간편하게 진행하고 싶을 때는 화면 공유가 가장 간단한 방법입니다. 반면, 모든 참가자가 각자의 브라우저에서 직접 영상을 시청하며 실제로 함께 보는 듯한 느낌을 원한다면 KoalaSync가 더 적합합니다.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "KoalaSync 사용해 보기",
"MOCK_HOST_CONTROL_LABEL": "호스트 제어",
"MOCK_HOST_CONTROL_TOGGLE": "나만 재생을 제어할 수 있음",
"LABEL_SELECT_VIDEO": "비디오 선택",
"OPTION_SELECT_TAB": "-- 탭 선택 --",
"THEME_TOGGLE_LABEL": "라이트/다크 모드 전환",
"FAQ_SHOW_ALL": "모든 질문 보기",
"FAQ_HIDE_ALL": "질문 숨기기",
"SELF_SHOW_SETUP": "설치 가이드 보기",
"SELF_HIDE_SETUP": "가이드 숨기기",
"HERO_TRUST": "무료 · 오픈소스 · 계정 불필요",
"COMP_MORE_GUIDES": "전체 비교 및 다른 대안 보기"
}
+102 -32
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "nl_NL",
"META_TITLE": "KoalaSync | Synchroniseer Netflix, YouTube & elke video met vrienden",
"META_DESCRIPTION": "Bekijk Netflix, YouTube, Twitch en elke HTML5-video in perfecte synchronisatie met vrienden. Gratis, open-source browserextensie voor Chrome en Firefox. Geen registratie nodig.",
"SCHEMA_APP_DESC": "Bekijk Netflix, YouTube, Twitch en elke HTML5-video in perfecte synchronisatie met vrienden. Gratis, open-source browserextensie voor Chrome en Firefox. Geen registratie nodig.",
"OG_TITLE": "KoalaSync | Synchroniseer Netflix, Emby, Jellyfin & bijna elke video met vrienden",
"OG_DESCRIPTION": "Bekijk Netflix, Emby, Jellyfin, YouTube, Twitch en bijna elke HTML5-video in perfecte synchronisatie. Privacy-first, open-source browserextensie voor Chrome & Firefox.",
"TWITTER_TITLE": "KoalaSync | Synchroniseer Netflix, Emby, Jellyfin & bijna elke video met vrienden Browserextensie",
"TWITTER_DESCRIPTION": "Bekijk Netflix, Emby, Jellyfin, YouTube, Twitch en bijna elke HTML5-video in perfecte synchronisatie met vrienden. Privacy-first, open-source browserextensie voor Chrome en Firefox.",
"META_TITLE": "KoalaSync | Watch party voor Netflix, YouTube & elke video",
"META_DESCRIPTION": "Watch party op Netflix, YouTube, Twitch en elke HTML5-video. Gratis open-source extensie voor Chrome en Firefox. Geen account, geen tracking, geen reclame.",
"SCHEMA_APP_DESC": "Watch party op Netflix, YouTube, Twitch en elke HTML5-video. Gratis open-source extensie voor Chrome en Firefox. Geen account, geen tracking, geen reclame.",
"OG_TITLE": "KoalaSync | Watch party voor Netflix, Emby, Jellyfin en bijna elke video",
"OG_DESCRIPTION": "Start een watch party op Netflix, Emby, Jellyfin, YouTube, Twitch en bijna elke HTML5-video. Open source, privacyvriendelijk en zonder account.",
"TWITTER_TITLE": "KoalaSync | Watch party voor Netflix, Emby, Jellyfin en bijna elke video Browserextensie",
"TWITTER_DESCRIPTION": "Start een watch party op Netflix, Emby, Jellyfin, YouTube, Twitch en bijna elke HTML5-video. Open source, privacyvriendelijk en zonder account.",
"NAV_FEATURES": "Functies",
"NAV_HOW_IT_WORKS": "Hoe het werkt",
"HERO_TITLE": "Samen Kijken.<span class='hero-line2'>Perfect Synchroniseren.</span>",
"HERO_SUBTITLE": "Uw filmavond op afstand zonder vertraging. Geen registratie, geen gegevensverzameling. Deel gewoon een link en kijk samen.",
"HERO_TITLE": "Maak van elke video een <span class='hero-highlight'>watch party</span>.",
"HERO_SUBTITLE": "Jouw filmavond op afstand, zonder vertraging. Geen account, geen tracking, geen advertenties. Deel een link en druk samen op play.",
"HERO_MASCOT_ALT": "Een schattige koala die staat en naar beneden kijkt naar de downloadknoppen",
"DEMO_SYNC": "Synchroon",
"DEMO_HINT": "Live demo: druk op play in een van de tabbladen en KoalaSync houdt beide synchroon.",
"DEMO_NO_ROOM": "Nog niet verbonden",
"DEMO_CREATE_ROOM": "Kamer aanmaken",
"DEMO_JOINED": "In de kamer",
"DEMO_TAB_SELECTED": "Tabblad geselecteerd",
"DEMO_SELECT_PLACEHOLDER": "Kies een videotabblad…",
"MOBILE_DEMO_CTA": "Bekijk hoe het werkt",
"MOBILE_DEMO_TITLE": "KoalaSync live demo",
"MOBILE_DEMO_CLOSE": "Demo sluiten",
"ADD_TO_CHROME": "Toevoegen aan Chrome",
"ADD_TO_FIREFOX": "Toevoegen aan Firefox",
"COMPAT_HEADING": "Werkt op uw favoriete platforms",
@@ -35,20 +45,20 @@
"USE_CASE_3_DESC": "Overbrug de afstand en geniet van date nights. Ervaar elke plotwending, lach om dezelfde grappen en deel emotionele momenten op exact dezelfde milliseconde.",
"WHY_TITLE": "Waarom KoalaSync?",
"WHY_SUBTITLE": "Gebouwd voor betrouwbare synchronisatie, privacy-first ontwerp en eenvoudige installatie.",
"FEATURE_1_TITLE": "Volledige controle / Directe synchronisatie",
"FEATURE_1_DESC": "Eén pauzeert, iedereen pauzeert. Eén spoelt, iedereen volgt. Ons tweefasige synchronisatieprotocol coördineert het afspelen in realtime voor alle deelnemers.",
"FEATURE_1_TITLE": "Host-controle & synchronisatie",
"FEATURE_1_DESC": "Jij bepaalt wie het afspelen bedient. Schakel hostbeheer in zodat alleen jij kunt pauzeren en spoelen, of wijs co-hosts aan.",
"FEATURE_2_TITLE": "Eindeloos binge-watchen",
"FEATURE_2_DESC": "Automatisch afspelen in sync. KoalaSync detecteert automatisch afleveringsovergangen en houdt het afspelen vast totdat alle deelnemers de volgende video succesvol hebben geladen.",
"FEATURE_3_TITLE": "Geen accounts / Pure privacy",
"FEATURE_3_DESC": "Geen registratie, geen tracking, geen permanente voetafdruk. De server draait volledig in het tijdelijke RAM en wist uw kamer wanneer u vertrekt.",
"FEATURE_4_TITLE": "Universele HTML5-ondersteuning",
"FEATURE_4_DESC": "Werkt op YouTube, Twitch, Netflix, Jellyfin, Emby en bijna elke standaard webpagina met een HTML5-video-element. Ook compatibel met aangepaste zelfgehoste installaties. Er worden nooit videogegevens naar onze servers verzonden — alleen synchronisatiemetagegevens.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby en vrijwel elke pagina met HTML5-video. Alleen sync-signalen verlaten je browser, nooit de video.",
"FEATURE_5_TITLE": "Zelf te hosten & Docker-ready",
"FEATURE_5_DESC": "Onze officiële servers zijn gratis en snel, maar u kunt de volledige controle nemen als u dat wilt. Start uw eigen privé-relayserver in seconden via Docker.",
"FEATURE_6_TITLE": "Directe uitnodigingen / 1-klik deelname",
"FEATURE_6_DESC": "Geen IP-adressen of wachtwoorden om uit te wisselen. Deel een gegenereerde uitnodigingslink met uw vrienden om ze met een enkele klik automatisch deel te laten nemen.",
"FEATURE_7_TITLE": "Volume egaliseren / Audiocompressor",
"FEATURE_7_DESC": "Niet langer aan de volumeknop zitten. De ingebouwde audiocompressor versterkt stille dialogen en dempt harde explosies. Werkt op Netflix, Prime Video, Disney+, YouTube en elke HTML5-video. Volledig lokaal — geen audiogegevens verlaten uw computer.",
"FEATURE_7_DESC": "Nooit meer aan de volumeknop draaien. Een ingebouwde compressor tilt stille dialogen op en temt luide explosies, allemaal lokaal in je browser verwerkt.",
"FEATURE_8_TITLE": "Open Source (MIT License)",
"FEATURE_8_DESC": "De volledige codebase is openbaar controleerbaar op GitHub. Geen zwarte dozen, geen propri?taire blobs - 100% transparantie van de relayserver tot de browserextensie.",
"COMP_TITLE": "KoalaSync vs. Teleparty",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Teleparty privacybeleid en trackinggegevensverzameling:",
"COMP_FOOTNOTE_4": "Werkt op websites die scriptinjecties in standaard HTML5-videotags toestaan. Websites met een zeer strikt Content Security Policy (CSP), DRM-kopieerbeveiliging of sterk versluierde spelers (zoals complexe shadow-DOM's) kunnen automatische controle of injectie beperken.",
"STEPS_TITLE": "Aan de slag",
"STEP_1_TITLE": "Extensie installeren",
"STEP_1_DESC": "Voeg KoalaSync toe aan uw browser vanuit de Chrome Web Store, Firefox-add-ons, of download de nieuwste ontwikkelaars-ZIP van GitHub.",
"STEP_1_ILLUS_DESC": "Privacy-first videosynchronisator",
"STEP_1_ILLUS_DL_CHROME": "Downloaden voor Chrome",
"STEP_1_ILLUS_DL_FIREFOX": "Downloaden voor Firefox",
"STEP_1_ILLUS_ACTIVE": "Extensie actief",
"STEP_1_ILLUS_READY": "Klaar om samen te kijken!",
"STEP_2_TITLE": "Kamer maken",
"STEP_2_DESC": "Open de extensie-popup en klik op '+ Nieuwe kamer maken'. KoalaSync genereert automatisch een beveiligde Kamer-ID en Wachtwoord, neemt deel en kopieert de uitnodigingslink naar uw klembord.",
"STEP_1_TITLE": "Kamer maken en delen",
"STEP_1_DESC": "Open de extensie, maak een kamer met één klik en deel de link met je vrienden. Ze nemen direct deel via de uitnodigingslink.",
"STEP_1_ILLUS_DESC": "Kies een tabblad om te koppelen",
"STEP_1_ILLUS_DL_CHROME": "YouTube-tabblad koppelen",
"STEP_1_ILLUS_DL_FIREFOX": "Netflix-tabblad koppelen",
"STEP_1_ILLUS_ACTIVE": "Tabblad gekoppeld",
"STEP_1_ILLUS_READY": "Klaar voor synchroon kijken !",
"STEP_2_TITLE": "Videotabblad selecteren",
"STEP_2_DESC": "Ga naar je videotabblad en selecteer dit in het menu van de extensie. KoalaSync koppelt vanaf nu automatisch jullie spelers.",
"STEP_2_ILLUS_ROOM": "Kamer",
"STEP_2_ILLUS_SYNC": "Sync",
"STEP_2_ILLUS_SETTINGS": "Instellingen",
"STEP_2_ILLUS_CREATE": "+ Nieuwe kamer maken",
"STEP_2_ILLUS_MANUAL": "Handmatig verbinden / Geavanceerd",
"STEP_2_ILLUS_COPIED": "Uitnodigingslink gekopieerd!",
"STEP_3_TITLE": "Delen & Synchroniseren",
"STEP_3_DESC": "Stuur de uitnodigingslink naar uw vrienden. Zodra ze deelnemen, selecteert u uw videotabblad en geniet u van gesynchroniseerd afspelen.",
"STEP_3_TITLE": "Genieten van de synchronisatie",
"STEP_3_DESC": "Druk op afspelen. Of iemand nu pauzeert, spoelt of afspeelt, de video van iedereen blijft perfect en zonder vertraging synchroon.",
"STEP_3_ILLUS_IN_SYNC": "IN SYNC",
"SELF_TITLE": "Voor zelf-hosters",
"SELF_SUBTITLE": "Vertrouwt u onze server niet? Behoud de volledige soevereiniteit over uw gegevens. Implementeer uw eigen privé-relayserver in enkele minuten.",
@@ -116,19 +126,21 @@
"FAQ_TITLE": "Veelgestelde vragen",
"FAQ_SUBTITLE": "Alles wat u moet weten over het samen bekijken van video's in perfecte synchronisatie.",
"FAQ_Q1": "Kan ik Netflix, Emby of Jellyfin samen in sync kijken met vrienden?",
"FAQ_A1": "Ja! KoalaSync werkt op Netflix, YouTube, Twitch en elke website met een HTML5-videospeler inclusief Emby, Jellyfin en zelfgehoste mediaservers. Installeer de extensie, maak een kamer, deel de uitnodigingslink en iedereen kijkt in perfecte sync.",
"FAQ_A1": "Ja! KoalaSync werkt op Netflix, YouTube, Twitch en elke website met een HTML5-videospeler, inclusief Emby, Jellyfin en zelfgehoste mediaservers. Installeer de extensie, maak een kamer, deel de uitnodigingslink en iedereen kijkt in perfecte sync.",
"FAQ_Q_VIDEO_ACCESS": "Streamt KoalaSync de video naar mijn vrienden?",
"FAQ_A_VIDEO_ACCESS": "Nee. KoalaSync streamt, host of deelt geen videocontent en omzeilt geen toegang tot videos. Iedereen kijkt de video lokaal in de eigen browser en heeft zelf toegang nodig tot de website of streamingdienst, bijvoorbeeld een eigen Netflix-account. KoalaSync synchroniseert alleen afspeelacties zoals afspelen, pauzeren en spoelen.",
"FAQ_Q2": "Is KoalaSync een gratis alternatief voor Teleparty?",
"FAQ_A2": "100% gratis en open-source onder de MIT-licentie. Geen premium-niveaus, geen vergrendelde functies, geen advertenties alles is inbegrepen. In tegenstelling tot Teleparty is de relayserver van KoalaSync alleen in het RAM-geheugen met nul gegevensverzameling of gebruikersaccounts vereist.",
"FAQ_A2": "100% gratis en open-source onder de MIT-licentie. Geen premium-niveaus, geen vergrendelde functies, geen advertenties, alles is inbegrepen. In tegenstelling tot Teleparty is de relayserver van KoalaSync alleen in het RAM-geheugen met nul gegevensverzameling of gebruikersaccounts vereist.",
"FAQ_Q3": "Werkt KoalaSync met Emby, Jellyfin en zelfgehoste mediaservers?",
"FAQ_A3": "Ja. KoalaSync detecteert elk standaard HTML5-video-element, wat betekent dat het perfect werkt met Emby, Jellyfin, Plex en elke aangepaste zelfgehoste streaminginstallatie. U kunt de KoalaSync-relayserver ook zelf hosten via Docker voor volledige soevereiniteit over uw gegevens.",
"FAQ_Q4": "Welke browsers en platforms ondersteunen KoalaSync?",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave en alle op Chromium gebaseerde browsers. Installeer het rechtstreeks vanuit de Chrome Web Store of Firefox Add-ons geen handmatige configuratie nodig.",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave en alle op Chromium gebaseerde browsers. Installeer het rechtstreeks vanuit de Chrome Web Store of Firefox Add-ons, geen handmatige configuratie nodig.",
"FAQ_Q5": "Moeten al mijn vrienden de extensie geïnstalleerd hebben?",
"FAQ_A5": "Ja, elke deelnemer moet KoalaSync geïnstalleerd hebben maar er is geen aanmelding of accountcreatie. Klik gewoon op de uitnodigingslink, installeer de extensie als u dat nog niet hebt gedaan, en u neemt binnen enkele seconden automatisch deel aan de kamer.",
"FAQ_A5": "Ja, elke deelnemer moet KoalaSync geïnstalleerd hebben, maar er is geen aanmelding of accountcreatie. Klik gewoon op de uitnodigingslink, installeer de extensie als u dat nog niet hebt gedaan, en u neemt binnen enkele seconden automatisch deel aan de kamer.",
"FAQ_Q6": "Zijn mijn gegevens veilig? Wat als ik de officiële server niet vertrouw?",
"FAQ_A6": "Volledig veilig. De relayserver van KoalaSync is alleen in het RAM-geheugen alle sessiegegevens worden permanent verwijderd zodra u de kamer verlaat. Geen database, geen logboeken, geen analyses, geen accounts. En als u de voorkeur geeft aan volledige controle: u kunt uw eigen privé-relayserver binnen enkele minuten zelf hosten via Docker. De volledige codebase is open-source en auditeerbaar op GitHub.",
"FAQ_A6": "Volledig veilig. De relayserver van KoalaSync is alleen in het RAM-geheugen, alle sessiegegevens worden permanent verwijderd zodra u de kamer verlaat. Geen database, geen logboeken, geen analyses, geen accounts. En als u de voorkeur geeft aan volledige controle: u kunt uw eigen privé-relayserver binnen enkele minuten zelf hosten via Docker. De volledige codebase is open-source en auditeerbaar op GitHub.",
"FAQ_Q7": "Kan ik stille dialogen en harde explosies oplossen bij het kijken van video's in de browser?",
"FAQ_A7": "Ja. KoalaSync 2.2.0 bevat een ingebouwde audiocompressor die het verschil tussen stille en luidruchtige delen van uw video verkleint. Het werkt lokaal via de Web Audio API geen audiogegevens verlaten ooit uw computer. Open de popup, maak een kamer, ga naar Instellingen → Audio Processing, schakel de compressor in en kies een preset. Werkt op Netflix, Prime Video, Disney+, YouTube en elke HTML5-videosite.",
"FAQ_A7": "Ja. KoalaSync 2.2.0 bevat een ingebouwde audiocompressor die het verschil tussen stille en luidruchtige delen van uw video verkleint. Het werkt lokaal via de Web Audio API, geen audiogegevens verlaten ooit uw computer. Open de popup, maak een kamer, ga naar Instellingen → Audio Processing, schakel de compressor in en kies een preset. Werkt op Netflix, Prime Video, Disney+, YouTube en elke HTML5-videosite.",
"HOWTO_TITLE": "Hoe video's te synchroniseren met vrienden via KoalaSync",
"HOWTO_DESC": "Bekijk elke video in perfecte synchronisatie met vrienden op YouTube, Netflix, Twitch en meer. Geen aanmelding, geen tracking.",
"HOWTO_STEP_1_NAME": "Installeer de browserextensie",
@@ -156,7 +168,7 @@
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SYNC",
"MOCK_14": "Spring naar anderen",
"MOCK_15": "Laatste activiteitsstatus",
"MOCK_16": "Wachten op 1 deelnemer (KoalaPC)...",
"MOCK_16": "Wachten op 1 deelnemer (🐶 HappyDog)...",
"MOCK_17": "Overslaan & toch afspelen",
"MOCK_18": "Uw gebruikersnaam",
"MOCK_19": "Overbodige tabbladen verbergen",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSync is niet verbonden aan, goedgekeurd door of geassocieerd met Netflix, Disney+, Amazon, YouTube, Twitch of enig ander streamingplatform. Alle handelsmerken zijn eigendom van hun respectieve eigenaren.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - Alternatieven en vergelijkingen",
"ALT_INDEX_META_DESC": "Ontdek eerlijke vergelijkingen en gedetailleerde gidsen tussen KoalaSync en andere watchparty-extensies en streamingoplossingen.",
"ALT_INDEX_HEADING": "Gidsen en vergelijkingen",
"ALT_INDEX_INTRO": "Hoe verhoudt KoalaSync zich tot andere watchparty-tools en streamingoplossingen? Lees onze eerlijke, technische vergelijkingen en vind de beste keuze voor je filmavonden op afstand.",
"ALT_INDEX_READ_COMPARISON": "Vergelijking lezen →",
"ALT_INDEX_READ_GUIDE": "Gids lezen →",
"ALT_INDEX_FOOTER_LINK": "Gidsen en vergelijkingen",
"ALT_TELEPARTY_TITLE": "KoalaSync vs. Teleparty: welke tool voor 'watch parties' moet je kiezen?",
"ALT_TELEPARTY_META_DESC": "Een praktische vergelijking tussen KoalaSync en Teleparty voor watch parties, met aandacht voor ingebouwde chat, mobiele ondersteuning, videosynchronisatie in de browser, self-hosted mediaservers en open-sourcegebruik.",
"ALT_TELEPARTY_INTRO": "KoalaSync en Teleparty bieden allebei een oplossing voor hetzelfde vervelende probleem: samen iets kijken zonder dat gedoe met '3, 2, 1, afspelen'. Ze pakken het alleen anders aan.<br><br>Teleparty is de logische keuze als je een ingebouwde chatfunctie wilt of op mobiel wilt kijken. KoalaSync is meer geschikt voor watch parties via de desktopbrowser, waarbij je al met elkaar praat via Discord, Teamspeak of een andere spraakverbinding en alleen wilt dat het afspelen synchroon loopt.<br><br>De belangrijkste reden waarom ik KoalaSync heb ontwikkeld, is flexibiliteit. Het is bedoeld om te werken met vrijwel elke website die een standaard videospeler in de browser gebruikt, en niet slechts met een vaste lijst platforms. Dit omvat ook self-hosted systemen zoals Emby, Jellyfin en Plex, lokale of privé-domeinen en talloze reguliere videopagina's in de browser.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Wanneer Teleparty toch een goede keuze kan zijn",
"ALT_TELEPARTY_WHEN_TELE_BODY": "Teleparty kan de betere keuze zijn als je wilt dat de chatfunctie deel uitmaakt van de watch party-tool zelf. KoalaSync heeft momenteel geen ingebouwde chat. Er wordt vanuit gegaan dat je al ergens anders met elkaar praat, bijvoorbeeld via Discord, Teamspeak, WhatsApp, Signal of een gewoon telefoongesprek.<br><br>Mobiel gebruik is een andere belangrijke factor. Teleparty heeft een app, terwijl KoalaSync momenteel alleen in desktopbrowsers werkt. Als je Netflix wilt synchroniseren op een telefoon of tablet, is KoalaSync daarvoor op dit moment niet de juiste tool.<br><br>Dus als chat en mobiele ondersteuning voor jou het belangrijkst zijn, past Teleparty wellicht beter bij jouw situatie.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "Wanneer KoalaSync wellicht beter past",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "KoalaSync is een betere keuze als je op zoek bent naar een flexibele tool voor het synchroniseren van video's in desktopbrowsers, in plaats van een oplossing die gebonden is aan een beperkt aantal platforms.<br><br>Het werkt met vrijwel elke website die gebruikmaakt van een standaard videospeler in de browser. Dit is vooral handig voor Emby, Jellyfin, Plex, privémediaservers, lokale domeinen of kleinere videosites die doorgaans worden overgeslagen door traditionele 'watch party'-tools.<br><br>Bovendien is het gratis en open source, vereist het geen account en kun je het zelf hosten als je je eigen relay wilt draaien. Ik heb het ontwikkeld omdat ik een privacyvriendelijke oplossing wilde die samenwerkte met mijn eigen Emby-opstelling, en niet weer een gesloten platform dat alleen geschikt is voor een paar grote streamingdiensten.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "Het belangrijkste verschil",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "Teleparty is meer een 'watch party'-product, inclusief chatfunctie en ondersteuning voor mobiele apparaten. KoalaSync fungeert eerder als een flexibele synchronisatielaag voor video's in de browser.<br><br>Dat lijkt misschien een klein verschil, maar het heeft grote gevolgen voor de manier waarop je de tool gebruikt. Als je een complete 'watch party'-app met ingebouwde chat nodig hebt, is Teleparty een logische keuze. Als je al over voicechat beschikt en alleen wilt dat de videobediening synchroon blijft lopen tussen verschillende browsers, is KoalaSync de eenvoudigere en geschiktere oplossing.",
"ALT_TELEPARTY_LIMITS_TITLE": "Eerlijke blik op de beperkingen",
"ALT_TELEPARTY_LIMITS_BODY": "KoalaSync is geen wondermiddel en doet niet geheimzinnig over zijn beperkingen. Op dit moment werkt de tool alleen in desktopbrowsers. Er is geen ingebouwde chatfunctie en geen mobiele app.<br><br>De werking hangt ook af van de browser en de videospeler. KoalaSync zou moeten werken met vrijwel elke standaard videospeler in de browser, maar websites kunnen op elk moment hun speler, DRM-implementatie of browserbeperkingen wijzigen.<br><br>KoalaSync is niet verbonden met Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex of enig ander hier genoemd merk. Deze namen worden uitsluitend gebruikt om typische gebruiksscenario's toe te lichten.",
"ALT_TELEPARTY_SUMMARY_TITLE": "Samenvatting",
"ALT_TELEPARTY_SUMMARY_BODY": "Als je behoefte hebt aan een ingebouwde chatfunctie of ondersteuning voor mobiele apparaten, is Teleparty wellicht de betere keuze. Als je echter op zoek bent naar gratis, open-source synchronisatie via desktopbrowsers die werkt met vrijwel elke standaard videospeler inclusief zelfgehoste mediaservers zoals Emby, Jellyfin en Plex dan is KoalaSync waarschijnlijk de betere optie.",
"ALT_SCREEN_TITLE": "KoalaSync vs. scherm delen: wanneer is videosynchronisatie beter?",
"ALT_SCREEN_META_DESC": "Een praktische vergelijking tussen KoalaSync en scherm delen voor filmavonden op afstand, met aandacht voor bediening van het afspelen, videokwaliteit, privacy, ondertiteling en kijken via de browser.",
"ALT_SCREEN_INTRO": "Scherm delen werkt prima en is soms ook echt voldoende. Eén persoon deelt het scherm, de rest kijkt mee, en klaar ben je.<br><br>Maar bij langere filmavonden of het samen kijken van afleveringen kan het vervelend worden. De videokwaliteit hangt af van de uploadsnelheid, CPU en browser van degene die het scherm deelt, evenals de compressie die wordt toegepast door apps als Discord, Teamspeak, Zoom of andere belprogramma's. Bovendien heeft slechts één persoon echt controle over het afspelen.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "Wanneer scherm delen voldoende is",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "Scherm delen is een prima oplossing als je de snelst mogelijke opzet wilt. Als slechts één persoon toegang heeft tot de video of als iedereen ermee akkoord gaat dat één persoon de bediening (afspelen, pauzeren, vooruit-/terugspoelen) in handen heeft, is dit wellicht de eenvoudigste optie.<br><br>Het is ook een logische keuze als de beeldkwaliteit minder belangrijk is, niemand specifieke ondertiteling of audio-instellingen nodig heeft en de persoon die het beeld deelt er geen bezwaar tegen heeft om dat browservenster of scherm te laten zien.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "Wanneer scherm delen vervelend kan zijn",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "Het grootste nadeel is dat niet iedereen in zijn eigen speler kijkt. Eén persoon streamt het scherm en alle anderen zien alleen die stream.<br><br>Dit kan leiden tot een lagere beeldkwaliteit, gecomprimeerde audio, vertraging bij het afspelen en minder controle voor de andere kijkers. Ook de ondertiteling en audiosporen zijn afhankelijk van wat de deler heeft gekozen.<br><br>Daarnaast speelt privacy een rol. Als je van venster of tabblad wisselt, kunnen anderen mogelijk kortstondig je browsertabbladen, e-mail, Reddit, chats of andere privégegevens zien. Dit is te omzeilen door een apart venster te gebruiken, maar het is wel iets om rekening mee te houden.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "Waarom KoalaSync 'netter' kan aanvoelen",
"ALT_SCREEN_WHY_CLEANER_BODY": "KoalaSync streamt je scherm niet. Iedereen bekijkt de video lokaal in de eigen browser; KoalaSync zorgt er alleen voor dat acties zoals afspelen, pauzeren en vooruit-/terugspoelen synchroon blijven lopen.<br><br>Hierdoor kan iedereen op het eigen apparaat in volledig scherm kijken, eigen instellingen voor ondertiteling of audio gebruiken (mits de speler dit ondersteunt) en de voicechat gescheiden houden via Discord, Teamspeak of een andere beldienst.<br><br>Voor filmavonden kan dit veel prettiger aanvoelen dan wanneer één persoon het scherm deelt. Het is vooral handig voor browsergebaseerde systemen zoals Emby, Jellyfin, Plex, lokale bestanden die in de browser worden geopend, privédomeinen of andere websites met standaard videospelers in de browser.",
"ALT_SCREEN_LIMITS_TITLE": "Eerlijke beperkingen",
"ALT_SCREEN_LIMITS_BODY": "KoalaSync is geen vervanging voor een spraakgesprek. Als je tijdens het kijken wilt praten, gebruik je gewoon Discord, Teamspeak, Signal, WhatsApp of wat je normaal ook gebruikt.<br><br>Iedereen heeft ook toegang tot de video of pagina op het eigen apparaat nodig. KoalaSync omzeilt geen abonnementen, inloggegevens, DRM, regionale beperkingen of platformregels.<br><br>Het werkt momenteel alleen in desktopbrowsers, heeft geen ingebouwde chat- of spraakfunctie en de compatibiliteit hangt altijd af van de browser en de videospeler.",
"ALT_SCREEN_SUMMARY_TITLE": "Samenvatting",
"ALT_SCREEN_SUMMARY_BODY": "Scherm delen is de eenvoudigste optie als je snel iets wilt doen en niet veel waarde hecht aan kwaliteit, privacy of individuele controle. KoalaSync is een betere keuze wanneer iedereen lokaal in de eigen browser moet kijken en de sessie meer moet aanvoelen als echt samen kijken.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "Probeer KoalaSync",
"MOCK_HOST_CONTROL_LABEL": "Hostbeheer",
"MOCK_HOST_CONTROL_TOGGLE": "Alleen ik mag de weergave bedienen",
"LABEL_SELECT_VIDEO": "Selecteer video",
"OPTION_SELECT_TAB": "-- Selecteer een tabblad --",
"THEME_TOGGLE_LABEL": "Licht/donker thema wisselen",
"FAQ_SHOW_ALL": "Alle vragen tonen",
"FAQ_HIDE_ALL": "Vragen verbergen",
"SELF_SHOW_SETUP": "Installatiehandleiding tonen",
"SELF_HIDE_SETUP": "Handleiding verbergen",
"HERO_TRUST": "Gratis · Open source · Geen account nodig",
"COMP_MORE_GUIDES": "Volledige vergelijking en meer alternatieven"
}
+103 -33
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "pl_PL",
"META_TITLE": "KoalaSync | Synchronizuj Netflix, YouTube i dowolne wideo ze znajomymi",
"META_DESCRIPTION": "Oglądaj Netflix, YouTube, Twitch i dowolne wideo HTML5 w idealnej synchronizacji ze znajomymi. Darmowe rozszerzenie o otwartym kodzie dla Chrome i Firefox. Bez rejestracji.",
"SCHEMA_APP_DESC": "Oglądaj Netflix, YouTube, Twitch i dowolne wideo HTML5 w idealnej synchronizacji ze znajomymi. Darmowe rozszerzenie o otwartym kodzie dla Chrome i Firefox. Bez rejestracji.",
"OG_TITLE": "KoalaSync | Synchronizuj Netflix, Emby, Jellyfin i prawie każde wideo ze znajomymi",
"OG_DESCRIPTION": "Oglądaj Netflix, Emby, Jellyfin, YouTube, Twitch i prawie każde wideo HTML5 w idealnej synchronizacji. Zorientowane na prywatność rozszerzenie o otwartym kodzie dla Chrome i Firefox.",
"TWITTER_TITLE": "KoalaSync | Synchronizuj Netflix, Emby, Jellyfin i prawie każde wideo ze znajomymi Rozszerzenie przeglądarki",
"TWITTER_DESCRIPTION": "Oglądaj Netflix, Emby, Jellyfin, YouTube, Twitch i prawie każde wideo HTML5 w idealnej synchronizacji ze znajomymi. Zorientowane na prywatność rozszerzenie o otwartym kodzie dla Chrome i Firefox.",
"META_TITLE": "KoalaSync | Watch party dla Netflix, YouTube i innych",
"META_DESCRIPTION": "Watch party na Netflix, YouTube, Twitch i dowolnym wideo HTML5. Darmowe, otwarte rozszerzenie do Chrome i Firefox. Bez konta, bez śledzenia, bez reklam.",
"SCHEMA_APP_DESC": "Watch party na Netflix, YouTube, Twitch i dowolnym wideo HTML5. Darmowe, otwarte rozszerzenie do Chrome i Firefox. Bez konta, bez śledzenia, bez reklam.",
"OG_TITLE": "KoalaSync | Watch party dla Netflix, Emby, Jellyfin i prawie każdego wideo",
"OG_DESCRIPTION": "Zorganizuj watch party na Netflix, Emby, Jellyfin, YouTube, Twitch i prawie każdym wideo HTML5. Open source, prywatnie i bez konta.",
"TWITTER_TITLE": "KoalaSync | Watch party dla Netflix, Emby, Jellyfin i prawie każdego wideo Rozszerzenie",
"TWITTER_DESCRIPTION": "Zorganizuj watch party na Netflix, Emby, Jellyfin, YouTube, Twitch i prawie każdym wideo HTML5. Open source, prywatnie i bez konta.",
"NAV_FEATURES": "Funkcje",
"NAV_HOW_IT_WORKS": "Jak to działa",
"HERO_TITLE": "Oglądajcie Razem.<span class='hero-line2'>Synchronizujcie Idealnie.</span>",
"HERO_SUBTITLE": "Twój zdalny wieczór filmowy bez opóźnień. Bez rejestracji, bez zbierania danych. Po prostu udostępnij link i oglądajcie razem.",
"HERO_TITLE": "Zamień dowolne wideo w <span class='hero-highlight'>watch party</span>.",
"HERO_SUBTITLE": "Twój zdalny wieczór filmowy bez opóźnień. Bez konta, bez śledzenia, bez reklam. Udostępnij link i naciśnijcie play razem.",
"HERO_MASCOT_ALT": "Uroczy koala stojący i patrzący w dół na przyciski pobierania",
"DEMO_SYNC": "Zsynchronizowane",
"DEMO_HINT": "Demo na żywo: naciśnij play w dowolnej karcie, a KoalaSync utrzyma obie w synchronizacji.",
"DEMO_NO_ROOM": "Jeszcze nie połączono",
"DEMO_CREATE_ROOM": "Utwórz pokój",
"DEMO_JOINED": "Dołączono do pokoju",
"DEMO_TAB_SELECTED": "Karta wybrana",
"DEMO_SELECT_PLACEHOLDER": "Wybierz kartę wideo…",
"MOBILE_DEMO_CTA": "Zobacz, jak działa",
"MOBILE_DEMO_TITLE": "Demo live KoalaSync",
"MOBILE_DEMO_CLOSE": "Zamknij demo",
"ADD_TO_CHROME": "Dodaj do Chrome",
"ADD_TO_FIREFOX": "Dodaj do Firefox",
"COMPAT_HEADING": "Działa na Twoich ulubionych platformach",
@@ -35,22 +45,22 @@
"USE_CASE_3_DESC": "Pokonaj odległość i ciesz się randkami. Przeżywajcie każdy zwrot akcji, śmiejcie się z tych samych żartów i dzielcie emocje w tej samej milisekundzie.",
"WHY_TITLE": "Dlaczego KoalaSync?",
"WHY_SUBTITLE": "Stworzony z myślą o niezawodnej synchronizacji, prywatności i łatwej konfiguracji.",
"FEATURE_1_TITLE": "Pełna kontrola / Natychmiastowa synchronizacja",
"FEATURE_1_DESC": "Jeden zatrzymuje, wszyscy zatrzymują. Jeden przewija, wszyscy podążają. Nasz dwufazowy protokół synchronizacji koordynuje odtwarzanie w czasie rzeczywistym u wszystkich uczestników.",
"FEATURE_1_TITLE": "Kontrola hosta i synchronizacja",
"FEATURE_1_DESC": "Decydujesz, kto steruje odtwarzaniem. Włącz kontrolę hosta, aby tylko Ty mógł wstrzymywać i przewijać, lub mianuj współhostów.",
"FEATURE_2_TITLE": "Niekończące się oglądanie",
"FEATURE_2_DESC": "Autoodtwarzanie w synchronizacji. KoalaSync automatycznie wykrywa zmiany odcinków i wstrzymuje odtwarzanie, dopóki wszyscy uczestnicy nie załadują następnego filmu.",
"FEATURE_3_TITLE": "Zero kont / Czysta prywatność",
"FEATURE_3_DESC": "Bez rejestracji, bez śledzenia, bez trwałych śladów. Serwer działa w całości w pamięci RAM i czyści Twój pokój, gdy go opuszczasz.",
"FEATURE_4_TITLE": "Uniwersalne wsparcie HTML5",
"FEATURE_4_DESC": "Działa na YouTube, Twitch, Netflix, Jellyfin, Emby i prawie każdej standardowej stronie z elementem wideo HTML5. Kompatybilny również z własnymi serwerami. Żadne dane wideo nigdy nie są wysyłane na nasze serwery — jedynie metadane synchronizacji.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby i niemal każda strona z wideo HTML5. Przeglądarkę opuszczają tylko sygnały synchronizacji, nigdy wideo.",
"FEATURE_5_TITLE": "Możliwość własnego hostowania i wsparcie Docker",
"FEATURE_5_DESC": "Nasze oficjalne serwery są darmowe i szybkie, ale możesz przejąć pełną kontrolę, jeśli chcesz. Uruchom własny serwer w kilka sekund za pomocą Dockera.",
"FEATURE_6_TITLE": "Natychmiastowe zaproszenia / Dołączanie 1 kliknięciem",
"FEATURE_6_DESC": "Bez wymiany adresów IP czy haseł. Udostępnij wygenerowany link zaproszenia znajomym, aby mogli automatycznie dołączyć do pokoju jednym kliknięciem.",
"FEATURE_7_TITLE": "Wyrównywanie głośności / Kompresor audio",
"FEATURE_7_DESC": "Koniec z ciągłym regulowaniem głośności. Wbudowany kompresor audio wzmacnia ciche dialogi i łagodzi głośne eksplozje. Działa na Netflix, Prime Video, Disney+, YouTube i każdym filmie HTML5. W pełni lokalny — żadne dane audio nie opuszczają twojego komputera.",
"FEATURE_7_DESC": "Koniec z ciągłym regulowaniem głośności. Wbudowany kompresor podbija ciche dialogi i wycisza głośne wybuchy, a wszystko przetwarzane lokalnie w przeglądarce.",
"FEATURE_8_TITLE": "Open Source (Licencja MIT)",
"FEATURE_8_DESC": "Cały kod źródłowy jest publicznie dostępny i możliwy do zweryfikowania na GitHubie. Żadnych czarnych skrzynek, żadnych własnościowych obiektów blob 100% przejrzystości od serwera przekaźnikowego (relay server) po rozszerzenie przeglądarki.",
"FEATURE_8_DESC": "Cały kod źródłowy jest publicznie dostępny i możliwy do zweryfikowania na GitHubie. Żadnych czarnych skrzynek, żadnych własnościowych obiektów blob. 100% przejrzystości od serwera przekaźnikowego (relay server) po rozszerzenie przeglądarki.",
"COMP_TITLE": "KoalaSync vs. Teleparty",
"COMP_SUBTITLE": "Zobacz, dlaczego otwarty kod, brak reklam i ochrona prywatności to lepszy sposób na wspólne oglądanie.",
"COMP_COL_FEATURE": "Funkcja",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Polityka prywatności Teleparty i zbieranie danych śledzenia:",
"COMP_FOOTNOTE_4": "Działa na stronach zezwalających na wstrzykiwanie skryptów do standardowych tagów wideo HTML5. Strony z bardzo rygorystycznymi zasadami bezpieczeństwa (CSP), zabezpieczeniami DRM (kopiowanie) lub mocno zaciemnionymi odtwarzaczami (jak złożone systemy shadow DOM) mogą ograniczać zdalne sterowanie.",
"STEPS_TITLE": "Jak zacząć",
"STEP_1_TITLE": "Zainstaluj rozszerzenie",
"STEP_1_DESC": "Dodaj KoalaSync do swojej przeglądarki z Chrome Web Store, Firefox Add-ons lub pobierz najnowsze archiwum ZIP dla deweloperów z GitHub.",
"STEP_1_ILLUS_DESC": "Synchronizator wideo dbający o prywatność",
"STEP_1_ILLUS_DL_CHROME": "Pobierz dla Chrome",
"STEP_1_ILLUS_DL_FIREFOX": "Pobierz dla Firefox",
"STEP_1_ILLUS_ACTIVE": "Rozszerzenie aktywne",
"STEP_1_ILLUS_READY": "Gotowe do wspólnego oglądania!",
"STEP_2_TITLE": "Utwórz pokój",
"STEP_2_DESC": "Otwórz okienko rozszerzenia i kliknij '+ Utwórz nowy pokój'. KoalaSync automatycznie wygeneruje bezpieczny ID pokoju oraz hasło, dołączy do niego i skopiuje link zaproszenia do schowka.",
"STEP_1_TITLE": "Utwórz i udostępnij pokój",
"STEP_1_DESC": "Otwórz rozszerzenie, utwórz pokój jednym kliknięciem i udostępnij link znajomym. Dołączą natychmiast przez link z zaproszeniem.",
"STEP_1_ILLUS_DESC": "Wybierz kartę do połączenia",
"STEP_1_ILLUS_DL_CHROME": "Połącz kartę YouTube",
"STEP_1_ILLUS_DL_FIREFOX": "Połącz kartę Netflix",
"STEP_1_ILLUS_ACTIVE": "Karta połączona",
"STEP_1_ILLUS_READY": "Gotowe do synchronizacji !",
"STEP_2_TITLE": "Wybierz kartę z wideo",
"STEP_2_DESC": "Przejdź do karty ze swoim wideo i wybierz ją w menu rozszerzenia. KoalaSync automatycznie połączy Wasze odtwarzacze.",
"STEP_2_ILLUS_ROOM": "Pokój",
"STEP_2_ILLUS_SYNC": "Sync",
"STEP_2_ILLUS_SETTINGS": "Ustawienia",
"STEP_2_ILLUS_CREATE": "+ Utwórz nowy pokój",
"STEP_2_ILLUS_MANUAL": "Połączenie ręczne / Zaawansowane",
"STEP_2_ILLUS_COPIED": "Link zaproszenia skopiowany!",
"STEP_3_TITLE": "Udostępnij i synchronizuj",
"STEP_3_DESC": "Wyślij link zaproszenia znajomym. Gdy dołączą, wybierz kartę z filmem i ciesz się zsynchronizowanym odtwarzaniem.",
"STEP_3_TITLE": "Ciesz się synchronizacją",
"STEP_3_DESC": "Naciśnij odtwarzaj. Gdy ktoś wstrzyma, przewinie lub odtworzy wideo, odtwarzanie u wszystkich pozostanie idealnie zsynchronizowane.",
"STEP_3_ILLUS_IN_SYNC": "ZSYNCHRONIZOWANO",
"SELF_TITLE": "Dla hostujących samodzielnie",
"SELF_SUBTITLE": "Nie ufasz naszemu serwerowi? Zachowaj pełną suwerenność danych. Wdróż własny prywatny serwer w kilka minut.",
@@ -116,19 +126,21 @@
"FAQ_TITLE": "Często zadawane pytania",
"FAQ_SUBTITLE": "Wszystko, co musisz wiedzieć o wspólnym oglądaniu filmów w idealnej synchronizacji.",
"FAQ_Q1": "Czy mogę oglądać Netflix, Emby lub Jellyfin w synchronizacji ze znajomymi?",
"FAQ_A1": "Tak! KoalaSync działa na Netflix, YouTube, Twitch i każdej stronie z odtwarzaczem wideo HTML5 w tym Emby, Jellyfin i własnych serwerach mediów. Zainstaluj rozszerzenie, utwórz pokój, udostępnij link i wszyscy oglądają w idealnej synchronizacji.",
"FAQ_A1": "Tak! KoalaSync działa na Netflix, YouTube, Twitch i każdej stronie z odtwarzaczem wideo HTML5, w tym Emby, Jellyfin i własnych serwerach mediów. Zainstaluj rozszerzenie, utwórz pokój, udostępnij link i wszyscy oglądają w idealnej synchronizacji.",
"FAQ_Q_VIDEO_ACCESS": "Czy KoalaSync przesyła wideo do moich znajomych?",
"FAQ_A_VIDEO_ACCESS": "Nie. KoalaSync nie przesyła, nie hostuje, nie udostępnia ani nie omija dostępu do treści wideo. Każda osoba ogląda wideo lokalnie we własnej przeglądarce i potrzebuje własnego dostępu do danej strony lub usługi streamingowej, na przykład własnego konta Netflix. KoalaSync synchronizuje tylko akcje odtwarzania, takie jak odtwarzanie, pauza i przewijanie.",
"FAQ_Q2": "Czy KoalaSync to darmowa alternatywa dla Teleparty?",
"FAQ_A2": "W 100% darmowy i o otwartym kodzie na licencji MIT. Brak planów premium, brak zablokowanych funkcji, brak reklam wszystko jest w cenie. W przeciwieństwie do Teleparty, serwer KoalaSync działa tylko w pamięci RAM, bez zbierania danych i bez kont użytkowników.",
"FAQ_A2": "W 100% darmowy i o otwartym kodzie na licencji MIT. Brak planów premium, brak zablokowanych funkcji, brak reklam, wszystko jest w cenie. W przeciwieństwie do Teleparty, serwer KoalaSync działa tylko w pamięci RAM, bez zbierania danych i bez kont użytkowników.",
"FAQ_Q3": "Czy KoalaSync działa z Emby, Jellyfin i własnymi serwerami mediów?",
"FAQ_A3": "Tak. KoalaSync wykrywa każdy standardowy element wideo HTML5, co oznacza, że działa idealnie z Emby, Jellyfin, Plexem i dowolnymi własnymi rozwiązaniami streamingowymi. Możesz również samodzielnie hostować serwer przez Dockera.",
"FAQ_Q4": "Które przeglądarki i platformy obsługują KoalaSync?",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave i wszystkie przeglądarki oparte na Chromium. Zainstaluj je bezpośrednio z Chrome Web Store lub Firefox Add-ons nie jest wymagana ręczna konfiguracja.",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave i wszystkie przeglądarki oparte na Chromium. Zainstaluj je bezpośrednio z Chrome Web Store lub Firefox Add-ons, nie jest wymagana ręczna konfiguracja.",
"FAQ_Q5": "Czy wszyscy moi znajomi muszą mieć zainstalowane rozszerzenie?",
"FAQ_A5": "Tak, każdy uczestnik musi mieć zainstalowane KoalaSync ale nie ma rejestracji ani tworzenia kont. Po prostu kliknij link zaproszenia, zainstaluj rozszerzenie, jeśli jeszcze tego nie zrobiłeś, a dołączysz do pokoju automatycznie w kilka sekund.",
"FAQ_A5": "Tak, każdy uczestnik musi mieć zainstalowane KoalaSync, ale nie ma rejestracji ani tworzenia kont. Po prostu kliknij link zaproszenia, zainstaluj rozszerzenie, jeśli jeszcze tego nie zrobiłeś, a dołączysz do pokoju automatycznie w kilka sekund.",
"FAQ_Q6": "Czy moje dane są bezpieczne? Co jeśli nie ufam oficjalnemu serwerowi?",
"FAQ_A6": "Całkowicie bezpieczne. Serwer KoalaSync działa tylko w pamięci RAM wszystkie dane sesji są trwale usuwane w momencie opuszczenia pokoju. Brak bazy danych, brak logowania, brak analityki, brak kont. A jeśli wolisz pełną kontrolę: możesz samodzielmente uruchomić własny serwer przez Dockera w kilka minut. Cały kod źródłowy jest otwarty i dostępny na GitHubie.",
"FAQ_A6": "Całkowicie bezpieczne. Serwer KoalaSync działa tylko w pamięci RAM, wszystkie dane sesji są trwale usuwane w momencie opuszczenia pokoju. Brak bazy danych, brak logowania, brak analityki, brak kont. A jeśli wolisz pełną kontrolę: możesz samodzielmente uruchomić własny serwer przez Dockera w kilka minut. Cały kod źródłowy jest otwarty i dostępny na GitHubie.",
"FAQ_Q7": "Czy mogę naprawić ciche dialogi i głośne eksplozje podczas oglądania filmów w przeglądarce?",
"FAQ_A7": "Tak. KoalaSync 2.2.0 zawiera wbudowany kompresor audio, który zmniejsza różnicę między cichymi a głośnymi częściami filmu. Działa lokalnie przez Web Audio API żadne dane audio nie opuszczają twojego komputera. Otwórz popup, utwórz pokój, przejdź do Ustawienia → Audio Processing, włącz kompresor i wybierz preset. Działa na Netflix, Prime Video, Disney+, YouTube i każdej stronie z wideo HTML5.",
"FAQ_A7": "Tak. KoalaSync 2.2.0 zawiera wbudowany kompresor audio, który zmniejsza różnicę między cichymi a głośnymi częściami filmu. Działa lokalnie przez Web Audio API, żadne dane audio nie opuszczają twojego komputera. Otwórz popup, utwórz pokój, przejdź do Ustawienia → Audio Processing, włącz kompresor i wybierz preset. Działa na Netflix, Prime Video, Disney+, YouTube i każdej stronie z wideo HTML5.",
"HOWTO_TITLE": "Jak synchronizować wideo ze znajomymi za pomocą KoalaSync",
"HOWTO_DESC": "Oglądaj dowolne wideo w idealnej synchronizacji ze znajomymi na YouTube, Netflix, Twitch i innych. Bez rejestracji, bez śledzenia.",
"HOWTO_STEP_1_NAME": "Zainstaluj rozszerzenie przeglądarki",
@@ -156,7 +168,7 @@
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SYNC",
"MOCK_14": "Skocz do innych",
"MOCK_15": "Status ostatniej aktywności",
"MOCK_16": "Oczekiwanie na 1 uczestnika (KoalaPC)...",
"MOCK_16": "Oczekiwanie na 1 uczestnika (🐶 HappyDog)...",
"MOCK_17": "Pomiń i odtwórz mimo to",
"MOCK_18": "Twoja nazwa użytkownika",
"MOCK_19": "Ukryj zbędne karty",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSync nie jest powiązany, wspierany ani stowarzyszony z Netflix, Disney+, Amazon, YouTube, Twitch ani żadną inną platformą streamingową. Wszystkie znaki towarowe są własnością ich odpowiednich właścicieli.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - Alternatywy i porównania",
"ALT_INDEX_META_DESC": "Poznaj uczciwe porównania i szczegółowe poradniki zestawiające KoalaSync z innymi rozszerzeniami do watch party oraz rozwiązaniami streamingowymi.",
"ALT_INDEX_HEADING": "Poradniki i porównania",
"ALT_INDEX_INTRO": "Jak KoalaSync wypada na tle innych narzędzi do watch party i rozwiązań streamingowych? Przeczytaj nasze uczciwe, techniczne porównania i wybierz najlepszą opcję na zdalne wieczory filmowe.",
"ALT_INDEX_READ_COMPARISON": "Przeczytaj porównanie →",
"ALT_INDEX_READ_GUIDE": "Przeczytaj poradnik →",
"ALT_INDEX_FOOTER_LINK": "Poradniki i porównania",
"ALT_TELEPARTY_TITLE": "KoalaSync czy Teleparty: którego narzędzia do wspólnego oglądania użyć?",
"ALT_TELEPARTY_META_DESC": "Praktyczne porównanie narzędzi KoalaSync i Teleparty pod kątem wspólnego oglądania: wbudowany czat, obsługa urządzeń mobilnych, synchronizacja wideo w przeglądarce, własne serwery multimediów i charakter open-source.",
"ALT_TELEPARTY_INTRO": "Zarówno KoalaSync, jak i Teleparty rozwiązują ten sam uciążliwy problem: jak oglądać coś wspólnie bez konieczności odliczania „3, 2, 1, start”. Różnią się jednak podejściem do tego zadania.<br><br>Teleparty sprawdza się najlepiej, jeśli zależy Ci na wbudowanym czacie lub oglądaniu na urządzeniach mobilnych. KoalaSync jest raczej rozwiązaniem do wspólnego oglądania w przeglądarce na komputerze gdy rozmawiacie już przez Discorda, Teamspeaka lub inny komunikator głosowy i chcecie jedynie zsynchronizować odtwarzanie.<br><br>Głównym powodem stworzenia KoalaSync była elastyczność. Narzędzie to ma współpracować z niemal każdą stroną wykorzystującą standardowy odtwarzacz wideo w przeglądarce, a nie tylko z wybraną listą platform. Obejmuje to własne serwery multimediów, takie jak Emby, Jellyfin czy Plex, domeny lokalne lub prywatne oraz wiele typowych stron z materiałami wideo.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Kiedy Teleparty wciąż może być dobrym wyborem",
"ALT_TELEPARTY_WHEN_TELE_BODY": "Teleparty może okazać się lepszym rozwiązaniem, jeśli chcesz, aby czat był częścią samego narzędzia do wspólnego oglądania. KoalaSync nie posiada obecnie wbudowanego czatu; zakłada, że rozmawiacie za pośrednictwem innych kanałów, takich jak Discord, Teamspeak, WhatsApp, Signal czy zwykłe połączenie głosowe.<br><br>Kolejną istotną kwestią jest obsługa urządzeń mobilnych. Teleparty posiada dedykowaną aplikację, podczas gdy KoalaSync działa obecnie tylko w przeglądarkach na komputerach stacjonarnych. Jeśli chcesz zsynchronizować Netflixa na telefonie lub tablecie, KoalaSync nie będzie w tej chwili odpowiednim narzędziem.<br><br>Zatem, jeśli czat i obsługa urządzeń mobilnych są dla Ciebie najważniejsze, Teleparty może po prostu lepiej pasować do Twoich potrzeb.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "Kiedy warto wybrać KoalaSync",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "KoalaSync to lepszy wybór, jeśli szukasz elastycznego narzędzia do synchronizacji odtwarzania wideo w przeglądarce, a nie rozwiązania ograniczonego do wąskiej listy platform.<br><br>Narzędzie współpracuje z niemal każdą stroną internetową wykorzystującą standardowy odtwarzacz wideo w przeglądarce. Jest to szczególnie przydatne w przypadku serwisów takich jak Emby, Jellyfin, Plex, prywatnych serwerów multimedialnych, domen lokalnych czy mniejszych serwisów wideo, które zazwyczaj są pomijane przez klasyczne narzędzia do wspólnego oglądania.<br><br>Co więcej, KoalaSync jest darmowe, ma otwarty kod źródłowy, nie wymaga zakładania konta i pozwala na samodzielne hostowanie (jeśli chcesz uruchomić własny serwer pośredniczący). Stworzyłem je, ponieważ zależało mi na rozwiązaniu szanującym prywatność, które współpracowałoby z moją własną konfiguracją Emby, a nie na kolejnej zamkniętej platformie ograniczonej do kilku największych serwisów streamingowych.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "Główna różnica",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "Teleparty to produkt nastawiony na wspólne oglądanie, oferujący wbudowany czat i obsługę urządzeń mobilnych. KoalaSync pełni raczej funkcję elastycznej warstwy synchronizującej odtwarzanie wideo w przeglądarce.<br><br>Może się to wydawać drobną różnicą, ale znacząco wpływa na sposób użytkowania. Jeśli potrzebujesz kompletnej aplikacji do wspólnego oglądania z wbudowanym czatem, Teleparty będzie dobrym wyborem. Jeśli natomiast korzystasz już z czatu głosowego i zależy Ci jedynie na synchronizacji sterowania odtwarzaniem między przeglądarkami, prostszym i lepszym rozwiązaniem będzie KoalaSync.",
"ALT_TELEPARTY_LIMITS_TITLE": "Szczerze o ograniczeniach",
"ALT_TELEPARTY_LIMITS_BODY": "KoalaSync nie jest magicznym rozwiązaniem i nie ukrywa swoich ograniczeń. Obecnie działa tylko w przeglądarkach na komputerach stacjonarnych. Nie posiada wbudowanego czatu ani aplikacji mobilnej.<br><br>Kompatybilność zależy również od przeglądarki i odtwarzacza wideo. KoalaSync powinno współpracować z niemal każdym standardowym odtwarzaczem przeglądarkowym, jednak właściciele stron mogą w każdej chwili zmienić odtwarzacz, sposób działania zabezpieczeń DRM czy ograniczenia przeglądarki.<br><br>KoalaSync nie jest powiązane z serwisami Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex ani żadną inną wymienioną tu marką. Nazwy te zostały użyte wyłącznie w celu zilustrowania typowych scenariuszy użycia.",
"ALT_TELEPARTY_SUMMARY_TITLE": "Podsumowanie",
"ALT_TELEPARTY_SUMMARY_BODY": "Jeśli zależy Ci na wbudowanym czacie lub obsłudze urządzeń mobilnych, lepszym wyborem może okazać się Teleparty. Jeśli natomiast szukasz darmowego, otwartoźródłowego rozwiązania do synchronizacji w przeglądarce na komputerze, które współpracuje z niemal każdym standardowym odtwarzaczem wideo w tym z własnymi serwerami multimedialnymi, takimi jak Emby, Jellyfin czy Plex lepszym rozwiązaniem będzie prawdopodobnie KoalaSync.",
"ALT_SCREEN_TITLE": "KoalaSync a udostępnianie ekranu: kiedy synchronizacja wideo sprawdza się lepiej?",
"ALT_SCREEN_META_DESC": "Praktyczne porównanie KoalaSync i udostępniania ekranu pod kątem wspólnych seansów filmowych online: sterowanie odtwarzaniem, jakość obrazu, prywatność, napisy i oglądanie w przeglądarce.",
"ALT_SCREEN_INTRO": "Udostępnianie ekranu działa i szczerze mówiąc czasem to wystarczy. Jedna osoba udostępnia swój ekran, pozostali oglądają i gotowe.<br><br>Jednak w przypadku dłuższych seansów czy wspólnego oglądania odcinków seriali może to stać się uciążliwe. Jakość obrazu zależy od szybkości wysyłania danych (uploadu), procesora i przeglądarki osoby udostępniającej, a także od kompresji stosowanej przez aplikacje takie jak Discord, Teamspeak, Zoom czy inne komunikatory. Co więcej, tylko jedna osoba ma faktyczną kontrolę nad odtwarzaniem.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "Kiedy udostępnianie ekranu wystarczy",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "Udostępnianie ekranu sprawdza się dobrze, gdy zależy Ci na jak najszybszym rozpoczęciu seansu. Jeśli tylko jedna osoba ma dostęp do materiału wideo lub jeśli wszystkim odpowiada, że jedna osoba steruje odtwarzaniem (start, pauza, przewijanie), może to być najprostsze rozwiązanie.<br><br>Ma to również sens, gdy jakość nie jest kluczowa, nikt nie potrzebuje własnych ustawień napisów czy dźwięku, a osoba udostępniająca nie ma nic przeciwko pokazywaniu okna przeglądarki lub całego ekranu.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "Kiedy udostępnianie ekranu bywa uciążliwe",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "Największą wadą jest to, że nie każdy ogląda materiał we własnym odtwarzaczu. Jedna osoba przesyła obraz swojego ekranu, a wszyscy pozostali widzą tylko ten przekaz.<br><br>Może to oznaczać gorszą jakość wideo, skompresowany dźwięk, opóźnienia w odtwarzaniu oraz mniejszą kontrolę dla pozostałych osób. Napisy i ścieżki dźwiękowe są również narzucone przez osobę udostępniającą.<br><br>Istnieje też kwestia prywatności. Jeśli przełączysz okna lub karty, inni mogą na chwilę zobaczyć zawartość Twoich kart w przeglądarce, e-maile, Reddita, czaty czy inne prywatne treści. Można temu zaradzić, używając osobnego okna, ale wciąż jest to kwestia, o której trzeba pamiętać.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "Dlaczego KoalaSync może wydawać się lepszym rozwiązaniem",
"ALT_SCREEN_WHY_CLEANER_BODY": "KoalaSync nie przesyła obrazu Twojego ekranu. Każdy ogląda wideo lokalnie we własnej przeglądarce, a KoalaSync jedynie synchronizuje działania takie jak start, pauza czy przewijanie.<br><br>Oznacza to, że każdy może korzystać z trybu pełnoekranowego na własnym urządzeniu, używać własnych ustawień napisów lub dźwięku (jeśli odtwarzacz na to pozwala) oraz prowadzić rozmowę głosową oddzielnie np. przez Discorda, TeamSpeaka czy inne komunikatory.<br><br>W przypadku wspólnych seansów filmowych rozwiązanie to może wydawać się znacznie bardziej uporządkowane i wygodne niż udostępnianie ekranu przez jedną osobę. Jest to szczególnie przydatne w przypadku rozwiązań przeglądarkowych, takich jak Emby, Jellyfin, Plex, plików lokalnych otwieranych w przeglądarce, prywatnych domen czy innych stron korzystających ze standardowych odtwarzaczy wideo.",
"ALT_SCREEN_LIMITS_TITLE": "Szczerze o ograniczeniach",
"ALT_SCREEN_LIMITS_BODY": "KoalaSync nie zastępuje rozmowy głosowej. Jeśli chcesz rozmawiać podczas oglądania, nadal możesz korzystać z Discorda, TeamSpeaka, Signala, WhatsAppa lub innych narzędzi, których używasz na co dzień.<br><br>Każda osoba musi również mieć dostęp do filmu lub strony na własnym urządzeniu. KoalaSync nie omija subskrypcji, konieczności logowania, zabezpieczeń DRM, ograniczeń regionalnych ani regulaminów platform.<br><br>Obecnie rozwiązanie to działa tylko w przeglądarkach na komputerach stacjonarnych i nie posiada wbudowanego czatu ani funkcji komunikacji głosowej, a kompatybilność zależy od używanej przeglądarki oraz odtwarzacza wideo.",
"ALT_SCREEN_SUMMARY_TITLE": "Podsumowanie",
"ALT_SCREEN_SUMMARY_BODY": "Udostępnianie ekranu to najprostsze rozwiązanie, gdy zależy Ci na szybkości, a kwestie jakości, prywatności czy indywidualnej kontroli nad odtwarzaniem nie są priorytetem. KoalaSync sprawdza się lepiej, gdy uczestnicy oglądają materiał lokalnie we własnych przeglądarkach, a sesja ma bardziej przypominać wspólny seans.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "Wypróbuj KoalaSync",
"MOCK_HOST_CONTROL_LABEL": "Kontrola hosta",
"MOCK_HOST_CONTROL_TOGGLE": "Tylko ja mogę sterować odtwarzaniem",
"LABEL_SELECT_VIDEO": "Wybierz wideo",
"OPTION_SELECT_TAB": "-- Wybierz kartę --",
"THEME_TOGGLE_LABEL": "Przełącz tryb jasny/ciemny",
"FAQ_SHOW_ALL": "Pokaż wszystkie pytania",
"FAQ_HIDE_ALL": "Ukryj pytania",
"SELF_SHOW_SETUP": "Pokaż instrukcję instalacji",
"SELF_HIDE_SETUP": "Ukryj instrukcję",
"HERO_TRUST": "Za darmo · Open source · Bez konta",
"COMP_MORE_GUIDES": "Pełne porównanie i więcej alternatyw"
}
+96 -26
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "pt_BR",
"META_TITLE": "KoalaSync | Sincronize Netflix, YouTube e qualquer vídeo com amigos",
"META_DESCRIPTION": "Assista Netflix, YouTube, Twitch e qualquer vídeo HTML5 em perfeita sincronia. Extensão de navegador gratuita e open source. Sem cadastro.",
"SCHEMA_APP_DESC": "Assista Netflix, YouTube, Twitch e qualquer vídeo HTML5 em perfeita sincronia. Extensão de navegador gratuita e open source. Sem cadastro.",
"OG_TITLE": "KoalaSync | Sincronize Netflix, Emby, Jellyfin e vídeos com amigos",
"OG_DESCRIPTION": "Assista a vídeos em perfeita sincronia com seus amigos. Extensão open source e focada em privacidade.",
"TWITTER_TITLE": "KoalaSync | Sincronize vídeos online com seus amigos",
"TWITTER_DESCRIPTION": "Assista a vídeos junto com seus amigos em sincronia perfeita. Open source e seguro.",
"META_TITLE": "KoalaSync | Watch party para Netflix, YouTube e mais",
"META_DESCRIPTION": "Watch party na Netflix, YouTube, Twitch e qualquer vídeo HTML5. Extensão gratuita e open source para Chrome e Firefox. Sem conta, sem rastreamento, sem anúncios.",
"SCHEMA_APP_DESC": "Watch party na Netflix, YouTube, Twitch e qualquer vídeo HTML5. Extensão gratuita e open source para Chrome e Firefox. Sem conta, sem rastreamento, sem anúncios.",
"OG_TITLE": "KoalaSync | Watch party para Netflix, Emby, Jellyfin e quase qualquer vídeo",
"OG_DESCRIPTION": "Crie uma watch party na Netflix, Emby, Jellyfin, YouTube, Twitch e quase qualquer vídeo HTML5. Open source, privado e sem conta.",
"TWITTER_TITLE": "KoalaSync | Watch party para Netflix, Emby, Jellyfin e quase qualquer vídeo Extensão",
"TWITTER_DESCRIPTION": "Crie uma watch party na Netflix, Emby, Jellyfin, YouTube, Twitch e quase qualquer vídeo HTML5. Open source, privado e sem conta.",
"NAV_FEATURES": "Recursos",
"NAV_HOW_IT_WORKS": "Como Funciona",
"HERO_TITLE": "Assistam Juntos.<span class='hero-line2'>Sincronia Perfeita.</span>",
"HERO_SUBTITLE": "Sua noite de cinema à distância sem atrasos. Sem contas, sem coleta de dados. Apenas compartilhe o link e assistam juntos.",
"HERO_TITLE": "Transforme qualquer vídeo em uma <span class='hero-highlight'>watch party</span>.",
"HERO_SUBTITLE": "Sua sessão de cinema à distância, sem atrasos. Sem conta, sem rastreamento, sem anúncios. Compartilhe um link e deem play juntos.",
"HERO_MASCOT_ALT": "Um coala fofo olhando para os botões de download",
"DEMO_SYNC": "Sincronizado",
"DEMO_HINT": "Demo ao vivo: dê play em qualquer aba e o KoalaSync mantém as duas em sincronia.",
"DEMO_NO_ROOM": "Ainda não conectado",
"DEMO_CREATE_ROOM": "Criar sala",
"DEMO_JOINED": "Entrou na sala",
"DEMO_TAB_SELECTED": "Aba selecionada",
"DEMO_SELECT_PLACEHOLDER": "Escolha uma aba de vídeo…",
"MOBILE_DEMO_CTA": "Ver como funciona",
"MOBILE_DEMO_TITLE": "Demo ao vivo do KoalaSync",
"MOBILE_DEMO_CLOSE": "Fechar demo",
"ADD_TO_CHROME": "Adicionar ao Chrome",
"ADD_TO_FIREFOX": "Adicionar ao Firefox",
"COMPAT_HEADING": "Funciona nas suas plataformas favoritas",
@@ -35,20 +45,20 @@
"USE_CASE_3_DESC": "Aproveite as noites a dois. Ria das mesmas piadas e sinta as mesmas emoções exatamente no mesmo segundo.",
"WHY_TITLE": "Por que o KoalaSync?",
"WHY_SUBTITLE": "Sincronização confiável, máxima privacidade e uso simples.",
"FEATURE_1_TITLE": "Controle Total / Sincronia Instantânea",
"FEATURE_1_DESC": "Se um pausa, todos pausam. Se um avança, todos seguem. Sincronização em tempo real para todos os participantes.",
"FEATURE_1_TITLE": "Controle do host e sincronização",
"FEATURE_1_DESC": "Você decide quem controla a reprodução. Ative o controle do anfitrião para que apenas você possa pausar e avançar, ou nomeie coanfitriões.",
"FEATURE_2_TITLE": "Maratonas Sem Interrupções",
"FEATURE_2_DESC": "O KoalaSync detecta a mudança de episódios e pausa automaticamente até que todos tenham carregado o vídeo seguinte.",
"FEATURE_3_TITLE": "Sem Contas / Privacidade Absoluta",
"FEATURE_3_DESC": "Sem cadastros ou rastreio. O servidor roda apenas na RAM e apaga sua sala assim que você sai.",
"FEATURE_4_TITLE": "Suporte HTML5 Universal",
"FEATURE_4_DESC": "Funciona no YouTube, Twitch, Netflix, Jellyfin, Emby e em qualquer site web. Nenhum dado de vídeo é enviado a nós.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby e quase qualquer página com vídeo HTML5. Só saem os sinais de sincronização, nunca o vídeo.",
"FEATURE_5_TITLE": "Servidores Próprios (Docker)",
"FEATURE_5_DESC": "Nossos servidores são gratuitos, mas você pode assumir o controle total e iniciar seu próprio servidor privado em segundos com Docker.",
"FEATURE_6_TITLE": "Convites Rápidos / Entrar com 1 Clique",
"FEATURE_6_DESC": "Chega de senhas complicadas. Compartilhe o link e seus amigos entram na sala automaticamente.",
"FEATURE_7_TITLE": "Compressor de Áudio Integrado",
"FEATURE_7_DESC": "O compressor de áudio reforça diálogos baixos e diminui o volume de explosões. Funciona localmente, nenhum áudio sai do seu computador.",
"FEATURE_7_DESC": "Nunca mais mexer no volume. Um compressor integrado realça diálogos baixos e suaviza explosões altas, tudo processado localmente no seu navegador.",
"FEATURE_8_TITLE": "Open Source (Licença MIT)",
"FEATURE_8_DESC": "Todo o código é público no GitHub. 100% de transparência, sem softwares ocultos.",
"COMP_TITLE": "KoalaSync vs Teleparty",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Políticas de privacidade do Teleparty:",
"COMP_FOOTNOTE_4": "Funciona em sites que permitem injeção de scripts. Plataformas com DRM estrito podem ter limitações.",
"STEPS_TITLE": "Como Começar",
"STEP_1_TITLE": "Instalar a Extensão",
"STEP_1_DESC": "Adicione o KoalaSync ao seu navegador (Chrome ou Firefox).",
"STEP_1_ILLUS_DESC": "Extensão segura para ver vídeos",
"STEP_1_ILLUS_DL_CHROME": "Baixar para Chrome",
"STEP_1_ILLUS_DL_FIREFOX": "Baixar para Firefox",
"STEP_1_ILLUS_ACTIVE": "Extensão Ativa",
"STEP_1_ILLUS_READY": "Pronto!",
"STEP_2_TITLE": "Criar uma Sala",
"STEP_2_DESC": "Abra a extensão e clique em '+ Criar nova sala'. O link de convite é copiado automaticamente.",
"STEP_1_TITLE": "Criar e compartilhar a sala",
"STEP_1_DESC": "Abra a extensão, crie uma sala com um clique e compartilhe o link com seus amigos. Eles entram instantaneamente pelo link de convite.",
"STEP_1_ILLUS_DESC": "Escolha uma guia para vincular",
"STEP_1_ILLUS_DL_CHROME": "Vincular guia do YouTube",
"STEP_1_ILLUS_DL_FIREFOX": "Vincular guia do Netflix",
"STEP_1_ILLUS_ACTIVE": "Guia vinculada",
"STEP_1_ILLUS_READY": "Pronto para ver em sincronia !",
"STEP_2_TITLE": "Selecionar a guia do vídeo",
"STEP_2_DESC": "Acesse a guia do seu vídeo e selecione-a no menu da extensão. O KoalaSync vincula seus reprodutores automaticamente.",
"STEP_2_ILLUS_ROOM": "Sala",
"STEP_2_ILLUS_SYNC": "Sincronia",
"STEP_2_ILLUS_SETTINGS": "Configurações",
"STEP_2_ILLUS_CREATE": "+ Criar nova sala",
"STEP_2_ILLUS_MANUAL": "Manual / Avançado",
"STEP_2_ILLUS_COPIED": "Link copiado!",
"STEP_3_TITLE": "Compartilhar e Assistir",
"STEP_3_DESC": "Envie o link aos amigos. Escolha um vídeo e aproveitem juntos.",
"STEP_3_TITLE": "Aproveitar a sincronização",
"STEP_3_DESC": "Inicie a reprodução. Se alguém pausar, avançar ou reproduzir, o vídeo de todos se mantém sincronizado em tempo real e sem atrasos.",
"STEP_3_ILLUS_IN_SYNC": "SINCRONIZADOS",
"SELF_TITLE": "Para usuários avançados",
"SELF_SUBTITLE": "Prefere gerenciar seus próprios dados? Inicie seu próprio servidor em minutos com Docker.",
@@ -117,6 +127,8 @@
"FAQ_SUBTITLE": "Tudo o que você precisa saber.",
"FAQ_Q1": "Posso assistir Netflix ou YouTube em sincronia com meus amigos?",
"FAQ_A1": "Sim! O KoalaSync funciona no Netflix, YouTube, Twitch e qualquer site com vídeo HTML5. Instale a extensão, compartilhe o link e pronto.",
"FAQ_Q_VIDEO_ACCESS": "O KoalaSync transmite o vídeo para meus amigos?",
"FAQ_A_VIDEO_ACCESS": "Não. O KoalaSync não transmite, hospeda, compartilha nem contorna o acesso a conteúdos de vídeo. Cada pessoa assiste ao vídeo localmente no próprio navegador e precisa ter seu próprio acesso ao site ou serviço de streaming, como sua própria conta da Netflix. O KoalaSync sincroniza apenas ações de reprodução, como reproduzir, pausar e avançar ou voltar.",
"FAQ_Q2": "É gratuito?",
"FAQ_A2": "Sim, é 100% gratuito e open source. Sem recursos pagos e sem anúncios.",
"FAQ_Q3": "Funciona com Emby ou Jellyfin?",
@@ -156,7 +168,7 @@
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SINCRO",
"MOCK_14": "Ir para a posição dos outros",
"MOCK_15": "Última Atividade",
"MOCK_16": "Aguardando 1 amigo (KoalaPC)...",
"MOCK_16": "Aguardando 1 amigo (🐶 HappyDog)...",
"MOCK_17": "Pular e reproduzir agora",
"MOCK_18": "Seu Nome",
"MOCK_19": "Ocultar abas sem vídeo",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSync não é afiliado à Netflix, Disney+, YouTube, Twitch, etc. Todas as marcas registradas são de propriedade de seus respectivos donos.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - Alternativas e comparações",
"ALT_INDEX_META_DESC": "Explore comparações honestas e guias detalhados entre o KoalaSync e outras extensões de watch party e soluções de streaming.",
"ALT_INDEX_HEADING": "Guias e comparações",
"ALT_INDEX_INTRO": "Como o KoalaSync se compara a outras ferramentas de watch party e soluções de streaming? Leia nossas comparações honestas e técnicas para encontrar a melhor opção para suas noites de cinema à distância.",
"ALT_INDEX_READ_COMPARISON": "Ler comparação →",
"ALT_INDEX_READ_GUIDE": "Ler guia →",
"ALT_INDEX_FOOTER_LINK": "Guias e comparações",
"ALT_TELEPARTY_TITLE": "KoalaSync vs. Teleparty: qual ferramenta de watch party você deve usar?",
"ALT_TELEPARTY_META_DESC": "Uma comparação prática entre KoalaSync e Teleparty para watch parties, abordando chat integrado, suporte para dispositivos móveis, sincronização de vídeo no navegador, servidores de mídia hospedados pelo próprio usuário e uso de código aberto.",
"ALT_TELEPARTY_INTRO": "Tanto o KoalaSync quanto o Teleparty ajudam a resolver o mesmo problema incômodo: assistir a algo em conjunto sem precisar daquela rotina de \"3, 2, 1, play\". A diferença é a abordagem que cada um utiliza.<br><br>O Teleparty é a opção mais indicada se você deseja um chat integrado ou quer assistir pelo celular. O KoalaSync é mais voltado para watch parties em navegadores de desktop, onde você já conversa via Discord, Teamspeak ou outra chamada de voz e só precisa que a reprodução do vídeo permaneça sincronizada.<br><br>O principal motivo pelo qual criei o KoalaSync foi a flexibilidade. Ele foi projetado para funcionar com praticamente qualquer site que utilize um reprodutor de vídeo padrão de navegador, e não apenas com uma lista fixa de plataformas. Isso inclui sistemas hospedados pelo próprio usuário, como Emby, Jellyfin e Plex, domínios locais ou privados e muitas páginas de vídeo comuns na web.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Quando o Teleparty ainda pode ser uma boa opção",
"ALT_TELEPARTY_WHEN_TELE_BODY": "O Teleparty ainda pode ser a melhor escolha se você quiser que o chat faça parte da própria ferramenta de watch party. Atualmente, o KoalaSync não possui chat integrado; ele parte do princípio de que você já está conversando por outro meio, como Discord, Teamspeak, WhatsApp, Signal ou uma chamada convencional.<br><br>O suporte para dispositivos móveis é outro fator importante. O Teleparty possui um aplicativo, enquanto o KoalaSync, no momento, funciona apenas em navegadores de desktop. Se você deseja sincronizar a Netflix em um celular ou tablet, o KoalaSync não é a ferramenta ideal para isso agora.<br><br>Portanto, se chat e suporte para dispositivos móveis são as suas prioridades, o Teleparty pode simplesmente se adequar melhor à sua configuração.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "Quando o KoalaSync pode ser a melhor opção",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "O KoalaSync é mais adequado se você busca uma ferramenta flexível de sincronização de vídeo em navegadores para desktop, em vez de algo restrito a uma lista limitada de plataformas.<br><br>Ele funciona com praticamente qualquer site que utilize o reprodutor de vídeo padrão do navegador. Isso é especialmente útil para Emby, Jellyfin, Plex, servidores de mídia privados, domínios locais ou sites de vídeo menores que as ferramentas tradicionais de 'watch party' costumam ignorar.<br><br>Além disso, é gratuito, de código aberto, não exige criação de conta e permite auto-hospedagem caso você queira executar seu próprio servidor de retransmissão (relay). Eu o criei porque queria algo que respeitasse a privacidade e funcionasse com minha própria configuração do Emby, e não outra plataforma fechada restrita a alguns grandes serviços de streaming.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "A principal diferença",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "O Teleparty é mais voltado para a experiência de 'watch party' (sessão de visualização conjunta), oferecendo chat e suporte para dispositivos móveis. Já o KoalaSync funciona mais como uma camada flexível de sincronização de vídeo entre navegadores.<br><br>Pode parecer uma diferença pequena, mas ela altera significativamente a forma de uso. Se você precisa de um aplicativo completo de 'watch party' com chat integrado, o Teleparty é a escolha ideal. Se você já utiliza chat de voz e quer apenas manter os controles de vídeo sincronizados entre navegadores, o KoalaSync é a solução mais simples.",
"ALT_TELEPARTY_LIMITS_TITLE": "Limitações reais",
"ALT_TELEPARTY_LIMITS_BODY": "O KoalaSync não faz mágica e não tenta esconder suas limitações. Atualmente, ele funciona apenas em navegadores para desktop. Não possui chat integrado nem aplicativo para dispositivos móveis.<br><br>A compatibilidade também depende do navegador e do reprodutor de vídeo utilizado. O KoalaSync deve funcionar com praticamente qualquer reprodutor de vídeo padrão de navegador, mas os sites podem alterar seus reprodutores, o comportamento de DRM ou as restrições do navegador a qualquer momento.<br><br>O KoalaSync não tem vínculo com Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex ou qualquer outra marca mencionada aqui. Os nomes são utilizados apenas para ilustrar casos de uso comuns.",
"ALT_TELEPARTY_SUMMARY_TITLE": "Resumo",
"ALT_TELEPARTY_SUMMARY_BODY": "Se você busca chat integrado ou suporte para dispositivos móveis, o Teleparty pode ser a melhor opção. Se você prefere uma solução gratuita e de código aberto para sincronização em navegadores de desktop, que funcione com praticamente qualquer reprodutor de vídeo comum, incluindo servidores de mídia auto-hospedados como Emby, Jellyfin e Plex, o KoalaSync é provavelmente a escolha ideal.",
"ALT_SCREEN_TITLE": "KoalaSync vs. compartilhamento de tela: quando a sincronização de vídeo é melhor?",
"ALT_SCREEN_META_DESC": "Uma comparação prática entre o KoalaSync e o compartilhamento de tela para sessões de cinema remotas, abordando controle de reprodução, qualidade de vídeo, privacidade, legendas e visualização via navegador.",
"ALT_SCREEN_INTRO": "O compartilhamento de tela funciona e, honestamente, às vezes é o suficiente. Uma pessoa compartilha a tela, as outras assistem e pronto.<br><br>Mas, para sessões de cinema mais longas ou para assistir a episódios em conjunto, isso pode se tornar incômodo. A qualidade do vídeo depende da velocidade de upload, da CPU e do navegador de quem está compartilhando, além da compressão aplicada por aplicativos como Discord, Teamspeak, Zoom ou outros. Além disso, apenas uma pessoa realmente controla a reprodução.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "Quando o compartilhamento de tela é suficiente",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "O compartilhamento de tela é uma boa opção quando você quer uma configuração rápida. Se apenas uma pessoa tem acesso ao vídeo ou se todos concordam que uma única pessoa controle a reprodução, a pausa e a navegação pelo vídeo, essa pode ser a opção mais simples.<br><br>Também faz sentido quando a qualidade não é uma prioridade, quando ninguém precisa de legendas ou configurações de áudio personalizadas e quando a pessoa que está compartilhando se sente confortável em exibir aquela janela do navegador ou a tela do computador.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "Quando o compartilhamento de tela pode ser incômodo",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "A maior desvantagem é que nem todos assistem através de seu próprio reprodutor de vídeo. Uma pessoa transmite a tela, e todas as outras apenas visualizam essa transmissão.<br><br>Isso pode resultar em qualidade de vídeo inferior, áudio comprimido, atraso na reprodução e menos controle para os demais participantes. As legendas e faixas de áudio também serão aquelas escolhidas por quem está compartilhando.<br><br>Há também a questão da privacidade. Se você alternar entre janelas ou abas, as pessoas poderão ver brevemente suas abas do navegador, e-mail, Reddit, chats ou outros conteúdos privados. É possível contornar isso usando uma janela separada, mas ainda é algo que você precisa levar em consideração.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "Por que o KoalaSync pode parecer uma solução mais organizada",
"ALT_SCREEN_WHY_CLEANER_BODY": "O KoalaSync não transmite a sua tela. Todos assistem ao vídeo localmente em seus próprios navegadores, e o KoalaSync mantém sincronizadas apenas as ações de reprodução, como iniciar, pausar e avançar ou retroceder o vídeo.<br><br>Isso significa que cada pessoa pode usar o modo de tela cheia em seu próprio dispositivo, utilizar suas configurações de legenda ou áudio (se o reprodutor oferecer suporte) e manter o chat de voz separado, via Discord, Teamspeak ou outra ferramenta de chamada.<br><br>Para noites de filmes, essa abordagem pode parecer muito mais organizada do que uma pessoa transmitindo a tela. É especialmente útil para configurações baseadas em navegador, como Emby, Jellyfin, Plex, arquivos locais abertos no navegador, domínios privados ou outros sites que utilizam reprodutores de vídeo padrão do navegador.",
"ALT_SCREEN_LIMITS_TITLE": "Limitações reais",
"ALT_SCREEN_LIMITS_BODY": "O KoalaSync não substitui uma chamada de voz. Se você quiser conversar enquanto assiste, continue usando Discord, Teamspeak, Signal, WhatsApp ou qualquer ferramenta que utilize normalmente.<br><br>Todos também precisam ter acesso ao vídeo ou à página em seus próprios dispositivos. O KoalaSync não contorna assinaturas, logins, DRM, restrições regionais ou regras da plataforma.<br><br>Atualmente, funciona apenas em navegadores para desktop, não possui recursos integrados de chat ou voz, e a compatibilidade depende sempre do navegador e do reprodutor de vídeo.",
"ALT_SCREEN_SUMMARY_TITLE": "Resumo",
"ALT_SCREEN_SUMMARY_BODY": "O compartilhamento de tela é a opção mais simples quando você quer algo rápido e não se preocupa muito com qualidade, privacidade ou controle individual. O KoalaSync é mais adequado quando todos devem assistir localmente em seus próprios navegadores e a sessão deve proporcionar uma experiência mais próxima de realmente assistir juntos.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "Experimente o KoalaSync",
"MOCK_HOST_CONTROL_LABEL": "Controle do anfitrião",
"MOCK_HOST_CONTROL_TOGGLE": "Apenas eu posso controlar a reprodução",
"LABEL_SELECT_VIDEO": "Selecionar vídeo",
"OPTION_SELECT_TAB": "-- Selecione uma aba --",
"THEME_TOGGLE_LABEL": "Alternar modo claro/escuro",
"FAQ_SHOW_ALL": "Mostrar todas as perguntas",
"FAQ_HIDE_ALL": "Ocultar perguntas",
"SELF_SHOW_SETUP": "Mostrar guia de configuração",
"SELF_HIDE_SETUP": "Ocultar guia",
"HERO_TRUST": "Grátis · Código aberto · Sem conta",
"COMP_MORE_GUIDES": "Comparação completa e mais alternativas"
}
+96 -26
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "pt_PT",
"META_TITLE": "KoalaSync | Sincronize Netflix, YouTube e vídeos com os amigos",
"META_DESCRIPTION": "Veja Netflix, YouTube, Twitch e qualquer vídeo HTML5 em sincronia perfeita. Extensão gratuita e de código aberto. Sem registo.",
"SCHEMA_APP_DESC": "Veja Netflix, YouTube, Twitch e qualquer vídeo HTML5 em sincronia perfeita. Extensão gratuita e de código aberto. Sem registo.",
"OG_TITLE": "KoalaSync | Sincronize Netflix, Emby, Jellyfin e vídeos com os amigos",
"OG_DESCRIPTION": "Veja vídeos em sincronia perfeita com os amigos. Extensão de código aberto e focada na privacidade.",
"TWITTER_TITLE": "KoalaSync | Sincronize vídeos em streaming com os amigos",
"TWITTER_DESCRIPTION": "Veja vídeos com os amigos em sincronia perfeita. Extensão de código aberto focada na privacidade.",
"META_TITLE": "KoalaSync | Watch party para Netflix, YouTube e mais",
"META_DESCRIPTION": "Watch party na Netflix, YouTube, Twitch e qualquer vídeo HTML5. Extensão gratuita e open source para Chrome e Firefox. Sem conta, sem rastreio, sem anúncios.",
"SCHEMA_APP_DESC": "Watch party na Netflix, YouTube, Twitch e qualquer vídeo HTML5. Extensão gratuita e open source para Chrome e Firefox. Sem conta, sem rastreio, sem anúncios.",
"OG_TITLE": "KoalaSync | Watch party para Netflix, Emby, Jellyfin e quase qualquer vídeo",
"OG_DESCRIPTION": "Cria uma watch party na Netflix, Emby, Jellyfin, YouTube, Twitch e quase qualquer vídeo HTML5. Open source, privado e sem conta.",
"TWITTER_TITLE": "KoalaSync | Watch party para Netflix, Emby, Jellyfin e quase qualquer vídeo Extensão",
"TWITTER_DESCRIPTION": "Cria uma watch party na Netflix, Emby, Jellyfin, YouTube, Twitch e quase qualquer vídeo HTML5. Open source, privado e sem conta.",
"NAV_FEATURES": "Funcionalidades",
"NAV_HOW_IT_WORKS": "Como Funciona",
"HERO_TITLE": "Vejam Juntos.<span class='hero-line2'>Sincronia Perfeita.</span>",
"HERO_SUBTITLE": "A sua noite de cinema à distância e sem atrasos. Sem contas, sem recolha de dados. Partilhe um link e vejam juntos.",
"HERO_TITLE": "Transforma qualquer vídeo numa <span class='hero-highlight'>watch party</span>.",
"HERO_SUBTITLE": "A tua noite de cinema à distância, sem atrasos. Sem conta, sem rastreio, sem anúncios. Partilha um link e carreguem no play juntos.",
"HERO_MASCOT_ALT": "Um coala fofo a olhar para os botões de download",
"DEMO_SYNC": "Sincronizado",
"DEMO_HINT": "Demo ao vivo: dê play em qualquer aba e o KoalaSync mantém as duas em sincronia.",
"DEMO_NO_ROOM": "Ainda não conectado",
"DEMO_CREATE_ROOM": "Criar sala",
"DEMO_JOINED": "Entrou na sala",
"DEMO_TAB_SELECTED": "Separador selecionado",
"DEMO_SELECT_PLACEHOLDER": "Escolha um separador de vídeo…",
"MOBILE_DEMO_CTA": "Ver como funciona",
"MOBILE_DEMO_TITLE": "Demo ao vivo do KoalaSync",
"MOBILE_DEMO_CLOSE": "Fechar demo",
"ADD_TO_CHROME": "Adicionar ao Chrome",
"ADD_TO_FIREFOX": "Adicionar ao Firefox",
"COMPAT_HEADING": "Funciona nas suas plataformas favoritas",
@@ -35,20 +45,20 @@
"USE_CASE_3_DESC": "Aproveite as noites a dois. Ria das mesmas piadas e sinta as mesmas emoções exatamente no mesmo milissegundo.",
"WHY_TITLE": "Porquê o KoalaSync?",
"WHY_SUBTITLE": "Sincronização fiável, máxima privacidade e uso simples.",
"FEATURE_1_TITLE": "Controlo Total / Sincronia Instantânea",
"FEATURE_1_DESC": "Se um pausa, todos pausam. Se um avança, todos seguem. Sincronização em tempo real para todos os participantes.",
"FEATURE_1_TITLE": "Controlo do host e sincronização",
"FEATURE_1_DESC": "Tu decides quem controla a reprodução. Ativa o controlo do anfitrião para que apenas tu possas pausar e avançar, ou nomeia co-anfitriões.",
"FEATURE_2_TITLE": "Maratonas sem interrupções",
"FEATURE_2_DESC": "O KoalaSync deteta a mudança de episódios e pausa automaticamente até que todos tenham carregado o vídeo seguinte.",
"FEATURE_3_TITLE": "Sem Contas / Privacidade Absoluta",
"FEATURE_3_DESC": "Sem registos ou rastreios. O servidor corre apenas na RAM e apaga a sua sala assim que sair.",
"FEATURE_4_TITLE": "Suporte HTML5 Universal",
"FEATURE_4_DESC": "Funciona no YouTube, Twitch, Netflix, Jellyfin, Emby e qualquer site web. Nenhum dado de vídeo é enviado para nós.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby e quase qualquer página com vídeo HTML5. Só saem os sinais de sincronização, nunca o vídeo.",
"FEATURE_5_TITLE": "Servidores Próprios (Docker)",
"FEATURE_5_DESC": "Os nossos servidores são gratuitos, mas pode assumir o controlo total e iniciar o seu próprio servidor privado em segundos com Docker.",
"FEATURE_6_TITLE": "Convites Rápidos / Entrar com 1 Clique",
"FEATURE_6_DESC": "Nada de senhas complicadas. Partilhe o link e os seus amigos entram na sala automaticamente.",
"FEATURE_7_TITLE": "Compressor de Áudio Integrado",
"FEATURE_7_DESC": "O compressor de áudio reforça os diálogos suaves e baixa o volume das explosões. Funciona localmente, nenhum áudio sai do seu computador.",
"FEATURE_7_DESC": "Nunca mais mexer no volume. Um compressor integrado realça diálogos baixos e suaviza explosões altas, tudo processado localmente no teu navegador.",
"FEATURE_8_TITLE": "Código Aberto (Licença MIT)",
"FEATURE_8_DESC": "Todo o código é público no GitHub. 100% de transparência, sem software oculto.",
"COMP_TITLE": "KoalaSync vs Teleparty",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Políticas de privacidade do Teleparty:",
"COMP_FOOTNOTE_4": "Funciona em websites que permitem a injeção de scripts. Plataformas com DRM estrito podem ter limitações.",
"STEPS_TITLE": "Como Começar",
"STEP_1_TITLE": "Instalar a Extensão",
"STEP_1_DESC": "Adicione o KoalaSync ao seu navegador (Chrome ou Firefox).",
"STEP_1_ILLUS_DESC": "Extensão segura para ver vídeos",
"STEP_1_ILLUS_DL_CHROME": "Descarregar para Chrome",
"STEP_1_ILLUS_DL_FIREFOX": "Descarregar para Firefox",
"STEP_1_ILLUS_ACTIVE": "Extensão Ativa",
"STEP_1_ILLUS_READY": "Pronto!",
"STEP_2_TITLE": "Criar uma Sala",
"STEP_2_DESC": "Abra a extensão e clique em '+ Criar Nova Sala'. O link de convite é copiado automaticamente.",
"STEP_1_TITLE": "Criar e partilhar a sala",
"STEP_1_DESC": "Abra a extensão, crie uma sala com um clique e partilhe o link com os seus amigos. Eles entram instantaneamente pelo link de convite.",
"STEP_1_ILLUS_DESC": "Escolha um separador para ligar",
"STEP_1_ILLUS_DL_CHROME": "Ligar separador do YouTube",
"STEP_1_ILLUS_DL_FIREFOX": "Ligar separador do Netflix",
"STEP_1_ILLUS_ACTIVE": "Separador ligado",
"STEP_1_ILLUS_READY": "Pronto para ver em sincronia !",
"STEP_2_TITLE": "Selecionar o separador do vídeo",
"STEP_2_DESC": "Aceda ao separador do seu vídeo e selecione-o no menu da extensão. O KoalaSync liga os vossos leitores automaticamente.",
"STEP_2_ILLUS_ROOM": "Sala",
"STEP_2_ILLUS_SYNC": "Sincronia",
"STEP_2_ILLUS_SETTINGS": "Definições",
"STEP_2_ILLUS_CREATE": "+ Criar Nova Sala",
"STEP_2_ILLUS_MANUAL": "Manual / Avançado",
"STEP_2_ILLUS_COPIED": "Link copiado!",
"STEP_3_TITLE": "Partilhar e Sincronizar",
"STEP_3_DESC": "Envie o link aos amigos. Escolha um vídeo e desfrutem da reprodução juntos.",
"STEP_3_TITLE": "Desfrutar da sincronização",
"STEP_3_DESC": "Inicie a reprodução. Se alguém pausar, avançar ou reproduzir, o vídeo de todos mantém-se sincronizado em tempo real e sem atrasos.",
"STEP_3_ILLUS_IN_SYNC": "SINCRONIZADOS",
"SELF_TITLE": "Para utilizadores avançados",
"SELF_SUBTITLE": "Prefere gerir os seus dados? Inicie o seu próprio servidor em minutos com Docker.",
@@ -117,6 +127,8 @@
"FAQ_SUBTITLE": "Tudo o que precisa de saber.",
"FAQ_Q1": "Posso ver a Netflix ou YouTube com os meus amigos?",
"FAQ_A1": "Sim! O KoalaSync funciona no Netflix, YouTube, Twitch e qualquer site com vídeo HTML5. Instale a extensão, partilhe o link e está feito.",
"FAQ_Q_VIDEO_ACCESS": "O KoalaSync transmite o vídeo para os meus amigos?",
"FAQ_A_VIDEO_ACCESS": "Não. O KoalaSync não transmite, aloja, partilha nem contorna o acesso a conteúdos de vídeo. Cada pessoa vê o vídeo localmente no seu próprio navegador e precisa do seu próprio acesso ao site ou serviço de streaming, por exemplo a sua própria conta Netflix. O KoalaSync apenas sincroniza ações de reprodução, como reproduzir, pausar e avançar ou recuar.",
"FAQ_Q2": "É gratuito?",
"FAQ_A2": "Sim, é 100% gratuito e de código aberto. Sem funções premium bloqueadas e sem publicidade.",
"FAQ_Q3": "Funciona com Emby ou Jellyfin?",
@@ -156,7 +168,7 @@
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SINCRO",
"MOCK_14": "Ir para a posição dos outros",
"MOCK_15": "Última Atividade",
"MOCK_16": "À espera de 1 amigo (KoalaPC)...",
"MOCK_16": "À espera de 1 amigo (🐶 HappyDog)...",
"MOCK_17": "Ignorar e reproduzir agora",
"MOCK_18": "O seu Nome",
"MOCK_19": "Ocultar separadores sem vídeo",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSync não é afiliado à Netflix, Disney+, YouTube, Twitch, etc. Todas as marcas são propriedade dos seus titulares.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - Alternativas e comparações",
"ALT_INDEX_META_DESC": "Explore comparações honestas e guias detalhados entre o KoalaSync e outras extensões de watch party e soluções de streaming.",
"ALT_INDEX_HEADING": "Guias e comparações",
"ALT_INDEX_INTRO": "Como o KoalaSync se compara a outras ferramentas de watch party e soluções de streaming? Leia as nossas comparações honestas e técnicas para encontrar a melhor opção para as suas noites de cinema à distância.",
"ALT_INDEX_READ_COMPARISON": "Ler comparação →",
"ALT_INDEX_READ_GUIDE": "Ler guia →",
"ALT_INDEX_FOOTER_LINK": "Guias e comparações",
"ALT_TELEPARTY_TITLE": "KoalaSync vs Teleparty: que ferramenta de festa de exibição deve utilizar?",
"ALT_TELEPARTY_META_DESC": "Uma comparação prática entre o KoalaSync e o Teleparty para festas de exibição, chat integrado, suporte para dispositivos móveis, sincronização de vídeo no browser, servidores de media auto-hospedados e utilização de código aberto.",
"ALT_TELEPARTY_INTRO": "O KoalaSync e o Teleparty resolvem o mesmo problema irritante: ver algo em conjunto sem ter de fazer toda aquela rotina de \"3, 2, 1, reproduzir\". Apenas usam abordagens diferentes.<br><br>O Teleparty é mais indicado se pretende um chat integrado ou se pretende ver em dispositivos móveis. O KoalaSync está mais virado para sessões de vídeo em browsers de desktop, onde já estão a conversar no Discord, Teamspeak ou noutra chamada de voz e apenas querem que a reprodução permaneça sincronizada.<br><br>A principal razão para eu ter criado o KoalaSync foi a flexibilidade. Foi concebido para funcionar com praticamente qualquer site que utilize um leitor de vídeo comum de browser, e não apenas com uma lista fixa de plataformas.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Quando o Teleparty ainda pode fazer sentido",
"ALT_TELEPARTY_WHEN_TELE_BODY": "O Teleparty pode ainda ser a melhor opção se quiser que o chat faça parte da própria ferramenta de visualização em grupo. O KoalaSync não tem um chat integrado de momento. Assume que já está a falar noutro local, como o Discord, Teamspeak, WhatsApp, Signal ou numa chamada telefónica normal.<br><br>A compatibilidade com dispositivos móveis é outro factor importante. O Teleparty tem uma aplicação, enquanto o KoalaSync funciona apenas em browsers de desktop. Se pretende sincronizar a Netflix num telemóvel ou tablet, o KoalaSync não é a ferramenta certa para isso agora.<br><br>Portanto, se o chat e a compatibilidade com dispositivos móveis são os aspetos mais importantes para si, o Teleparty pode simplesmente adequar-se melhor à sua configuração.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "Quando o KoalaSync pode ser a melhor opção",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "O KoalaSync é a melhor opção se procura uma ferramenta flexível de sincronização para navegadores de desktop, em vez de algo limitado a uma lista restrita de plataformas.<br><br>Funciona com praticamente qualquer site que utilize um leitor de vídeo padrão de navegador. Isto é especialmente útil para Emby, Jellyfin, Plex, servidores de media privados, domínios locais ou sites de vídeo mais pequenos do que as ferramentas clássicas de watch party geralmente ignoram.<br><br>Além disso, é gratuito, de código aberto, não requer contas e pode ser alojado no seu próprio servidor, caso queira executar o seu próprio relay.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "A principal diferença",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "O Teleparty é mais um produto para ver vídeos em grupo, com chat e suporte para dispositivos móveis. O KoalaSync é mais uma camada de sincronização flexível para vídeos nos browsers.<br><br>Isto pode parecer uma pequena diferença, mas muda bastante o caso de utilização. Se precisa de uma aplicação completa para ver vídeos em grupo, com chat integrado, o Teleparty faz sentido. Se já tem chat de voz e apenas quer que os controlos de vídeo permaneçam sincronizados entre browsers, o KoalaSync é a opção mais simples.",
"ALT_TELEPARTY_LIMITS_TITLE": "Limitações honestas",
"ALT_TELEPARTY_LIMITS_BODY": "O KoalaSync não é mágico e não tenta esconder as suas limitações. Atualmente, funciona apenas em navegadores de desktop. Não possui chat integrado nem aplicação para dispositivos móveis.<br><br>A compatibilidade também depende do navegador e do reprodutor de vídeo. O KoalaSync deve funcionar com quase qualquer reprodutor de vídeo comum de navegador, mas os sites podem alterar os seus reprodutores, comportamento de DRM ou restrições de navegador a qualquer momento.<br><br>O KoalaSync não tem qualquer ligação com Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex ou qualquer outra marca aqui mencionada.",
"ALT_TELEPARTY_SUMMARY_TITLE": "Resumo",
"ALT_TELEPARTY_SUMMARY_BODY": "Se pretende um chat integrado ou suporte para dispositivos móveis, o Teleparty pode ser a melhor opção. Se procura uma sincronização gratuita e de código aberto para browsers de desktop que funcione com praticamente qualquer leitor de vídeo comum, incluindo servidores de multimédia auto-hospedados como o Emby, Jellyfin e Plex, o KoalaSync é provavelmente a melhor escolha.",
"ALT_SCREEN_TITLE": "KoalaSync vs. partilha de ecrã: quando é que a sincronização de vídeo é melhor?",
"ALT_SCREEN_META_DESC": "Uma comparação prática entre o KoalaSync e a partilha de ecrã para noites de cinema remotas, controlo de reprodução, qualidade de vídeo, privacidade, legendas e visualização através do browser.",
"ALT_SCREEN_INTRO": "Partilhar o ecrã funciona e, por vezes, é suficiente. Uma pessoa partilha o ecrã, todos os outros vêem e pronto.<br><br>Mas para longas noites de cinema ou para ver episódios juntos, pode tornar-se irritante. A qualidade do vídeo depende da velocidade de upload de quem partilha, do CPU, do browser e da compressão aplicada pelo Discord, Teamspeak, Zoom ou outra aplicação de chamadas. Além disso, apenas uma pessoa controla a reprodução.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "Quando a partilha de ecrã é suficiente",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "Partilhar o ecrã é bom quando se pretende a configuração mais rápida possível. Se apenas uma pessoa tiver acesso ao vídeo ou se todos concordarem que uma pessoa controla a reprodução, a pausa e o avanço/retrocesso, esta pode ser a opção mais simples.<br><br>Também faz sentido quando a qualidade não é um fator crucial, quando ninguém precisa das suas próprias legendas ou definições de áudio e quando a pessoa que está a partilhar se sente confortável em mostrar a janela do browser ou o ecrã.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "Onde a partilha de ecrã se torna irritante",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "A maior desvantagem é que nem todos estão a ver nos seus próprios leitores. Uma pessoa transmite o seu ecrã e todos os outros apenas vêem essa transmissão.<br><br>Isto pode significar pior qualidade de vídeo, áudio comprimido, reprodução diferida e menos controlo para todos os outros. As legendas e as faixas de áudio também são definidas pela pessoa que está a partilhar.<br><br>Há também a questão da privacidade. Se alternar entre janelas ou separadores, as pessoas podem ver brevemente os seus separadores do browser, e-mails, Reddit, chats ou outros conteúdos privados. Pode contornar isto com uma janela separada, mas ainda é algo que tem de considerar.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "Porque é que o KoalaSync pode parecer mais limpo",
"ALT_SCREEN_WHY_CLEANER_BODY": "O KoalaSync não transmite o seu ecrã. Cada pessoa vê o vídeo localmente no seu próprio browser, e o KoalaSync mantém sincronizadas apenas as ações de reprodução, como reproduzir, pausar e avançar/retroceder.<br><br>Isto significa que todos podem utilizar o modo de ecrã inteiro nos seus próprios dispositivos, utilizar as suas próprias definições de legendas ou áudio, se o leitor for compatível, e manter o chat de voz separado no Discord, Teamspeak ou outra chamada.<br><br>Para as noites de cinema, isto pode parecer muito mais limpo do que uma pessoa a transmitir o seu ecrã. É especialmente útil para configurações baseadas no browser, como Emby, Jellyfin, Plex, ficheiros locais abertos no browser, domínios privados ou outros sites com leitores de vídeo comuns do browser.",
"ALT_SCREEN_LIMITS_TITLE": "Limitações honestas",
"ALT_SCREEN_LIMITS_BODY": "O KoalaSync não substitui uma chamada de voz. Se quiser conversar enquanto assiste, utilize ainda o Discord, Teamspeak, Signal, WhatsApp ou qualquer outra aplicação que utilize normalmente.<br><br>Todos também precisam de ter acesso ao vídeo ou à página nos seus próprios dispositivos. O KoalaSync não ignora as subscrições, logins, DRM, restrições regionais ou regras de plataforma.<br><br>Atualmente, funciona apenas em navegadores de desktop, não tem chat ou recurso de voz integrados e a compatibilidade depende sempre do navegador e do leitor de vídeo.",
"ALT_SCREEN_SUMMARY_TITLE": "Resumo",
"ALT_SCREEN_SUMMARY_BODY": "Partilhar o ecrã é a opção mais simples quando se quer algo rápido e não se liga muito à qualidade, privacidade ou controlo individual. O KoalaSync é mais adequado quando todos devem assistir localmente nos seus próprios browsers e a sessão deve parecer mais como assistir em conjunto.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "Experimente o KoalaSync",
"MOCK_HOST_CONTROL_LABEL": "Controlo do anfitrião",
"MOCK_HOST_CONTROL_TOGGLE": "Apenas eu posso controlar a reprodução",
"LABEL_SELECT_VIDEO": "Selecionar Vídeo",
"OPTION_SELECT_TAB": "-- Selecionar um Separador --",
"THEME_TOGGLE_LABEL": "Alternar modo claro/escuro",
"FAQ_SHOW_ALL": "Mostrar todas as perguntas",
"FAQ_HIDE_ALL": "Ocultar perguntas",
"SELF_SHOW_SETUP": "Mostrar guia de configuração",
"SELF_HIDE_SETUP": "Ocultar guia",
"HERO_TRUST": "Grátis · Open source · Sem conta",
"COMP_MORE_GUIDES": "Comparação completa e mais alternativas"
}
+98 -28
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "ru_RU",
"META_TITLE": "KoalaSync | Синхронизация Netflix, YouTube и любого видео с друзьями",
"META_DESCRIPTION": "Смотрите Netflix, YouTube, Twitch и любые HTML5-видео в синхронизации с друзьями. Бесплатное расширение браузера с открытым кодом. Регистрация не требуется.",
"SCHEMA_APP_DESC": "Смотрите Netflix, YouTube, Twitch и любые HTML5-видео в синхронизации с друзьями. Бесплатное расширение браузера с открытым кодом. Регистрация не требуется.",
"OG_TITLE": "KoalaSync | Синхронизация Netflix, Emby, Jellyfin и почти любого видео с друзьями",
"OG_DESCRIPTION": "Смотрите Netflix, Emby, Jellyfin, YouTube, Twitch и почти любое HTML5-видео в идеальной синхронизации. Открытое и ориентированное на конфиденциальность расширение для Chrome и Firefox.",
"TWITTER_TITLE": "KoalaSync | Синхронизация Netflix, Emby, Jellyfin и почти любого видео с друзьями Расширение",
"TWITTER_DESCRIPTION": "Смотрите Netflix, Emby, Jellyfin, YouTube, Twitch и почти любое HTML5-видео в идеальной синхронизации с друзьями. Открытое и конфиденциальное расширение для Chrome и Firefox.",
"META_TITLE": "KoalaSync | Watch party для Netflix, YouTube и любых видео",
"META_DESCRIPTION": "Watch party в Netflix, YouTube, Twitch и любом HTML5-видео. Бесплатное расширение с открытым кодом для Chrome и Firefox. Без аккаунта, слежки и рекламы.",
"SCHEMA_APP_DESC": "Watch party в Netflix, YouTube, Twitch и любом HTML5-видео. Бесплатное расширение с открытым кодом для Chrome и Firefox. Без аккаунта, слежки и рекламы.",
"OG_TITLE": "KoalaSync | Watch party для Netflix, Emby, Jellyfin и почти любого видео",
"OG_DESCRIPTION": "Запускайте watch party в Netflix, Emby, Jellyfin, YouTube, Twitch и почти любом HTML5-видео. Open source, приватно и без аккаунта.",
"TWITTER_TITLE": "KoalaSync | Watch party для Netflix, Emby, Jellyfin и почти любого видео – Расширение",
"TWITTER_DESCRIPTION": "Запускайте watch party в Netflix, Emby, Jellyfin, YouTube, Twitch и почти любом HTML5-видео. Open source, приватно и без аккаунта.",
"NAV_FEATURES": "Функции",
"NAV_HOW_IT_WORKS": "Как это работает",
"HERO_TITLE": "Смотрите вместе.<span class='hero-line2'>Синхронно на 100%.</span>",
"HERO_SUBTITLE": "Ваш киновечер на расстоянии без задержек. Без регистрации и сбора данных. Просто поделитесь ссылкой и смотрите вместе.",
"HERO_TITLE": "Преврати любое видео в <span class='hero-highlight'>watch party</span>.",
"HERO_SUBTITLE": "Ваш киновечер на расстоянии, без задержек. Без аккаунта, без слежки, без рекламы. Просто поделитесь ссылкой и нажмите play вместе.",
"HERO_MASCOT_ALT": "Милый коала стоит и смотрит вниз на кнопки загрузки",
"DEMO_SYNC": "Синхронно",
"DEMO_HINT": "Живое демо: нажмите play в любой вкладке, и KoalaSync синхронизирует обе.",
"DEMO_NO_ROOM": "Ещё не подключено",
"DEMO_CREATE_ROOM": "Создать комнату",
"DEMO_JOINED": "В комнате",
"DEMO_TAB_SELECTED": "Вкладка выбрана",
"DEMO_SELECT_PLACEHOLDER": "Выберите вкладку с видео…",
"MOBILE_DEMO_CTA": "Посмотреть, как работает",
"MOBILE_DEMO_TITLE": "Живое демо KoalaSync",
"MOBILE_DEMO_CLOSE": "Закрыть демо",
"ADD_TO_CHROME": "Установить в Chrome",
"ADD_TO_FIREFOX": "Установить в Firefox",
"COMPAT_HEADING": "Работает на ваших любимых платформах",
@@ -35,20 +45,20 @@
"USE_CASE_3_DESC": "Сократите расстояние и наслаждайтесь свиданиями. Переживайте каждый поворот сюжета, смейтесь над шутками и делитесь эмоциями в одну и ту же миллисекунду.",
"WHY_TITLE": "Почему KoalaSync?",
"WHY_SUBTITLE": "Создан для надежной синхронизации, конфиденциальности и простой настройки.",
"FEATURE_1_TITLE": "Полный контроль и мгновенный синхро",
"FEATURE_1_DESC": "Один ставит на паузу — пауза у всех. Один перематывает — все перематывают. Наш двухфазный протокол координирует воспроизведение в реальном времени у всех участников.",
"FEATURE_1_TITLE": "Управление хоста и синхронизация",
"FEATURE_1_DESC": "Вы решаете, кто управляет воспроизведением. Включите управление ведущего, чтобы только вы могли ставить на паузу и перемотку, или назначайте со-ведущих.",
"FEATURE_2_TITLE": "Бесконечные марафоны",
"FEATURE_2_DESC": "Синхронное автовоспроизведение. KoalaSync автоматически определяет переход на следующую серию и ждет, пока она загрузится у всех участников.",
"FEATURE_3_TITLE": "Без аккаунтов и хранения данных",
"FEATURE_3_DESC": "Без регистрации, без отслеживания, без следов на диске. Сервер работает исключительно во временной RAM и полностью стирает вашу комнату при выходе.",
"FEATURE_4_TITLE": "Универсальная поддержка HTML5",
"FEATURE_4_DESC": "Работает на YouTube, Twitch, Netflix, Jellyfin, Emby и почти на любой веб-странице со стандартным HTML5-видеотегом. Подходит для локальных медиатек. Никакие видеоданные никогда не отправляются на наши серверы — только метаданные синхронизации.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby и почти любая страница с HTML5-видео. Из браузера уходят только сигналы синхронизации, но не само видео.",
"FEATURE_5_TITLE": "Self-Hosted и готов к Docker",
"FEATURE_5_DESC": "Наши официальные серверы бесплатны и быстры, но вы можете получить полный контроль. Запустите свой собственный приватный ретранслятор в Docker за секунды.",
"FEATURE_6_TITLE": "Быстрые приглашения в 1 клик",
"FEATURE_6_DESC": "Не нужно обмениваться IP-адресами или паролями. Отправьте сгенерированную ссылку друзьям, чтобы они автоматически вошли в комнату в один клик.",
"FEATURE_7_TITLE": "Выравнивание громкости / Аудио компрессор",
"FEATURE_7_DESC": "Больше никакой постоянной регулировки громкости. Встроенный аудио компрессор усиливает тихие диалоги и приглушает громкие взрывы. Работает на Netflix, Prime Video, Disney+, YouTube и любом HTML5-видео. Полностью локально — никакие аудиоданные не покидают ваш компьютер.",
"FEATURE_7_DESC": "Больше не нужно крутить громкость. Встроенный компрессор поднимает тихие диалоги и смягчает громкие взрывы, обрабатывая всё локально в браузере.",
"FEATURE_8_TITLE": "Открытый исходный код (Лицензия MIT)",
"FEATURE_8_DESC": "Весь исходный код публично доступен для проверки на GitHub. Никаких «черных ящиков», никаких проприетарных бинарных файлов (blobs) — 100% прозрачность от relay-сервера до расширения браузера.",
"COMP_TITLE": "KoalaSync против Teleparty",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Политика конфиденциальности Teleparty и сбор трекеров:",
"COMP_FOOTNOTE_4": "Работает на сайтах, разрешающих инъекцию скриптов в стандартные видеотеги HTML5. Сайты с очень строгой политикой безопасности контента (CSP), защитой от копирования DRM или сильно обфусцированными оболочками плееров (например, сложные теневые DOM) могут блокировать автоматическое управление.",
"STEPS_TITLE": "С чего начать",
"STEP_1_TITLE": "Установить расширение",
"STEP_1_DESC": "Добавьте KoalaSync в браузер из Chrome Web Store, Firefox Add-ons or скачайте последний ZIP-архив разработчика с GitHub.",
"STEP_1_ILLUS_DESC": "Ориентированный на конфиденциальность синхронизатор видео",
"STEP_1_ILLUS_DL_CHROME": "Скачать для Chrome",
"STEP_1_ILLUS_DL_FIREFOX": "Скачать для Firefox",
"STEP_1_ILLUS_ACTIVE": "Расширение активно",
"STEP_1_ILLUS_READY": "Готовы смотреть вместе!",
"STEP_2_TITLE": "Создать комнату",
"STEP_2_DESC": "Откройте меню расширения и нажмите «+ Создать новую комнату». KoalaSync автоматически сгенерирует ID и пароль, войдет в нее и скопирует ссылку-приглашение.",
"STEP_1_TITLE": "Создать комнату и поделиться",
"STEP_1_DESC": "Откройте расширение, создайте комнату одним кликом и отправьте ссылку друзьям. Они мгновенно подключатся по приглашению.",
"STEP_1_ILLUS_DESC": "Выберите вкладку для подключения",
"STEP_1_ILLUS_DL_CHROME": "Подключить вкладку YouTube",
"STEP_1_ILLUS_DL_FIREFOX": "Подключить вкладку Netflix",
"STEP_1_ILLUS_ACTIVE": "Вкладка подключена",
"STEP_1_ILLUS_READY": "Готово к синхронному просмотру !",
"STEP_2_TITLE": "Выбрать вкладку с видео",
"STEP_2_DESC": "Перейдите на вкладку с видео и выберите её в меню расширения. KoalaSync автоматически свяжет ваши плееры.",
"STEP_2_ILLUS_ROOM": "Комната",
"STEP_2_ILLUS_SYNC": "Синхро",
"STEP_2_ILLUS_SETTINGS": "Опции",
"STEP_2_ILLUS_CREATE": "+ Создать комнату",
"STEP_2_ILLUS_MANUAL": "Ручное подключение / Дополнительно",
"STEP_2_ILLUS_COPIED": "Ссылка скопирована!",
"STEP_3_TITLE": "Поделиться и смотреть",
"STEP_3_DESC": "Отправьте ссылку друзьям. Как только они присоединятся, выберите вкладку с видео и наслаждайтесь синхронным просмотром.",
"STEP_3_TITLE": "Наслаждаться синхронизацией",
"STEP_3_DESC": "Нажмите кнопку воспроизведения. Когда кто-то ставит на паузу, перематывает или запускает видео, просмотр синхронизируется у всех без задержек.",
"STEP_3_ILLUS_IN_SYNC": "В СИНХРОНЕ",
"SELF_TITLE": "Для селф-хостеров",
"SELF_SUBTITLE": "Не доверяете нашему серверу? Сохраняйте полный контроль над данными. Разверните собственный приватный сервер за пару минут.",
@@ -117,6 +127,8 @@
"FAQ_SUBTITLE": "Всё, что нужно знать о синхронном просмотре видео с друзьями.",
"FAQ_Q1": "Могу ли я смотреть Netflix, Emby или Jellyfin синхронно с друзьями?",
"FAQ_A1": "Да! KoalaSync работает с Netflix, YouTube, Twitch и любым сайтом с HTML5-видеоплеером — включая Emby, Jellyfin и собственные медиасерверы. Установите расширение, создайте комнату, поделитесь ссылкой и смотрите в идеальной синхронизации.",
"FAQ_Q_VIDEO_ACCESS": "KoalaSync транслирует видео моим друзьям?",
"FAQ_A_VIDEO_ACCESS": "Нет. KoalaSync не транслирует, не размещает, не передает видеоконтент и не обходит доступ к нему. Каждый участник смотрит видео локально в своем браузере и должен иметь собственный доступ к сайту или стриминговому сервису, например собственный аккаунт Netflix. KoalaSync синхронизирует только действия воспроизведения: запуск, паузу и перемотку.",
"FAQ_Q2": "KoalaSync — бесплатная альтернатива Teleparty?",
"FAQ_A2": "100% бесплатно и с открытым исходным кодом под лицензией MIT. Без премиум-уровней, без заблокированных функций, без рекламы. В отличие от Teleparty, сервер KoalaSync работает только в ОЗУ — без сбора данных и учётных записей.",
"FAQ_Q3": "Работает ли KoalaSync с Emby, Jellyfin и собственными медиасерверами?",
@@ -124,9 +136,9 @@
"FAQ_Q4": "Какие браузеры и платформы поддерживают KoalaSync?",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave и все браузеры на базе Chromium. Установите из Chrome Web Store или Firefox Add-ons.",
"FAQ_Q5": "Всем ли моим друзьям нужно устанавливать расширение?",
"FAQ_A5": "Да, каждому участнику нужен KoalaSync но без регистрации и создания учётной записи. Просто перейдите по ссылке-приглашению, установите расширение (если ещё нет) и вы автоматически в комнате.",
"FAQ_A5": "Да, каждому участнику нужен KoalaSync, но без регистрации и создания учётной записи. Просто перейдите по ссылке-приглашению, установите расширение (если ещё нет) и вы автоматически в комнате.",
"FAQ_Q6": "Мои данные в безопасности? Что, если я не доверяю официальному серверу?",
"FAQ_A6": "Полностью безопасно. Relay-сервер KoalaSync работает только в ОЗУ все данные сессии удаляются при выходе из комнаты. Никаких баз данных, логов, аналитики и учётных записей. Если хотите полный контроль: разверните свой приватный relay-сервер через Docker за пару минут. Исходный код полностью открыт и доступен для аудита на GitHub.",
"FAQ_A6": "Полностью безопасно. Relay-сервер KoalaSync работает только в ОЗУ, все данные сессии удаляются при выходе из комнаты. Никаких баз данных, логов, аналитики и учётных записей. Если хотите полный контроль: разверните свой приватный relay-сервер через Docker за пару минут. Исходный код полностью открыт и доступен для аудита на GitHub.",
"FAQ_Q7": "Можно ли исправить тихие диалоги и громкие взрывы при просмотре видео в браузере?",
"FAQ_A7": "Да. KoalaSync 2.2.0 включает встроенный аудио компрессор, который уменьшает разницу между тихими и громкими частями видео. Он работает локально через Web Audio API — аудиоданные никогда не покидают ваш компьютер. Откройте попап, создайте комнату, перейдите в Настройки → Audio Processing, включите компрессор и выберите пресет. Работает на Netflix, Prime Video, Disney+, YouTube и любом сайте с HTML5-видео.",
"HOWTO_TITLE": "Как синхронизировать видео с друзьями с помощью KoalaSync",
@@ -155,7 +167,7 @@
"MOCK_12": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"14\" height=\"14\" aria-hidden=\"true\"><rect x=\"6\" y=\"4\" width=\"4\" height=\"16\"/><rect x=\"14\" y=\"4\" width=\"4\" height=\"16\"/></svg> Пауза",
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SYNC",
"MOCK_14": "Последняя активность",
"MOCK_15": "Ожидание 1 участника (KoalaPC)...",
"MOCK_15": "Ожидание 1 участника (🐶 HappyDog)...",
"MOCK_16": "Пропустить и играть",
"MOCK_17": "Ваше имя",
"MOCK_18": "Скрыть лишние вкладки",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSync не связан, не спонсируется и не аффилирован с Netflix, Disney+, Amazon, YouTube, Twitch или любой другой стриминговой платформой. Все товарные знаки являются собственностью их соответствующих владельцев.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync — альтернативы и сравнения",
"ALT_INDEX_META_DESC": "Изучите честные сравнения и подробные руководства о KoalaSync, других расширениях для совместного просмотра и стриминговых решениях.",
"ALT_INDEX_HEADING": "Руководства и сравнения",
"ALT_INDEX_INTRO": "Как KoalaSync выглядит на фоне других инструментов для совместного просмотра и стриминговых решений? Прочитайте наши честные технические сравнения, чтобы выбрать лучший вариант для удалённых киновечеров.",
"ALT_INDEX_READ_COMPARISON": "Читать сравнение →",
"ALT_INDEX_READ_GUIDE": "Читать руководство →",
"ALT_INDEX_FOOTER_LINK": "Руководства и сравнения",
"ALT_TELEPARTY_TITLE": "KoalaSync или Teleparty: какой инструмент выбрать для совместного просмотра?",
"ALT_TELEPARTY_META_DESC": "Практическое сравнение KoalaSync и Teleparty: встроенный чат, поддержка мобильных устройств, синхронизация видео в браузере, использование собственных медиасерверов и открытый исходный код.",
"ALT_TELEPARTY_INTRO": "И KoalaSync, и Teleparty решают одну и ту же надоедливую проблему: как смотреть что-либо вместе, не прибегая к команде «3, 2, 1, начали!». Просто они используют разные подходы.<br><br>Teleparty — отличный выбор, если вам нужен встроенный чат или возможность просмотра на мобильном устройстве. KoalaSync больше подходит для совместного просмотра через браузер на компьютере: вы общаетесь, например, в Discord, TeamSpeak или другом голосовом чате, а программа просто обеспечивает синхронизацию воспроизведения.<br><br>Главной причиной создания KoalaSync стала гибкость. Инструмент задуман так, чтобы работать практически с любым сайтом, использующим стандартный браузерный видеоплеер, а не только с фиксированным списком платформ. Это касается и решений для самостоятельного хостинга (например, Emby, Jellyfin и Plex), и локальных или частных доменов, и множества обычных веб-страниц с видео.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Когда Teleparty все же может быть полезен",
"ALT_TELEPARTY_WHEN_TELE_BODY": "Teleparty может оказаться лучшим выбором, если вам важно наличие чата непосредственно в самом инструменте для просмотра. В KoalaSync встроенного чата пока нет; предполагается, что вы общаетесь на другой платформе — например, в Discord, TeamSpeak, WhatsApp, Signal или по обычной телефонной связи.<br><br>Еще один важный фактор — поддержка мобильных устройств. У Teleparty есть приложение, тогда как KoalaSync пока работает только в браузерах на ПК. Если вы хотите синхронизировать просмотр Netflix на телефоне или планшете, KoalaSync для этого сейчас не подойдет.<br><br>Таким образом, если наличие чата и поддержка мобильных устройств для вас в приоритете, Teleparty может лучше вписаться в ваш сценарий использования.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "Когда лучше выбрать KoalaSync",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "KoalaSync — отличный выбор, если вам нужен гибкий инструмент для синхронизации видео в десктопном браузере, а не решение, привязанное к ограниченному списку платформ.<br><br>Сервис работает практически с любым сайтом, использующим стандартный браузерный видеоплеер. Это особенно удобно для Emby, Jellyfin, Plex, частных медиасерверов, локальных доменов или небольших видеосайтов, которые обычно игнорируются классическими инструментами для совместного просмотра.<br><br>Кроме того, это бесплатный проект с открытым исходным кодом; он не требует создания учетной записи и позволяет развернуть собственный сервер-посредник (relay), если вы хотите полного контроля. Я создал его, потому что искал решение, уважающее конфиденциальность и совместимое с моей собственной системой Emby, а не очередную закрытую платформу, работающую лишь с несколькими крупными стриминговыми сервисами.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "Главное отличие",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "Teleparty — это полноценный сервис для совместного просмотра со встроенным чатом и поддержкой мобильных устройств. KoalaSync — это скорее гибкий инструмент для синхронизации воспроизведения видео в браузере.<br><br>Разница кажется незначительной, но она существенно меняет сценарий использования. Если вам нужно комплексное приложение для совместного просмотра со встроенным чатом, Teleparty — отличный вариант. Если же у вас уже есть голосовая связь и вам просто нужно синхронизировать управление видео в разных браузерах, KoalaSync подойдет лучше и проще.",
"ALT_TELEPARTY_LIMITS_TITLE": "Честно об ограничениях",
"ALT_TELEPARTY_LIMITS_BODY": "KoalaSync не творит чудес и не скрывает своих ограничений. На данный момент сервис работает только в десктопных браузерах. В нем нет встроенного чата или мобильного приложения.<br><br>Совместимость также зависит от браузера и видеоплеера. KoalaSync должен работать практически с любым стандартным браузерным плеером, однако сайты могут в любой момент изменить свои плееры, механизмы DRM или ограничения браузера.<br><br>KoalaSync не связан с Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex или любыми другими упомянутыми здесь брендами. Названия используются исключительно для иллюстрации типичных сценариев использования.",
"ALT_TELEPARTY_SUMMARY_TITLE": "Краткий обзор",
"ALT_TELEPARTY_SUMMARY_BODY": "Если вам нужен встроенный чат или поддержка мобильных устройств, Teleparty может подойти лучше. Если же вы ищете бесплатное решение с открытым исходным кодом для синхронизации в настольном браузере, работающее практически с любым стандартным видеоплеером — включая собственные медиасерверы, такие как Emby, Jellyfin и Plex, — то, вероятно, лучшим выбором станет KoalaSync.",
"ALT_SCREEN_TITLE": "KoalaSync против демонстрации экрана: когда лучше использовать синхронизацию видео?",
"ALT_SCREEN_META_DESC": "Практическое сравнение KoalaSync и демонстрации экрана для совместного просмотра фильмов на расстоянии: управление воспроизведением, качество видео, конфиденциальность, субтитры и просмотр через браузер.",
"ALT_SCREEN_INTRO": "Демонстрация экрана — рабочий вариант, и порой его вполне достаточно. Один человек транслирует свой экран, остальные смотрят — и всё готово.<br><br>Однако при длительных киносеансах или совместном просмотре сериалов это может начать раздражать. Качество видео зависит от скорости исходящего соединения, мощности процессора и браузера того, кто ведет трансляцию, а также от алгоритмов сжатия, используемых в Discord, TeamSpeak, Zoom или других приложениях для связи. Кроме того, полноценно управлять воспроизведением может только один человек.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "Когда достаточно демонстрации экрана",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "Демонстрация экрана отлично подходит, если нужно максимально быстро всё настроить. Если доступ к видео есть только у одного человека или если всех устраивает, что один человек управляет воспроизведением (запуск, пауза, перемотка), то это может быть самым простым вариантом.<br><br>Этот способ также подходит, когда качество не критично, никому не нужны индивидуальные настройки субтитров или звука, а ведущему удобно демонстрировать окно браузера или экран.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "Когда демонстрация экрана может раздражать",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "Главный недостаток в том, что зрители не смотрят видео в собственных плеерах. Один человек транслирует свой экран, а остальные видят только эту трансляцию.<br><br>Это может привести к снижению качества видео, сжатию звука, задержкам воспроизведения и ограничению возможностей управления для остальных. Субтитры и звуковые дорожки также будут такими, какие выбрал ведущий.<br><br>Не стоит забывать и о конфиденциальности. Если вы переключите окно или вкладку, зрители могут на мгновение увидеть ваши вкладки браузера, электронную почту, Reddit, чаты или другую личную информацию. Эту проблему можно решить, используя отдельное окно, но об этом всё равно приходится помнить.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "Почему KoalaSync может быть удобнее",
"ALT_SCREEN_WHY_CLEANER_BODY": "KoalaSync не транслирует ваш экран. Каждый смотрит видео локально в своем браузере, а KoalaSync лишь синхронизирует действия: запуск, паузу и перемотку.<br><br>Это позволяет каждому развернуть видео на весь экран своего устройства, использовать собственные настройки субтитров или звука (если плеер это поддерживает) и отдельно общаться в голосовом чате через Discord, TeamSpeak или другой сервис.<br><br>Для совместного просмотра фильмов это решение может быть гораздо удобнее, чем трансляция экрана одним человеком. Оно особенно полезно при использовании веб-сервисов (например, Emby, Jellyfin, Plex), локальных файлов, открытых в браузере, закрытых ресурсов или любых других сайтов со стандартными браузерными видеоплеерами.",
"ALT_SCREEN_LIMITS_TITLE": "Честный взгляд на ограничения",
"ALT_SCREEN_LIMITS_BODY": "KoalaSync не заменяет голосовую связь. Если во время просмотра вы хотите общаться, используйте Discord, TeamSpeak, Signal, WhatsApp или любой другой привычный вам сервис.<br><br>Каждому участнику также необходим доступ к видео или веб-странице на собственном устройстве. KoalaSync не обходит требования к подписке, авторизации, DRM-защите, региональные ограничения или правила платформ.<br><br>На данный момент сервис работает только в браузерах на ПК, не имеет встроенного чата или функции голосовой связи, а совместимость зависит от конкретного браузера и видеоплеера.",
"ALT_SCREEN_SUMMARY_TITLE": "Краткий обзор",
"ALT_SCREEN_SUMMARY_BODY": "Демонстрация экрана — самый простой вариант, если нужно быстро всё организовать и вас не особо волнуют качество, конфиденциальность или возможность индивидуального управления. KoalaSync лучше подходит для ситуаций, когда каждый участник смотрит видео локально в своем браузере, а сам сеанс больше напоминает реальный совместный просмотр.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "Попробовать KoalaSync",
"MOCK_HOST_CONTROL_LABEL": "Управление ведущим",
"MOCK_HOST_CONTROL_TOGGLE": "Только я могу управлять воспроизведением",
"LABEL_SELECT_VIDEO": "Выбрать видео",
"OPTION_SELECT_TAB": "-- Выберите вкладку --",
"THEME_TOGGLE_LABEL": "Переключить светлую/тёмную тему",
"FAQ_SHOW_ALL": "Показать все вопросы",
"FAQ_HIDE_ALL": "Скрыть вопросы",
"SELF_SHOW_SETUP": "Показать инструкцию по установке",
"SELF_HIDE_SETUP": "Скрыть инструкцию",
"HERO_TRUST": "Бесплатно · Открытый код · Без аккаунта",
"COMP_MORE_GUIDES": "Полное сравнение и другие альтернативы"
}
+102 -32
View File
@@ -5,18 +5,28 @@
"LANG_TOGGLE_URL": "../",
"LANG_TOGGLE_TEXT": "EN",
"OG_LOCALE": "tr_TR",
"META_TITLE": "KoalaSync | Netflix, YouTube ve Herhangi Bir Videoyu Arkadaşlarınızla Eşitleyin",
"META_DESCRIPTION": "Netflix, YouTube, Twitch ve herhangi bir HTML5 videoyu arkadaşlarınızla mükemmel uyum içinde izleyin. Chrome ve Firefox için ücretsiz, açık kaynaklı tarayıcı eklentisi. Kayıt gerekmez.",
"SCHEMA_APP_DESC": "Netflix, YouTube, Twitch ve herhangi bir HTML5 videoyu arkadaşlarınızla mükemmel uyum içinde izleyin. Chrome ve Firefox için ücretsiz, açık kaynaklı tarayıcı eklentisi. Kayıt gerekmez.",
"OG_TITLE": "KoalaSync | Netflix, Emby, Jellyfin ve Neredeyse Her Videoyu Arkadaşlarınızla Eşitleyin",
"OG_DESCRIPTION": "Netflix, Emby, Jellyfin, YouTube, Twitch ve neredeyse tüm HTML5 videolarını mükemmel bir senkronizasyonla izleyin. Chrome ve Firefox için gizlilik öncelikli, açık kaynaklı tarayıcı eklentisi.",
"TWITTER_TITLE": "KoalaSync | Netflix, Emby, Jellyfin ve Neredeyse Her Videoyu Arkadaşlarla Eşitle Tarayıcı Eklentisi",
"TWITTER_DESCRIPTION": "Netflix, Emby, Jellyfin, YouTube, Twitch ve neredeyse tüm HTML5 videolarını arkadaşlarınızla mükemmel bir senkronizasyonla izleyin. Chrome ve Firefox için gizlilik öncelikli, açık kaynaklı tarayıcı eklentisi.",
"META_TITLE": "KoalaSync | Netflix, YouTube ve Her Video için Watch Party",
"META_DESCRIPTION": "Netflix, YouTube, Twitch ve her HTML5 videoda watch party. Chrome ve Firefox için ücretsiz, açık kaynak uzantı. Hesap yok, takip yok, reklam yok.",
"SCHEMA_APP_DESC": "Netflix, YouTube, Twitch ve her HTML5 videoda watch party. Chrome ve Firefox için ücretsiz, açık kaynak uzantı. Hesap yok, takip yok, reklam yok.",
"OG_TITLE": "KoalaSync | Netflix, Emby, Jellyfin ve neredeyse her video için watch party",
"OG_DESCRIPTION": "Netflix, Emby, Jellyfin, YouTube, Twitch ve neredeyse tüm HTML5 videolarında watch party başlatın. Açık kaynak, gizlilik odaklı ve hesapsız.",
"TWITTER_TITLE": "KoalaSync | Netflix, Emby, Jellyfin ve neredeyse her video için watch party Eklenti",
"TWITTER_DESCRIPTION": "Netflix, Emby, Jellyfin, YouTube, Twitch ve neredeyse tüm HTML5 videolarında watch party başlatın. Açık kaynak, gizlilik odaklı ve hesapsız.",
"NAV_FEATURES": "Özellikler",
"NAV_HOW_IT_WORKS": "Nasıl Çalışır",
"HERO_TITLE": "Birlikte İzleyin.<span class='hero-line2'>Mükemmel Eşitleyin.</span>",
"HERO_SUBTITLE": "Gecikmesiz uzaktan film geceniz. Kayıt yok, veri toplama yok. Sadece bir bağlantı paylaşın ve birlikte izleyin.",
"HERO_TITLE": "Herhangi bir videoyu <span class='hero-highlight'>watch party</span>'ye dönüştür.",
"HERO_SUBTITLE": "Gecikmesiz uzaktan film gecen. Hesap yok, takip yok, reklam yok. Sadece bir bağlantı paylaş ve birlikte oynatın.",
"HERO_MASCOT_ALT": "İndirme düğmelerine bakan sevimli bir koala",
"DEMO_SYNC": "Senkronize",
"DEMO_HINT": "Canlı demo: sekmelerden birinde oynat'a basın, KoalaSync ikisini de senkronize tutar.",
"DEMO_NO_ROOM": "Henüz bağlı değil",
"DEMO_CREATE_ROOM": "Oda oluştur",
"DEMO_JOINED": "Odaya katıldı",
"DEMO_TAB_SELECTED": "Sekme seçildi",
"DEMO_SELECT_PLACEHOLDER": "Bir video sekmesi seçin…",
"MOBILE_DEMO_CTA": "Nasıl çalıştığını izle",
"MOBILE_DEMO_TITLE": "KoalaSync canlı demo",
"MOBILE_DEMO_CLOSE": "Demoyu kapat",
"ADD_TO_CHROME": "Chrome'a Ekle",
"ADD_TO_FIREFOX": "Firefox'a Ekle",
"COMPAT_HEADING": "En sevdiğiniz platformlarda çalışır",
@@ -35,22 +45,22 @@
"USE_CASE_3_DESC": "Mesafeleri köprüleyin ve buluşma gecelerinin tadını çıkarın. Her olay örgüsünü deneyimleyin, aynı şakalara gülün ve duygusal anları tam olarak aynı milisaniyede paylaşın.",
"WHY_TITLE": "Neden KoalaSync?",
"WHY_SUBTITLE": "Güvenilir eşitleme, önce gizlilik ilkesi ve kolay kurulum için tasarlandı.",
"FEATURE_1_TITLE": "Tam Kontrol / Anında Eşitleme",
"FEATURE_1_DESC": "Biri duraklatır, herkes duraklatır. Biri arar, herkes takip eder. İki aşamalı eşitleme protokolümüz, oynatmayı tüm katılımcılar arasında gerçek zamanlı olarak koordine eder.",
"FEATURE_1_TITLE": "Oturum Sahibi Kontrolü ve Senkronizasyon",
"FEATURE_1_DESC": "Oynatmayı kimin kontrol edeceğine siz karar verin. Yalnızca sizin durdurup sarabilmeniz için sunucu kontrolünü etkinleştirin veya ortak sunucular atayın.",
"FEATURE_2_TITLE": "Kesintisiz İzleme Keyfi",
"FEATURE_2_DESC": "Senkronize otomatik oynatma. KoalaSync, bölüm geçişlerini otomatik olarak algılar ve tüm bağlantılar bir sonraki videoyu başarıyla yükleyene kadar oynatmayı bekletir.",
"FEATURE_3_TITLE": "Sıfır Hesap / Saf Gizlilik",
"FEATURE_3_DESC": "Kayıt yok, takip yok, kalıcı iz yok. Sunucu tamamen geçici RAM üzerinde çalışır ve ayrıldığınızda odanızı temizler.",
"FEATURE_4_TITLE": "Evrensel HTML5 Desteği",
"FEATURE_4_DESC": "YouTube, Twitch, Netflix, Jellyfin, Emby ve HTML5 video öğesi içeren neredeyse tüm standart web sayfalarında çalışır. Ayrıca özel barındırılan kurulumlarla da uyumludur. Sunucularımıza hiçbir video verisi asla gönderilmez — yalnızca senkronizasyon meta verileri gönderilir.",
"FEATURE_4_DESC": "Netflix, YouTube, Twitch, Jellyfin, Emby ve HTML5 videosu olan neredeyse her sayfa. Tarayıcından yalnızca sync sinyalleri çıkar, video asla.",
"FEATURE_5_TITLE": "Kendi Kendine Barındırılabilir ve Docker Uyumlu",
"FEATURE_5_DESC": "Resmi sunucularımız ücretsiz ve hızlıdır, ancak isterseniz tüm kontrolü elinize alabilirsiniz. Docker aracılığıyla saniyeler içinde kendi özel aktarım sunucunuzu başlatın.",
"FEATURE_6_TITLE": "Anında Davetler / Tek Tıkla Katılım",
"FEATURE_6_DESC": "Takas edilecek IP adresi veya şifre yok. Arkadaşlarınızın tek bir tıklamayla odanıza otomatik olarak katılması için oluşturulan bir davet bağlantısını paylaşın.",
"FEATURE_7_TITLE": "Ses Seviyesi Dengeleme / Ses Sıkıştırıcı",
"FEATURE_7_DESC": "Ses düğmesiyle sürekli oynamak yok. Dahili ses sıkıştırıcı, sessiz diyalogları güçlendirir ve yüksek patlamaları yumuşatır. Netflix, Prime Video, Disney+, YouTube ve herhangi bir HTML5 videosunda çalışır. Tamamen yerel — hiçbir ses verisi bilgisayarınızı terk etmez.",
"FEATURE_7_DESC": "Ses düğmesiyle uğraşmak yok. Yerleşik bir kompresör kısık diyalogları yükseltir, gürültülü patlamaları yumuşatır; hepsi tarayıcında yerel olarak işlenir.",
"FEATURE_8_TITLE": "Açık Kaynak (MIT Lisansı)",
"FEATURE_8_DESC": "Tüm kod tabanı GitHub'da herkese açık olarak incelenebilir. Kapalı kutular yok, tescilli blob'lar yok aktarma sunucusundan (relay server) tarayıcı eklentisine kadar %100 şeffaflık.",
"FEATURE_8_DESC": "Tüm kod tabanı GitHub'da herkese açık olarak incelenebilir. Kapalı kutular yok, tescilli blob'lar yok, aktarma sunucusundan (relay server) tarayıcı eklentisine kadar %100 şeffaflık.",
"COMP_TITLE": "KoalaSync vs. Teleparty",
"COMP_SUBTITLE": "Açık kaynaklı, reklamsız ve önce gizlilik diyen yöntemin neden birlikte izlemenin en iyi yolu olduğunu görün.",
"COMP_COL_FEATURE": "Özellik",
@@ -87,23 +97,23 @@
"COMP_FOOTNOTE_3": "Teleparty gizlilik politikaları ve takip verisi toplama:",
"COMP_FOOTNOTE_4": "Standart HTML5 video etiketlerine komut dosyası eklenmesine izin veren web sitelerinde çalışır. Çok katı İçerik Güvenliği Politikalarına (CSP) sahip, DRM kopya korumalı veya yoğun şekilde gizlenmiş oynatıcı sarmalayıcıları (karmaşık gölge DOM'lar gibi) olan web siteleri otomatik kontrolü veya enjeksiyonu kısıtlayabilir.",
"STEPS_TITLE": "Başlarken",
"STEP_1_TITLE": "Eklentiyi Kur",
"STEP_1_DESC": "KoalaSync'i Chrome Web Mağazası, Firefox Eklentileri aracılığıyla tarayıcınıza ekleyin veya en son geliştirici ZIP dosyasını GitHub'dan indirin.",
"STEP_1_ILLUS_DESC": "Gizlilik öncelikli video senkronize edici",
"STEP_1_ILLUS_DL_CHROME": "Chrome için İndir",
"STEP_1_ILLUS_DL_FIREFOX": "Firefox için İndir",
"STEP_1_ILLUS_ACTIVE": "Eklenti Etkin",
"STEP_1_ILLUS_READY": "Birlikte izlemeye hazır!",
"STEP_2_TITLE": "Oda Oluştur",
"STEP_2_DESC": "Eklenti açılır penceresini açın ve '+ Yeni Oda Oluştur' düğmesine tıklayın. KoalaSync otomatik olarak güvenli bir Oda Kimliği ve Şifresi oluşturur, odaya katılır ve davet bağlantısını panonuza kopyalar.",
"STEP_1_TITLE": "Oda Oluştur ve Paylaş",
"STEP_1_DESC": "Uanzıyı açın, tek tıkla oda oluşturun ve bağlantıyı arkadaşlarınızla paylaşın. Davet köprüsü ile anında katılırlar.",
"STEP_1_ILLUS_DESC": "Bağlanacak bir sekme seçin",
"STEP_1_ILLUS_DL_CHROME": "YouTube Sekmesini Bağla",
"STEP_1_ILLUS_DL_FIREFOX": "Netflix Sekmesini Bağla",
"STEP_1_ILLUS_ACTIVE": "Sekme Bağlandı",
"STEP_1_ILLUS_READY": "Senkronize izleme için hazır !",
"STEP_2_TITLE": "Video Sekmesini Seç",
"STEP_2_DESC": "Videonuzun bulunduğu sekmeye gidin ve uzantı menüsünden bu sekmeyi seçin. KoalaSync oynatıcılarınızı otomatik olarak bağlar.",
"STEP_2_ILLUS_ROOM": "Oda",
"STEP_2_ILLUS_SYNC": "Senkronizasyon",
"STEP_2_ILLUS_SETTINGS": "Ayarlar",
"STEP_2_ILLUS_CREATE": "+ Yeni Oda Oluştur",
"STEP_2_ILLUS_MANUAL": "Manuel Bağlantı / Gelişmiş",
"STEP_2_ILLUS_COPIED": "Davet bağlantısı kopyalandı!",
"STEP_3_TITLE": "Paylaş ve Eşitle",
"STEP_3_DESC": "Davet bağlantısını arkadaşlarınıza gönderin. Katıldıklarında video sekmenizi seçin ve senkronize oynatmanın keyfini çıkarın.",
"STEP_3_TITLE": "Senkronizasyonun Tadını Çıkar",
"STEP_3_DESC": "Oynat tuşuna basın. Birisi durdurduğunda, sardığında veya oynattığında herkesin videosu gecikmesiz olarak senkronize kalır.",
"STEP_3_ILLUS_IN_SYNC": "EŞİTLENDİ",
"SELF_TITLE": "Kendi Kendine Barındıranlar İçin",
"SELF_SUBTITLE": "Sunucumuza güvenmiyor musunuz? Veri egemenliğinizi koruyun. Kendi özel aktarım sunucunuzu dakikalar içinde devreye alın.",
@@ -117,18 +127,20 @@
"FAQ_SUBTITLE": "Videoları mükemmel bir uyum içinde birlikte izleme hakkında bilmeniz gereken her şey.",
"FAQ_Q1": "Arkadaşlarımla Netflix, Emby veya Jellyfin'i eş zamanlı olarak izleyebilir miyim?",
"FAQ_A1": "Evet! KoalaSync; Netflix, YouTube, Twitch ve HTML5 video oynatıcısı olan tüm web sitelerinde (Emby, Jellyfin ve kendi barındırdığınız medya sunucuları dahil) çalışır. Eklentiyi kurun, bir oda oluşturun, davet bağlantısını paylaşın; herkes mükemmel bir uyum içinde izlesin.",
"FAQ_Q_VIDEO_ACCESS": "KoalaSync videoyu arkadaşlarıma aktarır mı?",
"FAQ_A_VIDEO_ACCESS": "Hayır. KoalaSync video içeriğini yayınlamaz, barındırmaz, paylaşmaz veya erişim kısıtlamalarını aşmaz. Herkes videoyu kendi tarayıcısında yerel olarak izler ve ilgili web sitesine ya da yayın hizmetine kendi erişimine, örneğin kendi Netflix hesabına, ihtiyaç duyar. KoalaSync yalnızca oynat, duraklat ve ileri geri sarma gibi oynatma eylemlerini senkronize eder.",
"FAQ_Q2": "KoalaSync, Teleparty'ye ücretsiz bir alternatif mi?",
"FAQ_A2": "MIT Lisansı kapsamında %100 ücretsiz ve açık kaynaklıdır. Premium katmanlar yok, kilitli özellikler yok, reklam yok her şey dahil. Teleparty'nin aksine, KoalaSync'in aktarım sunucusu yalnızca RAM üzerindedir; sıfır veri toplama ve kullanıcı hesabı gereksinimi vardır.",
"FAQ_A2": "MIT Lisansı kapsamında %100 ücretsiz ve açık kaynaklıdır. Premium katmanlar yok, kilitli özellikler yok, reklam yok, her şey dahil. Teleparty'nin aksine, KoalaSync'in aktarım sunucusu yalnızca RAM üzerindedir; sıfır veri toplama ve kullanıcı hesabı gereksinimi vardır.",
"FAQ_Q3": "KoalaSync; Emby, Jellyfin ve kendi barındırdığınız medya sunucularıyla çalışır mı?",
"FAQ_A3": "Evet. KoalaSync, herhangi bir standart HTML5 video öğesini algılar; bu da Emby, Jellyfin, Plex ve her türlü özel barındırılan akış kurulumuyla mükemmel çalıştığı anlamına gelir. Tam veri egemenliği için Docker aracılığıyla KoalaSync aktarım sunucusunu kendiniz de barındırabilirsiniz.",
"FAQ_Q4": "Hangi tarayıcılar ve platformlar KoalaSync'i destekliyor?",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave ve tüm Chromium tabanlı tarayıcılar. Doğrudan Chrome Web Mağazası veya Firefox Eklentileri üzerinden kurun manuel yapılandırmaya gerek yoktur.",
"FAQ_A4": "Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave ve tüm Chromium tabanlı tarayıcılar. Doğrudan Chrome Web Mağazası veya Firefox Eklentileri üzerinden kurun, manuel yapılandırmaya gerek yoktur.",
"FAQ_Q5": "Tüm arkadaşlarımın eklentiyi kurması gerekiyor mu?",
"FAQ_A5": "Evet, her katılımcının KoalaSync'i kurması gerekir ancak kayıt veya hesap oluşturma yoktur. Sadece davet bağlantısına tıklayın, henüz yapmadıysanız eklentiyi kurun, saniyeler içinde odaya otomatik olarak katılacaksınız.",
"FAQ_A5": "Evet, her katılımcının KoalaSync'i kurması gerekir, ancak kayıt veya hesap oluşturma yoktur. Sadece davet bağlantısına tıklayın, henüz yapmadıysanız eklentiyi kurun, saniyeler içinde odaya otomatik olarak katılacaksınız.",
"FAQ_Q6": "Verilerim güvende mi? Ya resmi sunucuya güvenmiyorsam?",
"FAQ_A6": "Tamamen güvende. KoalaSync'in aktarım sunucusu yalnızca RAM üzerindedir odadan ayrıldığınız andan itibaren tüm oturum verileri kalıcı olarak silinir. Veritabanı yok, günlük kaydı yok, analitik yok, hesap yok. Ve tam kontrolü tercih ediyorsanız: Docker aracılığıyla kendi özel aktarım sunucunuzu dakikalar içinde barındırabilirsiniz. Tüm kod tabanı açık kaynaklıdır ve GitHub'da denetlenebilir.",
"FAQ_A6": "Tamamen güvende. KoalaSync'in aktarım sunucusu yalnızca RAM üzerindedir, odadan ayrıldığınız andan itibaren tüm oturum verileri kalıcı olarak silinir. Veritabanı yok, günlük kaydı yok, analitik yok, hesap yok. Ve tam kontrolü tercih ediyorsanız: Docker aracılığıyla kendi özel aktarım sunucunuzu dakikalar içinde barındırabilirsiniz. Tüm kod tabanı açık kaynaklıdır ve GitHub'da denetlenebilir.",
"FAQ_Q7": "Tarayıcıda video izlerken sessiz diyalogları ve yüksek patlamaları düzeltebilir miyim?",
"FAQ_A7": "Evet. KoalaSync 2.2.0, videonuzun sessiz ve yüksek kısımları arasındaki farkı azaltan yerleşik bir ses sıkıştırıcı içerir. Web Audio API aracılığıyla yerel olarak çalışır hiçbir ses verisi bilgisayarınızdan çıkmaz. Popup'ı açın, bir oda oluşturun, Ayarlar → Audio Processing'e gidin, sıkıştırıcıyı açın ve bir ön ayar seçin. Netflix, Prime Video, Disney+, YouTube ve herhangi bir HTML5 video sitesinde çalışır.",
"FAQ_A7": "Evet. KoalaSync 2.2.0, videonuzun sessiz ve yüksek kısımları arasındaki farkı azaltan yerleşik bir ses sıkıştırıcı içerir. Web Audio API aracılığıyla yerel olarak çalışır, hiçbir ses verisi bilgisayarınızdan çıkmaz. Popup'ı açın, bir oda oluşturun, Ayarlar → Audio Processing'e gidin, sıkıştırıcıyı açın ve bir ön ayar seçin. Netflix, Prime Video, Disney+, YouTube ve herhangi bir HTML5 video sitesinde çalışır.",
"HOWTO_TITLE": "KoalaSync kullanarak arkadaşlarınızla videoları senkronize etme",
"HOWTO_DESC": "YouTube, Netflix, Twitch ve daha fazlasında arkadaşlarınızla mükemmel uyum içinde herhangi bir videoyu izleyin. Kayıt yok, takip yok.",
"HOWTO_STEP_1_NAME": "Tarayıcı eklentisini kurun",
@@ -156,7 +168,7 @@
"MOCK_13": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"14\" height=\"14\" aria-hidden=\"true\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> SYNC",
"MOCK_14": "Diğerlerine Atla",
"MOCK_15": "Son Etkinlik Durumu",
"MOCK_16": "1 bağlantı bekleniyor (KoalaPC)...",
"MOCK_16": "1 bağlantı bekleniyor (🐶 HappyDog)...",
"MOCK_17": "Atla ve Yine de Oynat",
"MOCK_18": "Kullanıcı Adınız",
"MOCK_19": "Gereksiz Sekmeleri Gizle",
@@ -176,5 +188,63 @@
"FOOTER_LEGAL_LINK": "imprint",
"FOOTER_PRIVACY_LINK": "privacy",
"FOOTER_DISCLAIMER": "KoalaSync; Netflix, Disney+, Amazon, YouTube, Twitch veya başka herhangi bir yayın platformuna bağlı değildir, bunlar tarafından desteklenmemektedir ve bunlarla ilişkili değildir. Tüm ticari markalar ilgili sahiplerinin mülkiyetindedir.",
"FOOTER_SUPPORT": "Support KoalaSync"
"FOOTER_SUPPORT": "Support KoalaSync",
"ALT_INDEX_TITLE": "KoalaSync - Alternatifler ve karşılaştırmalar",
"ALT_INDEX_META_DESC": "KoalaSync ile diğer watch party eklentileri ve yayın çözümleri arasındaki dürüst karşılaştırmaları ve ayrıntılı rehberleri keşfedin.",
"ALT_INDEX_HEADING": "Rehberler ve karşılaştırmalar",
"ALT_INDEX_INTRO": "KoalaSync, diğer watch party araçları ve yayın çözümleriyle karşılaştırıldığında nasıl duruyor? Uzaktan film geceleriniz için en iyi seçeneği bulmak üzere dürüst ve teknik karşılaştırmalarımızı okuyun.",
"ALT_INDEX_READ_COMPARISON": "Karşılaştırmayı oku →",
"ALT_INDEX_READ_GUIDE": "Rehberi oku →",
"ALT_INDEX_FOOTER_LINK": "Rehberler ve karşılaştırmalar",
"ALT_TELEPARTY_TITLE": "KoalaSync ve Teleparty: Hangi ortak izleme aracını kullanmalısınız?",
"ALT_TELEPARTY_META_DESC": "Ortak izleme etkinlikleri (watch party) için KoalaSync ve Teleparty'nin; yerleşik sohbet, mobil desteği, tarayıcı tabanlı video senkronizasyonu, kendi kendine barındırılan (self-hosted) medya sunucuları ve açık kaynak kullanımı açısından pratik bir karşılaştırması.",
"ALT_TELEPARTY_INTRO": "KoalaSync ve Teleparty, aynı can sıkıcı sorunu çözmeye yardımcı oluyor: O meşhur \"3, 2, 1, başlat\" ritüeliyle uğraşmadan birlikte bir şeyler izlemek. Ancak bunu yaparken farklı yaklaşımlar benimsiyorlar.<br><br>Yerleşik bir sohbet özelliği istiyorsanız veya mobilde izlemek istiyorsanız Teleparty en mantıklı seçenek olabilir. KoalaSync ise daha çok; Discord, Teamspeak veya başka bir sesli görüşme üzerinden zaten konuştuğunuz ve sadece oynatmanın senkronize kalmasını istediğiniz, masaüstü tarayıcı tabanlı izleme etkinlikleri için uygundur.<br><br>KoalaSync'i geliştirmemin temel nedeni esneklikti. Sadece belirli platformlar listesiyle sınırlı kalmayıp, standart bir tarayıcı video oynatıcısı kullanan hemen hemen her web sitesiyle çalışacak şekilde tasarlandı. Buna Emby, Jellyfin ve Plex gibi kendi kendine barındırılan sistemler, yerel/özel alan adları ve tarayıcıdaki pek çok sıradan video sayfası da dahildir.",
"ALT_TELEPARTY_WHEN_TELE_TITLE": "Teleparty'nin hala mantıklı olabileceği durumlar",
"ALT_TELEPARTY_WHEN_TELE_BODY": "Sohbetin doğrudan izleme aracının bir parçası olmasını istiyorsanız Teleparty hala daha iyi bir seçenek olabilir. KoalaSync'te şu an için yerleşik bir sohbet özelliği bulunmuyor; uygulama, Discord, Teamspeak, WhatsApp, Signal veya normal bir telefon görüşmesi gibi başka bir kanal üzerinden zaten konuştuğunuz varsayımıyla çalışıyor.<br><br>Bir diğer önemli neden ise mobil desteği. Teleparty'nin bir uygulaması varken, KoalaSync şu anda yalnızca masaüstü tarayıcılarda çalışıyor. Eğer telefon veya tablette Netflix senkronizasyonu yapmak istiyorsanız, KoalaSync şu an için bunun doğru aracı değildir.<br><br>Özetle; sohbet ve mobil desteği sizin için en önemli unsurlarsa, Teleparty kurulumunuza daha uygun olabilir.",
"ALT_TELEPARTY_WHEN_KOALA_TITLE": "KoalaSync ne zaman daha iyi bir seçenek olabilir?",
"ALT_TELEPARTY_WHEN_KOALA_BODY": "Sınırlı sayıda platformla kısıtlı bir çözüm yerine esnek bir masaüstü tarayıcı senkronizasyon aracı istiyorsanız KoalaSync daha iyi bir tercihtir.<br><br>Standart tarayıcı video oynatıcısını kullanan hemen hemen her web sitesiyle çalışır. Bu özellik; özellikle Emby, Jellyfin, Plex, özel medya sunucuları, yerel alan adları veya klasik 'birlikte izleme' (watch party) araçlarının genellikle göz ardı ettiği daha küçük video siteleri için oldukça kullanışlıdır.<br><br>Ayrıca ücretsiz ve açık kaynaklıdır; hesap gerektirmez ve kendi aktarım sunucunuzu (relay) çalıştırmak isterseniz kendi sunucunuzda barındırılabilir. Bunu geliştirme sebebim, sadece birkaç büyük yayın platformuna özel kapalı bir sistem yerine, kendi Emby kurulumumla uyumlu ve gizlilik dostu bir çözüm istememdi.",
"ALT_TELEPARTY_MAIN_DIFF_TITLE": "Temel fark",
"ALT_TELEPARTY_MAIN_DIFF_BODY": "Teleparty daha çok sohbet ve mobil desteği sunan bir 'birlikte izleme' ürünüdür. KoalaSync ise tarayıcı videoları için daha esnek bir senkronizasyon katmanı işlevi görür.<br><br>Bu küçük bir fark gibi görünebilir ancak kullanım şeklini büyük ölçüde değiştirir. Eğer içinde sohbet özelliği de bulunan kapsamlı bir 'birlikte izleme' uygulamasına ihtiyacınız varsa Teleparty mantıklı bir seçimdir. Ancak sesli sohbet imkanınız zaten varsa ve sadece video kontrollerinin tarayıcılar arasında senkronize kalmasını istiyorsanız, KoalaSync daha basit ve uygun bir çözümdür.",
"ALT_TELEPARTY_LIMITS_TITLE": "Dürüstçe belirtilen sınırlamalar",
"ALT_TELEPARTY_LIMITS_BODY": "KoalaSync sihirli bir çözüm değildir ve sınırlarını gizlemeye çalışmaz. Şu anda yalnızca masaüstü tarayıcılarda çalışır; yerleşik sohbet özelliği veya mobil uygulaması yoktur.<br><br>Uyumluluk durumu tarayıcıya ve video oynatıcısına da bağlıdır. KoalaSync'in standart tarayıcı video oynatıcılarının neredeyse tamamıyla çalışması beklenir; ancak web siteleri oynatıcılarını, DRM (Dijital Hak Yönetimi) işleyişlerini veya tarayıcı kısıtlamalarını diledikleri zaman değiştirebilirler.<br><br>KoalaSync; Netflix, Disney+, Prime Video, YouTube, Teleparty, Emby, Jellyfin, Plex veya burada adı geçen diğer hiçbir markayla bağlantılı değildir. Bu isimler yalnızca tipik kullanım senaryolarını açıklamak amacıyla kullanılmıştır.",
"ALT_TELEPARTY_SUMMARY_TITLE": "Özet",
"ALT_TELEPARTY_SUMMARY_BODY": "Yerleşik sohbet veya mobil desteği istiyorsanız, Teleparty daha uygun bir seçenek olabilir. Emby, Jellyfin ve Plex gibi kendi barındırdığınız medya sunucuları da dahil olmak üzere hemen hemen her standart video oynatıcıyla çalışan, ücretsiz ve açık kaynaklı bir masaüstü tarayıcı senkronizasyon aracı arıyorsanız, KoalaSync muhtemelen daha iyi bir tercihtir.",
"ALT_SCREEN_TITLE": "KoalaSync ve ekran paylaşımı karşılaştırması: Video senkronizasyonu ne zaman daha iyidir?",
"ALT_SCREEN_META_DESC": "Uzaktan film geceleri, oynatma kontrolü, video kalitesi, gizlilik, altyazılar ve tarayıcı tabanlı izleme konularında KoalaSync ile ekran paylaşımının pratik bir karşılaştırması.",
"ALT_SCREEN_INTRO": "Ekran paylaşımı işe yarar ve dürüst olmak gerekirse bazen yeterlidir. Bir kişi ekranını paylaşır, diğerleri izler ve işlem tamamlanır.<br><br>Ancak daha uzun film gecelerinde veya dizi bölümlerini birlikte izlerken bu durum can sıkıcı bir hal alabilir. Video kalitesi; paylaşımı yapan kişinin yükleme hızına, işlemci (CPU) gücüne, tarayıcısına ve Discord, Teamspeak, Zoom ya da diğer görüşme uygulamalarının uyguladığı sıkıştırma işlemine bağlıdır. Ayrıca, oynatmayı gerçekten kontrol eden tek bir kişi vardır.",
"ALT_SCREEN_WHEN_ENOUGH_TITLE": "Ekran paylaşımı ne zaman yeterlidir?",
"ALT_SCREEN_WHEN_ENOUGH_BODY": "Mümkün olan en hızlı kurulumu istediğinizde ekran paylaşımı gayet uygundur. Eğer videoya yalnızca bir kişi erişebiliyorsa ya da herkes oynatma, duraklatma ve ileri/geri sarma işlemlerinin tek bir kişi tarafından kontrol edilmesini sorun etmiyorsa, bu en basit seçenek olabilir.<br><br>Ayrıca görüntü kalitesinin çok önemli olmadığı, kimsenin kendine özel altyazı veya ses ayarlarına ihtiyaç duymadığı ve paylaşımı yapan kişinin o tarayıcı penceresini veya ekranını göstermekte bir sakınca görmediği durumlarda da mantıklı bir yöntemdir.",
"ALT_SCREEN_WHEN_ANNOYING_TITLE": "Ekran paylaşımının can sıkıcı olabildiği durumlar",
"ALT_SCREEN_WHEN_ANNOYING_BODY": "En büyük dezavantaj, herkesin videoyu kendi oynatıcısında izlemiyor olmasıdır. Bir kişi ekranını yayınlar ve diğer herkes sadece bu yayını görür.<br><br>Bu durum; daha düşük görüntü kalitesi, sıkıştırılmış ses, gecikmeli oynatma ve diğerleri için daha az kontrol anlamına gelebilir. Altyazı ve ses seçenekleri de paylaşımı yapan kişinin seçtikleriyle sınırlı kalır.<br><br>Bir de gizlilik boyutu var. Pencereler veya sekmeler arasında geçiş yaparsanız, insanlar tarayıcı sekmelerinizi, e-postalarınızı, Reddit'i, sohbetlerinizi veya diğer özel içerikleri anlık olarak görebilir. Ayrı bir pencere kullanarak bunun önüne geçebilirsiniz ancak yine de dikkat etmeniz gereken bir husustur.",
"ALT_SCREEN_WHY_CLEANER_TITLE": "KoalaSync neden daha düzenli bir deneyim sunar?",
"ALT_SCREEN_WHY_CLEANER_BODY": "KoalaSync ekranınızı yayınlamaz. Herkes videoyu kendi tarayıcısında yerel olarak izler; KoalaSync ise yalnızca oynatma, duraklatma ve ileri/geri sarma gibi eylemleri senkronize halde tutar.<br><br>Bu sayede herkes kendi cihazında tam ekran modunu kullanabilir, oynatıcı destekliyorsa kendi altyazı veya ses ayarlarını seçebilir ve sesli sohbeti Discord, Teamspeak veya başka bir görüşme üzerinden ayrı olarak sürdürebilir.<br><br>Film geceleri için bu yöntem, bir kişinin ekranını yayınlamasına kıyasla çok daha düzenli ve temiz bir deneyim sunabilir. Özellikle Emby, Jellyfin, Plex gibi tarayıcı tabanlı sistemlerde, tarayıcıda açılan yerel dosyalarda, özel alan adlarında veya standart tarayıcı video oynatıcılarını kullanan diğer web sitelerinde oldukça kullanışlıdır.",
"ALT_SCREEN_LIMITS_TITLE": "Dürüstçe belirtilen sınırlamalar",
"ALT_SCREEN_LIMITS_BODY": "KoalaSync, sesli görüşmenin yerini tutmaz. İzleme sırasında konuşmak isterseniz, yine Discord, Teamspeak, Signal, WhatsApp veya normalde kullandığınız herhangi bir aracı kullanmaya devam edebilirsiniz.<br><br>Ayrıca herkesin ilgili videoya veya sayfaya kendi cihazından erişebilmesi gerekir. KoalaSync; abonelikleri, oturum açma işlemlerini, DRM korumalarını, bölgesel kısıtlamaları veya platform kurallarını devre dışı bırakmaz.<br><br>Şu anda yalnızca masaüstü tarayıcılarında çalışır; yerleşik bir sohbet veya sesli görüşme özelliği yoktur ve uyumluluk her zaman kullanılan tarayıcıya ve video oynatıcıya bağlıdır.",
"ALT_SCREEN_SUMMARY_TITLE": "Özet",
"ALT_SCREEN_SUMMARY_BODY": "Hızlı bir çözüm aradığınızda ve kalite, gizlilik ya da bireysel kontrol konularına çok önem vermediğinizde, ekran paylaşımı en basit seçenektir. Herkesin içeriği kendi tarayıcısında yerel olarak izlemesi gereken ve oturumun gerçekten birlikte izliyormuş hissi verdiği durumlarda ise KoalaSync daha uygun bir tercihtir.",
"ALT_W2G_TITLE": "KoalaSync vs Watch2Gether: Which watch party solution fits better?",
"ALT_W2G_META_DESC": "An honest comparison between KoalaSync and Watch2Gether. Discover when KoalaSync's flexibility is unbeatable and why Watch2Gether is still the top choice for YouTube.",
"ALT_W2G_INTRO": "KoalaSync and Watch2Gether both solve the same problem: watching videos in sync with friends online without manually coordinating. While Watch2Gether is one of the oldest and most popular hubs for shared movie nights, KoalaSync takes a completely different, lightweight approach. Which tool is right for you depends heavily on what and where you want to watch.",
"ALT_W2G_WHEN_W2G_TITLE": "When Watch2Gether is the better choice",
"ALT_W2G_WHEN_W2G_BODY": "Watch2Gether is unbeatable for <strong>YouTube watch parties</strong>. With its built-in playlist and search, anyone in the room can easily find, add, and play new videos. Everyone watches the next video together seamlessly.<br><br>With KoalaSync on YouTube, the site's default autoplay takes over, redirecting each user to a different recommended video. Our automatic next-episode feature (\"Episode Lobby\"), which works great on Netflix or Disney+, doesn't work on YouTube due to these dynamic recommendations. For pure YouTube nights, Watch2Gether is the better choice.",
"ALT_W2G_WHEN_KOALA_TITLE": "When KoalaSync fits better",
"ALT_W2G_WHEN_KOALA_BODY": "KoalaSync shines on <strong>real streaming services</strong> (Netflix, Prime Video, Disney+) and self-hosted media servers (Jellyfin, Plex, Emby). Unlike Watch2Gether, which often cannot play premium content at all, KoalaSync syncs the native player directly on the website.<br><br>It works on almost any site with a standard HTML5 video player. You don't need to set up a virtual room on a third-party site: just activate the extension, join a lobby, and start watching.",
"ALT_W2G_MAIN_DIFF_TITLE": "The main difference",
"ALT_W2G_MAIN_DIFF_BODY": "Watch2Gether is a <strong>standalone website</strong> with built-in chat, playlists, and webcam support. KoalaSync is a <strong>minimalist browser extension</strong> that hooks directly into your normal browser tabs.<br><br>Looking for an all-in-one package for YouTube with built-in chat? Use Watch2Gether. Want to watch movies on Netflix or your own server without distractions, in native quality (no server compression)? KoalaSync is the cleaner solution.",
"ALT_W2G_LIMITS_TITLE": "Honest limitations",
"ALT_W2G_LIMITS_BODY": "KoalaSync <strong>does not have built-in playlists or video search</strong>. On YouTube, this means that autoplaying the next video won't stay in sync.<br><br>Additionally, there is <strong>no built-in chat</strong>, and KoalaSync currently only works on desktop browsers. It is designed under the assumption that you are already talking via Discord, Teamspeak, or other voice tools anyway.",
"ALT_W2G_SUMMARY_TITLE": "In short",
"ALT_W2G_SUMMARY_BODY": "For spontaneous YouTube sessions with playlists and built-in chat, Watch2Gether is the best choice. For premium streaming platforms (like Netflix, Prime Video, Disney+), self-hosted servers (Jellyfin, Plex, Emby), and maximum browser flexibility, KoalaSync is the more suitable and lightweight tool.",
"ALT_TRY_KOALASYNC": "KoalaSync'i Deneyin",
"MOCK_HOST_CONTROL_LABEL": "Sunucu Kontrolü",
"MOCK_HOST_CONTROL_TOGGLE": "Oynatmayı yalnızca ben kontrol edebilirim",
"LABEL_SELECT_VIDEO": "Video Seç",
"OPTION_SELECT_TAB": "-- Bir Sekme Seçin --",
"THEME_TOGGLE_LABEL": "Açık/koyu modu değiştir",
"FAQ_SHOW_ALL": "Tüm soruları göster",
"FAQ_HIDE_ALL": "Soruları gizle",
"SELF_SHOW_SETUP": "Kurulum rehberini göster",
"SELF_HIDE_SETUP": "Rehberi gizle",
"HERO_TRUST": "Ücretsiz · Açık kaynak · Hesap gerekmez",
"COMP_MORE_GUIDES": "Tam karşılaştırma ve diğer alternatifler"
}

Some files were not shown because too many files have changed in this diff Show More