# Video Scene Detection & Splitter – Cut Video into Clips (`gazidev/video-scene-detector`) Actor

Video scene detection & splitter: finds every shot boundary (scene cut) in MP4, MOV, WebM or MKV files and splits the video into one MP4 clip per scene. Timestamps, thumbnails, CSV, EDL & JSON export. Frame-accurate or fast cuts. From $0.005 per video minute.

- **URL**: https://apify.com/gazidev/video-scene-detector.md
- **Developed by:** [Cemal Atakli](https://apify.com/gazidev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 sd video minute scanneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Video Scene Detection & Splitter – Split Video into Scenes and Clips

**Video scene detection** that also **cuts the video for you.** Give it links to video files and it finds every
**shot boundary** (scene cut), returns start/end timestamps with a thumbnail for every scene, and **splits the video into
scenes**: one MP4 clip per scene, with a public download link. Exports a **scenes CSV**, an **EDL (CMX3600)** for
Premiere Pro / DaVinci Resolve / Avid / Final Cut and a JSON summary per video.

No code, no ffmpeg install, no upload limits of online tools: paste URLs, click Start, download the clips.

### What this video splitter does

1. Downloads each video (up to 2 GB; MP4s with the index at the end are handled) – MP4, MOV, M4V, WebM, MKV, AVI, MPEG-TS.
2. **Scene cut detection:** decodes every frame once and scores the change between consecutive frames with ffmpeg's
   scene filter. A frame whose score passes your **threshold** starts a new scene – hard cuts, not just keyframes.
3. Merges scenes shorter than **minSceneSeconds** (no 3-frame clips from flashes).
4. For every scene: dataset row (index, start, end, duration, timecodes, frame numbers, cut score), a JPG thumbnail from
   the first frame of the scene (captured during the scan, no extra cost) and – if **splitClips** is on – an MP4 clip:
   - **Frame-accurate (`fastCut: false`):** re-encoded H.264 (veryfast, CRF 23) + AAC, source resolution capped at 1080p (or 720/480/360p).
     Each clip starts exactly on the first frame of its shot and ends before the next one.
   - **fastCut (default):** stream copy, original quality, ~20× faster and **free**; clips start at the keyframe at or before the cut.
5. Writes `v000-scenes.csv`, `v000-scenes.edl` and `v000-summary.json` per video to the key-value store.

### Use cases

- **Split video into scenes** for editing: drop the EDL into Premiere/Resolve and get the cut list on the timeline.
- **Shot boundary detection** datasets for ML / computer vision (timestamps + thumbnails + cut scores as JSON/CSV).
- Cut long recordings, trailers, ads or lectures into **short clips** for Reels, Shorts and TikTok.
- Thumbnails / storyboards / contact sheets of every shot for content review and video SEO.
- Feed scene clips to AI video models or vision LLMs one shot at a time.
- Ad and broadcast monitoring: detect cuts, count shots, measure average shot length.

### Input example

```json
{
  "videoUrls": ["https://test-videos.co.uk/vids/sintel/mp4/h264/360/Sintel_360_10s_1MB.mp4"],
  "threshold": "0.3",
  "minSceneSeconds": "1",
  "splitClips": true,
  "fastCut": true,
  "maxResolution": "1080",
  "thumbnails": true,
  "maxScenesPerVideo": 500,
  "maxDurationMinutes": 240
}
```

Empty input runs this demo (10 s of the Sintel trailer → 6 scenes, 6 clips, about $0.035).

| Option | Default | What it does |
|---|---|---|
| `threshold` | 0.3 | Scene sensitivity (0.02–0.95). Lower = more scenes. 0.2 for dark/soft footage, 0.4–0.5 for shaky video |
| `minSceneSeconds` | 1 | Merge scenes shorter than this |
| `splitClips` | true | One MP4 per scene (off = timestamps, thumbnails and exports only) |
| `fastCut` | true | Stream copy instead of re-encode (free, keyframe-accurate). Set false for frame-accurate re-encoded clips |
| `maxResolution` | 1080 | Max short side of re-encoded clips: 1080 / 720 / 480 / 360 |
| `thumbnails` / `thumbnailWidth` | true / 640 | JPG of the first frame of each scene |
| `maxScenesPerVideo` | 500 | Cap per video |
| `maxDurationMinutes` | 240 | Only the first N minutes of each video (0 = no limit) |

### Output example

One dataset row per scene (view **Scenes**) plus one row per video (view **Videos & exports**):

```json
{
  "type": "scene", "videoIndex": 0, "sceneIndex": 2,
  "startSec": 2.417, "endSec": 4.0, "durationSec": 1.583,
  "startTimecode": "00:00:02.417", "endTimecode": "00:00:04.000",
  "startFrame": 58, "endFrame": 95, "cutScore": 0.372,
  "thumbnailUrl": "https://api.apify.com/v2/key-value-stores/STORE_ID/records/v000-scene-0002.jpg",
  "clipUrl": "https://api.apify.com/v2/key-value-stores/STORE_ID/records/v000-scene-0002.mp4",
  "clipBytes": 132018, "clipMode": "reencode", "clipWidth": 640, "clipHeight": 360
}
```

```json
{
  "type": "video", "status": "ok", "durationSec": 10.0, "width": 640, "height": 360, "fps": 24.0,
  "cutsDetected": 7, "scenesDetected": 6, "scenes": 6, "clips": 6, "minutesScanned": 0.17, "clipMinutesRendered": 0.16,
  "csvUrl": ".../v000-scenes.csv", "edlUrl": ".../v000-scenes.edl", "summaryUrl": ".../v000-summary.json",
  "charged": {"video-minute-scanned": 1, "clip-minute-rendered": 1}
}
```

EDL excerpt (CMX3600):

```
TITLE: Sintel_360_10s_1MB
FCM: NON-DROP FRAME

002  AX       V     C        00:00:02:10 00:00:04:00 00:00:02:10 00:00:04:00
* FROM CLIP NAME: Sintel_360_10s_1MB
```

### Pricing

Pay per event – you pay only for successful work. Minutes are summed over the whole run and rounded up once.

| Event | Price |
|---|---|
| Video minute scanned – SD source (≤ 576p) | **$0.005** |
| Video minute scanned – HD / Full HD source (720p, 1080p) | $0.02 |
| Video minute scanned – 4K source | $0.08 |
| Clip minute rendered – SD output (≤ 576p) | $0.03 |
| Clip minute rendered – HD output (720p) | $0.10 |
| Clip minute rendered – Full HD output (1080p) | $0.20 |
| fastCut clips, thumbnails, CSV/EDL/JSON exports, failed items | free |
| Actor start | $0.00005 |

Scanning includes timestamps, thumbnails and all exports. Re-encoding is real CPU work (x264), which is why clip minutes
cost more; use `maxResolution: 720` to halve it or `fastCut` to make clips free.

| | This Actor | Typical scene-detection Actor |
|---|---|---|
| Price per video minute | $0.005 (SD) / $0.02 (HD), no start fee | $0.02 + $0.01 per run |
| Scene timestamps | ✓ | ✓ |
| Scene frames / thumbnails | ✓ | ✓ |
| One MP4 clip per scene | ✓ (frame-accurate or free fast cut) | – |
| CSV + EDL (CMX3600) + JSON | ✓ | – |

Examples: a 10-minute 1080p video with timestamps, thumbnails, exports and fast-cut clips = **$0.15**; with
frame-accurate 720p clips = $1.15. The Actor stops gracefully when your maximum total charge is reached.

### FAQ

**How does the scene detection work?** Every frame is decoded and compared with the previous one (ffmpeg `scene` score,
0–1). A score above `threshold` is a cut. This finds hard cuts reliably; slow cross-fades and fades to black may be
detected as one long scene or need a lower threshold.

**Which threshold should I use?** 0.3 for most edited content. More scenes: 0.2. Fewer (handheld, fast action): 0.4–0.5.
Every scene row has its `cutScore`, so you can tune after one run.

**Frame-accurate vs fastCut?** By default clips are stream-copied (fastCut, free). Set `fastCut: false` to re-encode so each clip starts on the exact first frame of each shot. fastCut
copies the original stream without quality loss and for free, but a clip can only start on a keyframe, so it may begin
with a fraction of a second (sometimes a few seconds) of the previous shot.

**Can I split YouTube or TikTok videos?** No. Use direct links to video files you are allowed to process (your CDN,
S3, Dropbox direct links, archive.org…). Platform pages are refused for free.

**How long can videos be?** Files up to 2 GB are downloaded first; larger ones are processed over HTTP.
`maxDurationMinutes` (default 240) caps very long files. Increase the run timeout for long videos – the Actor stops 30 s
before the timeout and keeps everything finished so far.

**Where are my files?** In the run's key-value store (`v000-scene-0001.mp4`, `.jpg`, `v000-scenes.csv`, `.edl`,
`v000-summary.json`). Every row has public links.

### Use with AI agents / Apify MCP

Add this Actor to Claude, Cursor or any MCP client via the Apify MCP server (`https://mcp.apify.com`, tool
`gazidev/video-scene-detector`). An agent can pass `videoUrls`, read the **Scenes** dataset (timestamps, thumbnails,
clip links) and then send individual scene clips or thumbnails to a vision model. Via API:
`POST https://api.apify.com/v2/acts/gazidev~video-scene-detector/run-sync-get-dataset-items?token=…` with the input JSON.

# Actor input Schema

## `videoUrls` (type: `array`):

Direct links to video FILES: MP4, MOV, M4V, WebM, MKV, AVI, MPEG-TS (your CDN, S3/R2/GCS bucket, Dropbox/Drive direct-download link, archive.org…). Every video is scanned for scene cuts and split into one clip per scene. Pages of YouTube, TikTok, Instagram, Vimeo etc. are refused (this is a splitter, not a downloader). Files up to 2 GB are downloaded first, so MP4s with the index at the end work too. Empty input runs a small demo.

## `bulkText` (type: `string`):

Optional: paste many URLs, one per line, or a CSV with a `url` column. Added to the list above.

## `threshold` (type: `string`):

ffmpeg scene-change score (0.02–0.95) a frame needs to start a new scene. Lower = more sensitive (more, shorter scenes, may split on fast motion or flashes); higher = only hard cuts. 0.3 suits most edited videos; try 0.2 for dark or soft footage, 0.4–0.5 for shaky handheld video.

## `minSceneSeconds` (type: `string`):

Scenes shorter than this are merged into the previous scene (avoids 3-frame clips from flashes or quick cutaways). 0 = keep every cut.

## `maxScenesPerVideo` (type: `integer`):

Safety cap: only the first N scenes of each video get rows, thumbnails and clips.

## `maxDurationMinutes` (type: `integer`):

Only the first N minutes of each video are scanned and split (0 = no limit, default 240 = 4 hours). Protects you from surprise costs on very long files. The built-in demo video is always limited to its first minute.

## `splitClips` (type: `boolean`):

Cut every scene into its own MP4 file with a public download link. Turn off to get only timestamps, thumbnails and the CSV/EDL/JSON exports (then you pay only the scan).

## `fastCut` (type: `boolean`):

On (default): clips are copied without re-encoding — original quality, ~20× faster and FREE; each clip starts at the keyframe at or before the cut, so it can begin with a few frames of the previous scene. Off: clips are re-encoded (H.264 veryfast, CRF 23, AAC) and start on the exact first frame of the scene (charged per clip minute).

## `maxResolution` (type: `string`):

Re-encoded clips keep the source resolution up to this limit (short side; portrait videos are handled). Never upscales. Lower = smaller files and cheaper clip minutes (see pricing). Ignored with fast cut.

## `thumbnails` (type: `boolean`):

Save one JPG of the first frame of every scene, captured during the scan (free).

## `thumbnailWidth` (type: `integer`):

Maximum thumbnail width; never upscales.

## `maxConcurrency` (type: `integer`):

How many videos are processed at the same time. Encoding is CPU-bound, so more than 1 only helps with more memory (CPU).

## `proxyConfiguration` (type: `object`):

Only needed if a host blocks Apify datacenter IPs (HTTP 403). Usually leave off.

## Actor input object example

```json
{
  "videoUrls": [
    "https://test-videos.co.uk/vids/sintel/mp4/h264/360/Sintel_360_10s_1MB.mp4"
  ],
  "threshold": "0.3",
  "minSceneSeconds": "1",
  "maxScenesPerVideo": 500,
  "maxDurationMinutes": 240,
  "splitClips": true,
  "fastCut": true,
  "maxResolution": "1080",
  "thumbnails": true,
  "thumbnailWidth": 640,
  "maxConcurrency": 1,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `scenes` (type: `string`):

No description

## `videos` (type: `string`):

No description

## `results` (type: `string`):

No description

## `files` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "videoUrls": [
        "https://test-videos.co.uk/vids/sintel/mp4/h264/360/Sintel_360_10s_1MB.mp4"
    ],
    "threshold": "0.3",
    "minSceneSeconds": "1",
    "maxScenesPerVideo": 500,
    "maxDurationMinutes": 240,
    "splitClips": true,
    "fastCut": true,
    "maxResolution": "1080",
    "thumbnails": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("gazidev/video-scene-detector").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "videoUrls": ["https://test-videos.co.uk/vids/sintel/mp4/h264/360/Sintel_360_10s_1MB.mp4"],
    "threshold": "0.3",
    "minSceneSeconds": "1",
    "maxScenesPerVideo": 500,
    "maxDurationMinutes": 240,
    "splitClips": True,
    "fastCut": True,
    "maxResolution": "1080",
    "thumbnails": True,
}

# Run the Actor and wait for it to finish
run = client.actor("gazidev/video-scene-detector").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "videoUrls": [
    "https://test-videos.co.uk/vids/sintel/mp4/h264/360/Sintel_360_10s_1MB.mp4"
  ],
  "threshold": "0.3",
  "minSceneSeconds": "1",
  "maxScenesPerVideo": 500,
  "maxDurationMinutes": 240,
  "splitClips": true,
  "fastCut": true,
  "maxResolution": "1080",
  "thumbnails": true
}' |
apify call gazidev/video-scene-detector --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gazidev/video-scene-detector"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/UjDABzRdKFQFtgcT4/builds/FGx81fZIFnAbhf853/openapi.json
