feat(media): service contracts between media slices
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138YqDj3ttfayRwuMKKrqVj
This commit is contained in:
parent
475710e27e
commit
2556ed197e
@ -11,10 +11,30 @@ import type {
|
|||||||
MediaSourceProvider,
|
MediaSourceProvider,
|
||||||
MotionGraphicsRenderer,
|
MotionGraphicsRenderer,
|
||||||
} from "@rakazo/adapter-kit";
|
} from "@rakazo/adapter-kit";
|
||||||
import type { ModelTier } from "@rakazo/contracts";
|
import type {
|
||||||
|
AiAlteration,
|
||||||
|
ClientMediaItem,
|
||||||
|
CreateMarkInput,
|
||||||
|
Edl,
|
||||||
|
EdlError,
|
||||||
|
FfprobeReport,
|
||||||
|
GateReport,
|
||||||
|
IntroVariant,
|
||||||
|
MarketingCopy,
|
||||||
|
MarkTreatment,
|
||||||
|
MediaEditPlan,
|
||||||
|
MediaMark,
|
||||||
|
MediaPublication,
|
||||||
|
MediaRender,
|
||||||
|
MediaRenderOutput,
|
||||||
|
MediaRenderQuality,
|
||||||
|
ModelTier,
|
||||||
|
PlatformProfile,
|
||||||
|
Utm,
|
||||||
|
} from "@rakazo/contracts";
|
||||||
import type { MediaRepos, PrismaClient } from "@rakazo/db";
|
import type { MediaRepos, PrismaClient } from "@rakazo/db";
|
||||||
import { getLogger } from "@rakazo/logging";
|
import { getLogger } from "@rakazo/logging";
|
||||||
import type { ProfileSet } from "@rakazo/media";
|
import type { CompileSource, ProfileSet } from "@rakazo/media";
|
||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
import { aiRecipient } from "./ai-consent.js";
|
import { aiRecipient } from "./ai-consent.js";
|
||||||
|
|
||||||
@ -184,3 +204,152 @@ export function createMediaModelClient(deps: {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Service contracts between media slices. Each slice implements its part; the
|
||||||
|
// API, worker, and agent tools only depend on these interfaces.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export type CreateMarkInputType = z.infer<typeof CreateMarkInput>;
|
||||||
|
|
||||||
|
export interface MediaActor {
|
||||||
|
spaceId: string;
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediaIngestService {
|
||||||
|
/** Client-pushed metadata (Apple Photos, iOS): upsert, apply deletions, enqueue work. */
|
||||||
|
pushItems(
|
||||||
|
actor: MediaActor,
|
||||||
|
input: {
|
||||||
|
sourceId: string;
|
||||||
|
items: ClientMediaItem[];
|
||||||
|
deletedExternalIds: string[];
|
||||||
|
syncToken: string | null;
|
||||||
|
complete: boolean;
|
||||||
|
},
|
||||||
|
): Promise<{ created: number; updated: number; unchanged: number; deleted: number }>;
|
||||||
|
/** Enqueue an incremental sync of a server-side source. */
|
||||||
|
requestSync(actor: MediaActor, sourceId: string): Promise<void>;
|
||||||
|
/** An upload finished: move the file into place and enqueue proxy and analysis. */
|
||||||
|
uploadCompleted(
|
||||||
|
actor: MediaActor,
|
||||||
|
input: { assetId: string; tempPath: string; sha256: string; fileName: string },
|
||||||
|
): Promise<void>;
|
||||||
|
recluster(actor: MediaActor): Promise<void>;
|
||||||
|
/** Remove a source with its assets and every derived file. */
|
||||||
|
removeSource(actor: MediaActor, sourceId: string): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediaPreparedSources {
|
||||||
|
sources: Map<string, CompileSource>;
|
||||||
|
/** Alterations applied while preparing (inpaint, synthetic identity, voice). */
|
||||||
|
aiAlterations: AiAlteration[];
|
||||||
|
/** Redactions that could not be honored and fell back, for the plan rationale. */
|
||||||
|
fallbacks: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediaPrivacyService {
|
||||||
|
/**
|
||||||
|
* Resolve every clip's source file and apply all face, mark, and region
|
||||||
|
* redactions from the EDL plus person and mark defaults. Fails closed: a
|
||||||
|
* redaction that cannot be applied throws instead of returning an unredacted source.
|
||||||
|
*/
|
||||||
|
prepareSources(input: {
|
||||||
|
actor: MediaActor;
|
||||||
|
projectId: string;
|
||||||
|
edl: Edl;
|
||||||
|
quality: MediaRenderQuality;
|
||||||
|
workDir: string;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
}): Promise<MediaPreparedSources>;
|
||||||
|
/** Re-detect on the rendered output and evaluate the full gate. */
|
||||||
|
gate(input: {
|
||||||
|
actor: MediaActor;
|
||||||
|
projectId: string;
|
||||||
|
edl: Edl;
|
||||||
|
profile: PlatformProfile;
|
||||||
|
outputPath: string;
|
||||||
|
ffprobe: FfprobeReport;
|
||||||
|
copy: { title: string | null; caption: string | null; tags: string[] };
|
||||||
|
bundleHasDisclosure: boolean;
|
||||||
|
autoFixRound: number;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
}): Promise<GateReport>;
|
||||||
|
/** Create a mark: segment from the prompt, propagate, score, and suggest a fallback. */
|
||||||
|
createMark(actor: MediaActor, input: CreateMarkInputType): Promise<MediaMark>;
|
||||||
|
updateMark(
|
||||||
|
actor: MediaActor,
|
||||||
|
input: {
|
||||||
|
markId: string;
|
||||||
|
treatment?: MarkTreatment;
|
||||||
|
label?: string | null;
|
||||||
|
personId?: string | null;
|
||||||
|
},
|
||||||
|
): Promise<MediaMark>;
|
||||||
|
/** Delete a person, their biometric data, and their files. */
|
||||||
|
removePerson(actor: MediaActor, personId: string): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediaRenderService {
|
||||||
|
start(
|
||||||
|
actor: MediaActor,
|
||||||
|
input: { planId: string; quality: MediaRenderQuality },
|
||||||
|
): Promise<MediaRender>;
|
||||||
|
cancel(actor: MediaActor, renderId: string): Promise<MediaRender>;
|
||||||
|
/** Absolute path of a finished output for streaming, or null. */
|
||||||
|
outputPath(actor: MediaActor, renderId: string, fileName: string): Promise<string | null>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediaMarketingService {
|
||||||
|
autoPlan(
|
||||||
|
actor: MediaActor,
|
||||||
|
input: { projectId: string; profileIds?: string[] },
|
||||||
|
): Promise<MediaEditPlan[]>;
|
||||||
|
submitPlan(
|
||||||
|
actor: MediaActor,
|
||||||
|
input: {
|
||||||
|
projectId: string;
|
||||||
|
edl: unknown;
|
||||||
|
rationale: string | null;
|
||||||
|
createdBy: "agent" | "user";
|
||||||
|
},
|
||||||
|
): Promise<{ ok: true; plan: MediaEditPlan } | { ok: false; errors: EdlError[] }>;
|
||||||
|
introVariants(
|
||||||
|
actor: MediaActor,
|
||||||
|
input: { brandKitId: string; count: number },
|
||||||
|
): Promise<IntroVariant[]>;
|
||||||
|
thumbnails(
|
||||||
|
actor: MediaActor,
|
||||||
|
input: { renderId: string; count: number },
|
||||||
|
): Promise<MediaRenderOutput[]>;
|
||||||
|
copy(actor: MediaActor, input: { renderId: string; language: string }): Promise<MarketingCopy>;
|
||||||
|
createPublication(
|
||||||
|
actor: MediaActor,
|
||||||
|
input: {
|
||||||
|
renderId: string;
|
||||||
|
destination: string;
|
||||||
|
targetUrl: string | null;
|
||||||
|
utm: Utm | null;
|
||||||
|
title: string | null;
|
||||||
|
caption: string | null;
|
||||||
|
tags: string[];
|
||||||
|
thumbnailVariant: string | null;
|
||||||
|
scheduledAt: string | null;
|
||||||
|
},
|
||||||
|
): Promise<MediaPublication>;
|
||||||
|
bundle(actor: MediaActor, publicationId: string): Promise<MediaPublication>;
|
||||||
|
cancelPublication(actor: MediaActor, publicationId: string): Promise<MediaPublication>;
|
||||||
|
importMetricsCsv(
|
||||||
|
actor: MediaActor,
|
||||||
|
input: { mappingId: string; csv: string },
|
||||||
|
): Promise<{ imported: number; skipped: Array<{ row: number; reason: string }> }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediaServices {
|
||||||
|
deps: MediaServiceDeps;
|
||||||
|
ingest: MediaIngestService;
|
||||||
|
privacy: MediaPrivacyService;
|
||||||
|
render: MediaRenderService;
|
||||||
|
marketing: MediaMarketingService;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user