@vkontakte/videoplayer-core 2.0.172-dev.ecd203d9a.0 → 2.0.173-dev.ebda080eb.0

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 (25) hide show
  1. package/es2015.cjs +17 -17
  2. package/es2015.esm.js +17 -17
  3. package/esnext.cjs +16 -16
  4. package/esnext.esm.js +16 -16
  5. package/evergreen.esm.js +9 -9
  6. package/package.json +2 -2
  7. package/types/providers/DashProvider/lib/buffer.d.ts +2 -0
  8. package/types/providers/DashProvider/lib/fetcher.d.ts +8 -4
  9. package/types/providers/DashProvider/lib/player.d.ts +1 -0
  10. package/types/providers/DashProviderVirtual/lib/buffer/types.d.ts +2 -0
  11. package/types/providers/DashProviderVirtual/lib/buffer/virtualBuffer/baseVirtualBufferManager.versionA.d.ts +3 -1
  12. package/types/providers/DashProviderVirtual/lib/buffer/virtualBuffer/byteRangeVirtualBufferManager.versionA.d.ts +1 -0
  13. package/types/providers/DashProviderVirtual/lib/fetcher.d.ts +8 -4
  14. package/types/providers/DashProviderVirtual/lib/player/basePlayer.d.ts +2 -0
  15. package/types/providers/ProviderContainer/index.d.ts +3 -0
  16. package/types/providers/utils/segmentCache/IndexedDbSegmentCacheStorage.d.ts +54 -0
  17. package/types/providers/utils/segmentCache/SegmentCache.d.ts +59 -0
  18. package/types/providers/utils/segmentCache/constants.d.ts +20 -0
  19. package/types/providers/utils/segmentCache/index.d.ts +8 -0
  20. package/types/providers/utils/segmentCache/segmentCacheDb.d.ts +25 -0
  21. package/types/providers/utils/segmentCache/segmentCacheUtils.d.ts +25 -0
  22. package/types/providers/utils/segmentCache/types.d.ts +76 -0
  23. package/types/utils/tuningConfig.d.ts +2 -9
  24. package/types/providers/DashProvider/lib/urlsCache.d.ts +0 -23
  25. package/types/providers/DashProviderVirtual/lib/urlsCache.d.ts +0 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vkontakte/videoplayer-core",
3
- "version": "2.0.172-dev.ecd203d9a.0",
3
+ "version": "2.0.173-dev.ebda080eb.0",
4
4
  "author": "vk.ru",
5
5
  "description": "Videoplayer core library based on the vk.ru platform",
6
6
  "homepage": "https://vk.ru",
@@ -42,6 +42,6 @@
42
42
  "**/*.d.ts"
43
43
  ],
44
44
  "dependencies": {
45
- "@vkontakte/videoplayer-shared": "1.0.101-dev.ecd203d9a.0"
45
+ "@vkontakte/videoplayer-shared": "1.0.102-dev.ebda080eb.0"
46
46
  }
47
47
  }
@@ -42,6 +42,7 @@ export declare class BufferManager {
42
42
  private parsedInitData;
43
43
  private representations;
44
44
  private segments;
45
+ private segmentIndexMap;
45
46
  private allInitsLoaded;
46
47
  private activeSegments;
47
48
  private forwardBufferRepresentations;
@@ -109,6 +110,7 @@ export declare class BufferManager {
109
110
  private feedPreDownloadedFirstSegment;
110
111
  private loadByteRangeSegments;
111
112
  private prepareByteRangeFetchSegmentParams;
113
+ private buildCacheMetadata;
112
114
  private prepareTemplateFetchSegmentParams;
113
115
  private abortActiveSegments;
114
116
  private onSomeTemplateDataLoaded;
@@ -4,6 +4,7 @@ import type { Byte, IValueSubject, ISubject, Milliseconds, IRange, ITracer, IErr
4
4
  import { abortable } from "@vkontakte/videoplayer-shared";
5
5
  import type { ExactVideoQuality } from "@vkontakte/videoplayer-shared";
6
6
  import type { CommonInit, GenericContainerParser, Segment, SegmentReference } from "../../utils/parsers/types";
7
+ import type { ISegmentCacheStorage, CacheMetadata } from "../../utils/segmentCache";
7
8
  declare global {
8
9
  interface Navigator {
9
10
  connection: {
@@ -24,11 +25,11 @@ export interface IParams {
24
25
  tracer: ITracer;
25
26
  useEnableSubtitlesParam?: boolean;
26
27
  handleExtendedNetworkErrorsSet?: boolean;
27
- useUrlCacheMechanism?: boolean;
28
28
  measureNonSegmentRequests?: boolean;
29
29
  measureFastSamples?: boolean;
30
30
  preloadFirstSegment?: boolean;
31
31
  maxQualityForFirstSegmentPreload?: ExactVideoQuality;
32
+ segmentCacheService?: ISegmentCacheStorage;
32
33
  }
33
34
  export type Priority = "high" | "low" | "auto";
34
35
  export interface FetchParamsWithUrl extends FetchParams {
@@ -45,8 +46,8 @@ export interface FetchParams {
45
46
  isLowLatency?: boolean;
46
47
  bufferOptimisation?: boolean;
47
48
  ignoreNetworkErrors?: boolean;
48
- urlCacheMechanismEnabled?: boolean;
49
49
  representationHeight?: number;
50
+ cacheMetadata?: CacheMetadata;
50
51
  }
51
52
  export type RepresentationFetchResult = {
52
53
  init: CommonInit | null;
@@ -73,16 +74,17 @@ export declare class Fetcher {
73
74
  private readonly subscription;
74
75
  private compatibilityMode;
75
76
  private useEnableSubtitlesParam;
76
- private useUrlCacheMechanism;
77
77
  private measureNonSegmentRequests;
78
78
  private measureFastSamples;
79
79
  private preloadFirstSegment;
80
80
  private maxQualityForFirstSegmentPreload;
81
+ private segmentCacheService;
82
+ private segmentCache;
81
83
  private startupPhase;
82
84
  private performanceObserver;
83
85
  private pendingConnectionMetrics;
84
86
  private handleExtendedNetworkErrorsSet;
85
- constructor({ throughputEstimator, requestQuic, tracer, compatibilityMode, useEnableSubtitlesParam, handleExtendedNetworkErrorsSet, useUrlCacheMechanism, measureNonSegmentRequests, measureFastSamples, preloadFirstSegment, maxQualityForFirstSegmentPreload }: IParams);
87
+ constructor({ throughputEstimator, requestQuic, tracer, compatibilityMode, useEnableSubtitlesParam, handleExtendedNetworkErrorsSet, measureNonSegmentRequests, measureFastSamples, preloadFirstSegment, maxQualityForFirstSegmentPreload, segmentCacheService }: IParams);
86
88
  private onHeadersReceived;
87
89
  private setupPerformanceObserver;
88
90
  private processPerformanceResourceTiming;
@@ -93,6 +95,8 @@ export declare class Fetcher {
93
95
  fetchManifest: ReturnType<typeof abortable<[string], string | null>>;
94
96
  fetch: ReturnType<typeof abortable<[string, FetchParams], ArrayBuffer | null>>;
95
97
  fetchRepresentation(segmentReference: SegmentReference, parser: GenericContainerParser<unknown>, fetchParams: FetchParams): Promise<RepresentationFetchResult | null>;
98
+ /** Скипнутые сегменты не записаны в кэш — при следующем запросе региона докачаются как miss. */
99
+ private traceSkippedCacheSegments;
96
100
  destroy(): void;
97
101
  private fetchByteRangeRepresentation;
98
102
  private fetchTemplateRepresentation;
@@ -40,6 +40,7 @@ export declare class Player {
40
40
  private subscriptionRemovable;
41
41
  private representationSubscription;
42
42
  private fetcher;
43
+ private segmentCacheService;
43
44
  state$: StateMachine<State>;
44
45
  currentVideoRepresentation$: IValueSubject<Representation["id"] | undefined>;
45
46
  currentVideoRepresentationInit$: IValueSubject<CommonInit | undefined>;
@@ -3,6 +3,7 @@ import type { CommonInit, Manifest, Representation, Segment } from "../../../uti
3
3
  import type { Fetcher } from "../fetcher";
4
4
  import type { ITuningConfig } from "../../../../utils/tuningConfig";
5
5
  import type { VideoSegmentLoadProgress } from "../../../utils/Abr/types";
6
+ import type { ISegmentCacheStorage } from "../../../utils/segmentCache";
6
7
  export type TRepresentationSwitchMode = "lazy" | "force";
7
8
  export declare enum SwithRepresentationMode {
8
9
  Lazy = "lazy",
@@ -15,6 +16,7 @@ export interface Dependencies {
15
16
  tuning: ITuningConfig;
16
17
  compatibilityMode?: boolean;
17
18
  manifest: Manifest | null;
19
+ segmentCacheService?: ISegmentCacheStorage;
18
20
  getCurrentPosition(): Milliseconds | undefined;
19
21
  getCurrentStallDuration(): Milliseconds;
20
22
  isActiveLowLatency(): boolean;
@@ -2,6 +2,7 @@ import type { Fetcher, FetchParamsWithUrl, Priority, RepresentationFetchResult }
2
2
  import type { CommonInit, ContainerParser, Representation, Segment } from "../../../../utils/parsers/types";
3
3
  import { StreamKind } from "../../../../utils/parsers/types";
4
4
  import type { ITuningConfig } from "../../../../../utils/tuningConfig";
5
+ import type { ISegmentCacheStorage } from "../../../../utils/segmentCache";
5
6
  import type { Byte, IError, IRange, ISubject, ITracer, IValueSubject, Milliseconds, Seconds } from "@vkontakte/videoplayer-shared";
6
7
  import { abortable, Subscription } from "@vkontakte/videoplayer-shared";
7
8
  import type { NativeBufferManager } from "../nativeBufferManager";
@@ -19,6 +20,7 @@ export declare abstract class BaseVirtualBufferManager<T extends Segment> implem
19
20
  protected readonly fetcher: Fetcher;
20
21
  protected readonly tracer: ITracer;
21
22
  protected readonly tuning: ITuningConfig;
23
+ protected readonly segmentCacheService: ISegmentCacheStorage | undefined;
22
24
  protected representations: Map<Representation["id"], Representation>;
23
25
  protected playingRepresentationId: Representation["id"] | undefined;
24
26
  protected downloadingRepresentationId: Representation["id"] | undefined;
@@ -53,7 +55,7 @@ export declare abstract class BaseVirtualBufferManager<T extends Segment> implem
53
55
  protected readonly getCurrentPosition: () => Milliseconds | undefined;
54
56
  protected readonly getCurrentStallDuration: () => Milliseconds | undefined;
55
57
  protected brokenSegmentAppendDetected: boolean;
56
- protected constructor(kind: StreamKind, nativeBufferManager: NativeBufferManager, representations: Representation[], { fetcher, tracer, tuning, getCurrentPosition, getCurrentStallDuration, manifest }: Dependencies);
58
+ protected constructor(kind: StreamKind, nativeBufferManager: NativeBufferManager, representations: Representation[], { fetcher, tracer, tuning, getCurrentPosition, getCurrentStallDuration, manifest, segmentCacheService }: Dependencies);
57
59
  protected abstract loadItems(itemsToLoad: IBufferPlaybackQueueItem<T>[], representation: Representation, priority?: Priority): Promise<void>;
58
60
  protected abstract selectItemsToLoad(): IBufferPlaybackQueueItem<T>[];
59
61
  protected abstract prepareFetchParams(items: IBufferPlaybackQueueItem<T>[], representation: Representation): FetchParamsWithUrl;
@@ -8,6 +8,7 @@ export declare class ByteRangeVirtualBufferManager extends BaseVirtualBufferMana
8
8
  protected override loadItems(itemsToLoad: IBufferPlaybackQueueItem<ByteRangeSegment>[], representation: Representation, priority?: Priority): Promise<void>;
9
9
  protected override selectItemsToLoad(): IBufferPlaybackQueueItem<ByteRangeSegment>[];
10
10
  protected override prepareFetchParams(items: IBufferPlaybackQueueItem<ByteRangeSegment>[], representation: Representation): FetchParamsWithUrl;
11
+ private buildCacheMetadata;
11
12
  /**
12
13
  * Закидываем в буфер сегменты атомарнее чем сегмент целиком. Например, по боксам в мпеге и по блокам в вебме.
13
14
  * Таким образом не ждём его полной загрузки и готовы играть его намного быстрее
@@ -4,6 +4,7 @@ import type { Byte, IError, IRange, ISubject, ITracer, IValueSubject, Millisecon
4
4
  import { abortable } from "@vkontakte/videoplayer-shared";
5
5
  import type { ExactVideoQuality } from "@vkontakte/videoplayer-shared";
6
6
  import type { CommonInit, GenericContainerParser, Segment, SegmentReference, StreamKind } from "../../utils/parsers/types";
7
+ import type { ISegmentCacheStorage, CacheMetadata } from "../../utils/segmentCache";
7
8
  declare global {
8
9
  interface Navigator {
9
10
  connection: {
@@ -24,11 +25,11 @@ export interface IParams {
24
25
  tracer: ITracer;
25
26
  useEnableSubtitlesParam?: boolean;
26
27
  handleExtendedNetworkErrorsSet?: boolean;
27
- useUrlCacheMechanism?: boolean;
28
28
  measureNonSegmentRequests?: boolean;
29
29
  measureFastSamples?: boolean;
30
30
  preloadFirstSegment?: boolean;
31
31
  maxQualityForFirstSegmentPreload?: ExactVideoQuality;
32
+ segmentCacheService?: ISegmentCacheStorage;
32
33
  }
33
34
  export type Priority = "high" | "low" | "auto";
34
35
  export interface FetchParamsWithUrl extends FetchParams {
@@ -44,8 +45,8 @@ export interface FetchParams {
44
45
  measureThroughput?: boolean;
45
46
  isLowLatency?: boolean;
46
47
  bufferOptimisation?: boolean;
47
- urlCacheMechanismEnabled?: boolean;
48
48
  kind: StreamKind;
49
+ cacheMetadata?: CacheMetadata;
49
50
  }
50
51
  export type RepresentationFetchResult<T extends Segment> = {
51
52
  initMetadata: CommonInit | null;
@@ -72,16 +73,17 @@ export declare class Fetcher {
72
73
  private subscription;
73
74
  private compatibilityMode;
74
75
  private useEnableSubtitlesParam;
75
- private useUrlCacheMechanism;
76
76
  private measureNonSegmentRequests;
77
77
  private measureFastSamples;
78
78
  private preloadFirstSegment;
79
79
  private maxQualityForFirstSegmentPreload;
80
+ private segmentCacheService;
81
+ private segmentCache;
80
82
  private startupPhase;
81
83
  private performanceObserver;
82
84
  private pendingConnectionMetrics;
83
85
  private handleExtendedNetworkErrorsSet;
84
- constructor({ throughputEstimator, requestQuic, tracer, compatibilityMode, useEnableSubtitlesParam, handleExtendedNetworkErrorsSet, useUrlCacheMechanism, measureNonSegmentRequests, measureFastSamples, preloadFirstSegment, maxQualityForFirstSegmentPreload }: IParams);
86
+ constructor({ throughputEstimator, requestQuic, tracer, compatibilityMode, useEnableSubtitlesParam, handleExtendedNetworkErrorsSet, measureNonSegmentRequests, measureFastSamples, preloadFirstSegment, maxQualityForFirstSegmentPreload, segmentCacheService }: IParams);
85
87
  private onHeadersReceived;
86
88
  private setupPerformanceObserver;
87
89
  private processResourceTiming;
@@ -92,6 +94,8 @@ export declare class Fetcher {
92
94
  fetchManifest: ReturnType<typeof abortable<[string], string | null>>;
93
95
  fetch: ReturnType<typeof abortable<[string, FetchParams], ArrayBuffer | null>>;
94
96
  fetchRepresentation<T extends Segment>(segmentReference: SegmentReference, parser: GenericContainerParser<unknown>, dowbloadAbortSignal: AbortSignal, priority?: Priority, representationHeight?: number, kind?: StreamKind): Promise<RepresentationFetchResult<T> | null | undefined>;
97
+ /** Скипнутые сегменты не записаны в кэш — при следующем запросе региона докачаются как miss. */
98
+ private traceSkippedCacheSegments;
95
99
  destroy(): void;
96
100
  private fetchByteRangeRepresentation;
97
101
  private fetchTemplateRepresentation;
@@ -9,6 +9,7 @@ import type { IError, IRange, ISubject, ISubscription, ITracer, IValueSubject, M
9
9
  import { abortable, Subject, Subscription, SubscriptionRemovable } from "@vkontakte/videoplayer-shared";
10
10
  import type { VideoSegmentLoadProgress } from "../../../utils/Abr/types";
11
11
  import { Fetcher } from "../fetcher";
12
+ import type { ISegmentCacheStorage } from "../../../utils/segmentCache";
12
13
  import type { Dependencies, IVirtualBufferManager, SwithRepresentationMode } from "../buffer/types";
13
14
  import type { Params } from "./types";
14
15
  import { State } from "./types";
@@ -35,6 +36,7 @@ export declare abstract class BasePlayer {
35
36
  protected subscriptionRemovable: SubscriptionRemovable;
36
37
  protected representationSubscription: Subscription;
37
38
  protected fetcher: Fetcher;
39
+ protected segmentCacheService: ISegmentCacheStorage | undefined;
38
40
  protected forceEnded$: ISubject<void>;
39
41
  protected stallWatchdogSubscription: ISubscription | undefined;
40
42
  protected destroyController: AbortController;
@@ -37,6 +37,7 @@ export default class ProviderContainer implements IProviderContainer {
37
37
  private currentProviderStarted;
38
38
  private volumeMultiplierManager;
39
39
  private dashMaxTvVideoQuality;
40
+ private segmentCacheCleared;
40
41
  private mayday;
41
42
  private maydayStatistics;
42
43
  constructor(params: IParams);
@@ -50,6 +51,8 @@ export default class ProviderContainer implements IProviderContainer {
50
51
  private destroyProvider;
51
52
  private handleMaydayRecommendation;
52
53
  private applyErrorSideEffects;
54
+ /** Битые данные в кэше могут вызывать повторные ошибки после реинита — очищаем кэш целиком. */
55
+ private clearSegmentCacheOnError;
53
56
  private createProvider;
54
57
  private createScreenProvider;
55
58
  private createChromecastProvider;
@@ -0,0 +1,54 @@
1
+ import type { InitSegmentParams, ISegmentCacheStorage, SegmentCacheConfig, SetSegmentParams } from "./types";
2
+ export declare class IndexedDbSegmentCacheStorage implements ISegmentCacheStorage {
3
+ private config;
4
+ private dbPromise;
5
+ private destroyed;
6
+ private evictionTimerId;
7
+ private globalTotalBytes;
8
+ private maxTotalStoredBytes;
9
+ private quotaExceeded;
10
+ private cacheEventsChannel;
11
+ constructor(config: SegmentCacheConfig);
12
+ private getDb;
13
+ private isFresh;
14
+ /**
15
+ * Читает записи по массиву ключей в одной транзакции.
16
+ * Свежие по TTL — возвращает данные и обновляет lastAccessAt (не чаще чем раз в TOUCH_INTERVAL_MS,
17
+ * чтобы избежать write amplification: каждый cache hit не должен делать store.put с полным ArrayBuffer).
18
+ * Протухшие — удаляет, декрементирует globalTotalBytes.
19
+ */
20
+ private readFreshRecord;
21
+ /**
22
+ * При открытии БД пересчитывает globalTotalBytes: проходит по всем записям,
23
+ * суммирует размер живых (не протухших по TTL) и удаляет протухшие.
24
+ * TTL различается: media — ttlMs, init/index — initTtlMs.
25
+ */
26
+ private handleDbOpened;
27
+ /** Возвращает TTL в зависимости от типа записи по первичному ключу. */
28
+ private ttlForEntry;
29
+ getSegments(normalizedUrl: string, indices: number[]): Promise<Map<number, ArrayBuffer>>;
30
+ /** Удаляет записи по индексам сегментов в одной транзакции, декрементируя globalTotalBytes. */
31
+ deleteSegments(normalizedUrl: string, indices: number[]): Promise<void>;
32
+ setSegment(params: SetSegmentParams): Promise<void>;
33
+ getInitSegment({ normalizedUrl, rangeType, range }: InitSegmentParams): Promise<ArrayBuffer | null>;
34
+ /** Универсальная запись с подсчётом delta, QuotaExceededError-обработкой и eviction. */
35
+ private putRecord;
36
+ private handleQuotaExceededError;
37
+ /** Полная очистка кэша с рассылкой события остальным инстансам (вкладки, другие плееры). */
38
+ clear(): Promise<void>;
39
+ destroy(): void;
40
+ private scheduleEviction;
41
+ /**
42
+ * LRU-eviction: запускается по дебаунсу после каждой записи.
43
+ * Без квоты — триггерит при достижении maxTotalStoredBytes.
44
+ * После QuotaExceededError — раньше, на maxTotalStoredBytes * evictionThreshold,
45
+ * чтобы не упереться в квоту повторно.
46
+ * Очищает с запасом — до maxTotalStoredBytes * evictionTargetPercent.
47
+ */
48
+ private runEviction;
49
+ /**
50
+ * Удаляет самые старые по lastAccessAt записи, пока не освободит хотя бы bytesToFree.
51
+ * maxBytesPerBatch ограничивает длительность одной транзакции; без лимита — при QuotaExceededError.
52
+ */
53
+ private evictOldest;
54
+ }
@@ -0,0 +1,59 @@
1
+ import type { ISegmentCacheStorage, CacheMetadata, CacheMetadataEntry } from "./types";
2
+ import type { SubRange } from "./segmentCacheUtils";
3
+ export declare enum CacheCheckResultType {
4
+ FullHit = "full_hit",
5
+ FullMiss = "full_miss",
6
+ Partial = "partial"
7
+ }
8
+ export type CacheCheckResult = {
9
+ type: CacheCheckResultType.FullHit;
10
+ data: ArrayBuffer;
11
+ } | {
12
+ type: CacheCheckResultType.FullMiss;
13
+ } | {
14
+ type: CacheCheckResultType.Partial;
15
+ cached: Map<number, ArrayBuffer>;
16
+ missingRange: SubRange;
17
+ startBuffer: {
18
+ buffer: ArrayBuffer;
19
+ loaded: number;
20
+ } | null;
21
+ };
22
+ export declare class SegmentCache {
23
+ private cacheService;
24
+ constructor(cacheService: ISegmentCacheStorage);
25
+ getCached(url: string, metadata: CacheMetadata): Promise<CacheCheckResult>;
26
+ /**
27
+ * Длина записи обязана совпадать с byteRange сегмента: и сборка (assembleFromCached склеивает
28
+ * конкатенацией, assembleStartBuffer — по абсолютным смещениям), и кормление буфера адресуют
29
+ * сегменты по этим смещениям. Битая запись сдвигает/зануляет весь батч после себя.
30
+ * Расхождение = отрава: считаем miss и удаляем ключ — сегмент перекачается и перезапишется.
31
+ */
32
+ private dropCorruptedEntries;
33
+ storeDownloadedSegments({ url, buffer, segments, networkDataOffset, validBytes, onSkipped }: {
34
+ url: string;
35
+ buffer: ArrayBuffer;
36
+ segments: CacheMetadataEntry[];
37
+ networkDataOffset?: number;
38
+ /**
39
+ * Сколько байт в начале buffer содержат реальные данные.
40
+ * Может быть меньше buffer.byteLength — при пре-аллокации (bufferOptimisation) хвост буфера остаётся нулями.
41
+ */
42
+ validBytes?: number;
43
+ /**
44
+ * Вызывается, если часть сегментов не влезла в validBytes: они не записаны в кэш
45
+ * и докачаются как miss при следующем запросе региона.
46
+ */
47
+ onSkipped?: (segments: CacheMetadataEntry[]) => void;
48
+ }): void;
49
+ private assembleStartBuffer;
50
+ private buildPartialFromCacheStart;
51
+ storeMissing({ url, missingRange, networkData, networkDataOffset, validBytes, onSkipped }: {
52
+ url: string;
53
+ missingRange: SubRange;
54
+ networkData: ArrayBuffer;
55
+ networkDataOffset?: number;
56
+ validBytes?: number;
57
+ onSkipped?: (segments: CacheMetadataEntry[]) => void;
58
+ }): void;
59
+ }
@@ -0,0 +1,20 @@
1
+ export declare const DB_NAME = "vk_player_segment_cache";
2
+ export declare const DB_VERSION = 1;
3
+ export declare const STORE = "cache";
4
+ export declare const EVICTION_DEBOUNCE_MS = 5e3;
5
+ /** Канал синхронизации инстансов storage (вкладки, несколько плееров на странице) */
6
+ export declare const CACHE_EVENTS_CHANNEL = "vk_player_segment_cache_events";
7
+ /** Событие полной очистки кэша: получатель сбрасывает in-memory счётчик байт, база уже пуста */
8
+ export declare const CACHE_CLEARED_EVENT = "cleared";
9
+ /**
10
+ * Минимальный интервал между обновлениями lastAccessAt при чтении.
11
+ * Без него каждый cache hit делал бы store.put с полным ArrayBuffer ради одного поля.
12
+ * При TTL в часы точность LRU до минут не нужна — 5 минут достаточно.
13
+ */
14
+ export declare const TOUCH_INTERVAL_MS: number;
15
+ /** Тип записи: media-сегмент, init-сегмент, index-сегмент */
16
+ export declare enum CacheEntryType {
17
+ Media = 0,
18
+ Init = 1,
19
+ Index = 2
20
+ }
@@ -0,0 +1,8 @@
1
+ export { IndexedDbSegmentCacheStorage } from "./IndexedDbSegmentCacheStorage";
2
+ export { SegmentCache, CacheCheckResultType } from "./SegmentCache";
3
+ export type { CacheCheckResult } from "./SegmentCache";
4
+ export { normalizeSegmentCacheUrl, assemblePartialBuffer, toEntryType, createMediaKey, createInitKey, isQuotaExceededError } from "./segmentCacheUtils";
5
+ export type { SubRange } from "./segmentCacheUtils";
6
+ export { clearSegmentCacheDb } from "./segmentCacheDb";
7
+ export { DB_NAME, DB_VERSION, STORE, EVICTION_DEBOUNCE_MS, CacheEntryType } from "./constants";
8
+ export type { ISegmentCacheStorage, CacheMetadata, CacheMetadataEntry, SegmentCacheConfig, SegmentCacheEntry, InitSegmentParams, InitSegmentRangeType, SetMediaSegmentParams, SetInitSegmentParams, SetSegmentParams, CacheRecord, CachePrimaryKey } from "./types";
@@ -0,0 +1,25 @@
1
+ export declare const INDEX_BY_LAST_ACCESS = "byLastAccess";
2
+ export declare const INDEX_BY_SIZE_AND_AGE = "bySizeAndAge";
3
+ export declare const INDEX_BY_LAST_ACCESS_AND_SIZE = "byLastAccessAndSize";
4
+ /**
5
+ * Открывает базу кэша, при первом создании поднимает store и индексы.
6
+ * onOpened (если передан) await-ится до resolve — используется только в clearSegmentCacheDb.
7
+ * Инстанс storage не использует onOpened: rehydrate запускается параллельно, чтобы не блокировать чтения.
8
+ */
9
+ export declare const openSegmentCacheDb: (onOpened?: (db: IDBDatabase) => Promise<void>) => Promise<IDBDatabase>;
10
+ /** Выполняет операции в одной readwrite-транзакции, резолвится по её завершении. */
11
+ export declare const execStoreTx: (db: IDBDatabase, body: (store: IDBObjectStore) => void) => Promise<void>;
12
+ /**
13
+ * Гоняет курсор по индексу, вызывая visit на каждой записи.
14
+ * visit возвращает false — итерация останавливается, транзакция завершается.
15
+ * Работает только внутри execStoreTx: завершение итерации отслеживается транзакцией.
16
+ */
17
+ export declare const iterateCursor: <T extends IDBCursor>(request: IDBRequest<T | null>, visit: (cursor: T) => boolean) => void;
18
+ /** Рассылает событие полной очистки кэша всем инстансам storage (вкладки, другие плееры на странице). */
19
+ export declare const broadcastCacheCleared: () => void;
20
+ /**
21
+ * Полностью очищает кэш без привязки к инстансу storage: можно звать из мест,
22
+ * где экземпляр IndexedDbSegmentCacheStorage недоступен (например, из ProviderContainer при ошибках).
23
+ * Живые инстансы узнают об очистке через broadcastCacheCleared и сбросят in-memory счётчики.
24
+ */
25
+ export declare const clearSegmentCacheDb: () => Promise<void>;
@@ -0,0 +1,25 @@
1
+ import type { Byte, IRange } from "@vkontakte/videoplayer-shared";
2
+ import type { CacheMetadata, CacheMetadataEntry, InitSegmentRangeType, CachePrimaryKey } from "./types";
3
+ import { CacheEntryType } from "./constants";
4
+ /** Приводит URL к каноническому виду: заменяет hostname на cache.local и удаляет CDN-параметры (sig, expires, bytes, fromCache). */
5
+ export declare function normalizeSegmentCacheUrl(url: string): string;
6
+ export declare function toEntryType(rangeType: InitSegmentRangeType): CacheEntryType;
7
+ export declare function createMediaKey(normalizedUrl: string, segmentIndex: number): CachePrimaryKey;
8
+ export declare function createInitKey(normalizedUrl: string, entryType: CacheEntryType, rangeFrom: number, rangeTo: number): CachePrimaryKey;
9
+ export declare function isQuotaExceededError(error: unknown): boolean;
10
+ export type SubRange = {
11
+ range: IRange<Byte>;
12
+ segments: CacheMetadataEntry[];
13
+ };
14
+ /** Проверяет, что сегменты идут подряд по индексам — без разрывов между ними. Ожидает отсортированный массив. */
15
+ export declare function isContiguous(segments: CacheMetadataEntry[]): boolean;
16
+ /** Склеивает буферы закэшированных сегментов в один ArrayBuffer. Используется при full hit. */
17
+ export declare function assembleFromCached(metadata: CacheMetadata, cached: Map<number, ArrayBuffer>): ArrayBuffer;
18
+ /** Склеивает закэшированные и сетевые данные в один ArrayBuffer для partial hit. */
19
+ export declare function assemblePartialBuffer({ metadata, cached, missingRange, networkData, networkDataOffset }: {
20
+ metadata: CacheMetadata;
21
+ cached: Map<number, ArrayBuffer>;
22
+ missingRange: SubRange;
23
+ networkData: ArrayBuffer;
24
+ networkDataOffset?: number;
25
+ }): ArrayBuffer;
@@ -0,0 +1,76 @@
1
+ import type { Byte, IRange } from "@vkontakte/videoplayer-shared";
2
+ import type { CacheEntryType } from "./constants";
3
+ export type InitSegmentRangeType = "init" | "index";
4
+ export interface SegmentCacheEntry {
5
+ data: ArrayBuffer;
6
+ segmentIndex: number;
7
+ }
8
+ export interface InitSegmentParams {
9
+ normalizedUrl: string;
10
+ rangeType: InitSegmentRangeType;
11
+ range: IRange<Byte>;
12
+ }
13
+ export interface SetMediaSegmentParams {
14
+ type: "media";
15
+ normalizedUrl: string;
16
+ segmentIndex: number;
17
+ data: ArrayBuffer;
18
+ }
19
+ export interface SetInitSegmentParams {
20
+ type: "init";
21
+ normalizedUrl: string;
22
+ rangeType: InitSegmentRangeType;
23
+ range: IRange<Byte>;
24
+ data: ArrayBuffer;
25
+ }
26
+ export type SetSegmentParams = SetMediaSegmentParams | SetInitSegmentParams;
27
+ export interface ISegmentCacheStorage {
28
+ getSegments(normalizedUrl: string, indices: number[]): Promise<Map<number, ArrayBuffer>>;
29
+ setSegment(params: SetSegmentParams): Promise<void>;
30
+ /** Удаляет media-записи по индексам сегментов. Используется для самолечения: запись с несовпадающей длиной удаляется и сегмент перекачивается */
31
+ deleteSegments(normalizedUrl: string, indices: number[]): Promise<void>;
32
+ getInitSegment(params: InitSegmentParams): Promise<ArrayBuffer | null>;
33
+ /** Полная очистка кэша (все записи, без привязки к url) с рассылкой события другим инстансам */
34
+ clear(): Promise<void>;
35
+ destroy(): void;
36
+ }
37
+ export interface CacheMetadataEntry {
38
+ index: number;
39
+ byteRange: IRange<Byte>;
40
+ }
41
+ export interface CacheMetadata {
42
+ segments: CacheMetadataEntry[];
43
+ }
44
+ export interface SegmentCacheConfig {
45
+ /** Включает кэширование media-сегментов в IndexedDB */
46
+ enabled: boolean;
47
+ /** Лимит суммарного размера кэша (media + init/index сегменты) в байтах. При QuotaExceededError автоматически опускается до текущего размера */
48
+ maxTotalSizeBytes: number;
49
+ /** Доля от эффективного лимита, при превышении которой запускается LRU-эвикт (0.9 = 90%) */
50
+ evictionThreshold: number;
51
+ /** Доля от максимального хранимого кэша, до которой LRU-эвикт очищает кэш (0.7 = 70%).
52
+ * Должна быть ниже evictionThreshold: запас между ними не даёт эвикту срабатывать на каждую запись у границы лимита
53
+ * */
54
+ evictionTargetPercent: number;
55
+ /** Максимальный объём данных в байтах, который LRU-эвикт удаляет за один проход (ограничивает длительность одной транзакции) */
56
+ evictionBatchMaxBytes: number;
57
+ /** Доля от maxTotalStoredBytes, которую нужно освободить при QuotaExceededError (0.5 = 50%) */
58
+ quotaExceededEvictPercent: number;
59
+ /** TTL для media-сегментов в миллисекундах. Протухшие записи удаляются при чтении и при rehydrate */
60
+ ttlMs: number;
61
+ /** TTL для init/index-сегментов в миллисекундах (обычно длиннее, т.к. они стабильнее) */
62
+ initTtlMs: number;
63
+ /** Стратегия очистки кэша при ошибках декодера/фетчера: 'once' — один раз за жизнь контейнера, 'always' — на каждую ошибку */
64
+ clearOnErrorStrategy: "once" | "always";
65
+ }
66
+ /**
67
+ * Единая запись кэша. Ключ передаётся явно (out-of-line) через createMediaKey / createInitKey.
68
+ */
69
+ export interface CacheRecord {
70
+ normalizedUrl: string;
71
+ data: ArrayBuffer;
72
+ byteLength: number;
73
+ createdAt: number;
74
+ lastAccessAt: number;
75
+ }
76
+ export type CachePrimaryKey = [string, CacheEntryType, number, number];
@@ -5,6 +5,7 @@ import { IOSPreferredFormat } from "../enums/IOSPreferredFormat";
5
5
  import type { Byte, Kbps, Milliseconds, RecursivePartial, Seconds, TimerModule } from "@vkontakte/videoplayer-shared";
6
6
  import { type ExactVideoQuality, VideoQuality } from "@vkontakte/videoplayer-shared";
7
7
  import { AudioRuleName, VideoRuleName } from "../providers/utils/Abr/types";
8
+ import type { SegmentCacheConfig } from "../providers/utils/segmentCache/types";
8
9
  export type ITuningConfig = {
9
10
  /** @deprecated */
10
11
  configName?: string[];
@@ -439,15 +440,6 @@ export type ITuningConfig = {
439
440
  */
440
441
  handleExtendedNetworkErrorsSet?: boolean;
441
442
  /**
442
- * Кэшируем ответы от сервера руками, так как из за динамичесокй природы некоторых параметров в урле
443
- * браузер это не может сделать.
444
- */
445
- useUrlCacheMechanism?: boolean;
446
- /**
447
- * Очищаем кэш урлов при падение провайдера.
448
- */
449
- dropUrlCacheWhenProviderCrashed?: boolean;
450
- /**
451
443
  * При смене/реините провайдера игнорируем результат того, смогло видео заиграть или нет.
452
444
  */
453
445
  ignoreForcePlayResultWhenProviderChanged?: boolean;
@@ -475,6 +467,7 @@ export type ITuningConfig = {
475
467
  * провайдер по-прежнему ждёт canplay, чтобы play() не вызывался до появления данных.
476
468
  */
477
469
  readyOnLoadedMetadata: boolean;
470
+ segmentCache: SegmentCacheConfig;
478
471
  };
479
472
  export type IOptionalTuningConfig = RecursivePartial<ITuningConfig>;
480
473
  export declare const fillDefault: (partial: IOptionalTuningConfig) => ITuningConfig;
@@ -1,23 +0,0 @@
1
- export type CacheManager = {
2
- get: () => Promise<Response | null>;
3
- set: (response: Response) => void;
4
- };
5
- /**
6
- * Пытаемся добиться кэширования вызовов.
7
- * Сейчас есть проблема, что expires и sig меняются, хотя контент не меняется, поэтому ходим мимо кэша.
8
- * Из за этого обрабатываем кэш вручную.
9
- */
10
- export declare const createCacheManager: (url: string) => Promise<CacheManager>;
11
- export declare function canUseCacheApi(): boolean;
12
- export declare function normalizeUrl(url: string): string;
13
- /**
14
- * Проверяет, не просрочен ли кэшированный ответ
15
- */
16
- export declare function isResponseExpired(response: Response): boolean;
17
- export declare function cloneResponse(response: Response, url: string): Response;
18
- /**
19
- * Извлекает expires из оригинального URL (параметр expires1)
20
- * Предполагается, что expires1 — timestamp в секундах
21
- */
22
- export declare function getExpiresFromUrl(url: string): number;
23
- export declare const dropUrlsCache: () => Promise<void>;
@@ -1,22 +0,0 @@
1
- export type CacheManager = {
2
- get: () => Promise<Response | null>;
3
- set: (response: Response) => Promise<void>;
4
- };
5
- /**
6
- * Пытаемся добиться кэширования вызовов.
7
- * Сейчас есть проблема, что expires и sig меняются, хотя контент не меняется, поэтому ходим мимо кэша.
8
- * Из за этого обрабатываем кэш вручную.
9
- */
10
- export declare const createCacheManager: (url: string) => Promise<CacheManager>;
11
- export declare function canUseCacheApi(): boolean;
12
- export declare function normalizeUrl(url: string): string;
13
- /**
14
- * Проверяет, не просрочен ли кэшированный ответ
15
- */
16
- export declare function isResponseExpired(response: Response): boolean;
17
- export declare function cloneResponse(response: Response, url: string): Response;
18
- /**
19
- * Извлекает expires из оригинального URL (параметр expires1)
20
- * Предполагается, что expires1 — timestamp в секундах
21
- */
22
- export declare function getExpiresFromUrl(url: string): number;