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>
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>
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>
The auth status API returns { setuped: bool } but the frontend was
checking for non-existent fields (initialized, enableAuth). Fix to
use the correct field name so unauthenticated users are properly
redirected to the signin page in server mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Refactor video player API from filesystem scanning to database-backed:
- Video list queries downloads with status="success" + file existence check
- Add GET /api/v1/videos/:id endpoint for playing specific video by task ID
- Add playerUrl to /api/env response (computed from request host)
- Whitelist player/video paths in auth middleware
- Fix video URL generation to preserve folder structure (relative path)
- Fix player-ui Vite base path and video URL resolution under /player/
- Add openUrl implementation to web platform stubs
- Player-UI supports ?id= query param to auto-play specific video
- Play button now passes task.id instead of filename
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>
Add proper TypeScript return types to every api/ function, using
existing shared types (Favorite, Video, Conversion, AppStore, etc.)
from @mediago/shared-common.
New types added:
- GoEnvPath (api/config.ts) for /api/env response
- AuthStatus (api/auth.ts) for /api/auth/status response
Updated SWR hooks to use typed data instead of Record<string, unknown>
casts: useFavorites, useConfig, useEnvPath, useAuthApi, useConversions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two issues:
1. http.ts 401 handler used window.location.hash (HashRouter syntax)
but the app uses BrowserRouter — changed to window.location.pathname
2. useAuth() blindly checked localStorage apiKey, which is always empty
on first visit. Now queries Go Core auth status first: only redirects
to /signin when auth is enabled and configured (or needs setup)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PlatformApi methods via Electron IPC return { code, data, msg } objects.
After migrating from useAPI() to usePlatform(), the auto-unwrap logic was
lost, causing consumers to receive raw objects instead of extracted data
(e.g. onSelectDownloadDir() returned [object Object] instead of a path).
Fix: wrap all PlatformApi function calls in the Proxy get trap to
auto-unwrap { code, data } responses, matching the old useAPI() behavior.
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>
Previously the app polled GET /api/tasks every second unconditionally
from startup, even with no downloads running. Now polling is driven
by SSE events: started on download-start, stopped when no tasks have
Downloading status (checked after success/failed/stop events).
Applied to both Electron main process (downloader.server.ts) and
web/UI mode (go-event-bridge.ts).
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>
Three root causes fixed:
1. ElectronStore migration deleted Go Core's config.json on every startup.
The old migration code unconditionally ran unlinkSync() on config.json
in the workspace directory — the same file Go Core uses for persistence.
Removed the migration entirely since it has already run for all users
(window-state.json now holds the bounds data).
2. core:dev mode saved config to the wrong directory (log dir instead of
data dir) because -config-dir was not passed. Added config_dir to
devConfig and the dev() command args, pointing to ~/.mediago/data to
avoid collision with the old v2.0 ~/.mediago/config.json format.
3. syncCLIToAppStore() overwrote user settings with CLI default values
on every Go Core startup in core:dev mode. Removed the function; CLI
args now only set initial cfg defaults, while appStore (user config)
takes precedence via the existing syncAppStoreToCfg().
Also aligns blockAds default (true) between Go Core and frontend Zustand store.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
getGoApi() was called eagerly inside useMemo during component mount,
before initGoAdapter() had run. Now GoApi methods resolve getGoApi()
at invocation time, so useAPI() can safely be called in App.tsx before
the adapter is initialized.
Co-Authored-By: Claude Opus 4.6 (1M context) <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>
1. Sync Go Core config to Zustand on app startup so settings persist
across restarts (Go Core is the single source of truth).
2. Go Core Create handler now respects startDownload param — downloads
start immediately when "Download Now" is clicked.
3. Player integration fixes:
- Electron: graceful skip when player binary is not built
- Server: start player service alongside Go Core, expose playerUrl
- Web adapter: derive playerUrl from current host instead of empty string
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Delete DownloadController (4 methods now handled by Go Core go-adapter),
move showDownloadDialog to WebviewController. Remove exportFavorites,
importFavorites, exportDownloadList from GO_METHODS so Electron mode
uses IPC with native file dialogs instead of bypassing them.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update go-adapter getEnvPath to call the new /api/env endpoint instead
of returning hardcoded empty strings for binPath and workspace.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add notification badge update after creating download tasks
- Add defensive null check in useAPI to prevent destructuring undefined adapter results
- Import download store for increase() calls in download-form
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove old Koa/Socket.IO server, replace with minimal ServiceRunner launcher
- UI now connects directly to Go Core via SDK (HTTP + SSE), no middleware
- Add GoEventBridge for SSE events and progress polling
- Add Dockerfile and docker-compose for single-container deployment
- Fix web mode adapter Proxy ownKeys for proper method enumeration
- Fix auth flow: setupAuth now sets apiKey, App.tsx passes stored apiKey
- Add error handling in download-form for getVideoFolders
- Remove conversion.controller import (file was deleted)
Co-Authored-By: Claude Opus 4.6 <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>