box-content-preview 3.89.0 → 3.91.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.
- package/README.md +7 -7
- package/dist/lib/index.css +3 -3
- package/dist/lib/index.js +1026 -361
- package/package.json +1 -1
package/dist/lib/index.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* and limitations under the license.
|
|
22
22
|
*/
|
|
23
23
|
import { decode as __WEBPACK_EXTERNAL_MODULE_box_ui_elements_es_utils_keys_d752524f_decode__ } from "box-ui-elements/es/utils/keys";
|
|
24
|
+
import { createPortal as __WEBPACK_EXTERNAL_MODULE_react_dom_7dac9eee_createPortal__ } from "react-dom";
|
|
24
25
|
import * as __WEBPACK_EXTERNAL_MODULE_react__ from "react";
|
|
25
26
|
import { default as __WEBPACK_EXTERNAL_MODULE_axios_default__ } from "axios";
|
|
26
27
|
import { bdlBoxBlue as __WEBPACK_EXTERNAL_MODULE_box_ui_elements_es_styles_variables_c815ce80_bdlBoxBlue__, bdlGray65 as __WEBPACK_EXTERNAL_MODULE_box_ui_elements_es_styles_variables_c815ce80_bdlGray65__, bdlGreenLight as __WEBPACK_EXTERNAL_MODULE_box_ui_elements_es_styles_variables_c815ce80_bdlGreenLight__, bdlGrimace as __WEBPACK_EXTERNAL_MODULE_box_ui_elements_es_styles_variables_c815ce80_bdlGrimace__, bdlWatermelonRed as __WEBPACK_EXTERNAL_MODULE_box_ui_elements_es_styles_variables_c815ce80_bdlWatermelonRed__, bdlYellorange as __WEBPACK_EXTERNAL_MODULE_box_ui_elements_es_styles_variables_c815ce80_bdlYellorange__, bdlYellow as __WEBPACK_EXTERNAL_MODULE_box_ui_elements_es_styles_variables_c815ce80_bdlYellow__, white as __WEBPACK_EXTERNAL_MODULE_box_ui_elements_es_styles_variables_c815ce80_white__ } from "box-ui-elements/es/styles/variables";
|
|
@@ -327,6 +328,7 @@ class PreviewError extends Error {
|
|
|
327
328
|
/* harmony export */ Jm: () => (/* binding */ SELECTOR_BOX_PREVIEW_LOGO_CUSTOM),
|
|
328
329
|
/* harmony export */ Kb: () => (/* binding */ CLASS_BOX_PREVIEW_THUMBNAILS_CONTAINER),
|
|
329
330
|
/* harmony export */ Kh: () => (/* binding */ SELECTOR_NAVIGATION_RIGHT),
|
|
331
|
+
/* harmony export */ Ku: () => (/* binding */ AUDIO_PLAYER_V2),
|
|
330
332
|
/* harmony export */ Lq: () => (/* binding */ PREVIEW_SCRIPT_NAME),
|
|
331
333
|
/* harmony export */ Lz: () => (/* binding */ CLASS_BOX_PREVIEW_THUMBNAILS_CLOSE),
|
|
332
334
|
/* harmony export */ M$: () => (/* binding */ CLASS_BOX_PREVIEW_PRELOAD_WRAPPER_DOCUMENT),
|
|
@@ -379,6 +381,7 @@ class PreviewError extends Error {
|
|
|
379
381
|
/* harmony export */ jF: () => (/* binding */ CLASS_IS_VISIBLE),
|
|
380
382
|
/* harmony export */ jP: () => (/* binding */ CLASS_PREVIEW_LOADED),
|
|
381
383
|
/* harmony export */ jf: () => (/* binding */ CLASS_FULLSCREEN_UNSUPPORTED),
|
|
384
|
+
/* harmony export */ jr: () => (/* binding */ X_REP_HINT_WAVEFORM),
|
|
382
385
|
/* harmony export */ lM: () => (/* binding */ CLASS_BOX_PREVIEW_MOBILE),
|
|
383
386
|
/* harmony export */ lR: () => (/* binding */ SELECTOR_BOX_PREVIEW_HEADER_CONTAINER),
|
|
384
387
|
/* harmony export */ lX: () => (/* binding */ CLASS_BOX_PREVIEW_PRELOAD_OVERLAY),
|
|
@@ -396,6 +399,8 @@ class PreviewError extends Error {
|
|
|
396
399
|
/* harmony export */ rR: () => (/* binding */ CLASS_BOX_PREVIEW_THEME_DARK),
|
|
397
400
|
/* harmony export */ rZ: () => (/* binding */ CLASS_BOX_PREVIEW_OVERLAY),
|
|
398
401
|
/* harmony export */ rr: () => (/* binding */ CLASS_IS_BUFFERING),
|
|
402
|
+
/* harmony export */ u4: () => (/* binding */ WAVEFORM_REP_NAME),
|
|
403
|
+
/* harmony export */ uD: () => (/* binding */ STATUS_ERROR),
|
|
399
404
|
/* harmony export */ uN: () => (/* binding */ MEDIA_PLAYBACK_SKIP_DURATION),
|
|
400
405
|
/* harmony export */ vV: () => (/* binding */ CLASS_BOX_PREVIEW_PRELOAD_WRAPPER_PRESENTATION),
|
|
401
406
|
/* harmony export */ v_: () => (/* binding */ SELECTOR_BOX_PREVIEW_BTN_DOWNLOAD),
|
|
@@ -415,7 +420,7 @@ class PreviewError extends Error {
|
|
|
415
420
|
/* harmony export */ zb: () => (/* binding */ CLASS_DOC_FIRST_IMAGE),
|
|
416
421
|
/* harmony export */ ze: () => (/* binding */ STATUS_PENDING)
|
|
417
422
|
/* harmony export */ });
|
|
418
|
-
/* unused harmony exports CLASS_ACTIVE, CLASS_BOX_PREVIEW, CLASS_BOX_PREVIEW_CONTAINER, CLASS_BOX_PREVIEW_HEADER_CONTAINER, CLASS_BOX_PREVIEW_ICON, CLASS_BOX_PREVIEW_LOGO_CUSTOM, CLASS_BOX_PREVIEW_LOGO_DEFAULT, CLASS_PREFETCHED_IMAGE, SELECTOR_BOX_PREVIEW_HEADER, SELECTOR_BOX_PREVIEW_NOTIFICATION
|
|
423
|
+
/* unused harmony exports CLASS_ACTIVE, CLASS_BOX_PREVIEW, CLASS_BOX_PREVIEW_CONTAINER, CLASS_BOX_PREVIEW_HEADER_CONTAINER, CLASS_BOX_PREVIEW_ICON, CLASS_BOX_PREVIEW_LOGO_CUSTOM, CLASS_BOX_PREVIEW_LOGO_DEFAULT, CLASS_PREFETCHED_IMAGE, SELECTOR_BOX_PREVIEW_HEADER, SELECTOR_BOX_PREVIEW_NOTIFICATION */
|
|
419
424
|
const CLASS_ACTIVE = 'bp-is-active';
|
|
420
425
|
const CLASS_ANNOTATIONS_CREATE_DRAWING = 'bp-annotations-create--drawing';
|
|
421
426
|
const CLASS_ANNOTATIONS_CREATE_HIGHLIGHT = 'bp-annotations-create--highlight';
|
|
@@ -502,6 +507,7 @@ const APP_HOST = 'https://app.box.com';
|
|
|
502
507
|
const ORIGINAL_REP_NAME = 'ORIGINAL';
|
|
503
508
|
const PRELOAD_REP_NAME = 'jpg';
|
|
504
509
|
const PRELOAD_PAGED_REP_NAME = 'webp';
|
|
510
|
+
const WAVEFORM_REP_NAME = 'waveform';
|
|
505
511
|
const VIDEO_VIEWER_NAMES = ['Dash', 'MP4'];
|
|
506
512
|
const STATUS_ERROR = 'error';
|
|
507
513
|
const STATUS_NONE = 'none';
|
|
@@ -516,7 +522,9 @@ const X_REP_HINT_IMAGE = '[jpg?dimensions=2048x2048,png?dimensions=2048x2048]';
|
|
|
516
522
|
const X_REP_HINT_VIDEO_DASH = '[dash,mp4][filmstrip]';
|
|
517
523
|
const X_REP_HINT_VIDEO_DASH_EXTRACTED_TEXT = '[extracted_text]';
|
|
518
524
|
const X_REP_HINT_VIDEO_MP4 = '[mp4]';
|
|
525
|
+
const X_REP_HINT_WAVEFORM = '[waveform]';
|
|
519
526
|
const AI_TRANSCRIPTION_FOR_VIDEO_SUBTITLES = 'aiTranscriptionForVideoSubtitles';
|
|
527
|
+
const AUDIO_PLAYER_V2 = 'audioPlayerV2.enabled';
|
|
520
528
|
const PDFJS_CSS_UNITS = 96.0 / 72.0; // Should match CSS_UNITS in pdf_viewer.js
|
|
521
529
|
const PDFJS_MAX_AUTO_SCALE = 1.25; // Should match MAX_AUTO_SCALE in pdf_viewer.js
|
|
522
530
|
const PDFJS_WIDTH_PADDING_PX = 40; // Should match SCROLLBAR_PADDING in pdf_viewer.js
|
|
@@ -1010,7 +1018,7 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
|
|
|
1010
1018
|
const CLIENT_NAME = "box-content-preview"; // eslint-disable-line no-undef
|
|
1011
1019
|
const CLIENT_NAME_KEY = 'box_client_name';
|
|
1012
1020
|
const CLIENT_VERSION_KEY = 'box_client_version';
|
|
1013
|
-
const CLIENT_VERSION = "3.
|
|
1021
|
+
const CLIENT_VERSION = "3.91.0"; // eslint-disable-line no-undef
|
|
1014
1022
|
const HEADER_CLIENT_NAME = 'X-Box-Client-Name';
|
|
1015
1023
|
const HEADER_CLIENT_VERSION = 'X-Box-Client-Version';
|
|
1016
1024
|
const PROMISE_MAP = {};
|
|
@@ -4480,7 +4488,7 @@ function getPdfjsWorkerSrc() {
|
|
|
4480
4488
|
|
|
4481
4489
|
/***/ },
|
|
4482
4490
|
|
|
4483
|
-
/***/
|
|
4491
|
+
/***/ 2668
|
|
4484
4492
|
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
4485
4493
|
|
|
4486
4494
|
|
|
@@ -4614,6 +4622,7 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
|
|
|
4614
4622
|
/** Visible window: start/end times, pixels-per-second, and the current zoom. */
|
|
4615
4623
|
function createWaveformViewport({
|
|
4616
4624
|
durationSec,
|
|
4625
|
+
gutterPx = 0,
|
|
4617
4626
|
heightPx,
|
|
4618
4627
|
maxZoom,
|
|
4619
4628
|
scrollLeftPx,
|
|
@@ -4623,11 +4632,16 @@ function createWaveformViewport({
|
|
|
4623
4632
|
const zoom = Number.isFinite(zoomLevel) && zoomLevel > 0 ? zoomLevel : constants/* WAVEFORM_ZOOM_MIN */.LW;
|
|
4624
4633
|
const viewDurationSec = durationSec > 0 ? durationSec / zoom : 0; // seconds visible at this zoom
|
|
4625
4634
|
const pixelsPerSecond = viewDurationSec > 0 && widthPx > 0 ? widthPx / viewDurationSec : 0;
|
|
4626
|
-
const
|
|
4627
|
-
const
|
|
4635
|
+
const leadingGutterPx = Number.isFinite(gutterPx) && gutterPx > 0 ? gutterPx : 0;
|
|
4636
|
+
const unclampedStartSec = pixelsPerSecond > 0 ? (scrollLeftPx - leadingGutterPx) / pixelsPerSecond : 0;
|
|
4637
|
+
const gutterDurationSec = pixelsPerSecond > 0 ? leadingGutterPx / pixelsPerSecond : 0;
|
|
4638
|
+
const latestWindowStartSec = leadingGutterPx > 0 ? durationSec + gutterDurationSec - viewDurationSec : Math.max(0, durationSec - viewDurationSec);
|
|
4639
|
+
const earliestWindowStartSec = leadingGutterPx > 0 ? -gutterDurationSec : 0;
|
|
4640
|
+
const startSec = pixelsPerSecond > 0 ? Math.min(latestWindowStartSec, Math.max(earliestWindowStartSec, unclampedStartSec)) : 0;
|
|
4628
4641
|
return {
|
|
4629
4642
|
durationSec,
|
|
4630
4643
|
endSec: startSec + viewDurationSec,
|
|
4644
|
+
gutterPx: leadingGutterPx,
|
|
4631
4645
|
heightPx,
|
|
4632
4646
|
maxZoom,
|
|
4633
4647
|
pixelsPerSecond,
|
|
@@ -4647,7 +4661,7 @@ function getViewportAtScroll(viewport, scrollLeftPx) {
|
|
|
4647
4661
|
|
|
4648
4662
|
/** True when the visible window has not moved — skip a React state update. */
|
|
4649
4663
|
function viewportEquals(prev, next) {
|
|
4650
|
-
return !!prev && prev.durationSec === next.durationSec && prev.endSec === next.endSec && prev.maxZoom === next.maxZoom && prev.scrollLeftPx === next.scrollLeftPx && prev.startSec === next.startSec && prev.widthPx === next.widthPx && prev.zoomLevel === next.zoomLevel;
|
|
4664
|
+
return !!prev && prev.durationSec === next.durationSec && prev.endSec === next.endSec && prev.gutterPx === next.gutterPx && prev.maxZoom === next.maxZoom && prev.scrollLeftPx === next.scrollLeftPx && prev.startSec === next.startSec && prev.widthPx === next.widthPx && prev.zoomLevel === next.zoomLevel;
|
|
4651
4665
|
}
|
|
4652
4666
|
|
|
4653
4667
|
/** How many CSS pixels from the left of the visible window this time sits. */
|
|
@@ -4694,17 +4708,34 @@ function clampWaveformZoom(zoomLevel, maxZoom = constants/* WAVEFORM_ZOOM_MIN */
|
|
|
4694
4708
|
/** WaveSurfer zoom density. 0 = fit the whole file in the view. */
|
|
4695
4709
|
function getZoomedPixelsPerSecond({
|
|
4696
4710
|
durationSec,
|
|
4711
|
+
isTape = false,
|
|
4697
4712
|
maxZoom = constants/* WAVEFORM_ZOOM_MIN */.LW,
|
|
4698
4713
|
viewWidthPx,
|
|
4699
4714
|
zoomLevel
|
|
4700
4715
|
}) {
|
|
4701
4716
|
const zoom = clampWaveformZoom(zoomLevel, maxZoom);
|
|
4702
|
-
if (
|
|
4717
|
+
if (!(durationSec > 0) || !(viewWidthPx > 0)) {
|
|
4718
|
+
return 0;
|
|
4719
|
+
}
|
|
4720
|
+
if (!isTape && zoom <= constants/* WAVEFORM_ZOOM_MIN */.LW) {
|
|
4703
4721
|
return 0;
|
|
4704
4722
|
}
|
|
4705
4723
|
return viewWidthPx / durationSec * zoom;
|
|
4706
4724
|
}
|
|
4707
4725
|
|
|
4726
|
+
/** Half-view empty space so t=0 and duration can sit under the center pin. */
|
|
4727
|
+
function getTapeGutterPx(widthPx) {
|
|
4728
|
+
return widthPx > 0 ? widthPx / 2 : 0;
|
|
4729
|
+
}
|
|
4730
|
+
|
|
4731
|
+
/** Tape first paint: 10s on screen, or 1× when the file is shorter than that. */
|
|
4732
|
+
function getTapeDefaultZoom(durationSec, maxZoom = constants/* WAVEFORM_ZOOM_MIN */.LW) {
|
|
4733
|
+
if (!(durationSec > 0)) {
|
|
4734
|
+
return constants/* WAVEFORM_ZOOM_MIN */.LW;
|
|
4735
|
+
}
|
|
4736
|
+
return clampWaveformZoom(durationSec / constants/* WAVEFORM_TAPE_DEFAULT_WINDOW_SEC */.Av, maxZoom);
|
|
4737
|
+
}
|
|
4738
|
+
|
|
4708
4739
|
/** Map zoom (1…max) onto the 0–100 slider. */
|
|
4709
4740
|
function sliderValueFromZoom(zoomLevel, maxZoom = constants/* WAVEFORM_ZOOM_MIN */.LW) {
|
|
4710
4741
|
const max = Math.max(constants/* WAVEFORM_ZOOM_MIN */.LW, maxZoom);
|
|
@@ -4725,9 +4756,10 @@ function zoomFromSliderValue(value, maxZoom = constants/* WAVEFORM_ZOOM_MIN */.L
|
|
|
4725
4756
|
return clampWaveformZoom(constants/* WAVEFORM_ZOOM_MIN */.LW + t * (max - constants/* WAVEFORM_ZOOM_MIN */.LW), max);
|
|
4726
4757
|
}
|
|
4727
4758
|
|
|
4728
|
-
/** Max scroll that still shows a full window (no overscroll). */
|
|
4759
|
+
/** Max scroll that still shows a full window (no overscroll). Gutters add empty lead/trail. */
|
|
4729
4760
|
function maxScrollLeft(viewport) {
|
|
4730
|
-
|
|
4761
|
+
const gutterPx = viewport.gutterPx || 0;
|
|
4762
|
+
return Math.max(0, viewport.durationSec * viewport.pixelsPerSecond + 2 * gutterPx - viewport.widthPx);
|
|
4731
4763
|
}
|
|
4732
4764
|
|
|
4733
4765
|
/** Scroll offset that still shows a full window (no overscroll). */
|
|
@@ -4740,7 +4772,7 @@ function clampScrollLeft(scrollLeftPx, viewport) {
|
|
|
4740
4772
|
|
|
4741
4773
|
/** Center this time in the view, clamped so the window still fills the canvas. */
|
|
4742
4774
|
function getCenteredScrollLeft(timeSec, viewport) {
|
|
4743
|
-
return clampScrollLeft(timeSec * viewport.pixelsPerSecond - viewport.widthPx / 2, viewport);
|
|
4775
|
+
return clampScrollLeft(timeSec * viewport.pixelsPerSecond + (viewport.gutterPx || 0) - viewport.widthPx / 2, viewport);
|
|
4744
4776
|
}
|
|
4745
4777
|
|
|
4746
4778
|
/**
|
|
@@ -4748,10 +4780,28 @@ function getCenteredScrollLeft(timeSec, viewport) {
|
|
|
4748
4780
|
* No-op at fit-to-width or when that time is already visible.
|
|
4749
4781
|
*/
|
|
4750
4782
|
function getSeekCameraAction({
|
|
4783
|
+
cameraMode = 'desktop',
|
|
4751
4784
|
timeSec,
|
|
4752
4785
|
viewport
|
|
4753
4786
|
}) {
|
|
4754
|
-
if (
|
|
4787
|
+
if (!(viewport.widthPx > 0) || !(viewport.pixelsPerSecond > 0)) {
|
|
4788
|
+
return {
|
|
4789
|
+
type: 'none'
|
|
4790
|
+
};
|
|
4791
|
+
}
|
|
4792
|
+
if (cameraMode === 'tape') {
|
|
4793
|
+
const scrollLeftPx = getCenteredScrollLeft(timeSec, viewport);
|
|
4794
|
+
if (Math.abs(scrollLeftPx - viewport.scrollLeftPx) < 1) {
|
|
4795
|
+
return {
|
|
4796
|
+
type: 'none'
|
|
4797
|
+
};
|
|
4798
|
+
}
|
|
4799
|
+
return {
|
|
4800
|
+
type: 'jump',
|
|
4801
|
+
scrollLeftPx
|
|
4802
|
+
};
|
|
4803
|
+
}
|
|
4804
|
+
if (viewport.zoomLevel <= constants/* WAVEFORM_ZOOM_MIN */.LW) {
|
|
4755
4805
|
return {
|
|
4756
4806
|
type: 'none'
|
|
4757
4807
|
};
|
|
@@ -4767,6 +4817,23 @@ function getSeekCameraAction({
|
|
|
4767
4817
|
};
|
|
4768
4818
|
}
|
|
4769
4819
|
|
|
4820
|
+
/** Always keep this time under the center pin. Reuses followRight so the camera hook can pin. */
|
|
4821
|
+
function getTapeCameraAction({
|
|
4822
|
+
timeSec,
|
|
4823
|
+
viewport
|
|
4824
|
+
}) {
|
|
4825
|
+
if (!(viewport.widthPx > 0) || !(viewport.pixelsPerSecond > 0)) {
|
|
4826
|
+
return {
|
|
4827
|
+
type: 'none'
|
|
4828
|
+
};
|
|
4829
|
+
}
|
|
4830
|
+
return {
|
|
4831
|
+
isPlayheadPinned: true,
|
|
4832
|
+
scrollLeftPx: getCenteredScrollLeft(timeSec, viewport),
|
|
4833
|
+
type: 'followRight'
|
|
4834
|
+
};
|
|
4835
|
+
}
|
|
4836
|
+
|
|
4770
4837
|
/** Follow inset in CSS px, capped at one third of the view so a narrow player still has room. */
|
|
4771
4838
|
function getFollowInsetPx(widthPx, insetPx = constants/* WAVEFORM_FOLLOW_INSET_PX */.NM) {
|
|
4772
4839
|
if (!(widthPx > 0)) {
|
|
@@ -4784,6 +4851,11 @@ function getPinnedPlayheadLeft(widthPx, insetPx = constants/* WAVEFORM_FOLLOW_IN
|
|
|
4784
4851
|
return `${(widthPx - inset) / widthPx * 100}%`;
|
|
4785
4852
|
}
|
|
4786
4853
|
|
|
4854
|
+
/** CSS left for a playhead locked to the center of the view. */
|
|
4855
|
+
function getTapePinnedPlayheadLeft() {
|
|
4856
|
+
return '50%';
|
|
4857
|
+
}
|
|
4858
|
+
|
|
4787
4859
|
/** CSS left % of the playhead from the left of the visible window. */
|
|
4788
4860
|
function timeLeftPercent(timeSec, durationSec, viewport) {
|
|
4789
4861
|
if (viewport.widthPx > 0 && viewport.pixelsPerSecond > 0) {
|
|
@@ -4798,12 +4870,19 @@ function timeLeftPercent(timeSec, durationSec, viewport) {
|
|
|
4798
4870
|
* Off-screen at play start jumps in; off-screen right while playing keeps following.
|
|
4799
4871
|
*/
|
|
4800
4872
|
function getPlayheadCameraAction({
|
|
4873
|
+
cameraMode = 'desktop',
|
|
4801
4874
|
insetPx = constants/* WAVEFORM_FOLLOW_INSET_PX */.NM,
|
|
4802
4875
|
isPlaying,
|
|
4803
4876
|
playJustStarted,
|
|
4804
4877
|
timeSec,
|
|
4805
4878
|
viewport
|
|
4806
4879
|
}) {
|
|
4880
|
+
if (cameraMode === 'tape') {
|
|
4881
|
+
return getTapeCameraAction({
|
|
4882
|
+
timeSec,
|
|
4883
|
+
viewport
|
|
4884
|
+
});
|
|
4885
|
+
}
|
|
4807
4886
|
if (!isPlaying || viewport.zoomLevel <= constants/* WAVEFORM_ZOOM_MIN */.LW || !(viewport.widthPx > 0) || !(viewport.pixelsPerSecond > 0)) {
|
|
4808
4887
|
return {
|
|
4809
4888
|
type: 'none'
|
|
@@ -4843,6 +4922,46 @@ function getPlayheadCameraAction({
|
|
|
4843
4922
|
type: 'none'
|
|
4844
4923
|
};
|
|
4845
4924
|
}
|
|
4925
|
+
;// ./src/lib/viewers/media/waveform/useTapeWaveform.ts
|
|
4926
|
+
|
|
4927
|
+
|
|
4928
|
+
function isIPadNavigator(tapeNavigator) {
|
|
4929
|
+
return /iPad/i.test(tapeNavigator.userAgent || '') || tapeNavigator.platform === 'MacIntel' && (tapeNavigator.maxTouchPoints || 0) > 1;
|
|
4930
|
+
}
|
|
4931
|
+
function isCoarsePrimaryPointer(tapeWindow) {
|
|
4932
|
+
return typeof tapeWindow.matchMedia === 'function' && tapeWindow.matchMedia(constants/* TAPE_POINTER_MEDIA_QUERY */.Le).matches;
|
|
4933
|
+
}
|
|
4934
|
+
|
|
4935
|
+
/**
|
|
4936
|
+
* Tape vs desktop camera: coarse primary pointer (finger) or iPad
|
|
4937
|
+
* (including iPadOS reporting itself as Mac). Not width, not hasTouch,
|
|
4938
|
+
* not Browser.isMobile().
|
|
4939
|
+
*/
|
|
4940
|
+
function isTapeWaveformInput(tapeWindow = window) {
|
|
4941
|
+
return isCoarsePrimaryPointer(tapeWindow) || isIPadNavigator(tapeWindow.navigator);
|
|
4942
|
+
}
|
|
4943
|
+
|
|
4944
|
+
/** Subscribe to the pointer media query; re-read the iPad heuristic on change. */
|
|
4945
|
+
function useTapeWaveform() {
|
|
4946
|
+
const [isTape, setIsTape] = (0,external_react_.useState)(() => typeof window !== 'undefined' && isTapeWaveformInput(window));
|
|
4947
|
+
(0,external_react_.useEffect)(() => {
|
|
4948
|
+
if (typeof window.matchMedia !== 'function') {
|
|
4949
|
+
setIsTape(isTapeWaveformInput(window));
|
|
4950
|
+
return undefined;
|
|
4951
|
+
}
|
|
4952
|
+
const mediaQuery = window.matchMedia(constants/* TAPE_POINTER_MEDIA_QUERY */.Le);
|
|
4953
|
+
const sync = () => {
|
|
4954
|
+
setIsTape(isTapeWaveformInput(window));
|
|
4955
|
+
};
|
|
4956
|
+
sync();
|
|
4957
|
+
mediaQuery.addEventListener('change', sync);
|
|
4958
|
+
return () => mediaQuery.removeEventListener('change', sync);
|
|
4959
|
+
}, []);
|
|
4960
|
+
return isTape;
|
|
4961
|
+
}
|
|
4962
|
+
// EXTERNAL MODULE: ./node_modules/classnames/index.js
|
|
4963
|
+
var classnames = __webpack_require__(2485);
|
|
4964
|
+
var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames);
|
|
4846
4965
|
// EXTERNAL MODULE: ./src/lib/viewers/controls/media/markers/helpers/useDismissableMarkerSelection.ts
|
|
4847
4966
|
var useDismissableMarkerSelection = __webpack_require__(2378);
|
|
4848
4967
|
// EXTERNAL MODULE: ./src/lib/viewers/controls/media/markers/helpers/buildClusters.ts
|
|
@@ -4862,6 +4981,7 @@ var utils = __webpack_require__(5346);
|
|
|
4862
4981
|
|
|
4863
4982
|
|
|
4864
4983
|
|
|
4984
|
+
|
|
4865
4985
|
const WAVEFORM_MARKER_SIZE_PX = 20;
|
|
4866
4986
|
function hasMappedWindow(viewport) {
|
|
4867
4987
|
return !!viewport && Number.isFinite(viewport.startSec) && viewport.endSec > viewport.startSec;
|
|
@@ -4900,11 +5020,12 @@ function clustersByExactTime(markers, durationSec) {
|
|
|
4900
5020
|
function WaveformCommentMarkers({
|
|
4901
5021
|
commentMarkers,
|
|
4902
5022
|
durationSec,
|
|
5023
|
+
isTape = false,
|
|
4903
5024
|
onCommentMarkerClick,
|
|
4904
5025
|
selectedId: hostSelectedId = null,
|
|
4905
5026
|
viewport = null
|
|
4906
5027
|
}) {
|
|
4907
|
-
const trackRef = (0,external_react_.useRef)(null);
|
|
5028
|
+
const trackRef = (0,external_react_.useRef)(null); // width source for clustering overlapping badges
|
|
4908
5029
|
const {
|
|
4909
5030
|
containerRef,
|
|
4910
5031
|
selectMarker,
|
|
@@ -4913,7 +5034,7 @@ function WaveformCommentMarkers({
|
|
|
4913
5034
|
const [trackWidth, setTrackWidth] = (0,external_react_.useState)(0);
|
|
4914
5035
|
const canShowTrack = durationSec > 0 && commentMarkers.length > 0;
|
|
4915
5036
|
const zoomLevel = viewport?.zoomLevel ?? constants/* WAVEFORM_ZOOM_MIN */.LW;
|
|
4916
|
-
const
|
|
5037
|
+
const alignsMarkersToVisibleWindow = hasMappedWindow(viewport) && (viewport.zoomLevel > constants/* WAVEFORM_ZOOM_MIN */.LW || isTape);
|
|
4917
5038
|
(0,external_react_.useLayoutEffect)(() => {
|
|
4918
5039
|
if (!canShowTrack) {
|
|
4919
5040
|
setTrackWidth(0);
|
|
@@ -4949,7 +5070,10 @@ function WaveformCommentMarkers({
|
|
|
4949
5070
|
}
|
|
4950
5071
|
return /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
4951
5072
|
ref: containerRef,
|
|
4952
|
-
className:
|
|
5073
|
+
className: classnames_default()('bp-WaveformCommentMarkers', {
|
|
5074
|
+
'bp-WaveformCommentMarkers--tape': isTape,
|
|
5075
|
+
'bp-WaveformCommentMarkers--zoomed': alignsMarkersToVisibleWindow
|
|
5076
|
+
}),
|
|
4953
5077
|
"data-testid": "bp-waveform-comment-markers"
|
|
4954
5078
|
}, /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
4955
5079
|
ref: trackRef,
|
|
@@ -4958,7 +5082,10 @@ function WaveformCommentMarkers({
|
|
|
4958
5082
|
const marker = cluster.markers[0];
|
|
4959
5083
|
const isGroup = cluster.markers.length > 1;
|
|
4960
5084
|
const isSelected = cluster.markers.some(entry => entry.id === selectedId);
|
|
4961
|
-
const className =
|
|
5085
|
+
const className = classnames_default()('bp-WaveformCommentMarkers-marker', {
|
|
5086
|
+
'bp-WaveformCommentMarkers-marker--group': isGroup,
|
|
5087
|
+
'bp-WaveformCommentMarkers-marker--selected': isSelected
|
|
5088
|
+
});
|
|
4962
5089
|
const left = `${markerLeftPercent(marker.time, durationSec, viewport)}%`;
|
|
4963
5090
|
if (isGroup) {
|
|
4964
5091
|
return /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
@@ -4996,6 +5123,8 @@ function WaveformCommentMarkers({
|
|
|
4996
5123
|
}));
|
|
4997
5124
|
})));
|
|
4998
5125
|
}
|
|
5126
|
+
;// external "react-dom"
|
|
5127
|
+
|
|
4999
5128
|
;// ./node_modules/wavesurfer.js/dist/wavesurfer.esm.js
|
|
5000
5129
|
function t(t,e,i,n){return new(i||(i=Promise))((function(s,r){function o(t){try{l(n.next(t))}catch(t){r(t)}}function a(t){try{l(n.throw(t))}catch(t){r(t)}}function l(t){var e;t.done?s(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(o,a)}l((n=n.apply(t,e||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class e{constructor(){this.listeners={}}on(t,e,i){if(this.listeners[t]||(this.listeners[t]=new Set),null==i?void 0:i.once){const i=(...n)=>{this.un(t,i),e(...n)};return this.listeners[t].add(i),()=>this.un(t,i)}return this.listeners[t].add(e),()=>this.un(t,e)}un(t,e){var i;null===(i=this.listeners[t])||void 0===i||i.delete(e)}once(t,e){return this.on(t,e,{once:!0})}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}const i={decode:function(e,i){return t(this,void 0,void 0,(function*(){const t=new AudioContext({sampleRate:i});try{return yield t.decodeAudioData(e)}finally{"closed"!==t.state&&(yield t.close().catch((()=>{})))}}))},createBuffer:function(t,e){if(!t||0===t.length)throw new Error("channelData must be a non-empty array");if(e<=0)throw new Error("duration must be greater than 0");if("number"==typeof t[0]&&(t=[t]),!t[0]||0===t[0].length)throw new Error("channelData must contain non-empty channel arrays");!function(t){const e=t[0];if(e.some((t=>t>1||t<-1))){const i=e.length;let n=0;for(let t=0;t<i;t++){const i=Math.abs(e[t]);i>n&&(n=i)}for(const e of t)for(let t=0;t<i;t++)e[t]/=n}}(t);const i=t.map((t=>t instanceof Float32Array?t:Float32Array.from(t)));return{duration:e,length:i[0].length,sampleRate:i[0].length/e,numberOfChannels:i.length,getChannelData:t=>{const e=i[t];if(!e)throw new Error(`Channel ${t} not found`);return e},copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}};function n(t,e){const i=e.xmlns?document.createElementNS(e.xmlns,t):document.createElement(t);for(const[t,s]of Object.entries(e))if("children"===t&&s)for(const[t,e]of Object.entries(s))e instanceof Node?i.appendChild(e):"string"==typeof e?i.appendChild(document.createTextNode(e)):i.appendChild(n(t,e));else"style"===t?Object.assign(i.style,s):"textContent"===t?i.textContent=s:i.setAttribute(t,s.toString());return i}function s(t,e,i){const s=n(t,e||{});return null==i||i.appendChild(s),s}function r(t){return t instanceof HTMLElement||"object"==typeof t&&null!==t&&t.nodeType===Node.ELEMENT_NODE&&"object"==typeof t.style}var o=Object.freeze({__proto__:null,createElement:s,default:s,isHTMLElement:r});const a={fetchBlob:function(e,i,n){return t(this,void 0,void 0,(function*(){var s;const r=yield fetch(e,n);if(r.status>=400)throw new Error(`Failed to fetch ${e}: ${r.status} (${r.statusText})`);return function(e,i,n){t(this,void 0,void 0,(function*(){var t;if(!e.body||!e.headers)return;const s=e.body.getReader(),r=Number(e.headers.get("Content-Length"))||0;let o=0;const a=()=>{s.cancel()};if(n){if(n.aborted)return void s.cancel();n.addEventListener("abort",a,{once:!0})}try{for(;;){const e=yield s.read();if(e.done)break;if(o+=(null===(t=e.value)||void 0===t?void 0:t.length)||0,r>0){const t=Math.round(o/r*100);i(t)}}}catch(t){if(t instanceof DOMException&&"AbortError"===t.name)return;console.warn("Progress tracking error:",t)}finally{n&&n.removeEventListener("abort",a)}}))}(r.clone(),i,null!==(s=null==n?void 0:n.signal)&&void 0!==s?s:void 0),r.blob()}))}};function l(t){let e=t;const i=new Set;return{get value(){return e},set(t){Object.is(e,t)||(e=t,i.forEach((t=>t(e))))},update(t){this.set(t(e))},subscribe:t=>(i.add(t),()=>i.delete(t))}}function h(t,e){const i=l(t());return e.forEach((e=>e.subscribe((()=>{const e=t();Object.is(i.value,e)||i.set(e)})))),{get value(){return i.value},subscribe:t=>i.subscribe(t)}}function c(t,e){let i;const n=()=>{i&&(i(),i=void 0),i=t()},s=e.map((t=>t.subscribe(n)));return n(),()=>{i&&(i(),i=void 0),s.forEach((t=>t()))}}class u extends e{get isPlayingSignal(){return this._isPlaying}get currentTimeSignal(){return this._currentTime}get durationSignal(){return this._duration}get volumeSignal(){return this._volume}get mutedSignal(){return this._muted}get playbackRateSignal(){return this._playbackRate}get seekingSignal(){return this._seeking}constructor(t){super(),this.isExternalMedia=!1,this._ownBlobUrl=null,this.reactiveMediaEventCleanups=[],t.media?(this.media=t.media,this.isExternalMedia=!0):this.media=document.createElement("audio"),this._isPlaying=l(!1),this._currentTime=l(0),this._duration=l(0),this._volume=l(this.media.volume),this._muted=l(this.media.muted),this._playbackRate=l(this.media.playbackRate||1),this._seeking=l(!1),this.setupReactiveMediaEvents(),t.mediaControls&&(this.media.controls=!0),t.autoplay&&(this.media.autoplay=!0),null!=t.playbackRate&&this.onMediaEvent("canplay",(()=>{null!=t.playbackRate&&(this.media.playbackRate=t.playbackRate)}),{once:!0})}setupReactiveMediaEvents(){this.reactiveMediaEventCleanups.push(this.onMediaEvent("play",(()=>{this._isPlaying.set(!0)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("pause",(()=>{this._isPlaying.set(!1)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("ended",(()=>{this._isPlaying.set(!1)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("timeupdate",(()=>{this._currentTime.set(this.media.currentTime)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("durationchange",(()=>{this._duration.set(this.media.duration||0)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("loadedmetadata",(()=>{this._duration.set(this.media.duration||0)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("seeking",(()=>{this._seeking.set(!0)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("seeked",(()=>{this._seeking.set(!1)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("volumechange",(()=>{this._volume.set(this.media.volume),this._muted.set(this.media.muted)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("ratechange",(()=>{this._playbackRate.set(this.media.playbackRate)})))}onMediaEvent(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e,i)}getSrc(){return this.media.currentSrc||this.media.src||""}revokeSrc(){this._ownBlobUrl&&(URL.revokeObjectURL(this._ownBlobUrl),this._ownBlobUrl=null)}canPlayType(t){return""!==this.media.canPlayType(t)}setSrc(t,e){const i=this.getSrc();if(t&&i===t)return;this.revokeSrc();const n=e instanceof Blob&&(this.canPlayType(e.type)||!t)?URL.createObjectURL(e):t;if(n!==t&&(this._ownBlobUrl=n),i&&this.media.removeAttribute("src"),n||t)try{this.media.src=n}catch(e){this.media.src=t}}destroy(){this.reactiveMediaEventCleanups.forEach((t=>t())),this.reactiveMediaEventCleanups=[],this.revokeSrc(),this.unAll(),this.isExternalMedia||(this.media.pause(),this.media.removeAttribute("src"),this.media.load(),this.media.remove())}setMediaElement(t){this.reactiveMediaEventCleanups.forEach((t=>t())),this.reactiveMediaEventCleanups=[],this.media=t,this.setupReactiveMediaEvents()}play(){return t(this,void 0,void 0,(function*(){try{return yield this.media.play()}catch(t){if(t instanceof DOMException&&"AbortError"===t.name)return;throw t}}))}pause(){this.media.pause()}isPlaying(){return!this.media.paused&&!this.media.ended}setTime(t){this.media.currentTime=Math.max(0,Math.min(t,this.getDuration()))}getDuration(){return this.media.duration}getCurrentTime(){return this.media.currentTime}getVolume(){return this.media.volume}setVolume(t){this.media.volume=t}getMuted(){return this.media.muted}setMuted(t){this.media.muted=t}getPlaybackRate(){return this.media.playbackRate}isSeeking(){return this.media.seeking}setPlaybackRate(t,e){null!=e&&(this.media.preservesPitch=e),this.media.playbackRate=t}getMediaElement(){return this.media}setSinkId(t){return this.media.setSinkId(t)}}function d({maxTop:t,maxBottom:e,halfHeight:i,vScale:n,barMinHeight:s=0,barAlign:r}){let o=Math.round(t*i*n);let a=o+Math.round(e*i*n)||1;return a<s&&(a=s,r||(o=a/2)),{topHeight:o,totalHeight:a}}function p({barAlign:t,halfHeight:e,topHeight:i,totalHeight:n,canvasHeight:s}){return"top"===t?0:"bottom"===t?s-n:e-i}function m(t,e,i){const n=e-t.left,s=i-t.top;return[n/t.width,s/t.height]}function g(t){return Boolean(t.barWidth||t.barGap||t.barAlign)}function f(t,e){if(!g(e))return t;const i=e.barWidth||.5,n=i+(e.barGap||i/2);return 0===n?t:Math.floor(t/n)*n}function v({scrollLeft:t,totalWidth:e,numCanvases:i}){if(0===e)return[0];const n=t/e,s=Math.floor(n*i);return[s-1,s,s+1]}function b(t){const e=t._cleanup;"function"==typeof e&&e()}function y(t){const e=l({scrollLeft:t.scrollLeft,scrollWidth:t.scrollWidth,clientWidth:t.clientWidth}),i=h((()=>function(t){const{scrollLeft:e,scrollWidth:i,clientWidth:n}=t;if(0===i)return{startX:0,endX:1};const s=e/i,r=(e+n)/i;return{startX:Math.max(0,Math.min(1,s)),endX:Math.max(0,Math.min(1,r))}}(e.value)),[e]),n=h((()=>function(t){return{left:t.scrollLeft,right:t.scrollLeft+t.clientWidth}}(e.value)),[e]),s=()=>{e.set({scrollLeft:t.scrollLeft,scrollWidth:t.scrollWidth,clientWidth:t.clientWidth})};t.addEventListener("scroll",s,{passive:!0});return{scrollData:e,percentages:i,bounds:n,cleanup:()=>{t.removeEventListener("scroll",s),b(e)}}}class C extends e{constructor(t,e){super(),this.timeouts=[],this.isScrollable=!1,this.audioData=null,this.resizeObserver=null,this.lastContainerWidth=0,this.isDragging=!1,this.subscriptions=[],this.unsubscribeOnScroll=[],this.dragStream=null,this.scrollStream=null,this.containerInlinePadding=0,this.onClickWrapper=t=>{const e=this.wrapper.getBoundingClientRect(),[i,n]=m(e,t.clientX,t.clientY);this.emit("click",i,n)},this.onDblClickWrapper=t=>{const e=this.wrapper.getBoundingClientRect(),[i,n]=m(e,t.clientX,t.clientY);this.emit("dblclick",i,n)},this.subscriptions=[],this.options=t;const i=this.parentFromOptionsContainer(t.container);this.parent=i;const[n,s]=this.initHtml();i.appendChild(n),this.container=n,this.scrollContainer=s.querySelector(".scroll"),this.wrapper=s.querySelector(".wrapper"),this.canvasWrapper=s.querySelector(".canvases"),this.progressWrapper=s.querySelector(".progress"),this.cursor=s.querySelector(".cursor"),this.calculateInlinePadding(),e&&s.appendChild(e),this.initEvents()}parentFromOptionsContainer(t){let e;if("string"==typeof t?e=document.querySelector(t):r(t)&&(e=t),!e)throw new Error("Container not found");return e}initEvents(){this.wrapper.addEventListener("click",this.onClickWrapper),this.wrapper.addEventListener("dblclick",this.onDblClickWrapper),!0!==this.options.dragToSeek&&"object"!=typeof this.options.dragToSeek||this.initDrag(),this.scrollStream=y(this.scrollContainer);const t=c((()=>{const{startX:t,endX:e}=this.scrollStream.percentages.value,{left:i,right:n}=this.scrollStream.bounds.value;this.emit("scroll",t,e,i,n)}),[this.scrollStream.percentages,this.scrollStream.bounds]);if(this.subscriptions.push(t),"function"==typeof ResizeObserver){const t=this.createDelay(100);this.resizeObserver=new ResizeObserver((()=>{t().then((()=>this.onContainerResize())).catch((()=>{}))})),this.resizeObserver.observe(this.scrollContainer)}}onContainerResize(){const t=this.parent.clientWidth;this.calculateInlinePadding(),t===this.lastContainerWidth&&"auto"!==this.options.height||(this.lastContainerWidth=t,this.reRender(),this.emit("resize"))}initDrag(){if(this.dragStream)return;this.dragStream=function(t,e={}){const{threshold:i=3,mouseButton:n=0,touchDelay:s=100}=e,r=l(null),o=new Map,a=matchMedia("(pointer: coarse)").matches;let h=()=>{};const c=e=>{if(e.button!==n)return;if(o.has(e.pointerId))return;if(o.set(e.pointerId,e),o.size>1)return;const l=e.pointerId;let c=e.clientX,u=e.clientY,d=!1;const p=Date.now(),m=t.getBoundingClientRect(),{left:g,top:f}=m,v=t=>{if(t.pointerId!==l)return;if(t.defaultPrevented||o.size>1)return;if(a&&Date.now()-p<s)return;const e=t.clientX,n=t.clientY,h=e-c,m=n-u;(d||Math.abs(h)>i||Math.abs(m)>i)&&(t.preventDefault(),t.stopPropagation(),d||(r.set({type:"start",x:c-g,y:u-f}),d=!0),r.set({type:"move",x:e-g,y:n-f,deltaX:h,deltaY:m}),c=e,u=n)},b=t=>{if(o.delete(t.pointerId)){if(t.pointerId===l&&d){const e=t.clientX,i=t.clientY;r.set({type:"end",x:e-g,y:i-f})}0===o.size&&h()}},y=t=>{t.relatedTarget&&t.relatedTarget!==document.documentElement||b(t)},C=t=>{d&&(t.stopPropagation(),t.preventDefault())},S=t=>{t.defaultPrevented||o.size>1||d&&t.preventDefault()};document.addEventListener("pointermove",v),document.addEventListener("pointerup",b),document.addEventListener("pointerout",y),document.addEventListener("pointercancel",y),document.addEventListener("touchmove",S,{passive:!1}),document.addEventListener("click",C,{capture:!0}),h=()=>{document.removeEventListener("pointermove",v),document.removeEventListener("pointerup",b),document.removeEventListener("pointerout",y),document.removeEventListener("pointercancel",y),document.removeEventListener("touchmove",S),setTimeout((()=>{document.removeEventListener("click",C,{capture:!0})}),10)}};return t.addEventListener("pointerdown",c),{signal:r,cleanup:()=>{h(),t.removeEventListener("pointerdown",c),o.clear(),b(r)}}}(this.wrapper);const t=c((()=>{const t=this.dragStream.signal.value;if(!t)return;const e=this.wrapper.getBoundingClientRect().width,i=(n=t.x/e)<0?0:n>1?1:n;var n;"start"===t.type?(this.isDragging=!0,this.emit("dragstart",i)):"move"===t.type?this.emit("drag",i):"end"===t.type&&(this.isDragging=!1,this.emit("dragend",i))}),[this.dragStream.signal]);this.subscriptions.push(t)}calculateInlinePadding(){const{paddingLeft:t,paddingRight:e}=getComputedStyle(this.scrollContainer),i=parseFloat(t)+parseFloat(e);this.containerInlinePadding=Number.isNaN(i)?0:i}initHtml(){const t=document.createElement("div"),e=t.attachShadow({mode:"open"}),i=this.options.cspNonce&&"string"==typeof this.options.cspNonce?this.options.cspNonce.replace(/"/g,""):"";return e.innerHTML=`\n <style${i?` nonce="${i}"`:""}>\n :host {\n user-select: none;\n min-width: 1px;\n }\n :host audio {\n display: block;\n width: 100%;\n }\n :host .scroll {\n overflow-x: auto;\n overflow-y: hidden;\n width: 100%;\n position: relative;\n }\n :host .noScrollbar {\n scrollbar-color: transparent;\n scrollbar-width: none;\n }\n :host .noScrollbar::-webkit-scrollbar {\n display: none;\n -webkit-appearance: none;\n }\n :host .wrapper {\n position: relative;\n overflow: visible;\n z-index: 2;\n }\n :host .canvases {\n min-height: ${this.getHeight(this.options.height,this.options.splitChannels)}px;\n pointer-events: none;\n }\n :host .canvases > div {\n position: relative;\n }\n :host canvas {\n display: block;\n position: absolute;\n top: 0;\n image-rendering: pixelated;\n }\n :host .progress {\n pointer-events: none;\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n width: 0;\n height: 100%;\n overflow: hidden;\n }\n :host .progress > div {\n position: relative;\n }\n :host .cursor {\n pointer-events: none;\n position: absolute;\n z-index: 5;\n top: 0;\n left: 0;\n height: 100%;\n border-radius: 2px;\n }\n </style>\n\n <div class="scroll" part="scroll">\n <div class="wrapper" part="wrapper">\n <div class="canvases" part="canvases"></div>\n <div class="progress" part="progress"></div>\n <div class="cursor" part="cursor"></div>\n </div>\n </div>\n `,[t,e]}setOptions(t){var e;if(this.options.container!==t.container){const e=this.parentFromOptionsContainer(t.container);e.appendChild(this.container),this.parent=e}!0===t.dragToSeek||"object"==typeof this.options.dragToSeek?this.initDrag():(null===(e=this.dragStream)||void 0===e||e.cleanup(),this.dragStream=null),this.options=t,this.reRender()}getWrapper(){return this.wrapper}getWidth(){return this.scrollContainer.clientWidth-this.containerInlinePadding}getScroll(){return this.scrollContainer.scrollLeft}setScroll(t){this.scrollContainer.scrollLeft=t}setScrollPercentage(t){const{scrollWidth:e}=this.scrollContainer,i=e*t;this.setScroll(i)}destroy(){var t;this.wrapper.removeEventListener("click",this.onClickWrapper),this.wrapper.removeEventListener("dblclick",this.onDblClickWrapper),this.timeouts.forEach((t=>t())),this.timeouts=[],this.subscriptions.forEach((t=>t())),this.container.remove(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),null===(t=this.unsubscribeOnScroll)||void 0===t||t.forEach((t=>t())),this.unsubscribeOnScroll=[],this.dragStream&&(this.dragStream.cleanup(),this.dragStream=null),this.scrollStream&&(this.scrollStream.cleanup(),this.scrollStream=null)}createDelay(t=10){let e,i;const n=()=>{e&&(clearTimeout(e),e=void 0),i&&(i(),i=void 0)};return this.timeouts.push(n),()=>new Promise(((s,r)=>{n(),i=r,e=setTimeout((()=>{e=void 0,i=void 0,s()}),t)}))}getHeight(t,e){var i;const n=(null===(i=this.audioData)||void 0===i?void 0:i.numberOfChannels)||1;return function({optionsHeight:t,optionsSplitChannels:e,parentHeight:i,numberOfChannels:n,defaultHeight:s=128}){if(null==t)return s;const r=Number(t);if(!isNaN(r))return r;if("auto"===t){const t=i||s;return(null==e?void 0:e.every((t=>!t.overlay)))?t/n:t}return s}({optionsHeight:t,optionsSplitChannels:e,parentHeight:this.parent.clientHeight,numberOfChannels:n,defaultHeight:128})}convertColorValues(t,e){return function(t,e,i){if(!Array.isArray(t))return t||"";if(0===t.length)return"#999";if(t.length<2)return t[0]||"";const n=document.createElement("canvas"),s=n.getContext("2d");if(!s)return t[0]||"";const r=i||n.height*e,o=s.createLinearGradient(0,0,0,r),a=1/(t.length-1);return t.forEach(((t,e)=>{o.addColorStop(e*a,t)})),o}(t,this.getPixelRatio(),null==e?void 0:e.canvas.height)}getPixelRatio(){return t=window.devicePixelRatio,Math.max(1,t||1);// removed by dead control flow
|
|
5001
5130
|
var t; }renderBarWaveform(t,e,i,n){const{width:s,height:r}=i.canvas,{halfHeight:o,barWidth:a,barRadius:l,barIndexScale:h,barSpacing:c,barMinHeight:u}=function({width:t,height:e,length:i,options:n,pixelRatio:s}){const r=e/2,o=n.barWidth?n.barWidth*s:1,a=n.barGap?n.barGap*s:n.barWidth?o/2:0,l=o+a||1;return{halfHeight:r,barWidth:o,barGap:a,barRadius:n.barRadius||0,barMinHeight:n.barMinHeight?n.barMinHeight*s:0,barIndexScale:i>0?t/l/i:0,barSpacing:l}}({width:s,height:r,length:(t[0]||[]).length,options:e,pixelRatio:this.getPixelRatio()}),m=function({channelData:t,barIndexScale:e,barSpacing:i,barWidth:n,halfHeight:s,vScale:r,canvasHeight:o,barAlign:a,barMinHeight:l}){const h=t[0]||[],c=t[1]||h,u=h.length,m=[];let g=0,f=0,v=0;for(let t=0;t<=u;t++){const u=Math.round(t*e);if(u>g){const{topHeight:t,totalHeight:e}=d({maxTop:f,maxBottom:v,halfHeight:s,vScale:r,barMinHeight:l,barAlign:a}),h=p({barAlign:a,halfHeight:s,topHeight:t,totalHeight:e,canvasHeight:o});m.push({x:g*i,y:h,width:n,height:e}),g=u,f=0,v=0}const b=Math.abs(h[t]||0),y=Math.abs(c[t]||0);b>f&&(f=b),y>v&&(v=y)}return m}({channelData:t,barIndexScale:h,barSpacing:c,barWidth:a,halfHeight:o,vScale:n,canvasHeight:r,barAlign:e.barAlign,barMinHeight:u});i.beginPath();for(const t of m)l&&"roundRect"in i?i.roundRect(t.x,t.y,t.width,t.height,l):i.rect(t.x,t.y,t.width,t.height);i.fill(),i.closePath()}renderLineWaveform(t,e,i,n){const{width:s,height:r}=i.canvas,o=function({channelData:t,width:e,height:i,vScale:n}){const s=i/2,r=t[0]||[];return[r,t[1]||r].map(((t,i)=>{const r=t.length,o=r?e/r:0,a=s,l=0===i?-1:1,h=[{x:0,y:a}];let c=0,u=0;for(let e=0;e<=r;e++){const i=Math.round(e*o);if(i>c){const t=a+(Math.round(u*s*n)||1)*l;h.push({x:c,y:t}),c=i,u=0}const r=Math.abs(t[e]||0);r>u&&(u=r)}return h.push({x:c,y:a}),h}))}({channelData:t,width:s,height:r,vScale:n});i.beginPath();for(const t of o)if(t.length){i.moveTo(t[0].x,t[0].y);for(let e=1;e<t.length;e++){const n=t[e];i.lineTo(n.x,n.y)}}i.fill(),i.closePath()}renderWaveform(t,e,i){if(i.fillStyle=this.convertColorValues(e.waveColor,i),e.renderFunction)return void e.renderFunction(t,i);const n=function({channelData:t,barHeight:e,normalize:i,maxPeak:n}){var s;const r=e||1;if(!i)return r;const o=t[0];if(!o||0===o.length)return r;let a=null!=n?n:0;if(!n)for(let t=0;t<o.length;t++){const e=null!==(s=o[t])&&void 0!==s?s:0,i=Math.abs(e);i>a&&(a=i)}return a?r/a:r}({channelData:t,barHeight:e.barHeight,normalize:e.normalize,maxPeak:e.maxPeak});g(e)?this.renderBarWaveform(t,e,i,n):this.renderLineWaveform(t,e,i,n)}renderSingleCanvas(t,e,i,n,s,r,o){const a=this.getPixelRatio(),l=document.createElement("canvas");l.width=Math.round(i*a),l.height=Math.round(n*a),l.style.width=`${i}px`,l.style.height=`${n}px`,l.style.left=`${Math.round(s)}px`,r.appendChild(l);const h=l.getContext("2d");if(e.renderFunction?(h.fillStyle=this.convertColorValues(e.waveColor,h),e.renderFunction(t,h)):this.renderWaveform(t,e,h),l.width>0&&l.height>0){const t=l.cloneNode(),i=t.getContext("2d");i.drawImage(l,0,0),i.globalCompositeOperation="source-in",i.fillStyle=this.convertColorValues(e.progressColor,i),i.fillRect(0,0,l.width,l.height),o.appendChild(t)}}renderMultiCanvas(t,e,i,n,s,r){const o=this.getPixelRatio(),{clientWidth:a}=this.scrollContainer,l=i/o,h=function({clientWidth:t,totalWidth:e,options:i}){return f(Math.min(8e3,t,e),i)}({clientWidth:a,totalWidth:l,options:e});let c={};if(0===h)return;const u=i=>{if(i<0||i>=d)return;if(c[i])return;c[i]=!0;const o=i*h;let a=Math.min(l-o,h);if(a=f(a,e),a<=0)return;const u=function({channelData:t,offset:e,clampedWidth:i,totalWidth:n}){return t.map((t=>{const s=Math.floor(e/n*t.length),r=Math.floor((e+i)/n*t.length);return t.slice(s,r)}))}({channelData:t,offset:o,clampedWidth:a,totalWidth:l});this.renderSingleCanvas(u,e,a,n,o,s,r)},d=Math.ceil(l/h);if(!this.isScrollable){for(let t=0;t<d;t++)u(t);return}if(v({scrollLeft:this.scrollContainer.scrollLeft,totalWidth:l,numCanvases:d}).forEach((t=>u(t))),d>1){const t=this.on("scroll",(()=>{const{scrollLeft:t}=this.scrollContainer;Object.keys(c).length>10&&(s.innerHTML="",r.innerHTML="",c={}),v({scrollLeft:t,totalWidth:l,numCanvases:d}).forEach((t=>u(t)))}));this.unsubscribeOnScroll.push(t)}}renderChannel(t,e,i,n){var{overlay:s}=e,r=function(t,e){var i={};for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&e.indexOf(n)<0&&(i[n]=t[n]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var s=0;for(n=Object.getOwnPropertySymbols(t);s<n.length;s++)e.indexOf(n[s])<0&&Object.prototype.propertyIsEnumerable.call(t,n[s])&&(i[n[s]]=t[n[s]])}return i}(e,["overlay"]);const o=document.createElement("div"),a=this.getHeight(r.height,r.splitChannels);o.style.height=`${a}px`,s&&n>0&&(o.style.marginTop=`-${a}px`),this.canvasWrapper.style.minHeight=`${a}px`,this.canvasWrapper.appendChild(o);const l=o.cloneNode();this.progressWrapper.appendChild(l),this.renderMultiCanvas(t,r,i,a,o,l)}render(e){return t(this,void 0,void 0,(function*(){var t;this.timeouts.forEach((t=>t())),this.timeouts=[],this.unsubscribeOnScroll.forEach((t=>t())),this.unsubscribeOnScroll=[],this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="",null!=this.options.width&&(this.scrollContainer.style.width="number"==typeof this.options.width?`${this.options.width}px`:this.options.width);const i=this.getPixelRatio(),n=this.scrollContainer.clientWidth-this.containerInlinePadding,{scrollWidth:s,isScrollable:r,useParentWidth:o,width:a}=function({duration:t,minPxPerSec:e=0,parentWidth:i,fillParent:n,pixelRatio:s}){const r=Math.ceil(t*e),o=r>i,a=Boolean(n&&!o);return{scrollWidth:r,isScrollable:o,useParentWidth:a,width:(a?i:r)*s}}({duration:e.duration,minPxPerSec:this.options.minPxPerSec||0,parentWidth:n,fillParent:this.options.fillParent,pixelRatio:i});if(this.isScrollable=r,this.wrapper.style.width=o?"100%":`${s}px`,this.scrollContainer.style.overflowX=this.isScrollable?"auto":"hidden",this.scrollContainer.classList.toggle("noScrollbar",!!this.options.hideScrollbar),this.cursor.style.backgroundColor=`${this.options.cursorColor||this.options.progressColor}`,this.cursor.style.width=`${this.options.cursorWidth}px`,this.audioData=e,this.emit("render"),this.options.splitChannels)for(let i=0;i<e.numberOfChannels;i++){const n=Object.assign(Object.assign({},this.options),null===(t=this.options.splitChannels)||void 0===t?void 0:t[i]);this.renderChannel([e.getChannelData(i)],n,a,i)}else{const t=[e.getChannelData(0)];e.numberOfChannels>1&&t.push(e.getChannelData(1)),this.renderChannel(t,this.options,a,0)}Promise.resolve().then((()=>this.emit("rendered")))}))}reRender(){if(this.unsubscribeOnScroll.forEach((t=>t())),this.unsubscribeOnScroll=[],!this.audioData)return;const{scrollWidth:t}=this.scrollContainer,{right:e}=this.progressWrapper.getBoundingClientRect();if(this.render(this.audioData),!this.isScrollable&&this.scrollContainer.scrollLeft)this.scrollContainer.scrollLeft=0;else if(this.isScrollable&&t!==this.scrollContainer.scrollWidth){const{right:t}=this.progressWrapper.getBoundingClientRect(),i=function(t){const e=2*t;return(e<0?Math.floor(e):Math.ceil(e))/2}(t-e);this.scrollContainer.scrollLeft+=i}}zoom(t){this.options.minPxPerSec=t,this.reRender()}scrollIntoView(t,e=!1){var i;const{scrollLeft:n,scrollWidth:s,clientWidth:r}=this.scrollContainer,o=t*s,a=n,l=n+r,h=r/2;if(this.isDragging){const t=30;o+t>l?this.scrollContainer.scrollLeft+=t:o-t<a&&(this.scrollContainer.scrollLeft-=t)}else{(o<a||o>l)&&(this.scrollContainer.scrollLeft=o-(this.options.autoCenter?h:0));const t=o-n-h;if(e&&this.options.autoCenter&&t>0){const e=null===(i=this.audioData)||void 0===i?void 0:i.duration;if(void 0===e||e<=0)return void(this.scrollContainer.scrollLeft+=t);const n=s/e;this.scrollContainer.scrollLeft+=n<=600?Math.min(t,10):t}}}renderProgress(t,e){if(isNaN(t))return;const i=100*t;this.canvasWrapper.style.clipPath=`polygon(${i}% 0%, 100% 0%, 100% 100%, ${i}% 100%)`,this.progressWrapper.style.width=`${i}%`,this.cursor.style.left=`${i}%`,this.cursor.style.transform=this.options.cursorWidth?`translateX(-${t*this.options.cursorWidth}px)`:"",this.isScrollable&&this.options.autoScroll&&this.audioData&&this.audioData.duration>0&&this.scrollIntoView(t,e)}exportImage(e,i,n){return t(this,void 0,void 0,(function*(){const t=this.canvasWrapper.querySelectorAll("canvas");if(!t.length)throw new Error("No waveform data");if("dataURL"===n){const n=Array.from(t).map((t=>t.toDataURL(e,i)));return Promise.resolve(n)}return Promise.all(Array.from(t).map((t=>new Promise(((n,s)=>{t.toBlob((t=>{t?n(t):s(new Error("Could not export image"))}),e,i)})))))}))}}class S extends e{constructor(){super(...arguments),this.animationFrameId=null,this.isRunning=!1}start(){if(this.isRunning)return;this.isRunning=!0;const t=()=>{this.isRunning&&(this.emit("tick"),this.animationFrameId=requestAnimationFrame(t))};t()}stop(){this.isRunning=!1,null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null)}destroy(){this.stop(),this.unAll()}}class E extends e{constructor(t){super(),this.bufferNode=null,this.playStartTime=0,this.playbackPosition=0,this._muted=!1,this._playbackRate=1,this._duration=void 0,this.buffer=null,this.currentSrc="",this.paused=!0,this.crossOrigin=null,this.seeking=!1,this.autoplay=!1,this.addEventListener=this.on,this.removeEventListener=this.un,this._destroyed=!1,function(){const t=globalThis.navigator;if(null==t?void 0:t.audioSession)try{t.audioSession.type="playback"}catch(t){console.warn("Setting navigator.audioSession.type failed:",t)}}(),this.audioContext=t||new AudioContext,this.gainNode=this.audioContext.createGain(),this.gainNode.connect(this.audioContext.destination)}load(){return t(this,void 0,void 0,(function*(){}))}remove(){this.destroy()}destroy(){if(!this._destroyed){if(this._destroyed=!0,this.currentSrc="",this.bufferNode){this.bufferNode.onended=null;try{this.bufferNode.stop()}catch(t){}this.bufferNode.disconnect(),this.bufferNode=null}this.gainNode.disconnect(),"function"==typeof this.audioContext.close&&Promise.resolve(this.audioContext.close.call(this.audioContext)).catch((()=>{})),this.buffer=null,this.unAll()}}get src(){return this.currentSrc}set src(t){if(this.currentSrc=t,this._duration=void 0,!t)return this.buffer=null,void this.emit("emptied");fetch(t).then((e=>{if(e.status>=400)throw new Error(`Failed to fetch ${t}: ${e.status} (${e.statusText})`);return e.arrayBuffer()})).then((e=>this.currentSrc!==t?null:this.audioContext.decodeAudioData(e))).then((e=>{this.currentSrc===t&&(this.buffer=e,this.emit("loadedmetadata"),this.emit("canplay"),this.autoplay&&this.play())})).catch((t=>{console.error("WebAudioPlayer load error:",t)}))}_play(){if(!this.paused)return;this.paused=!1,this.bufferNode&&(this.bufferNode.onended=null,this.bufferNode.disconnect()),this.bufferNode=this.audioContext.createBufferSource(),this.buffer&&(this.bufferNode.buffer=this.buffer),this.bufferNode.playbackRate.value=this._playbackRate,this.bufferNode.connect(this.gainNode);let t=this.playbackPosition;(t>=this.duration||t<0)&&(t=0,this.playbackPosition=0),this.bufferNode.start(this.audioContext.currentTime,t),this.playStartTime=this.audioContext.currentTime,this.bufferNode.onended=()=>{!this.paused&&this.duration-this.currentTime<.01&&(this.pause(),this.emit("ended"))}}_pause(){if(this.paused=!0,this.bufferNode){this.bufferNode.onended=null;try{this.bufferNode.stop()}catch(t){}}this.playbackPosition+=(this.audioContext.currentTime-this.playStartTime)*this._playbackRate}play(){return t(this,void 0,void 0,(function*(){this.paused&&(this._play(),this.emit("play"))}))}pause(){this.paused||(this._pause(),this.emit("pause"))}stopAt(t){const e=(t-this.currentTime)/this._playbackRate,i=this.bufferNode;null==i||i.stop(this.audioContext.currentTime+e),null==i||i.addEventListener("ended",(()=>{i===this.bufferNode&&(this.bufferNode=null,this.pause(),this.playbackPosition=Math.min(t,this.duration),this.emit("timeupdate"))}),{once:!0})}setSinkId(e){return t(this,void 0,void 0,(function*(){return this.audioContext.setSinkId(e)}))}get playbackRate(){return this._playbackRate}set playbackRate(t){const e=!this.paused;e&&this._pause(),this._playbackRate=t,e&&this._play(),this.bufferNode&&(this.bufferNode.playbackRate.value=t)}get currentTime(){return this.paused?this.playbackPosition:this.playbackPosition+(this.audioContext.currentTime-this.playStartTime)*this._playbackRate}set currentTime(t){const e=!this.paused;e&&this._pause(),this.playbackPosition=t,e&&this._play(),this.emit("seeking"),this.emit("timeupdate")}get duration(){var t,e;return null!==(t=this._duration)&&void 0!==t?t:(null===(e=this.buffer)||void 0===e?void 0:e.duration)||0}set duration(t){this._duration=t}get volume(){return this.gainNode.gain.value}set volume(t){this.gainNode.gain.value=t,this.emit("volumechange")}get muted(){return this._muted}set muted(t){this._muted!==t&&(this._muted=t,this._muted?this.gainNode.disconnect():this.gainNode.connect(this.audioContext.destination))}canPlayType(t){return/^(audio|video)\//.test(t)}getGainNode(){return this.gainNode}getChannelData(){const t=[];if(!this.buffer)return t;const e=this.buffer.numberOfChannels;for(let i=0;i<e;i++)t.push(this.buffer.getChannelData(i));return t}removeAttribute(t){switch(t){case"src":this.src="";break;case"playbackRate":this.playbackRate=0;break;case"currentTime":this.currentTime=0;break;case"duration":this.duration=0;break;case"volume":this.volume=0;break;case"muted":this.muted=!1}}}const P={waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,dragToSeek:!1,autoScroll:!0,autoCenter:!0,sampleRate:8e3};class w extends u{static create(t){return new w(t)}getState(){return this.wavesurferState}getRenderer(){return this.renderer}constructor(t){const e=t.media||("WebAudio"===t.backend?new E:void 0);super({media:e,mediaControls:t.mediaControls,autoplay:t.autoplay,playbackRate:t.audioRate}),this.plugins=[],this.decodedData=null,this.stopAtPosition=null,this.subscriptions=[],this.mediaSubscriptions=[],this.abortController=null,this._isDestroyed=!1,this._loadVersion=0,this.reactiveCleanups=[],this.options=Object.assign({},P,t);const{state:i,actions:n}=function(t){var e,i,n,s,r,o;const a=null!==(e=null==t?void 0:t.currentTime)&&void 0!==e?e:l(0),c=null!==(i=null==t?void 0:t.duration)&&void 0!==i?i:l(0),u=null!==(n=null==t?void 0:t.isPlaying)&&void 0!==n?n:l(!1),d=null!==(s=null==t?void 0:t.isSeeking)&&void 0!==s?s:l(!1),p=null!==(r=null==t?void 0:t.volume)&&void 0!==r?r:l(1),m=null!==(o=null==t?void 0:t.playbackRate)&&void 0!==o?o:l(1),g=l(null),f=l(null),v=l(""),b=l(0),y=l(0),C=h((()=>!u.value),[u]),S=h((()=>null!==g.value),[g]),E=h((()=>S.value&&c.value>0),[S,c]),P=h((()=>a.value),[a]),w=h((()=>c.value>0?a.value/c.value:0),[a,c]);return{state:{currentTime:a,duration:c,isPlaying:u,isPaused:C,isSeeking:d,volume:p,playbackRate:m,audioBuffer:g,peaks:f,url:v,zoom:b,scrollPosition:y,canPlay:S,isReady:E,progress:P,progressPercent:w},actions:{setCurrentTime:t=>{const e=Math.max(0,Math.min(c.value||1/0,t));a.set(e)},setDuration:t=>{c.set(Math.max(0,t))},setPlaying:t=>{u.set(t)},setSeeking:t=>{d.set(t)},setVolume:t=>{const e=Math.max(0,Math.min(1,t));p.set(e)},setPlaybackRate:t=>{const e=Math.max(.1,Math.min(16,t));m.set(e)},setAudioBuffer:t=>{g.set(t),t&&c.set(t.duration)},setPeaks:t=>{f.set(t)},setUrl:t=>{v.set(t)},setZoom:t=>{b.set(Math.max(0,t))},setScrollPosition:t=>{y.set(Math.max(0,t))}}}}({isPlaying:this.isPlayingSignal,currentTime:this.currentTimeSignal,duration:this.durationSignal,volume:this.volumeSignal,playbackRate:this.playbackRateSignal,isSeeking:this.seekingSignal});this.wavesurferState=i,this.wavesurferActions=n,this.timer=new S;const s=e?void 0:this.getMediaElement();this.renderer=new C(this.options,s),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReactiveState(),this.initPlugins();const r=this.options.url||this.getSrc()||"";Promise.resolve().then((()=>{this.emit("init");const{peaks:t,duration:e}=this.options;(r||t&&e)&&this.load(r,t,e).catch((()=>{}))}))}updateProgress(t=this.getCurrentTime()){return this.renderer.renderProgress(t/this.getDuration(),this.isPlaying()),t}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{if(!this.isSeeking()){const t=this.updateProgress();if(this.emit("timeupdate",t),this.emit("audioprocess",t),null!=this.stopAtPosition&&this.isPlaying()&&t>=this.stopAtPosition){const t=this.stopAtPosition;this.pause(),this.setTime(t)}}})))}initReactiveState(){this.reactiveCleanups.push(function(t,e){const i=[];i.push(c((()=>{const i=t.isPlaying.value;e.emit(i?"play":"pause")}),[t.isPlaying])),i.push(c((()=>{const i=t.currentTime.value;e.emit("timeupdate",i),t.isPlaying.value&&e.emit("audioprocess",i)}),[t.currentTime,t.isPlaying])),i.push(c((()=>{t.isSeeking.value&&e.emit("seeking",t.currentTime.value)}),[t.isSeeking,t.currentTime]));let n=!1;i.push(c((()=>{t.isReady.value&&!n&&(n=!0,e.emit("ready",t.duration.value))}),[t.isReady,t.duration])),i.push(c((()=>{null===t.audioBuffer.value&&(n=!1)}),[t.audioBuffer]));let s=!1;return i.push(c((()=>{const i=t.isPlaying.value,n=t.currentTime.value,r=t.duration.value,o=r>0&&n>=r;s&&!i&&o&&e.emit("finish"),s=i&&o}),[t.isPlaying,t.currentTime,t.duration])),i.push(c((()=>{const i=t.zoom.value;i>0&&e.emit("zoom",i)}),[t.zoom])),()=>{i.forEach((t=>t()))}}(this.wavesurferState,{emit:this.emit.bind(this)}))}initPlayerEvents(){this.isPlaying()&&(this.emit("play"),this.timer.start()),this.mediaSubscriptions.push(this.onMediaEvent("timeupdate",(()=>{const t=this.updateProgress();this.emit("timeupdate",t)})),this.onMediaEvent("play",(()=>{this.emit("play"),this.timer.start()})),this.onMediaEvent("pause",(()=>{this.emit("pause"),this.timer.stop(),this.stopAtPosition=null})),this.onMediaEvent("emptied",(()=>{this.timer.stop(),this.stopAtPosition=null})),this.onMediaEvent("ended",(()=>{this.emit("timeupdate",this.getDuration()),this.emit("finish"),this.stopAtPosition=null})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",this.getCurrentTime())})),this.onMediaEvent("error",(()=>{var t;this.emit("error",null!==(t=this.getMediaElement().error)&&void 0!==t?t:new Error("Media error")),this.stopAtPosition=null})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",((t,e)=>{this.options.interact&&(this.seekTo(t),this.emit("interaction",t*this.getDuration()),this.emit("click",t,e))})),this.renderer.on("dblclick",((t,e)=>{this.emit("dblclick",t,e)})),this.renderer.on("scroll",((t,e,i,n)=>{const s=this.getDuration();this.emit("scroll",t*s,e*s,i,n)})),this.renderer.on("render",(()=>{this.emit("redraw")})),this.renderer.on("rendered",(()=>{this.emit("redrawcomplete")})),this.renderer.on("dragstart",(t=>{this.emit("dragstart",t)})),this.renderer.on("dragend",(t=>{this.emit("dragend",t)})),this.renderer.on("resize",(()=>{this.emit("resize")})));{let t;const e=this.renderer.on("drag",(e=>{var i;if(!this.options.interact)return;this.renderer.renderProgress(e),clearTimeout(t);let n=0;const s=this.options.dragToSeek;this.isPlaying()?n=0:!0===s?n=200:s&&"object"==typeof s&&(n=null!==(i=s.debounceTime)&&void 0!==i?i:200),t=setTimeout((()=>{this.seekTo(e)}),n),this.emit("interaction",e*this.getDuration()),this.emit("drag",e)}));this.subscriptions.push((()=>{clearTimeout(t),e()}))}}initPlugins(){var t;(null===(t=this.options.plugins)||void 0===t?void 0:t.length)&&this.options.plugins.forEach((t=>{this.registerPlugin(t)}))}unsubscribePlayerEvents(){this.mediaSubscriptions.forEach((t=>t())),this.mediaSubscriptions=[]}setOptions(t){this.options=Object.assign({},this.options,t),t.duration&&!t.peaks&&(this.decodedData=i.createBuffer(this.exportPeaks(),t.duration)),t.peaks&&t.duration&&(this.decodedData=i.createBuffer(t.peaks,t.duration)),this.renderer.setOptions(this.options),t.audioRate&&this.setPlaybackRate(t.audioRate),null!=t.mediaControls&&(this.getMediaElement().controls=t.mediaControls)}registerPlugin(t){if(this.plugins.includes(t))return t;t._init(this),this.plugins.push(t);const e=t.once("destroy",(()=>{this.plugins=this.plugins.filter((e=>e!==t)),this.subscriptions=this.subscriptions.filter((t=>t!==e))}));return this.subscriptions.push(e),t}unregisterPlugin(t){this.plugins=this.plugins.filter((e=>e!==t)),t.destroy()}getWrapper(){return this.renderer.getWrapper()}getWidth(){return this.renderer.getWidth()}getScroll(){return this.renderer.getScroll()}setScroll(t){return this.renderer.setScroll(t)}setScrollTime(t){const e=t/this.getDuration();this.renderer.setScrollPercentage(e)}getActivePlugins(){return this.plugins}loadAudio(e,n,s,r){return t(this,void 0,void 0,(function*(){var t;const o=++this._loadVersion;if(this._isDestroyed=!1,this.emit("load",e),!this.options.media&&this.isPlaying()&&this.pause(),this.decodedData=null,this.stopAtPosition=null,null===(t=this.abortController)||void 0===t||t.abort(),this.abortController=null,!n&&!s){const t=this.options.fetchParams||{};window.AbortController&&!t.signal&&(this.abortController=new AbortController,t.signal=this.abortController.signal);const i=t=>this.emit("loading",t);if(n=yield a.fetchBlob(e,i,t),this._isDestroyed||o!==this._loadVersion)return;const s=this.options.blobMimeType;s&&(n=new Blob([n],{type:s}))}if(this._isDestroyed||o!==this._loadVersion)return;this.setSrc(e,n);const l=yield new Promise((t=>{const e=r||this.getDuration();e?t(e):this.mediaSubscriptions.push(this.onMediaEvent("loadedmetadata",(()=>t(this.getDuration())),{once:!0}))}));if(!this._isDestroyed&&o===this._loadVersion){if(!e&&!n){const t=this.getMediaElement();t instanceof E&&(t.duration=l)}if(s)this.decodedData=i.createBuffer(s,l||0);else if(n){const t=yield n.arrayBuffer();if(this._isDestroyed||o!==this._loadVersion)return;this.decodedData=yield i.decode(t,this.options.sampleRate)}this._isDestroyed||o!==this._loadVersion||(this.decodedData&&(this.emit("decode",this.getDuration()),this.renderer.render(this.decodedData)),this.emit("ready",this.getDuration()))}}))}load(e,i,n){return t(this,void 0,void 0,(function*(){try{return yield this.loadAudio(e,void 0,i,n)}catch(t){throw this.emit("error",t),t}}))}loadBlob(e,i,n){return t(this,void 0,void 0,(function*(){try{return yield this.loadAudio("",e,i,n)}catch(t){throw this.emit("error",t),t}}))}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(t),this.emit("zoom",t)}getDecodedData(){return this.decodedData}exportPeaks({channels:t=2,maxLength:e=8e3,precision:i=1e4}={}){if(!this.decodedData)throw new Error("The audio has not been decoded yet");const n=Math.min(t,this.decodedData.numberOfChannels),s=[];for(let t=0;t<n;t++){const n=this.decodedData.getChannelData(t),r=[],o=n.length/e;for(let t=0;t<e;t++){const e=n.slice(Math.floor(t*o),Math.ceil((t+1)*o));let s=0;for(let t=0;t<e.length;t++){const i=e[t];Math.abs(i)>Math.abs(s)&&(s=i)}r.push(Math.round(s*i)/i)}s.push(r)}return s}getDuration(){let t=super.getDuration()||0;return 0!==t&&t!==1/0||!this.decodedData||(t=this.decodedData.duration),t}toggleInteraction(t){this.options.interact=t}setTime(t){this.stopAtPosition=null,super.setTime(t),this.updateProgress(t),this.emit("timeupdate",t)}seekTo(t){const e=this.getDuration()*t;this.setTime(e)}play(e,i){const n=Object.create(null,{play:{get:()=>super.play}});return t(this,void 0,void 0,(function*(){null!=e&&this.setTime(e);const t=yield n.play.call(this);return null!=i&&(this.media instanceof E?this.media.stopAt(i):this.stopAtPosition=i),t}))}playPause(){return t(this,void 0,void 0,(function*(){return this.isPlaying()?this.pause():this.play()}))}stop(){this.pause(),this.setTime(0)}skip(t){this.setTime(this.getCurrentTime()+t)}empty(){this.load("",[[0]],.001)}setMediaElement(t){this.unsubscribePlayerEvents(),super.setMediaElement(t),this.initPlayerEvents()}exportImage(){return t(this,arguments,void 0,(function*(t="image/png",e=1,i="dataURL"){return this.renderer.exportImage(t,e,i)}))}destroy(){var t;this._isDestroyed=!0,this.emit("destroy"),null===(t=this.abortController)||void 0===t||t.abort(),this.plugins.forEach((t=>t.destroy())),this.subscriptions.forEach((t=>t())),this.unsubscribePlayerEvents(),this.reactiveCleanups.forEach((t=>t())),this.reactiveCleanups=[],this.timer.destroy(),this.renderer.destroy(),super.destroy()}}w.BasePlugin=class extends e{constructor(t){super(),this.subscriptions=[],this.isDestroyed=!1,this.options=t}onInit(){}_init(t){this.isDestroyed&&(this.subscriptions=[],this.isDestroyed=!1),this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.subscriptions=[],this.isDestroyed=!0,this.wavesurfer=void 0}},w.dom=o;
|
|
@@ -5080,18 +5209,31 @@ function getBufferedProgress(bufferedRange, durationSec) {
|
|
|
5080
5209
|
return clampTo0And1(bufferedRange.end(bufferedRange.length - 1) / durationSec);
|
|
5081
5210
|
}
|
|
5082
5211
|
|
|
5212
|
+
/** Played bars: rest token on desktop, hover-played (brightest) on tape. Tape never uses hover or buffer fills. */
|
|
5213
|
+
function getPlayedWaveformColor(isTape = false) {
|
|
5214
|
+
return isTape ? WAVEFORM_COLOR_HOVER_PLAYED : WAVEFORM_COLOR_PLAYED;
|
|
5215
|
+
}
|
|
5216
|
+
|
|
5083
5217
|
/**
|
|
5084
5218
|
* progressColor paints left of the playhead (clipped by wavesurfer).
|
|
5085
5219
|
* waveColor paints right of the playhead, including buffered vs not-yet-buffered.
|
|
5086
5220
|
*/
|
|
5087
5221
|
function getWaveformFills({
|
|
5088
5222
|
bufferProgress,
|
|
5089
|
-
hoverProgress
|
|
5223
|
+
hoverProgress,
|
|
5224
|
+
isTape = false
|
|
5090
5225
|
}) {
|
|
5091
5226
|
const buffer = clampTo0And1(bufferProgress);
|
|
5227
|
+
const playedColor = getPlayedWaveformColor(isTape);
|
|
5228
|
+
if (isTape) {
|
|
5229
|
+
return {
|
|
5230
|
+
progressColor: playedColor,
|
|
5231
|
+
waveColor: WAVEFORM_COLOR_UNPLAYED
|
|
5232
|
+
};
|
|
5233
|
+
}
|
|
5092
5234
|
if (hoverProgress == null) {
|
|
5093
5235
|
return {
|
|
5094
|
-
progressColor:
|
|
5236
|
+
progressColor: playedColor,
|
|
5095
5237
|
waveColor: gradientStopsFromColorRanges([{
|
|
5096
5238
|
color: WAVEFORM_COLOR_UNPLAYED,
|
|
5097
5239
|
end: buffer,
|
|
@@ -5268,6 +5410,7 @@ function prefersReducedMotion() {
|
|
|
5268
5410
|
* every frame. WaveformView owns WaveSurfer, zoom, and the media rAF loop.
|
|
5269
5411
|
*/
|
|
5270
5412
|
function usePlayheadCamera({
|
|
5413
|
+
cameraMode = 'desktop',
|
|
5271
5414
|
mediaElRef,
|
|
5272
5415
|
onViewportCommit,
|
|
5273
5416
|
playheadRef,
|
|
@@ -5276,6 +5419,8 @@ function usePlayheadCamera({
|
|
|
5276
5419
|
}) {
|
|
5277
5420
|
const onViewportCommitRef = (0,external_react_.useRef)(onViewportCommit);
|
|
5278
5421
|
onViewportCommitRef.current = onViewportCommit;
|
|
5422
|
+
const cameraModeRef = (0,external_react_.useRef)(cameraMode);
|
|
5423
|
+
cameraModeRef.current = cameraMode;
|
|
5279
5424
|
const programmaticScrollRef = (0,external_react_.useRef)(false);
|
|
5280
5425
|
const jumpAnimationRef = (0,external_react_.useRef)(0);
|
|
5281
5426
|
const lastCameraScrollRef = (0,external_react_.useRef)(null);
|
|
@@ -5286,6 +5431,8 @@ function usePlayheadCamera({
|
|
|
5286
5431
|
const scrollSettleTimerRef = (0,external_react_.useRef)(0);
|
|
5287
5432
|
const applyRef = (0,external_react_.useRef)(null);
|
|
5288
5433
|
const isFollowPinned = (0,external_react_.useCallback)(() => isFollowPinnedRef.current, []);
|
|
5434
|
+
const isUserPanning = (0,external_react_.useCallback)(() => userIsScrollingRef.current, []);
|
|
5435
|
+
const isTape = (0,external_react_.useCallback)(() => cameraModeRef.current === 'tape', []);
|
|
5289
5436
|
const cancelJump = (0,external_react_.useCallback)(() => {
|
|
5290
5437
|
if (!jumpAnimationRef.current) {
|
|
5291
5438
|
return;
|
|
@@ -5314,15 +5461,22 @@ function usePlayheadCamera({
|
|
|
5314
5461
|
const onSeek = (0,external_react_.useCallback)(timeSec => {
|
|
5315
5462
|
mediaTimeRef.current = timeSec;
|
|
5316
5463
|
cancelJump();
|
|
5317
|
-
|
|
5318
|
-
|
|
5464
|
+
if (!isTape()) {
|
|
5465
|
+
clearFollowPin();
|
|
5466
|
+
}
|
|
5467
|
+
}, [cancelJump, clearFollowPin, isTape]);
|
|
5319
5468
|
|
|
5320
5469
|
/** Unpin and hold follow so zoom setScroll (slider center or pinch origin) is not stolen. */
|
|
5321
5470
|
const onZoom = (0,external_react_.useCallback)(() => {
|
|
5322
5471
|
cancelJump();
|
|
5472
|
+
if (isTape()) {
|
|
5473
|
+
isFollowPinnedRef.current = true;
|
|
5474
|
+
holdFollowUntilInsetRef.current = false;
|
|
5475
|
+
return;
|
|
5476
|
+
}
|
|
5323
5477
|
isFollowPinnedRef.current = false;
|
|
5324
5478
|
holdFollowUntilInsetRef.current = true;
|
|
5325
|
-
}, [cancelJump]);
|
|
5479
|
+
}, [cancelJump, isTape]);
|
|
5326
5480
|
const applyScrollLeft = (0,external_react_.useCallback)((scrollLeftPx, shouldCommitState) => {
|
|
5327
5481
|
const wavesurfer = wavesurferRef.current;
|
|
5328
5482
|
if (!wavesurfer || !wavesurfer.setScroll) {
|
|
@@ -5350,8 +5504,8 @@ function usePlayheadCamera({
|
|
|
5350
5504
|
if (!playhead || !isPinned || !(viewport.widthPx > 0)) {
|
|
5351
5505
|
return;
|
|
5352
5506
|
}
|
|
5353
|
-
playhead.style.left = getPinnedPlayheadLeft(viewport.widthPx);
|
|
5354
|
-
}, []);
|
|
5507
|
+
playhead.style.left = isTape() ? getTapePinnedPlayheadLeft() : getPinnedPlayheadLeft(viewport.widthPx);
|
|
5508
|
+
}, [isTape]);
|
|
5355
5509
|
const apply = (0,external_react_.useCallback)((timeSec, playJustStarted = false) => {
|
|
5356
5510
|
mediaTimeRef.current = timeSec;
|
|
5357
5511
|
const wavesurfer = wavesurferRef.current;
|
|
@@ -5359,7 +5513,7 @@ function usePlayheadCamera({
|
|
|
5359
5513
|
if (!wavesurfer) {
|
|
5360
5514
|
return;
|
|
5361
5515
|
}
|
|
5362
|
-
if (viewport.zoomLevel <= constants/* WAVEFORM_ZOOM_MIN */.LW) {
|
|
5516
|
+
if (!isTape() && viewport.zoomLevel <= constants/* WAVEFORM_ZOOM_MIN */.LW) {
|
|
5363
5517
|
clearFollowPin();
|
|
5364
5518
|
return;
|
|
5365
5519
|
}
|
|
@@ -5371,12 +5525,13 @@ function usePlayheadCamera({
|
|
|
5371
5525
|
}
|
|
5372
5526
|
const liveViewport = getViewportAtScroll(viewport, getScrollLeft(wavesurfer, viewport.scrollLeftPx));
|
|
5373
5527
|
const action = getPlayheadCameraAction({
|
|
5528
|
+
cameraMode: cameraModeRef.current,
|
|
5374
5529
|
isPlaying: true,
|
|
5375
5530
|
playJustStarted,
|
|
5376
5531
|
timeSec,
|
|
5377
5532
|
viewport: liveViewport
|
|
5378
5533
|
});
|
|
5379
|
-
if (holdFollowUntilInsetRef.current && !playJustStarted) {
|
|
5534
|
+
if (!isTape() && holdFollowUntilInsetRef.current && !playJustStarted) {
|
|
5380
5535
|
if (action.type !== 'none') {
|
|
5381
5536
|
return;
|
|
5382
5537
|
}
|
|
@@ -5408,6 +5563,7 @@ function usePlayheadCamera({
|
|
|
5408
5563
|
const tick = now => {
|
|
5409
5564
|
const t = Math.min(1, (now - start) / constants/* WAVEFORM_PLAYHEAD_JUMP_MS */.Bq);
|
|
5410
5565
|
const liveAction = getPlayheadCameraAction({
|
|
5566
|
+
cameraMode: cameraModeRef.current,
|
|
5411
5567
|
isPlaying: true,
|
|
5412
5568
|
playJustStarted: true,
|
|
5413
5569
|
timeSec: mediaTimeRef.current,
|
|
@@ -5424,7 +5580,7 @@ function usePlayheadCamera({
|
|
|
5424
5580
|
apply(mediaTimeRef.current, false);
|
|
5425
5581
|
};
|
|
5426
5582
|
jumpAnimationRef.current = window.requestAnimationFrame(tick);
|
|
5427
|
-
}, [applyScrollLeft, cancelJump, clearFollowPin, pinFollowPlayhead, playheadRef, viewportRef, wavesurferRef]);
|
|
5583
|
+
}, [applyScrollLeft, cancelJump, clearFollowPin, isTape, pinFollowPlayhead, playheadRef, viewportRef, wavesurferRef]);
|
|
5428
5584
|
applyRef.current = apply;
|
|
5429
5585
|
|
|
5430
5586
|
/**
|
|
@@ -5447,9 +5603,12 @@ function usePlayheadCamera({
|
|
|
5447
5603
|
}
|
|
5448
5604
|
releaseUserPanHold();
|
|
5449
5605
|
cancelJump();
|
|
5450
|
-
|
|
5606
|
+
if (!isTape()) {
|
|
5607
|
+
isFollowPinnedRef.current = false;
|
|
5608
|
+
}
|
|
5451
5609
|
const liveViewport = getViewportAtScroll(viewportRef.current, getScrollLeft(wavesurfer, viewportRef.current.scrollLeftPx));
|
|
5452
5610
|
const action = getSeekCameraAction({
|
|
5611
|
+
cameraMode: cameraModeRef.current,
|
|
5453
5612
|
timeSec,
|
|
5454
5613
|
viewport: liveViewport
|
|
5455
5614
|
});
|
|
@@ -5469,7 +5628,7 @@ function usePlayheadCamera({
|
|
|
5469
5628
|
applyScrollLeft(from + (to - from) * (1 - (1 - t) * (1 - t)), false);
|
|
5470
5629
|
const playhead = playheadRef.current;
|
|
5471
5630
|
if (playhead) {
|
|
5472
|
-
playhead.style.left = timeLeftPercent(mediaTimeRef.current, viewportRef.current.durationSec, viewportRef.current);
|
|
5631
|
+
playhead.style.left = isTape() ? getTapePinnedPlayheadLeft() : timeLeftPercent(mediaTimeRef.current, viewportRef.current.durationSec, viewportRef.current);
|
|
5473
5632
|
}
|
|
5474
5633
|
if (t < 1) {
|
|
5475
5634
|
jumpAnimationRef.current = window.requestAnimationFrame(tick);
|
|
@@ -5479,7 +5638,7 @@ function usePlayheadCamera({
|
|
|
5479
5638
|
applyScrollLeft(to, true);
|
|
5480
5639
|
};
|
|
5481
5640
|
jumpAnimationRef.current = window.requestAnimationFrame(tick);
|
|
5482
|
-
}, [applyScrollLeft, cancelJump, playheadRef, releaseUserPanHold, viewportRef, wavesurferRef]);
|
|
5641
|
+
}, [applyScrollLeft, cancelJump, isTape, playheadRef, releaseUserPanHold, viewportRef, wavesurferRef]);
|
|
5483
5642
|
const handleScroll = (0,external_react_.useCallback)(onUserPan => {
|
|
5484
5643
|
const wavesurfer = wavesurferRef.current;
|
|
5485
5644
|
if (!wavesurfer) {
|
|
@@ -5491,8 +5650,28 @@ function usePlayheadCamera({
|
|
|
5491
5650
|
return;
|
|
5492
5651
|
}
|
|
5493
5652
|
viewportRef.current = getViewportAtScroll(viewportRef.current, scrollLeftPx);
|
|
5494
|
-
const timeSec = readMediaTime();
|
|
5495
5653
|
const playhead = playheadRef.current;
|
|
5654
|
+
if (isTape()) {
|
|
5655
|
+
if (playhead) {
|
|
5656
|
+
playhead.style.left = getTapePinnedPlayheadLeft();
|
|
5657
|
+
}
|
|
5658
|
+
const pinTimeSec = Math.min(viewportRef.current.durationSec, Math.max(0, timeFromPositionPx(viewportRef.current.widthPx / 2, viewportRef.current)));
|
|
5659
|
+
userIsScrollingRef.current = true;
|
|
5660
|
+
window.clearTimeout(scrollSettleTimerRef.current);
|
|
5661
|
+
scrollSettleTimerRef.current = window.setTimeout(() => {
|
|
5662
|
+
userIsScrollingRef.current = false;
|
|
5663
|
+
scrollSettleTimerRef.current = 0;
|
|
5664
|
+
if (!mediaElRef.current || mediaElRef.current.paused) {
|
|
5665
|
+
return;
|
|
5666
|
+
}
|
|
5667
|
+
applyRef.current?.(readMediaTime(), false);
|
|
5668
|
+
}, constants/* WAVEFORM_FOLLOW_SCROLL_SETTLE_MS */.Zs);
|
|
5669
|
+
cancelJump();
|
|
5670
|
+
programmaticScrollRef.current = false;
|
|
5671
|
+
onUserPan(pinTimeSec);
|
|
5672
|
+
return;
|
|
5673
|
+
}
|
|
5674
|
+
const timeSec = readMediaTime();
|
|
5496
5675
|
if (playhead) {
|
|
5497
5676
|
playhead.style.left = timeLeftPercent(timeSec, viewportRef.current.durationSec, viewportRef.current);
|
|
5498
5677
|
}
|
|
@@ -5511,7 +5690,7 @@ function usePlayheadCamera({
|
|
|
5511
5690
|
isFollowPinnedRef.current = false;
|
|
5512
5691
|
programmaticScrollRef.current = false;
|
|
5513
5692
|
onUserPan(timeSec);
|
|
5514
|
-
}, [cancelJump, isUserPan, mediaElRef, playheadRef, readMediaTime, viewportRef, wavesurferRef]);
|
|
5693
|
+
}, [cancelJump, isTape, isUserPan, mediaElRef, playheadRef, readMediaTime, viewportRef, wavesurferRef]);
|
|
5515
5694
|
(0,external_react_.useEffect)(() => () => {
|
|
5516
5695
|
releaseUserPanHold();
|
|
5517
5696
|
cancelJump();
|
|
@@ -5523,6 +5702,7 @@ function usePlayheadCamera({
|
|
|
5523
5702
|
clearFollowPin,
|
|
5524
5703
|
handleScroll,
|
|
5525
5704
|
isFollowPinned,
|
|
5705
|
+
isUserPanning,
|
|
5526
5706
|
onSeek,
|
|
5527
5707
|
onZoom,
|
|
5528
5708
|
releaseUserPanHold,
|
|
@@ -5548,6 +5728,8 @@ function WaveformView_toPrimitive(t, r) { if ("object" != typeof t || !t) return
|
|
|
5548
5728
|
|
|
5549
5729
|
|
|
5550
5730
|
|
|
5731
|
+
|
|
5732
|
+
|
|
5551
5733
|
/** Pointer X in the view and the media time under it; zoom keeps this point fixed. */
|
|
5552
5734
|
|
|
5553
5735
|
/** CSS width × devicePixelRatio, for canvas gradient fills. */
|
|
@@ -5577,6 +5759,25 @@ function disableScrollOverscroll(container) {
|
|
|
5577
5759
|
}
|
|
5578
5760
|
}
|
|
5579
5761
|
|
|
5762
|
+
/** Half-view margins so t=0 and duration can sit under the center pin. */
|
|
5763
|
+
function applyWaveformGutters(wavesurfer, gutterPx) {
|
|
5764
|
+
const wrapper = wavesurfer.getWrapper ? wavesurfer.getWrapper() : null;
|
|
5765
|
+
if (!wrapper || !wrapper.style) {
|
|
5766
|
+
return;
|
|
5767
|
+
}
|
|
5768
|
+
const pad = gutterPx > 0 ? `${gutterPx}px` : '';
|
|
5769
|
+
wrapper.style.marginLeft = pad;
|
|
5770
|
+
wrapper.style.marginRight = pad;
|
|
5771
|
+
// WaveSurfer sets overflow-x:hidden when duration * minPxPerSec <= view width
|
|
5772
|
+
// (tape 1x). Gutters live in these margins, so the scroller must stay pan-able.
|
|
5773
|
+
const scroll = wrapper.parentElement;
|
|
5774
|
+
if (scroll && scroll.style) {
|
|
5775
|
+
const scrollStyle = scroll.style;
|
|
5776
|
+
scrollStyle.overflowX = gutterPx > 0 ? 'auto' : '';
|
|
5777
|
+
scrollStyle.scrollbarWidth = gutterPx > 0 ? 'none' : '';
|
|
5778
|
+
}
|
|
5779
|
+
}
|
|
5780
|
+
|
|
5580
5781
|
/** Tint WaveSurfer's zoomed tiles with played/unplayed/hover/buffer colors. */
|
|
5581
5782
|
function tintZoomedWaveform(wavesurfer, fills, replaceSnapshot = false) {
|
|
5582
5783
|
const wrapper = wavesurfer.getWrapper ? wavesurfer.getWrapper() : null;
|
|
@@ -5590,6 +5791,15 @@ function tintZoomedWaveform(wavesurfer, fills, replaceSnapshot = false) {
|
|
|
5590
5791
|
totalWidthCss: wrapper.clientWidth
|
|
5591
5792
|
});
|
|
5592
5793
|
}
|
|
5794
|
+
|
|
5795
|
+
/** Touch (and the compatibility mouse events Chrome sends after a tap) must not drive hover fills. */
|
|
5796
|
+
function isTouchHoverInput(event) {
|
|
5797
|
+
if (event.pointerType === 'touch') {
|
|
5798
|
+
return true;
|
|
5799
|
+
}
|
|
5800
|
+
const native = event.nativeEvent;
|
|
5801
|
+
return !!native.sourceCapabilities?.firesTouchEvents;
|
|
5802
|
+
}
|
|
5593
5803
|
function touchDistance(touches) {
|
|
5594
5804
|
if (touches.length < 2) {
|
|
5595
5805
|
return 0;
|
|
@@ -5620,11 +5830,14 @@ function zoomOriginAtPointer(pointerX, wavesurfer, durationSec, fallbackWidth) {
|
|
|
5620
5830
|
*/
|
|
5621
5831
|
function WaveformView({
|
|
5622
5832
|
bufferedRange,
|
|
5833
|
+
cameraMode = 'desktop',
|
|
5623
5834
|
currentTime = 0,
|
|
5624
5835
|
durationSec,
|
|
5625
5836
|
height = constants/* WAVEFORM_HEIGHT */.oN,
|
|
5626
5837
|
interactive = true,
|
|
5838
|
+
isPlaying = false,
|
|
5627
5839
|
mediaEl,
|
|
5840
|
+
onPlayPause,
|
|
5628
5841
|
onSeek,
|
|
5629
5842
|
onViewportChange,
|
|
5630
5843
|
onZoomChange,
|
|
@@ -5632,42 +5845,59 @@ function WaveformView({
|
|
|
5632
5845
|
zoomLevel: zoomLevelProp
|
|
5633
5846
|
}) {
|
|
5634
5847
|
// DOM / WaveSurfer
|
|
5635
|
-
const containerRef = (0,external_react_.useRef)(null);
|
|
5636
|
-
const
|
|
5637
|
-
const playheadRef = (0,external_react_.useRef)(null);
|
|
5638
|
-
const
|
|
5639
|
-
const wavesurferRef = (0,external_react_.useRef)(null);
|
|
5848
|
+
const containerRef = (0,external_react_.useRef)(null); // WaveSurfer canvas host
|
|
5849
|
+
const playheadAnimationRef = (0,external_react_.useRef)(0); // animation frame id while the playhead follows playback
|
|
5850
|
+
const playheadRef = (0,external_react_.useRef)(null); // playhead element the camera positions
|
|
5851
|
+
const trackRef = (0,external_react_.useRef)(null); // hover / tap target around the canvas
|
|
5852
|
+
const wavesurferRef = (0,external_react_.useRef)(null); // WaveSurfer instance
|
|
5640
5853
|
|
|
5641
5854
|
// Latest props for WaveSurfer + media listeners that must not re-subscribe each render.
|
|
5642
|
-
const
|
|
5643
|
-
const
|
|
5644
|
-
const
|
|
5645
|
-
const
|
|
5646
|
-
const
|
|
5647
|
-
const
|
|
5648
|
-
const mediaElRef = (0,external_react_.useRef)(mediaEl);
|
|
5649
|
-
const
|
|
5650
|
-
const
|
|
5651
|
-
const
|
|
5855
|
+
const cameraModeRef = (0,external_react_.useRef)(cameraMode); // latest tape/desktop; WaveSurfer create() deps stay empty
|
|
5856
|
+
const currentTimeRef = (0,external_react_.useRef)(currentTime); // latest media time; zoom recenter reads this
|
|
5857
|
+
const durationSecRef = (0,external_react_.useRef)(durationSec); // latest duration; click-to-seek reads this
|
|
5858
|
+
const hasZoomHandlersRef = (0,external_react_.useRef)(false); // parent can zoom; wheel/pinch check this
|
|
5859
|
+
const interactiveRef = (0,external_react_.useRef)(interactive); // latest interactive; WaveSurfer listeners read this
|
|
5860
|
+
const isPlayingRef = (0,external_react_.useRef)(isPlaying); // latest isPlaying; tape tap toggles from this
|
|
5861
|
+
const mediaElRef = (0,external_react_.useRef)(mediaEl); // latest <audio>/<video>; camera + playhead tick read this
|
|
5862
|
+
const onPlayPauseRef = (0,external_react_.useRef)(onPlayPause); // latest play/pause; tape tap must not re-bind
|
|
5863
|
+
const onSeekRef = (0,external_react_.useRef)(onSeek); // latest onSeek; click/scroll handlers must not re-bind
|
|
5864
|
+
const onViewportChangeRef = (0,external_react_.useRef)(onViewportChange); // latest viewport callback; camera commits here
|
|
5865
|
+
const peaksRef = (0,external_react_.useRef)(peaks); // latest peaks; WaveSurfer create() + morph read this
|
|
5866
|
+
|
|
5867
|
+
const displayedPeaksRef = (0,external_react_.useRef)(null); // peaks currently drawn (for morph)
|
|
5868
|
+
const peakTransitionAnimationRef = (0,external_react_.useRef)(0); // animation frame id while peaks morph in
|
|
5652
5869
|
// Zoom / pinch / tile tint (read from pointer + WaveSurfer handlers)
|
|
5653
|
-
const
|
|
5654
|
-
const
|
|
5655
|
-
const
|
|
5656
|
-
const
|
|
5657
|
-
const
|
|
5658
|
-
|
|
5659
|
-
const
|
|
5660
|
-
const
|
|
5661
|
-
const
|
|
5662
|
-
|
|
5663
|
-
|
|
5870
|
+
const applyZoomWindowRef = (0,external_react_.useRef)(null); // latest applyZoomWindow; resize observer calls this
|
|
5871
|
+
const bufferProgressRef = (0,external_react_.useRef)(0); // latest buffer fill; zoomed tile tint reads this
|
|
5872
|
+
const hideScrubTimeChipTimerRef = (0,external_react_.useRef)(0); // hides the tape scrub time chip after scroll settles
|
|
5873
|
+
const hoverProgressRef = (0,external_react_.useRef)(null); // latest hover fill; zoomed tile tint reads this
|
|
5874
|
+
const lastObservedWidthRef = (0,external_react_.useRef)(0); // last ResizeObserver width; skip no-op resizes
|
|
5875
|
+
const liveHeightRef = (0,external_react_.useRef)(height); // canvas height last applied to WaveSurfer
|
|
5876
|
+
const pinchStartRef = (0,external_react_.useRef)(null); // two-finger distance and zoom at pinch start
|
|
5877
|
+
const pointerZoomClearTimerRef = (0,external_react_.useRef)(0); // clears pointerZoomRef after WAVEFORM_ZOOM_DISMISS_MS
|
|
5878
|
+
const pointerZoomRef = (0,external_react_.useRef)(false); // pinch/wheel in progress; skip play/pause and tape recenter
|
|
5879
|
+
const queuedTapeSeekTimeSecRef = (0,external_react_.useRef)(null); // seek time waiting for the next animation frame
|
|
5880
|
+
const suppressNextTapPlayPauseRef = (0,external_react_.useRef)(false); // swallow play/pause after a swipe or pinch
|
|
5881
|
+
const suppressNextTapPlayPauseTimerRef = (0,external_react_.useRef)(0); // clears suppressNextTapPlayPauseRef
|
|
5882
|
+
const suppressViewportSyncRef = (0,external_react_.useRef)(false); // ignore WaveSurfer zoom/scroll while we setOptions
|
|
5883
|
+
const tapeSeekAnimationRef = (0,external_react_.useRef)(0); // coalesces swipe seeks to one seek per animation frame
|
|
5884
|
+
const toggleTapePlaybackRef = (0,external_react_.useRef)(null); // latest tap play/pause; click/pointerup call this
|
|
5885
|
+
const zoomOriginRef = (0,external_react_.useRef)(null); // pointer+time so zoom keeps that point fixed
|
|
5886
|
+
|
|
5887
|
+
cameraModeRef.current = cameraMode;
|
|
5664
5888
|
currentTimeRef.current = currentTime;
|
|
5665
|
-
peaksRef.current = peaks;
|
|
5666
5889
|
durationSecRef.current = durationSec;
|
|
5890
|
+
interactiveRef.current = interactive;
|
|
5891
|
+
isPlayingRef.current = isPlaying;
|
|
5667
5892
|
mediaElRef.current = mediaEl;
|
|
5893
|
+
onPlayPauseRef.current = onPlayPause;
|
|
5894
|
+
onSeekRef.current = onSeek;
|
|
5668
5895
|
onViewportChangeRef.current = onViewportChange;
|
|
5896
|
+
peaksRef.current = peaks;
|
|
5669
5897
|
const [internalZoom, setInternalZoom] = (0,external_react_.useState)(constants/* WAVEFORM_ZOOM_MIN */.LW);
|
|
5670
5898
|
const [hoverProgress, setHoverProgress] = (0,external_react_.useState)(null);
|
|
5899
|
+
const [scrubPreviewTimeSec, setScrubPreviewTimeSec] = (0,external_react_.useState)(null);
|
|
5900
|
+
const [overlayPortalHost, setOverlayPortalHost] = (0,external_react_.useState)(null);
|
|
5671
5901
|
const [canvasWidthPx, setCanvasWidthPx] = (0,external_react_.useState)(0);
|
|
5672
5902
|
const [scrollLeft, setScrollLeft] = (0,external_react_.useState)(0);
|
|
5673
5903
|
const isControlled = typeof zoomLevelProp === 'number';
|
|
@@ -5680,19 +5910,22 @@ function WaveformView({
|
|
|
5680
5910
|
const zoomLevel = clampWaveformZoom(isControlled ? zoomLevelProp : internalZoom, maxZoom);
|
|
5681
5911
|
const zoomRef = (0,external_react_.useRef)(zoomLevel); // latest zoom for WaveSurfer redraw/zoom handlers
|
|
5682
5912
|
zoomRef.current = zoomLevel;
|
|
5683
|
-
const prevZoomRef = (0,external_react_.useRef)(null);
|
|
5913
|
+
const prevZoomRef = (0,external_react_.useRef)(null); // last applied zoom; tape recenters only when this changes
|
|
5914
|
+
const isTape = cameraMode === 'tape';
|
|
5684
5915
|
const isZoomed = zoomLevel > constants/* WAVEFORM_ZOOM_MIN */.LW;
|
|
5916
|
+
const isScrollableWindow = isZoomed || isTape;
|
|
5685
5917
|
const bufferProgress = getBufferedProgress(bufferedRange, durationSec);
|
|
5686
5918
|
bufferProgressRef.current = bufferProgress;
|
|
5687
5919
|
hoverProgressRef.current = hoverProgress;
|
|
5688
5920
|
const viewport = (0,external_react_.useMemo)(() => createWaveformViewport({
|
|
5689
5921
|
durationSec,
|
|
5922
|
+
gutterPx: isTape ? getTapeGutterPx(canvasWidthPx) : 0,
|
|
5690
5923
|
heightPx: height,
|
|
5691
5924
|
maxZoom,
|
|
5692
5925
|
scrollLeftPx: scrollLeft,
|
|
5693
5926
|
widthPx: canvasWidthPx,
|
|
5694
5927
|
zoomLevel
|
|
5695
|
-
}), [canvasWidthPx, durationSec, height, maxZoom, scrollLeft, zoomLevel]);
|
|
5928
|
+
}), [canvasWidthPx, durationSec, height, isTape, maxZoom, scrollLeft, zoomLevel]);
|
|
5696
5929
|
const viewportRef = (0,external_react_.useRef)(viewport); // live scroll window; prefer this over render-state while the camera is moving
|
|
5697
5930
|
const onViewportCommit = (0,external_react_.useCallback)((scrollLeftPx, nextViewport, commitReactState = true) => {
|
|
5698
5931
|
onViewportChangeRef.current?.(nextViewport);
|
|
@@ -5707,20 +5940,45 @@ function WaveformView({
|
|
|
5707
5940
|
clearFollowPin,
|
|
5708
5941
|
handleScroll: handleCameraScroll,
|
|
5709
5942
|
isFollowPinned,
|
|
5943
|
+
isUserPanning,
|
|
5710
5944
|
onSeek: onPlayheadSeek,
|
|
5711
5945
|
onZoom,
|
|
5712
5946
|
releaseUserPanHold,
|
|
5713
5947
|
seekTo
|
|
5714
5948
|
} = usePlayheadCamera({
|
|
5949
|
+
cameraMode,
|
|
5715
5950
|
mediaElRef,
|
|
5716
5951
|
onViewportCommit,
|
|
5717
5952
|
playheadRef,
|
|
5718
5953
|
viewportRef,
|
|
5719
5954
|
wavesurferRef
|
|
5720
5955
|
});
|
|
5956
|
+
const toggleTapePlayback = (0,external_react_.useCallback)(() => {
|
|
5957
|
+
if (!interactiveRef.current) {
|
|
5958
|
+
return;
|
|
5959
|
+
}
|
|
5960
|
+
if (isUserPanning() || pointerZoomRef.current) {
|
|
5961
|
+
return;
|
|
5962
|
+
}
|
|
5963
|
+
if (suppressNextTapPlayPauseRef.current) {
|
|
5964
|
+
suppressNextTapPlayPauseRef.current = false;
|
|
5965
|
+
window.clearTimeout(suppressNextTapPlayPauseTimerRef.current);
|
|
5966
|
+
suppressNextTapPlayPauseTimerRef.current = 0;
|
|
5967
|
+
return;
|
|
5968
|
+
}
|
|
5969
|
+
onPlayPauseRef.current?.(!isPlayingRef.current);
|
|
5970
|
+
suppressNextTapPlayPauseRef.current = true;
|
|
5971
|
+
window.clearTimeout(suppressNextTapPlayPauseTimerRef.current);
|
|
5972
|
+
suppressNextTapPlayPauseTimerRef.current = window.setTimeout(() => {
|
|
5973
|
+
suppressNextTapPlayPauseRef.current = false;
|
|
5974
|
+
suppressNextTapPlayPauseTimerRef.current = 0;
|
|
5975
|
+
}, constants/* WAVEFORM_TAPE_CLICK_SUPPRESS_MS */.fI);
|
|
5976
|
+
}, [isUserPanning]);
|
|
5977
|
+
toggleTapePlaybackRef.current = toggleTapePlayback;
|
|
5721
5978
|
(0,external_react_.useLayoutEffect)(() => {
|
|
5722
5979
|
viewportRef.current = createWaveformViewport({
|
|
5723
5980
|
durationSec: viewport.durationSec,
|
|
5981
|
+
gutterPx: viewport.gutterPx,
|
|
5724
5982
|
heightPx: viewport.heightPx,
|
|
5725
5983
|
maxZoom: viewport.maxZoom,
|
|
5726
5984
|
scrollLeftPx: viewportRef.current.scrollLeftPx,
|
|
@@ -5743,6 +6001,41 @@ function WaveformView({
|
|
|
5743
6001
|
pointerZoomClearTimerRef.current = 0;
|
|
5744
6002
|
}, constants/* WAVEFORM_ZOOM_DISMISS_MS */.m6);
|
|
5745
6003
|
}, []);
|
|
6004
|
+
|
|
6005
|
+
/**
|
|
6006
|
+
* Seek the time under the pin (one seek per frame). Swallow the delayed iOS click;
|
|
6007
|
+
* show the scrub chip until the swipe settles.
|
|
6008
|
+
*/
|
|
6009
|
+
const handleTapeSwipe = (0,external_react_.useCallback)(timeSec => {
|
|
6010
|
+
// Swallow the click WaveSurfer/iOS fires after a swipe or pinch.
|
|
6011
|
+
suppressNextTapPlayPauseRef.current = true;
|
|
6012
|
+
window.clearTimeout(suppressNextTapPlayPauseTimerRef.current);
|
|
6013
|
+
suppressNextTapPlayPauseTimerRef.current = window.setTimeout(() => {
|
|
6014
|
+
suppressNextTapPlayPauseRef.current = false;
|
|
6015
|
+
suppressNextTapPlayPauseTimerRef.current = 0;
|
|
6016
|
+
}, constants/* WAVEFORM_TAPE_CLICK_SUPPRESS_MS */.fI);
|
|
6017
|
+
queuedTapeSeekTimeSecRef.current = timeSec;
|
|
6018
|
+
if (!tapeSeekAnimationRef.current) {
|
|
6019
|
+
// One seek per frame; later scrolls just update the queued time.
|
|
6020
|
+
tapeSeekAnimationRef.current = window.requestAnimationFrame(() => {
|
|
6021
|
+
tapeSeekAnimationRef.current = 0;
|
|
6022
|
+
const next = queuedTapeSeekTimeSecRef.current;
|
|
6023
|
+
queuedTapeSeekTimeSecRef.current = null;
|
|
6024
|
+
if (next != null) {
|
|
6025
|
+
onSeekRef.current?.(next);
|
|
6026
|
+
}
|
|
6027
|
+
});
|
|
6028
|
+
}
|
|
6029
|
+
if (interactiveRef.current) {
|
|
6030
|
+
// Show the time under the pin; hide once scrolling has settled.
|
|
6031
|
+
setScrubPreviewTimeSec(timeSec);
|
|
6032
|
+
window.clearTimeout(hideScrubTimeChipTimerRef.current);
|
|
6033
|
+
hideScrubTimeChipTimerRef.current = window.setTimeout(() => {
|
|
6034
|
+
setScrubPreviewTimeSec(null);
|
|
6035
|
+
hideScrubTimeChipTimerRef.current = 0;
|
|
6036
|
+
}, constants/* WAVEFORM_FOLLOW_SCROLL_SETTLE_MS */.Zs);
|
|
6037
|
+
}
|
|
6038
|
+
}, []);
|
|
5746
6039
|
const syncViewport = (0,external_react_.useCallback)(() => {
|
|
5747
6040
|
const wavesurfer = wavesurferRef.current;
|
|
5748
6041
|
if (!wavesurfer) {
|
|
@@ -5758,14 +6051,16 @@ function WaveformView({
|
|
|
5758
6051
|
if (!playhead) {
|
|
5759
6052
|
return;
|
|
5760
6053
|
}
|
|
5761
|
-
if (
|
|
6054
|
+
if (isTape) {
|
|
6055
|
+
playhead.style.left = getTapePinnedPlayheadLeft();
|
|
6056
|
+
} else if (!isFollowPinned()) {
|
|
5762
6057
|
playhead.style.left = timeLeftPercent(timeSec, durationSecRef.current, viewportRef.current);
|
|
5763
6058
|
}
|
|
5764
6059
|
const wavesurfer = wavesurferRef.current;
|
|
5765
6060
|
if (wavesurfer && wavesurfer.setTime) {
|
|
5766
6061
|
wavesurfer.setTime(timeSec);
|
|
5767
6062
|
}
|
|
5768
|
-
}, [isFollowPinned]);
|
|
6063
|
+
}, [isFollowPinned, isTape]);
|
|
5769
6064
|
|
|
5770
6065
|
// Same zoom-window body as the zoom effect; extracted so resize can call it too.
|
|
5771
6066
|
const applyZoomWindow = (0,external_react_.useCallback)(() => {
|
|
@@ -5777,11 +6072,13 @@ function WaveformView({
|
|
|
5777
6072
|
const viewWidthPx = wavesurfer.getWidth ? wavesurfer.getWidth() : container.clientWidth;
|
|
5778
6073
|
const minPxPerSec = getZoomedPixelsPerSecond({
|
|
5779
6074
|
durationSec,
|
|
6075
|
+
isTape,
|
|
5780
6076
|
maxZoom,
|
|
5781
6077
|
viewWidthPx,
|
|
5782
6078
|
zoomLevel
|
|
5783
6079
|
});
|
|
5784
|
-
const
|
|
6080
|
+
const gutterPx = isTape ? getTapeGutterPx(viewWidthPx) : 0;
|
|
6081
|
+
const origin = isTape ? null : zoomOriginRef.current;
|
|
5785
6082
|
zoomOriginRef.current = null;
|
|
5786
6083
|
const didZoomChange = prevZoomRef.current !== zoomLevel;
|
|
5787
6084
|
if (origin || didZoomChange) {
|
|
@@ -5791,24 +6088,27 @@ function WaveformView({
|
|
|
5791
6088
|
try {
|
|
5792
6089
|
wavesurfer.setOptions(WaveformView_objectSpread({
|
|
5793
6090
|
autoScroll: false,
|
|
6091
|
+
fillParent: !isTape,
|
|
5794
6092
|
minPxPerSec
|
|
5795
|
-
},
|
|
5796
|
-
progressColor:
|
|
6093
|
+
}, isScrollableWindow ? {
|
|
6094
|
+
progressColor: getPlayedWaveformColor(isTape),
|
|
5797
6095
|
waveColor: WAVEFORM_COLOR_UNPLAYED
|
|
5798
6096
|
} : {}));
|
|
6097
|
+
applyWaveformGutters(wavesurfer, gutterPx);
|
|
5799
6098
|
if (minPxPerSec > 0) {
|
|
6099
|
+
const windowViewport = createWaveformViewport({
|
|
6100
|
+
durationSec,
|
|
6101
|
+
gutterPx,
|
|
6102
|
+
heightPx: liveHeightRef.current || height,
|
|
6103
|
+
maxZoom,
|
|
6104
|
+
scrollLeftPx: 0,
|
|
6105
|
+
widthPx: viewWidthPx,
|
|
6106
|
+
zoomLevel
|
|
6107
|
+
});
|
|
5800
6108
|
if (origin) {
|
|
5801
|
-
applyScrollLeft(origin.timeSec * minPxPerSec - origin.pointerX, true);
|
|
5802
|
-
} else if (didZoomChange && !pointerZoomRef.current) {
|
|
5803
|
-
|
|
5804
|
-
durationSec,
|
|
5805
|
-
heightPx: height,
|
|
5806
|
-
maxZoom,
|
|
5807
|
-
scrollLeftPx: 0,
|
|
5808
|
-
widthPx: viewWidthPx,
|
|
5809
|
-
zoomLevel
|
|
5810
|
-
});
|
|
5811
|
-
applyScrollLeft(Math.min(maxScrollLeft(zoomedViewport), Math.max(0, currentTimeRef.current * minPxPerSec - viewWidthPx / 2)), true);
|
|
6109
|
+
applyScrollLeft(origin.timeSec * minPxPerSec + gutterPx - origin.pointerX, true);
|
|
6110
|
+
} else if (didZoomChange && (isTape || !pointerZoomRef.current)) {
|
|
6111
|
+
applyScrollLeft(getCenteredScrollLeft(currentTimeRef.current, windowViewport), true);
|
|
5812
6112
|
}
|
|
5813
6113
|
}
|
|
5814
6114
|
wavesurfer.setTime(currentTimeRef.current);
|
|
@@ -5818,13 +6118,17 @@ function WaveformView({
|
|
|
5818
6118
|
}
|
|
5819
6119
|
syncViewport();
|
|
5820
6120
|
updatePlayheadPosition(currentTimeRef.current);
|
|
5821
|
-
}, [applyScrollLeft, durationSec, height, maxZoom, onZoom, syncViewport, updatePlayheadPosition, zoomLevel]);
|
|
6121
|
+
}, [applyScrollLeft, durationSec, height, isScrollableWindow, isTape, maxZoom, onZoom, syncViewport, updatePlayheadPosition, zoomLevel]);
|
|
5822
6122
|
applyZoomWindowRef.current = applyZoomWindow;
|
|
5823
6123
|
(0,external_react_.useEffect)(() => {
|
|
5824
6124
|
const container = containerRef.current;
|
|
5825
6125
|
if (!container) {
|
|
5826
6126
|
return undefined;
|
|
5827
6127
|
}
|
|
6128
|
+
const measuredHeight = cameraModeRef.current === 'tape' && container.clientHeight > 0 ? Math.round(container.clientHeight) : height;
|
|
6129
|
+
if (cameraModeRef.current === 'tape' && measuredHeight > 0) {
|
|
6130
|
+
liveHeightRef.current = measuredHeight;
|
|
6131
|
+
}
|
|
5828
6132
|
const wavesurfer = w.create({
|
|
5829
6133
|
autoScroll: false,
|
|
5830
6134
|
barGap: constants/* WAVEFORM_BAR_GAP */.Lu,
|
|
@@ -5835,25 +6139,34 @@ function WaveformView({
|
|
|
5835
6139
|
cursorWidth: 0,
|
|
5836
6140
|
duration: durationSecRef.current,
|
|
5837
6141
|
fillParent: true,
|
|
5838
|
-
height,
|
|
6142
|
+
height: measuredHeight,
|
|
5839
6143
|
hideScrollbar: true,
|
|
5840
|
-
|
|
6144
|
+
dragToSeek: false,
|
|
6145
|
+
interact: interactiveRef.current && cameraModeRef.current !== 'tape',
|
|
5841
6146
|
normalize: false,
|
|
5842
6147
|
peaks: toChannels(peaksRef.current),
|
|
5843
|
-
progressColor:
|
|
6148
|
+
progressColor: getPlayedWaveformColor(cameraModeRef.current === 'tape'),
|
|
5844
6149
|
waveColor: WAVEFORM_COLOR_UNPLAYED
|
|
5845
6150
|
});
|
|
5846
6151
|
const unsubscribeClick = wavesurfer.on('click', relativeX => {
|
|
5847
6152
|
if (!interactiveRef.current) {
|
|
5848
6153
|
return;
|
|
5849
6154
|
}
|
|
6155
|
+
if (cameraModeRef.current === 'tape') {
|
|
6156
|
+
toggleTapePlaybackRef.current?.();
|
|
6157
|
+
return;
|
|
6158
|
+
}
|
|
5850
6159
|
onSeekRef.current?.(relativeX * durationSecRef.current);
|
|
5851
6160
|
});
|
|
5852
6161
|
const unsubscribeScroll = wavesurfer.on('scroll', () => {
|
|
5853
6162
|
if (suppressViewportSyncRef.current) {
|
|
5854
6163
|
return;
|
|
5855
6164
|
}
|
|
5856
|
-
|
|
6165
|
+
// User pan: map scroll to the time under the playhead, then tape seeks it.
|
|
6166
|
+
handleCameraScroll(timeSec => {
|
|
6167
|
+
if (cameraModeRef.current === 'tape' && !pointerZoomRef.current) {
|
|
6168
|
+
handleTapeSwipe(timeSec);
|
|
6169
|
+
}
|
|
5857
6170
|
syncViewport();
|
|
5858
6171
|
});
|
|
5859
6172
|
});
|
|
@@ -5864,12 +6177,13 @@ function WaveformView({
|
|
|
5864
6177
|
syncViewport();
|
|
5865
6178
|
});
|
|
5866
6179
|
const unsubscribeRedraw = wavesurfer.on('redrawcomplete', () => {
|
|
5867
|
-
if (!(zoomRef.current > constants/* WAVEFORM_ZOOM_MIN */.LW)) {
|
|
6180
|
+
if (!(zoomRef.current > constants/* WAVEFORM_ZOOM_MIN */.LW) && cameraModeRef.current !== 'tape') {
|
|
5868
6181
|
return;
|
|
5869
6182
|
}
|
|
5870
6183
|
tintZoomedWaveform(wavesurfer, getWaveformFills({
|
|
5871
6184
|
bufferProgress: bufferProgressRef.current,
|
|
5872
|
-
hoverProgress: hoverProgressRef.current
|
|
6185
|
+
hoverProgress: cameraModeRef.current === 'tape' ? null : hoverProgressRef.current,
|
|
6186
|
+
isTape: cameraModeRef.current === 'tape'
|
|
5873
6187
|
}), true);
|
|
5874
6188
|
});
|
|
5875
6189
|
wavesurferRef.current = wavesurfer;
|
|
@@ -5880,6 +6194,9 @@ function WaveformView({
|
|
|
5880
6194
|
return () => {
|
|
5881
6195
|
releaseUserPanHold();
|
|
5882
6196
|
cancelJump();
|
|
6197
|
+
window.clearTimeout(hideScrubTimeChipTimerRef.current);
|
|
6198
|
+
window.clearTimeout(suppressNextTapPlayPauseTimerRef.current);
|
|
6199
|
+
window.cancelAnimationFrame(tapeSeekAnimationRef.current);
|
|
5883
6200
|
window.cancelAnimationFrame(peakTransitionAnimationRef.current);
|
|
5884
6201
|
unsubscribeClick();
|
|
5885
6202
|
unsubscribeScroll();
|
|
@@ -5889,18 +6206,39 @@ function WaveformView({
|
|
|
5889
6206
|
wavesurferRef.current = null;
|
|
5890
6207
|
displayedPeaksRef.current = null;
|
|
5891
6208
|
};
|
|
5892
|
-
}, [cancelJump, handleCameraScroll, height, releaseUserPanHold, syncViewport]);
|
|
6209
|
+
}, [cancelJump, handleCameraScroll, handleTapeSwipe, height, isUserPanning, releaseUserPanHold, syncViewport]);
|
|
5893
6210
|
(0,external_react_.useLayoutEffect)(() => {
|
|
5894
6211
|
const el = containerRef.current;
|
|
5895
6212
|
if (!el) {
|
|
5896
6213
|
return undefined;
|
|
5897
6214
|
}
|
|
5898
6215
|
setCanvasWidthPx(el.clientWidth);
|
|
6216
|
+
lastObservedWidthRef.current = el.clientWidth;
|
|
6217
|
+
const mountedHeight = Math.round(el.clientHeight);
|
|
6218
|
+
if (cameraModeRef.current === 'tape' && mountedHeight > 0) {
|
|
6219
|
+
liveHeightRef.current = mountedHeight;
|
|
6220
|
+
}
|
|
5899
6221
|
const observer = new ResizeObserver(entries => {
|
|
5900
6222
|
entries.forEach(entry => {
|
|
5901
|
-
|
|
6223
|
+
const nextWidth = entry.contentRect.width;
|
|
6224
|
+
const widthChanged = nextWidth !== lastObservedWidthRef.current;
|
|
6225
|
+
lastObservedWidthRef.current = nextWidth;
|
|
6226
|
+
setCanvasWidthPx(nextWidth);
|
|
6227
|
+
const nextHeight = Math.round(entry.contentRect.height);
|
|
6228
|
+
if (cameraModeRef.current === 'tape' && Number.isFinite(nextHeight) && nextHeight > 0 && nextHeight !== liveHeightRef.current) {
|
|
6229
|
+
liveHeightRef.current = nextHeight;
|
|
6230
|
+
const wavesurfer = wavesurferRef.current;
|
|
6231
|
+
if (wavesurfer && wavesurfer.setOptions) {
|
|
6232
|
+
wavesurfer.setOptions({
|
|
6233
|
+
height: nextHeight
|
|
6234
|
+
});
|
|
6235
|
+
}
|
|
6236
|
+
}
|
|
6237
|
+
syncViewport();
|
|
6238
|
+
if (widthChanged) {
|
|
6239
|
+
applyZoomWindowRef.current?.();
|
|
6240
|
+
}
|
|
5902
6241
|
});
|
|
5903
|
-
syncViewport();
|
|
5904
6242
|
});
|
|
5905
6243
|
observer.observe(el);
|
|
5906
6244
|
return () => observer.disconnect();
|
|
@@ -5925,7 +6263,7 @@ function WaveformView({
|
|
|
5925
6263
|
return;
|
|
5926
6264
|
}
|
|
5927
6265
|
wavesurfer.setOptions({
|
|
5928
|
-
interact: interactive
|
|
6266
|
+
interact: interactive && cameraModeRef.current !== 'tape'
|
|
5929
6267
|
});
|
|
5930
6268
|
}, [interactive]);
|
|
5931
6269
|
(0,external_react_.useLayoutEffect)(() => {
|
|
@@ -5956,13 +6294,17 @@ function WaveformView({
|
|
|
5956
6294
|
window.cancelAnimationFrame(playheadAnimationRef.current);
|
|
5957
6295
|
cancelJump();
|
|
5958
6296
|
releaseUserPanHold();
|
|
5959
|
-
|
|
6297
|
+
if (cameraModeRef.current !== 'tape') {
|
|
6298
|
+
clearFollowPin();
|
|
6299
|
+
}
|
|
5960
6300
|
syncViewport();
|
|
5961
6301
|
updatePlayheadPosition(media.currentTime);
|
|
5962
6302
|
};
|
|
5963
6303
|
const handleSeeked = () => {
|
|
5964
6304
|
onPlayheadSeek(media.currentTime);
|
|
5965
|
-
|
|
6305
|
+
if (!isUserPanning()) {
|
|
6306
|
+
seekTo(media.currentTime);
|
|
6307
|
+
}
|
|
5966
6308
|
updatePlayheadPosition(media.currentTime);
|
|
5967
6309
|
};
|
|
5968
6310
|
if (!media.paused) {
|
|
@@ -5981,7 +6323,7 @@ function WaveformView({
|
|
|
5981
6323
|
media.removeEventListener('pause', stopLoop);
|
|
5982
6324
|
media.removeEventListener('seeked', handleSeeked);
|
|
5983
6325
|
};
|
|
5984
|
-
}, [applyPlayheadCamera, cancelJump, clearFollowPin, mediaEl, onPlayheadSeek, releaseUserPanHold, seekTo, syncViewport, updatePlayheadPosition]);
|
|
6326
|
+
}, [applyPlayheadCamera, cancelJump, clearFollowPin, isUserPanning, mediaEl, onPlayheadSeek, releaseUserPanHold, seekTo, syncViewport, updatePlayheadPosition]);
|
|
5985
6327
|
(0,external_react_.useEffect)(() => {
|
|
5986
6328
|
const wavesurfer = wavesurferRef.current;
|
|
5987
6329
|
if (!wavesurfer || !wavesurfer.setOptions || !(durationSec > 0)) {
|
|
@@ -6016,9 +6358,10 @@ function WaveformView({
|
|
|
6016
6358
|
}
|
|
6017
6359
|
const fills = getWaveformFills({
|
|
6018
6360
|
bufferProgress,
|
|
6019
|
-
hoverProgress
|
|
6361
|
+
hoverProgress: isTape ? null : hoverProgress,
|
|
6362
|
+
isTape
|
|
6020
6363
|
});
|
|
6021
|
-
if (
|
|
6364
|
+
if (isScrollableWindow) {
|
|
6022
6365
|
tintZoomedWaveform(wavesurfer, fills);
|
|
6023
6366
|
return;
|
|
6024
6367
|
}
|
|
@@ -6027,7 +6370,7 @@ function WaveformView({
|
|
|
6027
6370
|
waveColor: toCanvasFill(fills.waveColor, devicePixelWidth(canvasWidthPx))
|
|
6028
6371
|
});
|
|
6029
6372
|
wavesurfer.setTime(currentTimeRef.current);
|
|
6030
|
-
}, [bufferProgress, canvasWidthPx, hoverProgress,
|
|
6373
|
+
}, [bufferProgress, canvasWidthPx, hoverProgress, isScrollableWindow, isTape]);
|
|
6031
6374
|
(0,external_react_.useEffect)(() => {
|
|
6032
6375
|
const track = trackRef.current;
|
|
6033
6376
|
if (!track) {
|
|
@@ -6108,7 +6451,7 @@ function WaveformView({
|
|
|
6108
6451
|
};
|
|
6109
6452
|
}, [durationSec, markPointerZoom, setZoomLevel]);
|
|
6110
6453
|
const onHoverMove = (0,external_react_.useCallback)(event => {
|
|
6111
|
-
if (!interactive) {
|
|
6454
|
+
if (!interactive || isTape || isTouchHoverInput(event)) {
|
|
6112
6455
|
return;
|
|
6113
6456
|
}
|
|
6114
6457
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
@@ -6125,19 +6468,48 @@ function WaveformView({
|
|
|
6125
6468
|
return;
|
|
6126
6469
|
}
|
|
6127
6470
|
setHoverProgress(Math.min(1, Math.max(0, pointerX / rect.width)));
|
|
6128
|
-
}, [durationSec, interactive]);
|
|
6471
|
+
}, [durationSec, interactive, isTape]);
|
|
6129
6472
|
const onHoverLeave = (0,external_react_.useCallback)(() => {
|
|
6130
6473
|
setHoverProgress(null);
|
|
6131
6474
|
}, []);
|
|
6475
|
+
const bindOverlayPortalHost = (0,external_react_.useCallback)(node => {
|
|
6476
|
+
const host = node?.parentElement ?? null;
|
|
6477
|
+
setOverlayPortalHost(prev => prev === host ? prev : host);
|
|
6478
|
+
}, []);
|
|
6132
6479
|
const hoverLeft = hoverProgress == null ? null : timeLeftPercent(hoverProgress * durationSec, durationSec, viewportRef.current);
|
|
6480
|
+
const scrubTimeChip = isTape && scrubPreviewTimeSec != null ? /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
6481
|
+
className: "bp-WaveformView-hover bp-WaveformView-hover--tape",
|
|
6482
|
+
"data-testid": "bp-waveform-hover"
|
|
6483
|
+
}, /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
6484
|
+
className: "bp-WaveformView-hoverTime",
|
|
6485
|
+
"data-testid": "bp-waveform-hover-time"
|
|
6486
|
+
}, formatTime(scrubPreviewTimeSec))) : null;
|
|
6133
6487
|
return /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
6134
|
-
|
|
6488
|
+
ref: bindOverlayPortalHost,
|
|
6489
|
+
className: classnames_default()('bp-WaveformView', {
|
|
6490
|
+
'bp-WaveformView--inert': !interactive,
|
|
6491
|
+
'bp-WaveformView--tape': isTape,
|
|
6492
|
+
'bp-WaveformView--zoomed': isZoomed
|
|
6493
|
+
}),
|
|
6135
6494
|
"data-testid": "bp-waveform-view"
|
|
6136
6495
|
}, /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
6137
6496
|
ref: trackRef,
|
|
6138
6497
|
className: "bp-WaveformView-track",
|
|
6139
|
-
onMouseLeave: interactive ? onHoverLeave : undefined,
|
|
6140
|
-
onMouseMove: interactive ? onHoverMove : undefined
|
|
6498
|
+
onMouseLeave: interactive && !isTape ? onHoverLeave : undefined,
|
|
6499
|
+
onMouseMove: interactive && !isTape ? onHoverMove : undefined,
|
|
6500
|
+
onPointerMove: interactive && !isTape ? event => {
|
|
6501
|
+
if (event.pointerType === 'touch') {
|
|
6502
|
+
onHoverLeave();
|
|
6503
|
+
return;
|
|
6504
|
+
}
|
|
6505
|
+
onHoverMove(event);
|
|
6506
|
+
} : undefined,
|
|
6507
|
+
onPointerUp: isTape ? event => {
|
|
6508
|
+
if (event.button > 0) {
|
|
6509
|
+
return;
|
|
6510
|
+
}
|
|
6511
|
+
toggleTapePlaybackRef.current?.();
|
|
6512
|
+
} : undefined
|
|
6141
6513
|
}, /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
6142
6514
|
ref: containerRef,
|
|
6143
6515
|
className: "bp-WaveformView-canvas"
|
|
@@ -6155,12 +6527,9 @@ function WaveformView({
|
|
|
6155
6527
|
}, /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
6156
6528
|
className: "bp-WaveformView-hoverTime",
|
|
6157
6529
|
"data-testid": "bp-waveform-hover-time"
|
|
6158
|
-
}, formatTime(hoverProgress * durationSec)))));
|
|
6530
|
+
}, formatTime(hoverProgress * durationSec)))), scrubTimeChip && overlayPortalHost ? /*#__PURE__*/__WEBPACK_EXTERNAL_MODULE_react_dom_7dac9eee_createPortal__(scrubTimeChip, overlayPortalHost) : null);
|
|
6159
6531
|
}
|
|
6160
6532
|
/* harmony default export */ const waveform_WaveformView = (/*#__PURE__*/external_react_["default"].memo(WaveformView));
|
|
6161
|
-
// EXTERNAL MODULE: ./node_modules/classnames/index.js
|
|
6162
|
-
var classnames = __webpack_require__(2485);
|
|
6163
|
-
var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames);
|
|
6164
6533
|
;// ./src/lib/viewers/controls/icons/IconZoomIn24.tsx
|
|
6165
6534
|
|
|
6166
6535
|
function IconZoomIn24() {
|
|
@@ -6246,11 +6615,14 @@ function WaveformZoomControl({
|
|
|
6246
6615
|
}) {
|
|
6247
6616
|
const [isHovered, setHovered] = (0,external_react_.useState)(false);
|
|
6248
6617
|
const [isFocused, setFocused] = (0,external_react_.useState)(false);
|
|
6249
|
-
const
|
|
6618
|
+
const [isPinned, setPinned] = (0,external_react_.useState)(false);
|
|
6619
|
+
const wasFlyoutOpenOnPointerDownRef = (0,external_react_.useRef)(true); // first tap on + pins the flyout instead of zooming
|
|
6620
|
+
const zoomControlElRef = (0,external_react_.useRef)(null); // root; outside pointerdown unpins the flyout
|
|
6621
|
+
const dismissTimerRef = (0,external_react_.useRef)(0); // delay before hover-close of the flyout
|
|
6250
6622
|
const sliderId = `bp-waveform-zoom-slider${(0,external_react_.useId)()}`;
|
|
6251
6623
|
const zoom = clampWaveformZoom(zoomLevel, maxZoom);
|
|
6252
6624
|
const zoomValue = Math.round(sliderValueFromZoom(zoom, maxZoom));
|
|
6253
|
-
const isOpen = isHovered || isFocused || isRevealed;
|
|
6625
|
+
const isOpen = isHovered || isFocused || isRevealed || isPinned;
|
|
6254
6626
|
const isAtMinZoom = zoomValue <= 0;
|
|
6255
6627
|
const isAtMaxZoom = zoomValue >= constants/* WAVEFORM_ZOOM_SLIDER_MAX */.nh;
|
|
6256
6628
|
const clearDismiss = (0,external_react_.useCallback)(() => {
|
|
@@ -6258,13 +6630,42 @@ function WaveformZoomControl({
|
|
|
6258
6630
|
dismissTimerRef.current = 0;
|
|
6259
6631
|
}, []);
|
|
6260
6632
|
(0,external_react_.useEffect)(() => () => window.clearTimeout(dismissTimerRef.current), []);
|
|
6633
|
+
(0,external_react_.useEffect)(() => {
|
|
6634
|
+
if (!isPinned) {
|
|
6635
|
+
return undefined;
|
|
6636
|
+
}
|
|
6637
|
+
const closeIfOutside = event => {
|
|
6638
|
+
const zoomControlEl = zoomControlElRef.current;
|
|
6639
|
+
if (!zoomControlEl || zoomControlEl.contains(event.target)) {
|
|
6640
|
+
return;
|
|
6641
|
+
}
|
|
6642
|
+
setPinned(false);
|
|
6643
|
+
setFocused(false);
|
|
6644
|
+
setHovered(false);
|
|
6645
|
+
};
|
|
6646
|
+
document.addEventListener('pointerdown', closeIfOutside);
|
|
6647
|
+
return () => document.removeEventListener('pointerdown', closeIfOutside);
|
|
6648
|
+
}, [isPinned]);
|
|
6261
6649
|
const handleSlider = (0,external_react_.useCallback)(newValue => {
|
|
6262
6650
|
onZoomChange(zoomFromSliderValue(newValue, maxZoom));
|
|
6263
6651
|
}, [maxZoom, onZoomChange]);
|
|
6264
6652
|
const handleStep = (0,external_react_.useCallback)(delta => {
|
|
6265
6653
|
onZoomChange(zoomFromSliderValue(zoomValue + delta, maxZoom));
|
|
6266
6654
|
}, [maxZoom, onZoomChange, zoomValue]);
|
|
6655
|
+
const handleZoomInPointerDown = (0,external_react_.useCallback)(() => {
|
|
6656
|
+
wasFlyoutOpenOnPointerDownRef.current = isHovered || isFocused || isRevealed || isPinned;
|
|
6657
|
+
}, [isFocused, isHovered, isPinned, isRevealed]);
|
|
6658
|
+
const handleZoomInClick = (0,external_react_.useCallback)(() => {
|
|
6659
|
+
if (!wasFlyoutOpenOnPointerDownRef.current) {
|
|
6660
|
+
setPinned(true);
|
|
6661
|
+
return;
|
|
6662
|
+
}
|
|
6663
|
+
if (!isAtMaxZoom) {
|
|
6664
|
+
handleStep(constants/* WAVEFORM_ZOOM_BUTTON_STEP */.QD);
|
|
6665
|
+
}
|
|
6666
|
+
}, [handleStep, isAtMaxZoom]);
|
|
6267
6667
|
return /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
6668
|
+
ref: zoomControlElRef,
|
|
6268
6669
|
"aria-label": "Zoom",
|
|
6269
6670
|
className: classnames_default()('bp-WaveformZoomControl', {
|
|
6270
6671
|
'bp-is-open': isOpen
|
|
@@ -6275,6 +6676,7 @@ function WaveformZoomControl({
|
|
|
6275
6676
|
return;
|
|
6276
6677
|
}
|
|
6277
6678
|
setFocused(false);
|
|
6679
|
+
setPinned(false);
|
|
6278
6680
|
},
|
|
6279
6681
|
onFocus: () => {
|
|
6280
6682
|
clearDismiss();
|
|
@@ -6292,15 +6694,12 @@ function WaveformZoomControl({
|
|
|
6292
6694
|
},
|
|
6293
6695
|
role: "group"
|
|
6294
6696
|
}, /*#__PURE__*/external_react_["default"].createElement(MediaToggle/* default */.A, {
|
|
6295
|
-
"aria-disabled": isAtMaxZoom,
|
|
6697
|
+
"aria-disabled": isOpen && isAtMaxZoom,
|
|
6296
6698
|
className: "bp-WaveformZoomControl-button",
|
|
6297
6699
|
"data-resin-target": "waveformZoomIn",
|
|
6298
6700
|
"data-testid": "bp-waveform-zoom-in",
|
|
6299
|
-
onClick:
|
|
6300
|
-
|
|
6301
|
-
handleStep(constants/* WAVEFORM_ZOOM_BUTTON_STEP */.QD);
|
|
6302
|
-
}
|
|
6303
|
-
},
|
|
6701
|
+
onClick: handleZoomInClick,
|
|
6702
|
+
onPointerDown: handleZoomInPointerDown,
|
|
6304
6703
|
title: "Zoom in"
|
|
6305
6704
|
}, /*#__PURE__*/external_react_["default"].createElement(icons_IconZoomIn24, null)), /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
6306
6705
|
"aria-hidden": !isOpen,
|
|
@@ -6354,6 +6753,7 @@ function WaveformZoomControl({
|
|
|
6354
6753
|
|
|
6355
6754
|
|
|
6356
6755
|
|
|
6756
|
+
|
|
6357
6757
|
const PLACEHOLDER_PEAKS = placeholderPeaks();
|
|
6358
6758
|
function MP3ControlsV2({
|
|
6359
6759
|
autoplay,
|
|
@@ -6375,16 +6775,21 @@ function MP3ControlsV2({
|
|
|
6375
6775
|
volume
|
|
6376
6776
|
}) {
|
|
6377
6777
|
const durationValue = typeof durationTime === 'number' && isFinite_default()(durationTime) ? durationTime : 0;
|
|
6778
|
+
const mediaDuration = mediaEl ? mediaEl.duration : NaN;
|
|
6779
|
+
const hasMediaMetadata = typeof mediaDuration === 'number' && Number.isFinite(mediaDuration) && mediaDuration > 0;
|
|
6780
|
+
const hasWaveformDuration = durationValue > 0;
|
|
6378
6781
|
const [zoomLevel, setZoomLevel] = (0,external_react_.useState)(constants/* WAVEFORM_ZOOM_MIN */.LW);
|
|
6379
6782
|
const [maxZoom, setMaxZoom] = (0,external_react_.useState)(constants/* WAVEFORM_ZOOM_MIN */.LW);
|
|
6380
6783
|
const [isZoomRevealed, setIsZoomRevealed] = (0,external_react_.useState)(false);
|
|
6381
|
-
const zoomRevealTimerRef = (0,external_react_.useRef)(0);
|
|
6784
|
+
const zoomRevealTimerRef = (0,external_react_.useRef)(0); // hides the zoom flyout after WAVEFORM_ZOOM_DISMISS_MS
|
|
6382
6785
|
const hasRealPeaks = !!(peaks && peaks.length);
|
|
6383
6786
|
const waveformPeaks = hasRealPeaks ? peaks : PLACEHOLDER_PEAKS;
|
|
6384
|
-
const
|
|
6385
|
-
const waveformDurationSec = hasMetadata ? durationValue : PLACEHOLDER_DURATION_SEC;
|
|
6787
|
+
const waveformDurationSec = hasWaveformDuration ? durationValue : PLACEHOLDER_DURATION_SEC;
|
|
6386
6788
|
const [playRequested, setPlayRequested] = (0,external_react_.useState)(false);
|
|
6387
6789
|
const [viewport, setViewport] = (0,external_react_.useState)(null);
|
|
6790
|
+
const isTape = useTapeWaveform();
|
|
6791
|
+
const hasAppliedTapeDefaultZoomRef = (0,external_react_.useRef)(false); // ~10s window applied; reset to 1× when leaving tape
|
|
6792
|
+
const userChangedTapeZoomRef = (0,external_react_.useRef)(false); // pinch/wheel zoom; skip re-applying the 10s default
|
|
6388
6793
|
const handleViewportChange = (0,external_react_.useCallback)(next => {
|
|
6389
6794
|
setMaxZoom(prev => prev === next.maxZoom ? prev : next.maxZoom);
|
|
6390
6795
|
setViewport(prev => viewportEquals(prev, next) ? prev : next);
|
|
@@ -6398,12 +6803,33 @@ function MP3ControlsV2({
|
|
|
6398
6803
|
}, constants/* WAVEFORM_ZOOM_DISMISS_MS */.m6);
|
|
6399
6804
|
}, []);
|
|
6400
6805
|
const handleWaveformZoom = (0,external_react_.useCallback)(nextZoom => {
|
|
6806
|
+
userChangedTapeZoomRef.current = true;
|
|
6401
6807
|
setZoomLevel(nextZoom);
|
|
6402
6808
|
revealZoomControl();
|
|
6403
6809
|
}, [revealZoomControl]);
|
|
6404
6810
|
(0,external_react_.useEffect)(() => {
|
|
6405
6811
|
setZoomLevel(prev => clampWaveformZoom(prev, maxZoom));
|
|
6406
6812
|
}, [maxZoom]);
|
|
6813
|
+
(0,external_react_.useEffect)(() => {
|
|
6814
|
+
if (!isTape) {
|
|
6815
|
+
if (hasAppliedTapeDefaultZoomRef.current) {
|
|
6816
|
+
hasAppliedTapeDefaultZoomRef.current = false;
|
|
6817
|
+
userChangedTapeZoomRef.current = false;
|
|
6818
|
+
setZoomLevel(constants/* WAVEFORM_ZOOM_MIN */.LW);
|
|
6819
|
+
}
|
|
6820
|
+
return;
|
|
6821
|
+
}
|
|
6822
|
+
if (!hasWaveformDuration || !hasRealPeaks || !viewport || !(viewport.widthPx > 0)) {
|
|
6823
|
+
return;
|
|
6824
|
+
}
|
|
6825
|
+
if (userChangedTapeZoomRef.current) {
|
|
6826
|
+
hasAppliedTapeDefaultZoomRef.current = true;
|
|
6827
|
+
return;
|
|
6828
|
+
}
|
|
6829
|
+
const nextZoom = getTapeDefaultZoom(durationValue, Math.max(maxZoom, viewport.maxZoom));
|
|
6830
|
+
hasAppliedTapeDefaultZoomRef.current = true;
|
|
6831
|
+
setZoomLevel(prev => prev === nextZoom ? prev : nextZoom);
|
|
6832
|
+
}, [durationValue, hasRealPeaks, hasWaveformDuration, isTape, maxZoom, viewport]);
|
|
6407
6833
|
(0,external_react_.useEffect)(() => () => window.clearTimeout(zoomRevealTimerRef.current), []);
|
|
6408
6834
|
(0,external_react_.useEffect)(() => {
|
|
6409
6835
|
if (isPlaying) {
|
|
@@ -6427,11 +6853,12 @@ function MP3ControlsV2({
|
|
|
6427
6853
|
}
|
|
6428
6854
|
onTimeChange(marker.time);
|
|
6429
6855
|
}, [onCommentMarkerClick, onPlayPause, onTimeChange]);
|
|
6430
|
-
const isWaveformInteractive = playRequested &&
|
|
6431
|
-
const isWaitingToPlay = playRequested && !
|
|
6856
|
+
const isWaveformInteractive = playRequested && hasMediaMetadata;
|
|
6857
|
+
const isWaitingToPlay = playRequested && !hasMediaMetadata;
|
|
6432
6858
|
const showPlayOverlay = !playRequested && !isPlaying;
|
|
6433
6859
|
const hasZoomHandlers = hasRealPeaks && !showPlayOverlay;
|
|
6434
|
-
const hasZoomControl = hasZoomHandlers &&
|
|
6860
|
+
const hasZoomControl = hasZoomHandlers && hasMediaMetadata && maxZoom > constants/* WAVEFORM_ZOOM_MIN */.LW;
|
|
6861
|
+
const waveformZoomLevel = isTape || hasZoomHandlers ? zoomLevel : constants/* WAVEFORM_ZOOM_MIN */.LW;
|
|
6435
6862
|
return /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
6436
6863
|
className: "bp-MP3ControlsV2",
|
|
6437
6864
|
"data-testid": "media-controls-wrapper-v2"
|
|
@@ -6441,18 +6868,22 @@ function MP3ControlsV2({
|
|
|
6441
6868
|
className: "bp-MP3ControlsV2-waveform"
|
|
6442
6869
|
}, /*#__PURE__*/external_react_["default"].createElement(waveform_WaveformView, {
|
|
6443
6870
|
bufferedRange: bufferedRange,
|
|
6871
|
+
cameraMode: isTape ? 'tape' : 'desktop',
|
|
6444
6872
|
currentTime: currentTime,
|
|
6445
6873
|
durationSec: waveformDurationSec,
|
|
6446
6874
|
interactive: isWaveformInteractive,
|
|
6875
|
+
isPlaying: isPlaying,
|
|
6447
6876
|
mediaEl: mediaEl,
|
|
6877
|
+
onPlayPause: isWaveformInteractive ? onPlayPause : undefined,
|
|
6448
6878
|
onSeek: isWaveformInteractive ? onTimeChange : undefined,
|
|
6449
6879
|
onViewportChange: hasRealPeaks ? handleViewportChange : undefined,
|
|
6450
6880
|
onZoomChange: hasZoomHandlers ? handleWaveformZoom : undefined,
|
|
6451
6881
|
peaks: waveformPeaks,
|
|
6452
|
-
zoomLevel:
|
|
6882
|
+
zoomLevel: waveformZoomLevel
|
|
6453
6883
|
}), /*#__PURE__*/external_react_["default"].createElement(WaveformCommentMarkers, {
|
|
6454
6884
|
commentMarkers: waveformMarkers,
|
|
6455
|
-
durationSec:
|
|
6885
|
+
durationSec: hasWaveformDuration ? durationValue : 0,
|
|
6886
|
+
isTape: isTape,
|
|
6456
6887
|
onCommentMarkerClick: handleCommentMarkerClick,
|
|
6457
6888
|
selectedId: selectedMarkerId,
|
|
6458
6889
|
viewport: viewport
|
|
@@ -6461,7 +6892,7 @@ function MP3ControlsV2({
|
|
|
6461
6892
|
}, /*#__PURE__*/external_react_["default"].createElement(WaveformZoomControl, {
|
|
6462
6893
|
isRevealed: isZoomRevealed,
|
|
6463
6894
|
maxZoom: maxZoom,
|
|
6464
|
-
onZoomChange:
|
|
6895
|
+
onZoomChange: handleWaveformZoom,
|
|
6465
6896
|
zoomLevel: zoomLevel
|
|
6466
6897
|
})), showPlayOverlay && /*#__PURE__*/external_react_["default"].createElement("button", {
|
|
6467
6898
|
className: "bp-MP3ControlsV2-playOverlay"
|
|
@@ -6479,7 +6910,7 @@ function MP3ControlsV2({
|
|
|
6479
6910
|
}), isWaitingToPlay && /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
6480
6911
|
className: "bp-media-buffering-spinner",
|
|
6481
6912
|
"data-testid": "bp-MP3ControlsV2-loading"
|
|
6482
|
-
})),
|
|
6913
|
+
})), hasMediaMetadata && /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
6483
6914
|
className: "bp-MP3ControlsV2-bar",
|
|
6484
6915
|
"data-testid": "bp-MP3ControlsV2-bar"
|
|
6485
6916
|
}, /*#__PURE__*/external_react_["default"].createElement("div", {
|
|
@@ -6581,6 +7012,7 @@ function isFpsAvailable(player) {
|
|
|
6581
7012
|
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
6582
7013
|
|
|
6583
7014
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
7015
|
+
/* harmony export */ Av: () => (/* binding */ WAVEFORM_TAPE_DEFAULT_WINDOW_SEC),
|
|
6584
7016
|
/* harmony export */ Bq: () => (/* binding */ WAVEFORM_PLAYHEAD_JUMP_MS),
|
|
6585
7017
|
/* harmony export */ DY: () => (/* binding */ WAVEFORM_BAR_RADIUS),
|
|
6586
7018
|
/* harmony export */ EB: () => (/* binding */ CLIENT_DECODE_MAX_COMPRESSED_BYTES),
|
|
@@ -6588,12 +7020,14 @@ function isFpsAvailable(player) {
|
|
|
6588
7020
|
/* harmony export */ GQ: () => (/* binding */ DURATION_MISMATCH_TOLERANCE_SEC),
|
|
6589
7021
|
/* harmony export */ Kl: () => (/* binding */ WAVEFORM_MIN_VIEW_WINDOW_SEC),
|
|
6590
7022
|
/* harmony export */ LW: () => (/* binding */ WAVEFORM_ZOOM_MIN),
|
|
7023
|
+
/* harmony export */ Le: () => (/* binding */ TAPE_POINTER_MEDIA_QUERY),
|
|
6591
7024
|
/* harmony export */ Lu: () => (/* binding */ WAVEFORM_BAR_GAP),
|
|
6592
7025
|
/* harmony export */ NM: () => (/* binding */ WAVEFORM_FOLLOW_INSET_PX),
|
|
6593
7026
|
/* harmony export */ QD: () => (/* binding */ WAVEFORM_ZOOM_BUTTON_STEP),
|
|
6594
7027
|
/* harmony export */ XS: () => (/* binding */ WAVEFORM_BAR_MIN_HEIGHT),
|
|
6595
7028
|
/* harmony export */ Zs: () => (/* binding */ WAVEFORM_FOLLOW_SCROLL_SETTLE_MS),
|
|
6596
7029
|
/* harmony export */ f3: () => (/* binding */ CLIENT_DECODE_PEAK_COUNT),
|
|
7030
|
+
/* harmony export */ fI: () => (/* binding */ WAVEFORM_TAPE_CLICK_SUPPRESS_MS),
|
|
6597
7031
|
/* harmony export */ i8: () => (/* binding */ PEAK_UNIT_MAX),
|
|
6598
7032
|
/* harmony export */ m6: () => (/* binding */ WAVEFORM_ZOOM_DISMISS_MS),
|
|
6599
7033
|
/* harmony export */ mJ: () => (/* binding */ PEAK_UNIT_MIN),
|
|
@@ -6633,6 +7067,10 @@ const WAVEFORM_ZOOM_MIN = 1;
|
|
|
6633
7067
|
const WAVEFORM_ZOOM_MAX = 24;
|
|
6634
7068
|
/** Visible window never shorter than this. */
|
|
6635
7069
|
const WAVEFORM_MIN_VIEW_WINDOW_SEC = 4;
|
|
7070
|
+
/** Tape (phone/tablet) default: seconds of audio visible in the viewport. */
|
|
7071
|
+
const WAVEFORM_TAPE_DEFAULT_WINDOW_SEC = 10;
|
|
7072
|
+
/** Primary pointer is a finger, not a mouse/trackpad. */
|
|
7073
|
+
const TAPE_POINTER_MEDIA_QUERY = '(hover: none) and (pointer: coarse)';
|
|
6636
7074
|
const WAVEFORM_ZOOM_SLIDER_MAX = 100;
|
|
6637
7075
|
/** One click on zoom in/out moves this many units on the 0–100 slider. */
|
|
6638
7076
|
const WAVEFORM_ZOOM_BUTTON_STEP = 10;
|
|
@@ -6641,6 +7079,8 @@ const WAVEFORM_FOLLOW_INSET_PX = 200;
|
|
|
6641
7079
|
const WAVEFORM_PLAYHEAD_JUMP_MS = 400;
|
|
6642
7080
|
/** After the last user pan, wait this long before the camera may pin again. */
|
|
6643
7081
|
const WAVEFORM_FOLLOW_SCROLL_SETTLE_MS = 150;
|
|
7082
|
+
/** Ignore a WaveSurfer click after a tape swipe (iOS delayed click). Must outlast the scrub-chip hide. */
|
|
7083
|
+
const WAVEFORM_TAPE_CLICK_SUPPRESS_MS = 200;
|
|
6644
7084
|
const WAVEFORM_BAR_GAP = 2;
|
|
6645
7085
|
const WAVEFORM_BAR_WIDTH = 2;
|
|
6646
7086
|
const WAVEFORM_BAR_RADIUS = WAVEFORM_BAR_WIDTH / 2;
|
|
@@ -6650,25 +7090,19 @@ const WAVEFORM_HEIGHT = 140;
|
|
|
6650
7090
|
|
|
6651
7091
|
/***/ },
|
|
6652
7092
|
|
|
6653
|
-
/***/
|
|
7093
|
+
/***/ 4971
|
|
6654
7094
|
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
6655
7095
|
|
|
6656
7096
|
|
|
6657
7097
|
// EXPORTS
|
|
6658
7098
|
__webpack_require__.d(__webpack_exports__, {
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
7099
|
+
oz: () => (/* binding */ WaveformLoadError),
|
|
7100
|
+
iY: () => (/* binding */ createCappedWaveformState),
|
|
7101
|
+
nR: () => (/* binding */ createWaveformLoader),
|
|
7102
|
+
Vq: () => (/* binding */ errorFromUnknown),
|
|
7103
|
+
zf: () => (/* binding */ isAbortError)
|
|
6664
7104
|
});
|
|
6665
|
-
// ESM COMPAT FLAG
|
|
6666
|
-
__webpack_require__.r(__webpack_exports__);
|
|
6667
7105
|
|
|
6668
|
-
// EXTERNAL MODULE: ./src/lib/util.js + 1 modules
|
|
6669
|
-
var util = __webpack_require__(4410);
|
|
6670
|
-
// EXTERNAL MODULE: ./src/lib/viewers/media/waveform/constants.ts
|
|
6671
|
-
var constants = __webpack_require__(4929);
|
|
6672
7106
|
;// ./src/lib/viewers/media/waveform/types.ts
|
|
6673
7107
|
/**
|
|
6674
7108
|
* Box V1 is the in-viewer form: unsigned mono peaks in [0, 1] (peak envelope, mono_max).
|
|
@@ -6698,15 +7132,21 @@ function isWaveformErrorCode(value) {
|
|
|
6698
7132
|
|
|
6699
7133
|
/** Visible slice of the timeline. Emitted whenever zoom, scroll, or width changes. */
|
|
6700
7134
|
|
|
7135
|
+
/** Navigator fields used to detect iPad, including iPadOS reporting itself as Mac. */
|
|
7136
|
+
|
|
7137
|
+
/** Window bits needed to choose tape vs desktop (coarse pointer + iPad). */
|
|
7138
|
+
|
|
7139
|
+
/** Desktop walks then pins near the right inset. Tape keeps the playhead at center. */
|
|
7140
|
+
|
|
6701
7141
|
/** One canvas linear-gradient stop. `offset` is 0–1 along the bar. */
|
|
6702
7142
|
|
|
6703
7143
|
/**
|
|
6704
7144
|
* Wavesurfer's two paints: left of the playhead (`progressColor`) and right of it (`waveColor`).
|
|
6705
7145
|
* A string is a solid fill; a stop list is a left-to-right step gradient.
|
|
6706
7146
|
*/
|
|
7147
|
+
// EXTERNAL MODULE: ./src/lib/viewers/media/waveform/validateWaveformPayload.ts
|
|
7148
|
+
var validateWaveformPayload = __webpack_require__(1053);
|
|
6707
7149
|
;// ./src/lib/viewers/media/waveform/createWaveformLoader.ts
|
|
6708
|
-
/* unused harmony import specifier */ var isRetryableWaveformError;
|
|
6709
|
-
/* unused harmony import specifier */ var validateWaveformPayload;
|
|
6710
7150
|
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
6711
7151
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
6712
7152
|
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
@@ -6753,7 +7193,7 @@ function failedLoadState(error) {
|
|
|
6753
7193
|
return {
|
|
6754
7194
|
status: 'failed',
|
|
6755
7195
|
error,
|
|
6756
|
-
retryable: isRetryableWaveformError(error.code)
|
|
7196
|
+
retryable: (0,validateWaveformPayload/* isRetryableWaveformError */.Oe)(error.code)
|
|
6757
7197
|
};
|
|
6758
7198
|
}
|
|
6759
7199
|
function readyLoadState(payload) {
|
|
@@ -6835,7 +7275,7 @@ function createWaveformLoader(fetchPayload, options = {}) {
|
|
|
6835
7275
|
if (staleAfterFetch) {
|
|
6836
7276
|
return staleAfterFetch;
|
|
6837
7277
|
}
|
|
6838
|
-
const validation = validateWaveformPayload(raw, options);
|
|
7278
|
+
const validation = (0,validateWaveformPayload/* validateWaveformPayload */.a9)(raw, options);
|
|
6839
7279
|
const staleAfterValidate = cancelledIfStale(request);
|
|
6840
7280
|
if (staleAfterValidate) {
|
|
6841
7281
|
return staleAfterValidate;
|
|
@@ -6877,185 +7317,29 @@ function createCappedWaveformState(message) {
|
|
|
6877
7317
|
}
|
|
6878
7318
|
};
|
|
6879
7319
|
}
|
|
6880
|
-
;// ./src/lib/viewers/media/waveform/validateWaveformPayload.ts
|
|
6881
7320
|
|
|
6882
|
-
|
|
6883
|
-
const DEFAULT_CHANNEL_POLICY = 'mono_max';
|
|
6884
|
-
const DEFAULT_ENVELOPE = 'peak';
|
|
6885
|
-
function fail(code, message, retryable = false) {
|
|
6886
|
-
return {
|
|
6887
|
-
ok: false,
|
|
6888
|
-
error: {
|
|
6889
|
-
code,
|
|
6890
|
-
message
|
|
6891
|
-
},
|
|
6892
|
-
retryable
|
|
6893
|
-
};
|
|
6894
|
-
}
|
|
6895
|
-
function isPlainObject(value) {
|
|
6896
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6897
|
-
}
|
|
6898
|
-
function payloadByteLength(raw) {
|
|
6899
|
-
try {
|
|
6900
|
-
const serialized = JSON.stringify(raw);
|
|
6901
|
-
if (typeof Buffer !== 'undefined') {
|
|
6902
|
-
return Buffer.byteLength(serialized, 'utf8');
|
|
6903
|
-
}
|
|
6904
|
-
if (typeof TextEncoder !== 'undefined') {
|
|
6905
|
-
return new TextEncoder().encode(serialized).length;
|
|
6906
|
-
}
|
|
6907
|
-
return serialized.length;
|
|
6908
|
-
} catch {
|
|
6909
|
-
return Number.MAX_SAFE_INTEGER;
|
|
6910
|
-
}
|
|
6911
|
-
}
|
|
6912
|
-
function readOptionalPolicy(raw, key, allowed) {
|
|
6913
|
-
if (!(key in raw) || raw[key] === undefined) {
|
|
6914
|
-
return undefined;
|
|
6915
|
-
}
|
|
6916
|
-
return raw[key] === allowed ? allowed : null;
|
|
6917
|
-
}
|
|
6918
|
-
function asWaveformPayloadV1(raw) {
|
|
6919
|
-
const {
|
|
6920
|
-
version,
|
|
6921
|
-
durationSec,
|
|
6922
|
-
peaks
|
|
6923
|
-
} = raw;
|
|
6924
|
-
if (version !== constants/* WAVEFORM_PAYLOAD_VERSION */.sh || typeof durationSec !== 'number' || !Array.isArray(peaks)) {
|
|
6925
|
-
return null;
|
|
6926
|
-
}
|
|
6927
|
-
const peakScale = readOptionalPolicy(raw, 'peakScale', DEFAULT_PEAK_SCALE);
|
|
6928
|
-
const channelPolicy = readOptionalPolicy(raw, 'channelPolicy', DEFAULT_CHANNEL_POLICY);
|
|
6929
|
-
const envelope = readOptionalPolicy(raw, 'envelope', DEFAULT_ENVELOPE);
|
|
6930
|
-
if (peakScale === null || channelPolicy === null || envelope === null) {
|
|
6931
|
-
return null;
|
|
6932
|
-
}
|
|
6933
|
-
if ('channels' in raw && raw.channels !== undefined && raw.channels !== 1) {
|
|
6934
|
-
return null;
|
|
6935
|
-
}
|
|
6936
|
-
return {
|
|
6937
|
-
version: constants/* WAVEFORM_PAYLOAD_VERSION */.sh,
|
|
6938
|
-
durationSec,
|
|
6939
|
-
peaks: peaks,
|
|
6940
|
-
peakScale: peakScale ?? DEFAULT_PEAK_SCALE,
|
|
6941
|
-
channelPolicy: channelPolicy ?? DEFAULT_CHANNEL_POLICY,
|
|
6942
|
-
envelope: envelope ?? DEFAULT_ENVELOPE,
|
|
6943
|
-
channels: 1
|
|
6944
|
-
};
|
|
6945
|
-
}
|
|
6946
|
-
function readPayloadObject(raw, maxPayloadBytes, isPayloadByteCheckSkipped = false) {
|
|
6947
|
-
if (raw === null || raw === undefined) {
|
|
6948
|
-
return fail('INVALID_PAYLOAD', 'Payload is null or undefined');
|
|
6949
|
-
}
|
|
6950
|
-
if (!isPayloadByteCheckSkipped) {
|
|
6951
|
-
const byteLength = payloadByteLength(raw);
|
|
6952
|
-
if (byteLength > maxPayloadBytes) {
|
|
6953
|
-
return fail('PAYLOAD_TOO_LARGE', `Payload size ${byteLength} exceeds limit ${maxPayloadBytes}`);
|
|
6954
|
-
}
|
|
6955
|
-
}
|
|
6956
|
-
if (!isPlainObject(raw)) {
|
|
6957
|
-
return fail('INVALID_PAYLOAD', 'Payload must be an object');
|
|
6958
|
-
}
|
|
6959
|
-
return {
|
|
6960
|
-
ok: true,
|
|
6961
|
-
value: raw
|
|
6962
|
-
};
|
|
6963
|
-
}
|
|
6964
|
-
function checkVersion(raw) {
|
|
6965
|
-
if (raw.version === constants/* WAVEFORM_PAYLOAD_VERSION */.sh) {
|
|
6966
|
-
return null;
|
|
6967
|
-
}
|
|
6968
|
-
const {
|
|
6969
|
-
version
|
|
6970
|
-
} = raw;
|
|
6971
|
-
if (typeof version === 'number' && version > constants/* WAVEFORM_PAYLOAD_VERSION */.sh) {
|
|
6972
|
-
return fail('UNSUPPORTED_VERSION', `Unsupported waveform version ${version}; max supported is ${constants/* WAVEFORM_PAYLOAD_VERSION */.sh}`);
|
|
6973
|
-
}
|
|
6974
|
-
return fail('INVALID_PAYLOAD', 'Missing or invalid version field');
|
|
6975
|
-
}
|
|
6976
|
-
function checkDuration(payload, expectedDurationSec) {
|
|
6977
|
-
if (!Number.isFinite(payload.durationSec) || payload.durationSec <= 0) {
|
|
6978
|
-
return fail('INVALID_DURATION', 'durationSec must be a positive finite number');
|
|
6979
|
-
}
|
|
6980
|
-
if (expectedDurationSec !== undefined && Number.isFinite(expectedDurationSec) && Math.abs(payload.durationSec - expectedDurationSec) > constants/* DURATION_MISMATCH_TOLERANCE_SEC */.GQ) {
|
|
6981
|
-
return fail('DURATION_MISMATCH', `Payload duration ${payload.durationSec}s differs from media duration ${expectedDurationSec}s`, true);
|
|
6982
|
-
}
|
|
6983
|
-
return null;
|
|
6984
|
-
}
|
|
6985
|
-
function normalizePeaks(peaks, maxPeakCount) {
|
|
6986
|
-
if (peaks.length === 0) {
|
|
6987
|
-
return fail('EMPTY_PEAKS', 'peaks array must not be empty');
|
|
6988
|
-
}
|
|
6989
|
-
if (peaks.length > maxPeakCount) {
|
|
6990
|
-
return fail('PEAK_COUNT_EXCEEDED', `peaks length ${peaks.length} exceeds max ${maxPeakCount}`);
|
|
6991
|
-
}
|
|
6992
|
-
const normalized = new Float32Array(peaks.length);
|
|
6993
|
-
for (let i = 0; i < peaks.length; i += 1) {
|
|
6994
|
-
const value = peaks[i];
|
|
6995
|
-
if (!Number.isFinite(value)) {
|
|
6996
|
-
return fail('NON_FINITE_PEAK', `Peak at index ${i} is not finite`);
|
|
6997
|
-
}
|
|
6998
|
-
if (value < constants/* PEAK_UNIT_MIN */.mJ || value > constants/* PEAK_UNIT_MAX */.i8) {
|
|
6999
|
-
return fail('PEAK_OUT_OF_RANGE', `Peak at index ${i} is outside [${constants/* PEAK_UNIT_MIN */.mJ}, ${constants/* PEAK_UNIT_MAX */.i8}]`);
|
|
7000
|
-
}
|
|
7001
|
-
normalized[i] = value;
|
|
7002
|
-
}
|
|
7003
|
-
return {
|
|
7004
|
-
ok: true,
|
|
7005
|
-
peaks: normalized
|
|
7006
|
-
};
|
|
7007
|
-
}
|
|
7321
|
+
/***/ },
|
|
7008
7322
|
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
* Wire JSON requires version, durationSec, and peaks. sampleCount and source are ignored.
|
|
7012
|
-
*/
|
|
7013
|
-
function validateWaveformPayload_validateWaveformPayload(raw, options = {}) {
|
|
7014
|
-
const maxPeakCount = options.maxPeakCount ?? constants/* MAX_PEAK_COUNT */.FY;
|
|
7015
|
-
const maxPayloadBytes = options.maxPayloadBytes ?? constants/* MAX_PAYLOAD_BYTES */.qY;
|
|
7016
|
-
const objectResult = readPayloadObject(raw, maxPayloadBytes, options.isPayloadByteCheckSkipped === true);
|
|
7017
|
-
if (!objectResult.ok) {
|
|
7018
|
-
return objectResult;
|
|
7019
|
-
}
|
|
7020
|
-
const versionFailure = checkVersion(objectResult.value);
|
|
7021
|
-
if (versionFailure) {
|
|
7022
|
-
return versionFailure;
|
|
7023
|
-
}
|
|
7024
|
-
const payload = asWaveformPayloadV1(objectResult.value);
|
|
7025
|
-
if (!payload) {
|
|
7026
|
-
return fail('INVALID_PAYLOAD', 'Payload failed structural validation');
|
|
7027
|
-
}
|
|
7028
|
-
const durationFailure = checkDuration(payload, options.expectedDurationSec);
|
|
7029
|
-
if (durationFailure) {
|
|
7030
|
-
return durationFailure;
|
|
7031
|
-
}
|
|
7032
|
-
const peaksResult = normalizePeaks(payload.peaks, maxPeakCount);
|
|
7033
|
-
if (!peaksResult.ok) {
|
|
7034
|
-
return peaksResult;
|
|
7035
|
-
}
|
|
7036
|
-
return {
|
|
7037
|
-
ok: true,
|
|
7038
|
-
payload: {
|
|
7039
|
-
version: constants/* WAVEFORM_PAYLOAD_VERSION */.sh,
|
|
7040
|
-
durationSec: payload.durationSec,
|
|
7041
|
-
peaks: peaksResult.peaks,
|
|
7042
|
-
peakScale: payload.peakScale ?? DEFAULT_PEAK_SCALE,
|
|
7043
|
-
channelPolicy: payload.channelPolicy ?? DEFAULT_CHANNEL_POLICY,
|
|
7044
|
-
envelope: payload.envelope ?? DEFAULT_ENVELOPE
|
|
7045
|
-
}
|
|
7046
|
-
};
|
|
7047
|
-
}
|
|
7323
|
+
/***/ 8296
|
|
7324
|
+
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
7048
7325
|
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7326
|
+
__webpack_require__.r(__webpack_exports__);
|
|
7327
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
7328
|
+
/* harmony export */ decodeToPeaks: () => (/* binding */ decodeToPeaks),
|
|
7329
|
+
/* harmony export */ extractPeaks: () => (/* binding */ extractPeaks),
|
|
7330
|
+
/* harmony export */ getDecodeDecision: () => (/* binding */ getDecodeDecision),
|
|
7331
|
+
/* harmony export */ loadPeaks: () => (/* binding */ loadPeaks),
|
|
7332
|
+
/* harmony export */ runClientDecode: () => (/* binding */ runClientDecode)
|
|
7333
|
+
/* harmony export */ });
|
|
7334
|
+
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4410);
|
|
7335
|
+
/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4929);
|
|
7336
|
+
/* harmony import */ var _createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4971);
|
|
7337
|
+
/* harmony import */ var _validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1053);
|
|
7054
7338
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
7055
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
|
|
7056
|
-
function
|
|
7057
|
-
function
|
|
7058
|
-
function
|
|
7339
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
7340
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
7341
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
7342
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
7059
7343
|
|
|
7060
7344
|
|
|
7061
7345
|
|
|
@@ -7067,9 +7351,6 @@ const EMPTY_TIMINGS = {
|
|
|
7067
7351
|
function createAbortError() {
|
|
7068
7352
|
return new DOMException('Aborted', 'AbortError');
|
|
7069
7353
|
}
|
|
7070
|
-
function isPositiveFinite(value) {
|
|
7071
|
-
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
7072
|
-
}
|
|
7073
7354
|
function getAudioContextConstructor() {
|
|
7074
7355
|
if (typeof window === 'undefined') {
|
|
7075
7356
|
return undefined;
|
|
@@ -7102,7 +7383,7 @@ function createSkippedDecodeResult(reason) {
|
|
|
7102
7383
|
reason
|
|
7103
7384
|
};
|
|
7104
7385
|
}
|
|
7105
|
-
return _objectSpread(_objectSpread({}, createCappedWaveformState(getDecodeSkipMessage(reason))), {}, {
|
|
7386
|
+
return _objectSpread(_objectSpread({}, (0,_createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .createCappedWaveformState */ .iY)(getDecodeSkipMessage(reason))), {}, {
|
|
7106
7387
|
isDecodeSkipped: true,
|
|
7107
7388
|
timings: EMPTY_TIMINGS,
|
|
7108
7389
|
reason
|
|
@@ -7126,11 +7407,11 @@ function decodeWithContext(context, buffer, signal) {
|
|
|
7126
7407
|
}
|
|
7127
7408
|
isSettled = true;
|
|
7128
7409
|
signal?.removeEventListener('abort', onAbort);
|
|
7129
|
-
if (isAbortError(error)) {
|
|
7410
|
+
if ((0,_createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .isAbortError */ .zf)(error)) {
|
|
7130
7411
|
reject(error instanceof DOMException ? error : createAbortError());
|
|
7131
7412
|
return;
|
|
7132
7413
|
}
|
|
7133
|
-
reject(error instanceof WaveformLoadError ? error : new WaveformLoadError('DECODE_FAILED', 'decodeAudioData failed'));
|
|
7414
|
+
reject(error instanceof _createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .WaveformLoadError */ .oz ? error : new _createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .WaveformLoadError */ .oz('DECODE_FAILED', 'decodeAudioData failed'));
|
|
7134
7415
|
};
|
|
7135
7416
|
onAbort = () => onFailure(createAbortError());
|
|
7136
7417
|
if (signal?.aborted) {
|
|
@@ -7156,13 +7437,13 @@ function decodeWithContext(context, buffer, signal) {
|
|
|
7156
7437
|
* skips decode so playback is never blocked by expanding the full audio buffer.
|
|
7157
7438
|
*/
|
|
7158
7439
|
function getDecodeDecision(media, caps = {}) {
|
|
7159
|
-
const maxCompressedBytes = caps.maxCompressedBytes ??
|
|
7160
|
-
const maxDurationSec = caps.maxDurationSec ??
|
|
7440
|
+
const maxCompressedBytes = caps.maxCompressedBytes ?? _constants__WEBPACK_IMPORTED_MODULE_1__/* .CLIENT_DECODE_MAX_COMPRESSED_BYTES */ .EB;
|
|
7441
|
+
const maxDurationSec = caps.maxDurationSec ?? _constants__WEBPACK_IMPORTED_MODULE_1__/* .CLIENT_DECODE_MAX_DURATION_SEC */ .nG;
|
|
7161
7442
|
const {
|
|
7162
7443
|
compressedBytes,
|
|
7163
7444
|
durationSec
|
|
7164
7445
|
} = media;
|
|
7165
|
-
if (!isPositiveFinite(compressedBytes) || !isPositiveFinite(durationSec)) {
|
|
7446
|
+
if (!(0,_validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__/* .isPositiveFinite */ .Ze)(compressedBytes) || !(0,_validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__/* .isPositiveFinite */ .Ze)(durationSec)) {
|
|
7166
7447
|
return {
|
|
7167
7448
|
isAllowed: false,
|
|
7168
7449
|
reason: 'missing_metadata'
|
|
@@ -7189,7 +7470,7 @@ function getDecodeDecision(media, caps = {}) {
|
|
|
7189
7470
|
* Collapse audio channels to one unsigned peak per time bucket (max abs, then clamp to unit).
|
|
7190
7471
|
* Accepts an AudioBuffer so callers can extract before closing the AudioContext.
|
|
7191
7472
|
*/
|
|
7192
|
-
function extractPeaks(audio, peakCount =
|
|
7473
|
+
function extractPeaks(audio, peakCount = _constants__WEBPACK_IMPORTED_MODULE_1__/* .CLIENT_DECODE_PEAK_COUNT */ .f3) {
|
|
7193
7474
|
const channels = Array.isArray(audio) ? audio : Array.from({
|
|
7194
7475
|
length: audio.numberOfChannels
|
|
7195
7476
|
}, (_, channelIndex) => audio.getChannelData(channelIndex));
|
|
@@ -7214,7 +7495,7 @@ function extractPeaks(audio, peakCount = constants/* CLIENT_DECODE_PEAK_COUNT */
|
|
|
7214
7495
|
}
|
|
7215
7496
|
}
|
|
7216
7497
|
}
|
|
7217
|
-
peaks[i] = Math.min(
|
|
7498
|
+
peaks[i] = Math.min(_constants__WEBPACK_IMPORTED_MODULE_1__/* .PEAK_UNIT_MAX */ .i8, Math.max(_constants__WEBPACK_IMPORTED_MODULE_1__/* .PEAK_UNIT_MIN */ .mJ, maxAbs));
|
|
7218
7499
|
}
|
|
7219
7500
|
return peaks;
|
|
7220
7501
|
}
|
|
@@ -7223,13 +7504,13 @@ function extractPeaks(audio, peakCount = constants/* CLIENT_DECODE_PEAK_COUNT */
|
|
|
7223
7504
|
* Decode compressed audio and extract unit peaks while the AudioBuffer is live.
|
|
7224
7505
|
* Does not attach a media element or fetch a URL. Does not copy channel data.
|
|
7225
7506
|
*/
|
|
7226
|
-
async function decodeToPeaks(arrayBuffer, signal, peakCount =
|
|
7507
|
+
async function decodeToPeaks(arrayBuffer, signal, peakCount = _constants__WEBPACK_IMPORTED_MODULE_1__/* .CLIENT_DECODE_PEAK_COUNT */ .f3) {
|
|
7227
7508
|
if (signal?.aborted) {
|
|
7228
7509
|
throw createAbortError();
|
|
7229
7510
|
}
|
|
7230
7511
|
const AudioContextConstructor = getAudioContextConstructor();
|
|
7231
7512
|
if (!AudioContextConstructor) {
|
|
7232
|
-
throw new WaveformLoadError('DECODE_FAILED', 'AudioContext is not available');
|
|
7513
|
+
throw new _createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .WaveformLoadError */ .oz('DECODE_FAILED', 'AudioContext is not available');
|
|
7233
7514
|
}
|
|
7234
7515
|
const context = new AudioContextConstructor();
|
|
7235
7516
|
try {
|
|
@@ -7237,15 +7518,15 @@ async function decodeToPeaks(arrayBuffer, signal, peakCount = constants/* CLIENT
|
|
|
7237
7518
|
if (signal?.aborted) {
|
|
7238
7519
|
throw createAbortError();
|
|
7239
7520
|
}
|
|
7240
|
-
const extractStarted = (0,
|
|
7521
|
+
const extractStarted = (0,_util__WEBPACK_IMPORTED_MODULE_0__/* .getCurrentTimeMs */ .RU)();
|
|
7241
7522
|
const peaks = extractPeaks(audioBuffer, peakCount);
|
|
7242
7523
|
return {
|
|
7243
7524
|
durationSec: audioBuffer.duration,
|
|
7244
7525
|
peaks,
|
|
7245
|
-
extractMs: (0,
|
|
7526
|
+
extractMs: (0,_util__WEBPACK_IMPORTED_MODULE_0__/* .getCurrentTimeMs */ .RU)() - extractStarted
|
|
7246
7527
|
};
|
|
7247
7528
|
} catch (error) {
|
|
7248
|
-
if (signal?.aborted || isAbortError(error)) {
|
|
7529
|
+
if (signal?.aborted || (0,_createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .isAbortError */ .zf)(error)) {
|
|
7249
7530
|
throw createAbortError();
|
|
7250
7531
|
}
|
|
7251
7532
|
throw error;
|
|
@@ -7278,31 +7559,31 @@ async function runClientDecode(options) {
|
|
|
7278
7559
|
};
|
|
7279
7560
|
}
|
|
7280
7561
|
const signal = options.signal ?? new AbortController().signal;
|
|
7281
|
-
const decodeStarted = (0,
|
|
7562
|
+
const decodeStarted = (0,_util__WEBPACK_IMPORTED_MODULE_0__/* .getCurrentTimeMs */ .RU)();
|
|
7282
7563
|
let decodeOutput;
|
|
7283
7564
|
try {
|
|
7284
7565
|
decodeOutput = await options.decode(signal);
|
|
7285
7566
|
} catch (error) {
|
|
7286
|
-
if (signal.aborted || isAbortError(error)) {
|
|
7567
|
+
if (signal.aborted || (0,_createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .isAbortError */ .zf)(error)) {
|
|
7287
7568
|
return {
|
|
7288
7569
|
status: 'cancelled',
|
|
7289
7570
|
isDecodeSkipped: false,
|
|
7290
7571
|
timings: EMPTY_TIMINGS
|
|
7291
7572
|
};
|
|
7292
7573
|
}
|
|
7293
|
-
const waveformError = errorFromUnknown(error, 'DECODE_FAILED');
|
|
7574
|
+
const waveformError = (0,_createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .errorFromUnknown */ .Vq)(error, 'DECODE_FAILED');
|
|
7294
7575
|
return {
|
|
7295
7576
|
status: 'failed',
|
|
7296
7577
|
error: waveformError,
|
|
7297
|
-
retryable:
|
|
7578
|
+
retryable: (0,_validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__/* .isRetryableWaveformError */ .Oe)(waveformError.code),
|
|
7298
7579
|
isDecodeSkipped: false,
|
|
7299
7580
|
timings: {
|
|
7300
|
-
attemptMs: (0,
|
|
7581
|
+
attemptMs: (0,_util__WEBPACK_IMPORTED_MODULE_0__/* .getCurrentTimeMs */ .RU)() - decodeStarted,
|
|
7301
7582
|
extractMs: null
|
|
7302
7583
|
}
|
|
7303
7584
|
};
|
|
7304
7585
|
}
|
|
7305
|
-
const attemptMs = (0,
|
|
7586
|
+
const attemptMs = (0,_util__WEBPACK_IMPORTED_MODULE_0__/* .getCurrentTimeMs */ .RU)() - decodeStarted;
|
|
7306
7587
|
if (signal.aborted) {
|
|
7307
7588
|
return {
|
|
7308
7589
|
status: 'cancelled',
|
|
@@ -7313,8 +7594,8 @@ async function runClientDecode(options) {
|
|
|
7313
7594
|
}
|
|
7314
7595
|
};
|
|
7315
7596
|
}
|
|
7316
|
-
const validation =
|
|
7317
|
-
version:
|
|
7597
|
+
const validation = (0,_validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__/* .validateWaveformPayload */ .a9)({
|
|
7598
|
+
version: _constants__WEBPACK_IMPORTED_MODULE_1__/* .WAVEFORM_PAYLOAD_VERSION */ .sh,
|
|
7318
7599
|
durationSec: options.durationSec ?? decodeOutput.durationSec,
|
|
7319
7600
|
peaks: decodeOutput.peaks
|
|
7320
7601
|
}, {
|
|
@@ -7324,7 +7605,7 @@ async function runClientDecode(options) {
|
|
|
7324
7605
|
return {
|
|
7325
7606
|
status: 'failed',
|
|
7326
7607
|
error: validation.error,
|
|
7327
|
-
retryable:
|
|
7608
|
+
retryable: (0,_validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__/* .isRetryableWaveformError */ .Oe)(validation.error.code),
|
|
7328
7609
|
isDecodeSkipped: false,
|
|
7329
7610
|
timings: {
|
|
7330
7611
|
attemptMs,
|
|
@@ -7361,6 +7642,193 @@ function loadPeaks(request) {
|
|
|
7361
7642
|
|
|
7362
7643
|
/***/ },
|
|
7363
7644
|
|
|
7645
|
+
/***/ 1053
|
|
7646
|
+
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
7647
|
+
|
|
7648
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
7649
|
+
/* harmony export */ Oe: () => (/* binding */ isRetryableWaveformError),
|
|
7650
|
+
/* harmony export */ Ze: () => (/* binding */ isPositiveFinite),
|
|
7651
|
+
/* harmony export */ a9: () => (/* binding */ validateWaveformPayload)
|
|
7652
|
+
/* harmony export */ });
|
|
7653
|
+
/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4929);
|
|
7654
|
+
|
|
7655
|
+
const DEFAULT_PEAK_SCALE = 'unit';
|
|
7656
|
+
const DEFAULT_CHANNEL_POLICY = 'mono_max';
|
|
7657
|
+
const DEFAULT_ENVELOPE = 'peak';
|
|
7658
|
+
function fail(code, message, retryable = false) {
|
|
7659
|
+
return {
|
|
7660
|
+
ok: false,
|
|
7661
|
+
error: {
|
|
7662
|
+
code,
|
|
7663
|
+
message
|
|
7664
|
+
},
|
|
7665
|
+
retryable
|
|
7666
|
+
};
|
|
7667
|
+
}
|
|
7668
|
+
function isPositiveFinite(value) {
|
|
7669
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
7670
|
+
}
|
|
7671
|
+
function isPlainObject(value) {
|
|
7672
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
7673
|
+
}
|
|
7674
|
+
function payloadByteLength(raw) {
|
|
7675
|
+
try {
|
|
7676
|
+
const serialized = JSON.stringify(raw);
|
|
7677
|
+
if (typeof Buffer !== 'undefined') {
|
|
7678
|
+
return Buffer.byteLength(serialized, 'utf8');
|
|
7679
|
+
}
|
|
7680
|
+
if (typeof TextEncoder !== 'undefined') {
|
|
7681
|
+
return new TextEncoder().encode(serialized).length;
|
|
7682
|
+
}
|
|
7683
|
+
return serialized.length;
|
|
7684
|
+
} catch {
|
|
7685
|
+
return Number.MAX_SAFE_INTEGER;
|
|
7686
|
+
}
|
|
7687
|
+
}
|
|
7688
|
+
function readOptionalPolicy(raw, key, allowed) {
|
|
7689
|
+
if (!(key in raw) || raw[key] === undefined) {
|
|
7690
|
+
return undefined;
|
|
7691
|
+
}
|
|
7692
|
+
return raw[key] === allowed ? allowed : null;
|
|
7693
|
+
}
|
|
7694
|
+
function asWaveformPayloadV1(raw) {
|
|
7695
|
+
const {
|
|
7696
|
+
version,
|
|
7697
|
+
durationSec,
|
|
7698
|
+
peaks
|
|
7699
|
+
} = raw;
|
|
7700
|
+
if (version !== _constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh || typeof durationSec !== 'number' || !Array.isArray(peaks)) {
|
|
7701
|
+
return null;
|
|
7702
|
+
}
|
|
7703
|
+
const peakScale = readOptionalPolicy(raw, 'peakScale', DEFAULT_PEAK_SCALE);
|
|
7704
|
+
const channelPolicy = readOptionalPolicy(raw, 'channelPolicy', DEFAULT_CHANNEL_POLICY);
|
|
7705
|
+
const envelope = readOptionalPolicy(raw, 'envelope', DEFAULT_ENVELOPE);
|
|
7706
|
+
if (peakScale === null || channelPolicy === null || envelope === null) {
|
|
7707
|
+
return null;
|
|
7708
|
+
}
|
|
7709
|
+
if ('channels' in raw && raw.channels !== undefined && raw.channels !== 1) {
|
|
7710
|
+
return null;
|
|
7711
|
+
}
|
|
7712
|
+
return {
|
|
7713
|
+
version: _constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh,
|
|
7714
|
+
durationSec,
|
|
7715
|
+
peaks: peaks,
|
|
7716
|
+
peakScale: peakScale ?? DEFAULT_PEAK_SCALE,
|
|
7717
|
+
channelPolicy: channelPolicy ?? DEFAULT_CHANNEL_POLICY,
|
|
7718
|
+
envelope: envelope ?? DEFAULT_ENVELOPE,
|
|
7719
|
+
channels: 1
|
|
7720
|
+
};
|
|
7721
|
+
}
|
|
7722
|
+
function readPayloadObject(raw, maxPayloadBytes, isPayloadByteCheckSkipped = false) {
|
|
7723
|
+
if (raw === null || raw === undefined) {
|
|
7724
|
+
return fail('INVALID_PAYLOAD', 'Payload is null or undefined');
|
|
7725
|
+
}
|
|
7726
|
+
if (!isPayloadByteCheckSkipped) {
|
|
7727
|
+
const byteLength = payloadByteLength(raw);
|
|
7728
|
+
if (byteLength > maxPayloadBytes) {
|
|
7729
|
+
return fail('PAYLOAD_TOO_LARGE', `Payload size ${byteLength} exceeds limit ${maxPayloadBytes}`);
|
|
7730
|
+
}
|
|
7731
|
+
}
|
|
7732
|
+
if (!isPlainObject(raw)) {
|
|
7733
|
+
return fail('INVALID_PAYLOAD', 'Payload must be an object');
|
|
7734
|
+
}
|
|
7735
|
+
return {
|
|
7736
|
+
ok: true,
|
|
7737
|
+
value: raw
|
|
7738
|
+
};
|
|
7739
|
+
}
|
|
7740
|
+
function checkVersion(raw) {
|
|
7741
|
+
if (raw.version === _constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh) {
|
|
7742
|
+
return null;
|
|
7743
|
+
}
|
|
7744
|
+
const {
|
|
7745
|
+
version
|
|
7746
|
+
} = raw;
|
|
7747
|
+
if (typeof version === 'number' && version > _constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh) {
|
|
7748
|
+
return fail('UNSUPPORTED_VERSION', `Unsupported waveform version ${version}; max supported is ${_constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh}`);
|
|
7749
|
+
}
|
|
7750
|
+
return fail('INVALID_PAYLOAD', 'Missing or invalid version field');
|
|
7751
|
+
}
|
|
7752
|
+
function checkDuration(payload, expectedDurationSec) {
|
|
7753
|
+
if (!isPositiveFinite(payload.durationSec)) {
|
|
7754
|
+
return fail('INVALID_DURATION', 'durationSec must be a positive finite number');
|
|
7755
|
+
}
|
|
7756
|
+
if (expectedDurationSec !== undefined && Number.isFinite(expectedDurationSec) && Math.abs(payload.durationSec - expectedDurationSec) > _constants__WEBPACK_IMPORTED_MODULE_0__/* .DURATION_MISMATCH_TOLERANCE_SEC */ .GQ) {
|
|
7757
|
+
return fail('DURATION_MISMATCH', `Payload duration ${payload.durationSec}s differs from media duration ${expectedDurationSec}s`, true);
|
|
7758
|
+
}
|
|
7759
|
+
return null;
|
|
7760
|
+
}
|
|
7761
|
+
function normalizePeaks(peaks, maxPeakCount) {
|
|
7762
|
+
if (peaks.length === 0) {
|
|
7763
|
+
return fail('EMPTY_PEAKS', 'peaks array must not be empty');
|
|
7764
|
+
}
|
|
7765
|
+
if (peaks.length > maxPeakCount) {
|
|
7766
|
+
return fail('PEAK_COUNT_EXCEEDED', `peaks length ${peaks.length} exceeds max ${maxPeakCount}`);
|
|
7767
|
+
}
|
|
7768
|
+
const normalized = new Float32Array(peaks.length);
|
|
7769
|
+
for (let i = 0; i < peaks.length; i += 1) {
|
|
7770
|
+
const value = peaks[i];
|
|
7771
|
+
if (!Number.isFinite(value)) {
|
|
7772
|
+
return fail('NON_FINITE_PEAK', `Peak at index ${i} is not finite`);
|
|
7773
|
+
}
|
|
7774
|
+
if (value < _constants__WEBPACK_IMPORTED_MODULE_0__/* .PEAK_UNIT_MIN */ .mJ || value > _constants__WEBPACK_IMPORTED_MODULE_0__/* .PEAK_UNIT_MAX */ .i8) {
|
|
7775
|
+
return fail('PEAK_OUT_OF_RANGE', `Peak at index ${i} is outside [${_constants__WEBPACK_IMPORTED_MODULE_0__/* .PEAK_UNIT_MIN */ .mJ}, ${_constants__WEBPACK_IMPORTED_MODULE_0__/* .PEAK_UNIT_MAX */ .i8}]`);
|
|
7776
|
+
}
|
|
7777
|
+
normalized[i] = value;
|
|
7778
|
+
}
|
|
7779
|
+
return {
|
|
7780
|
+
ok: true,
|
|
7781
|
+
peaks: normalized
|
|
7782
|
+
};
|
|
7783
|
+
}
|
|
7784
|
+
|
|
7785
|
+
/**
|
|
7786
|
+
* Validates a version-1 waveform payload and returns normalized peaks for rendering.
|
|
7787
|
+
* Wire JSON requires version, durationSec, and peaks. sampleCount and source are ignored.
|
|
7788
|
+
*/
|
|
7789
|
+
function validateWaveformPayload(raw, options = {}) {
|
|
7790
|
+
const maxPeakCount = options.maxPeakCount ?? _constants__WEBPACK_IMPORTED_MODULE_0__/* .MAX_PEAK_COUNT */ .FY;
|
|
7791
|
+
const maxPayloadBytes = options.maxPayloadBytes ?? _constants__WEBPACK_IMPORTED_MODULE_0__/* .MAX_PAYLOAD_BYTES */ .qY;
|
|
7792
|
+
const objectResult = readPayloadObject(raw, maxPayloadBytes, options.isPayloadByteCheckSkipped === true);
|
|
7793
|
+
if (!objectResult.ok) {
|
|
7794
|
+
return objectResult;
|
|
7795
|
+
}
|
|
7796
|
+
const versionFailure = checkVersion(objectResult.value);
|
|
7797
|
+
if (versionFailure) {
|
|
7798
|
+
return versionFailure;
|
|
7799
|
+
}
|
|
7800
|
+
const payload = asWaveformPayloadV1(objectResult.value);
|
|
7801
|
+
if (!payload) {
|
|
7802
|
+
return fail('INVALID_PAYLOAD', 'Payload failed structural validation');
|
|
7803
|
+
}
|
|
7804
|
+
const durationFailure = checkDuration(payload, options.expectedDurationSec);
|
|
7805
|
+
if (durationFailure) {
|
|
7806
|
+
return durationFailure;
|
|
7807
|
+
}
|
|
7808
|
+
const peaksResult = normalizePeaks(payload.peaks, maxPeakCount);
|
|
7809
|
+
if (!peaksResult.ok) {
|
|
7810
|
+
return peaksResult;
|
|
7811
|
+
}
|
|
7812
|
+
return {
|
|
7813
|
+
ok: true,
|
|
7814
|
+
payload: {
|
|
7815
|
+
version: _constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh,
|
|
7816
|
+
durationSec: payload.durationSec,
|
|
7817
|
+
peaks: peaksResult.peaks,
|
|
7818
|
+
peakScale: payload.peakScale ?? DEFAULT_PEAK_SCALE,
|
|
7819
|
+
channelPolicy: payload.channelPolicy ?? DEFAULT_CHANNEL_POLICY,
|
|
7820
|
+
envelope: payload.envelope ?? DEFAULT_ENVELOPE
|
|
7821
|
+
}
|
|
7822
|
+
};
|
|
7823
|
+
}
|
|
7824
|
+
|
|
7825
|
+
/** Maps validation / load failures to retry policy for loaders. */
|
|
7826
|
+
function isRetryableWaveformError(code) {
|
|
7827
|
+
return code === 'DURATION_MISMATCH' || code === 'DECODE_FAILED' || code === 'LOAD_FAILED';
|
|
7828
|
+
}
|
|
7829
|
+
|
|
7830
|
+
/***/ },
|
|
7831
|
+
|
|
7364
7832
|
/***/ 2485
|
|
7365
7833
|
(module, exports) {
|
|
7366
7834
|
|
|
@@ -23024,7 +23492,7 @@ class Browser {
|
|
|
23024
23492
|
;// ./src/lib/Logger.js
|
|
23025
23493
|
/* eslint-disable no-undef */
|
|
23026
23494
|
const CLIENT_NAME = "box-content-preview";
|
|
23027
|
-
const CLIENT_VERSION = "3.
|
|
23495
|
+
const CLIENT_VERSION = "3.91.0";
|
|
23028
23496
|
/* eslint-enable no-undef */
|
|
23029
23497
|
|
|
23030
23498
|
class Logger {
|
|
@@ -34403,10 +34871,9 @@ class MediaBaseViewer extends viewers_BaseViewer {
|
|
|
34403
34871
|
const template = this.options.representation.content.url_template;
|
|
34404
34872
|
this.mediaEl.addEventListener('error', this.errorHandler);
|
|
34405
34873
|
this.mediaEl.setAttribute('title', this.options.file.name);
|
|
34406
|
-
if (lib_Browser.isIOS()) {
|
|
34407
|
-
// iOS
|
|
34408
|
-
//
|
|
34409
|
-
// https://webkit.org/blog/6784/new-video-policies-for-ios/
|
|
34874
|
+
if (lib_Browser.isIOS() && this.mediaEl.tagName === 'VIDEO') {
|
|
34875
|
+
// Unblocks loadeddata on iOS <video>. Do not set this on <audio>:
|
|
34876
|
+
// after tap-to-open, Safari will start playback.
|
|
34410
34877
|
this.mediaEl.autoplay = true;
|
|
34411
34878
|
}
|
|
34412
34879
|
const contentUrl = this.createContentUrlV2(template);
|
|
@@ -35709,6 +36176,12 @@ class MP3ControlsRoot {
|
|
|
35709
36176
|
this.root.render(controls);
|
|
35710
36177
|
}
|
|
35711
36178
|
}
|
|
36179
|
+
// EXTERNAL MODULE: ./src/lib/viewers/media/waveform/constants.ts
|
|
36180
|
+
var waveform_constants = __webpack_require__(4929);
|
|
36181
|
+
// EXTERNAL MODULE: ./src/lib/viewers/media/waveform/createWaveformLoader.ts + 1 modules
|
|
36182
|
+
var createWaveformLoader = __webpack_require__(4971);
|
|
36183
|
+
// EXTERNAL MODULE: ./src/lib/viewers/media/waveform/validateWaveformPayload.ts
|
|
36184
|
+
var validateWaveformPayload = __webpack_require__(1053);
|
|
35712
36185
|
;// ./src/lib/viewers/media/MP3.scss
|
|
35713
36186
|
// extracted by mini-css-extract-plugin
|
|
35714
36187
|
|
|
@@ -35723,6 +36196,11 @@ function MP3Viewer_toPrimitive(t, r) { if ("object" != typeof t || !t) return t;
|
|
|
35723
36196
|
|
|
35724
36197
|
|
|
35725
36198
|
|
|
36199
|
+
|
|
36200
|
+
|
|
36201
|
+
|
|
36202
|
+
|
|
36203
|
+
|
|
35726
36204
|
const CSS_CLASS_MP3 = 'bp-media-mp3';
|
|
35727
36205
|
function createLoadFailedError(message) {
|
|
35728
36206
|
const error = new Error(message);
|
|
@@ -35738,7 +36216,7 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35738
36216
|
MP3Viewer_defineProperty(this, "handlePlayRequest", () => {
|
|
35739
36217
|
this.userRequestedPlay = true;
|
|
35740
36218
|
this.togglePlay();
|
|
35741
|
-
if (this.isWaveformDecodeRetryPending && !this.hasUsedWaveformDecodePlayRetry) {
|
|
36219
|
+
if (this.shouldRaceClientWaveformDecode() && this.isWaveformDecodeRetryPending && !this.hasUsedWaveformDecodePlayRetry) {
|
|
35742
36220
|
this.hasUsedWaveformDecodePlayRetry = true;
|
|
35743
36221
|
this.isWaveformDecodeRetryPending = false;
|
|
35744
36222
|
this.startClientWaveformDecode();
|
|
@@ -35775,11 +36253,11 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35775
36253
|
});
|
|
35776
36254
|
MP3Viewer_defineProperty(this, "handleCommentMarkerClick", marker => {
|
|
35777
36255
|
this.hostSelectedMarkerId = marker.id;
|
|
35778
|
-
this.pendingHostSelectedSeek =
|
|
36256
|
+
this.pendingHostSelectedSeek = marker;
|
|
35779
36257
|
if (this.mediaEl) {
|
|
35780
36258
|
this.mediaEl.pause();
|
|
35781
|
-
this.mediaEl.currentTime = marker.time;
|
|
35782
36259
|
}
|
|
36260
|
+
this.applyPendingHostSelectedSeek();
|
|
35783
36261
|
// Overlay paints the ring from click optimism until the host re-emits selected.
|
|
35784
36262
|
this.emit('comment_marker_select', {
|
|
35785
36263
|
id: marker.id,
|
|
@@ -35811,11 +36289,16 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35811
36289
|
this.wrapperEl.classList.add(CSS_CLASS_MP3);
|
|
35812
36290
|
this.isAudioPlayerV2 = this.getIsAudioPlayerV2();
|
|
35813
36291
|
this.waveformPeaks = [];
|
|
36292
|
+
this.waveformPeaksSource = null;
|
|
36293
|
+
this.waveformDurationSec = 0;
|
|
35814
36294
|
if (this.isAudioPlayerV2) {
|
|
35815
36295
|
this.wrapperEl.classList.add('bp-media--v2');
|
|
35816
36296
|
this.mediaContainerEl.classList.add('bp-media-container--v2');
|
|
35817
36297
|
this.ensureV2Controls();
|
|
35818
36298
|
this.importWaveformDecode();
|
|
36299
|
+
// Listen on the loading shell. The waveform is on
|
|
36300
|
+
// screen before the audio blob is playable; getViewer() is still null.
|
|
36301
|
+
this.bindCommentMarkersListener();
|
|
35819
36302
|
}
|
|
35820
36303
|
|
|
35821
36304
|
// Audio element
|
|
@@ -35823,6 +36306,13 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35823
36306
|
this.mediaEl.setAttribute('preload', 'auto');
|
|
35824
36307
|
this.commentMarkers = [];
|
|
35825
36308
|
}
|
|
36309
|
+
bindCommentMarkersListener() {
|
|
36310
|
+
if (!this.isAudioPlayerV2) {
|
|
36311
|
+
return;
|
|
36312
|
+
}
|
|
36313
|
+
this.removeListener('comment_markers', this.handleCommentMarkersUpdated);
|
|
36314
|
+
this.addListener('comment_markers', this.handleCommentMarkersUpdated);
|
|
36315
|
+
}
|
|
35826
36316
|
|
|
35827
36317
|
/**
|
|
35828
36318
|
* Audio player v2 (waveform). Off unless the host enables `audioPlayerV2`
|
|
@@ -35831,7 +36321,7 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35831
36321
|
* @return {boolean} whether v2 treatment is on
|
|
35832
36322
|
*/
|
|
35833
36323
|
getIsAudioPlayerV2() {
|
|
35834
|
-
return this.featureEnabled(
|
|
36324
|
+
return this.featureEnabled(constants/* AUDIO_PLAYER_V2 */.Ku) && this.useReactControls();
|
|
35835
36325
|
}
|
|
35836
36326
|
|
|
35837
36327
|
/**
|
|
@@ -35863,6 +36353,7 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35863
36353
|
*/
|
|
35864
36354
|
fallbackToV1Controls() {
|
|
35865
36355
|
this.isAudioPlayerV2 = false;
|
|
36356
|
+
this.abortWaveformLoads();
|
|
35866
36357
|
if (this.wrapperEl) {
|
|
35867
36358
|
this.wrapperEl.classList.remove('bp-media--v2');
|
|
35868
36359
|
}
|
|
@@ -35875,7 +36366,7 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35875
36366
|
* @return {Promise<{ default: Function }>} MP3ControlsV2 module
|
|
35876
36367
|
*/
|
|
35877
36368
|
importV2Controls() {
|
|
35878
|
-
return Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__,
|
|
36369
|
+
return Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 2668));
|
|
35879
36370
|
}
|
|
35880
36371
|
|
|
35881
36372
|
/**
|
|
@@ -35883,7 +36374,7 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35883
36374
|
*/
|
|
35884
36375
|
async importWaveformDecode() {
|
|
35885
36376
|
if (!this.waveformDecodeImport) {
|
|
35886
|
-
this.waveformDecodeImport = Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__,
|
|
36377
|
+
this.waveformDecodeImport = Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 8296));
|
|
35887
36378
|
}
|
|
35888
36379
|
try {
|
|
35889
36380
|
return await this.waveformDecodeImport;
|
|
@@ -35898,7 +36389,7 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35898
36389
|
*/
|
|
35899
36390
|
destroy() {
|
|
35900
36391
|
this.removeListener('comment_markers', this.handleCommentMarkersUpdated);
|
|
35901
|
-
this.
|
|
36392
|
+
this.abortWaveformLoads();
|
|
35902
36393
|
super.destroy();
|
|
35903
36394
|
}
|
|
35904
36395
|
|
|
@@ -35908,6 +36399,7 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35908
36399
|
load() {
|
|
35909
36400
|
if (this.isAudioPlayerV2) {
|
|
35910
36401
|
this.showAudioLoadingShell();
|
|
36402
|
+
this.startConversionWaveformLoad();
|
|
35911
36403
|
}
|
|
35912
36404
|
return super.load();
|
|
35913
36405
|
}
|
|
@@ -35945,6 +36437,9 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35945
36437
|
* @inheritdoc
|
|
35946
36438
|
*/
|
|
35947
36439
|
loadeddataHandler() {
|
|
36440
|
+
if (this.isAudioPlayerV2) {
|
|
36441
|
+
this.dropConversionPeaksOnDurationMismatch();
|
|
36442
|
+
}
|
|
35948
36443
|
super.loadeddataHandler();
|
|
35949
36444
|
if (!this.isAudioPlayerV2) {
|
|
35950
36445
|
return;
|
|
@@ -35955,7 +36450,173 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35955
36450
|
if (this.userRequestedPlay) {
|
|
35956
36451
|
this.play();
|
|
35957
36452
|
}
|
|
35958
|
-
this.
|
|
36453
|
+
if (this.shouldRaceClientWaveformDecode()) {
|
|
36454
|
+
this.startClientWaveformDecode();
|
|
36455
|
+
}
|
|
36456
|
+
}
|
|
36457
|
+
|
|
36458
|
+
/**
|
|
36459
|
+
* Client decode races conversion until some source applies peaks. Size and
|
|
36460
|
+
* duration caps still apply. First source to apply peaks wins; later
|
|
36461
|
+
* results are ignored.
|
|
36462
|
+
*
|
|
36463
|
+
* @return {boolean}
|
|
36464
|
+
*/
|
|
36465
|
+
shouldRaceClientWaveformDecode() {
|
|
36466
|
+
if (!this.isAudioPlayerV2 || this.destroyed || this.waveformPeaksSource) {
|
|
36467
|
+
return false;
|
|
36468
|
+
}
|
|
36469
|
+
const compressedBytes = this.options.file?.size;
|
|
36470
|
+
const durationSec = this.mediaEl?.duration;
|
|
36471
|
+
return (0,validateWaveformPayload/* isPositiveFinite */.Ze)(compressedBytes) && compressedBytes <= waveform_constants/* CLIENT_DECODE_MAX_COMPRESSED_BYTES */.EB && (0,validateWaveformPayload/* isPositiveFinite */.Ze)(durationSec) && durationSec <= waveform_constants/* CLIENT_DECODE_MAX_DURATION_SEC */.nG;
|
|
36472
|
+
}
|
|
36473
|
+
|
|
36474
|
+
/**
|
|
36475
|
+
* @param {number} durationSec
|
|
36476
|
+
* @return {boolean}
|
|
36477
|
+
*/
|
|
36478
|
+
isConversionDurationMismatch(durationSec) {
|
|
36479
|
+
const mediaDuration = this.mediaEl?.duration;
|
|
36480
|
+
return (0,validateWaveformPayload/* isPositiveFinite */.Ze)(mediaDuration) && (0,validateWaveformPayload/* isPositiveFinite */.Ze)(durationSec) && Math.abs(durationSec - mediaDuration) > waveform_constants/* DURATION_MISMATCH_TOLERANCE_SEC */.GQ;
|
|
36481
|
+
}
|
|
36482
|
+
|
|
36483
|
+
/**
|
|
36484
|
+
* Conversion JSON fetched before metadata skipped duration matching. Drop
|
|
36485
|
+
* those peaks once media duration is known if they disagree.
|
|
36486
|
+
*
|
|
36487
|
+
* @return {void}
|
|
36488
|
+
*/
|
|
36489
|
+
dropConversionPeaksOnDurationMismatch() {
|
|
36490
|
+
if (this.waveformPeaksSource !== 'conversion' || !this.isConversionDurationMismatch(this.waveformDurationSec)) {
|
|
36491
|
+
return;
|
|
36492
|
+
}
|
|
36493
|
+
this.waveformPeaks = [];
|
|
36494
|
+
this.waveformPeaksSource = null;
|
|
36495
|
+
this.waveformDurationSec = 0;
|
|
36496
|
+
this.renderUI();
|
|
36497
|
+
}
|
|
36498
|
+
|
|
36499
|
+
/**
|
|
36500
|
+
* Poll the conversion waveform representation and fetch its JSON.
|
|
36501
|
+
* Missing, failed, or unloadable conversion falls back to client decode
|
|
36502
|
+
* when the file is under size and duration caps.
|
|
36503
|
+
*
|
|
36504
|
+
* @return {Promise<void>}
|
|
36505
|
+
*/
|
|
36506
|
+
async startConversionWaveformLoad() {
|
|
36507
|
+
if (!this.isAudioPlayerV2 || this.destroyed) {
|
|
36508
|
+
return;
|
|
36509
|
+
}
|
|
36510
|
+
const {
|
|
36511
|
+
file
|
|
36512
|
+
} = this.options;
|
|
36513
|
+
const waveform = file?.representations?.entries ? getRepresentation(file, constants/* WAVEFORM_REP_NAME */.u4) : null;
|
|
36514
|
+
const template = waveform?.content?.url_template;
|
|
36515
|
+
const conversionStatus = waveform?.status;
|
|
36516
|
+
const statusState = conversionStatus && typeof conversionStatus === 'object' ? conversionStatus.state : null;
|
|
36517
|
+
if (!waveform || !template || !statusState || statusState === constants/* STATUS_ERROR */.uD) {
|
|
36518
|
+
this.startClientWaveformDecode();
|
|
36519
|
+
return;
|
|
36520
|
+
}
|
|
36521
|
+
this.abortConversionWaveformLoad();
|
|
36522
|
+
this.waveformStatus = this.getRepStatus(waveform);
|
|
36523
|
+
this.waveformStatus.removeListener('conversionpending', this.resetLoadTimeout);
|
|
36524
|
+
const status = this.waveformStatus;
|
|
36525
|
+
try {
|
|
36526
|
+
await status.getPromise();
|
|
36527
|
+
if (this.destroyed || this.waveformStatus !== status || this.waveformPeaksSource) {
|
|
36528
|
+
return;
|
|
36529
|
+
}
|
|
36530
|
+
const result = await this.loadConversionWaveformPayload(template);
|
|
36531
|
+
this.handleConversionWaveformResult(result, status);
|
|
36532
|
+
} catch {
|
|
36533
|
+
this.startClientWaveformDecode();
|
|
36534
|
+
}
|
|
36535
|
+
}
|
|
36536
|
+
|
|
36537
|
+
/**
|
|
36538
|
+
* Fetch and validate conversion waveform JSON after the rep is ready.
|
|
36539
|
+
* `{+asset_path}` is empty. Auth is a header, not a query token.
|
|
36540
|
+
*
|
|
36541
|
+
* @param {string} template - content URL template
|
|
36542
|
+
* @return {Promise<import('./waveform/types').WaveformLoadState>}
|
|
36543
|
+
*/
|
|
36544
|
+
loadConversionWaveformPayload(template) {
|
|
36545
|
+
const url = this.createContentUrlV2(template);
|
|
36546
|
+
const durationSec = this.mediaEl?.duration;
|
|
36547
|
+
const source = (0,createWaveformLoader/* createWaveformLoader */.nR)(signal => this.api.get(url, {
|
|
36548
|
+
headers: this.appendAuthHeader(),
|
|
36549
|
+
signal
|
|
36550
|
+
}), Number.isFinite(durationSec) ? {
|
|
36551
|
+
expectedDurationSec: durationSec
|
|
36552
|
+
} : {});
|
|
36553
|
+
this.waveformSource = source;
|
|
36554
|
+
return source.load();
|
|
36555
|
+
}
|
|
36556
|
+
abortWaveformLoads() {
|
|
36557
|
+
this.abortConversionWaveformLoad();
|
|
36558
|
+
this.abortClientWaveformDecode();
|
|
36559
|
+
}
|
|
36560
|
+
abortConversionWaveformLoad() {
|
|
36561
|
+
if (this.waveformSource) {
|
|
36562
|
+
this.waveformSource.abort();
|
|
36563
|
+
this.waveformSource = null;
|
|
36564
|
+
}
|
|
36565
|
+
if (this.waveformStatus) {
|
|
36566
|
+
this.waveformStatus.destroy();
|
|
36567
|
+
this.waveformStatus = null;
|
|
36568
|
+
}
|
|
36569
|
+
}
|
|
36570
|
+
|
|
36571
|
+
/**
|
|
36572
|
+
* First source to apply peaks wins. Later results are ignored.
|
|
36573
|
+
*
|
|
36574
|
+
* @param {'conversion'|'client'} source
|
|
36575
|
+
* @param {{ peaks: ArrayLike<number>, durationSec?: number }} payload
|
|
36576
|
+
* @return {boolean} true when this source claimed peaks
|
|
36577
|
+
*/
|
|
36578
|
+
applyWaveformPeaks(source, payload) {
|
|
36579
|
+
if (this.waveformPeaksSource) {
|
|
36580
|
+
return false;
|
|
36581
|
+
}
|
|
36582
|
+
this.waveformPeaks = payload.peaks;
|
|
36583
|
+
this.waveformPeaksSource = source;
|
|
36584
|
+
this.isWaveformDecodeRetryPending = false;
|
|
36585
|
+
if (source === 'conversion') {
|
|
36586
|
+
this.waveformDurationSec = payload.durationSec;
|
|
36587
|
+
this.abortClientWaveformDecode();
|
|
36588
|
+
} else if (this.waveformSource) {
|
|
36589
|
+
// RepStatus.destroy() does not reject getPromise().
|
|
36590
|
+
this.waveformSource.abort();
|
|
36591
|
+
this.waveformSource = null;
|
|
36592
|
+
}
|
|
36593
|
+
this.renderUI();
|
|
36594
|
+
return true;
|
|
36595
|
+
}
|
|
36596
|
+
|
|
36597
|
+
/**
|
|
36598
|
+
* Apply conversion peaks when the load is still current. Ignored if client
|
|
36599
|
+
* decode already applied peaks.
|
|
36600
|
+
*
|
|
36601
|
+
* @param {import('./waveform/types').WaveformLoadState} result
|
|
36602
|
+
* @param {Object} status - RepStatus instance that started this load
|
|
36603
|
+
* @return {void}
|
|
36604
|
+
*/
|
|
36605
|
+
handleConversionWaveformResult(result, status) {
|
|
36606
|
+
if (!result || this.destroyed || !this.isAudioPlayerV2 || this.waveformStatus !== status) {
|
|
36607
|
+
return;
|
|
36608
|
+
}
|
|
36609
|
+
if (result.status !== 'ready') {
|
|
36610
|
+
if (result.status !== 'cancelled') {
|
|
36611
|
+
this.startClientWaveformDecode();
|
|
36612
|
+
}
|
|
36613
|
+
return;
|
|
36614
|
+
}
|
|
36615
|
+
if (this.isConversionDurationMismatch(result.payload.durationSec)) {
|
|
36616
|
+
this.startClientWaveformDecode();
|
|
36617
|
+
return;
|
|
36618
|
+
}
|
|
36619
|
+
this.applyWaveformPeaks('conversion', result.payload);
|
|
35959
36620
|
}
|
|
35960
36621
|
/**
|
|
35961
36622
|
* Fetch compressed audio bytes for client decode. Prefers an already-fetched blob URL.
|
|
@@ -35996,16 +36657,19 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
35996
36657
|
}
|
|
35997
36658
|
|
|
35998
36659
|
/**
|
|
35999
|
-
* Decode peaks in the background
|
|
36000
|
-
*
|
|
36660
|
+
* Decode peaks in the background until conversion or client decode applies
|
|
36661
|
+
* them, when the file is under size and duration caps. Does not block
|
|
36662
|
+
* playback. First source to apply peaks wins.
|
|
36001
36663
|
*
|
|
36002
36664
|
* @return {Promise<void>}
|
|
36003
36665
|
*/
|
|
36004
36666
|
async startClientWaveformDecode() {
|
|
36005
|
-
if (!this.
|
|
36667
|
+
if (!this.shouldRaceClientWaveformDecode()) {
|
|
36668
|
+
return;
|
|
36669
|
+
}
|
|
36670
|
+
if (this.waveformDecodeController && !this.waveformDecodeController.signal.aborted) {
|
|
36006
36671
|
return;
|
|
36007
36672
|
}
|
|
36008
|
-
this.abortClientWaveformDecode();
|
|
36009
36673
|
const controller = new AbortController();
|
|
36010
36674
|
this.waveformDecodeController = controller;
|
|
36011
36675
|
const {
|
|
@@ -36054,9 +36718,7 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
36054
36718
|
return;
|
|
36055
36719
|
}
|
|
36056
36720
|
if (result.status === 'ready') {
|
|
36057
|
-
this.
|
|
36058
|
-
this.isWaveformDecodeRetryPending = false;
|
|
36059
|
-
this.renderUI();
|
|
36721
|
+
this.applyWaveformPeaks('client', result.payload);
|
|
36060
36722
|
return;
|
|
36061
36723
|
}
|
|
36062
36724
|
if (result.status === 'cancelled') {
|
|
@@ -36086,10 +36748,7 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
36086
36748
|
containerEl: this.mediaContainerEl
|
|
36087
36749
|
});
|
|
36088
36750
|
}
|
|
36089
|
-
|
|
36090
|
-
this.removeListener('comment_markers', this.handleCommentMarkersUpdated);
|
|
36091
|
-
this.addListener('comment_markers', this.handleCommentMarkersUpdated);
|
|
36092
|
-
}
|
|
36751
|
+
this.bindCommentMarkersListener();
|
|
36093
36752
|
this.renderUI();
|
|
36094
36753
|
}
|
|
36095
36754
|
/**
|
|
@@ -36117,11 +36776,13 @@ class MP3Viewer extends media_MediaBaseViewer {
|
|
|
36117
36776
|
if (!this.controls) {
|
|
36118
36777
|
return;
|
|
36119
36778
|
}
|
|
36779
|
+
const mediaDuration = this.mediaEl.duration;
|
|
36780
|
+
const durationTime = typeof mediaDuration === 'number' && mediaDuration > 0 ? mediaDuration : this.waveformDurationSec;
|
|
36120
36781
|
const sharedProps = {
|
|
36121
36782
|
autoplay: this.isAutoplayEnabled(),
|
|
36122
36783
|
bufferedRange: this.mediaEl.buffered,
|
|
36123
36784
|
currentTime: this.mediaEl.currentTime,
|
|
36124
|
-
durationTime
|
|
36785
|
+
durationTime,
|
|
36125
36786
|
isPlaying: !this.mediaEl.paused,
|
|
36126
36787
|
movePlayback: this.movePlayback,
|
|
36127
36788
|
onAutoplayChange: this.setAutoplay,
|
|
@@ -54353,8 +55014,12 @@ class Preview extends (events_default()) {
|
|
|
54353
55014
|
if (isDash && isFeatureEnabled(this.options.features, constants/* AI_TRANSCRIPTION_FOR_VIDEO_SUBTITLES */.Iu)) {
|
|
54354
55015
|
videoHint += constants/* X_REP_HINT_VIDEO_DASH_EXTRACTED_TEXT */.xU;
|
|
54355
55016
|
}
|
|
55017
|
+
let hints = `${constants/* X_REP_HINT_BASE */.pl}${constants/* X_REP_HINT_DOC_THUMBNAIL */.nC}${constants/* X_REP_HINT_IMAGE */.h3}${videoHint}`;
|
|
55018
|
+
if (isFeatureEnabled(this.options.features, constants/* AUDIO_PLAYER_V2 */.Ku)) {
|
|
55019
|
+
hints += constants/* X_REP_HINT_WAVEFORM */.jr;
|
|
55020
|
+
}
|
|
54356
55021
|
const headers = {
|
|
54357
|
-
'X-Rep-Hints':
|
|
55022
|
+
'X-Rep-Hints': hints
|
|
54358
55023
|
};
|
|
54359
55024
|
return (0,util/* getHeaders */.dJ)(headers, token || this.options.token, this.options.sharedLink, this.options.sharedLinkPassword);
|
|
54360
55025
|
}
|