@shaztech/video-pipeline 1.3.0 → 1.4.0

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.
@@ -9,7 +9,7 @@
9
9
  html, body, #root { height: 100%; width: 100%; overflow: hidden; }
10
10
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #1a1a2e; color: #e0e0e0; }
11
11
  </style>
12
- <script type="module" crossorigin src="./assets/index-yrb6jZba.js"></script>
12
+ <script type="module" crossorigin src="./assets/index-DK9i7gno.js"></script>
13
13
  <link rel="stylesheet" crossorigin href="./assets/index-CUxLpKni.css">
14
14
  </head>
15
15
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shaztech/video-pipeline",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Visual node-based video processing pipeline CLI",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -50,6 +50,8 @@ function buildDrawtextArgs(srcLabel, {
50
50
  fontFile,
51
51
  fontSize = 48,
52
52
  fontColor = 'white',
53
+ borderColor,
54
+ borderWidth,
53
55
  box = false,
54
56
  boxColor = 'black@0.5',
55
57
  padding = 20,
@@ -89,6 +91,11 @@ function buildDrawtextArgs(srcLabel, {
89
91
  `:x=${x}` +
90
92
  `:y=${y}`
91
93
 
94
+ if (borderWidth != null && borderWidth > 0) {
95
+ filterStr += `:borderw=${borderWidth}`
96
+ if (borderColor) filterStr += `:bordercolor=${borderColor}`
97
+ }
98
+
92
99
  if (box) {
93
100
  filterStr += `:box=1:boxcolor=${boxColor}:boxborderw=8`
94
101
  }
@@ -112,6 +119,8 @@ function buildDrawtextArgs(srcLabel, {
112
119
  * @param {string} opts.fontFile - path to a .ttf/.otf/.ttc font file (required)
113
120
  * @param {number} [opts.fontSize=48]
114
121
  * @param {string} [opts.fontColor='white']
122
+ * @param {string} [opts.borderColor] - color of text stroke/outline (e.g. 'black')
123
+ * @param {number} [opts.borderWidth] - width of text stroke in pixels (0 = off)
115
124
  * @param {boolean} [opts.box=false] - draw a semi-transparent background box
116
125
  * @param {string} [opts.boxColor='black@0.5']
117
126
  * @param {number} [opts.padding=20] - px distance from edges (used by presets)
@@ -209,6 +209,8 @@ export async function handleVideoStitcher(node, context, tempRoot, incomingEdges
209
209
  fontFile: sl.fontFile,
210
210
  fontSize: sl.fontSize,
211
211
  fontColor: sl.fontColor,
212
+ borderColor: sl.borderColor,
213
+ borderWidth: sl.borderWidth,
212
214
  box: sl.box,
213
215
  boxColor: sl.boxColor,
214
216
  padding: sl.padding,
@@ -251,6 +253,8 @@ export async function handleVideoStitcher(node, context, tempRoot, incomingEdges
251
253
  fontFile: videoSl.fontFile,
252
254
  fontSize: videoSl.fontSize,
253
255
  fontColor: videoSl.fontColor,
256
+ borderColor: videoSl.borderColor,
257
+ borderWidth: videoSl.borderWidth,
254
258
  box: videoSl.box,
255
259
  boxColor: videoSl.boxColor,
256
260
  padding: videoSl.padding,
@@ -14,27 +14,40 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { readFileSync, writeFileSync, renameSync } from 'fs'
17
+ import { readFileSync, writeFileSync, renameSync, existsSync, statSync } from 'fs'
18
18
  import { execFile } from 'child_process'
19
19
  import path from 'path'
20
20
  import { Router } from 'express'
21
21
 
22
- function nativeBrowse() {
22
+ function resolveStartFolder(from) {
23
+ if (!from || typeof from !== 'string') return null
24
+ try {
25
+ if (!existsSync(from)) return null
26
+ return statSync(from).isDirectory() ? from : path.dirname(from)
27
+ } catch { return null }
28
+ }
29
+
30
+ function nativeBrowse(from) {
31
+ const start = resolveStartFolder(from)
23
32
  return new Promise((resolve) => {
24
33
  const { platform } = process
25
34
  if (platform === 'darwin') {
26
- execFile('osascript', ['-e', 'POSIX path of (choose file)'], (err, stdout) => {
35
+ const loc = start ? ` default location (POSIX file "${start.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}")` : ''
36
+ execFile('osascript', ['-e', `POSIX path of (choose file${loc})`], (err, stdout) => {
27
37
  resolve(err ? null : stdout.trim())
28
38
  })
29
39
  } else if (platform === 'linux') {
30
- execFile('zenity', ['--file-selection'], (err, stdout) => {
40
+ const args = ['--file-selection']
41
+ if (start) args.push(`--filename=${start.endsWith('/') ? start : start + '/'}`)
42
+ execFile('zenity', args, (err, stdout) => {
31
43
  if (!err) return resolve(stdout.trim())
32
- execFile('kdialog', ['--getopenfilename', '.'], (err2, stdout2) => {
44
+ execFile('kdialog', ['--getopenfilename', start || '.'], (err2, stdout2) => {
33
45
  resolve(err2 ? null : stdout2.trim())
34
46
  })
35
47
  })
36
48
  } else if (platform === 'win32') {
37
- const ps = `Add-Type -AssemblyName System.Windows.Forms; $d = New-Object System.Windows.Forms.OpenFileDialog; if ($d.ShowDialog() -eq 'OK') { $d.FileName }`
49
+ const initLine = start ? `$d.InitialDirectory = '${start.replace(/'/g, "''")}'; ` : ''
50
+ const ps = `Add-Type -AssemblyName System.Windows.Forms; $d = New-Object System.Windows.Forms.OpenFileDialog; ${initLine}if ($d.ShowDialog() -eq 'OK') { $d.FileName }`
38
51
  execFile('powershell', ['-Command', ps], (err, stdout) => {
39
52
  resolve(err ? null : stdout.trim())
40
53
  })
@@ -44,22 +57,27 @@ function nativeBrowse() {
44
57
  })
45
58
  }
46
59
 
47
- function nativeBrowseFolder() {
60
+ function nativeBrowseFolder(from) {
61
+ const start = resolveStartFolder(from)
48
62
  return new Promise((resolve) => {
49
63
  const { platform } = process
50
64
  if (platform === 'darwin') {
51
- execFile('osascript', ['-e', 'POSIX path of (choose folder)'], (err, stdout) => {
65
+ const loc = start ? ` default location (POSIX file "${start.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}")` : ''
66
+ execFile('osascript', ['-e', `POSIX path of (choose folder${loc})`], (err, stdout) => {
52
67
  resolve(err ? null : stdout.trim())
53
68
  })
54
69
  } else if (platform === 'linux') {
55
- execFile('zenity', ['--file-selection', '--directory'], (err, stdout) => {
70
+ const args = ['--file-selection', '--directory']
71
+ if (start) args.push(`--filename=${start.endsWith('/') ? start : start + '/'}`)
72
+ execFile('zenity', args, (err, stdout) => {
56
73
  if (!err) return resolve(stdout.trim())
57
- execFile('kdialog', ['--getexistingdirectory', '.'], (err2, stdout2) => {
74
+ execFile('kdialog', ['--getexistingdirectory', start || '.'], (err2, stdout2) => {
58
75
  resolve(err2 ? null : stdout2.trim())
59
76
  })
60
77
  })
61
78
  } else if (platform === 'win32') {
62
- const ps = `Add-Type -AssemblyName System.Windows.Forms; $d = New-Object System.Windows.Forms.FolderBrowserDialog; if ($d.ShowDialog() -eq 'OK') { $d.SelectedPath }`
79
+ const initLine = start ? `$d.SelectedPath = '${start.replace(/'/g, "''")}'; ` : ''
80
+ const ps = `Add-Type -AssemblyName System.Windows.Forms; $d = New-Object System.Windows.Forms.FolderBrowserDialog; ${initLine}if ($d.ShowDialog() -eq 'OK') { $d.SelectedPath }`
63
81
  execFile('powershell', ['-Command', ps], (err, stdout) => {
64
82
  resolve(err ? null : stdout.trim())
65
83
  })
@@ -89,14 +107,16 @@ export function createRoutes(specPath, broadcast) {
89
107
  })
90
108
 
91
109
  // GET /api/browse — open a native OS file picker, return full path
92
- router.get('/api/browse', async (_req, res) => {
93
- const filePath = await nativeBrowse()
110
+ // Optional ?from=<path> opens the picker at that path's parent folder
111
+ router.get('/api/browse', async (req, res) => {
112
+ const filePath = await nativeBrowse(typeof req.query.from === 'string' ? req.query.from : null)
94
113
  res.json({ path: filePath })
95
114
  })
96
115
 
97
116
  // GET /api/browse-folder — open a native OS folder picker, return full path
98
- router.get('/api/browse-folder', async (_req, res) => {
99
- const folderPath = await nativeBrowseFolder()
117
+ // Optional ?from=<path> opens the picker at that path (or its parent if a file)
118
+ router.get('/api/browse-folder', async (req, res) => {
119
+ const folderPath = await nativeBrowseFolder(typeof req.query.from === 'string' ? req.query.from : null)
100
120
  res.json({ path: folderPath })
101
121
  })
102
122