Files
heos/templates/index.html
T
franzz a537d0dd18
Deploy HEOS panel / deploy (push) Successful in 25s
Center logo
2026-09-16 23:23:45 +02:00

159 lines
9.6 KiB
HTML

<!doctype html>
<html lang="en" data-step="{{ step }}" data-host="{{ host.label }}" data-base="{{ url_for('index') }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no">
<title>{{ app_name }}</title>
<!-- Added to the iOS home screen, this opens full-screen with no browser chrome. -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="{{ app_name }}">
<meta name="theme-color" content="#0a0d14">
<link rel="manifest" href="{{ url_for('manifest') }}">
<link rel="apple-touch-icon" href="{{ url_for('static', filename='icon-180.png') }}">
<link rel="icon" href="{{ url_for('static', filename='icon-180.png') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='panel.css') }}">
</head>
<body>
{% macro host_toggle() -%}
<button class="toggle" data-role="group" aria-pressed="false">
<svg class="cinema" viewBox="0 -16 576 512" aria-hidden="true"><path d="M0 112c0 70.7 57.3 128 128 128l224 0c70.7 0 128-57.3 128-128S422.7-16 352-16c-48.2 0-90.2 26.6-112 66-21.8-39.4-63.8-66-112-66-70.7 0-128 57.3-128 128zm304 0a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zM128 64a48 48 0 1 1 0 96 48 48 0 1 1 0-96zM64 352l0 64c0 35.3 28.7 64 64 64l224 0c35.3 0 64-28.7 64-64l0-64c0-35.3-28.7-64-64-64l-224 0c-35.3 0-64 28.7-64 64zM537.5 490.8c4.2 3.4 9.4 5.2 14.8 5.2 13.1 0 23.7-10.6 23.7-23.7l0-240.6c0-13.1-10.6-23.7-23.7-23.7-5.4 0-10.6 1.8-14.8 5.2l-73.5 58.8 0 160 73.5 58.8z"/></svg>
<svg class="leave" viewBox="0 0 512 512" aria-hidden="true"><path d="M502.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-128-128c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L402.7 224 192 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l210.7 0-73.4 73.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l128-128zM160 96c17.7 0 32-14.3 32-32s-14.3-32-32-32L96 32C43 32 0 75 0 128L0 384c0 53 43 96 96 96l64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0c-17.7 0-32-14.3-32-32l0-256c0-17.7 14.3-32 32-32l64 0z"/></svg>
<span data-role="group-label">Cinema</span>
</button>
{%- endmacro %}
{# The Spotify resume buttons with the host toggle in the middle of them. #}
{% macro control_row(target, accounts) -%}
{% set split = (accounts | length + 1) // 2 %}
<div class="spotify-row">
{% for account in accounts %}
{% if loop.index0 == split %}{{ host_toggle() }}{% endif %}
<button class="spotify" data-target="{{ target }}" data-account="{{ account.key }}"
aria-label="Resume {{ account.label }}'s Spotify" hidden>
<svg viewBox="0 0 496 512" aria-hidden="true"><path d="M248 8C111.1 8 0 119.1 0 256s111.1 248 248 248 248-111.1 248-248S384.9 8 248 8zm100.7 364.9c-4.2 0-6.8-1.3-10.7-3.6-62.4-37.6-135-39.2-206.7-24.5-3.9 1-9 2.6-11.9 2.6-9.7 0-15.8-7.7-15.8-15.8 0-10.3 6.1-15.2 13.6-16.8 81.9-18.1 165.6-16.5 237 26.2 6.1 3.9 9.7 7.4 9.7 16.5s-7.1 15.4-15.2 15.4zm26.9-65.6c-5.2 0-8.7-2.3-12.3-4.2-62.5-37-155.7-51.9-238.6-29.4-4.8 1.3-7.4 2.6-11.9 2.6-10.7 0-19.4-8.7-19.4-19.4s5.2-17.8 15.5-20.7c27.8-7.8 56.2-13.6 97.8-13.6 64.9 0 127.6 16.1 177 45.5 8.1 4.8 11.3 11 11.3 19.7-.1 10.8-8.5 19.5-19.4 19.5zm31-76.2c-5.2 0-8.4-1.3-12.9-3.9-71.2-42.5-198.5-52.7-280.9-29.7-3.6 1-8.1 2.6-12.9 2.6-13.2 0-23.3-10.3-23.3-23.6 0-13.6 8.4-21.3 17.4-23.9 35.2-10.3 74.6-15.2 117.5-15.2 73 0 149.5 15.2 205.4 47.8 7.8 4.5 12.9 10.7 12.9 22.6 0 13.6-11 23.3-23.2 23.3z"/></svg>
<span>{{ account.label }}</span>
</button>
{% endfor %}
{# Fewer accounts than the midpoint means the loop never reached it. #}
{% if accounts | length <= split %}{{ host_toggle() }}{% endif %}
</div>
{%- endmacro %}
<div class="app">
<header class="top">
<h1><img class="logo" src="{{ url_for('static', filename='logo.svg') }}" alt="{{ app_name }}"></h1>
</header>
{% for room in rooms %}
<div class="room-slot" data-slot="{{ room.key }}">
<section class="card room" data-room="{{ room.key }}">
<div class="card-head">
<h2><svg class="room-icon" data-icon="{{ room.kind }}" viewBox="0 0 512 512" aria-hidden="true"><path d=""/></svg><span class="room-label">{{ room.label }}</span></h2>
</div>
<div class="now-playing" data-role="now-playing" hidden>
<img class="cover" data-role="cover" alt="" hidden>
<div class="track">
<span class="song" data-role="song"></span>
<span class="artist" data-role="artist"></span>
</div>
</div>
<div class="progress" data-role="progress" data-target="{{ room.key }}" hidden>
<div class="progress-line">
<i data-role="progress-fill"></i>
<b class="progress-cursor" data-role="progress-cursor"></b>
<span class="progress-time elapsed" data-role="progress-elapsed"></span>
<span class="progress-time remaining" data-role="progress-remaining"></span>
</div>
<span class="progress-total" data-role="progress-total"></span>
</div>
<div class="volume">
<button class="step" data-delta="-1" aria-label="{{ room.label }}: volume down">
<svg viewBox="0 0 448 512" aria-hidden="true"><path d="M432 256c0 17.7-14.3 32-32 32L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l352 0c17.7 0 32 14.3 32 32z"/></svg>
</button>
<div class="meter" data-role="meter"><i></i><b class="knob" data-role="level"></b></div>
<button class="step" data-delta="1" aria-label="{{ room.label }}: volume up">
<svg viewBox="0 0 448 512" aria-hidden="true"><path d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 144L48 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l144 0 0 144c0 17.7 14.3 32 32 32s32-14.3 32-32l0-144 144 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-144 0 0-144z"/></svg>
</button>
</div>
<div class="actions" data-role="actions" hidden>
<button class="transport" data-role="prev" aria-label="{{ room.label }}: previous track">
<!-- backward-step: the forward-step glyph below, mirrored -->
<svg viewBox="0 0 320 512" aria-hidden="true"><path transform="matrix(-1 0 0 1 320 0)" d="M52.5 440.6c-9.5 7.9-22.8 9.7-34.1 4.4S0 428.4 0 416L0 96C0 83.6 7.2 72.3 18.4 67s24.5-3.6 34.1 4.4l192 160L256 241l0-145c0-17.7 14.3-32 32-32s32 14.3 32 32l0 320c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-145-11.5 9.6-192 160z"/></svg>
</button>
<button class="transport" data-role="play" aria-label="{{ room.label }}: play">
<svg class="icon-play" viewBox="0 0 384 512" aria-hidden="true"><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80L0 432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg>
<svg class="icon-pause" viewBox="0 0 320 512" aria-hidden="true"><path d="M48 64C21.5 64 0 85.5 0 112L0 400c0 26.5 21.5 48 48 48l32 0c26.5 0 48-21.5 48-48l0-288c0-26.5-21.5-48-48-48L48 64zm192 0c-26.5 0-48 21.5-48 48l0 288c0 26.5 21.5 48 48 48l32 0c26.5 0 48-21.5 48-48l0-288c0-26.5-21.5-48-48-48l-32 0z"/></svg>
</button>
<button class="transport" data-role="next" aria-label="{{ room.label }}: next track">
<svg viewBox="0 0 320 512" aria-hidden="true"><path d="M52.5 440.6c-9.5 7.9-22.8 9.7-34.1 4.4S0 428.4 0 416L0 96C0 83.6 7.2 72.3 18.4 67s24.5-3.6 34.1 4.4l192 160L256 241l0-145c0-17.7 14.3-32 32-32s32 14.3 32 32l0 320c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-145-11.5 9.6-192 160z"/></svg>
</button>
</div>
{{ control_row(room.key, spotify_accounts) }}
</section>
</div>
{% endfor %}
<section class="card source">
<div class="card-head">
<h2><svg class="room-icon" data-icon="{{ host.kind }}" viewBox="0 0 512 512" aria-hidden="true"><path d=""/></svg><span class="room-label">{{ host.label }}</span></h2>
<span class="pill" data-role="avr-status">offline</span>
</div>
<div class="now-playing" data-role="avr-now-playing" hidden>
<img class="cover poster" data-role="avr-cover" alt="" hidden>
<div class="track">
<span class="song" data-role="avr-song"></span>
<span class="artist" data-role="avr-artist"></span>
</div>
</div>
<div class="progress" data-role="avr-progress" data-target="{{ host.key }}" hidden>
<div class="progress-line">
<i data-role="progress-fill"></i>
<b class="progress-cursor" data-role="progress-cursor"></b>
<span class="progress-time elapsed" data-role="progress-elapsed"></span>
<span class="progress-time remaining" data-role="progress-remaining"></span>
</div>
<span class="progress-total" data-role="progress-total"></span>
</div>
<button class="source-button" data-role="input-button">
<span class="eyebrow">Input</span>
<svg class="input-icon" data-role="input-icon" viewBox="0 0 512 512" aria-hidden="true"><path d=""/></svg>
<span class="value" data-role="input-name"></span>
<svg class="chevron" viewBox="0 0 320 512" aria-hidden="true"><path d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z"/></svg>
</button>
<div class="joined" data-role="joined" hidden></div>
</section>
<p class="foot" data-role="foot"></p>
</div>
<div class="sheet-wrap" data-role="sheet" hidden>
<div class="scrim" data-role="scrim"></div>
<div class="sheet" role="dialog" aria-modal="true" aria-label="{{ host.label }} input">
<div class="grabber" aria-hidden="true"></div>
<h3>{{ host.label }} input</h3>
<div class="options" data-role="options"></div>
<button class="wide ghost" data-role="sheet-close">Cancel</button>
</div>
</div>
<div class="toast" data-role="toast" hidden></div>
<script src="{{ url_for('static', filename='app.js') }}"></script>
</body>
</html>