import { type RecordingPlaybackLocator, type RecordingPlaybackRef } from '../recording-playback-ref.js'; import { type ArkmeFileProgress } from '../file-transfer-contract.js'; import type { ArkmeSessionCredentials } from '../keychain-store.js'; import type { ArkmeContentBlock, ArkmeFileAssetDisplayItem, ArkmeImageBytes, ArkmeUploadedAsset } from '../types.js'; import { ProfileService } from './profile-service.js'; import { SelfRoleAvatarStore } from '../self-role-avatar-store.js'; import { ServiceRuntime } from './service.js'; export interface ArkmeWorldImageEntry { viewerUserId: number; sourceUrl: string; expiresAtMillis: number; } export interface ArkmeWorldImageReader { openWorldImageRef(imageRef: string, viewerUserId: number): Promise; } export interface ArkmeBotImageReader { openBotImageRef(imageRef: string, viewerUserId: number): Promise; } export interface ArkmeRecordIdentity { recordUid(raw: unknown): string; } export interface ArkmeUnmarkedSpeakerMediaTuple { viewerUserId: number; candidateId: string; segmentId: string; sessionId: string; childId: string; audioFileName: string; playbackRef?: RecordingPlaybackRef; } export interface ArkmeUnmarkedSpeakerSegmentResolver { resolveSegmentForMedia(segmentRef: string, candidateRef: string): Promise; } export interface ArkmeMediaDescriptor { purpose?: 'original'; viewerUserId: number; mimeType: string; fileName: string; size: number; expiresAtMillis: number; stableKey?: string; } type MediaSource = { remoteUrl: string; recordingClip?: never; } | { recordingClip: RecordingPlaybackRef; remoteUrl?: never; }; type IssuableMediaDescriptor = Omit & MediaSource; /** Safe browser playback data issued from one verified record audio asset. */ export interface ArkmeIssuedAudioMedia { mediaRef: string; durationSeconds?: number; } /** Persist only the confirmation payload, never signed storage URLs. */ export interface ArkmeUploadCompletion { upload_session_uid: string; uploaded_size: number; storage_etag: string; multipart_parts: Array<{ part_number: number; etag: string; }>; } export interface ArkmeUploadCompletionRecovery { read(): ArkmeUploadCompletion | undefined; save(completion: ArkmeUploadCompletion): void; } export declare const MAX_ARKME_IMAGE_BYTES: number; export declare class MediaService { private readonly runtime; private readonly profile; private readonly worldImages; private readonly recordIdentity; private readonly botImages?; private readonly selfRoleAvatars?; private readonly mediaRefs; private readonly stableMediaRefs; private readonly imageCache; private readonly imageInFlight; private imageCacheBytes; private activeImageDownloads; private readonly imageDownloadWaiters; private readonly avatarAssets; private avatarAssetTimer; private imageGeneration; private readonly localAvatarReads; constructor(runtime: ServiceRuntime, profile: ProfileService, worldImages: ArkmeWorldImageReader, recordIdentity: ArkmeRecordIdentity, botImages?: ArkmeBotImageReader | undefined, selfRoleAvatars?: SelfRoleAvatarStore | undefined); dispose(): void; queryFileAssets(fileAssetUids: readonly string[], signal?: AbortSignal): Promise; private queryFileAssetsForSession; uploadLocalFile(filePath: string, metadata: { size: number; sha256: string; mimeType: string; fileName: string; fileKind: 1 | 2 | 3 | 4; }, options?: { onProgress?: (progress: ArkmeFileProgress) => void; expectedUserId?: number; signal?: AbortSignal; completionRecovery?: ArkmeUploadCompletionRecovery; }): Promise; fetchMedia(mediaRef: string, range?: string, signal?: AbortSignal, originalOnly?: boolean, method?: 'GET' | 'HEAD'): Promise<{ response: Response; descriptor: ArkmeMediaDescriptor; }>; issueWorldVoiceprintMediaRef(viewerUserId: number, input: { remoteUrl: string; mimeType: string; }): string; issueUnmarkedSpeakerMediaRef(resolver: ArkmeUnmarkedSpeakerSegmentResolver, candidateRef: string, segmentRef: string, signal?: AbortSignal): Promise; /** Context selection authorizes immutable clips; the normal playback owner * rechecks access when streaming historical files or current packed media. */ issueContextPlayback(body: Record, signal?: AbortSignal): Promise; issueRecordingPlaybackMediaRef(input: { viewerUserId: number; locator: RecordingPlaybackLocator; }, signal?: AbortSignal): Promise; private issueRecordingClip; readImage(imageRef: string, options?: { maxBytes?: number; signal?: AbortSignal; refresh?: boolean; cacheOnly?: boolean; }): Promise; /** Immutable asset bytes survive URL expiry and Host restarts in the existing avatar store. */ private readFileAssetImage; private assertImageAccount; private validAvatar; private readLocalFileAssetImage; private loadFileAssetImage; private queueAvatarAsset; private flushAvatarAssets; private cachedImage; private cacheImage; withImageDownloadPermit(operation: () => Promise): Promise; private readImageUncached; private mediaKind; recordContentPayload(raw: unknown): Record; /** * Older quick-record projections carry their voice outside media_refs. The * Flutter clients accept these payload forms, so normalize them before the * shared display-item hydration and rich-content renderer see the record. */ private recordVoiceMediaRef; private recordMediaRefs; private queryRecordMediaDisplayItems; issueSearchAudioMedia(requests: Array<{ recordUid: string; fileAssetUid: string; }>, signal?: AbortSignal): Promise>; issueSearchAudioMediaRefs(requests: Array<{ recordUid: string; fileAssetUid: string; }>, signal?: AbortSignal): Promise>; issueSearchFileMediaRefs(requests: Array<{ recordUid: string; fileAssetUid: string; }>, signal?: AbortSignal): Promise>; /** Snapshot membership comes only from its refs; current media is an access-address source. */ hydrateRecordSnapshotMediaPage(snapshots: Record[], session: ArkmeSessionCredentials, chat?: { chatSessionUid: string; recordUid: string; recordOwnerUserId: number | string; relationUid: string; }, signal?: AbortSignal): Promise; hydrateRecordMediaPage(rawItems: unknown[], session: ArkmeSessionCredentials, signal?: AbortSignal): Promise<{ displayItemsByRecordUid: Map; unavailableRecordUids: Set; }>; /** Only the already-authorized received snapshot is used; never hydrate its source IDs. */ forwardContentBlocks(files: unknown[], viewerUserId: number, options?: { longArticle?: boolean; }): ArkmeContentBlock[]; issueImageMediaRef(viewerUserId: number, descriptor: IssuableMediaDescriptor, stableIdentity: string): string; favoriteStickerMediaRef(raw: unknown, viewerUserId: number): string | undefined; private issueMediaRef; /** A current Record version does not imply that every media URL was resolved. */ recordMediaUnavailable(raw: unknown, blocks: readonly ArkmeContentBlock[], requireComplete?: boolean): boolean; richContentBlocks(raw: unknown, viewerUserId: number, hydratedDisplayItems?: unknown[]): ArkmeContentBlock[]; private ossCredentials; private audioOssCredentials; downloadSignedImage(signedUrl: URL, byteLimit: number, signal?: AbortSignal, scope?: string): Promise; private downloadSignedImageDirect; private imageBytesFromResponse; } export {}; //# sourceMappingURL=media-service.d.ts.map