blob: 1fc583ef6f8e8f3a65c3e5e6947a14543694693a (
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
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KISS Media 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">KISS Media</a>
<div class="search-bar" 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>
<div class="spacer"></div>
<button id="shuffle-toggle" class="icon-btn" title="Shuffle (r)">🔀</button>
<button id="admin-toggle" class="icon-btn hidden" title="Admin panel (a)">⚙</button>
<button id="theme-toggle" class="icon-btn" title="Toggle theme">☀</button>
<span id="user-name" class="text-secondary text-xs"></span>
<button id="upload-toggle" class="icon-btn" title="Upload">⬆</button>
<button id="logout-btn" class="btn btn-ghost btn-sm">Log out</button>
<button id="menu-btn" class="icon-btn" title="Menu" aria-label="Open sets">☰</button>
</header>
<nav id="sidebar" class="sidebar" aria-label="Sets">
<h2>Sets</h2>
<div id="set-list"></div>
</nav>
<div class="page" role="main">
<div class="main-content">
<div class="toolbar">
<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>
<div id="media-grid" class="media-grid" tabindex="0" aria-label="Media grid"></div>
</div>
</div>
<aside id="player" class="player open" 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>
<!-- 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>
|