Token导航 LogoToken导航TokenDH.com
前端设计操作浏览器github未标认证来源可访问许可证需确认审计异常

filmkit-fujifilm-camera富士胶片相机

Agent Skill

filmkit-fujifilm-camera 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

15,336

周安装

639

GitHub Stars

39

下载量

5,112
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:filmkit-fujifilm-camera(富士胶片相机)
来源仓库:https://github.com/aradotso/trending-skills
仓库路径:skills/filmkit-fujifilm-camera
安装命令:
npx skills add https://github.com/aradotso/trending-skills --skill filmkit-fujifilm-camera
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/aradotso/trending-skills --skill filmkit-fujifilm-camera

简介

filmkit-fujifilm-camera 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息。

  • 适合在前端设计环境中围绕仓库状态或代码变更进行整理。
  • 通过 npx skills add 命令从指定仓库安装,需确认权限和维护状态。
  • 使用前应核实是否会触发联网、命令执行或文件读写操作。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

FilmKit Fujifilm Camera Skill

Skill by ara.so — Daily 2026 Skills collection.

FilmKit is a browser-based, zero-install preset manager and RAW converter for Fujifilm X-series cameras. It uses WebUSB to communicate via PTP (Picture Transfer Protocol) — the same protocol as Fujifilm X RAW STUDIO — so the camera's own image processor handles RAW-to-JPEG conversion. It runs entirely client-side (hosted on GitHub Pages) and supports desktop and Android.


What FilmKit Does

  • Preset Management: Read, edit, and write custom film simulation presets directly on-camera (slots D18E–D1A5 via PTP GetDevicePropValue / SetDevicePropValue)
  • Local Preset Library: Save presets locally, drag-and-drop between camera and local storage
  • RAW Conversion & Live Preview: Send RAF files to the camera, receive full-quality JPEGs back
  • Preset Detection: Loading a RAF file auto-detects which preset was used to shoot it
  • Import/Export: Presets as files, links, or text paste
  • Mobile Support: Works on Android via Chrome's WebUSB support

Requirements

  • Chromium-based browser (Google Chrome, Edge, Brave) on desktop or Android — WebUSB is required
  • Fujifilm X-series camera connected via USB (tested on X100VI; likely works on X-T5, X-H2, X-T30, etc.)
  • Linux udev rule (if running Chrome in Flatpak):
# /etc/udev/rules.d/99-fujifilm.rules
SUBSYSTEM=="usb", ATTR{idVendor}=="04cb", MODE="0666"

Reload rules after adding:

sudo udevadm control --reload-rules && sudo udevadm trigger

Installation / Setup (Development)

FilmKit is a static TypeScript app. To run locally:

git clone https://github.com/eggricesoy/filmkit.git
cd filmkit
npm install
npm run dev

Build for production:

npm run build

The built output is a static site — no server required. Open in Chrome at http://localhost:5173 (or wherever Vite serves it).


Architecture Overview

PTP over WebUSB

FilmKit speaks PTP (Picture Transfer Protocol) directly over USB bulk transfers. Key operations:

PTP OperationPurpose
GetDevicePropValueRead a camera preset property
SetDevicePropValueWrite a camera preset property
InitiateOpenCaptureStart RAW conversion session
SendObjectSend RAF file to camera
GetObjectRetrieve converted JPEG from camera

Preset Property Codes

Fujifilm X-series cameras expose film simulation parameters as device properties in the range 0xD18E0xD1A5:

// Example property codes (from QUICK_REFERENCE.md)
const PROP_FILM_SIMULATION = 0xD18E;
const PROP_GRAIN_EFFECT     = 0xD18F;
const PROP_COLOR_CHROME     = 0xD190;
const PROP_WHITE_BALANCE    = 0xD191;
const PROP_COLOR_TEMP       = 0xD192;
const PROP_DYNAMIC_RANGE    = 0xD193;
const PROP_HIGHLIGHT_TONE   = 0xD194;
const PROP_SHADOW_TONE      = 0xD195;
const PROP_COLOR            = 0xD196;
const PROP_SHARPNESS        = 0xD197;
const PROP_HIGH_ISO_NR      = 0xD198; // Non-linear encoding!
const PROP_CLARITY          = 0xD199;

Native Profile Format

The camera's native d185 profile is 625 bytes and uses different field indices/encoding from RAF file metadata. FilmKit uses a patch-based approach:

// Conceptual patch approach
function applyPresetPatch(baseProfile: Uint8Array, changes: PresetChanges): Uint8Array {
  // Copy base profile byte-for-byte
  const patched = new Uint8Array(baseProfile);

  // Only overwrite fields the user changed
  // This preserves EXIF sentinel values in unchanged fields
  for (const [fieldIndex, encodedValue] of Object.entries(changes)) {
    writeFieldToProfile(patched, parseInt(fieldIndex), encodedValue);
  }

  return patched;
}

Key Code Patterns

WebUSB Connection

// Request access to the Fujifilm camera
async function connectCamera(): Promise<USBDevice> {
  const device = await navigator.usb.requestDevice({
    filters: [{ vendorId: 0x04CB }] // Fujifilm vendor ID
  });

  await device.open();
  await device.selectConfiguration(1);
  await device.claimInterface(0);

  return device;
}

Sending a PTP Command

// PTP command packet structure
function buildPTPCommand(
  operationCode: number,
  transactionId: number,
  params: number[] = []
): ArrayBuffer {
  const paramCount = params.length;
  const length = 12 + paramCount * 4;
  const buffer = new ArrayBuffer(length);
  const view = new DataView(buffer);

  view.setUint32(0, length, true);        // Length
  view.setUint16(4, 0x0001, true);        // Type: Command
  view.setUint16(6, operationCode, true); // Operation code
  view.setUint32(8, transactionId, true); // Transaction ID

  params.forEach((p, i) => {
    view.setUint32(12 + i * 4, p, true);
  });

  return buffer;
}

// Send a PTP operation and read response
async function ptpTransaction(
  device: USBDevice,
  operationCode: number,
  transactionId: number,
  params: number[] = [],
  outData?: ArrayBuffer
): Promise<{ responseCode: number; data?: ArrayBuffer }> {
  const endpointOut = 0x02; // Bulk OUT
  const endpointIn  = 0x81; // Bulk IN

  // Send command
  const cmd = buildPTPCommand(operationCode, transactionId, params);
  await device.transferOut(endpointOut, cmd);

  // Send data phase if present
  if (outData) {
    await device.transferOut(endpointOut, outData);
  }

  // Read data response (if expected)
  const dataResult = await device.transferIn(endpointIn, 512);

  // Read response packet
  const respResult = await device.transferIn(endpointIn, 32);
  const respView = new DataView(respResult.data!.buffer);
  const responseCode = respView.getUint16(6, true);

  return { responseCode, data: dataResult.data?.buffer };
}

Reading a Preset Property

async function getDevicePropValue(
  device: USBDevice,
  propCode: number,
  txId: number
): Promise<DataView> {
  const PTP_OP_GET_DEVICE_PROP_VALUE = 0x1015;

  const { data } = await ptpTransaction(
    device,
    PTP_OP_GET_DEVICE_PROP_VALUE,
    txId,
    [propCode]
  );

  if (!data) throw new Error(`No data for prop 0x${propCode.toString(16)}`);

  // PTP data container: 12-byte header, then payload
  return new DataView(data, 12);
}

// Example: read film simulation
const filmSimView = await getDevicePropValue(device, 0xD18E, txId++);
const filmSimValue = filmSimView.getUint16(0, true);
console.log('Film simulation code:', filmSimValue);

Writing a Preset Property

async function setDevicePropValue(
  device: USBDevice,
  propCode: number,
  value: number,
  byteSize: 1 | 2 | 4,
  txId: number
): Promise<void> {
  const PTP_OP_SET_DEVICE_PROP_VALUE = 0x1016;

  // Build data container
  const dataLength = 12 + byteSize;
  const dataBuffer = new ArrayBuffer(dataLength);
  const view = new DataView(dataBuffer);

  view.setUint32(0, dataLength, true); // Length
  view.setUint16(4, 0x0002, true);     // Type: Data
  view.setUint16(6, PTP_OP_SET_DEVICE_PROP_VALUE, true);
  view.setUint32(8, txId, true);

  if (byteSize === 1) view.setUint8(12, value);
  else if (byteSize === 2) view.setUint16(12, value, true);
  else if (byteSize === 4) view.setUint32(12, value, true);

  await ptpTransaction(
    device,
    PTP_OP_SET_DEVICE_PROP_VALUE,
    txId,
    [propCode],
    dataBuffer
  );
}

// Example: set White Balance to Color Temperature mode
await setDevicePropValue(device, 0xD191, 0x0012, 2, txId++);
// Now safe to set Color Temperature value
await setDevicePropValue(device, 0xD192, 4500, 2, txId++);

HighIsoNR Special Encoding

HighIsoNR uses a non-linear proprietary encoding — do not write raw values directly:

// HighIsoNR encoding map (reverse-engineered via Wireshark)
const HIGH_ISO_NR_ENCODE: Record<number, number> = {
  [-4]: 0x00,
  [-3]: 0x01,
  [-2]: 0x02,
  [-1]: 0x03,
  [0]:  0x04,
  [1]:  0x08,
  [2]:  0x0C,
  [3]:  0x10,
  [4]:  0x14,
};

function encodeHighIsoNR(userValue: number): number {
  const encoded = HIGH_ISO_NR_ENCODE[userValue];
  if (encoded === undefined) throw new Error(`Invalid HighIsoNR value: ${userValue}`);
  return encoded;
}

// Usage
await setDevicePropValue(device, 0xD198, encodeHighIsoNR(2), 1, txId++);

Conditional Writes (Monochrome Film Simulations)

Monochrome film simulations reject Color property writes — guard against this:

const MONOCHROME_SIMULATIONS = new Set([
  0x0009, // ACROS
  0x000A, // ACROS+Ye
  0x000B, // ACROS+R
  0x000C, // ACROS+G
  0x0012, // Monochrome
  0x0013, // Monochrome+Ye
  0x0014, // Monochrome+R
  0x0015, // Monochrome+G
  0x001A, // Eterna Cinema BW
]);

async function writePreset(device: USBDevice, preset: Preset, txId: number): Promise<number> {
  const isMonochrome = MONOCHROME_SIMULATIONS.has(preset.filmSimulation);

  await setDevicePropValue(device, 0xD18E, preset.filmSimulation, 2, txId++);

  if (!isMonochrome) {
    await setDevicePropValue(device, 0xD196, preset.color, 2, txId++);
  }

  await setDevicePropValue(device, 0xD198, encodeHighIsoNR(preset.highIsoNR), 1, txId++);
  // ... write other properties

  return txId;
}

RAW Conversion Flow

async function convertRAW(
  device: USBDevice,
  rafData: ArrayBuffer,
  preset: Preset,
  txId: number
): Promise<ArrayBuffer> {
  // 1. Write preset properties to camera
  txId = await writePreset(device, preset, txId);

  // 2. Initiate open capture / conversion session
  await ptpTransaction(device, 0x101C, txId++); // InitiateOpenCapture

  // 3. Send the RAF file
  const sendObjectOp = 0x100D;
  await ptpTransaction(device, sendObjectOp, txId++, [], rafData);

  // 4. Poll for completion and get JPEG back
  const getObjectOp = 0x1009;
  const { data: jpegData } = await ptpTransaction(device, getObjectOp, txId++);

  if (!jpegData) throw new Error('No JPEG returned from camera');
  return jpegData;
}

Preset Import/Export Format

Presets are exported as structured data (JSON or encoded strings). When importing:

interface FilmKitPreset {
  name: string;
  filmSimulation: number;
  grainEffect: number;
  colorChrome: number;
  whiteBalance: number;
  colorTemperature?: number; // Only used when WB = Color Temp mode (0x0012)
  dynamicRange: number;
  highlightTone: number;
  shadowTone: number;
  color: number;
  sharpness: number;
  highIsoNR: number;       // User-facing value (-4 to +4), encode before writing
  clarity: number;
}

// Export preset as shareable link
function exportPresetAsLink(preset: FilmKitPreset): string {
  const encoded = btoa(JSON.stringify(preset));
  return `https://filmkit.eggrice.soy/?preset=${encoded}`;
}

// Import preset from link/text
function importPreset(input: string): FilmKitPreset {
  // Handle URL with ?preset= param
  try {
    const url = new URL(input);
    const param = url.searchParams.get('preset');
    if (param) return JSON.parse(atob(param));
  } catch {}

  // Handle raw base64 or JSON
  try { return JSON.parse(atob(input)); } catch {}
  try { return JSON.parse(input); } catch {}

  throw new Error('Invalid preset format');
}

Capturing USB Traffic for New Camera Support

To help add support for a new Fujifilm X-series camera:

  1. Install Wireshark with USBPcap
  2. Capture on USB bus: USBPcap1:\\.\USBPcap1
  3. Filter: usb.transfer_type == 0x02 (bulk transfers = PTP traffic)
  4. Perform these actions in X RAW STUDIO while capturing:

- Profile read (connect and let app read camera state) - Preset save (change all preset values, save to a slot) - RAW conversion (load RAF, convert with a preset)

  1. Save each capture as .pcapng
  2. Open a GitHub issue with: camera model, firmware version, all three .pcapng files, and the parameter values used

Troubleshooting

WebUSB Not Available

  • Must use Chrome or Chromium-based browser (Firefox does not support WebUSB)
  • On Android, use Chrome (not Firefox for Android)
  • Check chrome://flags — ensure "Disable WebUSB" is not enabled

Camera Not Detected

  • Ensure the camera is in USB mode (MTP or PTP, not Mass Storage)
  • On Linux without Flatpak: check that your user is in the plugdev group: sudo usermod -aG plugdev $USER
  • On Linux with Flatpak Chrome: add udev rule for vendor 04cb and reload

Permission Denied on Linux

# Check if udev rule is applied
lsusb | grep -i fuji
# Should show Fujifilm device

# Verify permissions
ls -la /dev/bus/usb/$(lsusb | grep -i fuji | awk '{print $2"/"$4}' | tr -d ':')
# Should show rw-rw-rw- or similar open permissions

PTP Transaction Errors

  • Ensure no other app (X RAW STUDIO, Capture One, etc.) is connected to the camera simultaneously
  • Only one WebUSB consumer can hold the interface at a time
  • Disconnect and reconnect the camera if the interface gets stuck

Preset Write Rejected

  • Writing Color property on a monochrome film simulation will be rejected — this is expected behavior (see conditional writes above)
  • Writing Color Temperature requires WB mode set to 0x0012 first
  • HighIsoNR must use the non-linear encoded value, not the raw user-facing value

Debug Log

In the FilmKit UI, scroll to the Debug section at the bottom of the right sidebar → click Copy Log → paste into a GitHub issue for bug reports.


Key Links

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

33.18%
按下载量换算1,696

Claude

32.12%
按下载量换算1,642

Cursor

19.68%
按下载量换算1,006

Gemini CLI

9.29%
按下载量换算475

安全审计

Gen Agent Trust Hub

未通过

Socket

通过

Snyk

可疑

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills