vyrplay 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,220 +0,0 @@
1
- const BAT_SVG = `<svg viewBox="0 0 420 220" aria-hidden="true">
2
- <path class="vyr-bat-base" d="M210 22 L188 65 Q176 82 186 96 Q142 85 110 120 Q85 92 60 80 Q85 45 28 20 Q65 22 115 55 Q155 85 185 120 Q195 175 210 205 Q225 175 235 120 Q265 85 305 55 Q355 22 392 20 Q335 45 360 80 Q335 92 310 120 Q278 85 234 96 Q244 82 232 65 L210 22"/>
3
- <path class="vyr-bat-line" d="M210 22 L188 65 Q176 82 186 96 Q142 85 110 120 Q85 92 60 80 Q85 45 28 20 Q65 22 115 55 Q155 85 185 120 Q195 175 210 205 Q225 175 235 120 Q265 85 305 55 Q355 22 392 20 Q335 45 360 80 Q335 92 310 120 Q278 85 234 96 Q244 82 232 65 L210 22"/>
4
- </svg>`;
5
-
6
- const ICONS = {
7
- back: `<svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>`,
8
- pip: `<svg viewBox="0 0 24 24"><path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 14H3V5h18v12z"/></svg>`,
9
- settings: `<svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.31.06-.63.06-.94s-.02-.63-.06-.94l2.03-1.58a.49.49 0 0 0 .12-.61l-1.92-3.32a.49.49 0 0 0-.59-.22l-2.39.96a7.2 7.2 0 0 0-1.62-.94l-.36-2.54A.48.48 0 0 0 14 2h-4a.48.48 0 0 0-.48.41l-.36 2.54c-.59.24-1.13.56-1.62.94l-2.39-.96a.49.49 0 0 0-.59.22L2.65 8.87a.48.48 0 0 0 .12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58a.49.49 0 0 0-.12.61l1.92 3.32c.13.23.4.32.64.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h4c.24 0 .44-.17.48-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.23.1.51 0 .64-.22l1.92-3.32a.49.49 0 0 0-.12-.61l-2.03-1.58zM12 15.5A3.5 3.5 0 1 1 12 8.5a3.5 3.5 0 0 1 0 7z"/></svg>`,
10
- lockOpen: `<svg class="vyr-ic-lock-open" viewBox="0 0 24 24"><path d="M12 17a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6-7h-1V7A5 5 0 0 0 7 7h1.5a3.5 3.5 0 0 1 7 0v3H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2z"/></svg>`,
11
- lockClosed: `<svg class="vyr-ic-lock-closed" viewBox="0 0 24 24" style="display:none"><path d="M12 17a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6-7h-1V7A5 5 0 0 0 7 7v3H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2zM8.5 7a3.5 3.5 0 0 1 7 0v3h-7V7z"/></svg>`,
12
- play: `<svg class="vyr-ic-play" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>`,
13
- pause: `<svg class="vyr-ic-pause" viewBox="0 0 24 24" style="display:none"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>`,
14
- rew: `<svg viewBox="0 0 24 24"><path d="M11.99 5V1l-5 5 5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6h-2c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/></svg>`,
15
- ff: `<svg viewBox="0 0 24 24"><path d="M12 5V1l5 5-5 5V7c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6h2c0 4.42-3.58 8-8 8s-8-3.58-8-8 3.58-8 8-8z"/></svg>`,
16
- fs: `<svg viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>`,
17
- volOn: `<svg class="vyr-ic-vol-on" viewBox="0 0 24 24"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg>`,
18
- cc: `<svg viewBox="0 0 24 24"><path d="M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v1z"/></svg>`,
19
- volOff: `<svg class="vyr-ic-vol-off" viewBox="0 0 24 24" style="display:none"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/></svg>`,
20
- };
21
-
22
- export function buildTemplate(opts = {}) {
23
- const title = opts.title || 'VyrPlay';
24
- const f = opts.features || {};
25
- return `
26
- <div class="vyr-loading">
27
- <div class="vyr-bat-wrap">${BAT_SVG}</div>
28
- <div class="label">LOADING</div>
29
- </div>
30
- <div class="vyr-buffer" aria-hidden="true">
31
- <div class="vyr-bat-wrap">${BAT_SVG}</div>
32
- </div>
33
- <div class="vyr-poster hide"></div>
34
- <video playsinline webkit-playsinline preload="metadata" controlslist="nodownload noplaybackrate"></video>
35
- <div class="vyr-sub-layer pos-bottom" aria-live="polite"><div class="vyr-sub-text"></div></div>
36
- <div class="vyr-pip-overlay" aria-hidden="true">
37
- <div class="vyr-pip-box">
38
- ${ICONS.pip}
39
- <div style="font-size:16px;font-weight:800;margin:12px 0 6px">Picture-in-Picture aktif</div>
40
- <div style="font-size:13px;opacity:.65;margin-bottom:8px">Video diputar di jendela mengambang</div>
41
- <button type="button" class="vyr-btn-pip-back">Kembali ke player</button>
42
- </div>
43
- </div>
44
- <div class="vyr-tap vyr-tap-left"></div>
45
- <div class="vyr-tap vyr-tap-right"></div>
46
- <div class="vyr-seek-toast vyr-seek-left"><div class="sec">«10</div></div>
47
- <div class="vyr-seek-toast vyr-seek-right"><div class="sec">10»</div></div>
48
- <div class="vyr-speed-badge">2x</div>
49
- <div class="vyr-vol-toast"><span class="vyr-vol-toast-text">100%</span></div>
50
-
51
- <div class="vyr-ui">
52
- <div class="vyr-top">
53
- <div class="vyr-top-left">
54
- ${f.back !== false ? `<button class="vyr-icon-btn vyr-btn-back" title="Kembali">${ICONS.back}</button>` : ''}
55
- <div class="vyr-title">${escapeHtml(title)}</div>
56
- </div>
57
- <div class="vyr-top-right">
58
- ${f.pip !== false ? `<button class="vyr-icon-btn vyr-btn-pip" title="PiP">${ICONS.pip}</button>` : ''}
59
- ${f.settings !== false ? `<button class="vyr-icon-btn vyr-btn-settings" title="Pengaturan">${ICONS.settings}</button>` : ''}
60
- ${f.lockUi !== false ? `<button class="vyr-icon-btn vyr-btn-lock" title="Kunci UI">${ICONS.lockOpen}${ICONS.lockClosed}</button>` : ''}
61
- </div>
62
- </div>
63
-
64
- <div class="vyr-center">
65
- ${f.seekButtons !== false ? `
66
- <button class="vyr-c-btn vyr-btn-rew" aria-label="Mundur">
67
- ${ICONS.rew}<span class="seek-badge vyr-badge-rew">10</span>
68
- </button>` : ''}
69
- <button class="vyr-c-btn play vyr-btn-pp" aria-label="Play/Pause">
70
- <div class="ring"></div>${ICONS.play}${ICONS.pause}
71
- </button>
72
- ${f.seekButtons !== false ? `
73
- <button class="vyr-c-btn vyr-btn-ff" aria-label="Maju">
74
- ${ICONS.ff}<span class="seek-badge vyr-badge-ff">10</span>
75
- </button>` : ''}
76
- </div>
77
-
78
- <div class="vyr-bottom">
79
- <div class="vyr-progress">
80
- <div class="vyr-scrub-tip">0:00</div>
81
- <div class="vyr-progress-track">
82
- <div class="vyr-progress-fill"></div>
83
- <div class="vyr-progress-thumb"></div>
84
- </div>
85
- </div>
86
- <div class="vyr-meta">
87
- <div class="vyr-time">0:00 / 0:00</div>
88
- <div class="vyr-meta-right">
89
- ${f.volume !== false ? `
90
- <div class="vyr-vol-wrap">
91
- <button type="button" class="vyr-vol-btn vyr-btn-vol" title="Mute">${ICONS.volOn}${ICONS.volOff}</button>
92
- <input type="range" class="vyr-vol-slider" min="0" max="100" step="1" value="100" aria-label="Volume" />
93
- </div>` : ''}
94
- ${f.subtitles !== false ? `<button class="vyr-chip vyr-btn-cc" title="Subtitle">CC</button>` : ''}
95
- ${f.quality !== false ? `<button class="vyr-chip vyr-btn-quality" title="Kualitas">HD</button>` : ''}
96
- <button class="vyr-chip vyr-btn-fs" title="Fullscreen">${ICONS.fs}</button>
97
- </div>
98
- </div>
99
- </div>
100
- </div>
101
-
102
- <div class="vyr-backdrop"></div>
103
- <div class="vyr-sheet vyr-sheet-quality">
104
- <div class="vyr-sheet-head">
105
- <div class="vyr-sheet-title">Kualitas</div>
106
- <button type="button" class="vyr-sheet-close" data-close>✕</button>
107
- </div>
108
- <div class="vyr-sheet-body vyr-quality-list"></div>
109
- </div>
110
- <div class="vyr-sheet vyr-sheet-settings">
111
- <div class="vyr-sheet-head">
112
- <div class="vyr-sheet-title">Pengaturan</div>
113
- <button type="button" class="vyr-sheet-close" data-close>✕</button>
114
- </div>
115
- <div class="vyr-sheet-body">
116
- <div class="vyr-set-row" data-set="seek">
117
- <div class="vyr-set-ic"><svg viewBox="0 0 24 24"><path d="M12 5V1l5 5-5 5V7c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6h2c0 4.42-3.58 8-8 8s-8-3.58-8-8 3.58-8 8-8z"/></svg></div>
118
- <div class="vyr-set-txt"><div class="vyr-set-title">Seek</div><div class="vyr-set-desc">Mundur / maju per ketukan</div></div>
119
- <div class="vyr-set-val vyr-set-seek-val">10s</div>
120
- </div>
121
- <div class="vyr-set-row" data-set="speed">
122
- <div class="vyr-set-ic"><svg viewBox="0 0 24 24"><path d="M13.5 5.5c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zM9.8 8.9L7 23h2.1l1.8-8 2.1 2v6h2v-7.5l-2.1-2 .6-3C14.8 12 16.8 13 19 13v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1L6 8.3V13h2V9.6l1.8-.7z"/></svg></div>
123
- <div class="vyr-set-txt"><div class="vyr-set-title">Speed</div><div class="vyr-set-desc">Kecepatan putar normal</div></div>
124
- <div class="vyr-set-val vyr-set-speed-val">1x</div>
125
- </div>
126
- <div class="vyr-set-row" data-set="hold">
127
- <div class="vyr-set-ic"><svg viewBox="0 0 24 24"><path d="M13 2v8h3l-4 10v-8H9l4-10z"/></svg></div>
128
- <div class="vyr-set-txt"><div class="vyr-set-title">Hold to speed</div><div class="vyr-set-desc">Tahan layar untuk percepat</div></div>
129
- <div class="vyr-set-val vyr-set-hold-val">2x</div>
130
- </div>
131
- <div class="vyr-set-row" data-set="substyle">
132
- <div class="vyr-set-ic"><svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM4 12h4v2H4v-2zm10 6H4v-2h10v2zm6 0h-4v-2h4v2zm0-4H10v-2h10v2z"/></svg></div>
133
- <div class="vyr-set-txt"><div class="vyr-set-title">Subtitle style</div><div class="vyr-set-desc">Posisi, warna, ukuran, background</div></div>
134
- <div class="vyr-set-val">›</div>
135
- </div>
136
- </div>
137
- </div>
138
- <div class="vyr-sheet vyr-sheet-seek">
139
- <div class="vyr-sheet-head">
140
- <div class="vyr-sheet-title">Seek</div>
141
- <button type="button" class="vyr-sheet-close" data-close>✕</button>
142
- </div>
143
- <div class="vyr-sheet-body vyr-seek-list"></div>
144
- </div>
145
- <div class="vyr-sheet vyr-sheet-speed">
146
- <div class="vyr-sheet-head">
147
- <div class="vyr-sheet-title">Speed</div>
148
- <button type="button" class="vyr-sheet-close" data-close>✕</button>
149
- </div>
150
- <div class="vyr-sheet-body vyr-speed-list"></div>
151
- </div>
152
- <div class="vyr-sheet vyr-sheet-hold">
153
- <div class="vyr-sheet-head">
154
- <div class="vyr-sheet-title">Hold to speed</div>
155
- <button type="button" class="vyr-sheet-close" data-close>✕</button>
156
- </div>
157
- <div class="vyr-sheet-body">
158
- <div class="vyr-set-row" data-hold-toggle style="margin-bottom:8px">
159
- <div class="vyr-set-txt"><div class="vyr-set-title">Aktifkan</div><div class="vyr-set-desc">Tahan layar = putar lebih cepat</div></div>
160
- <div class="vyr-set-val"><span class="vyr-tog on" data-tog-hold></span></div>
161
- </div>
162
- <div style="font-size:12px;font-weight:700;opacity:.55;padding:8px 4px 6px;letter-spacing:.04em;text-transform:uppercase">Saat ditahan</div>
163
- <div class="vyr-hold-list"></div>
164
- </div>
165
- </div>
166
-
167
- <div class="vyr-sheet vyr-sheet-subs">
168
- <div class="vyr-sheet-head">
169
- <div class="vyr-sheet-title">Subtitle</div>
170
- <button type="button" class="vyr-sheet-close" data-close>✕</button>
171
- </div>
172
- <div class="vyr-sheet-body">
173
- <div class="vyr-set-row" data-sub-toggle style="margin-bottom:8px">
174
- <div class="vyr-set-txt">
175
- <div class="vyr-set-title">Tampilkan subtitle</div>
176
- <div class="vyr-set-desc">On / off teks di layar</div>
177
- </div>
178
- <div class="vyr-set-val"><span class="vyr-tog on" data-tog-sub></span></div>
179
- </div>
180
- <div style="font-size:12px;font-weight:700;opacity:.55;padding:8px 4px 6px;letter-spacing:.04em;text-transform:uppercase">Bahasa / track</div>
181
- <div class="vyr-subs-list"></div>
182
- </div>
183
- </div>
184
- <div class="vyr-sheet vyr-sheet-substyle">
185
- <div class="vyr-sheet-head">
186
- <div class="vyr-sheet-title">Subtitle style</div>
187
- <button type="button" class="vyr-sheet-close" data-close>✕</button>
188
- </div>
189
- <div class="vyr-sheet-body">
190
- <div class="vyr-sub-style-row">
191
- <label>Posisi Y</label>
192
- <input type="range" class="vyr-sub-y" min="0" max="80" step="1" value="12" />
193
- <span class="vyr-sub-y-val" style="font-size:12px;font-weight:800;min-width:36px;font-family:var(--vyr-font)">12%</span>
194
- </div>
195
- <div class="vyr-sub-style-row">
196
- <label>Warna</label>
197
- <input type="color" class="vyr-sub-color" value="#ffffff" />
198
- </div>
199
- <div class="vyr-sub-style-row">
200
- <label>Ukuran</label>
201
- <input type="range" class="vyr-sub-size" min="14" max="42" step="1" value="20" />
202
- <span class="vyr-sub-size-val" style="font-size:12px;font-weight:800;min-width:36px">20px</span>
203
- </div>
204
- <div class="vyr-sub-style-row">
205
- <label>Background</label>
206
- <input type="range" class="vyr-sub-bg" min="0" max="100" step="5" value="55" />
207
- <span class="vyr-sub-bg-val" style="font-size:12px;font-weight:800;min-width:36px">55%</span>
208
- </div>
209
- </div>
210
- </div>
211
- `;
212
- }
213
-
214
- function escapeHtml(s) {
215
- return String(s)
216
- .replace(/&/g, '&amp;')
217
- .replace(/</g, '&lt;')
218
- .replace(/>/g, '&gt;')
219
- .replace(/"/g, '&quot;');
220
- }
@@ -1,25 +0,0 @@
1
- /** Tiny event emitter */
2
- export class Emitter {
3
- constructor() {
4
- this._map = new Map();
5
- }
6
- on(event, fn) {
7
- if (!this._map.has(event)) this._map.set(event, new Set());
8
- this._map.get(event).add(fn);
9
- return () => this.off(event, fn);
10
- }
11
- off(event, fn) {
12
- const set = this._map.get(event);
13
- if (set) set.delete(fn);
14
- }
15
- emit(event, payload) {
16
- const set = this._map.get(event);
17
- if (!set) return;
18
- set.forEach((fn) => {
19
- try { fn(payload); } catch (e) { console.error('[VyrPlay]', e); }
20
- });
21
- }
22
- destroy() {
23
- this._map.clear();
24
- }
25
- }
@@ -1,20 +0,0 @@
1
- /** Format seconds → m:ss or h:mm:ss */
2
- export function fmtTime(s) {
3
- if (!isFinite(s) || s < 0) return '0:00';
4
- s = Math.floor(s);
5
- const h = Math.floor(s / 3600);
6
- const m = Math.floor((s % 3600) / 60);
7
- const sec = s % 60;
8
- if (h > 0) {
9
- return h + ':' + String(m).padStart(2, '0') + ':' + String(sec).padStart(2, '0');
10
- }
11
- return m + ':' + String(sec).padStart(2, '0');
12
- }
13
-
14
- export function fmtRate(r) {
15
- return (Number.isInteger(r) ? r : r) + 'x';
16
- }
17
-
18
- export function clamp(n, min, max) {
19
- return Math.max(min, Math.min(max, n));
20
- }