Skip to content

Commit

Permalink
wip: refactor: use computed in stores
Browse files Browse the repository at this point in the history
  • Loading branch information
ferferga authored Dec 27, 2024
1 parent b2a7002 commit a58c6a8
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 126 deletions.
3 changes: 1 addition & 2 deletions frontend/src/components/Playback/PlayerElement.vue
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,7 @@ watch(mediaElementRef, async () => {
}
});
watch(
() => playbackManager.currentSourceUrl,
watch(playbackManager.currentSourceUrl,
(newUrl) => {
if (hls) {
hls.stopLoad();
Expand Down
153 changes: 29 additions & 124 deletions frontend/src/store/playback-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import { getItemsApi } from '@jellyfin/sdk/lib/utils/api/items-api';
import { getMediaInfoApi } from '@jellyfin/sdk/lib/utils/api/media-info-api';
import { getPlaystateApi } from '@jellyfin/sdk/lib/utils/api/playstate-api';
import { getTvShowsApi } from '@jellyfin/sdk/lib/utils/api/tv-shows-api';
import { useEventListener, watchThrottled } from '@vueuse/core';
import { computedAsync, useEventListener, watchThrottled } from '@vueuse/core';
import { v4 } from 'uuid';
import { watch, watchEffect } from 'vue';
import { computed, watch, watchEffect } from 'vue';
import { isNil, sealed } from '@/utils/validation';
import { useBaseItem } from '@/composables/apis';
import { useSnackbar } from '@/composables/use-snackbar';
Expand Down Expand Up @@ -78,7 +78,6 @@ export interface PlaybackExternalTrack extends PlaybackTrack {

interface PlaybackManagerState {
status: PlaybackStatus;
currentSourceUrl: string | undefined;
currentItemIndex: number | undefined;
currentMediaSource: MediaSourceInfo | undefined;
currentMediaSourceIndex: number | undefined;
Expand Down Expand Up @@ -112,7 +111,6 @@ class PlaybackManagerStore extends CommonStore<PlaybackManagerState> {
* Amount of time to wait between playback reports
*/
private readonly _progressReportInterval = 3500;
private _mediaSourceRequestId: string | undefined = undefined;
/**
* == GETTERS AND SETTERS ==
*/
Expand Down Expand Up @@ -183,9 +181,29 @@ class PlaybackManagerStore extends CommonStore<PlaybackManagerState> {
return this.queue[this._state.currentItemIndex ?? 0];
}

public get currentSourceUrl(): string | undefined {
return this._state.currentSourceUrl;
}
private readonly _currentPlaybackInfo = computedAsync(async (
) => {
if (this.currentItem?.Id && this.currentItem.MediaSources) {
return (
await remote.sdk.newUserApi(getMediaInfoApi).getPostedPlaybackInfo({
itemId: this.currentItem.Id,
userId: remote.auth.currentUserId,
autoOpenLiveStream: true,
playbackInfoDto: { DeviceProfile: playbackProfile },
mediaSourceId:
this.currentItem.MediaSources[this._state.currentMediaSourceIndex ?? 0]?.Id ?? this.currentItem.Id,
audioStreamIndex: this.currentAudioStreamIndex,
subtitleStreamIndex: this.currentSubtitleStreamIndex
})
).data;
}
});

public readonly currentSourceUrl = computed(() =>
this._currentPlaybackInfo.value?.MediaSources?.[0]
? this.getItemPlaybackUrl(this._currentPlaybackInfo.value.MediaSources[0])
: undefined
);

private get _previousItemIndex(): number | undefined {
if (this._state.repeatMode === RepeatMode.RepeatAll && this._state.currentItemIndex === 0) {
Expand Down Expand Up @@ -270,8 +288,9 @@ class PlaybackManagerStore extends CommonStore<PlaybackManagerState> {

public get currentItemParsedSubtitleTracks(): PlaybackTrack[] | undefined {
if (!isNil(this._state.currentMediaSource)) {
/*
* TODO: There is currently a bug in Jellyfin server when adding external subtitles may play the incorrect subtitle
/**
* TODO: There is currently a bug in Jellyfin server when adding external subtitles
* may play the incorrect subtitle
* https://github.com/jellyfin/jellyfin/issues/13198
*/
return this._state.currentMediaSource.MediaStreams?.filter(
Expand Down Expand Up @@ -783,7 +802,7 @@ class PlaybackManagerStore extends CommonStore<PlaybackManagerState> {
this._state.originalQueue = [];
this._state.isShuffling = false;
} else {
const queue = await runGenericWorkerFunc('shuffle')(this._state.queue) ?? this._state.originalQueue;
const queue = await runGenericWorkerFunc('shuffle')(this._state.queue) as string[];

this._state.originalQueue = this._state.queue;

Expand Down Expand Up @@ -846,28 +865,6 @@ class PlaybackManagerStore extends CommonStore<PlaybackManagerState> {
}
};

public readonly getItemPlaybackInfo = async (
item = this.currentItem,
mediaSourceIndex = this._state.currentMediaSourceIndex ?? 0,
audioStreamIndex = this.currentAudioStreamIndex,
subtitleStreamIndex = this.currentSubtitleStreamIndex
): Promise<PlaybackInfoResponse | undefined> => {
if (item?.Id && item.MediaSources) {
return (
await remote.sdk.newUserApi(getMediaInfoApi).getPostedPlaybackInfo({
itemId: item.Id,
userId: remote.auth.currentUserId,
autoOpenLiveStream: true,
playbackInfoDto: { DeviceProfile: playbackProfile },
mediaSourceId:
item.MediaSources[mediaSourceIndex]?.Id ?? item.Id,
audioStreamIndex,
subtitleStreamIndex
})
).data;
}
};

/**
* Builds an array of item ids based on a collection item (i.e album, tv show, etc...)
*
Expand Down Expand Up @@ -969,52 +966,9 @@ class PlaybackManagerStore extends CommonStore<PlaybackManagerState> {
}
};

private readonly _setCurrentMediaSource = async (): Promise<void> => {
/**
* Generate an identifier that can be compared with the class' one.
* If they don't match, we assume the playing item has been changed while this function
* has been running. Hence, it's results are stale and another run will take effect instead.
*/
const requestId = v4();

/**
* Saves the current playback time because it gets reset when changing audio source / burnt in subs.
*/
const currentTime = this.currentTime;

this._mediaSourceRequestId = requestId;
this._state.status = PlaybackStatus.Buffering;
/**
* Set values to undefined so the next item doesn't play the previous one while the requests are in progress
*/
this._state.playSessionId = undefined;
this._state.currentMediaSource = undefined;
this._state.currentSourceUrl = undefined;

const playbackInfo = await this.getItemPlaybackInfo();

if (playbackInfo && requestId === this._mediaSourceRequestId) {
const mediaSource = playbackInfo.MediaSources?.[0];
const playbackUrl = this.getItemPlaybackUrl(mediaSource);

if (mediaSource && playbackInfo.PlaySessionId && playbackUrl) {
this._state.playSessionId = playbackInfo.PlaySessionId;
this._state.currentMediaSource = mediaSource;
this._state.currentSourceUrl = playbackUrl;
this.currentTime = currentTime;
} else {
this._state.status = PlaybackStatus.Error;
useSnackbar(i18n.t('cantPlayItem'), 'error');
}

this._mediaSourceRequestId = undefined;
}
};

public constructor() {
super('playbackManager', () => ({
status: PlaybackStatus.Stopped,
currentSourceUrl: undefined,
currentItemIndex: undefined,
currentMediaSource: undefined,
currentMediaSourceIndex: undefined,
Expand Down Expand Up @@ -1174,21 +1128,6 @@ class PlaybackManagerStore extends CommonStore<PlaybackManagerState> {
/**
* == Server interaction ==
*/
/**
* Update media source, taking into account that currentItemIndex updates
* that occur when shuffling must be skipped
*/
watch(
[
(): typeof this.currentItemIndex => this.currentItemIndex,
(): typeof this.isShuffling => this.isShuffling
],
async (newValue, oldValue) => {
if (newValue[1] === oldValue[1] || isNil(this.currentMediaSource)) {
await this._setCurrentMediaSource();
}
}
);
/**
* Report stop for the old item and start for the new one
*/
Expand All @@ -1208,40 +1147,6 @@ class PlaybackManagerStore extends CommonStore<PlaybackManagerState> {
}
);

watch(
() => ({
currentSubtitleStreamIndex: this.currentSubtitleStreamIndex,
currentSubtitleTrack: this.currentSubtitleTrack
}),
async (oldVal, newVal) => {
if (
oldVal.currentSubtitleStreamIndex
!== newVal.currentSubtitleStreamIndex
&& (oldVal.currentSubtitleTrack?.DeliveryMethod
=== SubtitleDeliveryMethod.Encode
|| newVal.currentSubtitleTrack?.DeliveryMethod
=== SubtitleDeliveryMethod.Encode)
) {
/**
* We need to set a new media source when:
* - Subs change and
* - Going from or to a situation where subs are burnt in.
*/
await this._setCurrentMediaSource();
}
}
);

watch(() => this.currentAudioStreamIndex, async (oldVal, newVal) => {
if (!isNil(newVal) && oldVal !== newVal) {
/**
* We need to set a new media source when:
* - The audio stream index changes
*/
await this._setCurrentMediaSource();
}
});

watchThrottled(
() => this.currentTime,
this._reportPlaybackProgress,
Expand Down

0 comments on commit a58c6a8

Please sign in to comment.