@djangocfg/ui-nextjs 2.1.89 → 2.1.91
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 +6 -15
- package/package.json +6 -25
- package/src/blocks/SplitHero/SplitHeroMedia.tsx +1 -1
- package/src/components/index.ts +0 -40
- package/src/hooks/index.ts +0 -6
- package/src/index.ts +2 -11
- package/src/components/button-download.tsx +0 -277
- package/src/components/markdown/MarkdownMessage.tsx +0 -340
- package/src/components/markdown/index.ts +0 -5
- package/src/components/menubar.tsx +0 -275
- package/src/components/multi-select-pro/async.tsx +0 -598
- package/src/components/multi-select-pro/helpers.tsx +0 -84
- package/src/components/multi-select-pro/index.tsx +0 -612
- package/src/components/navigation-menu.tsx +0 -154
- package/src/components/otp/index.tsx +0 -197
- package/src/components/otp/types.ts +0 -133
- package/src/components/otp/use-otp-input.ts +0 -225
- package/src/components/phone-input.tsx +0 -277
- package/src/components/sonner.tsx +0 -32
- package/src/hooks/useLocalStorage.ts +0 -300
- package/src/hooks/useSessionStorage.ts +0 -290
- package/src/lib/index.ts +0 -5
- package/src/lib/logger/index.ts +0 -10
- package/src/lib/logger/logStore.ts +0 -122
- package/src/lib/logger/logger.ts +0 -175
- package/src/lib/logger/types.ts +0 -82
- package/src/stores/index.ts +0 -8
- package/src/stores/mediaCache.ts +0 -534
- package/src/tools/AudioPlayer/README.md +0 -206
- package/src/tools/AudioPlayer/components/HybridAudioPlayer.tsx +0 -216
- package/src/tools/AudioPlayer/components/HybridSimplePlayer.tsx +0 -280
- package/src/tools/AudioPlayer/components/HybridWaveform.tsx +0 -279
- package/src/tools/AudioPlayer/components/ReactiveCover/AudioReactiveCover.tsx +0 -149
- package/src/tools/AudioPlayer/components/ReactiveCover/effects/GlowEffect.tsx +0 -110
- package/src/tools/AudioPlayer/components/ReactiveCover/effects/MeshEffect.tsx +0 -58
- package/src/tools/AudioPlayer/components/ReactiveCover/effects/OrbsEffect.tsx +0 -45
- package/src/tools/AudioPlayer/components/ReactiveCover/effects/SpotlightEffect.tsx +0 -82
- package/src/tools/AudioPlayer/components/ReactiveCover/effects/index.ts +0 -8
- package/src/tools/AudioPlayer/components/ReactiveCover/index.ts +0 -6
- package/src/tools/AudioPlayer/components/index.ts +0 -22
- package/src/tools/AudioPlayer/context/HybridAudioProvider.tsx +0 -158
- package/src/tools/AudioPlayer/context/index.ts +0 -16
- package/src/tools/AudioPlayer/effects/index.ts +0 -412
- package/src/tools/AudioPlayer/hooks/index.ts +0 -35
- package/src/tools/AudioPlayer/hooks/useHybridAudio.ts +0 -387
- package/src/tools/AudioPlayer/hooks/useHybridAudioAnalysis.ts +0 -95
- package/src/tools/AudioPlayer/hooks/useVisualization.tsx +0 -207
- package/src/tools/AudioPlayer/index.ts +0 -133
- package/src/tools/AudioPlayer/types/effects.ts +0 -73
- package/src/tools/AudioPlayer/types/index.ts +0 -27
- package/src/tools/AudioPlayer/utils/debug.ts +0 -14
- package/src/tools/AudioPlayer/utils/formatTime.ts +0 -10
- package/src/tools/AudioPlayer/utils/index.ts +0 -6
- package/src/tools/ImageViewer/@refactoring/00-PLAN.md +0 -71
- package/src/tools/ImageViewer/@refactoring/01-TYPES.md +0 -121
- package/src/tools/ImageViewer/@refactoring/02-UTILS.md +0 -143
- package/src/tools/ImageViewer/@refactoring/03-HOOKS.md +0 -261
- package/src/tools/ImageViewer/@refactoring/04-COMPONENTS.md +0 -427
- package/src/tools/ImageViewer/@refactoring/05-EXECUTION-CHECKLIST.md +0 -126
- package/src/tools/ImageViewer/README.md +0 -200
- package/src/tools/ImageViewer/components/ImageInfo.tsx +0 -44
- package/src/tools/ImageViewer/components/ImageToolbar.tsx +0 -150
- package/src/tools/ImageViewer/components/ImageViewer.tsx +0 -241
- package/src/tools/ImageViewer/components/index.ts +0 -7
- package/src/tools/ImageViewer/hooks/index.ts +0 -9
- package/src/tools/ImageViewer/hooks/useImageLoading.ts +0 -204
- package/src/tools/ImageViewer/hooks/useImageTransform.ts +0 -101
- package/src/tools/ImageViewer/index.ts +0 -60
- package/src/tools/ImageViewer/types.ts +0 -81
- package/src/tools/ImageViewer/utils/constants.ts +0 -59
- package/src/tools/ImageViewer/utils/debug.ts +0 -14
- package/src/tools/ImageViewer/utils/index.ts +0 -17
- package/src/tools/ImageViewer/utils/lqip.ts +0 -47
- package/src/tools/JsonForm/JsonSchemaForm.tsx +0 -197
- package/src/tools/JsonForm/examples/BotConfigExample.tsx +0 -249
- package/src/tools/JsonForm/examples/RealBotConfigExample.tsx +0 -161
- package/src/tools/JsonForm/index.ts +0 -46
- package/src/tools/JsonForm/templates/ArrayFieldItemTemplate.tsx +0 -47
- package/src/tools/JsonForm/templates/ArrayFieldTemplate.tsx +0 -74
- package/src/tools/JsonForm/templates/BaseInputTemplate.tsx +0 -107
- package/src/tools/JsonForm/templates/ErrorListTemplate.tsx +0 -35
- package/src/tools/JsonForm/templates/FieldTemplate.tsx +0 -62
- package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +0 -116
- package/src/tools/JsonForm/templates/index.ts +0 -12
- package/src/tools/JsonForm/types.ts +0 -83
- package/src/tools/JsonForm/utils.ts +0 -213
- package/src/tools/JsonForm/widgets/CheckboxWidget.tsx +0 -37
- package/src/tools/JsonForm/widgets/ColorWidget.tsx +0 -219
- package/src/tools/JsonForm/widgets/NumberWidget.tsx +0 -89
- package/src/tools/JsonForm/widgets/SelectWidget.tsx +0 -97
- package/src/tools/JsonForm/widgets/SliderWidget.tsx +0 -148
- package/src/tools/JsonForm/widgets/SwitchWidget.tsx +0 -35
- package/src/tools/JsonForm/widgets/TextWidget.tsx +0 -96
- package/src/tools/JsonForm/widgets/index.ts +0 -14
- package/src/tools/JsonTree/index.tsx +0 -243
- package/src/tools/LottiePlayer/LottiePlayer.client.tsx +0 -213
- package/src/tools/LottiePlayer/index.tsx +0 -55
- package/src/tools/LottiePlayer/types.ts +0 -108
- package/src/tools/LottiePlayer/useLottie.ts +0 -164
- package/src/tools/Mermaid/Mermaid.client.tsx +0 -82
- package/src/tools/Mermaid/components/MermaidCodeViewer.tsx +0 -95
- package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +0 -103
- package/src/tools/Mermaid/hooks/index.ts +0 -4
- package/src/tools/Mermaid/hooks/useMermaidCleanup.ts +0 -73
- package/src/tools/Mermaid/hooks/useMermaidFullscreen.ts +0 -46
- package/src/tools/Mermaid/hooks/useMermaidRenderer.ts +0 -226
- package/src/tools/Mermaid/hooks/useMermaidValidation.ts +0 -29
- package/src/tools/Mermaid/index.tsx +0 -41
- package/src/tools/Mermaid/utils/mermaid-helpers.ts +0 -33
- package/src/tools/OpenapiViewer/components/EndpointInfo.tsx +0 -149
- package/src/tools/OpenapiViewer/components/EndpointsLibrary.tsx +0 -263
- package/src/tools/OpenapiViewer/components/PlaygroundLayout.tsx +0 -125
- package/src/tools/OpenapiViewer/components/PlaygroundStepper.tsx +0 -100
- package/src/tools/OpenapiViewer/components/RequestBuilder.tsx +0 -157
- package/src/tools/OpenapiViewer/components/RequestParametersForm.tsx +0 -253
- package/src/tools/OpenapiViewer/components/ResponseViewer.tsx +0 -173
- package/src/tools/OpenapiViewer/components/VersionSelector.tsx +0 -68
- package/src/tools/OpenapiViewer/components/index.ts +0 -14
- package/src/tools/OpenapiViewer/constants.ts +0 -39
- package/src/tools/OpenapiViewer/context/PlaygroundContext.tsx +0 -337
- package/src/tools/OpenapiViewer/hooks/index.ts +0 -8
- package/src/tools/OpenapiViewer/hooks/useMobile.ts +0 -10
- package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +0 -199
- package/src/tools/OpenapiViewer/index.tsx +0 -38
- package/src/tools/OpenapiViewer/types.ts +0 -151
- package/src/tools/OpenapiViewer/utils/apiKeyManager.ts +0 -149
- package/src/tools/OpenapiViewer/utils/formatters.ts +0 -71
- package/src/tools/OpenapiViewer/utils/index.ts +0 -9
- package/src/tools/OpenapiViewer/utils/versionManager.ts +0 -161
- package/src/tools/PrettyCode/PrettyCode.client.tsx +0 -208
- package/src/tools/PrettyCode/index.tsx +0 -45
- package/src/tools/VideoPlayer/@refactoring/00-PLAN.md +0 -91
- package/src/tools/VideoPlayer/@refactoring/01-TYPES.md +0 -284
- package/src/tools/VideoPlayer/@refactoring/02-UTILS.md +0 -141
- package/src/tools/VideoPlayer/@refactoring/03-HOOKS.md +0 -178
- package/src/tools/VideoPlayer/@refactoring/04-COMPONENTS.md +0 -95
- package/src/tools/VideoPlayer/@refactoring/05-EXECUTION-CHECKLIST.md +0 -139
- package/src/tools/VideoPlayer/README.md +0 -264
- package/src/tools/VideoPlayer/components/VideoControls.tsx +0 -138
- package/src/tools/VideoPlayer/components/VideoErrorFallback.tsx +0 -174
- package/src/tools/VideoPlayer/components/VideoPlayer.tsx +0 -201
- package/src/tools/VideoPlayer/components/index.ts +0 -14
- package/src/tools/VideoPlayer/context/VideoPlayerContext.tsx +0 -52
- package/src/tools/VideoPlayer/context/index.ts +0 -8
- package/src/tools/VideoPlayer/hooks/index.ts +0 -12
- package/src/tools/VideoPlayer/hooks/useVideoPlayerSettings.ts +0 -70
- package/src/tools/VideoPlayer/hooks/useVideoPositionCache.ts +0 -116
- package/src/tools/VideoPlayer/index.ts +0 -77
- package/src/tools/VideoPlayer/providers/NativeProvider.tsx +0 -284
- package/src/tools/VideoPlayer/providers/StreamProvider.tsx +0 -505
- package/src/tools/VideoPlayer/providers/VidstackProvider.tsx +0 -400
- package/src/tools/VideoPlayer/providers/index.ts +0 -8
- package/src/tools/VideoPlayer/types/index.ts +0 -38
- package/src/tools/VideoPlayer/types/player.ts +0 -116
- package/src/tools/VideoPlayer/types/provider.ts +0 -93
- package/src/tools/VideoPlayer/types/sources.ts +0 -97
- package/src/tools/VideoPlayer/utils/debug.ts +0 -14
- package/src/tools/VideoPlayer/utils/fileSource.ts +0 -78
- package/src/tools/VideoPlayer/utils/index.ts +0 -12
- package/src/tools/VideoPlayer/utils/resolvers.ts +0 -75
- package/src/tools/index.ts +0 -170
package/src/stores/mediaCache.ts
DELETED
|
@@ -1,534 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { create } from 'zustand';
|
|
4
|
-
import { persist, devtools } from 'zustand/middleware';
|
|
5
|
-
import { useShallow } from 'zustand/react/shallow';
|
|
6
|
-
import { createLogger } from '../lib/logger';
|
|
7
|
-
|
|
8
|
-
const cacheDebug = createLogger('MediaCache');
|
|
9
|
-
|
|
10
|
-
// Types
|
|
11
|
-
interface BlobUrlEntry {
|
|
12
|
-
url: string;
|
|
13
|
-
refCount: number;
|
|
14
|
-
createdAt: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface ImageDimensions {
|
|
18
|
-
width: number;
|
|
19
|
-
height: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface VideoMetadata {
|
|
23
|
-
duration: number;
|
|
24
|
-
width: number;
|
|
25
|
-
height: number;
|
|
26
|
-
codec?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
interface EffectConfig {
|
|
30
|
-
opacity: number;
|
|
31
|
-
scale: number;
|
|
32
|
-
blur: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Video player settings (persisted) */
|
|
36
|
-
interface VideoPlayerSettings {
|
|
37
|
-
volume: number;
|
|
38
|
-
isLooping: boolean;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Stream URL TTL (30 seconds - shorter to avoid stale session/token issues)
|
|
42
|
-
const STREAM_URL_TTL = 30 * 1000;
|
|
43
|
-
|
|
44
|
-
interface MediaCacheState {
|
|
45
|
-
// Blob URL cache (shared by Image, Audio, Video)
|
|
46
|
-
blobUrls: Map<string, BlobUrlEntry>;
|
|
47
|
-
|
|
48
|
-
// Image-specific
|
|
49
|
-
imageDimensions: Map<string, ImageDimensions>;
|
|
50
|
-
|
|
51
|
-
// Audio-specific
|
|
52
|
-
audioPlaybackPositions: Map<string, number>;
|
|
53
|
-
audioEffectConfigs: Map<string, EffectConfig>;
|
|
54
|
-
|
|
55
|
-
// Video-specific
|
|
56
|
-
videoStreamUrls: Map<string, { url: string; timestamp: number }>;
|
|
57
|
-
videoPosterUrls: Map<string, string>;
|
|
58
|
-
videoPlaybackPositions: Map<string, number>;
|
|
59
|
-
videoMetadata: Map<string, VideoMetadata>;
|
|
60
|
-
videoPlayerSettings: VideoPlayerSettings;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
interface MediaCacheActions {
|
|
64
|
-
// Blob URL management (shared)
|
|
65
|
-
getOrCreateBlobUrl: (
|
|
66
|
-
key: string,
|
|
67
|
-
content: ArrayBuffer,
|
|
68
|
-
mimeType: string
|
|
69
|
-
) => string;
|
|
70
|
-
releaseBlobUrl: (key: string) => void;
|
|
71
|
-
hasBlobUrl: (key: string) => boolean;
|
|
72
|
-
|
|
73
|
-
// Image actions
|
|
74
|
-
cacheDimensions: (src: string, dims: ImageDimensions) => void;
|
|
75
|
-
getDimensions: (src: string) => ImageDimensions | null;
|
|
76
|
-
|
|
77
|
-
// Audio actions
|
|
78
|
-
saveAudioPosition: (uri: string, time: number) => void;
|
|
79
|
-
getAudioPosition: (uri: string) => number | null;
|
|
80
|
-
getEffectConfig: (key: string) => EffectConfig | null;
|
|
81
|
-
cacheEffectConfig: (key: string, config: EffectConfig) => void;
|
|
82
|
-
|
|
83
|
-
// Video actions
|
|
84
|
-
getOrCreateStreamUrl: (
|
|
85
|
-
sessionId: string,
|
|
86
|
-
path: string,
|
|
87
|
-
generator: (sessionId: string, path: string) => string
|
|
88
|
-
) => string;
|
|
89
|
-
getPosterUrl: (title: string) => string | null;
|
|
90
|
-
cachePosterUrl: (title: string, url: string) => void;
|
|
91
|
-
saveVideoPosition: (key: string, time: number) => void;
|
|
92
|
-
getVideoPosition: (key: string) => number | null;
|
|
93
|
-
cacheVideoMetadata: (url: string, meta: VideoMetadata) => void;
|
|
94
|
-
getVideoMetadata: (url: string) => VideoMetadata | null;
|
|
95
|
-
invalidateSession: (sessionId: string) => void;
|
|
96
|
-
getVideoPlayerSettings: () => VideoPlayerSettings;
|
|
97
|
-
saveVideoPlayerSettings: (settings: Partial<VideoPlayerSettings>) => void;
|
|
98
|
-
|
|
99
|
-
// Global
|
|
100
|
-
clearCache: () => void;
|
|
101
|
-
getCacheStats: () => {
|
|
102
|
-
blobUrls: number;
|
|
103
|
-
dimensions: number;
|
|
104
|
-
audioPositions: number;
|
|
105
|
-
videoPositions: number;
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
type MediaCacheStore = MediaCacheState & MediaCacheActions;
|
|
110
|
-
|
|
111
|
-
// Initial state
|
|
112
|
-
const DEFAULT_VIDEO_SETTINGS: VideoPlayerSettings = {
|
|
113
|
-
volume: 1,
|
|
114
|
-
isLooping: false,
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const initialState: MediaCacheState = {
|
|
118
|
-
blobUrls: new Map(),
|
|
119
|
-
imageDimensions: new Map(),
|
|
120
|
-
audioPlaybackPositions: new Map(),
|
|
121
|
-
audioEffectConfigs: new Map(),
|
|
122
|
-
videoStreamUrls: new Map(),
|
|
123
|
-
videoPosterUrls: new Map(),
|
|
124
|
-
videoPlaybackPositions: new Map(),
|
|
125
|
-
videoMetadata: new Map(),
|
|
126
|
-
videoPlayerSettings: DEFAULT_VIDEO_SETTINGS,
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
export const useMediaCacheStore = create<MediaCacheStore>()(
|
|
130
|
-
devtools(
|
|
131
|
-
persist(
|
|
132
|
-
(set, get) => ({
|
|
133
|
-
...initialState,
|
|
134
|
-
|
|
135
|
-
// ========== Blob URL Management ==========
|
|
136
|
-
|
|
137
|
-
getOrCreateBlobUrl: (key, content, mimeType) => {
|
|
138
|
-
const existing = get().blobUrls.get(key);
|
|
139
|
-
if (existing) {
|
|
140
|
-
// Increment ref count
|
|
141
|
-
cacheDebug.debug(`Blob URL reused: ${key}`, { refCount: existing.refCount + 1 });
|
|
142
|
-
set(
|
|
143
|
-
(state) => ({
|
|
144
|
-
blobUrls: new Map(state.blobUrls).set(key, {
|
|
145
|
-
...existing,
|
|
146
|
-
refCount: existing.refCount + 1,
|
|
147
|
-
}),
|
|
148
|
-
}),
|
|
149
|
-
false,
|
|
150
|
-
'blobUrl/incrementRef'
|
|
151
|
-
);
|
|
152
|
-
return existing.url;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Create new blob URL
|
|
156
|
-
const blob = new Blob([content], { type: mimeType });
|
|
157
|
-
const url = URL.createObjectURL(blob);
|
|
158
|
-
const sizeMB = (content.byteLength / 1024 / 1024).toFixed(2);
|
|
159
|
-
cacheDebug.debug(`Blob URL created: ${key}`, { mimeType, size: `${sizeMB}MB` });
|
|
160
|
-
set(
|
|
161
|
-
(state) => ({
|
|
162
|
-
blobUrls: new Map(state.blobUrls).set(key, {
|
|
163
|
-
url,
|
|
164
|
-
refCount: 1,
|
|
165
|
-
createdAt: Date.now(),
|
|
166
|
-
}),
|
|
167
|
-
}),
|
|
168
|
-
false,
|
|
169
|
-
'blobUrl/create'
|
|
170
|
-
);
|
|
171
|
-
return url;
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
releaseBlobUrl: (key) => {
|
|
175
|
-
const entry = get().blobUrls.get(key);
|
|
176
|
-
if (!entry) return;
|
|
177
|
-
|
|
178
|
-
if (entry.refCount <= 1) {
|
|
179
|
-
// Last reference - revoke and remove
|
|
180
|
-
cacheDebug.debug(`Blob URL revoked: ${key}`);
|
|
181
|
-
URL.revokeObjectURL(entry.url);
|
|
182
|
-
set(
|
|
183
|
-
(state) => {
|
|
184
|
-
const newMap = new Map(state.blobUrls);
|
|
185
|
-
newMap.delete(key);
|
|
186
|
-
return { blobUrls: newMap };
|
|
187
|
-
},
|
|
188
|
-
false,
|
|
189
|
-
'blobUrl/revoke'
|
|
190
|
-
);
|
|
191
|
-
} else {
|
|
192
|
-
// Decrement ref count
|
|
193
|
-
cacheDebug.debug(`Blob URL released: ${key}`, { refCount: entry.refCount - 1 });
|
|
194
|
-
set(
|
|
195
|
-
(state) => ({
|
|
196
|
-
blobUrls: new Map(state.blobUrls).set(key, {
|
|
197
|
-
...entry,
|
|
198
|
-
refCount: entry.refCount - 1,
|
|
199
|
-
}),
|
|
200
|
-
}),
|
|
201
|
-
false,
|
|
202
|
-
'blobUrl/decrementRef'
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
|
|
207
|
-
hasBlobUrl: (key) => get().blobUrls.has(key),
|
|
208
|
-
|
|
209
|
-
// ========== Image Cache ==========
|
|
210
|
-
|
|
211
|
-
cacheDimensions: (src, dims) => {
|
|
212
|
-
set(
|
|
213
|
-
(state) => ({
|
|
214
|
-
imageDimensions: new Map(state.imageDimensions).set(src, dims),
|
|
215
|
-
}),
|
|
216
|
-
false,
|
|
217
|
-
'image/cacheDimensions'
|
|
218
|
-
);
|
|
219
|
-
},
|
|
220
|
-
|
|
221
|
-
getDimensions: (src) => get().imageDimensions.get(src) ?? null,
|
|
222
|
-
|
|
223
|
-
// ========== Audio Cache ==========
|
|
224
|
-
|
|
225
|
-
saveAudioPosition: (uri, time) => {
|
|
226
|
-
set(
|
|
227
|
-
(state) => ({
|
|
228
|
-
audioPlaybackPositions: new Map(state.audioPlaybackPositions).set(
|
|
229
|
-
uri,
|
|
230
|
-
time
|
|
231
|
-
),
|
|
232
|
-
}),
|
|
233
|
-
false,
|
|
234
|
-
'audio/savePosition'
|
|
235
|
-
);
|
|
236
|
-
},
|
|
237
|
-
|
|
238
|
-
getAudioPosition: (uri) =>
|
|
239
|
-
get().audioPlaybackPositions.get(uri) ?? null,
|
|
240
|
-
|
|
241
|
-
getEffectConfig: (key) => get().audioEffectConfigs.get(key) ?? null,
|
|
242
|
-
|
|
243
|
-
cacheEffectConfig: (key, config) => {
|
|
244
|
-
set(
|
|
245
|
-
(state) => ({
|
|
246
|
-
audioEffectConfigs: new Map(state.audioEffectConfigs).set(
|
|
247
|
-
key,
|
|
248
|
-
config
|
|
249
|
-
),
|
|
250
|
-
}),
|
|
251
|
-
false,
|
|
252
|
-
'audio/cacheEffectConfig'
|
|
253
|
-
);
|
|
254
|
-
},
|
|
255
|
-
|
|
256
|
-
// ========== Video Cache ==========
|
|
257
|
-
|
|
258
|
-
getOrCreateStreamUrl: (sessionId, path, generator) => {
|
|
259
|
-
const key = `${sessionId}:${path}`;
|
|
260
|
-
const cached = get().videoStreamUrls.get(key);
|
|
261
|
-
|
|
262
|
-
// Return if fresh
|
|
263
|
-
if (cached && Date.now() - cached.timestamp < STREAM_URL_TTL) {
|
|
264
|
-
return cached.url;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Generate and cache
|
|
268
|
-
const url = generator(sessionId, path);
|
|
269
|
-
set(
|
|
270
|
-
(state) => ({
|
|
271
|
-
videoStreamUrls: new Map(state.videoStreamUrls).set(key, {
|
|
272
|
-
url,
|
|
273
|
-
timestamp: Date.now(),
|
|
274
|
-
}),
|
|
275
|
-
}),
|
|
276
|
-
false,
|
|
277
|
-
'video/cacheStreamUrl'
|
|
278
|
-
);
|
|
279
|
-
return url;
|
|
280
|
-
},
|
|
281
|
-
|
|
282
|
-
getPosterUrl: (title) => get().videoPosterUrls.get(title) ?? null,
|
|
283
|
-
|
|
284
|
-
cachePosterUrl: (title, url) => {
|
|
285
|
-
set(
|
|
286
|
-
(state) => ({
|
|
287
|
-
videoPosterUrls: new Map(state.videoPosterUrls).set(title, url),
|
|
288
|
-
}),
|
|
289
|
-
false,
|
|
290
|
-
'video/cachePosterUrl'
|
|
291
|
-
);
|
|
292
|
-
},
|
|
293
|
-
|
|
294
|
-
saveVideoPosition: (key, time) => {
|
|
295
|
-
set(
|
|
296
|
-
(state) => ({
|
|
297
|
-
videoPlaybackPositions: new Map(state.videoPlaybackPositions).set(
|
|
298
|
-
key,
|
|
299
|
-
time
|
|
300
|
-
),
|
|
301
|
-
}),
|
|
302
|
-
false,
|
|
303
|
-
'video/savePosition'
|
|
304
|
-
);
|
|
305
|
-
},
|
|
306
|
-
|
|
307
|
-
getVideoPosition: (key) =>
|
|
308
|
-
get().videoPlaybackPositions.get(key) ?? null,
|
|
309
|
-
|
|
310
|
-
cacheVideoMetadata: (url, meta) => {
|
|
311
|
-
set(
|
|
312
|
-
(state) => ({
|
|
313
|
-
videoMetadata: new Map(state.videoMetadata).set(url, meta),
|
|
314
|
-
}),
|
|
315
|
-
false,
|
|
316
|
-
'video/cacheMetadata'
|
|
317
|
-
);
|
|
318
|
-
},
|
|
319
|
-
|
|
320
|
-
getVideoMetadata: (url) => get().videoMetadata.get(url) ?? null,
|
|
321
|
-
|
|
322
|
-
invalidateSession: (sessionId) => {
|
|
323
|
-
set(
|
|
324
|
-
(state) => {
|
|
325
|
-
const newUrls = new Map(state.videoStreamUrls);
|
|
326
|
-
for (const [key] of newUrls) {
|
|
327
|
-
if (key.startsWith(`${sessionId}:`)) {
|
|
328
|
-
newUrls.delete(key);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
return { videoStreamUrls: newUrls };
|
|
332
|
-
},
|
|
333
|
-
false,
|
|
334
|
-
'video/invalidateSession'
|
|
335
|
-
);
|
|
336
|
-
},
|
|
337
|
-
|
|
338
|
-
getVideoPlayerSettings: () => get().videoPlayerSettings,
|
|
339
|
-
|
|
340
|
-
saveVideoPlayerSettings: (settings) => {
|
|
341
|
-
set(
|
|
342
|
-
(state) => ({
|
|
343
|
-
videoPlayerSettings: { ...state.videoPlayerSettings, ...settings },
|
|
344
|
-
}),
|
|
345
|
-
false,
|
|
346
|
-
'video/savePlayerSettings'
|
|
347
|
-
);
|
|
348
|
-
},
|
|
349
|
-
|
|
350
|
-
// ========== Global ==========
|
|
351
|
-
|
|
352
|
-
clearCache: () => {
|
|
353
|
-
const stats = get().getCacheStats();
|
|
354
|
-
cacheDebug.info('Clearing cache', stats);
|
|
355
|
-
// Revoke all blob URLs before clearing
|
|
356
|
-
get().blobUrls.forEach(({ url }) => URL.revokeObjectURL(url));
|
|
357
|
-
set(initialState, false, 'clearCache');
|
|
358
|
-
},
|
|
359
|
-
|
|
360
|
-
getCacheStats: () => ({
|
|
361
|
-
blobUrls: get().blobUrls.size,
|
|
362
|
-
dimensions: get().imageDimensions.size,
|
|
363
|
-
audioPositions: get().audioPlaybackPositions.size,
|
|
364
|
-
videoPositions: get().videoPlaybackPositions.size,
|
|
365
|
-
}),
|
|
366
|
-
}),
|
|
367
|
-
{
|
|
368
|
-
name: 'media-cache-storage',
|
|
369
|
-
// Only persist playback positions, poster URLs, and player settings
|
|
370
|
-
partialize: (state) => ({
|
|
371
|
-
audioPlaybackPositions: Array.from(
|
|
372
|
-
state.audioPlaybackPositions.entries()
|
|
373
|
-
),
|
|
374
|
-
videoPlaybackPositions: Array.from(
|
|
375
|
-
state.videoPlaybackPositions.entries()
|
|
376
|
-
),
|
|
377
|
-
videoPosterUrls: Array.from(state.videoPosterUrls.entries()),
|
|
378
|
-
videoPlayerSettings: state.videoPlayerSettings,
|
|
379
|
-
}),
|
|
380
|
-
// Rehydrate Maps from arrays
|
|
381
|
-
onRehydrateStorage: () => (state) => {
|
|
382
|
-
if (state) {
|
|
383
|
-
// Type assertion for persisted data
|
|
384
|
-
const persistedAudio = state.audioPlaybackPositions as unknown;
|
|
385
|
-
const persistedVideo = state.videoPlaybackPositions as unknown;
|
|
386
|
-
const persistedPosters = state.videoPosterUrls as unknown;
|
|
387
|
-
const persistedSettings = state.videoPlayerSettings as unknown;
|
|
388
|
-
|
|
389
|
-
state.audioPlaybackPositions = new Map(
|
|
390
|
-
Array.isArray(persistedAudio)
|
|
391
|
-
? (persistedAudio as [string, number][])
|
|
392
|
-
: []
|
|
393
|
-
);
|
|
394
|
-
state.videoPlaybackPositions = new Map(
|
|
395
|
-
Array.isArray(persistedVideo)
|
|
396
|
-
? (persistedVideo as [string, number][])
|
|
397
|
-
: []
|
|
398
|
-
);
|
|
399
|
-
state.videoPosterUrls = new Map(
|
|
400
|
-
Array.isArray(persistedPosters)
|
|
401
|
-
? (persistedPosters as [string, string][])
|
|
402
|
-
: []
|
|
403
|
-
);
|
|
404
|
-
// Merge persisted settings with defaults (handles missing fields)
|
|
405
|
-
state.videoPlayerSettings = {
|
|
406
|
-
...DEFAULT_VIDEO_SETTINGS,
|
|
407
|
-
...(persistedSettings && typeof persistedSettings === 'object'
|
|
408
|
-
? (persistedSettings as Partial<VideoPlayerSettings>)
|
|
409
|
-
: {}),
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
},
|
|
413
|
-
}
|
|
414
|
-
),
|
|
415
|
-
{ name: 'MediaCacheStore' }
|
|
416
|
-
)
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
// ========== Selective Hooks ==========
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Hook for image-related cache operations
|
|
423
|
-
* Uses useShallow to prevent infinite re-renders
|
|
424
|
-
*/
|
|
425
|
-
export const useImageCache = () =>
|
|
426
|
-
useMediaCacheStore(
|
|
427
|
-
useShallow((state) => ({
|
|
428
|
-
getOrCreateBlobUrl: state.getOrCreateBlobUrl,
|
|
429
|
-
releaseBlobUrl: state.releaseBlobUrl,
|
|
430
|
-
hasBlobUrl: state.hasBlobUrl,
|
|
431
|
-
cacheDimensions: state.cacheDimensions,
|
|
432
|
-
getDimensions: state.getDimensions,
|
|
433
|
-
}))
|
|
434
|
-
);
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Hook for audio-related cache operations
|
|
438
|
-
* Uses useShallow to prevent infinite re-renders
|
|
439
|
-
*/
|
|
440
|
-
export const useAudioCache = () =>
|
|
441
|
-
useMediaCacheStore(
|
|
442
|
-
useShallow((state) => ({
|
|
443
|
-
getOrCreateBlobUrl: state.getOrCreateBlobUrl,
|
|
444
|
-
releaseBlobUrl: state.releaseBlobUrl,
|
|
445
|
-
hasBlobUrl: state.hasBlobUrl,
|
|
446
|
-
saveAudioPosition: state.saveAudioPosition,
|
|
447
|
-
getAudioPosition: state.getAudioPosition,
|
|
448
|
-
getEffectConfig: state.getEffectConfig,
|
|
449
|
-
cacheEffectConfig: state.cacheEffectConfig,
|
|
450
|
-
}))
|
|
451
|
-
);
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* Hook for video-related cache operations
|
|
455
|
-
* Uses useShallow to prevent infinite re-renders
|
|
456
|
-
*/
|
|
457
|
-
export const useVideoCache = () =>
|
|
458
|
-
useMediaCacheStore(
|
|
459
|
-
useShallow((state) => ({
|
|
460
|
-
getOrCreateBlobUrl: state.getOrCreateBlobUrl,
|
|
461
|
-
releaseBlobUrl: state.releaseBlobUrl,
|
|
462
|
-
hasBlobUrl: state.hasBlobUrl,
|
|
463
|
-
getOrCreateStreamUrl: state.getOrCreateStreamUrl,
|
|
464
|
-
getPosterUrl: state.getPosterUrl,
|
|
465
|
-
cachePosterUrl: state.cachePosterUrl,
|
|
466
|
-
saveVideoPosition: state.saveVideoPosition,
|
|
467
|
-
getVideoPosition: state.getVideoPosition,
|
|
468
|
-
cacheVideoMetadata: state.cacheVideoMetadata,
|
|
469
|
-
getVideoMetadata: state.getVideoMetadata,
|
|
470
|
-
invalidateSession: state.invalidateSession,
|
|
471
|
-
getVideoPlayerSettings: state.getVideoPlayerSettings,
|
|
472
|
-
saveVideoPlayerSettings: state.saveVideoPlayerSettings,
|
|
473
|
-
}))
|
|
474
|
-
);
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Hook for video player settings only
|
|
478
|
-
* Returns current settings and save function
|
|
479
|
-
*/
|
|
480
|
-
export const useVideoPlayerSettings = () =>
|
|
481
|
-
useMediaCacheStore(
|
|
482
|
-
useShallow((state) => ({
|
|
483
|
-
settings: state.videoPlayerSettings,
|
|
484
|
-
saveSettings: state.saveVideoPlayerSettings,
|
|
485
|
-
}))
|
|
486
|
-
);
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* Hook for blob URL cleanup on unmount
|
|
490
|
-
*/
|
|
491
|
-
export function useBlobUrlCleanup(key: string | null) {
|
|
492
|
-
const releaseBlobUrl = useMediaCacheStore((s) => s.releaseBlobUrl);
|
|
493
|
-
|
|
494
|
-
// Using inline effect to avoid importing useEffect in store
|
|
495
|
-
if (typeof window !== 'undefined') {
|
|
496
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
497
|
-
const { useEffect } = require('react');
|
|
498
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
499
|
-
useEffect(() => {
|
|
500
|
-
return () => {
|
|
501
|
-
if (key) {
|
|
502
|
-
releaseBlobUrl(key);
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
}, [key, releaseBlobUrl]);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// ========== Utilities ==========
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* Generate a cache key from ArrayBuffer content
|
|
513
|
-
* Uses first and last 1KB + length for fast hashing
|
|
514
|
-
*/
|
|
515
|
-
export function generateContentKey(content: ArrayBuffer): string {
|
|
516
|
-
const arr = new Uint8Array(content);
|
|
517
|
-
const len = arr.length;
|
|
518
|
-
|
|
519
|
-
// Take first 1KB
|
|
520
|
-
const start = arr.slice(0, Math.min(1024, len));
|
|
521
|
-
// Take last 1KB
|
|
522
|
-
const end = arr.slice(Math.max(0, len - 1024));
|
|
523
|
-
|
|
524
|
-
// Simple hash from bytes
|
|
525
|
-
let hash = len;
|
|
526
|
-
for (let i = 0; i < start.length; i++) {
|
|
527
|
-
hash = ((hash << 5) - hash + start[i]) | 0;
|
|
528
|
-
}
|
|
529
|
-
for (let i = 0; i < end.length; i++) {
|
|
530
|
-
hash = ((hash << 5) - hash + end[i]) | 0;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
return `blob-${len}-${hash.toString(16)}`;
|
|
534
|
-
}
|