@pictor-sdk/video-android 0.1.6 → 0.2.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/dist/index.mjs CHANGED
@@ -1712,11 +1712,26 @@ var VideoExportRejected = class extends Error {
1712
1712
  }
1713
1713
  };
1714
1714
  var DEFAULT_FPS = 30;
1715
- function describeUnsupported(project, spec) {
1715
+ function animatedSchedulesFor(project) {
1716
+ const schedules = {};
1717
+ for (const overlay of project.overlays) {
1718
+ if (overlay.type !== "sticker") continue;
1719
+ const data = overlay.data;
1720
+ if (data && data.animation) schedules[overlay.id] = data.animation;
1721
+ }
1722
+ return schedules;
1723
+ }
1724
+ function describeUnsupported(project, spec, options) {
1716
1725
  const reasons = [];
1717
1726
  for (const clip of project.clips) {
1718
1727
  if (clip.kind === "photo") {
1719
- reasons.push(`clip '${clip.id}' is a photo, which this exporter does not encode yet`);
1728
+ if (!options.renderPhotoBase) {
1729
+ reasons.push(`clip '${clip.id}' is a photo, and no photo renderer was supplied`);
1730
+ } else if (project.clips.length !== 1) {
1731
+ reasons.push(
1732
+ `clip '${clip.id}' is a photo in a project of ${project.clips.length} clips; only a single-photo story is supported`
1733
+ );
1734
+ }
1720
1735
  continue;
1721
1736
  }
1722
1737
  }
@@ -1748,11 +1763,107 @@ function createMedia3VideoExporter(options) {
1748
1763
  if (issues.length > 0) {
1749
1764
  throw new VideoExportRejected(issues.map((issue) => issue.message));
1750
1765
  }
1751
- const unsupported = describeUnsupported(project, spec);
1766
+ const unsupported = describeUnsupported(project, spec, options);
1752
1767
  if (unsupported.length > 0) throw new VideoExportRejected(unsupported);
1753
1768
  const decision = resolveDestination(spec.destination);
1754
1769
  if (decision.destination === null) throw new VideoExportRejected(decision.reasons);
1755
1770
  const destination = decision.destination;
1771
+ const photoOnly = project.clips.length === 1 && project.clips[0].kind === "photo";
1772
+ if (photoOnly) {
1773
+ const clip = project.clips[0];
1774
+ if (!options.renderPhotoBase) {
1775
+ throw new VideoExportRejected([`clip '${clip.id}' is a photo, and no photo renderer was supplied`]);
1776
+ }
1777
+ const pngPath = await options.renderPhotoBase(clip.source, clip.id);
1778
+ job.checkpoint("resolving");
1779
+ const photoProgram = compileVideoSceneProgram(project, {
1780
+ animatedSchedules: animatedSchedulesFor(project)
1781
+ });
1782
+ if (photoProgram.segments.length === 0) {
1783
+ throw new VideoExportRejected(["the compiled program has no segments"]);
1784
+ }
1785
+ const named2 = /* @__PURE__ */ new Set();
1786
+ for (const segment of photoProgram.segments) {
1787
+ for (const command of segment.commands) {
1788
+ if (command.op === "overlay") named2.add(command.overlayId);
1789
+ }
1790
+ }
1791
+ const overlayPlan2 = options.prepareOverlays ? options.prepareOverlays(photoProgram, project.size, 1) : {
1792
+ resources: [],
1793
+ pending: [],
1794
+ invalid: [...named2].map((overlayId) => ({
1795
+ overlayId,
1796
+ kind: "unknown",
1797
+ reason: "no prepareOverlays was supplied, so this overlay has no portable form"
1798
+ }))
1799
+ };
1800
+ const stagedNow2 = overlayPlan2.resources.map((resource) => resource.file);
1801
+ if (overlayPlan2.pending.length > 0) {
1802
+ options.releaseResources?.(stagedNow2);
1803
+ throw new VideoExportRejected(
1804
+ overlayPlan2.pending.map(
1805
+ (id) => `overlay '${id}' is still preparing; export would omit it`
1806
+ )
1807
+ );
1808
+ }
1809
+ if (overlayPlan2.invalid.length > 0) {
1810
+ options.releaseResources?.(stagedNow2);
1811
+ throw new VideoExportRejected(
1812
+ overlayPlan2.invalid.map(
1813
+ (bad) => `overlay '${bad.overlayId}' (${bad.kind}): ${bad.reason}`
1814
+ )
1815
+ );
1816
+ }
1817
+ job.checkpoint("overlays");
1818
+ let outcome2;
1819
+ try {
1820
+ outcome2 = JSON.parse(
1821
+ await nativeModule().exportRunPhotoProjectAsync(
1822
+ job.id,
1823
+ pngPath,
1824
+ JSON.stringify({
1825
+ version: VIDEO_SCENE_PROGRAM_VERSION,
1826
+ projectSize: { width: project.size.width, height: project.size.height },
1827
+ durationUs: photoProgram.durationUs,
1828
+ segments: photoProgram.segments
1829
+ }),
1830
+ JSON.stringify(overlayPlan2.resources),
1831
+ projectDuration3(project),
1832
+ 180,
1833
+ destination.kind === "file" ? JSON.stringify({
1834
+ kind: "file",
1835
+ path: destination.path,
1836
+ overwrite: destination.overwrite
1837
+ }) : ""
1838
+ )
1839
+ );
1840
+ } finally {
1841
+ options.releaseResources?.(stagedNow2);
1842
+ }
1843
+ if (!outcome2.ok) {
1844
+ const error = outcome2.error || "the export failed";
1845
+ if (isRefusal(error)) throw new VideoExportRejected([error]);
1846
+ throw new VideoExportFailed(job.id, "encoding", error);
1847
+ }
1848
+ const probe2 = JSON.parse(nativeModule().exportProbeOutput(outcome2.uri));
1849
+ if (!probe2.readable) {
1850
+ throw new VideoExportFailed(
1851
+ job.id,
1852
+ "publishing",
1853
+ `${describeTarget(outcome2.uri)} could not be read back: ${probe2.error ?? "no video track"}`
1854
+ );
1855
+ }
1856
+ if (destination.kind === "file") return { kind: "file", uri: outcome2.uri };
1857
+ const temporaryUri2 = outcome2.uri;
1858
+ return {
1859
+ kind: "temporary",
1860
+ uri: temporaryUri2,
1861
+ release: async () => {
1862
+ await Promise.resolve();
1863
+ nativeModule().exportReleaseTemporaryOutput(temporaryUri2);
1864
+ }
1865
+ };
1866
+ }
1756
1867
  const playable = project.clips.filter((clip) => isPlayableWindow(clip));
1757
1868
  if (playable.length === 0) throw new VideoExportRejected(["the project has no playable clips"]);
1758
1869
  if (!(projectDuration3(project) > 0)) {
@@ -1779,7 +1890,9 @@ function createMedia3VideoExporter(options) {
1779
1890
  targetFps: fps
1780
1891
  }));
1781
1892
  const plan = planClipShards(requests, fps);
1782
- const program = compileVideoSceneProgram(project);
1893
+ const program = compileVideoSceneProgram(project, {
1894
+ animatedSchedules: animatedSchedulesFor(project)
1895
+ });
1783
1896
  if (program.segments.length === 0) {
1784
1897
  throw new VideoExportRejected(["the compiled program has no segments"]);
1785
1898
  }