julianbou/streamcut

Frame-exact clip exporter for movies & series. Search a line, mark in/out, export MP4. Windows & macOS.

Kotlin

4

3,139 commits

updated Oct 4, 2026

See the code

See what people are saying

SourceMessageScoreDate

I added an MCP server to my desktop video-clipping app: one message with four half-remembered movie quotes, four trimmed clips back (r/mcp)

StreamCut is an open-source desktop app (Win/Mac, GPL-3.0) for cutting clips out of films and series without downloading the whole file. The latest release embeds an MCP server, so an assistant can do the finding and cutting. The message was "clip me: heres johnny, ill be back, bigger boat, looking…

2

Oct 4, 2026

README

StreamCut

StreamCut

Stargazers Issues License

A desktop clipping tool for movies and series.
Find the moment, mark in and out, export a shareable MP4.

⚠️ Alpha — personal project, expect breakage

StreamCut is in alpha and built for its author. Releases are unsigned (macOS and Windows), and breaking changes land without notice — stored clips, settings and compatibility can all change between builds. Don't rely on it for anything you can't redo.

About

StreamCut is a fork of Nuvio Desktop turned into a clip-export tool. Upstream is a media client that happens to play video; StreamCut makes the clip the subject and the player the instrument.

What it does differently:

  • The player is the clipping UI. No separate trim screen and no modal — in/out markers, timecode readouts, a zoomed timeline and the export controls live on the playback chrome.
  • Marking never seeks. I and O bring the in/out point to the playhead instead of moving the playhead to a handle, so you never lose the frame you just found. Fine adjustments pause first.
  • Frame-exact cuts. Clips are re-encoded rather than keyframe-snapped: H.264/AAC MP4 (yuv420p, stereo) via the platform hardware encoder — VideoToolbox, NVENC, QSV or AMF — with an automatic libx264 fallback.
  • Clips inherit what you were watching. The audio track that was playing carries over, and the active subtitle can be burned in. HDR sources are tonemapped to SDR instead of coming out grey.
  • Subtitle phrase search. Type a line, get the timestamps where it's said, jump there, and clip around it. Matching is fuzzy and spans consecutive cues.
  • Filmstrip scrubbing. Thumbnails are extracted on demand with tuned seeks and cached per title, so building a strip over a remote stream costs a fraction of the file.
  • Multi-range drafts. Mark several ranges in one pass with A and export them as a queue.
  • A clips-first library with real thumbnails, drag-out to Finder, and a 7-second undo window on delete.

Upstream's viewing features (next-episode cards, intro skip, parental guides, the app-icon picker and so on) are gated, not deleted — they sit behind AppFeaturePolicy.viewingChromeEnabled, which is false on desktop, so merges from NuvioMedia/NuvioDesktop keep applying cleanly.

Browsing, catalogs and stream resolution still come from user-installed addons and user-provided sources, exactly as upstream.

Keyboard shortcuts

KeyAction
I / OMark in / out at the playhead
, / .Step one frame back / forward (pauses first)
← / → on a focused handleMove that point one frame (Shift: one second)
AAdd the current range to the draft
XExport
RLoop-preview the range
BShow / hide the filmstrip
BackspaceUndo: the current draft first, then set-aside ranges newest-first
/ or Cmd/Ctrl + FSubtitle phrase search
?Show every shortcut

With unexported ranges, the first Esc only warns; press it again to leave the player.

Installation

Download the latest build from Releases.

Published builds:

  • macOS, Apple Silicon (.dmg). Ad-hoc signed and not notarized, so Gatekeeper blocks it on first launch. Approve it under System Settings → Privacy & Security → "Open Anyway", or strip the quarantine flag:

    xattr -dr com.apple.quarantine /Applications/StreamCut.app
    

    ffmpeg is bundled, so clipping works without installing anything else.

  • Windows x64 (.msi). Not signed, so SmartScreen shows "Windows protected your PC": click More info, then Run anyway. The installer bundles ffmpeg, so clipping works without installing anything else.

Linux packaging is inherited from upstream and untested here; build from source if you need it.

Installed builds check this repository's releases and offer updates in the app.

Your data stays on your computer unless you sign in to sync; see the privacy policy.

Requirements

  • JDK 17 to build.
  • ffmpeg and ffprobe for clip export. The Windows installer and the macOS DMG bundle them (a pinned jellyfin-ffmpeg build on Mac, fetched and checksummed by prepareMacosFfmpeg; -Pnuvio.macos.ffmpeg.dir swaps in a local folder). On Linux they must be installed: the build must include libass (subtitle burn-in) and libzimg (zscale, for HDR tonemapping). Homebrew's ffmpeg has neither; jellyfin-ffmpeg does. StreamCut probes each candidate binary for those filters and picks the first that has both; NUVIO_FFMPEG_PATH overrides the search.

Development

git clone https://github.com/julianbou/streamcut.git
cd streamcut

local.properties is gitignored, and Gradle fails configuration without it. Blank values are fine — only sign-in and cross-device sync depend on them:

NUVIO_SUPABASE_URL=
NUVIO_SUPABASE_ANON_KEY=

To run the backend yourself, point those at your own Supabase project and apply supabase/schema.sql (tables, RPCs, RLS and triggers) in its SQL editor, then enable email auth. Clipping, playback and the local library all work without it.

Run from source:

export JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home
./gradlew :composeApp:run

On Windows PowerShell:

.\gradlew.bat :composeApp:run

Compile-only check (~2 min cold):

./gradlew :composeApp:compileKotlinDesktop

Tests:

./gradlew :composeApp:desktopTest

Package a release for the current host:

./gradlew :composeApp:packageReleaseDmg -Pcompose.desktop.packaging.checkJdkVendor=false \
  --no-configuration-cache --no-daemon

The DMG lands in composeApp/build/compose/release-dmgs/. Other hosts:

# Windows
./gradlew :composeApp:packageReleaseMsi --rerun-tasks

# Linux
./gradlew :composeApp:packageReleaseDeb

# macOS, both architectures (needs an x86_64 JDK for the Intel slice)
./scripts/build-macos-release-dmgs.sh --package-only

The Windows MSI is built on GitHub Actions: run the Windows build workflow (.github/workflows/windows-build.yml) and download the MSI from the run's artifacts. It fetches a pinned ffmpeg build and bundles it; a local Windows build bundles one only when -Pnuvio.windows.ffmpeg.dir points at a folder with ffmpeg.exe and ffprobe.exe.

.github/workflows/desktop-release.yml is upstream's and cannot run in this repo — it requires Apple signing certificates, notarytool credentials, a Sentry DSN and a base64 local.properties with third-party API keys. macOS releases are built locally.

Syncing with upstream Nuvio

Upstream releases are merged, never cherry-picked, with scripts/upstream/sync-upstream.sh (status, start <tag>, verify). It resets the files StreamCut owns, re-deletes what StreamCut removed, re-applies the branding, and checks that no fork decision was undone. The sync-upstream Claude skill in .claude/skills/ drives the whole process.

Project structure

  • composeApp/src/commonMain/ — shared UI, features and repositories, including features/clip/ (the clip library, models, repository and filename templating).
  • composeApp/src/desktopMain/kotlin/.../features/clip/ — the export path: ClipExtractor.desktop.kt (ffmpeg graph: tonemap → crop → subtitles) and ClipStrip.desktop.kt (filmstrip extraction and caching).
  • composeApp/src/desktopMain/resources/player-ui/ — the player chrome, rendered in a webview. Clipper code is kept in its own clip-controls.{js,css} and clip-search.{js,css} to stay off upstream's merge surface.
  • composeApp/src/*/kotlin/com/nuvio/app/core/build/AppFeaturePolicy* — one expect and six per-target actuals; this is what gates viewing chrome.
  • composeApp/src/commonMain/composeResources/values/clip_strings.xml — clipper strings, separate from upstream's strings.xml for the same reason.
  • branding/make_brand.py — regenerates every icon, launch mark and wordmark from branding/logo-source.jpg.
  • composeApp/Configuration/DesktopVersion.properties — release version and build code.
  • composeApp/Configuration/UpstreamVersion.properties — the Nuvio Desktop release last merged, shown in About.
  • Fork identity (app name, vendor, bundle id, URL scheme) lives in the fork.* properties in gradle.properties.

Android and iOS source sets are still present, but clipping is desktop-only and neither mobile target is built or verified here.

Versioning

VERSION_NAME=0.1.0-alpha
VERSION_CODE=1

Use the helper, and keep the bump as the last commit before the tag. Tags carry no v prefix — the tag is the version verbatim.

./scripts/set-version.sh --desktop 0.1.1-alpha --desktop-code 2
./scripts/set-version.sh --show

StreamCut is a client-side interface for browsing metadata and playing media provided by user-installed addons and/or user-provided sources. It does not host, store, or distribute any media, and it is not affiliated with any addon, catalog, source, or content provider.

Clipping is intended for content you own or are otherwise authorized to use, and for uses permitted by applicable law. What you export is your responsibility.

Upstream's Legal & Disclaimer page covers the inherited addon and DMCA policy.

Built with

  • Kotlin Multiplatform / Compose Multiplatform
  • mpv via a native player bridge
  • ffmpeg / ffprobe for extraction, tonemapping and subtitle burn-in (bundled on Windows from BtbN/FFmpeg-Builds, GPL)
  • Supabase (optional, for auth and sync)
  • Compose Desktop packaging (jpackage)

License

GPL-3.0, inherited from Nuvio Desktop. See LICENSE.

clip
compose-desktop
fan-edit
ffmpeg
kotlin-multiplatform
mpv
scenepack
stremio
subtitle-search
video-clipper

julianbou/streamcut

Frame-exact clip exporter for movies & series. Search a line, mark in/out, export MP4. Windows & macOS.

Kotlin

4

3,139 commits

updated Oct 4, 2026

See the code

See what people are saying

SourceMessageScoreDate

I added an MCP server to my desktop video-clipping app: one message with four half-remembered movie quotes, four trimmed clips back (r/mcp)

StreamCut is an open-source desktop app (Win/Mac, GPL-3.0) for cutting clips out of films and series without downloading the whole file. The latest release embeds an MCP server, so an assistant can do the finding and cutting. The message was "clip me: heres johnny, ill be back, bigger boat, looking…

2

Oct 4, 2026

README

StreamCut

StreamCut

Stargazers Issues License

A desktop clipping tool for movies and series.
Find the moment, mark in and out, export a shareable MP4.

⚠️ Alpha — personal project, expect breakage

StreamCut is in alpha and built for its author. Releases are unsigned (macOS and Windows), and breaking changes land without notice — stored clips, settings and compatibility can all change between builds. Don't rely on it for anything you can't redo.

About

StreamCut is a fork of Nuvio Desktop turned into a clip-export tool. Upstream is a media client that happens to play video; StreamCut makes the clip the subject and the player the instrument.

What it does differently:

  • The player is the clipping UI. No separate trim screen and no modal — in/out markers, timecode readouts, a zoomed timeline and the export controls live on the playback chrome.
  • Marking never seeks. I and O bring the in/out point to the playhead instead of moving the playhead to a handle, so you never lose the frame you just found. Fine adjustments pause first.
  • Frame-exact cuts. Clips are re-encoded rather than keyframe-snapped: H.264/AAC MP4 (yuv420p, stereo) via the platform hardware encoder — VideoToolbox, NVENC, QSV or AMF — with an automatic libx264 fallback.
  • Clips inherit what you were watching. The audio track that was playing carries over, and the active subtitle can be burned in. HDR sources are tonemapped to SDR instead of coming out grey.
  • Subtitle phrase search. Type a line, get the timestamps where it's said, jump there, and clip around it. Matching is fuzzy and spans consecutive cues.
  • Filmstrip scrubbing. Thumbnails are extracted on demand with tuned seeks and cached per title, so building a strip over a remote stream costs a fraction of the file.
  • Multi-range drafts. Mark several ranges in one pass with A and export them as a queue.
  • A clips-first library with real thumbnails, drag-out to Finder, and a 7-second undo window on delete.

Upstream's viewing features (next-episode cards, intro skip, parental guides, the app-icon picker and so on) are gated, not deleted — they sit behind AppFeaturePolicy.viewingChromeEnabled, which is false on desktop, so merges from NuvioMedia/NuvioDesktop keep applying cleanly.

Browsing, catalogs and stream resolution still come from user-installed addons and user-provided sources, exactly as upstream.

Keyboard shortcuts

KeyAction
I / OMark in / out at the playhead
, / .Step one frame back / forward (pauses first)
← / → on a focused handleMove that point one frame (Shift: one second)
AAdd the current range to the draft
XExport
RLoop-preview the range
BShow / hide the filmstrip
BackspaceUndo: the current draft first, then set-aside ranges newest-first
/ or Cmd/Ctrl + FSubtitle phrase search
?Show every shortcut

With unexported ranges, the first Esc only warns; press it again to leave the player.

Installation

Download the latest build from Releases.

Published builds:

  • macOS, Apple Silicon (.dmg). Ad-hoc signed and not notarized, so Gatekeeper blocks it on first launch. Approve it under System Settings → Privacy & Security → "Open Anyway", or strip the quarantine flag:

    xattr -dr com.apple.quarantine /Applications/StreamCut.app
    

    ffmpeg is bundled, so clipping works without installing anything else.

  • Windows x64 (.msi). Not signed, so SmartScreen shows "Windows protected your PC": click More info, then Run anyway. The installer bundles ffmpeg, so clipping works without installing anything else.

Linux packaging is inherited from upstream and untested here; build from source if you need it.

Installed builds check this repository's releases and offer updates in the app.

Your data stays on your computer unless you sign in to sync; see the privacy policy.

Requirements

  • JDK 17 to build.
  • ffmpeg and ffprobe for clip export. The Windows installer and the macOS DMG bundle them (a pinned jellyfin-ffmpeg build on Mac, fetched and checksummed by prepareMacosFfmpeg; -Pnuvio.macos.ffmpeg.dir swaps in a local folder). On Linux they must be installed: the build must include libass (subtitle burn-in) and libzimg (zscale, for HDR tonemapping). Homebrew's ffmpeg has neither; jellyfin-ffmpeg does. StreamCut probes each candidate binary for those filters and picks the first that has both; NUVIO_FFMPEG_PATH overrides the search.

Development

git clone https://github.com/julianbou/streamcut.git
cd streamcut

local.properties is gitignored, and Gradle fails configuration without it. Blank values are fine — only sign-in and cross-device sync depend on them:

NUVIO_SUPABASE_URL=
NUVIO_SUPABASE_ANON_KEY=

To run the backend yourself, point those at your own Supabase project and apply supabase/schema.sql (tables, RPCs, RLS and triggers) in its SQL editor, then enable email auth. Clipping, playback and the local library all work without it.

Run from source:

export JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home
./gradlew :composeApp:run

On Windows PowerShell:

.\gradlew.bat :composeApp:run

Compile-only check (~2 min cold):

./gradlew :composeApp:compileKotlinDesktop

Tests:

./gradlew :composeApp:desktopTest

Package a release for the current host:

./gradlew :composeApp:packageReleaseDmg -Pcompose.desktop.packaging.checkJdkVendor=false \
  --no-configuration-cache --no-daemon

The DMG lands in composeApp/build/compose/release-dmgs/. Other hosts:

# Windows
./gradlew :composeApp:packageReleaseMsi --rerun-tasks

# Linux
./gradlew :composeApp:packageReleaseDeb

# macOS, both architectures (needs an x86_64 JDK for the Intel slice)
./scripts/build-macos-release-dmgs.sh --package-only

The Windows MSI is built on GitHub Actions: run the Windows build workflow (.github/workflows/windows-build.yml) and download the MSI from the run's artifacts. It fetches a pinned ffmpeg build and bundles it; a local Windows build bundles one only when -Pnuvio.windows.ffmpeg.dir points at a folder with ffmpeg.exe and ffprobe.exe.

.github/workflows/desktop-release.yml is upstream's and cannot run in this repo — it requires Apple signing certificates, notarytool credentials, a Sentry DSN and a base64 local.properties with third-party API keys. macOS releases are built locally.

Syncing with upstream Nuvio

Upstream releases are merged, never cherry-picked, with scripts/upstream/sync-upstream.sh (status, start <tag>, verify). It resets the files StreamCut owns, re-deletes what StreamCut removed, re-applies the branding, and checks that no fork decision was undone. The sync-upstream Claude skill in .claude/skills/ drives the whole process.

Project structure

  • composeApp/src/commonMain/ — shared UI, features and repositories, including features/clip/ (the clip library, models, repository and filename templating).
  • composeApp/src/desktopMain/kotlin/.../features/clip/ — the export path: ClipExtractor.desktop.kt (ffmpeg graph: tonemap → crop → subtitles) and ClipStrip.desktop.kt (filmstrip extraction and caching).
  • composeApp/src/desktopMain/resources/player-ui/ — the player chrome, rendered in a webview. Clipper code is kept in its own clip-controls.{js,css} and clip-search.{js,css} to stay off upstream's merge surface.
  • composeApp/src/*/kotlin/com/nuvio/app/core/build/AppFeaturePolicy* — one expect and six per-target actuals; this is what gates viewing chrome.
  • composeApp/src/commonMain/composeResources/values/clip_strings.xml — clipper strings, separate from upstream's strings.xml for the same reason.
  • branding/make_brand.py — regenerates every icon, launch mark and wordmark from branding/logo-source.jpg.
  • composeApp/Configuration/DesktopVersion.properties — release version and build code.
  • composeApp/Configuration/UpstreamVersion.properties — the Nuvio Desktop release last merged, shown in About.
  • Fork identity (app name, vendor, bundle id, URL scheme) lives in the fork.* properties in gradle.properties.

Android and iOS source sets are still present, but clipping is desktop-only and neither mobile target is built or verified here.

Versioning

VERSION_NAME=0.1.0-alpha
VERSION_CODE=1

Use the helper, and keep the bump as the last commit before the tag. Tags carry no v prefix — the tag is the version verbatim.

./scripts/set-version.sh --desktop 0.1.1-alpha --desktop-code 2
./scripts/set-version.sh --show

StreamCut is a client-side interface for browsing metadata and playing media provided by user-installed addons and/or user-provided sources. It does not host, store, or distribute any media, and it is not affiliated with any addon, catalog, source, or content provider.

Clipping is intended for content you own or are otherwise authorized to use, and for uses permitted by applicable law. What you export is your responsibility.

Upstream's Legal & Disclaimer page covers the inherited addon and DMCA policy.

Built with

  • Kotlin Multiplatform / Compose Multiplatform
  • mpv via a native player bridge
  • ffmpeg / ffprobe for extraction, tonemapping and subtitle burn-in (bundled on Windows from BtbN/FFmpeg-Builds, GPL)
  • Supabase (optional, for auth and sync)
  • Compose Desktop packaging (jpackage)

License

GPL-3.0, inherited from Nuvio Desktop. See LICENSE.

clip
compose-desktop
fan-edit
ffmpeg
kotlin-multiplatform
mpv
scenepack
stremio
subtitle-search
video-clipper

Languages

Kotlin

88.7%

C++

2.1%

JavaScript

1.6%

C

1.3%

CSS

1.3%

Objective-C++

1.1%

Swift

1.1%