clip-join 1.0.1 → 1.0.2

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/README.md CHANGED
@@ -1,66 +1,64 @@
1
- # ClipJoin 🎬
1
+ <h1 align="center">ClipJoin 🎬</h1>
2
2
 
3
- **Merge your videos into one file: fast, lossless, and right from your terminal.**
3
+ <p align="center"><strong>Merge your videos into one file: fast, lossless, and right from your terminal.</strong></p>
4
+
5
+ <p align="center">🏆 The best, easiest way to merge your clips. No editor, no re-encoding, no fuss.</p>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/clip-join"><img src="https://img.shields.io/npm/v/clip-join?logo=npm&color=green" alt="npm version"></a>
9
+ <a href="https://github.com/BrenoHA/clip-join"><img src="https://img.shields.io/badge/GitHub-BrenoHA%2Fclip--join-blue?logo=github" alt="GitHub"></a>
10
+ <a href="LICENSE"><img src="https://img.shields.io/npm/l/clip-join?color=blue" alt="license"></a>
11
+ </p>
4
12
 
5
13
  > _Back from a trip with a memory card full of GoPro clips? Select them, stitch them into
6
- > one file, and upload the whole adventure to YouTube in one shot, public or unlisted,
7
- > your call._
14
+ > one file, and upload the whole adventure to YouTube in one shot, public or unlisted, your call._
8
15
 
9
- Run ClipJoin, pick the files you want, reorder them, and hit join. Simple as that.
16
+ <p align="center">
17
+ <img src="docs/gifs/usage-demo.gif" alt="Usage demo">
18
+ </p>
10
19
 
11
- ![Usage demo](docs/gifs/usage-demo.gif)
20
+ Run ClipJoin, pick the files you want, reorder them, and hit join. Simple as that.
12
21
 
13
22
  Matching clips merge **losslessly in seconds**, with no re-encoding and no quality loss
14
23
  (40×1GB in seconds, not hours). Different formats? ClipJoin re-encodes them for you. No
15
24
  flags, no config, just a clean keyboard-driven flow from folder to finished file.
16
25
 
17
- ## How it works
26
+ ## Install
18
27
 
19
- 1. A short **clips-merging animation** plays on launch while ClipJoin checks for ffmpeg.
20
- 2. **Browse** the filesystem to a folder of clips (or select individual files).
21
- 3. Each clip's embedded `creation_time` (falling back to file mtime) sets the true
22
- capture order, which you can then reorder by hand.
23
- 4. **Toggle** clips in/out and rename the output, watching the live preview (clip
24
- count, total duration, size, lossless-vs-re-encode).
25
- 5. On join, ClipJoin first tries a **lossless concat** (`ffmpeg -c copy`), which keeps full
26
- quality and works when clips share a codec/resolution. If that fails (e.g. mixed formats
27
- or codecs) it **re-encodes** automatically (H.264/AAC, slower but always works).
28
+ Install once with npm:
28
29
 
29
- Optionally, add a **crossfade transition** between clips (press `t` on the arrange screen).
30
- It's off by default; enabling it re-encodes the output (transitions can't be done losslessly).
30
+ ```bash
31
+ npm install -g clip-join
32
+ ```
33
+
34
+ > **ClipJoin uses [ffmpeg](#ffmpeg-required) on your `PATH`.** Install it before running `clipjoin` follow the steps bellow if you don't have it installed yet.
31
35
 
32
- Every joined file is written to a fixed **ClipJoin** folder in your home —
33
- `~/Movies/ClipJoin` on macOS, `~/Videos/ClipJoin` on Linux/Windows — so `clipjoin`
34
- saves to the same predictable place no matter which directory you run it from. Files
35
- get a timestamped default name (`joined_output_YYYY-MM-DD_HHMMSS.mp4`), so repeated
36
- joins never overwrite one another. Point it elsewhere for a run with `--out`:
36
+ Then run it from any folder:
37
37
 
38
38
  ```bash
39
- clipjoin --out ~/Desktop # write joined videos to ~/Desktop instead
39
+ clipjoin # launch anywhere (clip-join works too, same command)
40
40
  ```
41
41
 
42
- ## Install
43
-
44
- Install globally with npm — you get a `clipjoin` command anywhere
45
- (`clip-join` works too, they're the same thing):
42
+ **Just trying it out?** Run it once, no install:
46
43
 
47
44
  ```bash
48
- npm install -g clip-join # then run: clipjoin
49
- npx clip-join # or run once without installing
45
+ npx clip-join
50
46
  ```
51
47
 
52
- **ffmpeg is required** (it bundles `ffprobe`) — install it with your package
53
- manager if you don't have it:
48
+ ### ffmpeg (required)
49
+
50
+ ClipJoin uses **ffmpeg** (which bundles `ffprobe`). Install it with your package manager:
54
51
 
55
52
  ```bash
56
53
  brew install ffmpeg # macOS
57
- sudo apt install ffmpeg # Debian/Ubuntu
54
+ sudo apt install ffmpeg # Debian / Ubuntu
55
+ winget install ffmpeg # Windows (or: choco install ffmpeg / scoop install ffmpeg)
58
56
  ```
59
57
 
60
58
  <details>
61
59
  <summary>Optional: one-line installer</summary>
62
60
 
63
- Prefer a single command that also checks Node + ffmpeg for you and fixes PATH?
61
+ Prefer a single command that also checks Node + ffmpeg for you and fixes your PATH?
64
62
 
65
63
  ```bash
66
64
  curl -fsSL https://raw.githubusercontent.com/BrenoHA/clip-join/main/install.sh | bash
@@ -70,60 +68,62 @@ It just wraps `npm install -g clip-join` with a few pre-flight checks.
70
68
 
71
69
  </details>
72
70
 
73
- ## Requirements
71
+ ## Where your videos are saved
74
72
 
75
- - **Node.js 18+**
76
- - **ffmpeg** (bundles `ffprobe`) on your `PATH`
73
+ Every joined file lands in a fixed **ClipJoin** folder in your home, so it's always in the
74
+ same predictable place no matter which directory you run `clipjoin` from:
77
75
 
78
- ## Building from source
76
+ - **macOS** `~/Movies/ClipJoin`
77
+ - **Linux / Windows** → `~/Videos/ClipJoin`
78
+
79
+ Files get a timestamped default name (`joined_output_YYYY-MM-DD_HHMMSS.mp4`), so repeated
80
+ joins never overwrite one another. Send the output somewhere else for a run with `--out`:
79
81
 
80
82
  ```bash
81
- git clone https://github.com/BrenoHA/clip-join.git
82
- cd clip-join
83
- npm install
84
- npm run build
83
+ clipjoin --out ~/Desktop # write joined videos to ~/Desktop instead
84
+ clipjoin ~/clips --out ~/Desktop # start in ~/clips, save to ~/Desktop
85
85
  ```
86
86
 
87
+ ## How it works
88
+
89
+ 1. A short **clips-merging animation** plays on launch while ClipJoin checks for ffmpeg.
90
+ 2. **Browse** the filesystem to a folder of clips (or select individual files).
91
+ 3. Each clip's embedded `creation_time` (falling back to file mtime) sets the true
92
+ capture order, which you can then reorder by hand.
93
+ 4. **Toggle** clips in/out and rename the output, watching the live preview (clip
94
+ count, total duration, size, lossless-vs-re-encode).
95
+ 5. On join, ClipJoin first tries a **lossless concat** (`ffmpeg -c copy`), which keeps full
96
+ quality and works when clips share a codec/resolution. If that fails (e.g. mixed formats
97
+ or codecs) it **re-encodes** automatically (H.264/AAC, slower but always works).
98
+
99
+ Optionally, add a **crossfade transition** between clips (press `t` on the arrange screen).
100
+ It's off by default; enabling it re-encodes the output (transitions can't be done losslessly).
101
+
87
102
  ## Usage
88
103
 
104
+ Everything is keyboard-driven.
105
+
89
106
  ```bash
90
- clipjoin # after installing globally
107
+ clipjoin # launch and browse from the current folder
91
108
  clipjoin ~/clips # jump straight into a folder
109
+ clipjoin --out ~/Desktop # change where joined files are saved
110
+ clipjoin --version # print the installed version (alias: -v)
111
+ ```
92
112
 
93
- npm run dev # dev mode from source, no build step (via tsx)
94
- npm start # from source, after `npm run build`
95
- npm start ~/clips # jump straight into a folder
113
+ No need to memorize anything, every available shortcut is shown right on the
114
+ terminal as you go, so you always know your options at each step.
96
115
 
97
- Everything is keyboard-driven.
116
+ ## Building from source
117
+
118
+ ```bash
119
+ git clone https://github.com/BrenoHA/clip-join.git
120
+ cd clip-join
121
+ npm install
122
+ npm run build # then: npm start
123
+ npm run dev # or run straight from source (via tsx), no build step
124
+ ```
98
125
 
99
- **Browsing**
100
-
101
- | Key | Action |
102
- | ---------------------------- | ---------------------------------------------------- |
103
- | `↑ ↓` | Move the cursor |
104
- | `space` / `Enter` | Open a directory, or select/deselect a video file |
105
- | `←` / `Backspace` / `Delete` | Up a directory (cursor lands on the folder you left) |
106
- | `~` | Jump to your home directory |
107
- | `s` | Use every video in the current folder |
108
- | `c` | Continue with the files you've selected |
109
- | `q` | Quit |
110
-
111
- **Arranging & joining**
112
-
113
- Once you've picked your clips, arrange them and watch the live preview (clip count,
114
- total duration, size, and whether the join will be lossless):
115
-
116
- | Key | Action |
117
- | ----------------- | --------------------------------------------- |
118
- | `↑ ↓` | Move the cursor |
119
- | `space` / `Enter` | Toggle a clip in/out of the join |
120
- | `Shift+↑ ↓` | Reorder the selected clip |
121
- | `o` | Rename the output file (saved to the ClipJoin folder) |
122
- | `t` | Toggle the transition (None / Crossfade) |
123
- | `j` | **▶ Start the join** |
124
- | `esc` | Back to the browser |
125
- | `r` | Join again (summary screen) |
126
- | `q` | Quit |
126
+ Requires **Node.js 18+** and **ffmpeg** on your `PATH`.
127
127
 
128
128
  ## Project structure
129
129
 
package/dist/index.js CHANGED
@@ -1,24 +1,46 @@
1
1
  #!/usr/bin/env node
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { readFileSync } from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
3
7
  import { render } from "ink";
4
8
  import { App } from "./ui/App.js";
5
9
  import { setOutputDir } from "./config.js";
10
+ const argv = process.argv.slice(2);
11
+ // Expand a leading `~` ourselves: Windows shells (cmd/PowerShell) don't do it,
12
+ // so `clipjoin --out ~/Desktop` would otherwise create a literal "~" folder.
13
+ function expandTilde(p) {
14
+ if (p === "~")
15
+ return os.homedir();
16
+ if (p.startsWith("~/") || p.startsWith("~\\"))
17
+ return path.join(os.homedir(), p.slice(2));
18
+ return p;
19
+ }
20
+ // `--version` / `-v`: print the package version and exit before starting the UI.
21
+ // package.json sits one level above the built entry (dist/index.js) and is always
22
+ // included in the npm tarball, so this resolves for global installs and dev alike.
23
+ if (argv.includes("--version") || argv.includes("-v")) {
24
+ const pkgPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
25
+ const { version } = JSON.parse(readFileSync(pkgPath, "utf8"));
26
+ console.log(version);
27
+ process.exit(0);
28
+ }
6
29
  // Minimal arg parsing: one optional positional (start folder) plus `--out <dir>`
7
30
  // (aliases: `-o`, `--out=<dir>`) to redirect where joined videos are written.
8
31
  let folderArg;
9
- const argv = process.argv.slice(2);
10
32
  for (let i = 0; i < argv.length; i++) {
11
33
  const arg = argv[i];
12
34
  if (arg === "--out" || arg === "-o") {
13
35
  const dir = argv[++i];
14
36
  if (dir)
15
- setOutputDir(dir);
37
+ setOutputDir(expandTilde(dir));
16
38
  }
17
39
  else if (arg.startsWith("--out=")) {
18
- setOutputDir(arg.slice("--out=".length));
40
+ setOutputDir(expandTilde(arg.slice("--out=".length)));
19
41
  }
20
42
  else if (!arg.startsWith("-") && folderArg === undefined) {
21
- folderArg = arg;
43
+ folderArg = expandTilde(arg);
22
44
  }
23
45
  }
24
46
  const { waitUntilExit } = render(_jsx(App, { initialFolder: folderArg }));
package/dist/ui/App.js CHANGED
@@ -16,6 +16,12 @@ import { BrowseScreen } from "./screens/BrowseScreen.js";
16
16
  import { EditScreen } from "./screens/EditScreen.js";
17
17
  import { JoinScreen } from "./screens/JoinScreen.js";
18
18
  import { SummaryScreen } from "./screens/SummaryScreen.js";
19
+ // Platform-appropriate way to install ffmpeg, shown when it's missing.
20
+ const FFMPEG_INSTALL_HINT = process.platform === "darwin"
21
+ ? "brew install ffmpeg"
22
+ : process.platform === "win32"
23
+ ? "winget install ffmpeg"
24
+ : "sudo apt install ffmpeg";
19
25
  export function App({ initialFolder }) {
20
26
  useFullscreen();
21
27
  const { exit } = useApp();
@@ -128,7 +134,7 @@ export function App({ initialFolder }) {
128
134
  let body = null;
129
135
  switch (phase) {
130
136
  case "missingdeps":
131
- body = (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: theme.danger, children: "ffmpeg / ffprobe not found on your PATH." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "ClipJoin needs ffmpeg to read and join videos. Install it with:" }) }), _jsx(Text, { color: theme.brand, children: " brew install ffmpeg" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.muted, children: "Press q to quit, then re-run clip-join." }) })] }));
137
+ body = (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: theme.danger, children: "ffmpeg / ffprobe not found on your PATH." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "ClipJoin needs ffmpeg to read and join videos. Install it with:" }) }), _jsxs(Text, { color: theme.brand, children: [" ", FFMPEG_INSTALL_HINT] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.muted, children: "Press q to quit, then re-run clip-join." }) })] }));
132
138
  break;
133
139
  case "browse":
134
140
  body = _jsx(BrowseScreen, { onConfirm: (files) => void beginProbe(files), onQuit: exit });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clip-join",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "ClipJoin — an interactive terminal UI for joining videos into one file.",
5
5
  "type": "module",
6
6
  "license": "MIT",