diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index 54ea2bd..764eca4 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -76,7 +76,8 @@ (clearCompleted)="clearCompletedDownloads()" (clearFailed)="clearFailedDownloads()" (retryFailed)="retryFailedDownloads()" - (downloadSelected)="downloadSelectedFiles($event)"> + (downloadSelected)="downloadSelectedFiles($event)" + (play)="openPlayer($event)"> @@ -146,4 +147,16 @@ + +
+ + diff --git a/ui/src/app/app.component.sass b/ui/src/app/app.component.sass index 50fe880..49c04d8 100644 --- a/ui/src/app/app.component.sass +++ b/ui/src/app/app.component.sass @@ -62,6 +62,45 @@ font-weight: 600 color: var(--fg) +// In-page video player +.player + position: fixed + top: 50% + left: 50% + transform: translate(-50%, -50%) + width: min(960px, 92vw) + background: var(--surface) + border: 1px solid var(--border) + border-radius: 12px + box-shadow: 0 24px 64px oklch(20% 0.02 240 / 0.35) + z-index: 50 + overflow: hidden + display: flex + flex-direction: column + +.player-head + display: flex + align-items: center + gap: 12px + padding: 10px 14px + border-bottom: 1px solid var(--border) + flex: none + + .player-title + flex: 1 + min-width: 0 + font-size: 14px + font-weight: 600 + color: var(--fg) + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + +.player-video + width: 100% + max-height: 78vh + background: #000 + .icon-btn margin-left: auto diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index 600dff6..355948c 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, HostListener, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, distinctUntilChanged, map } from 'rxjs'; @@ -45,6 +45,10 @@ export class AppComponent implements OnInit { sidebarOpen = false; activityOpen = false; + // In-page video player (completed items) + playerUrl: string | null = null; + playerTitle = ''; + // Metrics activeDownloads = 0; queuedDownloads = 0; @@ -136,6 +140,23 @@ export class AppComponent implements OnInit { this.isAdvancedOpen = open === undefined ? !this.isAdvancedOpen : open; } + // ---------- In-page player ---------- + + openPlayer(row: KeyedDownload): void { + this.playerUrl = this.buildDownloadLink(row.value); + this.playerTitle = row.value.title; + } + + closePlayer(): void { + this.playerUrl = null; + this.playerTitle = ''; + } + + @HostListener('document:keydown.escape') + onEscape(): void { + if (this.playerUrl) this.closePlayer(); + } + // ---------- Data getters ---------- statusEntries(tab: DownloadTab): KeyedDownload[] { diff --git a/ui/src/app/download-card.component.html b/ui/src/app/download-card.component.html index 7aa2058..a5a3be7 100644 --- a/ui/src/app/download-card.component.html +++ b/ui/src/app/download-card.component.html @@ -19,7 +19,10 @@ {{ row.value.size ? (row.value.size | fileSize) : '—' }}
- + +
@@ -74,7 +75,8 @@ (selectChange)="toggleSelect(row.key, $event)" (del)="del.emit([$event])" (retry)="retry.emit($event)" - (folderChange)="folderEdit.emit($event)"> + (folderChange)="folderEdit.emit($event)" + (play)="play.emit(row)"> diff --git a/ui/src/app/download-list.component.ts b/ui/src/app/download-list.component.ts index 434153e..c526b54 100644 --- a/ui/src/app/download-list.component.ts +++ b/ui/src/app/download-list.component.ts @@ -37,6 +37,7 @@ export class DownloadListComponent implements OnChanges { @Output() clearFailed = new EventEmitter(); @Output() retryFailed = new EventEmitter(); @Output() downloadSelected = new EventEmitter(); + @Output() play = new EventEmitter(); faList = faList; faThLarge = faThLarge; diff --git a/ui/src/app/download-row.component.html b/ui/src/app/download-row.component.html index 5589e99..c97d652 100644 --- a/ui/src/app/download-row.component.html +++ b/ui/src/app/download-row.component.html @@ -39,7 +39,10 @@ - + +