Skip to content

Commit 53a54c7

Browse files
Added picture-in-picture button to video player and search filter for manage content page
1 parent e8e6ba2 commit 53a54c7

File tree

4 files changed

+113
-5
lines changed

4 files changed

+113
-5
lines changed

public/index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ <h2 class="watch-title-overlay" id="player-channel-name"></h2>
189189
</div>
190190
</div>
191191

192+
<button class="watch-btn" id="btn-pip" title="Picture-in-Picture">
193+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
194+
<path
195+
d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z" />
196+
</svg>
197+
</button>
192198
<button class="watch-btn" id="btn-fullscreen" title="Fullscreen">
193199
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
194200
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" />
@@ -517,6 +523,10 @@ <h3>Keyboard Shortcuts</h3>
517523
<select id="content-source-select" class="source-select">
518524
<option value="">Select a source...</option>
519525
</select>
526+
<div class="search-wrapper">
527+
<input type="text" id="content-search" placeholder="Search groups/channels..." class="search-input">
528+
<button type="button" class="search-clear" title="Clear search">&times;</button>
529+
</div>
520530
<div class="content-actions">
521531
<button class="btn btn-sm btn-secondary" id="content-show-all">Show All</button>
522532
<button class="btn btn-sm btn-secondary" id="content-hide-all">Hide All</button>
@@ -674,6 +684,12 @@ <h3 class="watch-title" id="watch-title"></h3>
674684
</div>
675685
</div>
676686
</div>
687+
<button class="watch-btn" id="watch-pip" title="Picture-in-Picture">
688+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
689+
<path
690+
d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z" />
691+
</svg>
692+
</button>
677693
<button class="watch-btn" id="watch-fullscreen" title="Fullscreen">
678694
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="icon">
679695
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" />

public/js/components/SourceManager.js

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class SourceManager {
1515
this.hiddenSet = new Set(); // Set of hidden item keys (current state)
1616
this.originalHiddenSet = new Set(); // Set of hidden item keys (state when loaded)
1717
this.expandedGroups = new Set(); // Set of expanded group IDs
18+
this.searchQuery = ''; // Search filter for content browser
1819

1920
this.init();
2021
}
@@ -426,6 +427,23 @@ class SourceManager {
426427

427428
// Save Changes button
428429
document.getElementById('content-save')?.addEventListener('click', () => this.saveContentChanges());
430+
431+
// Search input
432+
const searchInput = document.getElementById('content-search');
433+
const searchClear = searchInput?.parentElement?.querySelector('.search-clear');
434+
435+
searchInput?.addEventListener('input', (e) => {
436+
this.searchQuery = e.target.value.toLowerCase().trim();
437+
this.renderTree();
438+
});
439+
440+
searchClear?.addEventListener('click', () => {
441+
if (searchInput) {
442+
searchInput.value = '';
443+
this.searchQuery = '';
444+
this.renderTree();
445+
}
446+
});
429447
}
430448

431449
/**
@@ -565,16 +583,45 @@ class SourceManager {
565583
}
566584
}
567585

586+
/**
587+
* Get groups filtered by search query
588+
*/
589+
getFilteredGroups() {
590+
if (!this.treeData?.groups) return [];
591+
if (!this.searchQuery) return this.treeData.groups;
592+
593+
return this.treeData.groups
594+
.map(group => {
595+
// Check if group name matches
596+
const groupMatches = group.name.toLowerCase().includes(this.searchQuery);
597+
598+
// Filter items that match
599+
const matchingItems = group.items.filter(item =>
600+
item.name.toLowerCase().includes(this.searchQuery)
601+
);
602+
603+
// Include group if name matches OR has matching items
604+
if (groupMatches || matchingItems.length > 0) {
605+
return { ...group, items: groupMatches ? group.items : matchingItems };
606+
}
607+
return null;
608+
})
609+
.filter(Boolean);
610+
}
611+
568612
/**
569613
* Render the full tree based on current state
570614
*/
571615
renderTree() {
572-
if (!this.treeData || !this.treeData.groups.length) {
573-
this.contentTree.innerHTML = '<p class="hint">No content found</p>';
616+
const groups = this.getFilteredGroups();
617+
618+
if (!groups.length) {
619+
const msg = this.searchQuery ? 'No matches found' : 'No content found';
620+
this.contentTree.innerHTML = `<p class="hint">${msg}</p>`;
574621
return;
575622
}
576623

577-
const html = this.treeData.groups.map(group => this.getGroupHtml(group)).join('');
624+
const html = groups.map(group => this.getGroupHtml(group)).join('');
578625
this.contentTree.innerHTML = html;
579626

580627
// Attach event listeners
@@ -669,10 +716,11 @@ class SourceManager {
669716
this.expandedGroups.add(groupId);
670717
}
671718

672-
// Re-render only this group
719+
// Re-render only this group - use filtered groups to respect search
673720
const groupEl = this.contentTree.querySelector(`.content-group[data-group-id="${CSS.escape(groupId)}"]`);
674721
if (groupEl) {
675-
const group = this.treeData.groups.find(g => g.id === groupId);
722+
const filteredGroups = this.getFilteredGroups();
723+
const group = filteredGroups.find(g => g.id === groupId);
676724
if (group) {
677725
const newHtml = this.getGroupHtml(group);
678726
groupEl.outerHTML = newHtml;

public/js/components/VideoPlayer.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,13 @@ class VideoPlayer {
267267
this.toggleFullscreen();
268268
});
269269

270+
// Picture-in-Picture
271+
const btnPip = document.getElementById('btn-pip');
272+
btnPip?.addEventListener('click', (e) => {
273+
e.stopPropagation();
274+
this.togglePictureInPicture();
275+
});
276+
270277
this.container.addEventListener('dblclick', () => this.toggleFullscreen());
271278

272279
// Overlay Auto-hide Logic
@@ -325,6 +332,24 @@ class VideoPlayer {
325332
}
326333
}
327334

335+
/**
336+
* Toggle Picture-in-Picture mode
337+
*/
338+
async togglePictureInPicture() {
339+
try {
340+
if (document.pictureInPictureElement) {
341+
await document.exitPictureInPicture();
342+
} else if (document.pictureInPictureEnabled && this.video.readyState >= 2) {
343+
await this.video.requestPictureInPicture();
344+
}
345+
} catch (err) {
346+
// Silently fail - Firefox users can use native PiP button
347+
if (err.name !== 'NotAllowedError') {
348+
console.error('Picture-in-Picture error:', err);
349+
}
350+
}
351+
}
352+
328353

329354

330355
/**

public/js/pages/WatchPage.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ class WatchPage {
102102
// Fullscreen
103103
this.fullscreenBtn?.addEventListener('click', () => this.toggleFullscreen());
104104

105+
// Picture-in-Picture
106+
const pipBtn = document.getElementById('watch-pip');
107+
pipBtn?.addEventListener('click', () => this.togglePictureInPicture());
108+
105109
// Progress bar
106110
this.progressSlider?.addEventListener('input', (e) => this.seek(e.target.value));
107111

@@ -374,6 +378,21 @@ class WatchPage {
374378
}
375379
}
376380

381+
async togglePictureInPicture() {
382+
try {
383+
if (document.pictureInPictureElement) {
384+
await document.exitPictureInPicture();
385+
} else if (document.pictureInPictureEnabled && this.video.readyState >= 2) {
386+
await this.video.requestPictureInPicture();
387+
}
388+
} catch (err) {
389+
// Silently fail - Firefox users can use native PiP button
390+
if (err.name !== 'NotAllowedError') {
391+
console.error('Picture-in-Picture error:', err);
392+
}
393+
}
394+
}
395+
377396
// === UI Updates ===
378397

379398
updateProgress() {

0 commit comments

Comments
 (0)