Skip to content

Add ARVIO Companion web app with Hebrew/English i18n#377

Merged
ProdigyV21 merged 6 commits into
ProdigyV21:mainfrom
test01203:main
Jun 19, 2026
Merged

Add ARVIO Companion web app with Hebrew/English i18n#377
ProdigyV21 merged 6 commits into
ProdigyV21:mainfrom
test01203:main

Conversation

@test01203

Copy link
Copy Markdown
Contributor

Summary

  • Add standalone ARVIO Companion web app at /companion/ — lets users manage their ARVIO account from any browser
  • Full Hebrew / English i18n with a live language toggle button in the sidebar (persisted in localStorage)
  • Sections covered: Dashboard, Profiles, Add-ons, IPTV, Plugins, Watch History, Watchlist, AI Subtitle Settings, Account Settings
  • All UI strings extracted into a STRINGS object with t() helper — supports pluralization via function values and RTL/LTR switching

What changed

netlify-arvio-tv-site/companion/ (new directory)

File Description
index.html Auth screen — English default, Google OAuth sign-in
style.css Full dark-theme CSS, responsive sidebar layout
app.js Vanilla JS SPA — Supabase auth + cloud sync, all sections, full i18n
mock-preview.html Static mock page (no network calls) for screenshots

Key app.js additions

  • STRINGS object with complete en + he translations for every string in the app
  • t(key, ...args) helper resolves strings/pluralizers by current language
  • setLang(lang) switches language live — updates <html lang/dir>, re-renders shell + active section
  • Language toggle button (עב / EN) in the sidebar user row

Data

Reads from Supabase profiles.addons.__arvioAccountSyncPayload (same cloud sync payload as the TV app). Watch history and watchlist use dedicated Supabase tables. AI and general settings are written back via the same payload.

Reviewer notes

  • No build step — pure vanilla JS + Supabase CDN
  • mock-preview.html is for development/screenshot use only and is not linked from production
  • OAuth redirect URL https://arvio.tv/companion/ must be added to Supabase Auth → URL Configuration before deployment

The-cpu-max and others added 4 commits June 16, 2026 20:21
Vanilla JS + Supabase single-page app served alongside the existing
arvio.tv marketing site. No build step required — just static files.

Features:
- Google OAuth login (via Supabase GoTrue)
- Dashboard with watch stats and recent activity
- Profiles view — shows all ARVIO sub-profiles with active/kids badges
- Addons manager — lists all Stremio + Telegram addons with enabled status
- Watch History — paginated by movie/tv/all, delete individual entries
- Watchlist — view and remove items
- AI Subtitle Translation — toggle on/off, select model (Groq Llama 70B /
  Gemini Flash 2.5), configure auto-select and hearing-impaired removal;
  settings saved directly to cloud sync payload
- Settings — card layout, language, OLED mode, skip profile selection

Data layer: reads/writes the cloud sync payload stored in
profiles.addons.__arvioAccountSyncPayload (same format the TV app uses),
and queries watch_history/watchlist tables directly via Supabase REST.

Co-Authored-By: koby455 <koby455@gmail.com>
Android TV app (CloudSyncRepository.kt):
- Inject PluginDataStore into CloudSyncRepository
- buildCloudPayload: export pluginRepositories, pluginScrapers, pluginsEnabled
  into __arvioAccountSyncPayload so plugins survive device wipes / multi-device
- applyCloudPayload: restore repositories + scrapers from cloud on first launch
  (scraper JS code stays local-only for security; only metadata is synced)

Companion web app (app.js):
- Add IPTV section: shows all M3U playlists per profile with M3U/EPG URLs,
  enabled status, favourite groups and favourite channels
- Add Plugins section: shows all plugin repositories (name, URL, scraper count,
  last updated) and individual scrapers (name, version, supported types,
  content languages, enabled status) with global plugins toggle
- Add both to sidebar navigation

Co-Authored-By: koby455 <koby455@gmail.com>
- All navigation labels, section titles, badges, toasts, and helper
  text translated to English in app.js and index.html
- Auth screen subtitle and Google button label now in English
- timeAgo() helper uses English relative-time strings
- Hebrew remains fully supported as a selectable app language (setting
  stored in cloud sync payload as before)
- Add mock-preview.html: standalone demo page with generic sample data
  for screenshots / PR previews (no Supabase dependency)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@test01203 test01203 marked this pull request as ready for review June 17, 2026 04:18
@ProdigyV21

Copy link
Copy Markdown
Owner

One thing should be fixed before merge/deploy though: the companion app renders a lot of synced/user-controlled data with raw innerHTML. Profile names, IPTV playlist names/URLs, plugin repo/scraper names/descriptions/logos, addon info, watch history titles, and even account display info should be escaped before rendering. Otherwise a bad synced value could inject HTML/script into the companion page.

Please add an escapeHtml() helper for text, a safeUrl() helper for image/link attributes, and use those everywhere cloud/Supabase data is interpolated into HTML. Or even better, render those values with DOM textContent instead of innerHTML.

Also small safety improvement: saveSyncPayload currently rewrites the whole profiles.addons wrapper. Please preserve any existing wrapper fields and only update __arvioAccountSyncPayload and __arvioAccountSyncUpdatedAt.

…cPayload

- Add escapeHtml() helper and apply it to every cloud value injected into innerHTML:
  user display name/email/avatar initial, profile names, addon name/description/logo,
  IPTV playlist name/URL/EPG/profile label/fav groups+channels, plugin repo name/URL,
  scraper name/description/version/logo/types/languages, history title/poster,
  watchlist tmdb_id, settings user ID
- Add safeUrl() helper (https/http only) and use it for all image src attributes
  that come from cloud data (addon logos, scraper logos, user avatar)
- saveSyncPayload now reads the existing wrapper first and only updates
  __arvioAccountSyncPayload and __arvioAccountSyncUpdatedAt, preserving any other
  fields the TV app may have written

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@test01203

Copy link
Copy Markdown
Contributor Author

Thanks for the review! Both issues are now fixed in the latest commit.

XSS / innerHTML escaping

Added two helpers:

  • escapeHtml(str) — escapes & < > " ' to HTML entities. Applied to every cloud/Supabase value injected into HTML: user display name, email, avatar initial, profile names, addon names/descriptions, IPTV playlist names/URLs/EPG URLs/profile labels, favourite group and channel names, plugin repo names/URLs, scraper names/descriptions/versions/type tags/language tags, watch history titles, and the account user ID.
  • safeUrl(url) — only passes through https:// and http:// URLs, everything else becomes an empty string. Applied to all src attributes sourced from cloud data: user avatar, addon logos, scraper logos, and TMDB poster paths.

saveSyncPayload wrapper preservation

saveSyncPayload now does a read-modify-write: it fetches the current profiles.addons JSON first, parses it, updates only __arvioAccountSyncPayload and __arvioAccountSyncUpdatedAt, and writes the merged object back. Any other fields the TV app wrote into the wrapper are preserved untouched.

@ProdigyV21

Copy link
Copy Markdown
Owner

One last small safety fix before merge: safeUrl() validates the URL with new URL(url), but then returns the original input string. Please return the normalized u.href instead, preferably escaped before putting it into img src attributes. That avoids weird quote/attribute edge cases in synced logo/avatar URLs.

After that I think this is good to merge

Normalizes the URL via the browser's URL parser before use and
HTML-escapes it, preventing quote/attribute injection from synced
logo or avatar URLs in innerHTML img src attributes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@test01203

Copy link
Copy Markdown
Contributor Author

Done! Fixed in commit 89c9bc5.

safeUrl() now returns escapeHtml(u.href) instead of the raw input string:

// Before
return (u.protocol === 'https:' || u.protocol === 'http:') ? url : '';

// After
return (u.protocol === 'https:' || u.protocol === 'http:') ? escapeHtml(u.href) : '';

u.href is the browser-normalized form of the URL (the URL parser re-serializes it, stripping any embedded quotes or weird encoding), and then escapeHtml() HTML-escapes it before it lands in the img src attribute — so a synced logo or avatar URL containing " or ' can't break out of the attribute or inject markup. Thanks for catching that!

@ProdigyV21 ProdigyV21 merged commit 363bd1c into ProdigyV21:main Jun 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants