UI updated

This commit is contained in:
2026-10-07 14:05:39 -07:00
parent 945b0a1836
commit d6ec1f93b8
3 changed files with 54 additions and 184 deletions

View File

@@ -1,177 +0,0 @@
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Our Architecture — Vyla</title>
<meta name="description"
<meta name="keywords"
content="Vyla architecture, Vyla Home, Vyla SDK, Stream API, system design, media stack, deployment patterns, self-hosted media" />
<link rel="canonical" href="https://vyla.cc/architecture.html" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://vyla.cc/architecture.html" />
<meta property="og:title" content="Our Architecture — Vyla" />
<meta property="og:description"
content="How Vyla Home, SDK, and Stream API relate, and where each responsibility belongs." />
<meta property="og:image" content="https://vyla.cc/assets/images/thumbnail.png" />
<meta property="og:site_name" content="Vyla" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://vyla.cc/architecture.html" />
<meta property="twitter:title" content="Our Architecture — Vyla" />
<meta property="twitter:description"
content="How Vyla Home, SDK, and Stream API relate, and where each responsibility belongs." />
<meta property="twitter:image" content="https://vyla.cc/assets/images/thumbnail.png" />
<meta property="twitter:site" content="@vylaentertainment" />
<!-- Additional SEO -->
<meta name="robots" content="index, follow" />
<meta name="author" content="Vyla" />
content="How Vyla Home, SDK, and Stream API relate, and where each responsibility belongs." />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/assets/base.css" />
<link rel="stylesheet" href="/assets/highlight.css" />
<link rel="stylesheet" href="/assets/mintlify-overrides.css" />
<script>
(function () {
var saved = localStorage.getItem('vyla-theme');
var isDark = saved ? saved === 'dark' : true;
document.documentElement.classList.toggle('dark', isDark);
})();
</script>
</head>
<body>
<div id="banner">
<span>Vyla is self-hosted. Run Vyla Home, embed the SDK, or deploy Stream API on infrastructure you control. Rights
holders can request source removal — <a href="/misc/dmca">DMCA policy</a>.</span>
<button id="banner-close" onclick="dismissBanner()" aria-label="Dismiss">✕</button>
</div>
<header id="navbar">
<div class="nav-top">
<a class="brand" href="/"><img src="/assets/images/logo.png" alt="Vyla" id="brand-logo"></a>
<button id="search-trigger" type="button" onclick="openSearch()" aria-label="Search"></button>
<div class="links">
<button id="theme-toggle" type="button" onclick="toggleThemeMenu(event)" aria-label="Theme"></button>
</div>
</div>
<nav class="tabs">
<a class="tab-link" href="/">Vyla</a>
<a class="tab-link" href="/sdk">SDK</a>
<a class="tab-link active" href="/stream-api/overview">Stream API</a>
<a class="tab-link" href="/guides/integrations">Resources</a>
<a class="tab-link" href="/misc/credit">Misc</a>
</nav>
</header>
<div id="layout">
<nav id="sidebar">
<div class="group">
<p class="group-title">Start here</p>
<ul>
<li><a class="active" href="/">Introduction</a></li></li>
<li><a class="active" href="/architecture">Architecture</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Products</p>
<ul>
<li><a class="" href="/home">Home</a></li>
<li><a class="" href="/go">Go</a></li>
<li><a class="" href="/tv">Tv</a></li>
<li><a class="" href="/player">Player</a></li>
</ul>
</div>
<div class="group">
<p class="group-title">Concepts</p>
<ul>
<li><a class="" href="/concepts/providers">Providers</a></li>
</ul>
</div>
</nav>
<div id="content-col">
<main class="prose">
<h1>Our Architecture</h1>
<p class="page-description">How Vyla Home, SDK, and Stream API relate, and where each responsibility belongs.
</p>
<h2 id="the-vyla-stack">The Vyla stack</h2>
<p>Vyla separates local application management, provider resolution, and client-facing delivery. That separation
lets each layer be used on its own while keeping the integration points clear.</p>
<h3 id="vyla-home">Vyla Home</h3>
<p>Home is responsible for a desktop session: local configuration, encrypted local credentials, update
coordination, managed processes, and the embedded interface. It is the right layer for launching and
supervising a local experience.</p>
<h3 id="vyla-sdk">Vyla SDK</h3>
<p>The SDK contains the provider-facing work: source configuration, stream resolution, subtitle retrieval,
download lookup, and health probes. It is the right layer when your Node.js application needs to decide how
requests are authorized, cached, selected, and returned.</p>
<h3 id="stream-api">Stream API</h3>
<p>Stream API wraps the SDK in a self-hosted HTTP service. Its movie and TV endpoints use Server-Sent Events so
metadata can arrive first and usable sources can be delivered progressively. It also provides subtitle,
download, health, test, and optional debug routes.</p>
<h2 id="common-deployment-patterns">Common deployment patterns</h2>
<table>
<thead>
<tr>
<th>Pattern</th>
<th>Best for</th>
<th>Key decision</th>
</tr>
</thead>
<tbody>
<tr>
<td>Home only</td>
<td>a managed local desktop experience</td>
<td>Home owns the lifecycle of bundled local services</td>
</tr>
<tr>
<td>App + SDK</td>
<td>a Node.js backend with a custom product API</td>
<td>your app owns authentication, policy, and response design</td>
</tr>
<tr>
<td>Player + Stream API</td>
<td>browser or mobile clients that need a stable HTTP contract</td>
<td>deploy Stream API and place it behind TLS when exposed remotely</td>
</tr>
<tr>
<td>Home + Stream API</td>
<td>a local player backed by a local service</td>
<td>keep the service bound and access-controlled for the local environment</td>
</tr>
</tbody>
</table>
<h2 id="data-flow-for-playback">Data flow for playback</h2>
<ol>
<li>A client provides a TMDB media ID (plus season and episode for TV).</li>
<li>Stream API, or your own SDK-based backend, asks active providers for candidates.</li>
<li>Candidates are checked and successful sources are returned progressively.</li>
<li>The player begins with the first compatible candidate and keeps later candidates as fallbacks.</li>
<li>Subtitle and download lookups are separate operations; neither should block first playback.</li>
</ol>
<p>Provider availability varies by source and network environment. Build the player experience around fallbacks
and empty-result states, as described in <a href="/guides/error-handling">Error handling</a>.</p>
</main>
<aside id="toc">
<p class="toc-title">On this page</p>
<ul>
<li><a href="#the-vyla-stack">The Vyla stack</a></li>
<li><a href="#common-deployment-patterns">Common deployment patterns</a></li>
<li><a href="#data-flow-for-playback">Data flow for playback</a></li>
</ul>
</aside>
</div>
</div>
<footer id="site-footer">
<a href="https://discord.gg/5eqRhKeywG" target="_blank" rel="noopener">Discord</a>
<a href="https://gitlab.com/vyla-entertainment/" target="_blank" rel="noopener">GitLab</a>
</footer>
<script src="/assets/app.js"></script>
</body>
</html>

View File

@@ -81,7 +81,7 @@ function buildThemeControls() {
seg.innerHTML = modes.map(function (m) {
return '<button type="button" data-mode="' + m + '" aria-label="' + m + '">' + icon(m) + "</button>";
}).join("");
links.appendChild(seg);
(document.querySelector("#sidebar .side-links") || links).appendChild(seg);
[menu, seg].forEach(function (el) {
el.addEventListener("click", function (e) {
var b = e.target.closest("button");
@@ -185,7 +185,9 @@ function buildMobileBar() {
var newSidebar = doc.querySelector("#sidebar");
if (newSidebar) {
var groups = newSidebar.querySelectorAll(".group");
var sideHead = sidebar.querySelector(".side-head");
sidebar.innerHTML = "";
if (sideHead) sidebar.appendChild(sideHead);
if (mobileTabs) sidebar.appendChild(mobileTabs);
groups.forEach(function (group) {
sidebar.appendChild(group.cloneNode(true));
@@ -202,6 +204,18 @@ function buildMobileBar() {
});
});
}
var head = document.createElement("div");
head.className = "side-head";
var brand = document.querySelector("#navbar .brand");
if (brand && sidebar) {
var b = brand.cloneNode(true);
b.querySelector("img").removeAttribute("id");
var sideLinks = document.createElement("div");
sideLinks.className = "side-links";
head.appendChild(b);
head.appendChild(sideLinks);
sidebar.insertBefore(head, sidebar.firstChild);
}
}
function addEyebrow() {

View File

@@ -1416,7 +1416,7 @@ html.dark .search-footer {
.mobile-tabs {
display: block;
margin-top: 50px;
margin-top: 8px;
}
body.nav-open #theme-seg {
@@ -1445,16 +1445,16 @@ html.dark .search-footer {
#sidebar {
position: fixed;
left: 0;
top: var(--sidebar-top, var(--header-height));
top: 0;
width: 80%;
max-width: 340px;
height: calc(100vh - var(--sidebar-top, var(--header-height)));
height: calc(100dvh - var(--sidebar-top, var(--header-height)));
padding: 20px 16px;
height: 100vh;
height: 100dvh;
padding: 0 16px 20px;
background: var(--bg-light);
transform: translateX(-100%);
transition: transform 0.2s ease;
z-index: 30;
z-index: 200;
}
html.dark #sidebar {
@@ -1498,4 +1498,37 @@ main.prose p {
main.prose p,
main.prose li {
line-height: 1.75;
}
.side-head {
display: none;
}
@media (max-width: 900px) {
.side-head {
display: flex;
align-items: center;
justify-content: space-between;
height: 60px;
margin: 0 -16px 12px;
padding: 0 20px;
}
#sidebar .side-head .brand {
display: flex;
align-items: center;
}
#sidebar .side-head .brand img {
height: 28px;
width: 28px;
border-radius: 50%;
display: block;
}
#sidebar .side-links {
display: flex;
align-items: center;
gap: 10px;
}
}