דילוג לתוכן
תיעוד Nodaro
תיעודמדריך הרכיביםמודליםתבניותסוכני AI (MCP)מפתחיםאירוח עצמימחקרהשוואות
SDK ל-TypeScript

עריכה

עריכת פודקאסטים וסרטונים ארוכים מ-TypeScript: הידוק פרק בקריאה אחת, זיהוי שקט, סנכרון הקלטות, תכנון חיתוכים ורינדור של רשימת עריכה.

client.edit מרכז את כלי העריכה לפודקאסטים ולסרטונים ארוכים. הוא מהדק פרק שלם בקריאה אחת, מוצא שקט ומודד את הפרשי הזמן בין כמה הקלטות. הוא גם מתכנן חיתוך מתוך תמלול ומרנדר רשימת עריכה, או EDL, לקובץ וידאו או אודיו מוגמר. רוב המתודות מתחילות משימה ומחזירות את המזהה שלה, ואת הסטטוס של המשימה בודקים מדי כמה שניות עם client.jobs.getStatus(). המתודה estimateTightenEpisode() נותנת אומדן מחיר להרצה, והמתודה remapTranscript() רצה מקומית, בלי בקשה לשרת.

מתודות

מתודהנקודת קצהמה היא עושה
edit.tightenEpisode(input, options?)POST /v1/tighten-episodeהידוק של הקלטה אחת לפרק מוגמר, בקריאה אחת
edit.estimateTightenEpisode(input)POST /v1/tighten-episode/estimateאומדן מחיר להרצה של הידוק פרק. לא נוצר כלום ולא נגבים קרדיטים
edit.continueUseCase(input, options?)POST /v1/<type>/continue, היום /v1/tighten-episode/continueהרצת המשך של הידוק פרק, כמשימה חדשה
edit.silenceDetect(input)POST /v1/silence-detectאיתור הטווחים השקטים במקור אודיו או וידאו
edit.audioSync(input)POST /v1/audio-syncמדידת ההיסט בזמן בין 2 ל-6 הקלטות
edit.editPlan(input)POST /v1/edit-planתכנון של חיתוך מהודק, קליפים קצרים, פרקים או טריילר מתוך תמלול
edit.applyEdl(input)POST /v1/apply-edlרינדור EDL לקובץ וידאו או אודיו
edit.remapTranscript(edl, transcript)אין, מקומיתהעברת התזמונים של תמלול לציר הזמן הערוך

עריכת פודקאסט מקוד

לפרק שלם, המתודה tightenEpisode() מריצה את כל השלבים שלמטה בקריאה אחת. כשצריך שליטה בכל שלב, הריצו את השלבים אחד אחד.

תמלול ההקלטה

הריצו את client.audio.transcribe() עם מנוע שמחזיר תזמון לכל מילה. השדה output_data.json של המשימה הוא התמלול שהתוכנית צריכה.

איתור השקט

הריצו את silenceDetect() על ההקלטה הראשית. ב-output_data.json של המשימה נמצאים הטווחים השקטים.

תכנון החיתוך

הריצו את editPlan() במצב tighten עם התמלול, המקורות והשקט. קבלו את התוכנית עם unwrapEditPlanOutput().

רינדור התוכנית

העבירו את התוכנית ל-applyEdl(). בפלט של המשימה שהסתיימה נמצאים הסרטון או האודיו הערוכים.

העלאת וידאוהפרקתמלולתזמון לכל מילהזיהוי שקטתוכנית עריכההידוקהחלת רשימת עריכה
אותה שרשרת עריכה, כרכיבים: תמלול ואיתור השקט, תכנון של חיתוך מהודק יותר, ואז רינדור.
import { unwrapEditPlanOutput } from "@nodaro/sdk"

async function outputOf(jobId: string): Promise<any> {
  for (;;) {
    const { data } = await client.jobs.getStatus(jobId)
    if (data.status === "completed") return data.output_data
    if (data.status === "failed" || data.status === "cancelled") throw new Error(data.error_message ?? data.status)
    await new Promise((resolve) => setTimeout(resolve, 3_000))
  }
}

// 1. Transcribe with word timings
const tr = await client.audio.transcribe({ audioUrl: masterUrl, provider: "elevenlabs-stt" })
const transcript = (await outputOf(tr.jobId)).json

// 2. Find the silence
const sd = await client.edit.silenceDetect({ audioUrl: masterUrl, thresholdDb: -35 })
const silence = (await outputOf(sd.jobId)).json

// 3. Plan a tighter cut
const plan = await client.edit.editPlan({
  mode: "tighten",
  planTier: "standard",
  transcript,
  sources: [{ id: "ep", url: masterUrl, kind: "video", role: "master-audio" }],
  silence,
})
const edl = unwrapEditPlanOutput(await outputOf(plan.jobId))

// 4. Render the plan
const render = await client.edit.applyEdl({ edl, output: "video", quality: "final" })
const { videoUrl } = await outputOf(render.jobId)

אותם שלבים קיימים גם כרכיבים: תמלול, זיהוי שקט, תוכנית עריכה והחלת רשימת עריכה.

client.edit

edit.tightenEpisode(input, options?)‎ ו-estimateTightenEpisode(input)‎

מהדקת הקלטה של פודקאסט בקריאה אחת, כמו רכיב הידוק פרק. ההרצה מתמללת את ההקלטה, מתכננת את החיתוך המהודק ומרנדרת אותו. לפי ההגדרות, היא גם מוסיפה לפרק כתוביות, ממסגרת אותו ומשלבת מתחתיו מוזיקת רקע. רק השדה mediaUrl נדרש, ושדה שלא נשלח מקבל את ברירת המחדל של הרכיב.

tightenEpisode(input: TightenEpisodeInput, options?: { idempotencyKey?: string }): Promise<TightenEpisodeJobResult>
estimateTightenEpisode(input: TightenEpisodeInput): Promise<{ data: TightenEpisodeEstimate }>

מאפיין

סוג

const quote = await client.edit.estimateTightenEpisode({ mediaUrl: episodeUrl })
console.log(quote.data.totalCredits)

const { jobId } = await client.edit.tightenEpisode(
  { mediaUrl: episodeUrl, framing: { aspect: "9:16", layout: "auto" } },
  { idempotencyKey: crypto.randomUUID() },
)

המתודה tightenEpisode() מחזירה { jobId, executionId?, deduped? }. אם שולחים שוב את אותה בקשה עם אותו idempotencyKey, התגובה היא ההרצה שכבר התחילה, עם deduped: true. בדקו את הסטטוס של jobId מדי כמה שניות. ב-output_data של המשימה שהסתיימה נמצא הפרק, ב-videoUrl, או ב-audioUrl בפרק אודיו. שם נמצאים גם ה-EDL של התוכנית, ב-json, והשדה quality, שערכו "proxy" לתצוגה מקדימה לבדיקה ו-"final" בכל מקרה אחר.

המתודה estimateTightenEpisode() מחזירה { totalCredits, stepsCredits, feeCredits, durationSec, estimate? }: השלבים של ההרצה, כל אחד בתעריף שלו, ועוד עמלה קבועה לכל הרצה. הערך estimate: true פירושו שהמחיר של אחד השלבים הוא אומדן של קריאה ל-LLM שמחויבת לפי שימוש, ולכן הציגו את הסכום הכולל כ-≈N. האומדן נוטה כלפי מעלה, כי הוא מתמחר את הרינדור לפי האורך המלא של ההקלטה, לפני שהעריכה ידועה. החיוב נקבע לפי הדקות שרונדרו בפועל. השדה durationSec הוא null כשאי אפשר היה לקרוא את האורך של ההקלטה. במהדורה בלי קרדיטים, כל סכום של קרדיטים הוא 0.

שתי המתודות דוחות בקשה לפני שמשהו נוצר ולפני שנגבים קרדיטים. בשגיאת 400 הקוד הוא validation_error, media_unreadable, media_has_no_audio, media_too_long או music_has_no_audio. בשגיאת 403 הקוד הוא node_not_available. בשגיאת 503 הקוד הוא nodaro_connection_required או use_case_unavailable. המתודה tightenEpisode() זורקת גם InsufficientCreditsError כשהיתרה לא מכסה את האומדן. בהתקנה באירוח עצמי, השלבים שרצים ב-Nodaro Cloud צריכים חיבור ל-Nodaro Cloud.

edit.continueUseCase(input, options?)‎

ממשיכה הרצה של הידוק פרק כמשימה חדשה, ומחזירה { jobId, executionId?, deduped? }, כמו tightenEpisode().

continueUseCase(input: UseCaseContinueInput, options?: { idempotencyKey?: string }): Promise<UseCaseJobResult>

מאפיין

סוג

לאיזה נתיב. עם nodeType, הרצת ההמשך נשלחת ישר לנתיב של הסוג הזה. בלי nodeType, המתודה continueUseCase טוענת קודם את המשימה (GET /v1/jobs/:id) כדי לברר את הסוג שההרצה רשמה, ואז ממשיכה בנתיב של הסוג הזה. הקריאה הזו דורשת את ההרשאה jobs:read באסימון של אפליקציה מחוברת. באסימון בלי ההרשאה, המתודה פונה לנתיב של הידוק פרק, כך שאסימון שיכול היה להמשיך הרצה קודם יכול לעשות זאת גם עכשיו. העבירו nodeType כדי לדלג על הקריאה. ל-TightenEpisodeJobResult יש אותו מבנה כמו ל-UseCaseJobResult.

const preview = await client.edit.tightenEpisode({ mediaUrl: episodeUrl, reviewBeforeRender: true })
// ...after a person reviews the preview:
const final = await client.edit.continueUseCase({ jobId: preview.jobId, mode: "render-final" })

רינדור סופי שהסתיים לא חוסם רינדור סופי נוסף של אותה תצוגה מקדימה עם עריכה ששונתה. השלבים הסופיים רצים אז שוב, ומשלמים עליהם שוב, עדיין בלי העמלה. בקשה עם בדיוק אותה עריכה נדחית. כל דחייה נזרקת כשגיאה עוד לפני שנוצרת הרצה. כשהדחייה מציינת משימה אחרת, המתודה זורקת ContinuationRefusedError, שהיא NodaroError עם סטטוס 409, וה-jobId שלה הוא המשימה ההיא. לשגיאה הזו יש שלושה קודים. הקוד already_running פירושו שהרצת המשך כבר מתבצעת או הסתיימה: בדקו את המצב שלה. הקוד continuation_superseded פירושו שגם הרצת המשך מאוחרת יותר נכשלה: המשיכו במקום זאת את ההרצה ההיא. הקוד render_final_already_rendered פירושו שהתוצאה של המשימה ההיא היא זו שצריך להשתמש בה. דחיות אחרות כוללות את not_found, continuation_not_found, continuation_not_failed ו-continuation_not_completed. הקוד continuation_settling פירושו שצריך לנסות שוב בעוד רגע, והקוד render_final_not_preview פירושו שהמשימה היא לא תצוגה מקדימה לבדיקה. כשמי שמשלם לא יכול לכסות את ההרצה, המתודה זורקת InsufficientCreditsError.

edit.silenceDetect(input)‎

מוצאת את הטווחים השקטים במקור אודיו או וידאו. המתודה רצה בשרת, בלי מודל AI.

silenceDetect(input: SilenceDetectInput): Promise<{ jobId: string }>

מאפיין

סוג

const { jobId } = await client.edit.silenceDetect({ audioUrl: masterUrl, minSilenceMs: 900 })

השדה output_data.json של המשימה שהסתיימה הוא אובייקט SilenceRanges: { version, ranges: [{ startMs, endMs }], durationMs }. העבירו את האובייקט כולו ל-editPlan() בפרמטר silence.

edit.audioSync(input)‎

מודדת כמה רחוקים זה מזה השעונים של 2–6 הקלטות של אותה שיחה, לפי האודיו שלהן, כמו רכיב סנכרון אודיו. המתודה רצה בשרת, בלי מודל AI. היא עולה 11 קרדיטים לכל מקור אחרי הראשון: 11 קרדיטים ל-2 מקורות, 33 ל-4 ו-55 ל-6.

audioSync(input: AudioSyncInput): Promise<{ jobId: string }>

מאפיין

סוג

const { jobId } = await client.edit.audioSync({
  sources: [
    { id: "mic", url: micUrl },
    { id: "camA", url: camAUrl },
  ],
  reference: "mic",
})

השדה output_data.json של המשימה שהסתיימה הוא AudioSyncResult:

{
  version: number
  reference: string // the source every offset is measured against
  offsets: Array<{
    sourceId: string
    offsetMs: number              // referenceMs = sourceMs + offsetMs
    confidence: number            // 0 to 1; below 0.5 a note asks you to check by ear
    driftMsPerHour: number | null // measured, never corrected; null when the overlap was too short
  }>
  notes: string[] // low confidence, drift above 33 ms over the overlap, no shared sound
}

כשההקלטה הראשית היא ה-reference, כל offsetMs הוא בדיוק ה-offsetMs של המקור הזה ב-EDL שלכם. בקשה פגומה נדחית עם NodaroError שה-code שלה הוא validation_error, לפני שקרדיטים משוריינים. זה כולל בקשה עם פחות מ-2 מקורות או יותר מ-6, עם מזהה שחוזר פעמיים או עם reference שהוא לא אחד מהמזהים.

edit.editPlan(input)‎

מתכננת עריכה מתוך תמלול מתוזמן, כמו רכיב תוכנית עריכה. המתודה כותבת אחד מארבעה סוגים של תוכניות: חיתוך מהודק יותר של ההקלטה כולה, סדרה של קליפים קצרים, פרקים או טריילר.

editPlan(input: EditPlanInput): Promise<{ jobId: string }>

מאפיין

סוג

import { unwrapEditPlanOutput } from "@nodaro/sdk"

const { jobId } = await client.edit.editPlan({
  mode: "clips",
  planTier: "standard",
  transcript,
  sources: [{ id: "ep", url: masterUrl, kind: "video", role: "master-audio" }],
  silence,
  count: 5,
  targetAspect: "9:16",
})
const { data } = await client.jobs.getStatus(jobId) // poll until completed
const clips = unwrapEditPlanOutput(data.output_data) // one EDL per clip

קבלו את הפלט של המשימה שהסתיימה עם unwrapEditPlanOutput(). הפונקציה מחזירה Edl במצבים tighten ו-trailer, מערך של Edl במצב clips, ו-ChapterSet במצב chapters.

בהתקנה באירוח עצמי, המתודה הזו צריכה חיבור ל-Nodaro Cloud. בלי חיבור, היא נכשלת עם 503 nodaro_connection_required.

edit.applyEdl(input)‎

מרנדרת רשימת עריכה לקובץ וידאו או אודיו, כמו רכיב החלת רשימת עריכה.

applyEdl(input: ApplyEdlInput): Promise<{ jobId: string }>

מאפיין

סוג

const { jobId } = await client.edit.applyEdl({ edl, output: "video", quality: "proxy", crossfadeMs: 80 })

ב-output_data של המשימה שהסתיימה נמצא החיתוך: השדות videoUrl ו-thumbnailUrl, או audioUrl. כששלחתם תמלול, נמצא שם גם התמלול שהועבר, ב-json. שם נמצאים גם quality ("proxy" או "final"), וגם clipKey ו-planBasis כששלחתם אותם. רינדור proxy הוא תצוגה מקדימה, והוא אף פעם לא מופיע בגלריה הציבורית. ראו את הסעיף תצוגה מקדימה ורינדור סופי בדף החלת רשימת עריכה, ואת הסעיף קרדיטים באותו דף, עם המחיר של כל רמת איכות.

ה-EDL נבדק לפני שקרדיטים משוריינים. כשיש מקור לא מוכר, מקטע בלי תמונה בעריכת וידאו או יותר מ-180 דקות של פלט ברינדור אחד, הבקשה נכשלת עם NodaroError שה-code שלה הוא invalid_edl. האורך נמדד אחרי המעברים הצולבים. השדה message מציין את הבעיה, למשל שהעריכה מרנדרת 200 דקות וצריך לפצל אותה לחלקים של 180 דקות לכל היותר.

edit.remapTranscript(edl, transcript)‎

מעבירה תמלול לציר הזמן של עריכה, מקומית, בלי בקשה לשרת. המתודה משמיטה את המילים שבטווחים שנחתכו, מקצרת מילים שחוצות נקודת חיתוך ומזיזה כל תזמון למקום שלו בפלט הערוך. המתודה applyEdl() עושה את אותו הדבר בשרת כשמעבירים לה transcript.

remapTranscript(edl: Edl, transcript: Transcript): Transcript

מאפיין

סוג

const editedTranscript = client.edit.remapTranscript(edl, transcript)
// caption the edited video without another transcription

השתמשו במתודה הזו כדי להוסיף כתוביות לעריכה בלי רינדור ובלי תמלול נוסף. היא גם הבחירה המהירה יותר לתמלול גדול, כשצריך רק את התזמונים החדשים.

שאלות נפוצות

איך מסירים את ההפסקות מפודקאסט עם ה-SDK של Nodaro?

תמללו את ההקלטה, הריצו את client.edit.silenceDetect, ואז הריצו את client.edit.editPlan במצב tighten עם התמלול ועם טווחי השקט. רנדרו את התוכנית שמתקבלת עם client.edit.applyEdl.

האם אפשר להדק פרק של פודקאסט בקריאה אחת?

כן. המתודה client.edit.tightenEpisode מקבלת כתובת של הקלטה אחת, וידאו או אודיו של עד 3 שעות, ומחזירה משימה. ההרצה מתמללת את ההקלטה, מתכננת את החיתוך ומרנדרת את הפרק המהודק, ויכולה גם להוסיף כתוביות, מוזיקה ומסגור.

מה זה EDL?

EDL, או רשימת עריכה, מציינת את המקורות של עריכה ואת המקטעים שנשארים בה, לפי הסדר, עם המעברים ביניהם. המתודה client.edit.editPlan כותבת רשימה כזו, והמתודה client.edit.applyEdl מרנדרת אותה לקובץ וידאו או אודיו.

מה האורך המרבי של רינדור EDL אחד?

רינדור EDL אחד מפיק לכל היותר 180 דקות של פלט. עריכה ארוכה יותר נדחית עם invalid_edl לפני שקרדיטים משוריינים. פצלו אותה לחלקים.

כמה עולה המתודה client.edit.audioSync?

המתודה client.edit.audioSync עולה 11 קרדיטים לכל מקור אחרי הראשון: 11 קרדיטים ל-2 הקלטות, 33 ל-4 ו-55 ל-6.

עודכן לאחרונה

בדף הזה