mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
style(embedded-mpv): align chrome with app theme and fix viewport crop
- Switch palette and surfaces to Material 3 tokens (--mat-sys-*) so the player respects the app theme instead of a hardcoded cyan/sky scheme. - Stop reserving 96 px below the viewport for the control panel; the panel now floats over the bottom with a gradient fade so the video fills the frame (especially in fullscreen). - Drop control panel height from 96 to 64 px (88 px on narrow widths). - Replace the corner "Loading stream in MPV..." pill with a centered spinner + concise label during load; move the transient status toast to the top-right and add role="status" aria-live. - Add a red badge with pulsing dot for live streams in place of the plain "LIVE" text label. - Apply font-variant-numeric: tabular-nums to time/volume readouts so digits stop jittering as playback advances. - Slightly loosen transport/actions gaps for a less cramped rhythm and use ease-out easing on control fade. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: f957cd9849e0
This commit is contained in:
1 parent
1c499856ab
commit
2b57f3a35d
3 files changed
+152
-68
No files matched your search
@@ -13,8 +13,27 @@
|
||||
(focusin)="onPlayerInteraction()"
|
||||
>
|
||||
<div #viewport class="embedded-mpv-player__viewport">
|
||||
@if (statusLabel()) {
|
||||
<div class="embedded-mpv-player__status">
|
||||
@if (isLoading()) {
|
||||
<div
|
||||
class="embedded-mpv-player__loader"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
<mat-progress-spinner
|
||||
mode="indeterminate"
|
||||
diameter="48"
|
||||
strokeWidth="3"
|
||||
/>
|
||||
@if (statusLabel()) {
|
||||
<span>{{ statusLabel() }}</span>
|
||||
}
|
||||
</div>
|
||||
} @else if (statusLabel()) {
|
||||
<div
|
||||
class="embedded-mpv-player__status"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
{{ statusLabel() }}
|
||||
</div>
|
||||
}
|
||||
@@ -172,11 +191,22 @@
|
||||
<span>{{
|
||||
formatTime(session()?.positionSeconds)
|
||||
}}</span>
|
||||
<span>{{
|
||||
canSeek()
|
||||
? formatTime(session()?.durationSeconds)
|
||||
: 'LIVE'
|
||||
}}</span>
|
||||
@if (canSeek()) {
|
||||
<span>{{
|
||||
formatTime(session()?.durationSeconds)
|
||||
}}</span>
|
||||
} @else {
|
||||
<span
|
||||
class="embedded-mpv-player__live-badge"
|
||||
aria-label="Live stream"
|
||||
>
|
||||
<span
|
||||
class="embedded-mpv-player__live-dot"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
LIVE
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,18 +4,20 @@
|
||||
}
|
||||
|
||||
.embedded-mpv-player {
|
||||
--embedded-mpv-accent: #7dd3fc;
|
||||
--embedded-mpv-glass: rgba(6, 10, 18, 0.78);
|
||||
--embedded-mpv-border: rgba(255, 255, 255, 0.12);
|
||||
--embedded-mpv-controls-height: 96px;
|
||||
--embedded-mpv-accent: var(--mat-sys-primary);
|
||||
--embedded-mpv-glass: color-mix(
|
||||
in srgb,
|
||||
var(--mat-sys-surface) 78%,
|
||||
transparent
|
||||
);
|
||||
--embedded-mpv-border: var(--mat-sys-outline-variant);
|
||||
--embedded-mpv-controls-height: 64px;
|
||||
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(10, 14, 24, 0.08), rgba(10, 14, 24, 0.24)),
|
||||
#02050a;
|
||||
background: var(--mat-sys-surface);
|
||||
border-radius: inherit;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -29,31 +31,50 @@
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
border-radius: 0;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.embedded-mpv-player:fullscreen .embedded-mpv-player__viewport,
|
||||
.embedded-mpv-player--fullscreen .embedded-mpv-player__viewport {
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.embedded-mpv-player__viewport {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
min-height: 0;
|
||||
background:
|
||||
radial-gradient(circle at top, rgba(96, 125, 139, 0.24), transparent 58%),
|
||||
linear-gradient(180deg, rgba(6, 8, 13, 0.88), rgba(2, 4, 8, 0.98));
|
||||
background: var(--mat-sys-surface);
|
||||
}
|
||||
|
||||
.embedded-mpv-player--controls-enabled .embedded-mpv-player__viewport {
|
||||
bottom: var(--embedded-mpv-controls-height);
|
||||
.embedded-mpv-player__loader {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
grid-auto-rows: max-content;
|
||||
place-content: center;
|
||||
justify-items: center;
|
||||
gap: 12px;
|
||||
color: var(--mat-sys-on-surface);
|
||||
font-size: 0.85rem;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.embedded-mpv-player__loader span {
|
||||
color: color-mix(in srgb, var(--mat-sys-on-surface) 80%, transparent);
|
||||
}
|
||||
|
||||
.embedded-mpv-player__status {
|
||||
position: absolute;
|
||||
inset: auto 16px 16px 16px;
|
||||
inset: 16px 16px auto auto;
|
||||
max-width: min(60%, 360px);
|
||||
padding: 10px 12px;
|
||||
color: rgba(245, 247, 250, 0.94);
|
||||
background: rgba(7, 10, 16, 0.72);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
color: var(--mat-sys-on-surface);
|
||||
background: var(--embedded-mpv-glass);
|
||||
border: 1px solid var(--embedded-mpv-border);
|
||||
border-radius: 12px;
|
||||
backdrop-filter: blur(14px);
|
||||
font-size: 0.9rem;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.4;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
@@ -67,24 +88,23 @@
|
||||
z-index: 2;
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
gap: 18px;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
height: var(--embedded-mpv-controls-height);
|
||||
padding: 12px 18px 14px;
|
||||
min-height: var(--embedded-mpv-controls-height);
|
||||
padding: 8px 16px 10px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateY(14px);
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(2, 5, 10, 0.28), rgba(5, 9, 15, 0.88) 30%),
|
||||
linear-gradient(180deg, rgba(12, 18, 29, 0.82), rgba(5, 9, 15, 0.94));
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||
box-shadow: 0 -28px 70px rgba(0, 0, 0, 0.46);
|
||||
backdrop-filter: blur(18px);
|
||||
transform: translateY(8px);
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
transparent 0%,
|
||||
rgba(0, 0, 0, 0.55) 50%,
|
||||
rgba(0, 0, 0, 0.78) 100%
|
||||
);
|
||||
transition:
|
||||
opacity 160ms ease,
|
||||
transform 180ms ease;
|
||||
opacity 180ms ease-out,
|
||||
transform 200ms ease-out;
|
||||
}
|
||||
|
||||
.embedded-mpv-player__controls--visible {
|
||||
@@ -93,37 +113,60 @@
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.embedded-mpv-player__controls--panel-open {
|
||||
background:
|
||||
linear-gradient(180deg, rgba(2, 5, 10, 0.42), rgba(5, 9, 15, 0.92) 28%),
|
||||
linear-gradient(180deg, rgba(12, 18, 29, 0.9), rgba(5, 9, 15, 0.98));
|
||||
}
|
||||
|
||||
.embedded-mpv-player__transport {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.embedded-mpv-player__timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.embedded-mpv-player__time {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
color: rgba(226, 232, 240, 0.84);
|
||||
color: color-mix(in srgb, var(--mat-sys-on-surface) 84%, transparent);
|
||||
font-size: 0.78rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.embedded-mpv-player__live-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
color: var(--mat-sys-on-error);
|
||||
background: var(--mat-sys-error);
|
||||
font-size: 0.66rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.embedded-mpv-player__live-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
animation: embedded-mpv-live-pulse 1.6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes embedded-mpv-live-pulse {
|
||||
50% {
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
|
||||
.embedded-mpv-player__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.embedded-mpv-player__popover-anchor {
|
||||
@@ -142,14 +185,11 @@
|
||||
|
||||
.embedded-mpv-player__control-panel {
|
||||
min-width: 0;
|
||||
color: rgba(240, 247, 255, 0.94);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(17, 26, 39, 0.9), rgba(5, 9, 16, 0.94));
|
||||
color: var(--mat-sys-on-surface);
|
||||
background: var(--embedded-mpv-glass);
|
||||
border: 1px solid var(--embedded-mpv-border);
|
||||
border-radius: 18px;
|
||||
box-shadow:
|
||||
0 18px 50px rgba(0, 0, 0, 0.42),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
@@ -163,8 +203,8 @@
|
||||
}
|
||||
|
||||
.embedded-mpv-player__back-button {
|
||||
color: rgba(241, 245, 249, 0.88);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
color: var(--mat-sys-on-surface);
|
||||
background: color-mix(in srgb, var(--mat-sys-on-surface) 6%, transparent);
|
||||
}
|
||||
|
||||
.embedded-mpv-player__volume-panel {
|
||||
@@ -183,8 +223,9 @@
|
||||
}
|
||||
|
||||
.embedded-mpv-player__volume-value {
|
||||
color: rgba(226, 232, 240, 0.82);
|
||||
color: color-mix(in srgb, var(--mat-sys-on-surface) 80%, transparent);
|
||||
font-size: 0.76rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -198,7 +239,7 @@
|
||||
|
||||
.embedded-mpv-player__menu-title {
|
||||
padding: 0 2px;
|
||||
color: rgba(226, 232, 240, 0.7);
|
||||
color: color-mix(in srgb, var(--mat-sys-on-surface) 70%, transparent);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
@@ -224,7 +265,7 @@
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 8px 10px;
|
||||
color: rgba(241, 245, 249, 0.88);
|
||||
color: var(--mat-sys-on-surface);
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 12px;
|
||||
@@ -241,8 +282,11 @@
|
||||
|
||||
.embedded-mpv-player__audio-track:hover,
|
||||
.embedded-mpv-player__audio-track--selected {
|
||||
color: #f8fafc;
|
||||
background: rgba(125, 211, 252, 0.16);
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--embedded-mpv-accent) 16%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.embedded-mpv-player__audio-track mat-icon {
|
||||
@@ -253,22 +297,26 @@
|
||||
}
|
||||
|
||||
.embedded-mpv-player :is(button[mat-icon-button]) {
|
||||
color: rgba(241, 245, 249, 0.9);
|
||||
color: var(--mat-sys-on-surface);
|
||||
}
|
||||
|
||||
.embedded-mpv-player :is(button[mat-icon-button]:hover) {
|
||||
color: #ffffff;
|
||||
background: rgba(125, 211, 252, 0.14);
|
||||
color: var(--mat-sys-on-surface);
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--embedded-mpv-accent) 14%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.embedded-mpv-player {
|
||||
--embedded-mpv-controls-height: 148px;
|
||||
--embedded-mpv-controls-height: 88px;
|
||||
}
|
||||
|
||||
.embedded-mpv-player__controls {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.embedded-mpv-player__actions {
|
||||
|
||||
@@ -23,6 +23,7 @@ const HIDDEN_BOUNDS = Object.freeze({
|
||||
});
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import {
|
||||
EmbeddedMpvAudioTrack,
|
||||
@@ -36,7 +37,12 @@ import {
|
||||
selector: 'app-embedded-mpv-player',
|
||||
templateUrl: './embedded-mpv-player.component.html',
|
||||
styleUrl: './embedded-mpv-player.component.scss',
|
||||
imports: [MatButtonModule, MatIconModule, MatTooltipModule],
|
||||
imports: [
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatTooltipModule,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: {
|
||||
class: 'embedded-mpv-player-host',
|
||||
@@ -104,7 +110,7 @@ export class EmbeddedMpvPlayerComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
if (!session || session.status === 'loading') {
|
||||
return 'Loading stream in MPV...';
|
||||
return 'Loading stream…';
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
Reference in new issue
Block a user