Add a curl/wget one-line installer and popup setup detection

Add install-remote.sh, which fetches the latest release and runs its
install.sh so setup no longer needs a git clone. The popup now probes
the native host with a "ping" action on open and, if it's not
registered yet, shows the one-liner (with copy-to-clipboard) instead
of the auto-capture toggle -- so a Web Store install can be completed
end-to-end by clicking the extension icon and pasting one command.
This commit is contained in:
2026-08-12 15:07:55 +03:00
parent bd24586113
commit 08744915c4
6 changed files with 202 additions and 26 deletions
+19 -10
View File
@@ -14,6 +14,8 @@ browser's built-in downloader.
- Linux with KGet installed and running (or set to autostart) - Linux with KGet installed and running (or set to autostart)
- A Chromium-based browser: Chrome, Chromium, Brave, Vivaldi, or Edge - A Chromium-based browser: Chrome, Chromium, Brave, Vivaldi, or Edge
- `python3`, `dbus-send` - `python3`, `dbus-send`
- `curl` or `wget`, for the one-line remote installer below (skip if you'd
rather download and run `install.sh` yourself)
- `notify-send`, optional, for the completion notification - `notify-send`, optional, for the completion notification
- `kwriteconfig6` (KDE Frameworks), optional, silences KGet's own duplicate - `kwriteconfig6` (KDE Frameworks), optional, silences KGet's own duplicate
notification notification
@@ -49,22 +51,26 @@ without them.)
1. Install the extension from the Chrome Web Store: **`<CHROME_WEB_STORE_URL>`** 1. Install the extension from the Chrome Web Store: **`<CHROME_WEB_STORE_URL>`**
*(placeholder, fill in once the listing is live)*. *(placeholder, fill in once the listing is live)*.
2. Install the native messaging host that lets the extension talk to KGet. 2. Click the extension's toolbar icon. The popup detects that the native
Download and extract `kget-capture-vX.Y.Z.zip` (the source archive, not messaging host isn't set up yet and shows a one-line command; run it in a
`kget-capture-extension-vX.Y.Z.zip`) from the terminal, then click "I ran it, check again" in the popup:
[Releases page](https://github.com/andwati/kget-capture/releases), then
from inside that folder:
```bash ```bash
./install.sh curl -fsSL https://raw.githubusercontent.com/andwati/kget-capture/main/install-remote.sh | bash
``` ```
(or with `wget`: `wget -qO- https://raw.githubusercontent.com/andwati/kget-capture/main/install-remote.sh | bash`)
This downloads the latest release and runs its `install.sh`, which
registers the native host for every supported browser it finds installed
and prints any remaining setup warnings. No git clone required. If you'd
rather run it yourself: download and extract `kget-capture-vX.Y.Z.zip`
(the source archive, not `kget-capture-extension-vX.Y.Z.zip`) from the
[Releases page](https://github.com/andwati/kget-capture/releases), then
from inside that folder run `./install.sh` directly.
3. Open your browser's download settings (`chrome://settings/downloads`, 3. Open your browser's download settings (`chrome://settings/downloads`,
`brave://settings/downloads`, etc.) and turn **off** "Ask where to save `brave://settings/downloads`, etc.) and turn **off** "Ask where to save
each file before downloading". Chrome shows this dialog itself, before the each file before downloading". Chrome shows this dialog itself, before the
extension gets a chance to intercept the download, so this one-time change extension gets a chance to intercept the download, so this one-time change
is required. is required (no extension API can do it for you).
`install.sh` registers the native host for every supported browser it finds
installed and prints any remaining setup warnings.
### Building from source ### Building from source
@@ -131,6 +137,9 @@ Prebuilt releases are published on the
## Troubleshooting ## Troubleshooting
- **`install-remote.sh` fails to look up the latest release?** It queries
GitHub's API, which rate-limits unauthenticated requests (60/hour per IP);
wait a bit, or fall back to the manual `install.sh` steps in Install above.
- **Check native host registration:** confirm - **Check native host registration:** confirm
`~/.config/<browser>/NativeMessagingHosts/com.kget_capture.host.json` exists `~/.config/<browser>/NativeMessagingHosts/com.kget_capture.host.json` exists
and its `path` points at an executable file and its `path` points at an executable file
+36 -1
View File
@@ -1,5 +1,5 @@
body { body {
width: 240px; width: 320px;
padding: 12px; padding: 12px;
font-family: system-ui, sans-serif; font-family: system-ui, sans-serif;
font-size: 13px; font-size: 13px;
@@ -36,3 +36,38 @@ h1 {
font-family: inherit; font-family: inherit;
cursor: pointer; cursor: pointer;
} }
.cmd-row {
display: flex;
align-items: center;
gap: 6px;
margin-top: 8px;
}
.cmd-row code {
flex: 1;
min-width: 0;
overflow-x: auto;
white-space: nowrap;
background: #f0f0f0;
padding: 4px 6px;
border-radius: 3px;
font-size: 11px;
}
.cmd-row button {
flex: none;
padding: 4px 8px;
font-size: 12px;
font-family: inherit;
cursor: pointer;
}
#recheck-setup {
display: block;
margin-top: 10px;
padding: 4px 8px;
font-size: 12px;
font-family: inherit;
cursor: pointer;
}
+26 -11
View File
@@ -6,17 +6,32 @@
</head> </head>
<body> <body>
<h1>KGet Capture</h1> <h1>KGet Capture</h1>
<label class="toggle-row">
<input type="checkbox" id="auto-capture-toggle"> <div id="setup-needed" hidden>
<span>Auto-capture all downloads</span> <p class="hint">One-time setup needed: KGet Capture needs a small local
</label> helper installed before it can talk to KGet. Run this in a terminal:</p>
<p class="status" id="status-line"></p> <div class="cmd-row">
<p class="hint">Right-click any link, image, video, or audio and choose <code id="setup-command"></code>
"Download with KGet" to send it to KGet on demand.</p> <button id="copy-command" type="button">Copy</button>
<p class="hint">Downloads still prompting for a location? Chrome shows that </div>
dialog itself, before this extension can intercept anything. <p class="status" id="copy-feedback"></p>
<button id="open-download-settings" type="button">Turn off Chrome's download prompt</button> <button id="recheck-setup" type="button">I ran it, check again</button>
</p> </div>
<div id="ready" hidden>
<label class="toggle-row">
<input type="checkbox" id="auto-capture-toggle">
<span>Auto-capture all downloads</span>
</label>
<p class="status" id="status-line"></p>
<p class="hint">Right-click any link, image, video, or audio and choose
"Download with KGet" to send it to KGet on demand.</p>
<p class="hint">Downloads still prompting for a location? Chrome shows that
dialog itself, before this extension can intercept anything.
<button id="open-download-settings" type="button">Turn off Chrome's download prompt</button>
</p>
</div>
<script src="popup.js"></script> <script src="popup.js"></script>
</body> </body>
</html> </html>
+50 -3
View File
@@ -1,13 +1,60 @@
const NATIVE_HOST = "com.kget_capture.host";
const INSTALL_COMMAND =
"curl -fsSL https://raw.githubusercontent.com/andwati/kget-capture/main/install-remote.sh | bash";
const setupPanel = document.getElementById("setup-needed");
const readyPanel = document.getElementById("ready");
const checkbox = document.getElementById("auto-capture-toggle"); const checkbox = document.getElementById("auto-capture-toggle");
const statusLine = document.getElementById("status-line"); const statusLine = document.getElementById("status-line");
const copyFeedback = document.getElementById("copy-feedback");
function renderStatus(enabled) { function renderStatus(enabled) {
statusLine.textContent = `Auto-capture: ${enabled ? "ON" : "OFF"}`; statusLine.textContent = `Auto-capture: ${enabled ? "ON" : "OFF"}`;
} }
chrome.storage.local.get({ autoCaptureEnabled: true }).then(({ autoCaptureEnabled }) => { function showReady() {
checkbox.checked = autoCaptureEnabled; setupPanel.hidden = true;
renderStatus(autoCaptureEnabled); readyPanel.hidden = false;
chrome.storage.local.get({ autoCaptureEnabled: true }).then(({ autoCaptureEnabled }) => {
checkbox.checked = autoCaptureEnabled;
renderStatus(autoCaptureEnabled);
});
}
function showSetupNeeded() {
document.getElementById("setup-command").textContent = INSTALL_COMMAND;
readyPanel.hidden = true;
setupPanel.hidden = false;
}
// The native host is only reachable once install.sh (or install-remote.sh)
// has registered it -- sendNativeMessage fails with a lastError like
// "Specified native messaging host not found" until then, which is exactly
// the signal we want to gate the rest of the popup on.
function checkNativeHost() {
chrome.runtime.sendNativeMessage(NATIVE_HOST, { action: "ping" }, () => {
if (chrome.runtime.lastError) {
showSetupNeeded();
} else {
showReady();
}
});
}
checkNativeHost();
document.getElementById("recheck-setup").addEventListener("click", checkNativeHost);
document.getElementById("copy-command").addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(INSTALL_COMMAND);
copyFeedback.textContent = "Copied to clipboard.";
} catch (e) {
copyFeedback.textContent = "Couldn't copy -- select and copy the command manually.";
}
setTimeout(() => {
copyFeedback.textContent = "";
}, 2000);
}); });
checkbox.addEventListener("change", () => { checkbox.addEventListener("change", () => {
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
# Fetches the latest release source archive and runs install.sh from it, so
# setup doesn't require a git clone:
#
# curl -fsSL https://raw.githubusercontent.com/andwati/kget-capture/main/install-remote.sh | bash
# wget -qO- https://raw.githubusercontent.com/andwati/kget-capture/main/install-remote.sh | bash
set -euo pipefail
REPO="andwati/kget-capture"
fetch_stdout() {
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$1"
elif command -v wget >/dev/null 2>&1; then
wget -qO- "$1"
else
echo "ERROR: this installer needs 'curl' or 'wget' on PATH." >&2
exit 1
fi
}
fetch_to_file() {
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$1" -o "$2"
else
wget -qO "$2" "$1"
fi
}
echo "Looking up the latest release..."
tag="$(fetch_stdout "https://api.github.com/repos/$REPO/releases/latest" \
| grep -m1 '"tag_name"' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/')"
if [ -z "$tag" ]; then
echo "ERROR: couldn't determine the latest release tag from GitHub." >&2
exit 1
fi
echo "Latest release: $tag"
if ! command -v python3 >/dev/null 2>&1; then
echo "ERROR: 'python3' is required but not found on PATH." >&2
exit 1
fi
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
zip_path="$tmp_dir/kget-capture.zip"
zip_url="https://github.com/$REPO/releases/download/$tag/kget-capture-$tag.zip"
echo "Downloading $zip_url..."
fetch_to_file "$zip_url" "$zip_path"
echo "Extracting..."
src_dir="$tmp_dir/src"
python3 -c "
import sys, zipfile
zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])
" "$zip_path" "$src_dir"
echo "Running install.sh..."
bash "$src_dir/install.sh"
+11 -1
View File
@@ -154,7 +154,17 @@ def spawn_notifier(transfer_path, dest):
def main(): def main():
message = read_message() message = read_message()
if not message or message.get("action") != "addTransfer" or "url" not in message: if not message:
write_message({"ok": False, "error": "invalid request"})
return
# The extension popup uses this to detect whether the native host is
# installed/registered at all, distinct from whether a transfer succeeds.
if message.get("action") == "ping":
write_message({"ok": True})
return
if message.get("action") != "addTransfer" or "url" not in message:
write_message({"ok": False, "error": "invalid request"}) write_message({"ok": False, "error": "invalid request"})
return return