@phi-code-admin/camofox-browser 1.0.0 → 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/AGENTS.md +571 -571
- package/Dockerfile +86 -86
- package/LICENSE +21 -21
- package/README.md +691 -691
- package/camofox.config.json +10 -10
- package/lib/auth.js +134 -134
- package/lib/camoufox-executable.js +189 -189
- package/lib/config.js +153 -153
- package/lib/cookies.js +119 -119
- package/lib/downloads.js +168 -168
- package/lib/extract.js +74 -74
- package/lib/fly.js +54 -54
- package/lib/images.js +88 -88
- package/lib/inflight.js +16 -16
- package/lib/launcher.js +47 -47
- package/lib/macros.js +31 -31
- package/lib/metrics.js +184 -184
- package/lib/openapi.js +105 -105
- package/lib/persistence.js +89 -89
- package/lib/plugins.js +178 -175
- package/lib/proxy.js +277 -277
- package/lib/reporter.js +1102 -1102
- package/lib/request-utils.js +59 -59
- package/lib/resources.js +76 -76
- package/lib/snapshot.js +41 -41
- package/lib/tmp-cleanup.js +108 -108
- package/lib/tracing.js +137 -137
- package/openclaw.plugin.json +268 -268
- package/package.json +148 -148
- package/plugin.ts +758 -758
- package/plugins/persistence/AGENTS.md +37 -37
- package/plugins/persistence/README.md +48 -48
- package/plugins/persistence/index.js +124 -124
- package/plugins/vnc/AGENTS.md +42 -42
- package/plugins/vnc/README.md +165 -165
- package/plugins/vnc/apt.txt +7 -7
- package/plugins/vnc/index.js +142 -142
- package/plugins/vnc/spawn.js +8 -8
- package/plugins/vnc/vnc-launcher.js +64 -64
- package/plugins/vnc/vnc-watcher.sh +82 -82
- package/plugins/youtube/AGENTS.md +25 -25
- package/plugins/youtube/apt.txt +1 -1
- package/plugins/youtube/index.js +206 -206
- package/plugins/youtube/post-install.sh +5 -5
- package/plugins/youtube/youtube.js +301 -301
- package/run.sh +37 -37
- package/scripts/exec.js +8 -8
- package/scripts/generate-openapi.js +24 -24
- package/scripts/install-plugin-deps.sh +63 -63
- package/scripts/plugin.js +342 -342
- package/scripts/sync-version.js +25 -25
- package/server.js +6062 -6059
- package/tsconfig.json +12 -12
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VNC launcher -- owns all process spawning and env reads.
|
|
3
|
-
* Isolated from route handlers to keep subprocess management separate.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { spawn } from './spawn.js';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import { fileURLToPath } from 'node:url';
|
|
9
|
-
|
|
10
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Resolve VNC configuration from pluginConfig + env var fallbacks.
|
|
14
|
-
* All process.env reads live here -- callers get a plain config object.
|
|
15
|
-
*/
|
|
16
|
-
export function resolveVncConfig(pluginConfig = {}) {
|
|
17
|
-
const enabled = process.env.ENABLE_VNC === '1' || pluginConfig.enabled === true;
|
|
18
|
-
|
|
19
|
-
const rawResolution = process.env.VNC_RESOLUTION || pluginConfig.resolution || '1920x1080';
|
|
20
|
-
const resolution = rawResolution.includes('x', rawResolution.indexOf('x') + 1)
|
|
21
|
-
? rawResolution
|
|
22
|
-
: `${rawResolution}x24`;
|
|
23
|
-
|
|
24
|
-
const vncPassword = process.env.VNC_PASSWORD || pluginConfig.password || '';
|
|
25
|
-
const viewOnly = process.env.VIEW_ONLY === '1' || pluginConfig.viewOnly === true;
|
|
26
|
-
const vncPort = process.env.VNC_PORT || pluginConfig.vncPort || '5900';
|
|
27
|
-
const novncPort = process.env.NOVNC_PORT || pluginConfig.novncPort || '6080';
|
|
28
|
-
|
|
29
|
-
return { enabled, resolution, vncPassword, viewOnly, vncPort, novncPort };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Start the vnc-watcher.sh child process.
|
|
34
|
-
* Returns the spawned ChildProcess.
|
|
35
|
-
*/
|
|
36
|
-
export function startWatcher({ resolution, vncPassword, viewOnly, vncPort, novncPort, log, events }) {
|
|
37
|
-
const watcherPath = path.join(__dirname, 'vnc-watcher.sh');
|
|
38
|
-
const watcher = spawn('sh', [watcherPath], {
|
|
39
|
-
env: {
|
|
40
|
-
...process.env,
|
|
41
|
-
VNC_PASSWORD: vncPassword,
|
|
42
|
-
VNC_RESOLUTION: resolution,
|
|
43
|
-
VIEW_ONLY: viewOnly ? '1' : '0',
|
|
44
|
-
VNC_PORT: String(vncPort),
|
|
45
|
-
NOVNC_PORT: String(novncPort),
|
|
46
|
-
},
|
|
47
|
-
stdio: ['ignore', 'inherit', 'inherit'],
|
|
48
|
-
detached: false,
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
watcher.on('error', (err) => {
|
|
52
|
-
log('error', 'vnc watcher failed to start', { error: err.message });
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
watcher.on('exit', (code, signal) => {
|
|
56
|
-
log('warn', 'vnc watcher exited', { code, signal });
|
|
57
|
-
events.emit('vnc:watcher:stopped', { code, signal });
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
log('info', 'vnc watcher started', { pid: watcher.pid });
|
|
61
|
-
events.emit('vnc:watcher:started', { pid: watcher.pid });
|
|
62
|
-
|
|
63
|
-
return watcher;
|
|
64
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* VNC launcher -- owns all process spawning and env reads.
|
|
3
|
+
* Isolated from route handlers to keep subprocess management separate.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { spawn } from './spawn.js';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resolve VNC configuration from pluginConfig + env var fallbacks.
|
|
14
|
+
* All process.env reads live here -- callers get a plain config object.
|
|
15
|
+
*/
|
|
16
|
+
export function resolveVncConfig(pluginConfig = {}) {
|
|
17
|
+
const enabled = process.env.ENABLE_VNC === '1' || pluginConfig.enabled === true;
|
|
18
|
+
|
|
19
|
+
const rawResolution = process.env.VNC_RESOLUTION || pluginConfig.resolution || '1920x1080';
|
|
20
|
+
const resolution = rawResolution.includes('x', rawResolution.indexOf('x') + 1)
|
|
21
|
+
? rawResolution
|
|
22
|
+
: `${rawResolution}x24`;
|
|
23
|
+
|
|
24
|
+
const vncPassword = process.env.VNC_PASSWORD || pluginConfig.password || '';
|
|
25
|
+
const viewOnly = process.env.VIEW_ONLY === '1' || pluginConfig.viewOnly === true;
|
|
26
|
+
const vncPort = process.env.VNC_PORT || pluginConfig.vncPort || '5900';
|
|
27
|
+
const novncPort = process.env.NOVNC_PORT || pluginConfig.novncPort || '6080';
|
|
28
|
+
|
|
29
|
+
return { enabled, resolution, vncPassword, viewOnly, vncPort, novncPort };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Start the vnc-watcher.sh child process.
|
|
34
|
+
* Returns the spawned ChildProcess.
|
|
35
|
+
*/
|
|
36
|
+
export function startWatcher({ resolution, vncPassword, viewOnly, vncPort, novncPort, log, events }) {
|
|
37
|
+
const watcherPath = path.join(__dirname, 'vnc-watcher.sh');
|
|
38
|
+
const watcher = spawn('sh', [watcherPath], {
|
|
39
|
+
env: {
|
|
40
|
+
...process.env,
|
|
41
|
+
VNC_PASSWORD: vncPassword,
|
|
42
|
+
VNC_RESOLUTION: resolution,
|
|
43
|
+
VIEW_ONLY: viewOnly ? '1' : '0',
|
|
44
|
+
VNC_PORT: String(vncPort),
|
|
45
|
+
NOVNC_PORT: String(novncPort),
|
|
46
|
+
},
|
|
47
|
+
stdio: ['ignore', 'inherit', 'inherit'],
|
|
48
|
+
detached: false,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
watcher.on('error', (err) => {
|
|
52
|
+
log('error', 'vnc watcher failed to start', { error: err.message });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
watcher.on('exit', (code, signal) => {
|
|
56
|
+
log('warn', 'vnc watcher exited', { code, signal });
|
|
57
|
+
events.emit('vnc:watcher:stopped', { code, signal });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
log('info', 'vnc watcher started', { pid: watcher.pid });
|
|
61
|
+
events.emit('vnc:watcher:started', { pid: watcher.pid });
|
|
62
|
+
|
|
63
|
+
return watcher;
|
|
64
|
+
}
|
|
@@ -1,82 +1,82 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# VNC watcher: detects Camoufox's dynamically-assigned Xvfb display and attaches
|
|
3
|
-
# x11vnc + noVNC to it. Handles browser restarts (re-attaches on display change).
|
|
4
|
-
#
|
|
5
|
-
# Called by the VNC plugin via child_process.spawn. Not meant to run standalone.
|
|
6
|
-
#
|
|
7
|
-
# Env vars (set by the plugin):
|
|
8
|
-
# VNC_PASSWORD If set, x11vnc requires this password
|
|
9
|
-
# VIEW_ONLY "1" for view-only mode
|
|
10
|
-
# VNC_PORT VNC port (default: 5900)
|
|
11
|
-
# NOVNC_PORT noVNC websocket port (default: 6080)
|
|
12
|
-
|
|
13
|
-
set -e
|
|
14
|
-
|
|
15
|
-
VNC_PORT="${VNC_PORT:-5900}"
|
|
16
|
-
NOVNC_PORT="${NOVNC_PORT:-6080}"
|
|
17
|
-
VNC_RESOLUTION="${VNC_RESOLUTION:-1920x1080x24}"
|
|
18
|
-
|
|
19
|
-
log() { printf '[vnc-watcher] %s\n' "$*" >&2; }
|
|
20
|
-
|
|
21
|
-
CURRENT_DISPLAY=""
|
|
22
|
-
X11VNC_PID=""
|
|
23
|
-
|
|
24
|
-
# Prepare password file if requested
|
|
25
|
-
PASSFILE=""
|
|
26
|
-
if [ -n "${VNC_PASSWORD:-}" ]; then
|
|
27
|
-
mkdir -p /tmp/.vnc
|
|
28
|
-
x11vnc -storepasswd "$VNC_PASSWORD" /tmp/.vnc/passwd >/dev/null 2>&1
|
|
29
|
-
PASSFILE="/tmp/.vnc/passwd"
|
|
30
|
-
log "x11vnc: password protected"
|
|
31
|
-
else
|
|
32
|
-
log "x11vnc: NO password (bind $NOVNC_PORT to 127.0.0.1 on host + SSH tunnel)"
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
# Start noVNC (websockify) -- proxies to x11vnc regardless of whether it's up yet
|
|
36
|
-
NOVNC_DIR="/usr/share/novnc"
|
|
37
|
-
if [ ! -d "$NOVNC_DIR" ]; then
|
|
38
|
-
log "ERROR: $NOVNC_DIR not found; noVNC cannot start"
|
|
39
|
-
exit 1
|
|
40
|
-
fi
|
|
41
|
-
VNC_BIND="${VNC_BIND:-127.0.0.1}"
|
|
42
|
-
log "Starting noVNC (websockify) on $VNC_BIND:$NOVNC_PORT -> 127.0.0.1:$VNC_PORT"
|
|
43
|
-
websockify --web "$NOVNC_DIR" "$VNC_BIND:$NOVNC_PORT" "127.0.0.1:$VNC_PORT" >/var/log/novnc.log 2>&1 &
|
|
44
|
-
|
|
45
|
-
log "VNC watcher started -- will attach x11vnc when Camoufox's Xvfb appears"
|
|
46
|
-
|
|
47
|
-
while true; do
|
|
48
|
-
# Find Xvfb with our patched resolution
|
|
49
|
-
FOUND=$(ps -eo args= 2>/dev/null | awk -v res="$VNC_RESOLUTION" '
|
|
50
|
-
/\/Xvfb :[0-9]+/ && index($0, res) {
|
|
51
|
-
for (i=1;i<=NF;i++) if ($i ~ /^:[0-9]+$/) { print $i; exit }
|
|
52
|
-
}
|
|
53
|
-
' | head -1)
|
|
54
|
-
|
|
55
|
-
if [ -n "$FOUND" ] && [ "$FOUND" != "$CURRENT_DISPLAY" ]; then
|
|
56
|
-
# New or changed display -- (re)attach x11vnc
|
|
57
|
-
if [ -n "$X11VNC_PID" ] && kill -0 "$X11VNC_PID" 2>/dev/null; then
|
|
58
|
-
log "Camoufox display changed ($CURRENT_DISPLAY -> $FOUND), restarting x11vnc"
|
|
59
|
-
kill "$X11VNC_PID" 2>/dev/null || true
|
|
60
|
-
sleep 0.5
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
CURRENT_DISPLAY="$FOUND"
|
|
64
|
-
log "Attaching x11vnc to DISPLAY=$CURRENT_DISPLAY"
|
|
65
|
-
|
|
66
|
-
X11VNC_ARGS="-display $CURRENT_DISPLAY -forever -shared -rfbport $VNC_PORT -noxdamage -quiet -bg -o /var/log/x11vnc.log"
|
|
67
|
-
[ "${VIEW_ONLY:-0}" = "1" ] && X11VNC_ARGS="$X11VNC_ARGS -viewonly"
|
|
68
|
-
if [ -n "$PASSFILE" ]; then
|
|
69
|
-
X11VNC_ARGS="$X11VNC_ARGS -rfbauth $PASSFILE"
|
|
70
|
-
else
|
|
71
|
-
X11VNC_ARGS="$X11VNC_ARGS -nopw"
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
# shellcheck disable=SC2086
|
|
75
|
-
x11vnc $X11VNC_ARGS
|
|
76
|
-
sleep 1
|
|
77
|
-
X11VNC_PID=$(pgrep -f "x11vnc.*-display $CURRENT_DISPLAY" | head -1)
|
|
78
|
-
log "x11vnc running (pid=$X11VNC_PID) on DISPLAY=$CURRENT_DISPLAY"
|
|
79
|
-
fi
|
|
80
|
-
|
|
81
|
-
sleep 2
|
|
82
|
-
done
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# VNC watcher: detects Camoufox's dynamically-assigned Xvfb display and attaches
|
|
3
|
+
# x11vnc + noVNC to it. Handles browser restarts (re-attaches on display change).
|
|
4
|
+
#
|
|
5
|
+
# Called by the VNC plugin via child_process.spawn. Not meant to run standalone.
|
|
6
|
+
#
|
|
7
|
+
# Env vars (set by the plugin):
|
|
8
|
+
# VNC_PASSWORD If set, x11vnc requires this password
|
|
9
|
+
# VIEW_ONLY "1" for view-only mode
|
|
10
|
+
# VNC_PORT VNC port (default: 5900)
|
|
11
|
+
# NOVNC_PORT noVNC websocket port (default: 6080)
|
|
12
|
+
|
|
13
|
+
set -e
|
|
14
|
+
|
|
15
|
+
VNC_PORT="${VNC_PORT:-5900}"
|
|
16
|
+
NOVNC_PORT="${NOVNC_PORT:-6080}"
|
|
17
|
+
VNC_RESOLUTION="${VNC_RESOLUTION:-1920x1080x24}"
|
|
18
|
+
|
|
19
|
+
log() { printf '[vnc-watcher] %s\n' "$*" >&2; }
|
|
20
|
+
|
|
21
|
+
CURRENT_DISPLAY=""
|
|
22
|
+
X11VNC_PID=""
|
|
23
|
+
|
|
24
|
+
# Prepare password file if requested
|
|
25
|
+
PASSFILE=""
|
|
26
|
+
if [ -n "${VNC_PASSWORD:-}" ]; then
|
|
27
|
+
mkdir -p /tmp/.vnc
|
|
28
|
+
x11vnc -storepasswd "$VNC_PASSWORD" /tmp/.vnc/passwd >/dev/null 2>&1
|
|
29
|
+
PASSFILE="/tmp/.vnc/passwd"
|
|
30
|
+
log "x11vnc: password protected"
|
|
31
|
+
else
|
|
32
|
+
log "x11vnc: NO password (bind $NOVNC_PORT to 127.0.0.1 on host + SSH tunnel)"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Start noVNC (websockify) -- proxies to x11vnc regardless of whether it's up yet
|
|
36
|
+
NOVNC_DIR="/usr/share/novnc"
|
|
37
|
+
if [ ! -d "$NOVNC_DIR" ]; then
|
|
38
|
+
log "ERROR: $NOVNC_DIR not found; noVNC cannot start"
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
VNC_BIND="${VNC_BIND:-127.0.0.1}"
|
|
42
|
+
log "Starting noVNC (websockify) on $VNC_BIND:$NOVNC_PORT -> 127.0.0.1:$VNC_PORT"
|
|
43
|
+
websockify --web "$NOVNC_DIR" "$VNC_BIND:$NOVNC_PORT" "127.0.0.1:$VNC_PORT" >/var/log/novnc.log 2>&1 &
|
|
44
|
+
|
|
45
|
+
log "VNC watcher started -- will attach x11vnc when Camoufox's Xvfb appears"
|
|
46
|
+
|
|
47
|
+
while true; do
|
|
48
|
+
# Find Xvfb with our patched resolution
|
|
49
|
+
FOUND=$(ps -eo args= 2>/dev/null | awk -v res="$VNC_RESOLUTION" '
|
|
50
|
+
/\/Xvfb :[0-9]+/ && index($0, res) {
|
|
51
|
+
for (i=1;i<=NF;i++) if ($i ~ /^:[0-9]+$/) { print $i; exit }
|
|
52
|
+
}
|
|
53
|
+
' | head -1)
|
|
54
|
+
|
|
55
|
+
if [ -n "$FOUND" ] && [ "$FOUND" != "$CURRENT_DISPLAY" ]; then
|
|
56
|
+
# New or changed display -- (re)attach x11vnc
|
|
57
|
+
if [ -n "$X11VNC_PID" ] && kill -0 "$X11VNC_PID" 2>/dev/null; then
|
|
58
|
+
log "Camoufox display changed ($CURRENT_DISPLAY -> $FOUND), restarting x11vnc"
|
|
59
|
+
kill "$X11VNC_PID" 2>/dev/null || true
|
|
60
|
+
sleep 0.5
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
CURRENT_DISPLAY="$FOUND"
|
|
64
|
+
log "Attaching x11vnc to DISPLAY=$CURRENT_DISPLAY"
|
|
65
|
+
|
|
66
|
+
X11VNC_ARGS="-display $CURRENT_DISPLAY -forever -shared -rfbport $VNC_PORT -noxdamage -quiet -bg -o /var/log/x11vnc.log"
|
|
67
|
+
[ "${VIEW_ONLY:-0}" = "1" ] && X11VNC_ARGS="$X11VNC_ARGS -viewonly"
|
|
68
|
+
if [ -n "$PASSFILE" ]; then
|
|
69
|
+
X11VNC_ARGS="$X11VNC_ARGS -rfbauth $PASSFILE"
|
|
70
|
+
else
|
|
71
|
+
X11VNC_ARGS="$X11VNC_ARGS -nopw"
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# shellcheck disable=SC2086
|
|
75
|
+
x11vnc $X11VNC_ARGS
|
|
76
|
+
sleep 1
|
|
77
|
+
X11VNC_PID=$(pgrep -f "x11vnc.*-display $CURRENT_DISPLAY" | head -1)
|
|
78
|
+
log "x11vnc running (pid=$X11VNC_PID) on DISPLAY=$CURRENT_DISPLAY"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
sleep 2
|
|
82
|
+
done
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
# YouTube Plugin — Agent Guide
|
|
2
|
-
|
|
3
|
-
Extracts video transcripts via yt-dlp (preferred) with Playwright browser fallback.
|
|
4
|
-
|
|
5
|
-
## Endpoint
|
|
6
|
-
|
|
7
|
-
`POST /youtube/transcript` — unauthenticated by default (set `"auth": true` in plugin config to require auth).
|
|
8
|
-
|
|
9
|
-
## Key Files
|
|
10
|
-
|
|
11
|
-
- `index.js` — route handler + browser fallback logic
|
|
12
|
-
- `youtube.js` — yt-dlp process management + transcript parsing (`child_process` isolated here)
|
|
13
|
-
- `youtube.test.js` — parser unit tests
|
|
14
|
-
- `apt.txt` — system deps (python3-minimal for yt-dlp)
|
|
15
|
-
- `post-install.sh` — downloads yt-dlp binary
|
|
16
|
-
|
|
17
|
-
## Code Separation
|
|
18
|
-
|
|
19
|
-
`child_process` is in `youtube.js`, route handlers are in `index.js` — separate files per project conventions.
|
|
20
|
-
|
|
21
|
-
## Maintainers
|
|
22
|
-
|
|
23
|
-
- [@pradeepe](https://github.com/pradeepe) — extracted from core into plugin system
|
|
24
|
-
|
|
25
|
-
For PRs touching this plugin, tag the maintainers above for review.
|
|
1
|
+
# YouTube Plugin — Agent Guide
|
|
2
|
+
|
|
3
|
+
Extracts video transcripts via yt-dlp (preferred) with Playwright browser fallback.
|
|
4
|
+
|
|
5
|
+
## Endpoint
|
|
6
|
+
|
|
7
|
+
`POST /youtube/transcript` — unauthenticated by default (set `"auth": true` in plugin config to require auth).
|
|
8
|
+
|
|
9
|
+
## Key Files
|
|
10
|
+
|
|
11
|
+
- `index.js` — route handler + browser fallback logic
|
|
12
|
+
- `youtube.js` — yt-dlp process management + transcript parsing (`child_process` isolated here)
|
|
13
|
+
- `youtube.test.js` — parser unit tests
|
|
14
|
+
- `apt.txt` — system deps (python3-minimal for yt-dlp)
|
|
15
|
+
- `post-install.sh` — downloads yt-dlp binary
|
|
16
|
+
|
|
17
|
+
## Code Separation
|
|
18
|
+
|
|
19
|
+
`child_process` is in `youtube.js`, route handlers are in `index.js` — separate files per project conventions.
|
|
20
|
+
|
|
21
|
+
## Maintainers
|
|
22
|
+
|
|
23
|
+
- [@pradeepe](https://github.com/pradeepe) — extracted from core into plugin system
|
|
24
|
+
|
|
25
|
+
For PRs touching this plugin, tag the maintainers above for review.
|
package/plugins/youtube/apt.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
python3-minimal
|
|
1
|
+
python3-minimal
|