Users naturally expect the "Current version" label in chrome://extensions
to match the version shown inside the MediaGo window. Tie both to a single
source of truth: `apps/electron/app/package.json`, which Electron's build
pipeline (`apps/electron/tsdown.config.ts`) already reads and exposes as
`process.env.APP_VERSION`.
`manifest.config.ts` now imports the same file and:
- `version` — stripped down to the numeric prefix (`split(/[-+]/)[0]`)
so Chrome accepts it. Chrome's manifest parser only
allows 1–4 dot-separated integers; SemVer pre-release
or build suffixes (`-beta.2`, `+build.7`) get rejected
at install time.
- `version_name` — keeps the full SemVer string. It's free-form, not
used for update-ordering, and is what chrome://extensions
displays when present.
Effect: bumping `apps/electron/app/package.json` once is enough to
re-stamp both the Desktop binary and the bundled extension manifest.
The extension's own `packages/mediago-extension/package.json` is a
private workspace package — its `version` field is unused by the
manifest and intentionally left alone to avoid a pointless sync step
on every release.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Go Core already broadcast a `download-create` SSE event whenever
`/api/downloads` POST lands (UI, browser extension, Docker clients —
any route). The UI listened, but only to bump the sidebar badge via
`useDownloadStore.increase()`. The SWR list fetched by `useTasks`
stayed stale until the user refreshed manually, so tasks imported
through the extension's desktop-http / docker-http modes never
appeared without a page reload.
Fan the same SSE event through the existing `dispatchDownload`
pipeline so `useTasks` can mutate its SWR cache alongside the other
lifecycle events (success / failed / stopped).
- `packages/shared/common/src/types/index.ts`: new
`DownloadCreatedEvent` (`type: "created"`, carries `{ ids, count }`).
- `apps/ui/src/api/events.ts`: existing `download-create` handler now
additionally calls `dispatchDownload({ type: "created", ... })`
after its badge-increment side effect. Parses `ids` from the
payload for downstream filtering if anyone needs it later.
- `apps/ui/src/hooks/use-tasks.ts`: adds `isCreatedEvent` type guard
and calls `mutate()` on hit, same pattern as the other events.
Internal task creation (UI form submit) is untouched — it still
refreshes via its own local mutate. The SSE path only matters for
tasks that show up out-of-band.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
If a stray MediaGo instance (typically the installed Desktop build
running in the background from the Start menu) already holds the
preferred port, a freshly-spawned child fails to bind and exits
immediately — but `waitForHealthy()` happily answers green because
the *other* instance's `/healthy` responds on the same port.
ServiceRunner then considers the service "started" and every
subsequent request silently lands on the wrong process. In practice
this surfaced as dev mode talking to the installed Go Core and
reporting a stale gopeed binary path, even though the dev build had
already been rebuilt with aria2c.
Pre-check: before spawning, open and close a throwaway TCP listener
on `<bindHost>:<preferredPort>`. If the bind fails, throw a
human-readable error instead of proceeding. The tiny check-vs-bind
race window is acceptable for catching the real-world "long-running
stray instance" case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ServiceRunner used to pass the detected LAN IPv4 as the `HOST` env var
when `internal: false`, so the Go Core bound *only* to that LAN IP —
requests to 127.0.0.1 / localhost were refused. That blocked the
browser-extension's `desktop-http` mode (fixed at `http://127.0.0.1`).
Split bind host from display host:
- Bind (`HOST` env to child): `0.0.0.0` when `internal: false`, else
`127.0.0.1`. Listener now accepts traffic on every interface.
- Display (`state.host` / `getURL()`): keeps the detected LAN IPv4
when `internal: false` so the settings UI can surface a shareable
LAN URL, falls back to 127.0.0.1 otherwise.
Net effect: Desktop is reachable via 127.0.0.1, localhost, and its LAN
IP simultaneously, while the UI still shows the LAN address.
Also translates all remaining Chinese JSDoc / inline comments in
`index.ts` and `utils.ts` to English, matching the rest of the package.
Behaviour otherwise unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ship the MediaGo browser extension alongside the Desktop installer so
users can "Load unpacked" it from Chrome / Edge without building the
repo or downloading a separate zip. Adds a discovery path from inside
the app, a three-language doc page, and fixes the Desktop HTTP port
the extension was pointing at.
Packaging:
- `pnpm build:electron` now also builds `@mediago/extension` (turbo
filter), and `apps/electron/scripts/build.ts` copies the dist to
`app/build/extension/` and declares it in electron-builder
`extraResources` → installers land `resources/extension/`.
Runtime:
- `resolveExtensionDir()` in binaryResolver — dev points at the
monorepo dist, prod at `process.resourcesPath/extension`, env
override via `MEDIAGO_EXTENSION_DIR`.
- New IPC `app.getExtensionDir()` returns the resolved path. UI pairs
it with the existing `shell.open()` (no dedicated open-folder IPC)
so the pattern matches configDir / binDir / localDir.
Settings UI:
- New "Browser extension directory" button in Settings → More Settings,
right next to the existing folder shortcuts. Web/server mode hides
the row behind `isWeb` and the stub returns "".
- New i18n key `extensionDir` in shared zh/en resources; duplicate
`currentVersion` key removed from zh.ts along the way.
Port fix:
- Extension `DESKTOP_HTTP_BASE` corrected from `:9900` → `:39719` to
match the Electron-side `preferredPort`. 9900 is the standalone
Go Core / web-server port — two separate deployments.
Docs:
- New `docs/extension.md` + `docs/en/extension.md` + `docs/jp/extension.md`
covering what the extension does, how to install the unpacked
build, the three dispatch modes (schema / desktop-http / docker-http),
import-behaviour toggles, language switching, and common pitfalls.
- Sidebar entries registered in `.vitepress/config.ts` for all three
locales.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The MediaGo extension shipped Chinese-only (50+ hardcoded strings across
popup, options, background). Wire up i18next + react-i18next so the UI
follows the browser language by default and lets the user override to
zh / en in a new LanguageCard on the options page.
- `ExtensionSettings.language: "system" | "zh" | "en"` (default system),
persisted via existing `chrome.storage.local`
- `resolveLanguage()` maps system -> `chrome.i18n.getUILanguage()` or
navigator.language with a zh/en fallback
- Bootstrap one i18next instance per page (popup / options), subscribe
to `chrome.storage.onChanged` so switching on one surface updates the
other without a reload
- Background service worker emits `LocalizedMessage` descriptors
(`{ key, values? }`) for toast copy; popup calls `t(key, values)` so
the SW never has to carry an i18n instance
- Full zh/en coverage for popup, options, error paths
- Chrome-native `_locales/{en,zh_CN}/messages.json` + `__MSG_*__` for
`manifest.name` / `description` / `action.default_title` so the
chrome://extensions row and toolbar tooltip follow the browser UI
language (independent dimension from the in-UI toggle, by design)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The useUrlInvoke hook at apps/ui/src/hooks/use-url-invoke.ts reads
both `silent` and `downloadNow` query params; they were previously
hard-coded on the extension side. Surface them as user toggles so
Schema-mode users can opt into "open the download form for review"
and both modes can opt into "start downloading immediately instead
of just queuing".
- ExtensionSettings gains `downloadNow` (default false) and
`schemaSilent` (default true).
- importViaHttp threads `startDownload` into the POST body.
- buildTaskDeeplink writes `silent=1` / `downloadNow=1` into the
deeplink URL based on the flags (falsy => omit the key, matching
the truthy-check in useUrlInvoke).
- New headless Switch component (no @radix-ui/react-switch dep).
- New ImportBehaviourCard in options, with a patch-style save hook
so each toggle persists immediately.
- use-options.ts now merges on top of the current persisted settings
so the server-config card doesn't wipe the new fields.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A Manifest V3 extension that sniffs downloadable video / audio URLs
across every site the user visits and hands captured sources to a
MediaGo server in one click. Ships unlisted (load-unpacked .zip), no
Chrome Web Store.
Dispatch modes (user-picked in options, no silent fallback):
- desktop-schema: navigates the current tab to
`mediago-community://index.html/?n=1&silent=1&url=…` using the
cat-catch `chrome.tabs.update` pattern. Reuses the existing
`useUrlInvoke` hook in apps/ui — no new deeplink plumbing.
- desktop-http: POST http://127.0.0.1:9900/api/downloads against the
Go Core bundled in a running Desktop.
- docker-http: same POST against a user-configured host, with an
optional X-API-Key header for `--enable-auth` deployments.
UI is React 19 + Tailwind v4 + shadcn/ui (new-york, neutral), matched
to apps/ui's stack. Popup shows per-tab sources with a red badge count;
options page has a 3-mode radio + per-mode field panel.
Supporting changes:
- Abstract sniff filter rules into @mediago/shared-common/sniff and
point the Electron sniffing helper at the same exports so desktop
and extension stay in lock-step.
- Tighten the YouTube host rule to actual video / short / live / embed
URLs (drops the homepage and feed false-positives).
- Fix the macOS electron-builder config: CFBundleURLSchemes was
hard-coded to a test string "mediagoaaa" — now sourced from
process.env.APP_NAME (mediago-community in .env) like everywhere
else. Also add a top-level `protocols` entry for clarity.
- Vite build pulls APP_NAME from the repo root .env via loadEnv() +
`define`, so the scheme name stays single-sourced.
- Root scripts: `pnpm dev:extension`, `pnpm build:extension`,
`pnpm pack:extension` (+ scripts/pack-extension.ts that cross-
platform-zips dist/ into release/).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Resolve AppStore.language at apply-time via a shared resolveAppLanguage
helper so both renderer (navigator.language) and Electron main
(app.getLocale) follow the actual OS locale instead of silently
falling back to zh when the stored value is "system".
- Settings page: horizontal form with a fixed label column, borderless
cards flowing naturally in a 2-column CSS columns layout; drop
width="xl" on inputs and wrap button groups to fix English-label
overflow.
- scripts/download-deps: add a PowerShell fallback for zip extraction
on Windows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rewrite README.md in idiomatic English as the default language
- Move original Chinese README to README.zh.md, delete README.en.md
- Polish ~80 UI translation strings in en.ts for natural English
- Fix hardcoded Chinese in Skills setup commands (setting-page)
- Fix mismatched translation key: reppeatPassword → repeatPassword
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add youtube download type with yt-dlp schema (CLI args, progress regex)
- Replace 6 individual --xxx-bin flags with single --deps-dir flag
- Add BinaryNames map in Go core to auto-resolve binary paths from deps dir
- Simplify Electron/Server binary resolvers to return depsDir only
- Add yt-dlp to deps-versions.json (v2026.03.17)
- Add YouTube option to UI download form with URL auto-detection
- Fix download-terminal.tsx writing [object Object] instead of log content
- Improve error messages when binary not found (include path and type)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FloatButton was effectively dead — it listened for "webview-link-message" which
no one sends after the IPC namespace refactoring, and its functionality is fully
covered by BrowserViewPanel in the main UI. Remove it along with jQuery, Lit and
nanoid dependencies. Consolidate remaining BilibiliButton into a single-file
vanilla custom element, replace fragile global-index DOM lookup with
closest()-based card-relative queries, and replace setInterval polling with
MutationObserver. Build output reduced from 607KB to 1.9KB.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract shared navigation logic into useBrowserActions hook, fix race conditions
in sniffing helper, add event listener cleanup in webview service, split store
selectors for granular re-renders, memoize list items, throttle ResizeObserver,
and add loading/error states to favorites list. Also remove unused getMachineId
IPC handler and fix IPC callback type signatures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of capturing a screenshot, transferring a large DataURL via IPC,
and hiding the browser WebContentsView, use a separate overlay
WebContentsView layered above the browser view to render the download
form. This eliminates flicker, avoids potential page state loss, and
removes the inefficient screenshot transfer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add mediago-core (caorushizi/mediago-core) as a fourth download type
alongside m3u8, bilibili, and direct. This enables HLS/DASH streaming
downloads using the custom Go-based downloader.
Changes span the full integration path: dependency download config,
Go Core backend (type, schema, CLI flag, binary map), binary resolution
for Electron/Server, shared TypeScript types, i18n labels, UI dropdown,
dev scripts, and Dockerfile.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Skills:
- Rewrite SKILL.md as OpenClaw Skill (remove scripts dependency)
- Guide users to install MediaGo + initialize config in one message
- Support Chinese and English natural language commands
- Remove mediago-api.sh (use curl directly, cross-platform)
Docs:
- Add OpenClaw Skill page (zh/en/jp) with install, config, usage guide
- Add to VitePress sidebar for all 3 languages
Settings UI:
- Add "Skills 设置" tab with install command + init command
- Electron mode: init shows URL only
- Docker mode: init shows URL + API key
- One-click copy buttons
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove dynamic port finding via portfinder. ServiceRunner now uses
preferredPort directly as the fixed port. Electron mode uses port 39719.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously the frontend hashed passwords with MD5 + a hardcoded salt
before sending to the backend. This exposed the salt in client-side JS
and used an insecure hash algorithm.
Backend changes:
- Setup: accepts plain password, bcrypt hashes it, generates UUID apiKey
- Signin: accepts plain password, bcrypt verifies, returns stored apiKey
- Status: checks passwordHash instead of apiKey
- AppStore: new passwordHash field
- Middleware: add X-API-Key header support (was only reading Authorization)
- CORS: allow X-API-Key header
Frontend changes:
- Signin page: send plain password, receive apiKey from response
- Remove md5/crypto-js dependency and APIKEY_SALT_KEY constant
- api/download-task: auto-inject localPath/deleteSegments from store
(was lost during go-adapter migration, caused EOF errors)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Use Go build tag `dev` to conditionally compile swagger docs and routes.
Production binaries no longer include swaggo/files (swagger UI assets):
- mediago-core: 34 MB → 24 MB (-10 MB, -29%)
- mediago-player: 29 MB → 13 MB (-16 MB, -55%)
Dev builds (-tags dev) retain full swagger at /swagger and /docs.
2. Set executable permission (0o755) on built binaries (non-Windows).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Redesign the "Add File" flow as a modal dialog containing:
- File path field with "Browse" button (opens native file picker)
- Output format selector (Video: MP4/MKV/WebM, Audio: MP3/AAC/FLAC/WAV)
- Quality selector (High/Medium/Low)
- Two action buttons: "Add to List" (saves for later) and
"Convert Now" (saves and starts conversion immediately)
Also fix silent failure when adding files — getFileName() used
new URL() which throws on local file paths. Replaced with simple
path.split() extraction and added try-catch error handling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move ffmpeg execution from Electron to Go Core so conversion works in
both Electron and web server modes.
Go Core backend:
- Add -ffmpeg-bin CLI flag to pass ffmpeg binary path
- Enhance Conversion DB model with status, outputPath, outputFormat,
quality, progress, and error fields
- Create converter executor (service/converter.go) that spawns ffmpeg,
builds args per format/quality, and parses stderr for progress
- Add StartConversion/StopConversion to service with SSE events for
real-time progress (conversion-start/progress/success/failed/stop)
- Add POST /api/conversions/:id/start and /:id/stop endpoints
Supported formats:
- Video: MP4 (H.264), MKV (H.264), WebM (VP9) with CRF quality
- Audio: MP3, AAC, FLAC, WAV with bitrate quality presets
- Quality presets: high/medium/low mapping to CRF and bitrate values
Frontend (converter-page):
- Add format selector (Video/Audio groups) and quality dropdown
- Show per-item status badge, progress bar during conversion
- Action buttons: Start/Stop/Open Folder/Delete based on status
- "Convert All" batch button for pending items
- SWR auto-refresh during active conversions
Integration:
- Pass -ffmpeg-bin from Electron and core:dev to Go Core
- Remove old broken Electron-only convertToAudio code
- Add startConversion/stopConversion to core-sdk and GoApi
- Add i18n keys for new UI elements (en/zh)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Converted all Chinese-language comments across 47 source files to
English, covering Go backend (apps/core, apps/player), TypeScript
scripts, Electron main process, UI components, and shared packages.
Only comment text was modified — code, string literals, log messages,
and i18n translation values were left untouched.
Also fix pre-existing lint errors in scripts/release.ts and
scripts/utils.ts (node: protocol prefix, unused imports, bare catch).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Major cleanup of the frontend adapter layer:
1. Type split: MediaGoApi (55 methods) → GoApi (22 data/CRUD methods via
Go Core HTTP) + PlatformApi (~30 Electron-native methods via IPC)
2. Remove Proxy-based routing: replace the fragile apiAdapter Proxy +
GO_METHODS set + ALL_API_METHODS array with direct object composition.
GoApi methods never fall through to Electron IPC.
3. Clean dead code: remove 18 preload methods that called IPC channels
with no handler (getFavorites, getAppStore, createDownloadTasks, etc.)
Remove unused event constants (SOCKET_TEST, SETUP_AUTH, SIGNIN, etc.)
4. New platform-stubs.ts: explicit no-op stubs for web/server mode
instead of a catch-all Proxy that returns empty responses.
5. Simplified useAPI: direct spread of wrapped GoApi + PlatformApi
instead of dynamic Object.keys().reduce() enumeration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Failed and stopped events were only logged but never sent to the UI,
causing tasks to stay stuck showing "downloading" status. Now all terminal
state changes (success/failed/stopped) are forwarded via IPC/Socket.io
and trigger SWR revalidation for immediate UI refresh.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the entire TypeORM-based data layer (entities, repositories, services)
and refactor Electron/Server controllers to delegate to Go backend via HTTP API.
Add go-adapter for UI to communicate directly with Go core service.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Prettier with oxfmt for formatting and expand oxlint config with
full rule categories, React/TS overrides, and per-app config inheritance.
Add husky + lint-staged for pre-commit hooks to incrementally enforce
formatting on changed files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace @typescript/native-preview with standard typescript package
and remove tsgo-specific dts config from tsdown configurations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>