blob: 33478adde064710287e7d99104979da464b4c693 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Player</title>
<link rel="manifest" href="/manifest.json">
<link rel="stylesheet" href="/css/theme.css">
<link rel="stylesheet" href="/css/layout.css">
<link rel="stylesheet" href="/css/components.css">
<link rel="stylesheet" href="/css/player.css">
<script type="module" src="/js/app.js"></script>
</head>
<body>
<header class="site-header" role="banner">
<a href="/" class="logo">Player</a>
<div class="spacer"></div>
<button id="help-toggle" class="icon-btn" title="Help (?)">?</button>
<button id="admin-toggle" class="icon-btn hidden" title="Admin panel">⚙</button>
<button id="logout-btn" class="btn btn-ghost btn-sm">Log out</button>
</header>
<!-- Sidebar: hidden by default, toggled via keyboard or menu -->
<nav id="sidebar" class="sidebar" aria-label="Sets">
<div class="sidebar-header">
<h2>Sets</h2>
<button id="menu-close" class="icon-btn btn-sm" title="Close">✕</button>
</div>
<div id="set-list"></div>
</nav>
<!-- Main page area -->
<div class="page" role="main">
<div class="main-content">
<!-- Toolbar: hidden by default, shown on 't' key or when searching -->
<div id="toolbar" class="toolbar hidden">
<select id="filter-type" aria-label="Filter by type">
<option value="">All</option>
<option value="video">Video</option>
<option value="audio">Audio</option>
</select>
<button id="filter-favorites" class="btn btn-ghost btn-sm">♥ Favorites</button>
<div class="spacer"></div>
<button id="filter-toggle" class="btn btn-ghost btn-sm" title="More filters">Filters ▾</button>
<span id="result-count" class="text-muted text-xs"></span>
</div>
<div id="filter-advanced" class="toolbar hidden">
<input id="filter-tags" type="text" placeholder="Tags (comma)" aria-label="Filter by tags" class="input-sm">
<input id="filter-min-duration" type="number" min="0" placeholder="Min s" aria-label="Min duration seconds" class="input-sm">
<input id="filter-max-duration" type="number" min="0" placeholder="Max s" aria-label="Max duration seconds" class="input-sm">
<input id="filter-min-filesize" type="number" min="0" placeholder="Min MB" aria-label="Min file size MB" class="input-sm">
<input id="filter-max-filesize" type="number" min="0" placeholder="Max MB" aria-label="Max file size MB" class="input-sm">
</div>
<!-- Search bar: hidden by default, shown on '/' key -->
<div id="search-bar" class="search-bar hidden" role="search">
<span aria-hidden="true">🔎</span>
<input id="search-input" type="search" placeholder="Search… (press /)" autocomplete="off" aria-label="Search media">
<button id="search-clear" class="icon-btn btn-sm" title="Clear">✕</button>
</div>
<!-- Empty state hint: shown when no set is selected -->
<div id="empty-hint" class="empty-hint">
<p>Welcome to Player</p>
<p class="text-muted text-sm">Press <kbd>m</kbd> to browse sets, <kbd>/</kbd> to search, or <kbd>?</kbd> for help.</p>
</div>
<!-- Media grid: hidden until a set is loaded or search is active -->
<div id="media-grid" class="media-grid hidden" tabindex="0" aria-label="Media grid"></div>
</div>
</div>
<!-- Player: hidden until something plays -->
<aside id="player" class="player" aria-label="Player">
<div class="stage">
<video id="media-video" playsinline controlslist="nodownload"></video>
<audio id="media-audio" class="hidden"></audio>
<div id="big-play" class="big-play" role="button" aria-label="Play" tabindex="0">
<div class="play-icon">▶</div>
</div>
</div>
<div class="controls">
<button id="btn-play" class="icon-btn" aria-label="Play/Pause (Space/p)">▶</button>
<div class="time" id="time-elapsed">0:00</div>
<div id="progress-track" class="progress-track" tabindex="0" aria-label="Seek">
<div id="progress-fill" class="progress-fill"></div>
<div id="progress-thumb" class="progress-thumb" aria-hidden="true"></div>
</div>
<div class="time" id="time-total">0:00</div>
<button id="btn-prev" class="icon-btn" aria-label="Previous">⏮</button>
<button id="btn-next" class="icon-btn" aria-label="Next">⏭</button>
<div class="volume-control">
<button id="btn-mute" class="icon-btn" aria-label="Mute">🔊</button>
<input id="volume-slider" type="range" min="0" max="1" step="0.05" value="1" class="volume-slider" aria-label="Volume">
</div>
<button id="btn-fullscreen" class="icon-btn" aria-label="Fullscreen (f)">⛶</button>
</div>
</aside>
<footer class="toast" id="toast" role="status" aria-live="polite"></footer>
<!-- Help Modal -->
<div id="help-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="help-title">
<div class="modal">
<div class="modal-header">
<h3 id="help-title">Keyboard Shortcuts</h3>
<div class="spacer"></div>
<button id="help-close" class="icon-btn" aria-label="Close">✕</button>
</div>
<div class="help-content">
<table class="help-table">
<tr><td><kbd>↑</kbd> <kbd>↓</kbd></td><td>Navigate media list</td></tr>
<tr><td><kbd>k</kbd> <kbd>j</kbd></td><td>Navigate media list (vim-style)</td></tr>
<tr><td><kbd>←</kbd> <kbd>→</kbd></td><td>Switch sets / pages</td></tr>
<tr><td><kbd>h</kbd> <kbd>l</kbd></td><td>Switch sets / pages (vim-style)</td></tr>
<tr><td><kbd>Enter</kbd></td><td>Open selected media</td></tr>
<tr><td><kbd>Space</kbd> <kbd>p</kbd></td><td>Play / pause</td></tr>
<tr><td><kbd>f</kbd></td><td>Toggle fullscreen</td></tr>
<tr><td><kbd>Esc</kbd></td><td>Exit fullscreen, deselect item</td></tr>
<tr><td><kbd>r</kbd></td><td>Toggle shuffle</td></tr>
<tr><td><kbd>s</kbd></td><td>Share selected media</td></tr>
<tr><td><kbd>/</kbd></td><td>Focus search bar</td></tr>
<tr><td><kbd>n</kbd></td><td>Open notes for selected media</td></tr>
<tr><td><kbd>t</kbd></td><td>Toggle toolbar / filters</td></tr>
<tr><td><kbd>m</kbd></td><td>Toggle sets sidebar</td></tr>
<tr><td><kbd>?</kbd></td><td>Show / hide this help</td></tr>
</table>
<p class="text-muted text-xs mt-2">Click the <strong>?</strong> button or press <kbd>?</kbd> anytime to show or hide this help.</p>
</div>
</div>
</div>
<!-- Notes Modal -->
<div id="notes-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="notes-title">
<div class="modal">
<div class="modal-header">
<h3 id="notes-title">Notes</h3>
<div class="spacer"></div>
<button id="notes-close" class="icon-btn" aria-label="Close">✕</button>
</div>
<textarea id="notes-textarea" rows="6" placeholder="Add your notes…"></textarea>
<div class="flex justify-end gap-2 mt-1">
<button id="notes-delete" class="btn btn-danger btn-sm">Delete</button>
<button id="notes-save" class="btn btn-primary btn-sm">Save</button>
</div>
</div>
</div>
<!-- Tags Modal -->
<div id="tags-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="tags-title">
<div class="modal">
<div class="modal-header">
<h3 id="tags-title">Tags</h3>
<div class="spacer"></div>
<button id="tags-close" class="icon-btn" aria-label="Close">✕</button>
</div>
<div id="tags-list" class="flex flex-wrap gap-2 mb-2"></div>
<div class="flex gap-2">
<input id="tags-new" type="text" placeholder="New tag…" autocomplete="off">
<button id="tags-add" class="btn btn-primary btn-sm">Add</button>
</div>
</div>
</div>
<!-- Upload Modal -->
<div id="upload-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="upload-title">
<div class="modal">
<div class="modal-header">
<h3 id="upload-title">Upload</h3>
<div class="spacer"></div>
<button id="upload-close" class="icon-btn" aria-label="Close">✕</button>
</div>
<form id="upload-form" class="flex flex-col gap-2">
<input id="upload-file" type="file" required>
<button type="submit" class="btn btn-primary btn-sm">Upload to current set</button>
</form>
</div>
</div>
<!-- Admin Modal -->
<div id="admin-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="admin-title">
<div class="modal modal-wide">
<div class="modal-header">
<h3 id="admin-title">Admin</h3>
<div class="spacer"></div>
<button id="admin-close" class="icon-btn" aria-label="Close">✕</button>
</div>
<div class="flex gap-4 flex-wrap">
<section class="flex-1-18">
<h4 class="my-1 text-90">Users</h4>
<div id="admin-users"></div>
<form id="admin-user-form" class="mt-2">
<input name="username" placeholder="Username" required autocomplete="off">
<input type="password" name="password" placeholder="Password" required autocomplete="new-password">
<label class="flex align-center gap-1 text-xs">
<input type="checkbox" name="is_admin"> Admin
</label>
<button type="submit" class="btn btn-primary btn-sm mt-1">Add user</button>
</form>
</section>
<section class="flex-1-18">
<h4 class="my-1 text-90">Permissions</h4>
<div id="admin-permissions"></div>
</section>
</div>
<div class="flex gap-2 mt-3">
<button id="admin-rescan" class="btn btn-primary btn-sm">Trigger rescan</button>
<button id="admin-trash" class="btn btn-danger btn-sm">View trash</button>
</div>
</div>
</div>
</body>
</html>
|