@streamscloud/embeddable 14.0.1 → 14.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/external-api/media-page/index.js +1 -1
  2. package/dist/media-center/media-center/media-center-settings.svelte.js +2 -2
  3. package/dist/posts/post-viewer/cmp.post-viewer.svelte.d.ts +1 -1
  4. package/dist/posts/posts-player/posts-player-view.svelte +7 -7
  5. package/dist/streams/streams-player/streams-player-view.svelte +15 -13
  6. package/dist/ui/player/index.d.ts +1 -0
  7. package/dist/ui/player/index.js +1 -0
  8. package/dist/ui/player/{content-player/cmp.content-player.svelte → player/cmp.player.svelte} +16 -16
  9. package/dist/ui/player/{content-player/cmp.content-player.svelte.d.ts → player/cmp.player.svelte.d.ts} +2 -2
  10. package/dist/ui/player/{content-player → player}/controls-and-attachments.svelte +1 -1
  11. package/dist/ui/player/{content-player → player}/controls-and-attachments.svelte.d.ts +2 -2
  12. package/dist/ui/player/player/index.d.ts +3 -0
  13. package/dist/ui/player/player/index.js +3 -0
  14. package/dist/ui/player/{content-player → player}/overview-panel.svelte.d.ts +4 -4
  15. package/dist/ui/player/player/player-config.svelte.d.ts +21 -0
  16. package/dist/ui/player/player/player-config.svelte.js +19 -0
  17. package/dist/ui/player/{content-player/content-player-settings.svelte.d.ts → player/player-settings.svelte.d.ts} +1 -1
  18. package/dist/ui/player/{content-player/content-player-settings.svelte.js → player/player-settings.svelte.js} +1 -1
  19. package/dist/ui/player/{content-player → player}/ui-manager.svelte.d.ts +1 -1
  20. package/dist/ui/player/{content-player → player}/ui-manager.svelte.js +1 -1
  21. package/dist/ui/player/providers/chunks-player-buffer/player-chunk.svelte.d.ts +3 -5
  22. package/dist/ui/player/providers/chunks-player-buffer/player-chunk.svelte.js +39 -39
  23. package/dist/ui/player/providers/chunks-player-buffer/player-chunks-manager.svelte.d.ts +4 -4
  24. package/dist/ui/player/providers/chunks-player-buffer/player-chunks-manager.svelte.js +80 -63
  25. package/dist/ui/player/providers/default-chunks-player-buffer.svelte.js +5 -1
  26. package/package.json +1 -1
  27. package/dist/ui/player/content-player/content-player-config.svelte.d.ts +0 -29
  28. package/dist/ui/player/content-player/content-player-config.svelte.js +0 -27
  29. package/dist/ui/player/content-player/index.d.ts +0 -3
  30. package/dist/ui/player/content-player/index.js +0 -3
  31. /package/dist/ui/player/{content-player → player}/overview-panel.svelte +0 -0
@@ -74,7 +74,7 @@ export function openMediaPageModal(init) {
74
74
  viewerSettings: {
75
75
  hideCloseButton: viewerSettings?.hideCloseButton,
76
76
  locale: viewerSettings?.locale,
77
- showStreamsCloudWatermark: true,
77
+ showStreamsCloudWatermark: viewerSettings?.showStreamsCloudWatermark,
78
78
  theme: viewerSettings?.theme
79
79
  },
80
80
  on
@@ -1,6 +1,6 @@
1
1
  export class MediaCenterSettings {
2
2
  locale = $state('en');
3
- showStreamsCloudWatermark = $state(true);
3
+ showStreamsCloudWatermark = $state(false);
4
4
  disableBackground = $state(false);
5
5
  theme = $state('dark');
6
6
  state = $state.raw(null);
@@ -9,7 +9,7 @@ export class MediaCenterSettings {
9
9
  }
10
10
  update = (data) => {
11
11
  this.locale = data?.locale ?? 'en';
12
- this.showStreamsCloudWatermark = data?.showStreamsCloudWatermark ?? true;
12
+ this.showStreamsCloudWatermark = data?.showStreamsCloudWatermark ?? false;
13
13
  this.disableBackground = data?.disableBackground ?? false;
14
14
  this.theme = data?.theme ?? 'dark';
15
15
  this.state = data?.state ?? null;
@@ -4,7 +4,7 @@ import { PostModel } from '../model';
4
4
  import { type PlayerButtonDef } from '../../ui/player/button';
5
5
  type Props = {
6
6
  model: PostModel;
7
- trackingParams: TrackingParams | null | false;
7
+ trackingParams: TrackingParams | null;
8
8
  enableAttachments?: boolean;
9
9
  enableControls?: boolean;
10
10
  controlsColors: {
@@ -14,7 +14,7 @@ import { PostActionsGenerator } from '../controls';
14
14
  import { getPostCoverImage, PostModel } from '../model';
15
15
  import { PostViewer } from '../post-viewer';
16
16
  import { IconColor } from '../../ui/icon';
17
- import { ContentPlayer, ContentPlayerConfig, ContentPlayerSettings } from '../../ui/player/content-player';
17
+ import { Player, PlayerConfig, PlayerSettings } from '../../ui/player';
18
18
  import {} from '../../ui/player/providers';
19
19
  import { initBufferFromProvider } from '../../ui/player/providers/service';
20
20
  import IconDelete from '@fluentui/svg-icons/icons/delete_32_regular.svg?raw';
@@ -55,9 +55,9 @@ const getLoadedItemById = (id) => {
55
55
  }
56
56
  return buffer.loaded.find((item) => item.id === id) || null;
57
57
  };
58
- const contentPlayerConfig = new ContentPlayerConfig({
58
+ const contentPlayerConfig = new PlayerConfig({
59
59
  playerBuffer: null,
60
- settings: new ContentPlayerSettings(playerSettings),
60
+ settings: new PlayerSettings(playerSettings),
61
61
  closeOrchestrator,
62
62
  playerSliderCallbacks: {
63
63
  itemActivated: (id) => {
@@ -161,7 +161,7 @@ const currentItemActions = $derived.by(() => {
161
161
  salePrice: contentPlayerConfig.playerColors.salePrice,
162
162
  buttonBackground: contentPlayerConfig.playerColors.cardButton
163
163
  }}
164
- trackingParams={contentPlayerConfig.trackingParams}
164
+ trackingParams={false}
165
165
  locale={contentPlayerConfig.settings.locale}
166
166
  on={{
167
167
  productClick: (id) => onProductClick(id, postModel.id),
@@ -170,7 +170,7 @@ const currentItemActions = $derived.by(() => {
170
170
  adImpression: (id) => onAdImpression(id)
171
171
  }} />
172
172
  {/snippet}
173
- <ContentPlayer
173
+ <Player
174
174
  config={contentPlayerConfig}
175
175
  itemActions={currentItemActions}
176
176
  attachmentsView={buffer?.current && itemAsPostModel(buffer.current).attachments ? attachmentsView : undefined}>
@@ -181,7 +181,7 @@ const currentItemActions = $derived.by(() => {
181
181
  model={postModel}
182
182
  controlsColors={{ active: contentPlayerConfig.playerColors.button, inactive: contentPlayerConfig.playerColors.buttonInactive }}
183
183
  controlActions={handler.actions}
184
- trackingParams={contentPlayerConfig.trackingParams}
184
+ trackingParams={false}
185
185
  enableAttachments={contentPlayerConfig.uiManager.showAttachmentsOverlay}
186
186
  enableControls={contentPlayerConfig.uiManager.showControlsOverlay}
187
187
  autoplay="on-appearance"
@@ -193,4 +193,4 @@ const currentItemActions = $derived.by(() => {
193
193
  adImpression: (adId) => onAdImpression(adId)
194
194
  }} />
195
195
  {/snippet}
196
- </ContentPlayer>
196
+ </Player>
@@ -16,7 +16,7 @@ import { StreamActionsGenerator } from '../controls';
16
16
  import { mapToPostModel } from '../layout/models';
17
17
  import { StreamPageViewer } from '../stream-page-viewer';
18
18
  import { IconColor } from '../../ui/icon';
19
- import { ContentPlayer, ContentPlayerConfig, ContentPlayerSettings } from '../../ui/player/content-player';
19
+ import { Player, PlayerConfig, PlayerSettings } from '../../ui/player';
20
20
  import { default as Overview } from './stream-overview.svelte';
21
21
  import { StreamPlayerLocalization } from './stream-player-localization';
22
22
  import { StreamsPlayerBuffer } from './streams-player-buffer.svelte';
@@ -35,6 +35,14 @@ let isActive = true;
35
35
  let activityTimeout = null;
36
36
  let trackingInterval = null;
37
37
  let maxPageIndexViewed = 0;
38
+ const trackingParams = $derived.by(() => {
39
+ return currentStreamModel
40
+ ? {
41
+ streamId: currentStreamModel.id,
42
+ campaignId: amplificationParameters === null || amplificationParameters === void 0 ? void 0 : amplificationParameters.campaignId
43
+ }
44
+ : false;
45
+ });
38
46
  $effect(() => {
39
47
  void dataProvider;
40
48
  untrack(() => {
@@ -45,12 +53,6 @@ $effect(() => {
45
53
  });
46
54
  return () => { };
47
55
  });
48
- $effect(() => contentPlayerConfig.updateTrackingParams(currentStreamModel
49
- ? {
50
- streamId: currentStreamModel.id,
51
- campaignId: amplificationParameters === null || amplificationParameters === void 0 ? void 0 : amplificationParameters.campaignId
52
- }
53
- : null));
54
56
  $effect(() => {
55
57
  var _a;
56
58
  const stream = (_a = buffer === null || buffer === void 0 ? void 0 : buffer.activeChunk) === null || _a === void 0 ? void 0 : _a.model;
@@ -103,9 +105,9 @@ const initPlayerBuffer = (dataProvider) => __awaiter(void 0, void 0, void 0, fun
103
105
  buffer = newBuffer;
104
106
  contentPlayerConfig.playerBuffer = newBuffer;
105
107
  });
106
- const contentPlayerConfig = new ContentPlayerConfig({
108
+ const contentPlayerConfig = new PlayerConfig({
107
109
  playerBuffer: null,
108
- settings: new ContentPlayerSettings(playerSettings),
110
+ settings: new PlayerSettings(playerSettings),
109
111
  closeOrchestrator,
110
112
  playerSliderCallbacks: {
111
113
  itemActivated: (item) => onPageActivated(item),
@@ -288,7 +290,7 @@ const stopActivityTracking = () => {
288
290
  salePrice: contentPlayerConfig.playerColors.salePrice,
289
291
  buttonBackground: contentPlayerConfig.playerColors.cardButton
290
292
  }}
291
- trackingParams={contentPlayerConfig.trackingParams}
293
+ trackingParams={trackingParams}
292
294
  locale={contentPlayerConfig.settings.locale}
293
295
  on={{
294
296
  productClick: (id) => onShortVideoProductClick(id, postModel.id),
@@ -298,7 +300,7 @@ const stopActivityTracking = () => {
298
300
  }} />
299
301
  {/if}
300
302
  {/snippet}
301
- <ContentPlayer
303
+ <Player
302
304
  config={contentPlayerConfig}
303
305
  itemActions={currentItemActions}
304
306
  attachmentsView={buffer?.current && itemAsPostModel(buffer.current)?.attachments ? attachmentsView : undefined}>
@@ -336,7 +338,7 @@ const stopActivityTracking = () => {
336
338
  <PostViewer
337
339
  model={postModel}
338
340
  controlsColors={{ active: contentPlayerConfig.playerColors.button, inactive: contentPlayerConfig.playerColors.buttonInactive }}
339
- trackingParams={contentPlayerConfig.trackingParams}
341
+ trackingParams={trackingParams}
340
342
  enableAttachments={contentPlayerConfig.uiManager.showAttachmentsOverlay}
341
343
  enableControls={contentPlayerConfig.uiManager.showControlsOverlay}
342
344
  controlActions={handler?.actions ?? []}
@@ -362,4 +364,4 @@ const stopActivityTracking = () => {
362
364
  }} />
363
365
  {/if}
364
366
  {/snippet}
365
- </ContentPlayer>
367
+ </Player>
@@ -0,0 +1 @@
1
+ export { Player, PlayerConfig, PlayerSettings } from './player';
@@ -0,0 +1 @@
1
+ export { Player, PlayerConfig, PlayerSettings } from './player';
@@ -13,7 +13,7 @@ let overviewPosition = $state({
13
13
  right: 0
14
14
  });
15
15
  const uiManager = config.uiManager;
16
- const handleContentPlayerMounted = (node) => {
16
+ const handlePlayerMounted = (node) => {
17
17
  const markAsTouched = () => {
18
18
  everTouched = true;
19
19
  removeListeners();
@@ -74,9 +74,9 @@ const handleSliderMounted = (node) => {
74
74
  break;
75
75
  }
76
76
  }
77
- node.style.setProperty('--_content-player--content--width', width);
78
- node.style.setProperty('--_content-player--content--height', height);
79
- node.style.setProperty('--_content-player--content--margin', margin);
77
+ node.style.setProperty('--_player--content--width', width);
78
+ node.style.setProperty('--_player--content--height', height);
79
+ node.style.setProperty('--_player--content--margin', margin);
80
80
  uiManager.contentViewWidth = contentWidthNumber;
81
81
  const verticalIndent = uiManager.isMobileView ? 0 : 13;
82
82
  overviewPosition = {
@@ -101,19 +101,19 @@ const handleSliderMounted = (node) => {
101
101
  }
102
102
  }} />
103
103
 
104
- <div class="content-player" use:handleContentPlayerMounted>
104
+ <div class="player" use:handlePlayerMounted>
105
105
  {#if config.settings.showStreamsCloudWatermark}
106
- <div class="content-player__watermark">
106
+ <div class="player__watermark">
107
107
  <a href="https://streamscloud.com/" tabindex="-1" aria-label="none">
108
108
  <img src="https://embedabble-assets.streamscloud-cdn.com/watermark.svg" alt="StreamsCloud" />
109
109
  </a>
110
110
  </div>
111
111
  {/if}
112
112
  {#if config.playerBuffer}
113
- <div class="content-player__slider" use:handleSliderMounted>
113
+ <div class="player__slider" use:handleSliderMounted>
114
114
  <PlayerSlider buffer={config.playerBuffer} on={config.playerSliderCallbacks}>
115
115
  {#snippet children({ item })}
116
- <div class="content-player__content">
116
+ <div class="player__content">
117
117
  {@render itemView({ item })}
118
118
  {#if uiManager.isMobileView && config.playerBuffer && config.playerBuffer.loaded.length > 1 && !everTouched}
119
119
  <SwipeIndicator locale={config.settings.locale} />
@@ -145,7 +145,7 @@ const handleSliderMounted = (node) => {
145
145
  opacity: 1;
146
146
  }
147
147
  }
148
- .content-player {
148
+ .player {
149
149
  display: flex;
150
150
  flex: 1;
151
151
  min-height: 0;
@@ -154,11 +154,11 @@ const handleSliderMounted = (node) => {
154
154
  /* Set 'container-type: inline-size;' to reference container*/
155
155
  }
156
156
  @container (width < 576px) {
157
- .content-player {
157
+ .player {
158
158
  padding: 0;
159
159
  }
160
160
  }
161
- .content-player__slider {
161
+ .player__slider {
162
162
  width: 100%;
163
163
  min-width: 100%;
164
164
  max-width: 100%;
@@ -166,15 +166,15 @@ const handleSliderMounted = (node) => {
166
166
  min-height: 100%;
167
167
  max-height: 100%;
168
168
  }
169
- .content-player__watermark {
169
+ .player__watermark {
170
170
  position: absolute;
171
171
  bottom: 5rem;
172
172
  left: 4.375rem;
173
173
  }
174
- .content-player__content {
175
- width: var(--_content-player--content--width, 100%);
176
- height: var(--_content-player--content--height, 100%);
177
- margin: var(--_content-player--content--margin, 0);
174
+ .player__content {
175
+ width: var(--_player--content--width, 100%);
176
+ height: var(--_player--content--height, 100%);
177
+ margin: var(--_player--content--margin, 0);
178
178
  position: relative;
179
179
  display: flex;
180
180
  justify-content: center;
@@ -1,11 +1,11 @@
1
1
  import type { PlayerButtonDef } from '../button';
2
- import type { ContentPlayerConfig } from './content-player-config.svelte';
2
+ import type { PlayerConfig } from './player-config.svelte';
3
3
  import { type Snippet } from 'svelte';
4
4
  declare class __sveltets_Render<T extends {
5
5
  id: string;
6
6
  }> {
7
7
  props(): {
8
- config: ContentPlayerConfig<T>;
8
+ config: PlayerConfig<T>;
9
9
  itemActions: PlayerButtonDef[];
10
10
  itemView: Snippet<[{
11
11
  item: T;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">import { slideHorizontally } from '../../../core/transitions';
2
2
  import { PlayerButton, ResponsivePlayerButtonsGroup } from '../button';
3
- import { ContentPlayerConfig } from './content-player-config.svelte';
3
+ import { PlayerConfig } from './player-config.svelte.js';
4
4
  import IconCalendarWeekNumbers from '@fluentui/svg-icons/icons/calendar_week_numbers_24_regular.svg?raw';
5
5
  import IconChevronDown from '@fluentui/svg-icons/icons/chevron_down_28_regular.svg?raw';
6
6
  import IconChevronUp from '@fluentui/svg-icons/icons/chevron_up_28_regular.svg?raw';
@@ -1,11 +1,11 @@
1
1
  import { type PlayerButtonDef } from '../button';
2
- import { ContentPlayerConfig } from './content-player-config.svelte';
2
+ import { PlayerConfig } from './player-config.svelte.js';
3
3
  import type { Snippet } from 'svelte';
4
4
  declare class __sveltets_Render<T extends {
5
5
  id: string;
6
6
  }> {
7
7
  props(): {
8
- config: ContentPlayerConfig<T>;
8
+ config: PlayerConfig<T>;
9
9
  hasOverview: boolean;
10
10
  itemActions: PlayerButtonDef[];
11
11
  attachmentsView?: Snippet<[{
@@ -0,0 +1,3 @@
1
+ export { default as Player } from './cmp.player.svelte';
2
+ export { PlayerConfig } from './player-config.svelte.js';
3
+ export { PlayerSettings } from './player-settings.svelte.js';
@@ -0,0 +1,3 @@
1
+ export { default as Player } from './cmp.player.svelte';
2
+ export { PlayerConfig } from './player-config.svelte.js';
3
+ export { PlayerSettings } from './player-settings.svelte.js';
@@ -1,12 +1,12 @@
1
- import type { ContentPlayerConfig } from './content-player-config.svelte';
2
- import type { ContentPlayerUIManager } from './ui-manager.svelte';
1
+ import type { PlayerConfig } from './player-config.svelte.js';
2
+ import type { PlayerUIManager } from './ui-manager.svelte';
3
3
  import type { Snippet } from 'svelte';
4
4
  declare class __sveltets_Render<T extends {
5
5
  id: string;
6
6
  }> {
7
7
  props(): {
8
- config: ContentPlayerConfig<T>;
9
- uiManager: ContentPlayerUIManager;
8
+ config: PlayerConfig<T>;
9
+ uiManager: PlayerUIManager;
10
10
  position: {
11
11
  top: number;
12
12
  bottom: number;
@@ -0,0 +1,21 @@
1
+ import type { ICloseOrchestrator } from '../close-orchestrator';
2
+ import type { IPlayerBuffer } from '../providers';
3
+ import type { PlayerSliderCallbacks } from '../slider/types';
4
+ import { PlayerSettings } from './player-settings.svelte';
5
+ import { PlayerUIManager } from './ui-manager.svelte';
6
+ export declare class PlayerConfig<T extends {
7
+ id: string;
8
+ }> {
9
+ playerBuffer: IPlayerBuffer<T> | null;
10
+ readonly settings: PlayerSettings;
11
+ readonly playerSliderCallbacks: PlayerSliderCallbacks | undefined;
12
+ readonly closeOrchestrator: ICloseOrchestrator;
13
+ readonly uiManager: PlayerUIManager;
14
+ constructor(init: {
15
+ playerBuffer: IPlayerBuffer<T> | null;
16
+ settings?: PlayerSettings;
17
+ playerSliderCallbacks?: PlayerSliderCallbacks;
18
+ closeOrchestrator: ICloseOrchestrator;
19
+ });
20
+ get playerColors(): import("../colors").PlayerColors;
21
+ }
@@ -0,0 +1,19 @@
1
+ import { PlayerSettings } from './player-settings.svelte';
2
+ import { PlayerUIManager } from './ui-manager.svelte';
3
+ export class PlayerConfig {
4
+ playerBuffer = $state.raw(null);
5
+ settings;
6
+ playerSliderCallbacks;
7
+ closeOrchestrator;
8
+ uiManager = new PlayerUIManager();
9
+ constructor(init) {
10
+ const { playerBuffer, settings, playerSliderCallbacks, closeOrchestrator } = init;
11
+ this.playerBuffer = playerBuffer;
12
+ this.settings = settings || new PlayerSettings();
13
+ this.playerSliderCallbacks = playerSliderCallbacks;
14
+ this.closeOrchestrator = closeOrchestrator;
15
+ }
16
+ get playerColors() {
17
+ return this.settings.playerColors;
18
+ }
19
+ }
@@ -1,7 +1,7 @@
1
1
  import type { Locale } from '../../../core/locale';
2
2
  import { type ThemeValue } from '../../../core/theme';
3
3
  import { PlayerColors } from '../colors';
4
- export declare class ContentPlayerSettings {
4
+ export declare class PlayerSettings {
5
5
  locale: Locale;
6
6
  showStreamsCloudWatermark: boolean;
7
7
  allPlayerColors: Record<ThemeValue, PlayerColors>;
@@ -1,6 +1,6 @@
1
1
  import { Theme } from '../../../core/theme';
2
2
  import { PlayerColors } from '../colors';
3
- export class ContentPlayerSettings {
3
+ export class PlayerSettings {
4
4
  locale = $state('en');
5
5
  showStreamsCloudWatermark = $state(false);
6
6
  allPlayerColors = $state.raw({
@@ -1,4 +1,4 @@
1
- export declare class ContentPlayerUIManager {
1
+ export declare class PlayerUIManager {
2
2
  overviewCollapsed: boolean;
3
3
  readonly overviewCanBeShown: boolean;
4
4
  readonly overviewMaxWidth = 150;
@@ -1,7 +1,7 @@
1
1
  const ATTACHMENTS_MAX_WIDTH = 176;
2
2
  const OVERLAY_MAX_WIDTH = 150;
3
3
  const SAFE_AREA_SIZE = 70;
4
- export class ContentPlayerUIManager {
4
+ export class PlayerUIManager {
5
5
  overviewCollapsed = $state(true);
6
6
  overviewCanBeShown = $derived.by(() => this.overviewMaxWidth <= this.sidePanelsMaxWidth);
7
7
  overviewMaxWidth = OVERLAY_MAX_WIDTH;
@@ -5,11 +5,9 @@ export declare class PlayerChunk<TItem extends WithId, TChunk extends WithId> {
5
5
  readonly items: TItem[];
6
6
  readonly chunkItems: PlayerChunkItem<TItem>[];
7
7
  readonly activeChunkItem: PlayerChunkItem<TItem>;
8
- readonly isEmpty: boolean;
9
- isFullyLoaded: boolean;
10
8
  private _chunkItems;
11
9
  private _activeItemIndex;
12
- private _isLoading;
10
+ private _fetchDeferred;
13
11
  private _itemsLoader;
14
12
  constructor(data: {
15
13
  chunk: TChunk;
@@ -24,9 +22,9 @@ export declare class PlayerChunk<TItem extends WithId, TChunk extends WithId> {
24
22
  };
25
23
  });
26
24
  get isLoading(): boolean;
25
+ get canLoadMore(): boolean;
27
26
  get activeItemIndex(): number;
28
27
  loadMore: () => Promise<TItem[]>;
29
- setActiveItemIndex: (index: number, warmUp?: boolean) => Promise<void>;
28
+ setActiveItemIndex: (index: number) => Promise<void>;
30
29
  mutateChunkItems: (items: PlayerChunkItem<TItem>[]) => void;
31
- warmUp: () => Promise<void>;
32
30
  }
@@ -1,73 +1,73 @@
1
1
  import { ContinuationToken } from '../../../../core/continuation-token';
2
2
  import { CursorDataLoader } from '../../../../core/data-loaders';
3
+ import { Deferred } from '../../../../core/deferred';
3
4
  import { PlayerChunkItem } from './player-chunk-item.svelte';
4
- const CHUNK_ITEMS_BUFFER_SIZE = 5;
5
5
  export class PlayerChunk {
6
6
  model;
7
7
  items = $derived.by(() => this._chunkItems.map((i) => i.model));
8
8
  chunkItems = $derived.by(() => this._chunkItems);
9
9
  activeChunkItem = $derived.by(() => this._chunkItems[this._activeItemIndex] ?? null);
10
- isEmpty = $derived.by(() => this.isFullyLoaded && this._chunkItems.length === 0);
11
- isFullyLoaded = $state(false);
12
10
  _chunkItems = $state.raw([]);
13
11
  _activeItemIndex = $state(0);
14
- _isLoading = $state(false);
12
+ _fetchDeferred = $state.raw(null);
15
13
  _itemsLoader;
16
14
  constructor(data) {
17
15
  const { chunk, provider, callbacks } = data;
18
16
  this.model = chunk;
19
17
  this._itemsLoader = new CursorDataLoader({
20
18
  loadPage: async (continuationToken) => {
21
- if (this.isFullyLoaded) {
19
+ if (this._fetchDeferred) {
20
+ return await this._fetchDeferred.promise;
21
+ }
22
+ if (!this.canLoadMore) {
23
+ return null;
24
+ }
25
+ try {
26
+ this._fetchDeferred = new Deferred();
27
+ const itemsResult = await provider.loadChunkItems(this.model.id, continuationToken.toNextChunkString());
28
+ const newItems = itemsResult.items;
29
+ this._chunkItems = [
30
+ ...this._chunkItems,
31
+ ...newItems.map((item) => new PlayerChunkItem({
32
+ model: item,
33
+ chunkId: this.model.id
34
+ }))
35
+ ];
36
+ const continuationTokenResult = ContinuationToken.fromPayload(itemsResult.continuationToken);
37
+ if (!continuationTokenResult.canLoadMore) {
38
+ callbacks?.onChunkFullyLoaded(this);
39
+ }
40
+ const result = {
41
+ items: newItems,
42
+ continuationToken: continuationTokenResult
43
+ };
44
+ this._fetchDeferred.resolve(result);
45
+ return result;
46
+ }
47
+ catch {
48
+ this._fetchDeferred?.resolve(null);
22
49
  return null;
23
50
  }
24
- const result = await provider.loadChunkItems(this.model.id, continuationToken.toNextChunkString());
25
- const newItems = result.items;
26
- this._chunkItems = [
27
- ...this._chunkItems,
28
- ...newItems.map((item) => new PlayerChunkItem({
29
- model: item,
30
- chunkId: this.model.id
31
- }))
32
- ];
33
- const continuationTokenResult = ContinuationToken.fromPayload(result.continuationToken);
34
- if (!continuationTokenResult.canLoadMore) {
35
- this.isFullyLoaded = true;
36
- callbacks?.onChunkFullyLoaded(this);
51
+ finally {
52
+ this._fetchDeferred = null;
37
53
  }
38
- return {
39
- items: newItems,
40
- continuationToken: continuationTokenResult
41
- };
42
54
  }
43
55
  });
44
56
  }
45
57
  get isLoading() {
46
- return this._isLoading;
58
+ return !!this._fetchDeferred;
59
+ }
60
+ get canLoadMore() {
61
+ return this._itemsLoader.continuationToken.canLoadMore;
47
62
  }
48
63
  get activeItemIndex() {
49
64
  return this._activeItemIndex;
50
65
  }
51
66
  loadMore = () => this._itemsLoader.loadMore();
52
- setActiveItemIndex = async (index, warmUp = true) => {
67
+ setActiveItemIndex = async (index) => {
53
68
  this._activeItemIndex = index;
54
- if (warmUp) {
55
- await this.warmUp();
56
- }
57
69
  };
58
70
  mutateChunkItems = (items) => {
59
71
  this._chunkItems = items;
60
72
  };
61
- warmUp = async () => {
62
- if (this._chunkItems.length >= this._activeItemIndex + CHUNK_ITEMS_BUFFER_SIZE || this._isLoading) {
63
- return;
64
- }
65
- this._isLoading = true;
66
- try {
67
- await this.loadMore();
68
- }
69
- finally {
70
- this._isLoading = false;
71
- }
72
- };
73
73
  }
@@ -4,7 +4,7 @@ export declare class PlayerChunksManager<TItem extends WithId, TChunk extends Wi
4
4
  private provider;
5
5
  private _activeChunkIndex;
6
6
  private _loadedChunks;
7
- private _fetchDeferred;
7
+ private _warmUpDeferred;
8
8
  constructor(provider: IChunksPlayerDataProvider<TItem, TChunk>);
9
9
  get activeChunk(): PlayerChunk<TItem, TChunk>;
10
10
  get loadedChunks(): PlayerChunk<TItem, TChunk>[];
@@ -15,9 +15,9 @@ export declare class PlayerChunksManager<TItem extends WithId, TChunk extends Wi
15
15
  removeItemById: (id: string) => boolean;
16
16
  removeChunkById: (id: string) => boolean | undefined;
17
17
  initialize: () => Promise<void>;
18
- setActiveChunkIndex: (index: number) => Promise<void>;
18
+ setActiveChunkIndex: (index: number, chunkItemIndex: number) => Promise<void>;
19
19
  activateItemAtFlattenedIndex: (index: number) => Promise<void>;
20
20
  warmUp: () => Promise<void>;
21
- reset: () => void;
22
- private populateChunkAtIndex;
21
+ reset: () => Promise<void>;
22
+ private warmUpSequentially;
23
23
  }
@@ -1,12 +1,14 @@
1
1
  import { Deferred } from '../../../../core/deferred';
2
2
  import { PlayerChunk } from './player-chunk.svelte';
3
- const CHUNKS_BUFFER_SIZE = 5;
4
- const FIXED_START_INDEX = 0;
3
+ const ITEMS_BUFFER_SIZE = 10;
4
+ // Configuration: if true, always start from first item when switching chunks
5
+ // if false, activate the exact item at its position in the new chunk
6
+ const START_FROM_FIRST_ITEM_ON_CHUNK_SWITCH = true;
5
7
  export class PlayerChunksManager {
6
8
  provider;
7
9
  _activeChunkIndex = $state(-1);
8
10
  _loadedChunks = $state.raw([]);
9
- _fetchDeferred = $state.raw(null);
11
+ _warmUpDeferred = $state.raw(null);
10
12
  constructor(provider) {
11
13
  this.provider = provider;
12
14
  }
@@ -17,7 +19,7 @@ export class PlayerChunksManager {
17
19
  return this._loadedChunks;
18
20
  }
19
21
  get isLoading() {
20
- return this._fetchDeferred !== null || this._loadedChunks.some((c) => c.isLoading);
22
+ return this._warmUpDeferred !== null || this._loadedChunks.some((c) => c.isLoading);
21
23
  }
22
24
  get flattenedChunkItems() {
23
25
  return this._loadedChunks.reduce((acc, chunk) => {
@@ -42,8 +44,7 @@ export class PlayerChunksManager {
42
44
  return false;
43
45
  }
44
46
  const itemIndex = chunkWithItem.items.findIndex((item) => item.id === id);
45
- this.setActiveChunkIndex(chunkWithItemIndex);
46
- this.activeChunk.setActiveItemIndex(itemIndex);
47
+ this.setActiveChunkIndex(chunkWithItemIndex, itemIndex);
47
48
  return true;
48
49
  };
49
50
  removeItemById = (id) => {
@@ -106,37 +107,27 @@ export class PlayerChunksManager {
106
107
  else if (this._activeChunkIndex > chunkIndex) {
107
108
  newActiveChunkIndex--;
108
109
  }
109
- this.setActiveChunkIndex(newActiveChunkIndex);
110
+ this.setActiveChunkIndex(newActiveChunkIndex, 0);
110
111
  return true;
111
112
  };
112
113
  initialize = async () => {
113
- const handleInitialized = async () => {
114
- const startIndex = Math.min(FIXED_START_INDEX, this._loadedChunks.length - 1);
115
- const populateChunkResult = await this.populateChunkAtIndex(startIndex, (currentIndex) => currentIndex + 1);
116
- if (!populateChunkResult) {
117
- return;
118
- }
119
- this.setActiveChunkIndex(populateChunkResult.closestReadyChunkIndex);
120
- if (populateChunkResult.closestReadyChunkIndex === FIXED_START_INDEX &&
121
- this.provider.initialData.startItemIndex &&
122
- this.provider.initialData.startItemIndex > 0) {
123
- this.activeChunk.setActiveItemIndex(this.provider.initialData.startItemIndex);
124
- }
125
- };
126
114
  this._loadedChunks = this.provider.initialData.prefetchedChunks.map((chunk) => new PlayerChunk({ chunk, provider: { loadChunkItems: this.provider.loadChunkItems } }));
127
- const considerInitialized = this._loadedChunks.length !== 0;
128
- if (considerInitialized) {
129
- handleInitialized();
130
- }
131
115
  await this.warmUp();
132
- if (!considerInitialized) {
133
- handleInitialized();
116
+ if (this._loadedChunks.length === 0 || this.flattenedChunkItems.length === 0) {
117
+ this.provider.onEndReached?.();
118
+ return;
134
119
  }
120
+ const firstFilledChunkIndex = this._loadedChunks.findIndex((c) => c.items.length > 0);
121
+ const initialStartItemIndex = firstFilledChunkIndex === 0 && this.provider.initialData.startItemIndex && this.provider.initialData.startItemIndex > 0
122
+ ? this.provider.initialData.startItemIndex
123
+ : 0;
124
+ this.setActiveChunkIndex(firstFilledChunkIndex, initialStartItemIndex);
125
+ // Start background warm-up after initialization
126
+ this.warmUp();
135
127
  };
136
- setActiveChunkIndex = async (index) => {
128
+ setActiveChunkIndex = async (index, chunkItemIndex) => {
137
129
  this._activeChunkIndex = index;
138
- this._loadedChunks.forEach((c) => c.setActiveItemIndex(0, false));
139
- await this.populateChunkAtIndex(this._activeChunkIndex + 1, (currentIndex) => currentIndex + 1);
130
+ this.activeChunk.setActiveItemIndex(chunkItemIndex);
140
131
  // Don't wait for warm up to be finished, it runs in the background
141
132
  this.warmUp();
142
133
  };
@@ -148,56 +139,82 @@ export class PlayerChunksManager {
148
139
  return;
149
140
  }
150
141
  if (nextItem.chunkId !== activeChunkId) {
151
- this.setActiveChunkIndex(this.loadedChunks.findIndex((c) => c.model.id === nextItem.chunkId));
152
- this.activeChunk.warmUp();
142
+ const nextChunkIndex = this.loadedChunks.findIndex((c) => c.model.id === nextItem.chunkId);
143
+ if (nextChunkIndex === -1) {
144
+ return;
145
+ }
146
+ const itemIndexInChunk = START_FROM_FIRST_ITEM_ON_CHUNK_SWITCH ? 0 : this.loadedChunks[nextChunkIndex].chunkItems.indexOf(nextItem);
147
+ this.setActiveChunkIndex(nextChunkIndex, itemIndexInChunk);
153
148
  }
154
149
  else {
155
150
  this.activeChunk.setActiveItemIndex(this.activeChunk.chunkItems.indexOf(nextItem));
156
151
  }
157
152
  };
158
153
  warmUp = async () => {
159
- // Early return if manager is sufficient or already loading
160
- if (this._fetchDeferred) {
161
- return this._fetchDeferred.promise;
162
- }
163
- if (this._loadedChunks.length >= this._activeChunkIndex + CHUNKS_BUFFER_SIZE) {
164
- return;
154
+ if (this._warmUpDeferred) {
155
+ return this._warmUpDeferred.promise;
165
156
  }
166
- this._fetchDeferred = new Deferred();
157
+ this._warmUpDeferred = new Deferred();
167
158
  try {
168
- const result = await this.provider.loadMoreChunks();
169
- this._loadedChunks = [
170
- ...this._loadedChunks,
171
- ...result.map((chunk) => new PlayerChunk({
172
- chunk,
173
- provider: { loadChunkItems: this.provider.loadChunkItems }
174
- }))
175
- ];
159
+ await this.warmUpSequentially();
176
160
  }
177
161
  finally {
178
- this._fetchDeferred.resolve();
179
- this._fetchDeferred = null;
162
+ this._warmUpDeferred.resolve();
163
+ this._warmUpDeferred = null;
180
164
  }
181
165
  };
182
- reset = () => {
166
+ reset = async () => {
183
167
  this._activeChunkIndex = -1;
184
168
  this._loadedChunks = [];
185
- this._fetchDeferred = null;
186
- this.warmUp();
169
+ this._warmUpDeferred = null;
170
+ await this.warmUp();
187
171
  };
188
- populateChunkAtIndex = async (index, nextIndexFn) => {
189
- const chunkAtIndex = this._loadedChunks[index];
190
- if (!chunkAtIndex) {
191
- return null;
192
- }
193
- await chunkAtIndex.warmUp();
194
- if (chunkAtIndex.items.length === 0) {
195
- const nextIndex = nextIndexFn(index);
196
- if (nextIndex > index) {
197
- await this.warmUp();
172
+ warmUpSequentially = async () => {
173
+ const startChunkIndex = Math.max(0, this._activeChunkIndex);
174
+ // Calculate how many items we need ahead of current position
175
+ const getItemsAhead = () => {
176
+ const currentFlatIndex = this.flattenedActiveChunkItemIndex;
177
+ const totalItems = this.flattenedChunkItems.length;
178
+ return totalItems - currentFlatIndex - 1; // -1 because current item doesn't count
179
+ };
180
+ while (getItemsAhead() < ITEMS_BUFFER_SIZE) {
181
+ // Find first non-fully-loaded chunk starting from active
182
+ let targetChunkIndex = -1;
183
+ for (let i = startChunkIndex; i < this._loadedChunks.length; i++) {
184
+ if (this._loadedChunks[i].canLoadMore) {
185
+ targetChunkIndex = i;
186
+ break;
187
+ }
188
+ }
189
+ // If all chunks are fully loaded, load more chunks
190
+ if (targetChunkIndex === -1) {
191
+ const result = await this.provider.loadMoreChunks();
192
+ if (result.length === 0) {
193
+ // No more chunks available
194
+ break;
195
+ }
196
+ this._loadedChunks = [
197
+ ...this._loadedChunks,
198
+ ...result.map((chunk) => new PlayerChunk({
199
+ chunk,
200
+ provider: { loadChunkItems: this.provider.loadChunkItems }
201
+ }))
202
+ ];
203
+ continue; // Re-check for chunks to fill
204
+ }
205
+ // Load one page from target chunk
206
+ const chunk = this._loadedChunks[targetChunkIndex];
207
+ const itemsBefore = chunk.items.length;
208
+ await chunk.loadMore();
209
+ const itemsAfter = chunk.items.length;
210
+ // If chunk became fully loaded but added no items, continue to next chunk
211
+ if (itemsAfter === itemsBefore && !chunk.canLoadMore) {
212
+ continue;
213
+ }
214
+ // If no progress made and chunk is not fully loaded, something went wrong
215
+ if (itemsAfter === itemsBefore && chunk.canLoadMore) {
216
+ break; // Avoid infinite loop
198
217
  }
199
- return await this.populateChunkAtIndex(nextIndexFn(index), nextIndexFn);
200
218
  }
201
- return { closestReadyChunkIndex: index };
202
219
  };
203
220
  }
@@ -9,7 +9,11 @@ export class DefaultChunksPlayerBuffer {
9
9
  if (this.loaded.length && this.currentIndex < this.loaded.length - 1) {
10
10
  return true;
11
11
  }
12
- return !this._playerChunksManager.isLoading && !!this._onEndReachedFn;
12
+ // Check if manager is still loading
13
+ if (this._playerChunksManager.isLoading) {
14
+ return false; // Don't trigger onEndReached while loading
15
+ }
16
+ return !!this._onEndReachedFn;
13
17
  });
14
18
  canLoadPrevious = $derived(this.currentIndex > 0);
15
19
  navigationDisabled = $derived(!this.canLoadNext && !this.canLoadPrevious);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/embeddable",
3
- "version": "14.0.1",
3
+ "version": "14.0.3",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,29 +0,0 @@
1
- import type { ICloseOrchestrator } from '../close-orchestrator';
2
- import type { IPlayerBuffer } from '../providers';
3
- import type { PlayerSliderCallbacks } from '../slider/types';
4
- import { ContentPlayerSettings } from './content-player-settings.svelte';
5
- import { ContentPlayerUIManager } from './ui-manager.svelte';
6
- export declare class ContentPlayerConfig<T extends {
7
- id: string;
8
- }> {
9
- playerBuffer: IPlayerBuffer<T> | null;
10
- readonly settings: ContentPlayerSettings;
11
- readonly playerSliderCallbacks: PlayerSliderCallbacks | undefined;
12
- readonly closeOrchestrator: ICloseOrchestrator;
13
- readonly uiManager: ContentPlayerUIManager;
14
- private _trackingParams;
15
- constructor(init: {
16
- playerBuffer: IPlayerBuffer<T> | null;
17
- settings?: ContentPlayerSettings;
18
- playerSliderCallbacks?: PlayerSliderCallbacks;
19
- trackingParams?: ContentPlayerTrackingParams | null;
20
- closeOrchestrator: ICloseOrchestrator;
21
- });
22
- get playerColors(): import("../colors").PlayerColors;
23
- get trackingParams(): ContentPlayerConfig<T>['_trackingParams'];
24
- updateTrackingParams: (data: ContentPlayerTrackingParams | null) => void;
25
- }
26
- export type ContentPlayerTrackingParams = {
27
- streamId?: string;
28
- campaignId?: string;
29
- } | false;
@@ -1,27 +0,0 @@
1
- import { ContentPlayerSettings } from './content-player-settings.svelte';
2
- import { ContentPlayerUIManager } from './ui-manager.svelte';
3
- export class ContentPlayerConfig {
4
- playerBuffer = $state.raw(null);
5
- settings;
6
- playerSliderCallbacks;
7
- closeOrchestrator;
8
- uiManager = new ContentPlayerUIManager();
9
- _trackingParams = $state.raw(null);
10
- constructor(init) {
11
- const { playerBuffer, trackingParams, settings, playerSliderCallbacks, closeOrchestrator } = init;
12
- this.playerBuffer = playerBuffer;
13
- this._trackingParams = trackingParams ?? null;
14
- this.settings = settings || new ContentPlayerSettings();
15
- this.playerSliderCallbacks = playerSliderCallbacks;
16
- this.closeOrchestrator = closeOrchestrator;
17
- }
18
- get playerColors() {
19
- return this.settings.playerColors;
20
- }
21
- get trackingParams() {
22
- return this._trackingParams;
23
- }
24
- updateTrackingParams = (data) => {
25
- this._trackingParams = data ?? null;
26
- };
27
- }
@@ -1,3 +0,0 @@
1
- export { default as ContentPlayer } from './cmp.content-player.svelte';
2
- export { ContentPlayerConfig } from './content-player-config.svelte';
3
- export { ContentPlayerSettings } from './content-player-settings.svelte';
@@ -1,3 +0,0 @@
1
- export { default as ContentPlayer } from './cmp.content-player.svelte';
2
- export { ContentPlayerConfig } from './content-player-config.svelte';
3
- export { ContentPlayerSettings } from './content-player-settings.svelte';