Add seek controls to the player

- '−10s/+10s' buttons under the player for both modes: local video
  seeks directly, the YouTube embed now uses the official IFrame Player
  API (nocookie host, playsinline) with a plain-iframe fallback if the
  API fails to load.
- Double-tap left/right halves of the local player to seek ±10s with a
  transient indicator (announced via role=status).
This commit is contained in:
vrubelroman 2026-09-25 11:06:33 +00:00
parent fb6e639309
commit a89f972afa
3 changed files with 357 additions and 14 deletions

View file

@ -183,7 +183,22 @@ button, .button-primary, .button-secondary, .button-quiet, .button-danger { tran
.video-page { max-width: 1320px; }
.player-wrapper { position: relative; width: 100%; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 11px; background: #000; }
.player-wrapper iframe, .player-wrapper video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; background: #000; }
.video-page-source { margin: 11px 0 22px; color: var(--muted); font-size: 12px; }
.player-wrapper video { touch-action: manipulation; }
.yt-player-host { position: absolute; inset: 0; }
.video-page-meta-row { display: flex; align-items: center; justify-content: space-between; gap: 8px 16px; flex-wrap: wrap; margin: 11px 0 22px; }
.video-page-meta-row .video-page-source { margin: 0; }
.video-page-source { color: var(--muted); font-size: 12px; }
.video-page-actions { display: flex; align-items: center; gap: 8px; }
.video-page-actions .button-secondary { min-height: 36px; padding: 0 13px; }
.tap-seek-indicator { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2; padding: 10px 15px; border-radius: 10px; background: rgba(0, 0, 0, .66); color: #fff; font-size: 13px; font-weight: 650; pointer-events: none; animation: tap-seek-fade .75s ease-out forwards; }
.tap-seek-indicator.left { left: 26%; }
.tap-seek-indicator.right { right: 26%; }
@keyframes tap-seek-fade {
0% { opacity: 0; transform: translateY(-50%) scale(.92); }
10% { opacity: 1; transform: translateY(-50%) scale(1); }
70% { opacity: 1; }
100% { opacity: 0; transform: translateY(-50%) scale(1); }
}
.video-detail-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 25px; }
.video-page-title { font-size: clamp(21px, 2vw, 29px); line-height: 1.32; margin-bottom: 9px; }
.video-page-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; color: var(--muted); font-size: 13px; }
@ -277,7 +292,7 @@ button, .button-primary, .button-secondary, .button-quiet, .button-danger { tran
.video-thumbnail { border-radius: 9px; }
.video-info { padding: 10px 2px 0; }
.video-actions { padding-top: 11px; }
.video-actions .button-secondary, .download-badge { min-height: 44px; }
.video-actions .button-secondary, .download-badge, .video-page-actions .button-secondary { min-height: 44px; }
.channels-toolbar { align-items: stretch; }
.manage-link { width: 43px; flex: none; overflow: hidden; font-size: 0; padding: 0; }
.manage-link svg { flex: none; }
@ -290,7 +305,7 @@ button, .button-primary, .button-secondary, .button-quiet, .button-danger { tran
.local-filter-nav a { min-height: 44px; }
.video-detail-head { flex-direction: column; gap: 9px; }
.player-wrapper { border-radius: 0; margin-left: -16px; width: calc(100% + 32px); }
.video-page-source { margin-top: 9px; }
.video-page-meta-row { gap: 6px 12px; }
.settings-grid { grid-template-columns: 1fr; }
.settings-panel { padding: 18px; }
.category-form { flex-wrap: wrap; }
@ -304,7 +319,7 @@ button, .button-primary, .button-secondary, .button-quiet, .button-danger { tran
}
@media (min-width: 621px) and (max-width: 699px) { .video-grid { grid-template-columns: 1fr; } }
@media (pointer: coarse) {
.video-actions .button-secondary, .download-badge, .category-nav button, .local-filter-nav a, .mobile-category-nav a, .chip, .chip-edit, .category-checkbox, .unsubscribe-button, .sidebar-badge { min-height: 44px; }
.video-actions .button-secondary, .download-badge, .video-page-actions .button-secondary, .category-nav button, .local-filter-nav a, .mobile-category-nav a, .chip, .chip-edit, .category-checkbox, .unsubscribe-button, .sidebar-badge { min-height: 44px; }
.sidebar-badge { display: inline-flex; align-items: center; }
.view-toggle a { min-height: 44px; }
.reorder-buttons .icon-button { width: 40px; height: 40px; }
@ -312,3 +327,4 @@ button, .button-primary, .button-secondary, .button-quiet, .button-danger { tran
.popover-create button { width: 44px; }
}
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; scroll-behavior: auto !important; } }
@media (prefers-reduced-motion: reduce) { .tap-seek-indicator { animation: none; opacity: 1; } }