clip-join 1.0.0 → 1.0.1
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 +28 -15
- package/dist/config.js +22 -2
- package/dist/core/output.js +6 -6
- package/dist/index.js +19 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,34 +29,47 @@ flags, no config, just a clean keyboard-driven flow from folder to finished file
|
|
|
29
29
|
Optionally, add a **crossfade transition** between clips (press `t` on the arrange screen).
|
|
30
30
|
It's off by default; enabling it re-encodes the output (transitions can't be done losslessly).
|
|
31
31
|
|
|
32
|
-
Every joined file is written to
|
|
33
|
-
|
|
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`:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
clipjoin --out ~/Desktop # write joined videos to ~/Desktop instead
|
|
40
|
+
```
|
|
34
41
|
|
|
35
42
|
## Install
|
|
36
43
|
|
|
37
|
-
|
|
44
|
+
Install globally with npm — you get a `clipjoin` command anywhere
|
|
38
45
|
(`clip-join` works too, they're the same thing):
|
|
39
46
|
|
|
40
47
|
```bash
|
|
41
|
-
|
|
48
|
+
npm install -g clip-join # then run: clipjoin
|
|
49
|
+
npx clip-join # or run once without installing
|
|
42
50
|
```
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
**ffmpeg is required** (it bundles `ffprobe`) — install it with your package
|
|
53
|
+
manager if you don't have it:
|
|
46
54
|
|
|
47
55
|
```bash
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
brew install ffmpeg # macOS
|
|
57
|
+
sudo apt install ffmpeg # Debian/Ubuntu
|
|
50
58
|
```
|
|
51
59
|
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
<details>
|
|
61
|
+
<summary>Optional: one-line installer</summary>
|
|
62
|
+
|
|
63
|
+
Prefer a single command that also checks Node + ffmpeg for you and fixes PATH?
|
|
54
64
|
|
|
55
65
|
```bash
|
|
56
|
-
|
|
57
|
-
sudo apt install ffmpeg # Debian/Ubuntu
|
|
66
|
+
curl -fsSL https://raw.githubusercontent.com/BrenoHA/clip-join/main/install.sh | bash
|
|
58
67
|
```
|
|
59
68
|
|
|
69
|
+
It just wraps `npm install -g clip-join` with a few pre-flight checks.
|
|
70
|
+
|
|
71
|
+
</details>
|
|
72
|
+
|
|
60
73
|
## Requirements
|
|
61
74
|
|
|
62
75
|
- **Node.js 18+**
|
|
@@ -105,7 +118,7 @@ total duration, size, and whether the join will be lossless):
|
|
|
105
118
|
| `↑ ↓` | Move the cursor |
|
|
106
119
|
| `space` / `Enter` | Toggle a clip in/out of the join |
|
|
107
120
|
| `Shift+↑ ↓` | Reorder the selected clip |
|
|
108
|
-
| `o` | Rename the output file (saved
|
|
121
|
+
| `o` | Rename the output file (saved to the ClipJoin folder) |
|
|
109
122
|
| `t` | Toggle the transition (None / Crossfade) |
|
|
110
123
|
| `j` | **▶ Start the join** |
|
|
111
124
|
| `esc` | Back to the browser |
|
|
@@ -165,8 +178,8 @@ conventions, and the PR process.
|
|
|
165
178
|
slightly different per-clip encoding perfectly. If playback looks off, the clips likely
|
|
166
179
|
need re-encoding (ClipJoin falls back automatically when stream copy fails).
|
|
167
180
|
- **Original files are never modified or deleted.**
|
|
168
|
-
- Output names are always confined to
|
|
169
|
-
reduced to its basename).
|
|
181
|
+
- Output names are always confined to the output folder (a path typed into the rename
|
|
182
|
+
field is reduced to its basename); use `--out <dir>` to change the folder itself.
|
|
170
183
|
|
|
171
184
|
## License
|
|
172
185
|
|
package/dist/config.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os from "node:os";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
export const DEFAULT_EXTS = ["mp4", "mov", "m4v", "avi", "mkv"];
|
|
3
4
|
/**
|
|
@@ -11,8 +12,27 @@ export const TRANSITIONS = [
|
|
|
11
12
|
{ id: "none", label: "None" },
|
|
12
13
|
{ id: "crossfade", label: "Crossfade", xfade: "fade" },
|
|
13
14
|
];
|
|
14
|
-
/**
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Default home for joined videos: a fixed, always-writable folder under the
|
|
17
|
+
* user's home so `clipjoin` behaves identically from any directory (rather than
|
|
18
|
+
* scattering `output/` folders wherever it happens to be launched).
|
|
19
|
+
* macOS → ~/Movies/ClipJoin · Linux/Windows → ~/Videos/ClipJoin
|
|
20
|
+
*/
|
|
21
|
+
export function defaultOutputDir() {
|
|
22
|
+
const parent = process.platform === "darwin" ? "Movies" : "Videos";
|
|
23
|
+
return path.join(os.homedir(), parent, "ClipJoin");
|
|
24
|
+
}
|
|
25
|
+
// The active output directory. Defaults per-platform; overridden at startup by
|
|
26
|
+
// the `--out <dir>` flag via setOutputDir().
|
|
27
|
+
let outputDir = defaultOutputDir();
|
|
28
|
+
/** Absolute directory every joined video (and its chapters file) is written to. */
|
|
29
|
+
export function getOutputDir() {
|
|
30
|
+
return outputDir;
|
|
31
|
+
}
|
|
32
|
+
/** Point output at a specific directory (from `--out`). Resolved to absolute. */
|
|
33
|
+
export function setOutputDir(dir) {
|
|
34
|
+
outputDir = path.resolve(dir);
|
|
35
|
+
}
|
|
16
36
|
export const REENCODE_CRF = 18;
|
|
17
37
|
export const REENCODE_PRESET = "veryfast";
|
|
18
38
|
/**
|
package/dist/core/output.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { getOutputDir, defaultOutputName } from "../config.js";
|
|
4
4
|
import { humanClock } from "./format.js";
|
|
5
5
|
/**
|
|
6
|
-
* Resolve a user-supplied name to an absolute path inside
|
|
7
|
-
* basename is honored, so "../evil.mp4" or an absolute path can't escape
|
|
6
|
+
* Resolve a user-supplied name to an absolute path inside the output dir. Only
|
|
7
|
+
* the basename is honored, so "../evil.mp4" or an absolute path can't escape it.
|
|
8
8
|
*/
|
|
9
9
|
export function resolveOutputPath(name) {
|
|
10
10
|
let base = path.basename(name.trim());
|
|
11
11
|
// basename() strips separators; "." / ".." would still resolve to or above
|
|
12
|
-
//
|
|
12
|
+
// the output dir, so fall back to the default for those (and for an empty name).
|
|
13
13
|
if (!base || base === "." || base === "..")
|
|
14
14
|
base = defaultOutputName();
|
|
15
|
-
return path.join(
|
|
15
|
+
return path.join(getOutputDir(), base);
|
|
16
16
|
}
|
|
17
17
|
export async function ensureOutputDir() {
|
|
18
|
-
await fs.mkdir(
|
|
18
|
+
await fs.mkdir(getOutputDir(), { recursive: true });
|
|
19
19
|
}
|
|
20
20
|
/** Build the line-per-chapter text content for the given (included) clips. */
|
|
21
21
|
function buildChaptersContent(clips) {
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { render } from "ink";
|
|
4
4
|
import { App } from "./ui/App.js";
|
|
5
|
-
|
|
5
|
+
import { setOutputDir } from "./config.js";
|
|
6
|
+
// Minimal arg parsing: one optional positional (start folder) plus `--out <dir>`
|
|
7
|
+
// (aliases: `-o`, `--out=<dir>`) to redirect where joined videos are written.
|
|
8
|
+
let folderArg;
|
|
9
|
+
const argv = process.argv.slice(2);
|
|
10
|
+
for (let i = 0; i < argv.length; i++) {
|
|
11
|
+
const arg = argv[i];
|
|
12
|
+
if (arg === "--out" || arg === "-o") {
|
|
13
|
+
const dir = argv[++i];
|
|
14
|
+
if (dir)
|
|
15
|
+
setOutputDir(dir);
|
|
16
|
+
}
|
|
17
|
+
else if (arg.startsWith("--out=")) {
|
|
18
|
+
setOutputDir(arg.slice("--out=".length));
|
|
19
|
+
}
|
|
20
|
+
else if (!arg.startsWith("-") && folderArg === undefined) {
|
|
21
|
+
folderArg = arg;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
6
24
|
const { waitUntilExit } = render(_jsx(App, { initialFolder: folderArg }));
|
|
7
25
|
await waitUntilExit();
|