var __defProp = Object.defineProperty;
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var _worker, _resolves, _rejects, _logEventCallbacks, _progressEventCallbacks, _registerHandlers, _send;
function toManifestToolEntry(tool) {
  return {
    name: tool.name,
    description: tool.description,
    parameters: tool.parameters
  };
}
function definePlugin(plugin) {
  var _a;
  if (!plugin.manifest || typeof plugin.manifest !== "object") {
    throw new Error("[definePlugin] manifest is required and must be an object");
  }
  if (!plugin.manifest.name) {
    throw new Error("[definePlugin] manifest.name is required");
  }
  if ((_a = plugin.tools) == null ? void 0 : _a.length) {
    plugin.manifest.tools = plugin.tools.map(toManifestToolEntry);
  }
  const hasInstall = typeof plugin.install === "function";
  const hasTools = plugin.tools && plugin.tools.length > 0;
  const hasManifestTools = plugin.manifest.tools && plugin.manifest.tools.length > 0;
  if (!hasInstall && !hasTools && !hasManifestTools) {
    throw new Error("[definePlugin] must provide tools, install(), or manifest.tools");
  }
  return plugin;
}
function defineTool(tool) {
  if (!tool.name) {
    throw new Error("[defineTool] name is required");
  }
  if (typeof tool.execute !== "function") {
    throw new Error("[defineTool] execute must be a function");
  }
  return tool;
}
var FFMessageType;
(function(FFMessageType2) {
  FFMessageType2["LOAD"] = "LOAD";
  FFMessageType2["EXEC"] = "EXEC";
  FFMessageType2["FFPROBE"] = "FFPROBE";
  FFMessageType2["WRITE_FILE"] = "WRITE_FILE";
  FFMessageType2["READ_FILE"] = "READ_FILE";
  FFMessageType2["DELETE_FILE"] = "DELETE_FILE";
  FFMessageType2["RENAME"] = "RENAME";
  FFMessageType2["CREATE_DIR"] = "CREATE_DIR";
  FFMessageType2["LIST_DIR"] = "LIST_DIR";
  FFMessageType2["DELETE_DIR"] = "DELETE_DIR";
  FFMessageType2["ERROR"] = "ERROR";
  FFMessageType2["DOWNLOAD"] = "DOWNLOAD";
  FFMessageType2["PROGRESS"] = "PROGRESS";
  FFMessageType2["LOG"] = "LOG";
  FFMessageType2["MOUNT"] = "MOUNT";
  FFMessageType2["UNMOUNT"] = "UNMOUNT";
})(FFMessageType || (FFMessageType = {}));
const getMessageID = /* @__PURE__ */ (() => {
  let messageID = 0;
  return () => messageID++;
})();
const ERROR_NOT_LOADED = new Error("ffmpeg is not loaded, call `await ffmpeg.load()` first");
const ERROR_TERMINATED = new Error("called FFmpeg.terminate()");
class FFmpeg {
  constructor() {
    __privateAdd(this, _worker, null);
    /**
     * #resolves and #rejects tracks Promise resolves and rejects to
     * be called when we receive message from web worker.
     */
    __privateAdd(this, _resolves, {});
    __privateAdd(this, _rejects, {});
    __privateAdd(this, _logEventCallbacks, []);
    __privateAdd(this, _progressEventCallbacks, []);
    __publicField(this, "loaded", false);
    /**
     * register worker message event handlers.
     */
    __privateAdd(this, _registerHandlers, () => {
      if (__privateGet(this, _worker)) {
        __privateGet(this, _worker).onmessage = ({ data: { id, type, data } }) => {
          switch (type) {
            case FFMessageType.LOAD:
              this.loaded = true;
              __privateGet(this, _resolves)[id](data);
              break;
            case FFMessageType.MOUNT:
            case FFMessageType.UNMOUNT:
            case FFMessageType.EXEC:
            case FFMessageType.FFPROBE:
            case FFMessageType.WRITE_FILE:
            case FFMessageType.READ_FILE:
            case FFMessageType.DELETE_FILE:
            case FFMessageType.RENAME:
            case FFMessageType.CREATE_DIR:
            case FFMessageType.LIST_DIR:
            case FFMessageType.DELETE_DIR:
              __privateGet(this, _resolves)[id](data);
              break;
            case FFMessageType.LOG:
              __privateGet(this, _logEventCallbacks).forEach((f) => f(data));
              break;
            case FFMessageType.PROGRESS:
              __privateGet(this, _progressEventCallbacks).forEach((f) => f(data));
              break;
            case FFMessageType.ERROR:
              __privateGet(this, _rejects)[id](data);
              break;
          }
          delete __privateGet(this, _resolves)[id];
          delete __privateGet(this, _rejects)[id];
        };
      }
    });
    /**
     * Generic function to send messages to web worker.
     */
    __privateAdd(this, _send, ({ type, data }, trans = [], signal) => {
      if (!__privateGet(this, _worker)) {
        return Promise.reject(ERROR_NOT_LOADED);
      }
      return new Promise((resolve, reject) => {
        const id = getMessageID();
        __privateGet(this, _worker) && __privateGet(this, _worker).postMessage({ id, type, data }, trans);
        __privateGet(this, _resolves)[id] = resolve;
        __privateGet(this, _rejects)[id] = reject;
        signal == null ? void 0 : signal.addEventListener("abort", () => {
          reject(new DOMException(`Message # ${id} was aborted`, "AbortError"));
        }, { once: true });
      });
    });
    /**
     * Loads ffmpeg-core inside web worker. It is required to call this method first
     * as it initializes WebAssembly and other essential variables.
     *
     * @category FFmpeg
     * @returns `true` if ffmpeg core is loaded for the first time.
     */
    __publicField(this, "load", ({ classWorkerURL, ...config } = {}, { signal } = {}) => {
      if (!__privateGet(this, _worker)) {
        __privateSet(this, _worker, classWorkerURL ? new Worker(new URL(classWorkerURL, import.meta.url), {
          type: "module"
        }) : (
          // We need to duplicated the code here to enable webpack
          // to bundle worekr.js here.
          new Worker(new URL(
            /* @vite-ignore */
            "/assets/worker-D3WqxKMJ.js",
            import.meta.url
          ), {
            type: "module"
          })
        ));
        __privateGet(this, _registerHandlers).call(this);
      }
      return __privateGet(this, _send).call(this, {
        type: FFMessageType.LOAD,
        data: config
      }, void 0, signal);
    });
    /**
     * Execute ffmpeg command.
     *
     * @remarks
     * To avoid common I/O issues, ["-nostdin", "-y"] are prepended to the args
     * by default.
     *
     * @example
     * ```ts
     * const ffmpeg = new FFmpeg();
     * await ffmpeg.load();
     * await ffmpeg.writeFile("video.avi", ...);
     * // ffmpeg -i video.avi video.mp4
     * await ffmpeg.exec(["-i", "video.avi", "video.mp4"]);
     * const data = ffmpeg.readFile("video.mp4");
     * ```
     *
     * @returns `0` if no error, `!= 0` if timeout (1) or error.
     * @category FFmpeg
     */
    __publicField(this, "exec", (args, timeout = -1, { signal } = {}) => __privateGet(this, _send).call(this, {
      type: FFMessageType.EXEC,
      data: { args, timeout }
    }, void 0, signal));
    /**
     * Execute ffprobe command.
     *
     * @example
     * ```ts
     * const ffmpeg = new FFmpeg();
     * await ffmpeg.load();
     * await ffmpeg.writeFile("video.avi", ...);
     * // Getting duration of a video in seconds: ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 video.avi -o output.txt
     * await ffmpeg.ffprobe(["-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", "video.avi", "-o", "output.txt"]);
     * const data = ffmpeg.readFile("output.txt");
     * ```
     *
     * @returns `0` if no error, `!= 0` if timeout (1) or error.
     * @category FFmpeg
     */
    __publicField(this, "ffprobe", (args, timeout = -1, { signal } = {}) => __privateGet(this, _send).call(this, {
      type: FFMessageType.FFPROBE,
      data: { args, timeout }
    }, void 0, signal));
    /**
     * Terminate all ongoing API calls and terminate web worker.
     * `FFmpeg.load()` must be called again before calling any other APIs.
     *
     * @category FFmpeg
     */
    __publicField(this, "terminate", () => {
      const ids = Object.keys(__privateGet(this, _rejects));
      for (const id of ids) {
        __privateGet(this, _rejects)[id](ERROR_TERMINATED);
        delete __privateGet(this, _rejects)[id];
        delete __privateGet(this, _resolves)[id];
      }
      if (__privateGet(this, _worker)) {
        __privateGet(this, _worker).terminate();
        __privateSet(this, _worker, null);
        this.loaded = false;
      }
    });
    /**
     * Write data to ffmpeg.wasm.
     *
     * @example
     * ```ts
     * const ffmpeg = new FFmpeg();
     * await ffmpeg.load();
     * await ffmpeg.writeFile("video.avi", await fetchFile("../video.avi"));
     * await ffmpeg.writeFile("text.txt", "hello world");
     * ```
     *
     * @category File System
     */
    __publicField(this, "writeFile", (path, data, { signal } = {}) => {
      const trans = [];
      if (data instanceof Uint8Array) {
        trans.push(data.buffer);
      }
      return __privateGet(this, _send).call(this, {
        type: FFMessageType.WRITE_FILE,
        data: { path, data }
      }, trans, signal);
    });
    __publicField(this, "mount", (fsType, options, mountPoint) => {
      const trans = [];
      return __privateGet(this, _send).call(this, {
        type: FFMessageType.MOUNT,
        data: { fsType, options, mountPoint }
      }, trans);
    });
    __publicField(this, "unmount", (mountPoint) => {
      const trans = [];
      return __privateGet(this, _send).call(this, {
        type: FFMessageType.UNMOUNT,
        data: { mountPoint }
      }, trans);
    });
    /**
     * Read data from ffmpeg.wasm.
     *
     * @example
     * ```ts
     * const ffmpeg = new FFmpeg();
     * await ffmpeg.load();
     * const data = await ffmpeg.readFile("video.mp4");
     * ```
     *
     * @category File System
     */
    __publicField(this, "readFile", (path, encoding = "binary", { signal } = {}) => __privateGet(this, _send).call(this, {
      type: FFMessageType.READ_FILE,
      data: { path, encoding }
    }, void 0, signal));
    /**
     * Delete a file.
     *
     * @category File System
     */
    __publicField(this, "deleteFile", (path, { signal } = {}) => __privateGet(this, _send).call(this, {
      type: FFMessageType.DELETE_FILE,
      data: { path }
    }, void 0, signal));
    /**
     * Rename a file or directory.
     *
     * @category File System
     */
    __publicField(this, "rename", (oldPath, newPath, { signal } = {}) => __privateGet(this, _send).call(this, {
      type: FFMessageType.RENAME,
      data: { oldPath, newPath }
    }, void 0, signal));
    /**
     * Create a directory.
     *
     * @category File System
     */
    __publicField(this, "createDir", (path, { signal } = {}) => __privateGet(this, _send).call(this, {
      type: FFMessageType.CREATE_DIR,
      data: { path }
    }, void 0, signal));
    /**
     * List directory contents.
     *
     * @category File System
     */
    __publicField(this, "listDir", (path, { signal } = {}) => __privateGet(this, _send).call(this, {
      type: FFMessageType.LIST_DIR,
      data: { path }
    }, void 0, signal));
    /**
     * Delete an empty directory.
     *
     * @category File System
     */
    __publicField(this, "deleteDir", (path, { signal } = {}) => __privateGet(this, _send).call(this, {
      type: FFMessageType.DELETE_DIR,
      data: { path }
    }, void 0, signal));
  }
  on(event, callback) {
    if (event === "log") {
      __privateGet(this, _logEventCallbacks).push(callback);
    } else if (event === "progress") {
      __privateGet(this, _progressEventCallbacks).push(callback);
    }
  }
  off(event, callback) {
    if (event === "log") {
      __privateSet(this, _logEventCallbacks, __privateGet(this, _logEventCallbacks).filter((f) => f !== callback));
    } else if (event === "progress") {
      __privateSet(this, _progressEventCallbacks, __privateGet(this, _progressEventCallbacks).filter((f) => f !== callback));
    }
  }
}
_worker = new WeakMap();
_resolves = new WeakMap();
_rejects = new WeakMap();
_logEventCallbacks = new WeakMap();
_progressEventCallbacks = new WeakMap();
_registerHandlers = new WeakMap();
_send = new WeakMap();
var FFFSType;
(function(FFFSType2) {
  FFFSType2["MEMFS"] = "MEMFS";
  FFFSType2["NODEFS"] = "NODEFS";
  FFFSType2["NODERAWFS"] = "NODERAWFS";
  FFFSType2["IDBFS"] = "IDBFS";
  FFFSType2["WORKERFS"] = "WORKERFS";
  FFFSType2["PROXYFS"] = "PROXYFS";
})(FFFSType || (FFFSType = {}));
const EXEC_TIMEOUT_MS = 10 * 60 * 1e3;
let ffmpeg = null;
let loadingPromise = null;
let workerBlobUrl = null;
const coreBlobUrls = [];
async function ensureWorkerBlobUrl(api) {
  if (workerBlobUrl) return workerBlobUrl;
  const blob = await api.readPluginAsset("assets/worker.js");
  workerBlobUrl = URL.createObjectURL(new Blob([await blob.text()], { type: "text/javascript" }));
  return workerBlobUrl;
}
async function loadCoreFromPluginAssets(api) {
  const coreJsBlob = await api.readPluginAsset("assets/ffmpeg-core.js");
  const coreWasmBlob = await api.readPluginAsset("assets/ffmpeg-core.wasm");
  const coreURL = URL.createObjectURL(coreJsBlob);
  const wasmURL = URL.createObjectURL(coreWasmBlob);
  coreBlobUrls.push(coreURL, wasmURL);
  return { coreURL, wasmURL };
}
async function getFFmpegInstance(reportProgress, api) {
  if (ffmpeg == null ? void 0 : ffmpeg.loaded) return ffmpeg;
  if (loadingPromise) return loadingPromise;
  loadingPromise = (async () => {
    const instance = new FFmpeg();
    instance.on("progress", ({ progress }) => {
      const pct = Math.round(progress * 100);
      reportProgress({ phase: "executing", message: `处理中... ${pct}%`, progress });
    });
    reportProgress({ phase: "loading", message: "正在加载 FFmpeg 核心文件..." });
    const [coreAssets, classWorkerURL] = await Promise.all([
      loadCoreFromPluginAssets(api),
      ensureWorkerBlobUrl(api)
    ]);
    await instance.load({ classWorkerURL, ...coreAssets });
    ffmpeg = instance;
    loadingPromise = null;
    return instance;
  })();
  try {
    return await loadingPromise;
  } catch (err) {
    loadingPromise = null;
    ffmpeg = null;
    throw err;
  }
}
function extractFileName(path) {
  const segments = path.split("/");
  return segments[segments.length - 1] || "input";
}
const ffmpegExecTool = defineTool({
  name: "ffmpeg_exec",
  description: "使用 ffmpeg 处理音视频文件（转换格式、提取音频、截取片段等）",
  parameters: {
    type: "object",
    properties: {
      input: {
        type: "string",
        description: "输入文件路径（workspace 相对路径）"
      },
      output: {
        type: "string",
        description: "输出文件路径（workspace 相对路径）"
      },
      args: {
        type: "array",
        items: { type: "string" },
        description: 'ffmpeg 命令参数（不含 -i 和输出路径，由插件自动添加）。例如 ["-ss","00:00:10","-t","30"] 表示从第10秒截取30秒'
      }
    },
    required: ["input", "output"]
  },
  execute: async (args, api) => {
    const inputPath = String(args.input || "").trim();
    const outputPath = String(args.output || "").trim();
    if (!inputPath || !outputPath) {
      return { success: false, error: "必须提供 input 和 output 文件路径" };
    }
    const extraArgs = Array.isArray(args.args) ? args.args.map(String) : [];
    const logBuffer = [];
    try {
      const instance = await getFFmpegInstance(api.reportProgress.bind(api), api);
      const logHandler = ({ message }) => {
        logBuffer.push(message);
      };
      instance.on("log", logHandler);
      try {
        api.reportProgress({ phase: "reading", message: `正在读取输入文件: ${inputPath}` });
        const inputBlob = await api.workspace.readFileBlob(inputPath);
        const inputBuffer = new Uint8Array(await inputBlob.arrayBuffer());
        const inputName = extractFileName(inputPath);
        const outputName = extractFileName(outputPath);
        await instance.writeFile(inputName, inputBuffer);
        const cmd = ["-i", inputName, ...extraArgs, outputName];
        api.reportProgress({ phase: "executing", message: `正在执行: ffmpeg ${cmd.join(" ")}` });
        const exitCode = await instance.exec(cmd, EXEC_TIMEOUT_MS);
        if (exitCode !== 0) {
          const recentLogs = logBuffer.slice(-20).join("\n");
          return {
            success: false,
            error: `ffmpeg 执行失败，退出码: ${exitCode}`,
            logs: recentLogs
          };
        }
        const outputData = await instance.readFile(outputName);
        const outputBytes = typeof outputData === "string" ? new TextEncoder().encode(outputData) : outputData;
        api.reportProgress({ phase: "writing", message: `正在写入输出文件: ${outputPath}` });
        await api.workspace.writeFile(outputPath, outputBytes);
        try {
          await instance.deleteFile(inputName);
        } catch {
        }
        try {
          await instance.deleteFile(outputName);
        } catch {
        }
        return {
          success: true,
          output: outputPath,
          size: outputBytes.length
        };
      } finally {
        instance.off("log", logHandler);
      }
    } catch (err) {
      const errMsg = err instanceof Error ? err.message : String(err);
      return {
        success: false,
        error: `处理失败: ${errMsg}`,
        logs: logBuffer.length > 0 ? logBuffer.slice(-20).join("\n") : void 0
      };
    }
  }
});
const index = definePlugin({
  manifest: {
    name: "com.example.ffmpeg",
    version: "1.0.0",
    displayName: "FFmpeg 音视频处理",
    description: "使用 ffmpeg.wasm 处理音视频文件：格式转换、提取音频、截取片段、调整参数等（离线可用，无需网络）",
    author: "Playground Demo",
    capabilities: ["workspace.user"],
    sdkVersion: "^1.0.0"
  },
  tools: [ffmpegExecTool],
  destroy() {
    if (ffmpeg) {
      try {
        ffmpeg.terminate();
      } catch {
      }
      ffmpeg = null;
    }
    loadingPromise = null;
    if (workerBlobUrl) {
      URL.revokeObjectURL(workerBlobUrl);
      workerBlobUrl = null;
    }
    for (const url of coreBlobUrls) {
      URL.revokeObjectURL(url);
    }
    coreBlobUrls.length = 0;
  }
});
export {
  index as default
};
