1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge pull request #6196 from viown/prompt-to-skip

Add 'ask to skip' to media segments
This commit is contained in:
Bill Thornton 2024-10-25 17:18:30 -04:00 committed by GitHub
commit fa1934a124
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 277 additions and 25 deletions

View file

@ -3,5 +3,6 @@
*/
export enum MediaSegmentAction {
None = 'None',
AskToSkip = 'AskToSkip',
Skip = 'Skip'
}

View file

@ -14,6 +14,7 @@ export enum PlayerEvent {
PlaylistItemAdd = 'playlistitemadd',
PlaylistItemMove = 'playlistitemmove',
PlaylistItemRemove = 'playlistitemremove',
PromptSkip = 'promptskip',
RepeatModeChange = 'repeatmodechange',
ShuffleModeChange = 'shufflequeuemodechange',
Stopped = 'stopped',

View file

@ -37,6 +37,38 @@ class MediaSegmentManager extends PlaybackSubscriber {
}
}
skipSegment(mediaSegment: MediaSegmentDto) {
// Ignore segment if playback progress has passed the segment's start time
if (mediaSegment.StartTicks !== undefined && this.lastTime > mediaSegment.StartTicks) {
console.info('[MediaSegmentManager] ignoring skipping segment that has been seeked back into', mediaSegment);
this.isLastSegmentIgnored = true;
} else if (mediaSegment.EndTicks) {
// If there is an end time, seek to it
// Do not skip if duration < 1s to avoid slow stream changes
if (mediaSegment.StartTicks && mediaSegment.EndTicks - mediaSegment.StartTicks < TICKS_PER_SECOND) {
console.info('[MediaSegmentManager] ignoring skipping segment with duration <1s', mediaSegment);
this.isLastSegmentIgnored = true;
return;
}
console.debug('[MediaSegmentManager] skipping to %s ms', mediaSegment.EndTicks / TICKS_PER_MILLISECOND);
this.playbackManager.seek(mediaSegment.EndTicks, this.player);
} else {
// If there is no end time, skip to the next track
console.debug('[MediaSegmentManager] skipping to next item in queue');
this.playbackManager.nextTrack(this.player);
}
}
promptToSkip(mediaSegment: MediaSegmentDto) {
if (mediaSegment.StartTicks && mediaSegment.EndTicks
&& mediaSegment.EndTicks - mediaSegment.StartTicks < TICKS_PER_SECOND * 3) {
console.info('[MediaSegmentManager] ignoring segment prompt with duration <3s', mediaSegment);
this.isLastSegmentIgnored = true;
return;
}
this.playbackManager.promptToSkip(mediaSegment);
}
private performAction(mediaSegment: MediaSegmentDto) {
if (!this.mediaSegmentTypeActions || !mediaSegment.Type || !this.mediaSegmentTypeActions[mediaSegment.Type]) {
console.error('[MediaSegmentManager] segment type missing from action map', mediaSegment, this.mediaSegmentTypeActions);
@ -45,27 +77,9 @@ class MediaSegmentManager extends PlaybackSubscriber {
const action = this.mediaSegmentTypeActions[mediaSegment.Type];
if (action === MediaSegmentAction.Skip) {
// Ignore segment if playback progress has passed the segment's start time
if (mediaSegment.StartTicks !== undefined && this.lastTime > mediaSegment.StartTicks) {
console.info('[MediaSegmentManager] ignoring skipping segment that has been seeked back into', mediaSegment);
this.isLastSegmentIgnored = true;
return;
} else if (mediaSegment.EndTicks) {
// If there is an end time, seek to it
// Do not skip if duration < 1s to avoid slow stream changes
if (mediaSegment.StartTicks && mediaSegment.EndTicks - mediaSegment.StartTicks < TICKS_PER_SECOND) {
console.info('[MediaSegmentManager] ignoring skipping segment with duration <1s', mediaSegment);
this.isLastSegmentIgnored = true;
return;
}
console.debug('[MediaSegmentManager] skipping to %s ms', mediaSegment.EndTicks / TICKS_PER_MILLISECOND);
this.playbackManager.seek(mediaSegment.EndTicks, this.player);
} else {
// If there is no end time, skip to the next track
console.debug('[MediaSegmentManager] skipping to next item in queue');
this.playbackManager.nextTrack(this.player);
}
this.skipSegment(mediaSegment);
} else if (action === MediaSegmentAction.AskToSkip) {
this.promptToSkip(mediaSegment);
}
}

View file

@ -13,7 +13,7 @@ const isBeforeSegment = (segment: MediaSegmentDto, time: number, direction: numb
);
};
const isInSegment = (segment: MediaSegmentDto, time: number) => (
export const isInSegment = (segment: MediaSegmentDto, time: number) => (
typeof segment.StartTicks !== 'undefined'
&& segment.StartTicks <= time
&& (typeof segment.EndTicks === 'undefined' || segment.EndTicks > time)

View file

@ -11,6 +11,7 @@ import Events, { type Event } from 'utils/events';
import { PlaybackManagerEvent } from '../constants/playbackManagerEvent';
import { PlayerEvent } from '../constants/playerEvent';
import type { ManagedPlayerStopInfo, MovedItem, PlayerError, PlayerErrorCode, PlayerStopInfo, RemovedItems } from '../types/callbacks';
import type { MediaSegmentDto } from '@jellyfin/sdk/lib/generated-client/models/media-segment-dto';
export interface PlaybackSubscriber {
onPlaybackCancelled?(e: Event): void
@ -18,6 +19,7 @@ export interface PlaybackSubscriber {
onPlaybackStart?(e: Event, player: Plugin, state: PlayerState): void
onPlaybackStop?(e: Event, info: PlaybackStopInfo): void
onPlayerChange?(e: Event, player: Plugin, target: PlayTarget, previousPlayer: Plugin): void
onPromptSkip?(e: Event, mediaSegment: MediaSegmentDto): void
onPlayerError?(e: Event, error: PlayerError): void
onPlayerFullscreenChange?(e: Event): void
onPlayerItemStarted?(e: Event, item?: BaseItemDto, mediaSource?: MediaSourceInfo): void
@ -62,6 +64,7 @@ export abstract class PlaybackSubscriber {
[PlayerEvent.PlaylistItemAdd]: this.onPlayerPlaylistItemAdd?.bind(this),
[PlayerEvent.PlaylistItemMove]: this.onPlayerPlaylistItemMove?.bind(this),
[PlayerEvent.PlaylistItemRemove]: this.onPlayerPlaylistItemRemove?.bind(this),
[PlayerEvent.PromptSkip]: this.onPromptSkip?.bind(this),
[PlayerEvent.RepeatModeChange]: this.onPlayerRepeatModeChange?.bind(this),
[PlayerEvent.ShuffleModeChange]: this.onPlayerShuffleModeChange?.bind(this),
[PlayerEvent.Stopped]: this.onPlayerStopped?.bind(this),