diff --git a/client/package.json b/client/package.json index 9ab4ac49..b90e72b1 100755 --- a/client/package.json +++ b/client/package.json @@ -25,7 +25,7 @@ "typescript": "^4.6.3", "web-vitals": "^0.2.4" }, - "homepage": "https://killergerbah.github.io/asbplayer", + "homepage": "https://killergerbah.github.io/asbplayer-staging", "scripts": { "predeploy": "npm run build", "deploy": "gh-pages -d build", diff --git a/common/app/components/VideoPlayer.tsx b/common/app/components/VideoPlayer.tsx index c2f90438..1207c9a9 100755 --- a/common/app/components/VideoPlayer.tsx +++ b/common/app/components/VideoPlayer.tsx @@ -23,7 +23,7 @@ import { changeForTextSubtitleSetting, textSubtitleSettingsForTrack, } from '@project/common/settings'; -import { surroundingSubtitles, mockSurroundingSubtitles } from '@project/common/util'; +import { surroundingSubtitles, mockSurroundingSubtitles, seekWithNudge } from '@project/common/util'; import { SubtitleCollection } from '@project/common/subtitle-collection'; import SubtitleTextImage from '@project/common/components/SubtitleTextImage'; import Clock from '../services/clock'; @@ -500,8 +500,10 @@ export default function VideoPlayer({ }); playerChannel.onCurrentTime((currentTime) => { + let actualCurrentTime = currentTime; + if (videoRef.current) { - videoRef.current.currentTime = currentTime; + actualCurrentTime = seekWithNudge(videoRef.current, currentTime); } if (videoRef.current?.readyState === 4) { @@ -509,7 +511,7 @@ export default function VideoPlayer({ } clock.stop(); - clock.setTime(currentTime * 1000); + clock.setTime(actualCurrentTime * 1000); autoPauseContextRef.current?.clear(); }); diff --git a/common/util/util.ts b/common/util/util.ts index c82b52e7..db5b8a7c 100755 --- a/common/util/util.ts +++ b/common/util/util.ts @@ -367,3 +367,15 @@ export function hexToRgb(hex: string): Rgb { export function sourceString(subtitleFileName: string, timestamp: number) { return timestamp === 0 ? subtitleFileName : `${subtitleFileName} (${humanReadableTime(timestamp)})`; } + +export function seekWithNudge(media: HTMLMediaElement, timestampSeconds: number) { + media.currentTime = timestampSeconds; + + if (media.currentTime < timestampSeconds) { + // Seeking is imprecise and may not land on the desired timestamp + // Favor seeking slightly ahead to avoid getting stuck when seeking between subtitles + media.currentTime = Math.min(media.duration, media.currentTime + 0.01); + } + + return media.currentTime; +} diff --git a/extension/src/services/binding.ts b/extension/src/services/binding.ts index 29bf0139..abb57bf1 100755 --- a/extension/src/services/binding.ts +++ b/extension/src/services/binding.ts @@ -46,7 +46,7 @@ import { adjacentSubtitle } from '@project/common/key-binder'; import { extractAnkiSettings, SettingsProvider, SubtitleListPreference } from '@project/common/settings'; import { SubtitleSlice } from '@project/common/subtitle-collection'; import { SubtitleReader } from '@project/common/subtitle-reader'; -import { extractText, sourceString, surroundingSubtitlesAroundInterval } from '@project/common/util'; +import { extractText, seekWithNudge, sourceString, surroundingSubtitlesAroundInterval } from '@project/common/util'; import AnkiUiController from '../controllers/anki-ui-controller'; import ControlsController from '../controllers/controls-controller'; import DragController from '../controllers/drag-controller'; @@ -1094,7 +1094,7 @@ export default class Binding { }) ); } else { - this.video.currentTime = timestamp; + seekWithNudge(this.video, timestamp); } }