Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d654368290 | |||
| ab67f6b409 | |||
| 3eb7f13eca | |||
| 3b279e4a05 | |||
| ed03781692 | |||
| fb9dd15186 | |||
| 115572c513 | |||
| 1aa2bcd511 | |||
| 248237d593 | |||
| 99281d89f9 | |||
| aa24ef5966 |
@@ -16,3 +16,7 @@ apps/main/bin/
|
||||
.deps/
|
||||
|
||||
tmp/
|
||||
|
||||
# Local planning notes that should not be published with the open-source repo.
|
||||
docs/.vitepress/seo-keyword-map*.md
|
||||
docs/.vitepress/private/
|
||||
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
<div align="center">
|
||||
<h1>MediaGo</h1>
|
||||
<a href="https://downloader.caorushizi.cn/it/guides.html?form=github">Avvio rapido</a>
|
||||
<span> • </span>
|
||||
<a href="https://downloader.caorushizi.cn/it?form=github">Sito web</a>
|
||||
<span> • </span>
|
||||
<a href="https://downloader.caorushizi.cn/it/documents.html?form=github">Documentazione</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/discussions">Discussioni</a>
|
||||
<span> • </span>
|
||||
<a href="https://discord.gg/yxWBVRWGqM">Discord</a>
|
||||
<span> • </span>
|
||||
<a href="https://www.reddit.com/r/MediaGo_Studio/">Reddit</a>
|
||||
<br>
|
||||
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.md">English</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.zh.md">中文</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.jp.md">日本語</a>
|
||||
<br>
|
||||
|
||||
<img alt="GitHub Downloads (all assets, all releases)" src="https://img.shields.io/github/downloads/caorushizi/mediago/total">
|
||||
<img alt="GitHub Downloads (all assets, latest release)" src="https://img.shields.io/github/downloads/caorushizi/mediago/latest/total">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/caorushizi/mediago">
|
||||
<img alt="GitHub forks" src="https://img.shields.io/github/forks/caorushizi/mediago">
|
||||
<img alt="GitCode" src="https://gitcode.com/caorushizi/mediago/star/badge.svg">
|
||||
<br>
|
||||
|
||||
<a href="https://trendshift.io/repositories/11083" target="_blank">
|
||||
<img src="https://trendshift.io/api/badge/repositories/11083" alt="caorushizi%2Fmediago | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/>
|
||||
</a>
|
||||
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
Un downloader video multipiattaforma con sniffing integrato: apri una
|
||||
pagina, scegli la risorsa che ti interessa e salvala. Nessuna cattura dei
|
||||
pacchetti, nessuna configurazione complicata di estensioni browser, nessuno
|
||||
strumento da riga di comando da gestire.
|
||||
|
||||
L'interfaccia dell'app include attualmente inglese, cinese semplificato e
|
||||
italiano.
|
||||
|
||||
## ✨ Cosa include
|
||||
|
||||
### 🌐 Estensione browser per Chrome / Edge
|
||||
|
||||
Trovi un video interessante su un sito qualsiasi → clicchi l'estensione →
|
||||
lo invii a MediaGo con un clic. Rileva automaticamente le risorse video,
|
||||
mostra il numero di elementi trovati nel badge della toolbar e funziona con
|
||||
le principali piattaforme video, incluse YouTube, Bilibili e molte altre.
|
||||
L'estensione è inclusa nell'app desktop: apri **Impostazioni → Altre
|
||||
impostazioni → Directory estensione browser** per trovare la cartella di
|
||||
installazione.
|
||||
|
||||
### 🎬 YouTube e oltre 1000 siti
|
||||
|
||||
Basato su yt-dlp. Supporta YouTube, Twitter/X, Instagram, Reddit e
|
||||
[oltre mille altri siti video](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md).
|
||||
|
||||
### 🦞 Gli assistenti AI possono scaricare per te — OpenClaw Skill
|
||||
|
||||
Usi Claude Code, Cursor o un altro assistente AI per programmare? Installa
|
||||
la skill MediaGo e scrivi semplicemente _"please download this video:
|
||||
<url>"_. L'assistente gestisce il resto.
|
||||
|
||||
```shell
|
||||
npx clawhub@latest install mediago
|
||||
```
|
||||
|
||||
### 🔌 Funziona con altri strumenti
|
||||
|
||||
MediaGo espone una API HTTP completa: script, automazioni e app di terze
|
||||
parti possono creare attività di download, consultare l'avanzamento e
|
||||
gestire la lista. L'estensione browser usa la stessa API per parlare con
|
||||
l'app desktop, e puoi integrarla anche nei tuoi workflow.
|
||||
|
||||
### 🎞️ Conversione formato integrata
|
||||
|
||||
Dopo il download puoi convertire il file in un altro formato o qualità
|
||||
direttamente da MediaGo. Non serve aprire uno strumento ffmpeg separato.
|
||||
|
||||
### 🐳 Deploy Docker con un solo comando
|
||||
|
||||
Installazione headless sul tuo server, poi accesso alla UI web da qualsiasi
|
||||
dispositivo nella stessa rete:
|
||||
|
||||
```shell
|
||||
docker run -d --name mediago -p 8899:8899 -v /path/to/mediago:/app/mediago caorushizi/mediago:3.5.0
|
||||
```
|
||||
|
||||
Disponibile su [Docker Hub](https://hub.docker.com/r/caorushizi/mediago)
|
||||
e GHCR (`ghcr.io/caorushizi/mediago`): la stessa immagine, scegli il
|
||||
registry più veloce per te. Supporta Intel / AMD (amd64) e ARM (arm64).
|
||||
Nella build desktop, MediaGo ascolta sia su `127.0.0.1` sia sull'IP LAN,
|
||||
così telefoni e tablet sulla stessa rete Wi-Fi possono aprire direttamente
|
||||
la UI web.
|
||||
|
||||
## 📷 Screenshot
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 📥 Download
|
||||
|
||||
### v3.5.0 (stabile)
|
||||
|
||||
- [Windows — installer](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-win32-x64-3.5.0.exe)
|
||||
- [Windows — portable](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-portable-win32-x64-3.5.0.exe)
|
||||
- [macOS — Apple Silicon (arm64)](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-darwin-arm64-3.5.0.dmg)
|
||||
- [macOS — Intel (x64)](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-darwin-x64-3.5.0.dmg)
|
||||
- [Linux (deb)](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-linux-amd64-3.5.0.deb)
|
||||
- [**Docker Hub**](https://hub.docker.com/r/caorushizi/mediago): `docker run -d --name mediago -p 8899:8899 -v /path/to/mediago:/app/mediago caorushizi/mediago:3.5.0`
|
||||
- **GHCR**: `docker run -d --name mediago -p 8899:8899 -v /path/to/mediago:/app/mediago ghcr.io/caorushizi/mediago:3.5.0`
|
||||
|
||||
Per le versioni precedenti, consulta la [pagina GitHub Releases](https://github.com/caorushizi/mediago/releases).
|
||||
|
||||
### 🪄 Deploy Docker con un clic tramite BT Panel
|
||||
|
||||
1. Installa [BT Panel](https://www.bt.cn/new/download.html?r=dk_mediago)
|
||||
usando lo script ufficiale.
|
||||
2. Accedi al pannello, clicca **Docker** nella barra laterale e completa la
|
||||
configurazione del servizio Docker seguendo le istruzioni.
|
||||
3. Trova **MediaGo** nello store delle app, clicca **Install**, configura il
|
||||
dominio e hai finito.
|
||||
|
||||
## 📝 Novità in v3.5.0
|
||||
|
||||
- **🌐 Estensione browser** — sniffing video su qualsiasi sito e invio a
|
||||
MediaGo con un clic
|
||||
- **🎬 YouTube + 1000+ siti** — integrazione con yt-dlp
|
||||
- **🦞 OpenClaw Skill** — scarica video tramite assistenti AI per programmare
|
||||
- **🔌 API HTTP** — integrazione con script, automazioni e strumenti di terze parti
|
||||
- **🎞️ Conversione formato in app** — scegli formato e qualità di output
|
||||
- **🐳 Deploy Docker più semplice** — monta una sola cartella, immagini multi-arch su GHCR
|
||||
- **⚡ Avvio più rapido** — backend riscritto, minore consumo di memoria, player video integrato
|
||||
|
||||
## 🛠️ Tecnologie
|
||||
|
||||
[](https://react.dev/)
|
||||
[](https://www.electronjs.org)
|
||||
[](https://vitejs.dev)
|
||||
[](https://www.typescriptlang.org/)
|
||||
[](https://tailwindcss.com)
|
||||
[](https://ui.shadcn.com/)
|
||||
[](https://go.dev/)
|
||||
[](https://ant.design)
|
||||
|
||||
## 🙏 Ringraziamenti
|
||||
|
||||
- [N_m3u8DL-RE](https://github.com/nilaoda/N_m3u8DL-RE)
|
||||
- [BBDown](https://github.com/nilaoda/BBDown)
|
||||
- [yt-dlp](https://github.com/yt-dlp/yt-dlp)
|
||||
- [aria2](https://aria2.github.io/)
|
||||
- [mediago-core](https://github.com/caorushizi/mediago-core)
|
||||
|
||||
## ⚖️ Disclaimer
|
||||
|
||||
> **Questo progetto è destinato esclusivamente a scopi educativi e di ricerca. Non usarlo per finalità commerciali o illegali.**
|
||||
>
|
||||
> 1. Tutto il codice e tutte le funzionalità fornite da questo progetto sono pensati solo come riferimento per lo studio delle tecnologie di streaming. Gli utenti devono rispettare le leggi e i regolamenti della propria giurisdizione.
|
||||
> 2. Qualsiasi contenuto scaricato tramite questo progetto resta di proprietà dei rispettivi titolari dei diritti. Gli utenti devono eliminare i contenuti scaricati entro 24 ore o ottenere un'autorizzazione adeguata.
|
||||
> 3. Gli sviluppatori del progetto non sono responsabili delle azioni degli utenti, incluso il download di contenuti protetti da copyright o l'impatto su piattaforme di terze parti.
|
||||
> 4. È vietato usare questo progetto per scraping massivo, interruzione dei servizi delle piattaforme o qualsiasi attività che violi diritti legittimi altrui.
|
||||
> 5. Usando questo progetto confermi di aver letto e accettato questo disclaimer. Se non lo accetti, interrompi subito l'uso del progetto ed eliminalo.
|
||||
|
||||
---
|
||||
|
||||
> Vuoi compilare da sorgente? Consulta [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
>
|
||||
> Vuoi tradurre MediaGo? Consulta [TRANSLATION.md](./TRANSLATION.md).
|
||||
+8
-9
@@ -7,22 +7,19 @@
|
||||
<a href="https://downloader.caorushizi.cn/jp/documents.html?form=github">ドキュメント</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/discussions">Discussions</a>
|
||||
<span> • </span>
|
||||
<a href="https://discord.gg/yxWBVRWGqM">Discord</a>
|
||||
<span> • </span>
|
||||
<a href="https://www.reddit.com/r/MediaGo_Studio/">Reddit</a>
|
||||
<br>
|
||||
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.md">English</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.zh.md">中文</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.it.md">Italiano</a>
|
||||
<br>
|
||||
|
||||
<!-- MediaGo Pro -->
|
||||
<a href="https://mediago.torchstellar.com/?from=github">
|
||||
<img src="https://img.shields.io/badge/✨_新登場-MediaGo_Pro-ff6b6b?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyTDMgN2wzIDMgNi00IDYgNCAzLTMtOS01eiIvPjxwYXRoIGQ9Ik0zIDE3bDkgNSA5LTUtMy0zLTYgNC02LTQtMyAzeiIvPjwvc3ZnPg==" alt="MediaGo Pro" />
|
||||
</a>
|
||||
<a href="https://mediago.torchstellar.com/?from=github">
|
||||
<img src="https://img.shields.io/badge/🚀_今すぐ試す-オンライン版_インストール不要-2a82f6?style=for-the-badge" alt="Try Now" />
|
||||
</a>
|
||||
<br>
|
||||
|
||||
<img alt="GitHub Downloads (all assets, all releases)" src="https://img.shields.io/github/downloads/caorushizi/mediago/total">
|
||||
<img alt="GitHub Downloads (all assets, latest release)" src="https://img.shields.io/github/downloads/caorushizi/mediago/latest/total">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/caorushizi/mediago">
|
||||
@@ -39,6 +36,8 @@
|
||||
|
||||
ビルトインのスニッフィング機能を備えたクロスプラットフォームの動画ダウンローダー —— ページを開いて、欲しいリソースを選んで、保存するだけ。パケットキャプチャ不要、ブラウザ拡張の設定不要、コマンドラインの操作も不要です。
|
||||
|
||||
アプリ UI は現在、英語・簡体中国語・イタリア語に対応しています。
|
||||
|
||||
## ✨ 主な機能
|
||||
|
||||
### 🌐 ブラウザ拡張機能(Chrome / Edge)
|
||||
|
||||
@@ -7,22 +7,19 @@
|
||||
<a href="https://downloader.caorushizi.cn/en/documents.html?form=github">Docs</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/discussions">Discussions</a>
|
||||
<span> • </span>
|
||||
<a href="https://discord.gg/yxWBVRWGqM">Discord</a>
|
||||
<span> • </span>
|
||||
<a href="https://www.reddit.com/r/MediaGo_Studio/">Reddit</a>
|
||||
<br>
|
||||
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.zh.md">中文</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.jp.md">日本語</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.it.md">Italiano</a>
|
||||
<br>
|
||||
|
||||
<!-- MediaGo Pro -->
|
||||
<a href="https://mediago.torchstellar.com/?from=github">
|
||||
<img src="https://img.shields.io/badge/✨_New_Release-MediaGo_Pro-ff6b6b?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyTDMgN2wzIDMgNi00IDYgNCAzLTMtOS01eiIvPjxwYXRoIGQ9Ik0zIDE3bDkgNSA5LTUtMy0zLTYgNC02LTQtMyAzeiIvPjwvc3ZnPg==" alt="MediaGo Pro" />
|
||||
</a>
|
||||
<a href="https://mediago.torchstellar.com/?from=github">
|
||||
<img src="https://img.shields.io/badge/🚀_Try_Now-Online_Version_No_Install-2a82f6?style=for-the-badge" alt="Try Now" />
|
||||
</a>
|
||||
<br>
|
||||
|
||||
<img alt="GitHub Downloads (all assets, all releases)" src="https://img.shields.io/github/downloads/caorushizi/mediago/total">
|
||||
<img alt="GitHub Downloads (all assets, latest release)" src="https://img.shields.io/github/downloads/caorushizi/mediago/latest/total">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/caorushizi/mediago">
|
||||
@@ -41,6 +38,8 @@ A cross-platform video downloader with built-in sniffing — point it at a
|
||||
page, pick what you want, and save. No packet capture, no browser
|
||||
extensions to configure, no fiddling with command-line tools.
|
||||
|
||||
The app UI currently ships with English, Simplified Chinese, and Italian.
|
||||
|
||||
## ✨ What's inside
|
||||
|
||||
### 🌐 Browser extension for Chrome / Edge
|
||||
|
||||
+11
-9
@@ -7,22 +7,19 @@
|
||||
<a href="https://downloader.caorushizi.cn/documents.html?form=github">文档</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/discussions">Discussions</a>
|
||||
<span> • </span>
|
||||
<a href="https://discord.gg/yxWBVRWGqM">Discord</a>
|
||||
<span> • </span>
|
||||
<a href="https://www.reddit.com/r/MediaGo_Studio/">Reddit</a>
|
||||
<br>
|
||||
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.md">English</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.jp.md">日本語</a>
|
||||
<span> • </span>
|
||||
<a href="https://github.com/caorushizi/mediago/blob/master/README.it.md">Italiano</a>
|
||||
<br>
|
||||
|
||||
<!-- MediaGo Pro 推广 -->
|
||||
<a href="https://mediago.torchstellar.com/?from=github">
|
||||
<img src="https://img.shields.io/badge/✨_全新发布-MediaGo_Pro-ff6b6b?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0xMiAyTDMgN2wzIDMgNi00IDYgNCAzLTMtOS01eiIvPjxwYXRoIGQ9Ik0zIDE3bDkgNSA5LTUtMy0zLTYgNC02LTQtMyAzeiIvPjwvc3ZnPg==" alt="MediaGo Pro" />
|
||||
</a>
|
||||
<a href="https://mediago.torchstellar.com/?from=github">
|
||||
<img src="https://img.shields.io/badge/🚀_立即体验-在线版本_无需安装-2a82f6?style=for-the-badge" alt="Try Now" />
|
||||
</a>
|
||||
<br>
|
||||
|
||||
<img alt="GitHub Downloads (all assets, all releases)" src="https://img.shields.io/github/downloads/caorushizi/mediago/total">
|
||||
<img alt="GitHub Downloads (all assets, latest release)" src="https://img.shields.io/github/downloads/caorushizi/mediago/latest/total">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/caorushizi/mediago">
|
||||
@@ -39,6 +36,8 @@
|
||||
|
||||
跨平台视频下载器,内置嗅探 —— 打开网页、选一下想要的资源、保存完事。不用抓包、不用折腾浏览器插件、不用面对命令行。
|
||||
|
||||
应用界面目前内置中文、英文和意大利语。
|
||||
|
||||
## ✨ 主打功能
|
||||
|
||||
### 🌐 浏览器扩展(Chrome / Edge)
|
||||
@@ -149,3 +148,6 @@ docker run -d --name mediago -p 8899:8899 -v /path/to/mediago:/app/mediago caoru
|
||||
> 想从源码构建?见 [CONTRIBUTING.md](./CONTRIBUTING.md)(英文)。
|
||||
>
|
||||
> 想为 MediaGo 做翻译?见 [TRANSLATION.md](./TRANSLATION.md)(英文)。
|
||||
|
||||
尾注: 感谢[吾爱破解论坛](https://www.52pojie.cn/)
|
||||
lp_Zain@www.52pojie.cn
|
||||
|
||||
+36
-12
@@ -16,7 +16,8 @@ pnpm dev:electron
|
||||
|
||||
1. Copy `packages/shared/common/src/i18n/resources/en.ts` to `<lang>.ts`,
|
||||
translate every value.
|
||||
2. Register the new locale (four small edits — see below).
|
||||
2. Register the new locale in the shared resources, resolver, UI dropdown,
|
||||
and browser extension (see below).
|
||||
3. Open **Settings → Language**, pick your locale, and iterate. Vite HMR
|
||||
reflects edits in the running app within a second.
|
||||
4. Open a PR — we review and merge.
|
||||
@@ -24,9 +25,9 @@ pnpm dev:electron
|
||||
## Where strings live
|
||||
|
||||
- **Main app UI** (desktop + self-hosted web):
|
||||
`packages/shared/common/src/i18n/resources/{en,zh}.ts`
|
||||
`packages/shared/common/src/i18n/resources/{en,it,zh}.ts`
|
||||
- **Browser extension** (separate, smaller catalog):
|
||||
`packages/mediago-extension/src/i18n/resources/{en,zh}.ts`
|
||||
`packages/mediago-extension/src/i18n/resources/{en,it,zh}.ts`
|
||||
|
||||
Resources are plain TypeScript modules — each file exports a flat object of
|
||||
`key: "translation"` pairs. Keys are shared across languages; only values
|
||||
@@ -55,12 +56,12 @@ export const fr = {
|
||||
```
|
||||
|
||||
Don't forget to add the new `french: "Français"` key to **every** resource
|
||||
file (`en.ts`, `zh.ts`, and your new `fr.ts`) so the Settings dropdown can
|
||||
render it in each language.
|
||||
file (`en.ts`, `it.ts`, `zh.ts`, and your new `fr.ts`) so the Settings
|
||||
dropdown can render it in each language.
|
||||
|
||||
### 2. Register the resource
|
||||
|
||||
Three tiny edits:
|
||||
Core app registration:
|
||||
|
||||
**`packages/shared/common/src/i18n/resources/index.ts`** — import the new
|
||||
locale and add it to both exports:
|
||||
@@ -68,26 +69,36 @@ locale and add it to both exports:
|
||||
```ts
|
||||
import { fr } from "./fr";
|
||||
// ...
|
||||
export const i18nResources = { en, zh, fr } as const;
|
||||
export const SUPPORTED_LANGUAGES = ["en", "zh", "fr"] as const;
|
||||
export { en, zh, fr };
|
||||
export const i18nResources = { en, it, zh, fr } as const;
|
||||
export const SUPPORTED_LANGUAGES = ["en", "it", "zh", "fr"] as const;
|
||||
export { en, it, zh, fr };
|
||||
```
|
||||
|
||||
**`packages/shared/common/src/i18n/config.ts`** — widen the
|
||||
`resolveAppLanguage` return type and the check inside:
|
||||
|
||||
```ts
|
||||
export type ResolvedAppLanguage = "zh" | "en" | "it" | "fr";
|
||||
|
||||
export function resolveAppLanguage(
|
||||
language: string | undefined,
|
||||
systemLocale: string | undefined,
|
||||
): "zh" | "en" | "fr" {
|
||||
if (language === "zh" || language === "en" || language === "fr") {
|
||||
): ResolvedAppLanguage {
|
||||
if (
|
||||
language === "zh" ||
|
||||
language === "en" ||
|
||||
language === "it" ||
|
||||
language === "fr"
|
||||
) {
|
||||
return language;
|
||||
}
|
||||
// ...existing fallback...
|
||||
}
|
||||
```
|
||||
|
||||
If the new locale should follow the OS/browser locale automatically, add the
|
||||
matching `systemLocale` prefix check in the same function.
|
||||
|
||||
**`packages/shared/common/src/types/index.ts`** — extend the `AppLanguage`
|
||||
enum:
|
||||
|
||||
@@ -100,6 +111,11 @@ export enum AppLanguage {
|
||||
}
|
||||
```
|
||||
|
||||
**`apps/ui/src/App.tsx`** — if Ant Design ships a locale for your language,
|
||||
import it and include it in `getAntdLocale`. Otherwise, explicitly fall back
|
||||
to `enUS` for Ant Design components while your app strings still use your
|
||||
translated resource file.
|
||||
|
||||
### 3. Add the Settings dropdown option
|
||||
|
||||
**`apps/ui/src/pages/setting-page/index.tsx`** — inside the **Language**
|
||||
@@ -117,7 +133,15 @@ options={[
|
||||
### 4. (Optional) Translate the browser extension
|
||||
|
||||
Same pattern, under `packages/mediago-extension/src/i18n/resources/`. It's a
|
||||
much smaller catalog and lives in its own `index.ts`.
|
||||
much smaller catalog and lives in its own `index.ts`. Also update:
|
||||
|
||||
- `packages/mediago-extension/src/i18n/index.ts` for language resolution.
|
||||
- `packages/mediago-extension/src/shared/types.ts` for the persisted language
|
||||
union.
|
||||
- `packages/mediago-extension/src/options/components/LanguageCard.tsx` for
|
||||
the options-page selector.
|
||||
- `packages/mediago-extension/public/_locales/<lang>/messages.json` for the
|
||||
Chrome extension name, description, and action tooltip.
|
||||
|
||||
## Live preview workflow
|
||||
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"caorushizi.cn/mediago/internal/app"
|
||||
"caorushizi.cn/mediago/internal/core"
|
||||
"caorushizi.cn/mediago/internal/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := newRootCommand().Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func newRootCommand() *cobra.Command {
|
||||
cfg := app.DefaultConfig()
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "mediago-cli",
|
||||
Short: "MediaGo command-line interface",
|
||||
Long: "MediaGo CLI directly uses the core downloader runtime without going through the HTTP API server.",
|
||||
}
|
||||
addConfigFlags(rootCmd.PersistentFlags(), cfg)
|
||||
rootCmd.AddCommand(newDownloadCommand(cfg))
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
func newDownloadCommand(cfg *app.AppConfig) *cobra.Command {
|
||||
var id string
|
||||
var typ string
|
||||
var name string
|
||||
var folder string
|
||||
var headers []string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "download <url>",
|
||||
Short: "Download a URL directly with MediaGo core",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg.ApplyEnvAndDefaults()
|
||||
if err := app.InitLogger(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
defer logger.Sync()
|
||||
|
||||
if id == "" {
|
||||
id = uuid.New().String()
|
||||
}
|
||||
if name == "" {
|
||||
name = "download-" + uuid.New().String()[:8]
|
||||
}
|
||||
|
||||
rt, err := app.NewRuntime(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rt.Close()
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer stop()
|
||||
|
||||
params := core.DownloadParams{
|
||||
ID: core.TaskID(id),
|
||||
Type: core.DownloadType(typ),
|
||||
URL: args[0],
|
||||
Name: core.SanitizeFilename(name),
|
||||
Folder: folder,
|
||||
Headers: headers,
|
||||
}
|
||||
|
||||
lastPercent := -1
|
||||
fmt.Printf("Starting %s download: %s\n", params.Type, params.Name)
|
||||
err = rt.Downloader.Download(ctx, params, core.Callbacks{
|
||||
OnProgress: func(e core.ProgressEvent) {
|
||||
percent := int(e.Percent)
|
||||
if percent != lastPercent {
|
||||
lastPercent = percent
|
||||
if e.Speed != "" {
|
||||
fmt.Printf("\r%3d%% %s", percent, e.Speed)
|
||||
} else {
|
||||
fmt.Printf("\r%3d%%", percent)
|
||||
}
|
||||
}
|
||||
},
|
||||
OnMessage: func(e core.MessageEvent) {
|
||||
if strings.TrimSpace(e.Message) != "" {
|
||||
logger.Debugf("[%s] %s", e.ID, e.Message)
|
||||
}
|
||||
},
|
||||
})
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Download completed")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&id, "id", "", "Download task id")
|
||||
cmd.Flags().StringVarP(&typ, "type", "t", string(core.TypeM3U8), "Download type (m3u8, bilibili, direct, mediago, youtube)")
|
||||
cmd.Flags().StringVarP(&name, "name", "n", "", "Output file name")
|
||||
cmd.Flags().StringVar(&folder, "folder", "", "Subdirectory under the download directory")
|
||||
cmd.Flags().StringArrayVarP(&headers, "header", "H", nil, "HTTP header, can be repeated")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func addConfigFlags(flags *pflag.FlagSet, cfg *app.AppConfig) {
|
||||
flags.StringVar(&cfg.LogLevel, "log-level", cfg.LogLevel, "Log level (debug/info/warn/error)")
|
||||
flags.StringVar(&cfg.LogDir, "log-dir", cfg.LogDir, "Log directory")
|
||||
flags.StringVar(&cfg.DepsDir, "deps-dir", cfg.DepsDir, "Directory containing downloader tool binaries")
|
||||
flags.StringVar(&cfg.SchemaPath, "schema-path", cfg.SchemaPath, "Path to the download schema config.json")
|
||||
flags.StringVar(&cfg.LocalDir, "local-dir", cfg.LocalDir, "Default download directory")
|
||||
flags.BoolVar(&cfg.DeleteSegments, "delete-segments", cfg.DeleteSegments, "Delete segments after download")
|
||||
flags.StringVar(&cfg.Proxy, "proxy", cfg.Proxy, "Proxy for downloader")
|
||||
flags.BoolVar(&cfg.UseProxy, "use-proxy", cfg.UseProxy, "Enable proxy")
|
||||
flags.IntVar(&cfg.MaxRunner, "max-runner", cfg.MaxRunner, "Maximum concurrent download runners")
|
||||
flags.StringVar(&cfg.DBPath, "db-path", cfg.DBPath, "Path to SQLite database file")
|
||||
flags.StringVar(&cfg.ConfigDir, "config-dir", cfg.ConfigDir, "Directory for persistent config file")
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package main
|
||||
|
||||
// AppStore holds all user-facing configuration options.
|
||||
// Field names and defaults match the TypeScript AppStore interface
|
||||
// in @mediago/shared-common.
|
||||
type AppStore struct {
|
||||
Local string `json:"local"`
|
||||
PromptTone bool `json:"promptTone"`
|
||||
Proxy string `json:"proxy"`
|
||||
UseProxy bool `json:"useProxy"`
|
||||
DeleteSegments bool `json:"deleteSegments"`
|
||||
OpenInNewWindow bool `json:"openInNewWindow"`
|
||||
BlockAds bool `json:"blockAds"`
|
||||
Theme string `json:"theme"`
|
||||
UseExtension bool `json:"useExtension"`
|
||||
IsMobile bool `json:"isMobile"`
|
||||
MaxRunner int `json:"maxRunner"`
|
||||
Language string `json:"language"`
|
||||
ShowTerminal bool `json:"showTerminal"`
|
||||
Privacy bool `json:"privacy"`
|
||||
MachineId string `json:"machineId"`
|
||||
DownloadProxySwitch bool `json:"downloadProxySwitch"`
|
||||
AutoUpgrade bool `json:"autoUpgrade"`
|
||||
AllowBeta bool `json:"allowBeta"`
|
||||
CloseMainWindow bool `json:"closeMainWindow"`
|
||||
AudioMuted bool `json:"audioMuted"`
|
||||
EnableDocker bool `json:"enableDocker"`
|
||||
DockerUrl string `json:"dockerUrl"`
|
||||
EnableMobilePlayer bool `json:"enableMobilePlayer"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
PasswordHash string `json:"passwordHash"`
|
||||
}
|
||||
|
||||
// defaultAppStore returns default config values matching the TS appStoreDefaults.
|
||||
func defaultAppStore() AppStore {
|
||||
return AppStore{
|
||||
Local: "",
|
||||
PromptTone: true,
|
||||
Proxy: "",
|
||||
UseProxy: false,
|
||||
DeleteSegments: true,
|
||||
OpenInNewWindow: false,
|
||||
BlockAds: true,
|
||||
Theme: "system",
|
||||
UseExtension: false,
|
||||
IsMobile: false,
|
||||
MaxRunner: 2,
|
||||
Language: "system",
|
||||
ShowTerminal: false,
|
||||
Privacy: false,
|
||||
MachineId: "",
|
||||
DownloadProxySwitch: false,
|
||||
AutoUpgrade: true,
|
||||
AllowBeta: false,
|
||||
CloseMainWindow: false,
|
||||
AudioMuted: true,
|
||||
EnableDocker: false,
|
||||
DockerUrl: "",
|
||||
EnableMobilePlayer: false,
|
||||
ApiKey: "",
|
||||
PasswordHash: "",
|
||||
}
|
||||
}
|
||||
+47
-352
@@ -3,21 +3,15 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"caorushizi.cn/mediago/internal/api"
|
||||
"caorushizi.cn/mediago/internal/api/handler"
|
||||
"caorushizi.cn/mediago/internal/core"
|
||||
"caorushizi.cn/mediago/internal/core/runner"
|
||||
"caorushizi.cn/mediago/internal/core/schema"
|
||||
"caorushizi.cn/mediago/internal/db"
|
||||
"caorushizi.cn/mediago/internal/app"
|
||||
"caorushizi.cn/mediago/internal/logger"
|
||||
"caorushizi.cn/mediago/internal/tasklog"
|
||||
"caorushizi.cn/mediago/pkg/conf"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -48,227 +42,64 @@ import (
|
||||
// @tag.name Events
|
||||
// @tag.description Real-time event streaming endpoints
|
||||
|
||||
// AppConfig stores all startup configuration options (passed via command-line flags or environment variables).
|
||||
type AppConfig struct {
|
||||
GinMode string `json:"gin_mode"`
|
||||
Host string `json:"host"`
|
||||
Port string `json:"port"`
|
||||
LogLevel string `json:"log_level"`
|
||||
LogDir string `json:"log_dir"`
|
||||
SchemaPath string `json:"schema_path"`
|
||||
DepsDir string `json:"deps_dir"`
|
||||
MaxRunner int `json:"max_runner"`
|
||||
LocalDir string `json:"local_dir"`
|
||||
DeleteSegments bool `json:"delete_segments"`
|
||||
Proxy string `json:"proxy"`
|
||||
UseProxy bool `json:"use_proxy"`
|
||||
DBPath string `json:"db_path"`
|
||||
ConfigDir string `json:"config_dir"`
|
||||
EnableAuth bool `json:"enable_auth"`
|
||||
StaticDir string `json:"static_dir"`
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetLocalDir() string {
|
||||
return c.LocalDir
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetDeleteSegments() bool {
|
||||
return c.DeleteSegments
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetProxy() string {
|
||||
return c.Proxy
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetUseProxy() bool {
|
||||
return c.UseProxy
|
||||
}
|
||||
|
||||
func (c *AppConfig) SetLocalDir(dir string) {
|
||||
c.LocalDir = dir
|
||||
}
|
||||
|
||||
func (c *AppConfig) SetDeleteSegments(del bool) {
|
||||
c.DeleteSegments = del
|
||||
}
|
||||
|
||||
func (c *AppConfig) SetProxy(proxy string) {
|
||||
c.Proxy = proxy
|
||||
}
|
||||
|
||||
func (c *AppConfig) SetUseProxy(useProxy bool) {
|
||||
c.UseProxy = useProxy
|
||||
}
|
||||
|
||||
// getSystemDownloadsDir returns the system downloads directory.
|
||||
// Prefers $HOME/Downloads; falls back to $HOME if it does not exist.
|
||||
func getSystemDownloadsDir() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "."
|
||||
}
|
||||
downloads := filepath.Join(home, "Downloads")
|
||||
if info, err := os.Stat(downloads); err == nil && info.IsDir() {
|
||||
return downloads
|
||||
}
|
||||
return home
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 1. Initialize the logger with default config first so it is available during config parsing
|
||||
// if err := logger.Init(logger.DefaultConfig()); err != nil {
|
||||
// panic("Failed to initialize logger: " + err.Error())
|
||||
// }
|
||||
if err := run(os.Args[1:]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Initialize and parse configuration
|
||||
cfg := initConfig()
|
||||
func run(args []string) error {
|
||||
cfg := app.DefaultConfig()
|
||||
fs := flag.NewFlagSet("mediago-core server", flag.ContinueOnError)
|
||||
registerConfigFlags(fs, cfg)
|
||||
if err := fs.Parse(args); err != nil {
|
||||
if err == flag.ErrHelp {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
cfg.ApplyEnvAndDefaults()
|
||||
|
||||
// 3. Re-initialize the logger using the resolved configuration
|
||||
logCfg := logger.DefaultConfig()
|
||||
logCfg.Level = cfg.LogLevel
|
||||
logCfg.LogDir = cfg.LogDir
|
||||
|
||||
if err := logger.Init(logCfg); err != nil {
|
||||
logger.Fatalf("Failed to reinitialize logger with config: %v", err)
|
||||
if err := app.InitLogger(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
defer logger.Sync()
|
||||
|
||||
logger.Info("MediaGo Downloader Service Starting...")
|
||||
logger.Infof("Final Config: %+v", cfg)
|
||||
|
||||
// 4. Initialize AppStore configuration (user-level persistent config)
|
||||
appStore, err := conf.New(conf.Options[AppStore]{
|
||||
ConfigName: "config",
|
||||
CWD: cfg.ConfigDir,
|
||||
Defaults: defaultAppStore(),
|
||||
})
|
||||
rt, err := app.NewRuntime(cfg)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to initialize app store: %v", err)
|
||||
return err
|
||||
}
|
||||
logger.Infof("App store initialized at: %s", appStore.Path())
|
||||
defer rt.Close()
|
||||
|
||||
// Ensure machineId is set (generate on first run)
|
||||
if appStore.Store().MachineId == "" {
|
||||
newId := uuid.New().String()
|
||||
_ = appStore.Set("machineId", newId)
|
||||
logger.Infof("Generated new machineId: %s", newId)
|
||||
}
|
||||
return runServer(rt)
|
||||
}
|
||||
|
||||
// Sync appStore values back into cfg (so cfg reflects the final state of persistent config).
|
||||
// Note: CLI arguments are no longer written into appStore to avoid overwriting settings
|
||||
// saved by the user in the UI on every startup. CLI args serve only as initial defaults;
|
||||
// appStore (user config) takes higher priority.
|
||||
syncAppStoreToCfg(appStore, cfg)
|
||||
func registerConfigFlags(fs *flag.FlagSet, cfg *app.AppConfig) {
|
||||
fs.StringVar(&cfg.LogLevel, "log-level", cfg.LogLevel, "Log level (debug/info/warn/error)")
|
||||
fs.StringVar(&cfg.LogDir, "log-dir", cfg.LogDir, "Log directory")
|
||||
fs.StringVar(&cfg.DepsDir, "deps-dir", cfg.DepsDir, "Directory containing downloader tool binaries")
|
||||
fs.StringVar(&cfg.SchemaPath, "schema-path", cfg.SchemaPath, "Path to the download schema config.json")
|
||||
fs.StringVar(&cfg.Port, "port", cfg.Port, "Server port")
|
||||
fs.StringVar(&cfg.LocalDir, "local-dir", cfg.LocalDir, "Default download directory")
|
||||
fs.BoolVar(&cfg.DeleteSegments, "delete-segments", cfg.DeleteSegments, "Delete segments after download")
|
||||
fs.StringVar(&cfg.Proxy, "proxy", cfg.Proxy, "Proxy for downloader")
|
||||
fs.BoolVar(&cfg.UseProxy, "use-proxy", cfg.UseProxy, "Enable proxy")
|
||||
fs.IntVar(&cfg.MaxRunner, "max-runner", cfg.MaxRunner, "Maximum concurrent download runners")
|
||||
fs.StringVar(&cfg.DBPath, "db-path", cfg.DBPath, "Path to SQLite database file")
|
||||
fs.StringVar(&cfg.ConfigDir, "config-dir", cfg.ConfigDir, "Directory for persistent config file")
|
||||
fs.BoolVar(&cfg.EnableAuth, "enable-auth", cfg.EnableAuth, "Enable API key authentication")
|
||||
fs.StringVar(&cfg.StaticDir, "static-dir", cfg.StaticDir, "Directory to serve static files from (SPA mode)")
|
||||
}
|
||||
|
||||
// If the download directory is empty, is the default value, or does not exist, use the system downloads directory
|
||||
{
|
||||
needDefault := cfg.LocalDir == "" || cfg.LocalDir == "./downloads"
|
||||
if !needDefault {
|
||||
if info, err := os.Stat(cfg.LocalDir); err != nil || !info.IsDir() {
|
||||
needDefault = true
|
||||
}
|
||||
}
|
||||
if needDefault {
|
||||
sysDownloads := getSystemDownloadsDir()
|
||||
logger.Infof("Download dir %q unavailable, using system default: %s", cfg.LocalDir, sysDownloads)
|
||||
cfg.LocalDir = sysDownloads
|
||||
}
|
||||
// Ensure appStore has the download directory (so UI can read it via /api/config)
|
||||
if appStore.Store().Local == "" {
|
||||
_ = appStore.Set("local", cfg.LocalDir)
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Load JSON Schema configuration
|
||||
logger.Infof("Loading schemas from: %s", cfg.SchemaPath)
|
||||
schemas, err := schema.LoadSchemasFromJSON(cfg.SchemaPath)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to load schemas: %v", err)
|
||||
}
|
||||
logger.Infof("Loaded %d download schemas", len(schemas.Schemas))
|
||||
|
||||
// 6. Configure downloader binary paths
|
||||
binMap := getBinaryMap(cfg)
|
||||
for dt, binPath := range binMap {
|
||||
logger.Infof("%s downloader: %s", dt, binPath)
|
||||
if binPath == "" {
|
||||
continue
|
||||
}
|
||||
if info, err := os.Stat(binPath); err != nil {
|
||||
logger.Warnf("%s binary not found: %v", dt, err)
|
||||
} else if info.Mode()&0o111 == 0 {
|
||||
logger.Warnf("%s binary is not executable: %s", dt, binPath)
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Create core components
|
||||
r := runner.NewPTYRunner()
|
||||
downloader := core.NewDownloader(binMap, r, schemas, cfg)
|
||||
queue := core.NewTaskQueue(downloader, cfg.MaxRunner)
|
||||
taskLogs := tasklog.NewManager(filepath.Join(cfg.LogDir, "tasks"))
|
||||
|
||||
logger.Infof("Task queue initialized (maxRunner=%d)", cfg.MaxRunner)
|
||||
logger.Infof("Task logs will be stored in %s", filepath.Join(cfg.LogDir, "tasks"))
|
||||
|
||||
// 8. Watch appStore changes and sync them to the downloader
|
||||
appStore.OnDidChange("maxRunner", func(newVal, oldVal any) {
|
||||
if v, ok := toInt(newVal); ok {
|
||||
queue.SetMaxRunner(v)
|
||||
logger.Infof("maxRunner updated to %d via config change", v)
|
||||
}
|
||||
})
|
||||
appStore.OnDidChange("proxy", func(newVal, oldVal any) {
|
||||
if v, ok := newVal.(string); ok {
|
||||
cfg.SetProxy(v)
|
||||
logger.Infof("proxy updated to %q via config change", v)
|
||||
}
|
||||
})
|
||||
appStore.OnDidChange("useProxy", func(newVal, oldVal any) {
|
||||
if v, ok := newVal.(bool); ok {
|
||||
cfg.SetUseProxy(v)
|
||||
logger.Infof("useProxy updated to %v via config change", v)
|
||||
}
|
||||
})
|
||||
appStore.OnDidChange("deleteSegments", func(newVal, oldVal any) {
|
||||
if v, ok := newVal.(bool); ok {
|
||||
cfg.SetDeleteSegments(v)
|
||||
logger.Infof("deleteSegments updated to %v via config change", v)
|
||||
}
|
||||
})
|
||||
appStore.OnDidChange("local", func(newVal, oldVal any) {
|
||||
if v, ok := newVal.(string); ok {
|
||||
cfg.SetLocalDir(v)
|
||||
logger.Infof("localDir updated to %q via config change", v)
|
||||
}
|
||||
})
|
||||
|
||||
// 9. Initialize database (optional)
|
||||
var database *db.Database
|
||||
if cfg.DBPath != "" {
|
||||
// Ensure the database parent directory exists
|
||||
if dir := filepath.Dir(cfg.DBPath); dir != "" {
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
logger.Fatalf("Failed to create database directory %s: %v", dir, err)
|
||||
}
|
||||
}
|
||||
var dbErr error
|
||||
database, dbErr = db.New(cfg.DBPath)
|
||||
if dbErr != nil {
|
||||
logger.Fatalf("Failed to open database: %v", dbErr)
|
||||
}
|
||||
defer database.Close()
|
||||
logger.Infof("Database opened: %s", cfg.DBPath)
|
||||
} else {
|
||||
logger.Info("No database path provided, running without persistence")
|
||||
}
|
||||
|
||||
// 10. Start HTTP server
|
||||
confStore := handler.WrapConfStore[AppStore](appStore)
|
||||
func runServer(rt *app.Runtime) error {
|
||||
cfg := rt.Config
|
||||
confStore := handler.WrapConfStore[app.AppStore](rt.AppStore)
|
||||
execPath, _ := os.Executable()
|
||||
server := api.NewServer(queue, taskLogs, database, confStore, api.ServerOptions{
|
||||
server := api.NewServer(rt.Queue, rt.TaskLogs, rt.Database, confStore, api.ServerOptions{
|
||||
EnableAuth: cfg.EnableAuth,
|
||||
StaticDir: cfg.StaticDir,
|
||||
FFmpegBin: getFFmpegBin(cfg),
|
||||
FFmpegBin: app.FFmpegBinPath(cfg),
|
||||
VideoRoot: cfg.LocalDir,
|
||||
EnvPaths: handler.EnvPaths{
|
||||
ConfigDir: cfg.ConfigDir,
|
||||
@@ -280,141 +111,5 @@ func main() {
|
||||
gin.SetMode(cfg.GinMode)
|
||||
logger.Infof("Starting HTTP server on %s", addr)
|
||||
|
||||
if err := server.Run(addr); err != nil {
|
||||
logger.Fatalf("Failed to start server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// syncAppStoreToCfg reads appStore values back into cfg.
|
||||
// CLI flags take priority: if --local-dir was explicitly set (not the default),
|
||||
// the CLI value is kept and written back to appStore.
|
||||
func syncAppStoreToCfg(store *conf.Conf[AppStore], cfg *AppConfig) {
|
||||
s := store.Store()
|
||||
cliLocalDir := cfg.LocalDir
|
||||
cliExplicit := cliLocalDir != "" && cliLocalDir != "./downloads"
|
||||
|
||||
if cliExplicit {
|
||||
// CLI explicitly set --local-dir, use it and update appStore
|
||||
_ = store.Set("local", cliLocalDir)
|
||||
} else if s.Local != "" {
|
||||
cfg.LocalDir = s.Local
|
||||
}
|
||||
cfg.Proxy = s.Proxy
|
||||
cfg.UseProxy = s.UseProxy
|
||||
cfg.DeleteSegments = s.DeleteSegments
|
||||
if s.MaxRunner > 0 {
|
||||
cfg.MaxRunner = s.MaxRunner
|
||||
}
|
||||
}
|
||||
|
||||
// initConfig initializes configuration following priority order: CLI flags > environment variables > JSON string > defaults.
|
||||
func initConfig() *AppConfig {
|
||||
// Default configuration
|
||||
cfg := &AppConfig{
|
||||
GinMode: "release",
|
||||
Host: "0.0.0.0",
|
||||
Port: "8080",
|
||||
LogLevel: "info",
|
||||
LogDir: "./logs",
|
||||
SchemaPath: "", // computed later
|
||||
DepsDir: "",
|
||||
MaxRunner: 2,
|
||||
LocalDir: "./downloads",
|
||||
DeleteSegments: true,
|
||||
Proxy: "",
|
||||
UseProxy: false,
|
||||
ConfigDir: "",
|
||||
}
|
||||
|
||||
// 1. Define command-line flags
|
||||
flag.StringVar(&cfg.LogLevel, "log-level", cfg.LogLevel, "Log level (debug/info/warn/error)")
|
||||
flag.StringVar(&cfg.LogDir, "log-dir", cfg.LogDir, "Log directory")
|
||||
flag.StringVar(&cfg.DepsDir, "deps-dir", cfg.DepsDir, "Directory containing downloader tool binaries")
|
||||
flag.StringVar(&cfg.SchemaPath, "schema-path", cfg.SchemaPath, "Path to the download schema config.json")
|
||||
flag.StringVar(&cfg.Port, "port", cfg.Port, "Server port")
|
||||
flag.StringVar(&cfg.LocalDir, "local-dir", cfg.LocalDir, "Default download directory")
|
||||
flag.BoolVar(&cfg.DeleteSegments, "delete-segments", cfg.DeleteSegments, "Delete segments after download")
|
||||
flag.StringVar(&cfg.Proxy, "proxy", cfg.Proxy, "Proxy for downloader")
|
||||
flag.BoolVar(&cfg.UseProxy, "use-proxy", cfg.UseProxy, "Enable proxy")
|
||||
flag.IntVar(&cfg.MaxRunner, "max-runner", cfg.MaxRunner, "Maximum concurrent download runners")
|
||||
flag.StringVar(&cfg.DBPath, "db-path", cfg.DBPath, "Path to SQLite database file")
|
||||
flag.StringVar(&cfg.ConfigDir, "config-dir", cfg.ConfigDir, "Directory for persistent config file")
|
||||
flag.BoolVar(&cfg.EnableAuth, "enable-auth", cfg.EnableAuth, "Enable API key authentication")
|
||||
flag.StringVar(&cfg.StaticDir, "static-dir", cfg.StaticDir, "Directory to serve static files from (SPA mode)")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// 3. Load from environment variables (overrides JSON and defaults)
|
||||
cfg.GinMode = getEnv("GIN_MODE", cfg.GinMode)
|
||||
cfg.Host = getEnv("HOST", cfg.Host)
|
||||
cfg.Port = getEnv("PORT", cfg.Port)
|
||||
cfg.DBPath = getEnv("DB_PATH", cfg.DBPath)
|
||||
|
||||
// If SchemaPath is still empty, compute its default value
|
||||
if cfg.SchemaPath == "" {
|
||||
cfg.SchemaPath = getDefaultSchemaPath()
|
||||
}
|
||||
|
||||
// If ConfigDir is empty, default to the LogDir path
|
||||
if cfg.ConfigDir == "" {
|
||||
cfg.ConfigDir = cfg.LogDir
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// getDefaultSchemaPath returns the default path for the schema config file.
|
||||
func getDefaultSchemaPath() string {
|
||||
// Default path: prefer config.json in the same directory as the executable
|
||||
execPath, _ := os.Executable()
|
||||
execDir := filepath.Dir(execPath)
|
||||
localConfig := filepath.Join(execDir, "config.json")
|
||||
if _, err := os.Stat(localConfig); err == nil {
|
||||
return localConfig
|
||||
}
|
||||
// Fall back to the config file path inside the repository
|
||||
return "configs/config.json"
|
||||
}
|
||||
|
||||
// exeExt returns ".exe" on Windows, empty string otherwise.
|
||||
func exeExt() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return ".exe"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getBinaryMap builds the downloader binary path map from a single deps directory.
|
||||
func getBinaryMap(cfg *AppConfig) map[core.DownloadType]string {
|
||||
ext := exeExt()
|
||||
m := make(map[core.DownloadType]string, len(core.BinaryNames))
|
||||
for dt, name := range core.BinaryNames {
|
||||
m[dt] = filepath.Join(cfg.DepsDir, name+ext)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// getFFmpegBin returns the ffmpeg binary path derived from the deps directory.
|
||||
func getFFmpegBin(cfg *AppConfig) string {
|
||||
return filepath.Join(cfg.DepsDir, core.FFmpegBinaryName+exeExt())
|
||||
}
|
||||
|
||||
func getEnv(key, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// toInt converts a JSON-decoded number (float64) or int to int.
|
||||
func toInt(v any) (int, bool) {
|
||||
switch n := v.(type) {
|
||||
case float64:
|
||||
return int(n), true
|
||||
case int:
|
||||
return n, true
|
||||
case int64:
|
||||
return int(n), true
|
||||
}
|
||||
return 0, false
|
||||
return server.Run(addr)
|
||||
}
|
||||
|
||||
+4
-1
@@ -9,10 +9,13 @@ require (
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/pflag v1.0.9
|
||||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.1
|
||||
github.com/swaggo/swag v1.16.6
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.43.0
|
||||
golang.org/x/text v0.30.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gorm.io/gorm v1.31.1
|
||||
@@ -43,6 +46,7 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.28.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
@@ -61,7 +65,6 @@ require (
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/arch v0.22.0 // indirect
|
||||
golang.org/x/crypto v0.43.0 // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/net v0.46.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
|
||||
@@ -10,6 +10,7 @@ github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZw
|
||||
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -20,6 +21,7 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
|
||||
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
|
||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
@@ -72,6 +74,8 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbu
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
@@ -106,6 +110,11 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package app
|
||||
|
||||
// AppStore holds all user-facing configuration options.
|
||||
// Field names and defaults match the TypeScript AppStore interface
|
||||
// in @mediago/shared-common.
|
||||
type AppStore struct {
|
||||
Local string `json:"local"`
|
||||
PromptTone bool `json:"promptTone"`
|
||||
Proxy string `json:"proxy"`
|
||||
UseProxy bool `json:"useProxy"`
|
||||
DeleteSegments bool `json:"deleteSegments"`
|
||||
OpenInNewWindow bool `json:"openInNewWindow"`
|
||||
BlockAds bool `json:"blockAds"`
|
||||
Theme string `json:"theme"`
|
||||
UseExtension bool `json:"useExtension"`
|
||||
IsMobile bool `json:"isMobile"`
|
||||
MaxRunner int `json:"maxRunner"`
|
||||
Language string `json:"language"`
|
||||
ShowTerminal bool `json:"showTerminal"`
|
||||
Privacy bool `json:"privacy"`
|
||||
MachineId string `json:"machineId"`
|
||||
DownloadProxySwitch bool `json:"downloadProxySwitch"`
|
||||
AutoUpgrade bool `json:"autoUpgrade"`
|
||||
AllowBeta bool `json:"allowBeta"`
|
||||
CloseMainWindow bool `json:"closeMainWindow"`
|
||||
AudioMuted bool `json:"audioMuted"`
|
||||
EnableDocker bool `json:"enableDocker"`
|
||||
DockerUrl string `json:"dockerUrl"`
|
||||
EnableMobilePlayer bool `json:"enableMobilePlayer"`
|
||||
ApiKey string `json:"apiKey"`
|
||||
PasswordHash string `json:"passwordHash"`
|
||||
}
|
||||
|
||||
// DefaultAppStore returns default config values matching the TS appStoreDefaults.
|
||||
func DefaultAppStore() AppStore {
|
||||
return AppStore{
|
||||
Local: "",
|
||||
PromptTone: true,
|
||||
Proxy: "",
|
||||
UseProxy: false,
|
||||
DeleteSegments: true,
|
||||
OpenInNewWindow: false,
|
||||
BlockAds: true,
|
||||
Theme: "system",
|
||||
UseExtension: false,
|
||||
IsMobile: false,
|
||||
MaxRunner: 2,
|
||||
Language: "system",
|
||||
ShowTerminal: false,
|
||||
Privacy: false,
|
||||
MachineId: "",
|
||||
DownloadProxySwitch: false,
|
||||
AutoUpgrade: true,
|
||||
AllowBeta: false,
|
||||
CloseMainWindow: false,
|
||||
AudioMuted: true,
|
||||
EnableDocker: false,
|
||||
DockerUrl: "",
|
||||
EnableMobilePlayer: false,
|
||||
ApiKey: "",
|
||||
PasswordHash: "",
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// AppConfig stores startup configuration options passed by flags or environment.
|
||||
type AppConfig struct {
|
||||
GinMode string `json:"gin_mode"`
|
||||
Host string `json:"host"`
|
||||
Port string `json:"port"`
|
||||
LogLevel string `json:"log_level"`
|
||||
LogDir string `json:"log_dir"`
|
||||
SchemaPath string `json:"schema_path"`
|
||||
DepsDir string `json:"deps_dir"`
|
||||
MaxRunner int `json:"max_runner"`
|
||||
LocalDir string `json:"local_dir"`
|
||||
DeleteSegments bool `json:"delete_segments"`
|
||||
Proxy string `json:"proxy"`
|
||||
UseProxy bool `json:"use_proxy"`
|
||||
DBPath string `json:"db_path"`
|
||||
ConfigDir string `json:"config_dir"`
|
||||
EnableAuth bool `json:"enable_auth"`
|
||||
StaticDir string `json:"static_dir"`
|
||||
}
|
||||
|
||||
func DefaultConfig() *AppConfig {
|
||||
return &AppConfig{
|
||||
GinMode: "release",
|
||||
Host: "0.0.0.0",
|
||||
Port: "8080",
|
||||
LogLevel: "info",
|
||||
LogDir: "./logs",
|
||||
SchemaPath: "",
|
||||
DepsDir: "",
|
||||
MaxRunner: 2,
|
||||
LocalDir: "./downloads",
|
||||
DeleteSegments: true,
|
||||
Proxy: "",
|
||||
UseProxy: false,
|
||||
ConfigDir: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AppConfig) ApplyEnvAndDefaults() {
|
||||
c.GinMode = getEnv("GIN_MODE", c.GinMode)
|
||||
c.Host = getEnv("HOST", c.Host)
|
||||
c.Port = getEnv("PORT", c.Port)
|
||||
c.DBPath = getEnv("DB_PATH", c.DBPath)
|
||||
|
||||
if c.SchemaPath == "" {
|
||||
c.SchemaPath = getDefaultSchemaPath()
|
||||
}
|
||||
if c.ConfigDir == "" {
|
||||
c.ConfigDir = c.LogDir
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetLocalDir() string {
|
||||
return c.LocalDir
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetDeleteSegments() bool {
|
||||
return c.DeleteSegments
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetProxy() string {
|
||||
return c.Proxy
|
||||
}
|
||||
|
||||
func (c *AppConfig) GetUseProxy() bool {
|
||||
return c.UseProxy
|
||||
}
|
||||
|
||||
func (c *AppConfig) SetLocalDir(dir string) {
|
||||
c.LocalDir = dir
|
||||
}
|
||||
|
||||
func (c *AppConfig) SetDeleteSegments(del bool) {
|
||||
c.DeleteSegments = del
|
||||
}
|
||||
|
||||
func (c *AppConfig) SetProxy(proxy string) {
|
||||
c.Proxy = proxy
|
||||
}
|
||||
|
||||
func (c *AppConfig) SetUseProxy(useProxy bool) {
|
||||
c.UseProxy = useProxy
|
||||
}
|
||||
|
||||
func getDefaultSchemaPath() string {
|
||||
execPath, _ := os.Executable()
|
||||
execDir := filepath.Dir(execPath)
|
||||
localConfig := filepath.Join(execDir, "config.json")
|
||||
if _, err := os.Stat(localConfig); err == nil {
|
||||
return localConfig
|
||||
}
|
||||
return "configs/config.json"
|
||||
}
|
||||
|
||||
func getEnv(key, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"caorushizi.cn/mediago/internal/core"
|
||||
"caorushizi.cn/mediago/internal/core/runner"
|
||||
"caorushizi.cn/mediago/internal/core/schema"
|
||||
"caorushizi.cn/mediago/internal/db"
|
||||
"caorushizi.cn/mediago/internal/logger"
|
||||
"caorushizi.cn/mediago/internal/tasklog"
|
||||
"caorushizi.cn/mediago/pkg/conf"
|
||||
)
|
||||
|
||||
type Runtime struct {
|
||||
Config *AppConfig
|
||||
AppStore *conf.Conf[AppStore]
|
||||
Downloader *core.DownloaderSvc
|
||||
Queue *core.TaskQueue
|
||||
TaskLogs *tasklog.Manager
|
||||
Database *db.Database
|
||||
}
|
||||
|
||||
func InitLogger(cfg *AppConfig) error {
|
||||
logCfg := logger.DefaultConfig()
|
||||
logCfg.Level = cfg.LogLevel
|
||||
logCfg.LogDir = cfg.LogDir
|
||||
|
||||
if err := logger.Init(logCfg); err != nil {
|
||||
return fmt.Errorf("failed to initialize logger: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewRuntime(cfg *AppConfig) (*Runtime, error) {
|
||||
logger.Info("MediaGo Downloader Core Starting...")
|
||||
logger.Infof("Final Config: %+v", cfg)
|
||||
|
||||
appStore, err := conf.New(conf.Options[AppStore]{
|
||||
ConfigName: "config",
|
||||
CWD: cfg.ConfigDir,
|
||||
Defaults: DefaultAppStore(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize app store: %w", err)
|
||||
}
|
||||
logger.Infof("App store initialized at: %s", appStore.Path())
|
||||
|
||||
if appStore.Store().MachineId == "" {
|
||||
newId := uuid.New().String()
|
||||
_ = appStore.Set("machineId", newId)
|
||||
logger.Infof("Generated new machineId: %s", newId)
|
||||
}
|
||||
|
||||
syncAppStoreToCfg(appStore, cfg)
|
||||
ensureDownloadDir(appStore, cfg)
|
||||
|
||||
logger.Infof("Loading schemas from: %s", cfg.SchemaPath)
|
||||
schemas, err := schema.LoadSchemasFromJSON(cfg.SchemaPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load schemas: %w", err)
|
||||
}
|
||||
logger.Infof("Loaded %d download schemas", len(schemas.Schemas))
|
||||
|
||||
binMap := getBinaryMap(cfg)
|
||||
for dt, binPath := range binMap {
|
||||
logger.Infof("%s downloader: %s", dt, binPath)
|
||||
if binPath == "" {
|
||||
continue
|
||||
}
|
||||
if info, err := os.Stat(binPath); err != nil {
|
||||
logger.Warnf("%s binary not found: %v", dt, err)
|
||||
} else if info.Mode()&0o111 == 0 {
|
||||
logger.Warnf("%s binary is not executable: %s", dt, binPath)
|
||||
}
|
||||
}
|
||||
|
||||
r := runner.NewPTYRunner()
|
||||
downloader := core.NewDownloader(binMap, r, schemas, cfg)
|
||||
queue := core.NewTaskQueue(downloader, cfg.MaxRunner)
|
||||
taskLogs := tasklog.NewManager(filepath.Join(cfg.LogDir, "tasks"))
|
||||
|
||||
logger.Infof("Task queue initialized (maxRunner=%d)", cfg.MaxRunner)
|
||||
logger.Infof("Task logs will be stored in %s", filepath.Join(cfg.LogDir, "tasks"))
|
||||
|
||||
appStore.OnDidChange("maxRunner", func(newVal, oldVal any) {
|
||||
if v, ok := toInt(newVal); ok {
|
||||
queue.SetMaxRunner(v)
|
||||
logger.Infof("maxRunner updated to %d via config change", v)
|
||||
}
|
||||
})
|
||||
appStore.OnDidChange("proxy", func(newVal, oldVal any) {
|
||||
if v, ok := newVal.(string); ok {
|
||||
cfg.SetProxy(v)
|
||||
logger.Infof("proxy updated to %q via config change", v)
|
||||
}
|
||||
})
|
||||
appStore.OnDidChange("useProxy", func(newVal, oldVal any) {
|
||||
if v, ok := newVal.(bool); ok {
|
||||
cfg.SetUseProxy(v)
|
||||
logger.Infof("useProxy updated to %v via config change", v)
|
||||
}
|
||||
})
|
||||
appStore.OnDidChange("deleteSegments", func(newVal, oldVal any) {
|
||||
if v, ok := newVal.(bool); ok {
|
||||
cfg.SetDeleteSegments(v)
|
||||
logger.Infof("deleteSegments updated to %v via config change", v)
|
||||
}
|
||||
})
|
||||
appStore.OnDidChange("local", func(newVal, oldVal any) {
|
||||
if v, ok := newVal.(string); ok {
|
||||
cfg.SetLocalDir(v)
|
||||
logger.Infof("localDir updated to %q via config change", v)
|
||||
}
|
||||
})
|
||||
|
||||
var database *db.Database
|
||||
if cfg.DBPath != "" {
|
||||
if dir := filepath.Dir(cfg.DBPath); dir != "" {
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create database directory %s: %w", dir, err)
|
||||
}
|
||||
}
|
||||
var dbErr error
|
||||
database, dbErr = db.New(cfg.DBPath)
|
||||
if dbErr != nil {
|
||||
return nil, fmt.Errorf("failed to open database: %w", dbErr)
|
||||
}
|
||||
logger.Infof("Database opened: %s", cfg.DBPath)
|
||||
} else {
|
||||
logger.Info("No database path provided, running without persistence")
|
||||
}
|
||||
|
||||
return &Runtime{
|
||||
Config: cfg,
|
||||
AppStore: appStore,
|
||||
Downloader: downloader,
|
||||
Queue: queue,
|
||||
TaskLogs: taskLogs,
|
||||
Database: database,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (rt *Runtime) Close() {
|
||||
if rt.Database != nil {
|
||||
_ = rt.Database.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func syncAppStoreToCfg(store *conf.Conf[AppStore], cfg *AppConfig) {
|
||||
s := store.Store()
|
||||
cliLocalDir := cfg.LocalDir
|
||||
cliExplicit := cliLocalDir != "" && cliLocalDir != "./downloads"
|
||||
|
||||
if cliExplicit {
|
||||
_ = store.Set("local", cliLocalDir)
|
||||
} else if s.Local != "" {
|
||||
cfg.LocalDir = s.Local
|
||||
}
|
||||
cfg.Proxy = s.Proxy
|
||||
cfg.UseProxy = s.UseProxy
|
||||
cfg.DeleteSegments = s.DeleteSegments
|
||||
if s.MaxRunner > 0 {
|
||||
cfg.MaxRunner = s.MaxRunner
|
||||
}
|
||||
}
|
||||
|
||||
func ensureDownloadDir(store *conf.Conf[AppStore], cfg *AppConfig) {
|
||||
needDefault := cfg.LocalDir == "" || cfg.LocalDir == "./downloads"
|
||||
if !needDefault {
|
||||
if info, err := os.Stat(cfg.LocalDir); err != nil || !info.IsDir() {
|
||||
needDefault = true
|
||||
}
|
||||
}
|
||||
if needDefault {
|
||||
sysDownloads := getSystemDownloadsDir()
|
||||
logger.Infof("Download dir %q unavailable, using system default: %s", cfg.LocalDir, sysDownloads)
|
||||
cfg.LocalDir = sysDownloads
|
||||
}
|
||||
if store.Store().Local == "" {
|
||||
_ = store.Set("local", cfg.LocalDir)
|
||||
}
|
||||
}
|
||||
|
||||
func getSystemDownloadsDir() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "."
|
||||
}
|
||||
downloads := filepath.Join(home, "Downloads")
|
||||
if info, err := os.Stat(downloads); err == nil && info.IsDir() {
|
||||
return downloads
|
||||
}
|
||||
return home
|
||||
}
|
||||
|
||||
func exeExt() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return ".exe"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getBinaryMap(cfg *AppConfig) map[core.DownloadType]string {
|
||||
ext := exeExt()
|
||||
m := make(map[core.DownloadType]string, len(core.BinaryNames))
|
||||
for dt, name := range core.BinaryNames {
|
||||
m[dt] = filepath.Join(cfg.DepsDir, name+ext)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func FFmpegBinPath(cfg *AppConfig) string {
|
||||
return filepath.Join(cfg.DepsDir, core.FFmpegBinaryName+exeExt())
|
||||
}
|
||||
|
||||
func toInt(v any) (int, bool) {
|
||||
switch n := v.(type) {
|
||||
case float64:
|
||||
return int(n), true
|
||||
case int:
|
||||
return n, true
|
||||
case int64:
|
||||
return int(n), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "mediago",
|
||||
"name": "mediago-community",
|
||||
"version": "3.5.0",
|
||||
"description": "A powerful and easy-to-use online video downloader",
|
||||
"main": "main/index.js",
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import i18n, { type InitOptions, type Resource } from "i18next";
|
||||
import { BASE_I18N_OPTIONS, i18nResources } from "@mediago/shared-common";
|
||||
import {
|
||||
BASE_I18N_OPTIONS,
|
||||
i18nResources,
|
||||
SUPPORTED_LANGUAGES,
|
||||
} from "@mediago/shared-common";
|
||||
|
||||
const nodeResources: Resource = {
|
||||
en: { translation: i18nResources.en },
|
||||
zh: { translation: i18nResources.zh },
|
||||
};
|
||||
const nodeResources: Resource = SUPPORTED_LANGUAGES.reduce<Resource>(
|
||||
(resources, language) => {
|
||||
resources[language] = { translation: i18nResources[language] };
|
||||
return resources;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const nodeI18nOptions: InitOptions = {
|
||||
...BASE_I18N_OPTIONS,
|
||||
|
||||
+18
-2
@@ -2,9 +2,11 @@ import { App as AntdApp, ConfigProvider, theme as antdTheme } from "antd";
|
||||
import { type FC, lazy, Suspense, useEffect, useState } from "react";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import "dayjs/locale/it";
|
||||
import { useAsyncEffect, useMemoizedFn } from "ahooks";
|
||||
import zhCN from "antd/es/locale/zh_CN";
|
||||
import enUS from "antd/es/locale/en_US";
|
||||
import itIT from "antd/es/locale/it_IT";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import Loading from "./components/loading";
|
||||
import { PAGE_LOAD } from "./const";
|
||||
@@ -26,7 +28,7 @@ import { getConfig } from "./api/config";
|
||||
import { initGoEvents, onConfigChanged } from "./api/events";
|
||||
import { DownloadFilter } from "@mediago/shared-common";
|
||||
import { useAuth } from "./hooks/use-auth";
|
||||
import { Locale } from "antd/es/locale";
|
||||
import type { Locale } from "antd/es/locale";
|
||||
|
||||
const AppLayout = lazy(() => import("./layout/app-layout"));
|
||||
const HomePage = lazy(() => import("./pages/home-page"));
|
||||
@@ -42,6 +44,20 @@ function getAlgorithm(appTheme: "dark" | "light") {
|
||||
: antdTheme.defaultAlgorithm;
|
||||
}
|
||||
|
||||
function getAntdLocale(
|
||||
language: ReturnType<typeof resolveAppLanguage>,
|
||||
): Locale {
|
||||
switch (language) {
|
||||
case "zh":
|
||||
return zhCN;
|
||||
case "it":
|
||||
return itIT;
|
||||
case "en":
|
||||
default:
|
||||
return enUS;
|
||||
}
|
||||
}
|
||||
|
||||
const App: FC = () => {
|
||||
useAuth();
|
||||
const { on, off } = usePlatform();
|
||||
@@ -56,7 +72,7 @@ const App: FC = () => {
|
||||
const [adapterReady, setAdapterReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setAppLocale(resolveAppLanguage(language) === "zh" ? zhCN : enUS);
|
||||
setAppLocale(getAntdLocale(resolveAppLanguage(language)));
|
||||
}, [language]);
|
||||
|
||||
const themeChange = useMemoizedFn((event: MediaQueryListEvent) => {
|
||||
|
||||
@@ -250,6 +250,7 @@ const SettingPage: React.FC = () => {
|
||||
{ label: t("followSystem"), value: AppLanguage.System },
|
||||
{ label: t("chinese"), value: AppLanguage.ZH },
|
||||
{ label: t("english"), value: AppLanguage.EN },
|
||||
{ label: t("italian"), value: AppLanguage.IT },
|
||||
]}
|
||||
placeholder={t("pleaseSelectLanguage")}
|
||||
allowClear={false}
|
||||
|
||||
@@ -111,6 +111,8 @@ export function getBrowserLang(): string {
|
||||
return lang.toLowerCase();
|
||||
}
|
||||
|
||||
export function resolveAppLanguage(language: string | undefined): "zh" | "en" {
|
||||
export function resolveAppLanguage(
|
||||
language: string | undefined,
|
||||
): ReturnType<typeof sharedResolveAppLanguage> {
|
||||
return sharedResolveAppLanguage(language, getBrowserLang());
|
||||
}
|
||||
|
||||
+371
-44
@@ -4,6 +4,7 @@ import { defineConfig, type HeadConfig } from "vitepress";
|
||||
import { baiduAnalytics, googleAnalytics } from "./plugins";
|
||||
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
const siteUrl = "https://downloader.caorushizi.cn";
|
||||
|
||||
const head: HeadConfig[] = [
|
||||
["link", { rel: "shortcut icon", href: "/favicon.svg" }],
|
||||
@@ -12,12 +13,243 @@ if (!isDev) {
|
||||
head.push(...baiduAnalytics(), ...googleAnalytics());
|
||||
}
|
||||
|
||||
const translatedBlogAlternates: Record<string, Record<string, string>> = {
|
||||
"blog/video-downloader-review/index": {
|
||||
"zh-CN": `${siteUrl}/blog/video-downloader-review/`,
|
||||
en: `${siteUrl}/en/blog/video-downloader-review/`,
|
||||
"x-default": `${siteUrl}/blog/video-downloader-review/`,
|
||||
},
|
||||
"en/blog/video-downloader-review/index": {
|
||||
"zh-CN": `${siteUrl}/blog/video-downloader-review/`,
|
||||
en: `${siteUrl}/en/blog/video-downloader-review/`,
|
||||
"x-default": `${siteUrl}/blog/video-downloader-review/`,
|
||||
},
|
||||
};
|
||||
|
||||
function getPageUrl(page: string) {
|
||||
const normalized = page.replace(/\.md$/, "");
|
||||
if (normalized === "index") {
|
||||
return `${siteUrl}/`;
|
||||
}
|
||||
if (normalized.endsWith("/index")) {
|
||||
return `${siteUrl}/${normalized.slice(0, -"/index".length)}/`;
|
||||
}
|
||||
return `${siteUrl}/${normalized}.html`;
|
||||
}
|
||||
|
||||
function getPageLanguage(page: string) {
|
||||
if (page.startsWith("en/")) {
|
||||
return "en";
|
||||
}
|
||||
if (page.startsWith("jp/")) {
|
||||
return "ja";
|
||||
}
|
||||
if (page.startsWith("it/")) {
|
||||
return "it";
|
||||
}
|
||||
return "zh-CN";
|
||||
}
|
||||
|
||||
function isBlogPagePath(page: string) {
|
||||
return page.startsWith("blog/") || /^[a-z]{2}\/blog\//.test(page);
|
||||
}
|
||||
|
||||
function isBlogIndexPage(page: string) {
|
||||
return page === "blog/index.md" || /^[a-z]{2}\/blog\/index\.md$/.test(page);
|
||||
}
|
||||
|
||||
function getAbsoluteUrl(url: string) {
|
||||
if (/^https?:\/\//.test(url)) {
|
||||
return url;
|
||||
}
|
||||
return `${siteUrl}${url.startsWith("/") ? "" : "/"}${url}`;
|
||||
}
|
||||
|
||||
function getStringArray(value: unknown) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(String);
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
return value
|
||||
.split(",")
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function getBreadcrumbItems(
|
||||
pageUrl: string,
|
||||
pageTitle: string,
|
||||
isBlogPage: boolean,
|
||||
language: string,
|
||||
) {
|
||||
const isEnglish = language === "en";
|
||||
const localePrefix = isEnglish ? "/en" : "";
|
||||
const items = [
|
||||
{
|
||||
"@type": "ListItem",
|
||||
position: 1,
|
||||
name: isEnglish ? "Home" : "首页",
|
||||
item: `${siteUrl}${localePrefix}/`,
|
||||
},
|
||||
];
|
||||
|
||||
if (isBlogPage) {
|
||||
items.push({
|
||||
"@type": "ListItem",
|
||||
position: 2,
|
||||
name: isEnglish ? "Blog" : "博客",
|
||||
item: `${siteUrl}${localePrefix}/blog/`,
|
||||
});
|
||||
|
||||
if (pageUrl !== `${siteUrl}${localePrefix}/blog/`) {
|
||||
items.push({
|
||||
"@type": "ListItem",
|
||||
position: 3,
|
||||
name: pageTitle,
|
||||
item: pageUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: "MediaGo",
|
||||
description: "简单易用,快速下载",
|
||||
lastUpdated: true,
|
||||
head,
|
||||
sitemap: {
|
||||
hostname: siteUrl,
|
||||
},
|
||||
transformHead({ page, pageData, title, description }) {
|
||||
const normalizedPage = page.replace(/\.md$/, "");
|
||||
const pageUrl = getPageUrl(page);
|
||||
const frontmatter = pageData.frontmatter;
|
||||
const language = getPageLanguage(page);
|
||||
const isBlogPage = isBlogPagePath(page);
|
||||
const pageTitle = String(frontmatter.title || title || "MediaGo");
|
||||
const pageDescription = String(
|
||||
frontmatter.description || description || "简单易用,快速下载",
|
||||
);
|
||||
const pageTags = getStringArray(frontmatter.tags);
|
||||
const pageImage =
|
||||
typeof frontmatter.image === "string"
|
||||
? getAbsoluteUrl(frontmatter.image)
|
||||
: undefined;
|
||||
const entries: HeadConfig[] = [
|
||||
["link", { rel: "canonical", href: pageUrl }],
|
||||
["meta", { property: "og:url", content: pageUrl }],
|
||||
["meta", { property: "og:title", content: pageTitle }],
|
||||
["meta", { property: "og:description", content: pageDescription }],
|
||||
["meta", { property: "og:site_name", content: "MediaGo" }],
|
||||
[
|
||||
"meta",
|
||||
{ property: "og:type", content: isBlogPage ? "article" : "website" },
|
||||
],
|
||||
["meta", { name: "twitter:card", content: "summary_large_image" }],
|
||||
["meta", { name: "twitter:title", content: pageTitle }],
|
||||
["meta", { name: "twitter:description", content: pageDescription }],
|
||||
[
|
||||
"script",
|
||||
{ type: "application/ld+json" },
|
||||
JSON.stringify({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
itemListElement: getBreadcrumbItems(
|
||||
pageUrl,
|
||||
pageTitle,
|
||||
isBlogPage,
|
||||
language,
|
||||
),
|
||||
}),
|
||||
],
|
||||
];
|
||||
|
||||
const alternates = translatedBlogAlternates[normalizedPage];
|
||||
if (alternates) {
|
||||
for (const [hreflang, href] of Object.entries(alternates)) {
|
||||
entries.push(["link", { rel: "alternate", hreflang, href }]);
|
||||
}
|
||||
}
|
||||
|
||||
if (pageTags.length > 0) {
|
||||
entries.push(["meta", { name: "keywords", content: pageTags.join(",") }]);
|
||||
}
|
||||
|
||||
if (pageImage) {
|
||||
entries.push(
|
||||
["meta", { property: "og:image", content: pageImage }],
|
||||
["meta", { name: "twitter:image", content: pageImage }],
|
||||
);
|
||||
}
|
||||
|
||||
if (isBlogPage && !isBlogIndexPage(page)) {
|
||||
const article: Record<string, unknown> = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BlogPosting",
|
||||
headline: pageTitle,
|
||||
description: pageDescription,
|
||||
author: {
|
||||
"@type": "Organization",
|
||||
name: String(frontmatter.author || "MediaGo"),
|
||||
url: siteUrl,
|
||||
},
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "MediaGo",
|
||||
url: siteUrl,
|
||||
},
|
||||
datePublished: frontmatter.date,
|
||||
dateModified: frontmatter.updated || frontmatter.date,
|
||||
mainEntityOfPage: pageUrl,
|
||||
inLanguage: language,
|
||||
};
|
||||
|
||||
if (pageTags.length > 0) {
|
||||
article.keywords = pageTags;
|
||||
}
|
||||
|
||||
if (pageImage) {
|
||||
article.image = [pageImage];
|
||||
}
|
||||
|
||||
entries.push([
|
||||
"script",
|
||||
{ type: "application/ld+json" },
|
||||
JSON.stringify(article),
|
||||
]);
|
||||
|
||||
if (frontmatter.date) {
|
||||
entries.push([
|
||||
"meta",
|
||||
{
|
||||
property: "article:published_time",
|
||||
content: String(frontmatter.date),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
if (frontmatter.updated || frontmatter.date) {
|
||||
entries.push([
|
||||
"meta",
|
||||
{
|
||||
property: "article:modified_time",
|
||||
content: String(frontmatter.updated || frontmatter.date),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
for (const tag of pageTags) {
|
||||
entries.push(["meta", { property: "article:tag", content: tag }]);
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
},
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{ text: "Home", link: "/" },
|
||||
@@ -25,29 +257,57 @@ export default defineConfig({
|
||||
{ text: "更新日志", link: "/changelog" },
|
||||
],
|
||||
|
||||
sidebar: [
|
||||
{
|
||||
text: "开始",
|
||||
items: [
|
||||
{ text: "快速开始", link: "/guides" },
|
||||
{ text: "使用说明", link: "/documents" },
|
||||
{ text: "更新日志", link: "/changelog" },
|
||||
{ text: "通过宝塔面板部署", link: "/bt-install" },
|
||||
{ text: "浏览器扩展", link: "/extension" },
|
||||
{ text: "配合猫爪下载视频", link: "/catcatch" },
|
||||
{ text: "🦞 OpenClaw Skill", link: "/skills" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Q&A",
|
||||
items: [
|
||||
{ text: "常见问题", link: "/qa" },
|
||||
{ text: "windows7支持(64位)", link: "/history" },
|
||||
{ text: "意见收集", link: "/proposal" },
|
||||
{ text: "支持列表", link: "/list" },
|
||||
],
|
||||
},
|
||||
],
|
||||
sidebar: {
|
||||
"/blog/": [
|
||||
{
|
||||
text: "博客",
|
||||
items: [
|
||||
{ text: "博客首页", link: "/blog/" },
|
||||
{
|
||||
text: "视频下载器推荐",
|
||||
link: "/blog/video-downloader-recommendation/",
|
||||
},
|
||||
{ text: "视频下载器评测", link: "/blog/video-downloader-review/" },
|
||||
{ text: "网页视频下载指南", link: "/blog/video-download/" },
|
||||
{ text: "M3U8 / HLS 下载指南", link: "/blog/m3u8-hls-download/" },
|
||||
{ text: "网页视频嗅探指南", link: "/blog/video-sniffer/" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "产品文档",
|
||||
items: [
|
||||
{ text: "快速开始", link: "/guides" },
|
||||
{ text: "使用说明", link: "/documents" },
|
||||
{ text: "浏览器扩展", link: "/extension" },
|
||||
{ text: "下载接口", link: "/api" },
|
||||
],
|
||||
},
|
||||
],
|
||||
"/": [
|
||||
{
|
||||
text: "开始",
|
||||
items: [
|
||||
{ text: "快速开始", link: "/guides" },
|
||||
{ text: "使用说明", link: "/documents" },
|
||||
{ text: "下载接口", link: "/api" },
|
||||
{ text: "更新日志", link: "/changelog" },
|
||||
{ text: "通过宝塔面板部署", link: "/bt-install" },
|
||||
{ text: "浏览器扩展", link: "/extension" },
|
||||
{ text: "配合猫爪下载视频", link: "/catcatch" },
|
||||
{ text: "🦞 OpenClaw Skill", link: "/skills" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Q&A",
|
||||
items: [
|
||||
{ text: "常见问题", link: "/qa" },
|
||||
{ text: "windows7支持(64位)", link: "/history" },
|
||||
{ text: "意见收集", link: "/proposal" },
|
||||
{ text: "支持列表", link: "/list" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
socialLinks: [
|
||||
{ icon: "github", link: "https://github.com/caorushizi/m3u8-downloader" },
|
||||
@@ -66,30 +326,55 @@ export default defineConfig({
|
||||
nav: [
|
||||
{ text: "Home", link: "/en" },
|
||||
{ text: "Guides", link: "/en/guides" },
|
||||
{ text: "Blog", link: "/en/blog/" },
|
||||
{ text: "Changelog", link: "/en/changelog" },
|
||||
],
|
||||
|
||||
sidebar: [
|
||||
{
|
||||
text: "Quick start",
|
||||
items: [
|
||||
{ text: "Quick start", link: "/en/guides" },
|
||||
{ text: "Baota Panel", link: "/en/bt-install" },
|
||||
{ text: "Documents", link: "/en/documents" },
|
||||
{ text: "Changelog", link: "/en/changelog" },
|
||||
{ text: "Browser extension", link: "/en/extension" },
|
||||
{ text: "🦞 OpenClaw Skill", link: "/en/skills" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Q&A",
|
||||
items: [
|
||||
{ text: "History", link: "/en/history" },
|
||||
{ text: "Proposal", link: "/en/proposal" },
|
||||
{ text: "Support list", link: "/en/list" },
|
||||
],
|
||||
},
|
||||
],
|
||||
sidebar: {
|
||||
"/en/blog/": [
|
||||
{
|
||||
text: "Blog",
|
||||
items: [
|
||||
{ text: "Blog Home", link: "/en/blog/" },
|
||||
{
|
||||
text: "Video Downloader Review",
|
||||
link: "/en/blog/video-downloader-review/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Product Docs",
|
||||
items: [
|
||||
{ text: "Quick Start", link: "/en/guides" },
|
||||
{ text: "User Guide", link: "/en/documents" },
|
||||
{ text: "Browser Extension", link: "/en/extension" },
|
||||
{ text: "Download API", link: "/en/api" },
|
||||
],
|
||||
},
|
||||
],
|
||||
"/en/": [
|
||||
{
|
||||
text: "Quick start",
|
||||
items: [
|
||||
{ text: "Quick start", link: "/en/guides" },
|
||||
{ text: "Baota Panel", link: "/en/bt-install" },
|
||||
{ text: "Documents", link: "/en/documents" },
|
||||
{ text: "Download API", link: "/en/api" },
|
||||
{ text: "Changelog", link: "/en/changelog" },
|
||||
{ text: "Browser extension", link: "/en/extension" },
|
||||
{ text: "🦞 OpenClaw Skill", link: "/en/skills" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Q&A",
|
||||
items: [
|
||||
{ text: "History", link: "/en/history" },
|
||||
{ text: "Proposal", link: "/en/proposal" },
|
||||
{ text: "Support list", link: "/en/list" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
socialLinks: [
|
||||
{
|
||||
@@ -116,6 +401,7 @@ export default defineConfig({
|
||||
{ text: "早く始めます", link: "/jp/guides" },
|
||||
{ text: "塔のパネル配置です", link: "/jp/bt-install" },
|
||||
{ text: "使用説明書です", link: "/jp/documents" },
|
||||
{ text: "ダウンロード API", link: "/jp/api" },
|
||||
{ text: "ログを更新します。", link: "/jp/changelog" },
|
||||
{ text: "ブラウザ拡張機能", link: "/jp/extension" },
|
||||
{ text: "🦞 OpenClaw Skill", link: "/jp/skills" },
|
||||
@@ -131,6 +417,47 @@ export default defineConfig({
|
||||
},
|
||||
],
|
||||
|
||||
socialLinks: [
|
||||
{
|
||||
icon: "github",
|
||||
link: "https://github.com/caorushizi/mediago",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
it: {
|
||||
label: "Italiano",
|
||||
lang: "it",
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{ text: "Home", link: "/it" },
|
||||
{ text: "Guide", link: "/it/guides" },
|
||||
{ text: "Changelog", link: "/it/changelog" },
|
||||
],
|
||||
|
||||
sidebar: [
|
||||
{
|
||||
text: "Avvio rapido",
|
||||
items: [
|
||||
{ text: "Avvio rapido", link: "/it/guides" },
|
||||
{ text: "BT Panel", link: "/it/bt-install" },
|
||||
{ text: "Guida utente", link: "/it/documents" },
|
||||
{ text: "API di download", link: "/it/api" },
|
||||
{ text: "Changelog", link: "/it/changelog" },
|
||||
{ text: "Estensione browser", link: "/it/extension" },
|
||||
{ text: "🦞 OpenClaw Skill", link: "/it/skills" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Q&A",
|
||||
items: [
|
||||
{ text: "Versioni precedenti", link: "/it/history" },
|
||||
{ text: "Feedback", link: "/it/proposal" },
|
||||
{ text: "Siti supportati", link: "/it/list" },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
socialLinks: [
|
||||
{
|
||||
icon: "github",
|
||||
|
||||
@@ -1,21 +1,61 @@
|
||||
<script setup lang="ts">
|
||||
import DefaultTheme from "vitepress/theme";
|
||||
import { useData, inBrowser } from "vitepress";
|
||||
import { watchEffect } from "vue";
|
||||
import { useData, inBrowser, useRoute } from "vitepress";
|
||||
import { computed, watchEffect } from "vue";
|
||||
import Comments from "./components/Comments.vue";
|
||||
import Footer from "./components/Footer.vue";
|
||||
import QrCode from "./components/QrCode.vue";
|
||||
import TopBanner from "./components/TopBanner.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import AdBanner from "./components/AdBanner.vue";
|
||||
|
||||
const { lang } = useData();
|
||||
const route = useRoute();
|
||||
const { locale } = useI18n();
|
||||
|
||||
const translatedBlogPaths = new Set([
|
||||
"/blog/video-downloader-review/",
|
||||
"/en/blog/video-downloader-review/",
|
||||
]);
|
||||
|
||||
const translatedBlogLinks: Record<
|
||||
string,
|
||||
{ href: string; label: string; note: string }
|
||||
> = {
|
||||
"/blog/video-downloader-review/": {
|
||||
href: "/en/blog/video-downloader-review/",
|
||||
label: "English",
|
||||
note: "这篇文章已有英文版",
|
||||
},
|
||||
"/en/blog/video-downloader-review/": {
|
||||
href: "/blog/video-downloader-review/",
|
||||
label: "简体中文",
|
||||
note: "This article is also available in Chinese",
|
||||
},
|
||||
};
|
||||
|
||||
function normalizeRoutePath(path: string) {
|
||||
return path.replace(/index\.html$/, "").replace(/\.html$/, "/");
|
||||
}
|
||||
|
||||
const normalizedPath = computed(() => normalizeRoutePath(route.path));
|
||||
const blogTranslationLink = computed(
|
||||
() => translatedBlogLinks[normalizedPath.value],
|
||||
);
|
||||
|
||||
watchEffect(() => {
|
||||
locale.value = lang.value;
|
||||
|
||||
if (inBrowser) {
|
||||
locale.value = lang.value;
|
||||
const isBlogPage =
|
||||
normalizedPath.value.startsWith("/blog/") ||
|
||||
normalizedPath.value.startsWith("/en/blog/") ||
|
||||
normalizedPath.value.startsWith("/jp/blog/") ||
|
||||
normalizedPath.value.startsWith("/it/blog/");
|
||||
|
||||
document.cookie = `nf_lang=${lang.value}; expires=Mon, 1 Jan 2030 00:00:00 UTC; path=/`;
|
||||
document.documentElement.classList.toggle("is-blog-page", isBlogPage);
|
||||
document.documentElement.classList.toggle(
|
||||
"has-blog-translation",
|
||||
translatedBlogPaths.has(normalizedPath.value),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -24,14 +64,15 @@ const { Layout } = DefaultTheme;
|
||||
|
||||
<template>
|
||||
<Layout>
|
||||
<template #layout-top>
|
||||
<TopBanner />
|
||||
</template>
|
||||
<!-- <template #sidebar-nav-before>
|
||||
<AdBanner />
|
||||
</template> -->
|
||||
<template #doc-footer-before>
|
||||
<QrCode />
|
||||
<template #doc-before>
|
||||
<div
|
||||
v-if="blogTranslationLink"
|
||||
class="blog-language-switch"
|
||||
aria-label="Article language switch"
|
||||
>
|
||||
<span>{{ blogTranslationLink.note }}</span>
|
||||
<a :href="blogTranslationLink.href">{{ blogTranslationLink.label }}</a>
|
||||
</div>
|
||||
</template>
|
||||
<template #doc-after>
|
||||
<Comments />
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 MiB |
@@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<div class="ad-container">
|
||||
<img src="/images/ad.png" alt="ad" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped>
|
||||
.ad-container {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -17,18 +17,7 @@
|
||||
<div class="text-sm text-[var(--vp-c-text-2)]">{{ t("slogan") }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧区域 -->
|
||||
<div class="flex flex-col md:flex-row gap-8 md:gap-12">
|
||||
<!-- 二维码部分 -->
|
||||
<div class="px-3 flex-shrink-0 mt-8 md:mt-0">
|
||||
<!-- <div class="font-semibold mb-2 text-[var(--vp-c-text-1)]">{{ t('contact') }}</div> -->
|
||||
<img
|
||||
src="../assets/wx.png"
|
||||
alt="WeChat QR Code"
|
||||
class="w-[280px] object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 帮助链接部分 -->
|
||||
<div class="px-3 text-sm flex-shrink-0 min-w-[150px] mt-8 md:mt-0">
|
||||
<div class="font-semibold mb-2 text-[var(--vp-c-text-1)]">
|
||||
@@ -37,29 +26,10 @@
|
||||
<ul class="list-none m-0 p-0">
|
||||
<li class="mb-1">
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://mediago.pro/"
|
||||
href="/blog/"
|
||||
class="text-[var(--vp-c-text-2)] no-underline transition-colors hover:text-[var(--vp-c-text-1)] duration-300"
|
||||
>
|
||||
{{ t("search") }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 友情链接部分 -->
|
||||
<div class="px-3 text-sm flex-shrink-0 min-w-[150px] mt-8 md:mt-0">
|
||||
<div class="font-semibold mb-2 text-[var(--vp-c-text-1)]">
|
||||
{{ t("links") }}
|
||||
</div>
|
||||
<ul class="list-none m-0 p-0">
|
||||
<li class="mb-1">
|
||||
<a
|
||||
href="https://www.jiexi.im/player/"
|
||||
class="text-[var(--vp-c-text-2)] no-underline transition-colors hover:text-[var(--vp-c-text-1)] duration-300"
|
||||
target="_blank"
|
||||
>
|
||||
{{ t("jiexi.im") }}
|
||||
{{ t("blog") }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<template>
|
||||
<div class="mt-12 py-6 text-center border-t border-[var(--vp-c-divider)]">
|
||||
<img
|
||||
src="../assets/wx.png"
|
||||
alt="WeChat QR Code"
|
||||
class="w-[300px] object-contain mx-auto"
|
||||
/>
|
||||
<p class="mt-3 text-sm text-[var(--vp-c-text-2)]">
|
||||
扫码关注,获取更多精彩内容
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,245 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const isVisible = ref(true);
|
||||
|
||||
const closeBanner = () => {
|
||||
isVisible.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="isVisible" class="top-banner">
|
||||
<div class="banner-content">
|
||||
<div class="banner-left">
|
||||
<span class="banner-badge">{{ t("banner.new") }}</span>
|
||||
<span class="banner-text">
|
||||
<span class="banner-highlight">{{ t("banner.title") }}</span>
|
||||
{{ t("banner.desc") }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="banner-right">
|
||||
<a
|
||||
href="https://mediago.torchstellar.com/?from=banner"
|
||||
target="_blank"
|
||||
class="banner-button"
|
||||
>
|
||||
{{ t("banner.action") }}
|
||||
<svg
|
||||
class="banner-icon"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path d="M5 12h14M12 5l7 7-7 7" />
|
||||
</svg>
|
||||
</a>
|
||||
<button
|
||||
class="banner-close"
|
||||
@click="closeBanner"
|
||||
:aria-label="t('banner.close')"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path d="M18 6L6 18M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="banner-shimmer"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.top-banner {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
width: 100%;
|
||||
height: var(--vp-layout-top-height, 60px);
|
||||
background: linear-gradient(135deg, #5e9ef3 0%, #2a82f6 50%, #1a6dd6 100%);
|
||||
padding: 16px 20px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.banner-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
padding: 0 36px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.banner-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.banner-badge {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
backdrop-filter: blur(10px);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
padding: 4px 10px;
|
||||
border-radius: 20px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 0 20px 5px rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.banner-text {
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.banner-highlight {
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.banner-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.banner-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
color: #2a82f6;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding: 8px 18px;
|
||||
border-radius: 25px;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.banner-button:hover {
|
||||
background: #fff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.banner-button:hover .banner-icon {
|
||||
transform: translateX(3px);
|
||||
}
|
||||
|
||||
.banner-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.banner-close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.banner-close:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.banner-close svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.banner-shimmer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.15),
|
||||
transparent
|
||||
);
|
||||
animation: shimmer 3s infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
left: -100%;
|
||||
}
|
||||
100% {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile responsiveness */
|
||||
@media (max-width: 768px) {
|
||||
.top-banner {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.banner-content {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.banner-left {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.banner-text {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.banner-button {
|
||||
font-size: 12px;
|
||||
padding: 6px 14px;
|
||||
}
|
||||
|
||||
.banner-close {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -13,49 +13,22 @@ const i18n = createI18n({
|
||||
slogan: "Easy to use, fast download",
|
||||
articles: "Articles",
|
||||
help: "Help",
|
||||
search: "intelligent search",
|
||||
blog: "Blog",
|
||||
privacy: "Privacy Policy",
|
||||
links: "Links",
|
||||
"jiexi.im": "Smart Video Analysis",
|
||||
banner: {
|
||||
new: "NEW",
|
||||
title: "MediaGo Pro is Live!",
|
||||
desc: "- Experience faster downloads & exclusive features",
|
||||
action: "Try Now",
|
||||
close: "Close",
|
||||
},
|
||||
},
|
||||
jp: {
|
||||
slogan: "使いやすく、ダウンロードも速い",
|
||||
articles: "記事",
|
||||
help: "ヘルプ",
|
||||
search: "スマート検索",
|
||||
blog: "ブログ",
|
||||
privacy: "プライバシーポリシー",
|
||||
links: "友情リンク",
|
||||
"jiexi.im": "スマートビデオ解析",
|
||||
banner: {
|
||||
new: "新着",
|
||||
title: "MediaGo Pro がリリースされました!",
|
||||
desc: "- より高速なダウンロードと限定機能をお楽しみください",
|
||||
action: "今すぐ試す",
|
||||
close: "閉じる",
|
||||
},
|
||||
},
|
||||
zh: {
|
||||
slogan: "简单易用,快速下载",
|
||||
articles: "文章",
|
||||
help: "帮助",
|
||||
search: "智能搜索",
|
||||
blog: "博客",
|
||||
privacy: "隐私政策",
|
||||
links: "友情链接",
|
||||
"jiexi.im": "智能视频解析",
|
||||
banner: {
|
||||
new: "全新上线",
|
||||
title: "MediaGo Pro 正式发布!",
|
||||
desc: "- 更快的下载速度,更多专属功能",
|
||||
action: "立即体验",
|
||||
close: "关闭",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,13 +1,63 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
/* Top Banner - 使用 VitePress 官方变量 */
|
||||
:root {
|
||||
--vp-layout-top-height: 60px;
|
||||
/* Keep blog tables inside the article column. */
|
||||
.vp-doc[class*="_blog_"] table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* 移动端横幅高度 */
|
||||
@media (max-width: 768px) {
|
||||
:root {
|
||||
--vp-layout-top-height: 80px;
|
||||
.vp-doc[class*="_blog_"] th,
|
||||
.vp-doc[class*="_blog_"] td {
|
||||
min-width: 120px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.vp-doc[class*="_blog_"] th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Blog translations are handled by an explicit article-level link to avoid untranslated locale URLs. */
|
||||
body:has(.vp-doc[class*="_blog_"]) .VPNavBarTranslations,
|
||||
body:has(.vp-doc[class*="_blog_"]) .VPNavBarExtra .translations,
|
||||
body:has(.vp-doc[class*="_blog_"]) .VPNavScreen .translations {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.blog-language-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin: 0 0 24px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.blog-language-switch a {
|
||||
flex: 0 0 auto;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
background: var(--vp-c-brand-soft);
|
||||
color: var(--vp-c-brand-1);
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.blog-language-switch a:hover {
|
||||
background: var(--vp-c-brand-2);
|
||||
color: var(--vp-c-white);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.blog-language-switch {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 下载接口
|
||||
|
||||
MediaGo 把下载核心暴露成一个 HTTP 服务。桌面端在 `39719` 端口,Docker 部署在 `9900` 端口。
|
||||
|
||||
你可以用任何支持 HTTP 的工具(curl / Python / Node.js / Postman 等)直接调用接口,新建下载任务、启动、停止、查询进度 —— MediaGo 自己的浏览器扩展、AI Skill 都是这套接口的消费者。
|
||||
|
||||
## 基础信息
|
||||
|
||||
### 接口地址
|
||||
|
||||
| 部署方式 | Base URL |
|
||||
| -------- | ---------------------------------------------------- |
|
||||
| 桌面端 | `http://localhost:39719` |
|
||||
| Docker | `http://<服务器地址>:9900`(按实际 `-p` 端口映射调整) |
|
||||
|
||||
所有接口都在 `/api` 前缀下。下文示例默认用桌面端的 `39719` 端口,Docker 部署请自行替换。
|
||||
|
||||
### 响应格式
|
||||
|
||||
所有 `/api/*` 接口都返回统一的 JSON 包裹结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"code": 0,
|
||||
"message": "ok",
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --------- | ------ | --------------------------- |
|
||||
| `success` | bool | 业务是否成功 |
|
||||
| `code` | number | 业务错误码,`0` 表示成功 |
|
||||
| `message` | string | 人类可读的提示 |
|
||||
| `data` | any | 实际响应载荷,结构因接口而异 |
|
||||
|
||||
下文示例中的"响应"只展示 `data` 字段的内容。
|
||||
|
||||
### 认证
|
||||
|
||||
- **桌面端**:默认**无需认证**,直接请求 `localhost:39719` 即可
|
||||
- **Docker 部署**:启用认证时,在 MediaGo **设置页面**中获取 API Key,之后的请求带 `Authorization: Bearer <key>` 头
|
||||
|
||||
## 快速上手
|
||||
|
||||
下面这三条命令串起"新建 → 开始下载 → 完成通知"的完整流程。
|
||||
|
||||
### 1. 新建下载任务
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:39719/api/downloads \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"name": "我的视频"
|
||||
}
|
||||
],
|
||||
"startDownload": true
|
||||
}'
|
||||
```
|
||||
|
||||
- `type`:下载类型,可选 `m3u8` / `bilibili` / `direct` / `youtube` / `mediago`
|
||||
- `url`:视频链接
|
||||
- `name`:任务名称(会作为保存文件名)
|
||||
- `startDownload`:创建后是否立即开始下载
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 123,
|
||||
"name": "我的视频",
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"status": "waiting",
|
||||
"createdDate": "2026-04-23T10:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
记下返回的 `id`,后续接口会用到。
|
||||
|
||||
### 2. 订阅下载事件(SSE)
|
||||
|
||||
```bash
|
||||
curl -N http://localhost:39719/api/events
|
||||
```
|
||||
|
||||
这是一条长连接,服务端推什么、你收什么:
|
||||
|
||||
```text
|
||||
event: download-start
|
||||
data: {"id": "123"}
|
||||
|
||||
event: download-success
|
||||
data: {"id": "123"}
|
||||
```
|
||||
|
||||
浏览器 / Node.js 里:
|
||||
|
||||
```javascript
|
||||
const es = new EventSource("http://localhost:39719/api/events");
|
||||
es.addEventListener("download-success", (e) => {
|
||||
const { id } = JSON.parse(e.data);
|
||||
console.log("任务完成:", id);
|
||||
});
|
||||
```
|
||||
|
||||
### 3. 查询状态 / 手动控制
|
||||
|
||||
```bash
|
||||
# 列出所有下载任务(分页)
|
||||
curl "http://localhost:39719/api/downloads?current=1&pageSize=20"
|
||||
|
||||
# 查单个任务
|
||||
curl http://localhost:39719/api/downloads/123
|
||||
|
||||
# 启动已存在的任务
|
||||
curl -X POST http://localhost:39719/api/downloads/123/start \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"localPath": "/Downloads/MediaGo", "deleteSegments": true}'
|
||||
|
||||
# 停止任务
|
||||
curl -X POST http://localhost:39719/api/downloads/123/stop
|
||||
|
||||
# 查下载日志
|
||||
curl http://localhost:39719/api/downloads/123/logs
|
||||
```
|
||||
|
||||
## 下载事件
|
||||
|
||||
`GET /api/events` 是 Server-Sent Events 流,下载相关的事件:
|
||||
|
||||
| 事件名 | 载荷 | 说明 |
|
||||
| ------------------ | -------------------------------- | ------------ |
|
||||
| `download-create` | `{ids: number[], count: number}` | 批量创建任务 |
|
||||
| `download-start` | `{id: string}` | 下载开始 |
|
||||
| `download-success` | `{id: string}` | 下载成功 |
|
||||
| `download-failed` | `{id: string, error: string}` | 下载失败 |
|
||||
| `download-stop` | `{id: string}` | 下载手动停止 |
|
||||
|
||||
## 接口参考
|
||||
|
||||
### 列表 / 查询
|
||||
|
||||
#### `GET /api/downloads` — 分页列出下载任务
|
||||
|
||||
**Query 参数:**
|
||||
|
||||
- `current` (number, 默认 1):页码
|
||||
- `pageSize` (number, 默认 20):每页条数
|
||||
- `filter` (string, 可选):按状态筛选,如 `downloading` / `success` / `failed`
|
||||
- `localPath` (string, 可选):按保存路径筛选
|
||||
|
||||
**响应:**
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 42,
|
||||
"list": [
|
||||
/* DownloadTask[] */
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/downloads/active` — 列出活动中的任务
|
||||
|
||||
返回所有 `waiting` / `downloading` 状态的任务。
|
||||
|
||||
#### `GET /api/downloads/:id` — 查单个任务
|
||||
|
||||
**响应**(`DownloadTask` 结构):
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"name": "我的视频",
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"folder": "my-folder",
|
||||
"headers": "User-Agent: ...",
|
||||
"isLive": false,
|
||||
"status": "success",
|
||||
"file": "/path/to/saved.mp4",
|
||||
"createdDate": "2026-04-23T10:00:00Z",
|
||||
"updatedDate": "2026-04-23T10:05:30Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/downloads/folders` — 列出所有不重复的保存目录
|
||||
|
||||
**响应:** `string[]`
|
||||
|
||||
#### `GET /api/downloads/export` — 导出下载列表
|
||||
|
||||
返回纯文本,每行一个 URL。
|
||||
|
||||
#### `GET /api/downloads/:id/logs` — 查下载日志
|
||||
|
||||
**响应:** `{ id, log: string }`
|
||||
|
||||
### 创建 / 删除
|
||||
|
||||
#### `POST /api/downloads` — 批量新建下载
|
||||
|
||||
**请求体:**
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "m3u8 | bilibili | direct | youtube | mediago",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"name": "任务名",
|
||||
"folder": "可选子目录",
|
||||
"headers": "可选,多行 HTTP 头"
|
||||
}
|
||||
],
|
||||
"startDownload": true
|
||||
}
|
||||
```
|
||||
|
||||
**响应:** `DownloadTask[]`
|
||||
|
||||
#### `DELETE /api/downloads/:id` — 删除任务
|
||||
|
||||
**响应:** `{}`
|
||||
|
||||
### 编辑 / 状态
|
||||
|
||||
#### `PUT /api/downloads/:id` — 编辑任务
|
||||
|
||||
**请求体**(字段都可选):
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "新名字",
|
||||
"url": "新 URL",
|
||||
"headers": "新的 headers",
|
||||
"folder": "新的子目录"
|
||||
}
|
||||
```
|
||||
|
||||
#### `PUT /api/downloads/:id/live` — 标记 / 取消直播流
|
||||
|
||||
**请求体:** `{ "isLive": true }`
|
||||
|
||||
#### `PUT /api/downloads/status` — 批量修改任务状态
|
||||
|
||||
**请求体:** `{ "ids": number[], "status": "waiting | downloading | success | failed | stopped" }`
|
||||
|
||||
### 启动 / 停止
|
||||
|
||||
#### `POST /api/downloads/:id/start` — 启动下载
|
||||
|
||||
**请求体:**
|
||||
|
||||
```json
|
||||
{
|
||||
"localPath": "/Users/me/Downloads/MediaGo",
|
||||
"deleteSegments": true
|
||||
}
|
||||
```
|
||||
|
||||
- `localPath`:保存到哪里(绝对路径)
|
||||
- `deleteSegments`:m3u8 下载完成后是否删除分段 `.ts` 文件
|
||||
|
||||
#### `POST /api/downloads/:id/stop` — 停止下载
|
||||
|
||||
**响应:** `{}`
|
||||
|
||||
## 枚举值
|
||||
|
||||
### 下载类型 `type`
|
||||
|
||||
| 值 | 说明 |
|
||||
| ---------- | ----------------------------------- |
|
||||
| `m3u8` | HLS 流媒体(底层 N_m3u8DL-RE) |
|
||||
| `bilibili` | B 站视频(底层 BBDown) |
|
||||
| `direct` | 直接 HTTP 下载(底层 aria2) |
|
||||
| `youtube` | YouTube 及 yt-dlp 支持的 1000+ 站点 |
|
||||
| `mediago` | MediaGo 内部类型 |
|
||||
|
||||
### 任务状态 `status`
|
||||
|
||||
| 值 | 说明 |
|
||||
| ------------- | ---------- |
|
||||
| `waiting` | 等待开始 |
|
||||
| `downloading` | 下载中 |
|
||||
| `success` | 已完成 |
|
||||
| `failed` | 失败 |
|
||||
| `stopped` | 已手动停止 |
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: MediaGo 博客
|
||||
description: MediaGo 博客围绕网页视频下载、M3U8/HLS、视频嗅探、NAS/Docker 部署和下载器评测,提供面向搜索和 AI 摘要的专题指南。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [MediaGo, 视频下载器, m3u8, HLS, 视频嗅探]
|
||||
---
|
||||
|
||||
# MediaGo 博客
|
||||
|
||||
这里是 MediaGo 的学习中心,内容围绕网页视频下载、M3U8/HLS、视频嗅探、NAS/Docker 部署、下载器评测和自动化工作流展开。
|
||||
|
||||
如果你刚开始了解 MediaGo,建议先看 [快速开始](/guides);如果你正在比较工具,优先阅读 [2026 年视频下载器推荐](/blog/video-downloader-recommendation/),再进入完整评测报告。
|
||||
|
||||
## 推荐阅读路径
|
||||
|
||||
| 主题 | 适合问题 | 推荐入口 |
|
||||
| ------------ | ------------------------------------------------------------- | -------------------------------------------------------- |
|
||||
| 工具推荐 | 哪个视频下载器更适合我?免费、电脑端、m3u8、IDM、猫抓怎么选? | [视频下载器推荐](/blog/video-downloader-recommendation/) |
|
||||
| 深度评测 | MediaGo 和 yt-dlp、4K、NAS 工具有什么区别? | [视频下载器评测](/blog/video-downloader-review/) |
|
||||
| 网页视频下载 | 网页里的视频怎么下载?B站视频怎么保存?复制链接无效怎么办? | [网页视频下载指南](/blog/video-download/) |
|
||||
| M3U8 / HLS | m3u8 视频怎么下载?下载失败、转 MP4、NAS 工具怎么选? | [M3U8 / HLS 下载指南](/blog/m3u8-hls-download/) |
|
||||
| 视频嗅探 | 视频嗅探器浏览器哪个好用?插件、内置浏览器、网页工具怎么选? | [网页视频嗅探指南](/blog/video-sniffer/) |
|
||||
|
||||
## 支柱专题
|
||||
|
||||
### [2026 年视频下载器推荐](/blog/video-downloader-recommendation/)
|
||||
|
||||
面向国内搜索里的“视频下载器推荐”“视频下载软件哪个好”“m3u8 下载器”“IDM 下载器”“猫抓下载器”等需求,先给出按场景选择工具的结论。
|
||||
|
||||
配套文章:
|
||||
|
||||
- [IDM 下载器和 MediaGo 怎么选?](/blog/video-downloader-review/idm-vs-mediago/)
|
||||
- [m3u8 下载器推荐:电脑、Docker、NAS 和浏览器工具怎么选?](/blog/video-downloader-review/m3u8-downloader-recommendation/)
|
||||
- [猫抓下载器和 MediaGo 有什么区别?](/blog/video-downloader-review/cat-catch-vs-mediago/)
|
||||
- [MediaGo 和 yt-dlp 怎么选?](/blog/video-downloader-review/mediago-vs-ytdlp/)
|
||||
|
||||
### [2026 年视频下载器评测](/blog/video-downloader-review/)
|
||||
|
||||
从站点覆盖、HLS/M3U8、浏览器嗅探、NAS/Docker、API、AI 后处理等维度,对比 MediaGo、yt-dlp、4K Video Downloader、Video DownloadHelper、MeTube、Tube Archivist 等工具。
|
||||
|
||||
子文章:
|
||||
|
||||
- [IDM 下载器和 MediaGo 怎么选?](/blog/video-downloader-review/idm-vs-mediago/)
|
||||
- [m3u8 下载器推荐:电脑、Docker、NAS 和浏览器工具怎么选?](/blog/video-downloader-review/m3u8-downloader-recommendation/)
|
||||
- [猫抓下载器和 MediaGo 有什么区别?](/blog/video-downloader-review/cat-catch-vs-mediago/)
|
||||
- [MediaGo 和 yt-dlp 怎么选?](/blog/video-downloader-review/mediago-vs-ytdlp/)
|
||||
- [MediaGo 和 4K Video Downloader 怎么选?](/blog/video-downloader-review/mediago-vs-4k-video-downloader/)
|
||||
- [NAS 视频下载器怎么选?](/blog/video-downloader-review/nas-video-downloader-tools/)
|
||||
|
||||
### [网页视频下载完整指南](/blog/video-download/)
|
||||
|
||||
解释网页视频下载的常见入口、使用场景、操作流程和工具选择,适合作为普通用户的入门专题。
|
||||
|
||||
子文章:
|
||||
|
||||
- [网页视频怎么下载?](/blog/video-download/download-web-video/)
|
||||
- [网页视频下载不了怎么办?](/blog/video-download/web-video-download-failed/)
|
||||
- [B站视频怎么下载?](/blog/video-download/bilibili-video-download/)
|
||||
- [B站视频怎么转文字稿、提取字幕和音频?](/blog/video-download/bilibili-video-to-text-audio/)
|
||||
- [课程回看视频怎么下载?](/blog/video-download/download-course-video/)
|
||||
- [视频下载工作流怎么搭?](/blog/video-download/video-download-workflow/)
|
||||
|
||||
### [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
|
||||
围绕 m3u8、HLS、DASH、直播流和分段视频,说明协议差异、下载流程和常见失败原因。
|
||||
|
||||
子文章:
|
||||
|
||||
- [M3U8 是什么?](/blog/m3u8-hls-download/what-is-m3u8/)
|
||||
- [m3u8 视频怎么下载?](/blog/m3u8-hls-download/download-m3u8-video/)
|
||||
- [m3u8 下载失败怎么办?](/blog/m3u8-hls-download/fix-m3u8-download-failed/)
|
||||
- [m3u8 下载后怎么转 MP4?](/blog/m3u8-hls-download/m3u8-to-mp4/)
|
||||
|
||||
### [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
|
||||
解释浏览器扩展、内置浏览器和桌面端嗅探的区别,帮助用户选择更稳定的网页视频识别方式。
|
||||
|
||||
子文章:
|
||||
|
||||
- [Chrome 视频嗅探怎么用?](/blog/video-sniffer/chrome-video-sniffer/)
|
||||
- [视频嗅探器浏览器哪个好用?](/blog/video-sniffer/browser-extension-vs-desktop/)
|
||||
- [为什么网页视频嗅探到很多资源?](/blog/video-sniffer/detect-video-resources/)
|
||||
|
||||
## 相关产品文档
|
||||
|
||||
- [快速开始](/guides)
|
||||
- [使用说明](/documents)
|
||||
- [浏览器扩展](/extension)
|
||||
- [下载接口](/api)
|
||||
- [Docker / 宝塔面板部署](/bt-install)
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: m3u8 视频怎么下载?MediaGo 下载 HLS 视频教程
|
||||
description: 介绍 m3u8/HLS 视频的下载步骤,包括网页嗅探、选择资源、下载分片、合并保存和常见注意事项。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [m3u8 下载, HLS 下载, 视频下载, MediaGo]
|
||||
---
|
||||
|
||||
# m3u8 视频怎么下载?
|
||||
|
||||
一句话答案:可以用 MediaGo 打开视频页面,自动识别 HLS/M3U8 资源,然后选择目标清晰度加入下载队列,由下载器完成分片下载和合并保存。
|
||||
|
||||
## 快速步骤
|
||||
|
||||
1. 打开 MediaGo。
|
||||
2. 使用内置浏览器访问目标视频页面。
|
||||
3. 播放视频并等待资源识别。
|
||||
4. 在资源列表中选择 m3u8/HLS 项。
|
||||
5. 添加下载任务。
|
||||
6. 等待分片下载、合并和保存完成。
|
||||
|
||||
## 下载前检查
|
||||
|
||||
- 视频页面是否可以正常播放;
|
||||
- 是否需要登录或授权访问;
|
||||
- 目标资源是否有多个清晰度;
|
||||
- 下载路径是否有足够空间;
|
||||
- 是否需要保留字幕、音频或封面。
|
||||
|
||||
## 为什么推荐内置浏览器
|
||||
|
||||
内置浏览器可以保留页面播放过程中的上下文信息,比如 Referer、Cookie、User-Agent 和临时请求。对于复杂的 HLS 页面,它比手动复制 m3u8 更稳定。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/) 的子文章,用于承接 “m3u8 视频怎么下载” 这个核心长尾词。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [M3U8 是什么?](/blog/m3u8-hls-download/what-is-m3u8/)
|
||||
- [m3u8 下载失败怎么办?](/blog/m3u8-hls-download/fix-m3u8-download-failed/)
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: m3u8 下载失败怎么办?HLS 视频常见问题排查
|
||||
description: 总结 m3u8/HLS 视频下载失败的常见原因,包括链接过期、请求头缺失、分片失败、音视频分离和直播流停止条件。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [m3u8 下载失败, HLS, 视频下载问题, MediaGo]
|
||||
---
|
||||
|
||||
# m3u8 下载失败怎么办?
|
||||
|
||||
一句话答案:m3u8 下载失败通常和链接过期、请求头缺失、分片无法访问、音视频分离或直播流规则有关。优先用 MediaGo 内置浏览器重新识别资源,再确认页面是否仍能正常播放。
|
||||
|
||||
## 常见原因
|
||||
|
||||
| 问题 | 可能原因 | 处理建议 |
|
||||
| -------------------- | ---------------------------------- | ---------------------- |
|
||||
| 任务一开始就失败 | m3u8 地址过期 | 重新打开页面并识别资源 |
|
||||
| 部分分片失败 | 网络波动或分片地址失效 | 重试任务或降低并发 |
|
||||
| 播放器能看但下载失败 | 缺少 Referer、Cookie 或 User-Agent | 使用内置浏览器识别 |
|
||||
| 只有视频没有声音 | 音视频分离 | 检查是否需要混流 |
|
||||
| 直播流无法结束 | 没有固定总时长 | 设置停止条件或手动停止 |
|
||||
|
||||
## 排查顺序
|
||||
|
||||
1. 确认网页还能正常播放;
|
||||
2. 重新用内置浏览器打开页面;
|
||||
3. 重新选择资源并创建任务;
|
||||
4. 检查网络和保存路径;
|
||||
5. 如果是直播流,确认停止条件;
|
||||
6. 如果仍失败,再考虑手动工具或日志排查。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/) 的子文章,用于承接下载失败和问题排查场景。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [M3U8 是什么?](/blog/m3u8-hls-download/what-is-m3u8/)
|
||||
- [m3u8 视频怎么下载?](/blog/m3u8-hls-download/download-m3u8-video/)
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: M3U8 / HLS 视频下载完整指南
|
||||
description: 解释 M3U8、HLS、DASH、直播流和分段视频的关系,并介绍如何使用 MediaGo 下载和处理网页中的流媒体资源。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [m3u8, HLS, DASH, 直播流, MediaGo]
|
||||
---
|
||||
|
||||
# M3U8 / HLS 视频下载完整指南
|
||||
|
||||
一句话答案:m3u8 是 HLS 流媒体常见的播放列表格式,里面通常记录了一组视频分片地址。MediaGo 可以识别网页中的 HLS/M3U8 资源,并调用内置下载能力完成分片下载、合并和保存。
|
||||
|
||||
这篇支柱页覆盖“m3u8下载”“m3u8下载器”“m3u8下载器电脑版”“m3u8下载转mp4工具”“HLS下载”等搜索意图。概念、教程、排错和工具选型会分到不同子文章里。
|
||||
|
||||
## M3U8、HLS 和 DASH 是什么
|
||||
|
||||
| 名称 | 说明 | 常见场景 |
|
||||
| ------------- | ------------------------------------------------------ | ---------------------------- |
|
||||
| M3U8 | HLS 播放列表文件,通常包含清晰度、分片地址或子播放列表 | 网页视频、课程回看、直播回放 |
|
||||
| HLS | Apple 推出的 HTTP Live Streaming 协议 | 移动端播放、直播、长视频平台 |
|
||||
| DASH / MPD | 另一类自适应流媒体协议 | YouTube、部分国际视频平台 |
|
||||
| TS / M4S 分片 | 实际承载视频和音频的数据片段 | HLS/DASH 下载和合并 |
|
||||
|
||||
## 为什么 m3u8 下载容易失败
|
||||
|
||||
- 播放地址有时效性,过期后无法继续访问;
|
||||
- 视频分片需要 Referer、Cookie、User-Agent 等请求头;
|
||||
- 音频和视频可能分离,需要下载后重新混流;
|
||||
- 直播流没有固定结束点,需要按任务规则停止;
|
||||
- 部分页面只暴露播放器地址,不直接暴露 m3u8 地址。
|
||||
|
||||
## MediaGo 的处理方式
|
||||
|
||||
MediaGo 的价值在于把“识别资源”和“下载处理”连起来。用户不需要手动抓包、复制 m3u8、拼接 ts 分片或反复调整命令行参数。
|
||||
|
||||
推荐流程:
|
||||
|
||||
1. 用 MediaGo 内置浏览器打开视频页面。
|
||||
2. 等待资源列表识别出 HLS/M3U8 或直播流。
|
||||
3. 选择目标清晰度和视频资源。
|
||||
4. 添加下载任务。
|
||||
5. 下载完成后按需进行格式转换或移动端播放。
|
||||
|
||||
## 什么时候用专业命令行工具
|
||||
|
||||
如果你已经拿到了稳定的 m3u8、mpd 或直播流地址,并且需要精细控制 headers、分片并发、混流、字幕、断点续传等参数,N_m3u8DL-RE、FFmpeg 和 Streamlink 仍然适合高级场景。
|
||||
|
||||
如果你希望把这些能力放到图形界面、浏览器嗅探、下载队列和 NAS/Docker 工作流里,MediaGo 会更省心。
|
||||
|
||||
## 按搜索问题快速进入
|
||||
|
||||
| 你搜索的问题 | 应该先看 |
|
||||
| ------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| m3u8 是什么、HLS 是什么 | [M3U8 是什么?](/blog/m3u8-hls-download/what-is-m3u8/) |
|
||||
| m3u8 视频怎么下载 | [m3u8 视频怎么下载?](/blog/m3u8-hls-download/download-m3u8-video/) |
|
||||
| m3u8 下载失败、key 请求失败 | [m3u8 下载失败怎么办?](/blog/m3u8-hls-download/fix-m3u8-download-failed/) |
|
||||
| m3u8 下载转 MP4 工具 | [m3u8 下载后怎么转 MP4?](/blog/m3u8-hls-download/m3u8-to-mp4/) |
|
||||
| 好用的 m3u8 下载器、电脑版、NAS | [m3u8 下载器推荐](/blog/video-downloader-review/m3u8-downloader-recommendation/) |
|
||||
|
||||
## 专题文章
|
||||
|
||||
| 文章 | 解决的问题 | 目标搜索意图 |
|
||||
| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------ | --------------------------- |
|
||||
| [M3U8 是什么?](/blog/m3u8-hls-download/what-is-m3u8/) | 解释 M3U8、HLS、TS 分片关系 | m3u8 是什么、HLS 是什么 |
|
||||
| [m3u8 视频怎么下载?](/blog/m3u8-hls-download/download-m3u8-video/) | 具体下载步骤和工具流程 | m3u8 视频怎么下载、HLS 下载 |
|
||||
| [m3u8 下载失败怎么办?](/blog/m3u8-hls-download/fix-m3u8-download-failed/) | 排查链接过期、请求头、分片失败 | m3u8 下载失败、HLS 下载失败 |
|
||||
| [m3u8 下载后怎么转 MP4?](/blog/m3u8-hls-download/m3u8-to-mp4/) | 分片合并、转码、离线播放 | m3u8 下载转 mp4 工具 |
|
||||
| [m3u8 下载器推荐:电脑、Docker、NAS 和浏览器工具怎么选?](/blog/video-downloader-review/m3u8-downloader-recommendation/) | 工具选型、桌面端、NAS 场景 | m3u8 下载器、HLS 下载工具 |
|
||||
|
||||
## 常见问题
|
||||
|
||||
### m3u8 文件就是视频文件吗?
|
||||
|
||||
不是。m3u8 通常是播放列表,真正的视频数据在它引用的分片里。下载工具需要读取播放列表,再逐个获取分片并合并。
|
||||
|
||||
### 为什么播放器能看,下载器不能下?
|
||||
|
||||
播放器可能携带了页面上下文、Cookie、Referer 或临时签名。下载器如果没有这些请求信息,就可能访问失败。
|
||||
|
||||
### 直播流可以下载吗?
|
||||
|
||||
可以,但直播流和普通点播不同。它通常没有固定总时长,需要下载器持续读取新分片,并按用户设置的时间或手动停止条件结束任务。
|
||||
|
||||
### m3u8 下载后一定要转 MP4 吗?
|
||||
|
||||
不一定。如果播放器能直接读取本地 HLS 文件夹,可以不转。但对普通用户来说,MP4 更适合离线播放、移动设备传输和长期归档。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
- [2026 年视频下载器评测](/blog/video-downloader-review/)
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: m3u8 下载后怎么转 MP4?自动合并、转码和离线播放完整说明
|
||||
description: 解释 m3u8 下载转 MP4 的流程,说明为什么下载后只有几 KB、为什么需要合并 TS/M4S 分片,以及如何用 MediaGo、FFmpeg 等工具完成转码。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [m3u8下载转mp4工具, m3u8下载, HLS下载, m3u8转MP4, 视频格式转换]
|
||||
---
|
||||
|
||||
# m3u8 下载后怎么转 MP4?自动合并、转码和离线播放完整说明
|
||||
|
||||
一句话答案:m3u8 本身通常不是视频文件,而是 HLS 播放列表。要转成 MP4,需要先读取播放列表,下载里面的 TS/M4S 分片,再合并或转码成一个本地 MP4 文件。MediaGo 适合普通用户完成识别、下载、合并和后续格式处理;FFmpeg 更适合高级参数和脚本化处理。
|
||||
|
||||
本文只讨论公开可访问内容、用户自有内容、授权内容、课程回看、企业内部资料和个人素材归档。
|
||||
|
||||
## m3u8、TS 分片和 MP4 的区别
|
||||
|
||||
| 名称 | 它是什么 | 能不能直接当视频播放 |
|
||||
| -------- | ------------------------------ | -------------------------------- |
|
||||
| m3u8 | 播放列表,记录分片地址和清晰度 | 通常不能,它只是索引文件 |
|
||||
| TS / M4S | 真实的视频或音频分片 | 单个分片通常不适合独立观看 |
|
||||
| MP4 | 常见本地视频容器 | 可以,适合离线播放和跨设备保存 |
|
||||
| HLS | 使用 m3u8 和分片的流媒体协议 | 需要播放器或下载器按顺序读取分片 |
|
||||
|
||||
如果你下载后只看到一个几 KB 的 m3u8 文件,说明你保存的是播放列表,不是完整视频。
|
||||
|
||||
## m3u8 转 MP4 的完整流程
|
||||
|
||||
1. 获取有效的 m3u8 播放列表。
|
||||
2. 读取播放列表里的分片地址。
|
||||
3. 下载所有 TS/M4S 分片。
|
||||
4. 如果音频和视频分离,需要分别下载后混流。
|
||||
5. 合并分片,必要时转码为 MP4。
|
||||
6. 检查时长、画面、声音和字幕是否正常。
|
||||
|
||||
普通用户不建议手动复制分片地址。分片数量可能很多,而且地址经常带临时签名,复制后很快过期。
|
||||
|
||||
## 用 MediaGo 处理 m3u8 转 MP4
|
||||
|
||||
MediaGo 更适合不想写命令的用户。推荐方式是:
|
||||
|
||||
1. 用 MediaGo 内置浏览器打开视频页面。
|
||||
2. 等待资源列表识别出 HLS/M3U8。
|
||||
3. 选择目标清晰度或主播放列表。
|
||||
4. 添加下载任务。
|
||||
5. 下载完成后按需进行格式转换或移动端播放。
|
||||
|
||||
这种方式的关键是保留页面上下文。很多 m3u8 资源需要 Referer、Cookie、User-Agent 或临时签名,直接复制链接可能会失败。
|
||||
|
||||
## 用 FFmpeg 转 MP4 适合什么情况
|
||||
|
||||
如果你已经拿到了稳定、有效、可访问的 m3u8 地址,并且熟悉命令行,可以使用 FFmpeg 处理。
|
||||
|
||||
常见场景包括:
|
||||
|
||||
- 已有本地 m3u8 和分片文件;
|
||||
- 需要批量转码;
|
||||
- 需要指定编码、码率、字幕或音轨;
|
||||
- 需要在服务器或自动化脚本里处理。
|
||||
|
||||
但如果 m3u8 地址依赖浏览器登录态、临时签名或请求头,单独拿 FFmpeg 处理可能会遇到 403、key 请求失败或分片下载失败。这时先用 MediaGo 重新识别资源更稳。
|
||||
|
||||
## 下载后还用联网吗
|
||||
|
||||
如果已经完整下载并合并为 MP4,本地播放通常不需要联网。如果你只保存了 m3u8 播放列表,播放时仍然需要访问原始分片地址;一旦分片过期或服务器不可访问,就无法播放。
|
||||
|
||||
所以判断是否真正离线保存,可以看这三点:
|
||||
|
||||
1. 文件是不是一个完整 MP4,而不是几 KB 的 m3u8;
|
||||
2. 断网后是否能播放;
|
||||
3. 视频时长、声音和画面是否完整。
|
||||
|
||||
## 转 MP4 后没有画面或没有声音怎么办
|
||||
|
||||
| 现象 | 可能原因 | 处理方式 |
|
||||
| ------------ | -------------------------- | ------------------------ |
|
||||
| 有声音没画面 | 视频轨没有合并或编码不兼容 | 重新下载完整视频轨并转码 |
|
||||
| 有画面没声音 | 音频轨分离或缺失 | 检查是否有单独音频资源 |
|
||||
| 时长不完整 | 分片缺失或下载中断 | 重新识别资源后重试 |
|
||||
| 文件无法播放 | 容器或编码不兼容 | 转为通用 MP4 编码 |
|
||||
|
||||
如果问题发生在下载阶段,先看:[m3u8 下载失败怎么办?](/blog/m3u8-hls-download/fix-m3u8-download-failed/)。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### m3u8 下载转 MP4 工具怎么选?
|
||||
|
||||
普通用户优先选择能自动识别、下载、合并和转换的图形界面工具,例如 MediaGo。开发者和高级用户可以选择 FFmpeg、N_m3u8DL-RE 等命令行工具。
|
||||
|
||||
### m3u8 下载器电脑版一定比浏览器插件好吗?
|
||||
|
||||
不一定。插件适合发现资源,桌面端适合下载、合并、转码和失败重试。复杂网页通常需要两者配合,或直接使用内置浏览器。
|
||||
|
||||
### m3u8 转 MP4 会降低画质吗?
|
||||
|
||||
如果只是无损封装或合并,画质通常不会明显变化。如果重新编码、压缩码率或转换格式,画质可能下降。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
- [m3u8 视频怎么下载?](/blog/m3u8-hls-download/download-m3u8-video/)
|
||||
- [m3u8 下载器推荐:电脑、Docker、NAS 和浏览器工具怎么选?](/blog/video-downloader-review/m3u8-downloader-recommendation/)
|
||||
- [网页视频下载不了怎么办?](/blog/video-download/web-video-download-failed/)
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: M3U8 是什么?HLS 播放列表、TS 分片和网页视频的关系
|
||||
description: 用通俗方式解释 M3U8、HLS、TS 分片和网页视频播放的关系,帮助用户理解为什么 m3u8 不是普通视频文件。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [m3u8, HLS, TS 分片, 流媒体]
|
||||
---
|
||||
|
||||
# M3U8 是什么?
|
||||
|
||||
一句话答案:M3U8 通常不是视频文件本身,而是 HLS 流媒体的播放列表,里面记录了不同清晰度、音视频轨道或视频分片地址。
|
||||
|
||||
## M3U8 的作用
|
||||
|
||||
播放器拿到 m3u8 后,会继续读取里面的分片地址,再按顺序下载并播放这些分片。用户看到的是连续视频,底层可能是一系列 ts 或 m4s 小文件。
|
||||
|
||||
## 常见结构
|
||||
|
||||
| 内容 | 说明 |
|
||||
| ------------- | ------------------------------- |
|
||||
| 主播放列表 | 可能包含多个清晰度或码率 |
|
||||
| 子播放列表 | 指向具体分片列表 |
|
||||
| TS / M4S 分片 | 实际承载视频或音频数据 |
|
||||
| Key 信息 | 某些 HLS 流可能包含加密相关信息 |
|
||||
|
||||
## 为什么理解 M3U8 很重要
|
||||
|
||||
理解 m3u8 之后,就能知道为什么普通复制链接经常失败:你复制到的可能只是页面地址,真正需要处理的是播放列表、分片、请求头和有效期。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/) 的子文章,负责解释基础概念。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
- [m3u8 视频怎么下载?](/blog/m3u8-hls-download/download-m3u8-video/)
|
||||
- [m3u8 下载失败怎么办?](/blog/m3u8-hls-download/fix-m3u8-download-failed/)
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: B站视频怎么下载?保存到电脑、手机本地和后续处理方法
|
||||
description: 面向“b站视频怎么下载”“b站视频怎么下载到本地”“b站视频如何下载到电脑/手机”的合规教程,说明公开内容、自有稿件和课程回看的保存思路。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags:
|
||||
[b站视频怎么下载, b站视频下载, b站视频下载到本地, b站视频下载工具, Bilibili]
|
||||
---
|
||||
|
||||
# B站视频怎么下载?保存到电脑、手机本地和后续处理方法
|
||||
|
||||
一句话答案:B站视频下载要先确认你是否有权保存该内容。对公开可访问内容、自有稿件、已授权素材、课程回看或企业内部资料,可以用 MediaGo 这类工具在电脑端识别真实视频资源,再保存到本地、NAS 或后续处理流程中。
|
||||
|
||||
本文只讨论公开可访问内容、用户自有内容、授权内容、课程回看、企业内部资料和个人素材归档,不讨论去水印、绕过权限、破解会员内容或下载受保护内容。
|
||||
|
||||
## 适合哪些场景
|
||||
|
||||
| 场景 | 是否适合写入下载流程 | 说明 |
|
||||
| ---------------------- | -------------------- | ------------------------ |
|
||||
| 自己发布的稿件 | 适合 | 用于备份、剪辑、归档 |
|
||||
| 已获授权的视频素材 | 适合 | 适合团队协作和素材管理 |
|
||||
| 课程回看或学习资料 | 视授权而定 | 只处理有保存权限的内容 |
|
||||
| 公开素材或公开视频 | 视平台规则而定 | 需要遵守平台和原作者要求 |
|
||||
| 会员、付费、受保护内容 | 不建议 | 不讨论绕过限制或访问控制 |
|
||||
|
||||
## B站视频下载到电脑怎么做
|
||||
|
||||
推荐先用电脑端处理,因为电脑端更适合识别、下载、合并、转码和长期管理。
|
||||
|
||||
1. 安装 MediaGo 桌面端或部署 Docker/NAS 版本。
|
||||
2. 打开需要保存的 B站视频页面。
|
||||
3. 用 MediaGo 内置浏览器或浏览器扩展识别视频资源。
|
||||
4. 在资源列表里选择目标视频、清晰度或音视频资源。
|
||||
5. 添加到下载队列,等待下载、合并和格式处理完成。
|
||||
6. 按需移动到素材库、NAS、课程资料文件夹或后续处理流程。
|
||||
|
||||
如果复制页面链接后不能下载,通常是因为页面地址不是视频真实地址。可以看:[网页视频下载不了怎么办?](/blog/video-download/web-video-download-failed/)。
|
||||
|
||||
## B站视频怎么下载到手机本地
|
||||
|
||||
如果你搜索的是“b站视频如何下载到手机”“b站视频怎么保存在手机本地”,要先区分两件事:
|
||||
|
||||
- B站客户端缓存:适合在 App 内离线观看,但通常不是通用本地视频文件;
|
||||
- 本地视频文件:适合跨设备播放、剪辑、归档或转文字,需要先完整保存为可播放文件。
|
||||
|
||||
MediaGo 当前更适合电脑端、浏览器扩展、Docker/NAS 和局域网访问。更稳的流程是先在电脑或 NAS 上完成识别和下载,再通过局域网、移动硬盘、网盘或媒体库在手机上访问。
|
||||
|
||||
## B站视频链接在哪里找
|
||||
|
||||
用户常问“B站视频的链接在哪里找到”,通常有三类链接:
|
||||
|
||||
| 链接类型 | 说明 | 下载时是否足够 |
|
||||
| ------------ | ---------------------------- | ------------------------ |
|
||||
| 页面链接 | 浏览器地址栏里的视频页面地址 | 不一定,常只是播放器页面 |
|
||||
| 分享链接 | App 或网页分享出来的短链接 | 不一定,需要还原到页面 |
|
||||
| 真实视频资源 | 播放过程中请求的视频或音频流 | 下载器真正需要识别的资源 |
|
||||
|
||||
普通用户不建议手动从开发者工具里找资源。B站页面可能存在音视频分离、清晰度选择、临时签名和分段请求,手动复制很容易选错或过期。
|
||||
|
||||
## B站视频下载工具怎么选
|
||||
|
||||
| 需求 | 更适合的方式 |
|
||||
| ---------------------- | ------------------------------ |
|
||||
| 不想写命令 | MediaGo 桌面端或内置浏览器 |
|
||||
| 边浏览边识别 | MediaGo 浏览器扩展 |
|
||||
| 批量、脚本化 | 命令行工具或 API 工作流 |
|
||||
| 长期保存和多端访问 | Docker/NAS、局域网和私有媒体库 |
|
||||
| 下载后转文字或提取音频 | 下载后处理流程 |
|
||||
|
||||
如果你的重点是工具对比,可以先看:[2026 年视频下载器推荐](/blog/video-downloader-recommendation/)。
|
||||
|
||||
## 下载后可以做什么
|
||||
|
||||
B站视频下载到本地后,常见后续需求包括:
|
||||
|
||||
- 转为 MP4 便于本地播放;
|
||||
- 提取音频或转 MP3;
|
||||
- 提取字幕或转文字稿;
|
||||
- 生成 AI 总结;
|
||||
- 归档到 NAS 或课程资料库;
|
||||
- 按项目、课程、作者或日期整理。
|
||||
|
||||
如果你的目标是“b站视频转文字稿、提取字幕、提取音频 mp3”,可以看:[B站视频怎么转文字稿、提取字幕和音频?](/blog/video-download/bilibili-video-to-text-audio/)。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### B站视频缓存等于下载到本地吗?
|
||||
|
||||
不完全等于。缓存通常服务于 App 内离线观看,不一定是可自由管理的本地 MP4 文件。如果你需要剪辑、转文字或归档,需要保存为可处理的视频文件。
|
||||
|
||||
### B站视频可以批量下载吗?
|
||||
|
||||
自有稿件、授权课程或公开资料可以考虑批量整理,但要遵守平台规则和内容授权。批量下载更适合放到后续工作流或 NAS 自动化中,不建议把它写成绕过限制的教程。
|
||||
|
||||
### B站视频去水印、无水印下载要不要做?
|
||||
|
||||
不建议。去水印和无水印下载容易涉及平台规则和作者权益。MediaGo 内容应该围绕有权保存和处理的场景,不主攻这类词。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [网页视频下载不了怎么办?](/blog/video-download/web-video-download-failed/)
|
||||
- [视频嗅探器浏览器哪个好用?](/blog/video-sniffer/browser-extension-vs-desktop/)
|
||||
- [B站视频怎么转文字稿、提取字幕和音频?](/blog/video-download/bilibili-video-to-text-audio/)
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: B站视频怎么转文字稿、提取字幕和音频?
|
||||
description: 面向“b站视频怎么转文字”“b站视频提取字幕”“b站视频提取音频 mp3”“b站视频 AI 总结”的后处理指南,说明下载后转写、字幕和音频处理流程。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags:
|
||||
[b站视频转文字, b站视频提取字幕, b站视频提取音频, b站视频转mp3, b站视频AI总结]
|
||||
---
|
||||
|
||||
# B站视频怎么转文字稿、提取字幕和音频?
|
||||
|
||||
一句话答案:B站视频转文字稿、提取字幕或音频,通常分两步:先在有授权的前提下保存视频或音频文件,再使用转写、字幕提取、音频提取或 AI 总结工具处理。MediaGo 适合作为“识别和保存视频资源”的入口,后续可以接转写和音频处理流程。
|
||||
|
||||
本文只讨论公开可访问内容、用户自有内容、授权内容、课程回看、企业内部资料和个人素材归档,不讨论去水印、绕过权限或处理无授权内容。
|
||||
|
||||
## 常见后处理需求
|
||||
|
||||
| 需求 | 目标结果 | 适合场景 |
|
||||
| --------------- | -------------------- | ------------------------ |
|
||||
| B站视频转文字稿 | 一份可编辑文本 | 课程笔记、会议资料、复盘 |
|
||||
| B站视频提取字幕 | srt、vtt 或文本字幕 | 学习、剪辑、资料整理 |
|
||||
| B站视频提取音频 | 音频文件 | 听课、播客化、语音资料 |
|
||||
| B站视频转 mp3 | 更通用的音频格式 | 手机收听、离线整理 |
|
||||
| B站视频 AI 总结 | 摘要、要点、章节大纲 | 快速浏览长视频内容 |
|
||||
|
||||
## 推荐处理流程
|
||||
|
||||
1. 确认内容是否允许保存和二次处理。
|
||||
2. 使用 MediaGo 保存公开视频、自有稿件或授权课程回看。
|
||||
3. 检查下载文件是否完整,确认画面、声音和时长正常。
|
||||
4. 如果只需要音频,先提取音轨或转 MP3。
|
||||
5. 如果需要文字稿,使用语音转文字工具生成初稿。
|
||||
6. 对照原视频校对术语、时间点、人名和专有名词。
|
||||
7. 按用途整理成字幕、笔记、摘要或知识库内容。
|
||||
|
||||
## 转文字稿和提取字幕有什么区别
|
||||
|
||||
| 类型 | 来源 | 优点 | 局限 |
|
||||
| -------- | ------------------------ | -------------------------- | -------------------- |
|
||||
| 提取字幕 | 视频自带字幕或外挂字幕 | 准确度通常更高,时间轴完整 | 前提是原视频有字幕 |
|
||||
| 语音转写 | 从音频识别出文字 | 没有字幕也能生成文本 | 需要校对,受音质影响 |
|
||||
| AI 总结 | 基于文字稿或字幕生成摘要 | 适合快速提炼要点 | 不能替代原文校对 |
|
||||
|
||||
如果视频没有字幕,先提取音频再做语音转文字会更稳。音质差、多人说话、背景音乐明显的视频,需要人工校对。
|
||||
|
||||
## B站视频提取音频 mp3 怎么做
|
||||
|
||||
如果你有权处理该视频,可以先保存完整视频,再从视频文件中提取音频或转为 MP3。
|
||||
|
||||
适合提取音频的场景:
|
||||
|
||||
- 自己上传的视频备份;
|
||||
- 已授权课程或培训内容;
|
||||
- 公开演讲、公开资料的个人学习归档;
|
||||
- 企业内部视频资料整理。
|
||||
|
||||
不建议把“转 MP3”写成规避平台播放规则或无授权搬运的教程。SEO 页面可以承接这个搜索意图,但正文必须清楚说明使用边界。
|
||||
|
||||
## AI 总结适合放在哪一步
|
||||
|
||||
AI 总结最好放在转文字之后,而不是直接跳过文字稿。推荐顺序是:
|
||||
|
||||
1. 下载或保存有权处理的视频;
|
||||
2. 提取音频或字幕;
|
||||
3. 生成文字稿;
|
||||
4. 清理口癖、错字和重复段落;
|
||||
5. 再生成摘要、章节、关键词和待办事项。
|
||||
|
||||
这样得到的总结更稳定,也更适合被搜索和 AI 摘要理解。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### B站视频提取字幕一定能成功吗?
|
||||
|
||||
不一定。只有视频本身带字幕或可识别的字幕轨时,才可能直接提取。没有字幕的视频,需要用语音转文字生成。
|
||||
|
||||
### B站视频 AI 总结可以直接替代看视频吗?
|
||||
|
||||
不建议。AI 总结适合快速了解结构和重点,但关键信息、数据、引用和结论仍然需要回到原视频或文字稿核对。
|
||||
|
||||
### B站视频去水印、无水印和提取文字是一回事吗?
|
||||
|
||||
不是。提取文字和字幕是内容理解与资料整理;去水印和无水印下载涉及平台规则和作者权益,不建议作为 MediaGo 的内容方向。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [B站视频怎么下载?](/blog/video-download/bilibili-video-download/)
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [视频下载工作流怎么搭?](/blog/video-download/video-download-workflow/)
|
||||
- [网页视频下载不了怎么办?](/blog/video-download/web-video-download-failed/)
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: 课程回看视频怎么下载?公开课程与自有学习资料保存思路
|
||||
description: 介绍课程回看、培训视频和自有学习资料的保存流程,以及如何使用 MediaGo 识别网页视频资源并管理下载任务。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [课程回看, 网页视频下载, 学习资料, MediaGo]
|
||||
---
|
||||
|
||||
# 课程回看视频怎么下载?
|
||||
|
||||
一句话答案:课程回看通常嵌在网页播放器里,可能使用 MP4、M3U8/HLS 或分段流。MediaGo 可以在授权访问的前提下,通过内置浏览器识别播放过程中的视频资源,并加入下载队列保存。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- 公开课程回看;
|
||||
- 个人购买或授权访问的学习资料;
|
||||
- 企业内部培训资料;
|
||||
- 自己上传或制作的视频素材;
|
||||
- 需要长期归档的学习视频。
|
||||
|
||||
## 推荐流程
|
||||
|
||||
1. 确认你有保存该视频的权限。
|
||||
2. 使用 MediaGo 内置浏览器打开课程页面。
|
||||
3. 播放视频,等待资源识别。
|
||||
4. 选择目标资源并添加下载任务。
|
||||
5. 使用文件夹、命名规则或 NAS 路径整理资料。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 为什么课程视频比普通网页视频更难下载?
|
||||
|
||||
课程平台常常依赖登录态、临时链接、请求头和分段播放。使用内置浏览器能更好地保留播放上下文。
|
||||
|
||||
### 下载后怎么管理?
|
||||
|
||||
可以按课程、章节、日期或主题建立目录。后续如果使用 NAS 形态,可以进一步做媒体库和跨端播放。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [网页视频下载完整指南](/blog/video-download/) 的子文章,用于承接课程回看和学习资料保存场景。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
- [NAS 视频下载器怎么选?](/blog/video-downloader-review/nas-video-downloader-tools/)
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: 网页视频怎么下载?MediaGo 保存网页视频的基础流程
|
||||
description: 介绍网页视频下载的基本流程,包括打开页面、识别真实资源、选择清晰度、添加下载任务和处理常见失败原因。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [网页视频下载, 视频下载, MediaGo, 视频资源]
|
||||
---
|
||||
|
||||
# 网页视频怎么下载?
|
||||
|
||||
一句话答案:网页视频通常需要先识别真实媒体资源,再添加到下载器。MediaGo 可以通过内置浏览器或浏览器扩展识别网页中的视频资源,然后把目标资源加入下载队列。
|
||||
|
||||
## 快速步骤
|
||||
|
||||
1. 打开 MediaGo。
|
||||
2. 使用内置浏览器打开目标视频页面。
|
||||
3. 等待 MediaGo 自动识别视频资源。
|
||||
4. 选择目标清晰度或目标视频流。
|
||||
5. 添加到下载队列。
|
||||
6. 下载完成后按需转换格式或在局域网播放。
|
||||
|
||||
## 为什么网页地址不等于视频地址
|
||||
|
||||
很多网页只是播放器页面,真正的视频地址可能来自接口、m3u8 播放列表、mpd 清单、ts 分片或临时签名链接。只复制浏览器地址栏里的 URL,下载器不一定能直接处理。
|
||||
|
||||
## 推荐入口
|
||||
|
||||
如果你只是偶尔下载,可以先用内置浏览器。日常浏览视频较多时,可以安装 [浏览器扩展](/extension),检测到资源后直接发送到 MediaGo。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [网页视频下载完整指南](/blog/video-download/) 的子文章,用于承接 “网页视频怎么下载” 这个基础搜索问题。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
- [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: 网页视频下载完整指南
|
||||
description: 介绍网页视频下载的常见方式、适用场景、工具选择,以及如何使用 MediaGo 通过内置浏览器、浏览器扩展和下载队列保存视频资源。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [网页视频下载, 视频下载器, MediaGo, 浏览器扩展]
|
||||
---
|
||||
|
||||
# 网页视频下载完整指南
|
||||
|
||||
一句话答案:网页视频下载的关键不是“复制一个页面地址”,而是识别页面背后的真实视频资源。MediaGo 可以通过内置浏览器、Chrome/Edge 扩展和下载队列,把网页视频识别、选择、下载和格式转换放在同一个工作流里。
|
||||
|
||||
这篇支柱页覆盖“网页视频下载”“网页视频怎么下载”“网页视频下载工具”“网页视频下载插件”“网页视频下载不了”“b站视频怎么下载”等搜索意图。具体教程、平台场景和排错会放到子文章里,避免把所有问题都堆在一个页面里。
|
||||
|
||||
## 适合谁阅读
|
||||
|
||||
- 想下载公开网页视频、课程回看、自有素材或企业内部资料的用户;
|
||||
- 不想学习 yt-dlp、FFmpeg、N_m3u8DL-RE 命令行参数的普通用户;
|
||||
- 需要同时覆盖普通 MP4、HLS/M3U8、DASH、直播流和 Bilibili、YouTube、Twitter/X 等站点的用户;
|
||||
- 希望把下载任务放到 Docker、NAS 或局域网环境中长期运行的用户。
|
||||
|
||||
## 常见下载方式
|
||||
|
||||
| 方式 | 优点 | 局限 | 适合场景 |
|
||||
| ---------------- | -------------------- | ------------------------------ | ---------------------- |
|
||||
| 复制链接到下载器 | 简单直接 | 页面地址不一定等于真实视频地址 | 平台链接、公开视频 |
|
||||
| 浏览器扩展嗅探 | 入口自然,边看边识别 | 容易受浏览器限制和站点策略影响 | Chrome/Edge 日常浏览 |
|
||||
| 内置浏览器嗅探 | 识别和下载链路更完整 | 需要在客户端里打开页面 | 复杂网页视频、课程回看 |
|
||||
| 命令行工具 | 参数灵活,适合批处理 | 学习成本高 | 开发者、脚本化任务 |
|
||||
| NAS / Docker | 可长期运行,多端访问 | 初始部署成本更高 | 家庭服务器、私有媒体库 |
|
||||
|
||||
## MediaGo 的推荐流程
|
||||
|
||||
1. 安装 MediaGo 桌面端或部署 Docker 版本。
|
||||
2. 打开需要下载的视频页面。
|
||||
3. 使用内置浏览器或 Chrome/Edge 扩展识别视频资源。
|
||||
4. 在资源列表里选择目标清晰度、格式或视频流。
|
||||
5. 添加到下载队列,等待下载和格式处理完成。
|
||||
6. 如需自动化,使用 [HTTP 下载接口](/api) 或 [OpenClaw Skill](/skills) 创建任务。
|
||||
|
||||
## 和其他工具怎么选
|
||||
|
||||
如果你只需要命令行批处理,可以直接使用 yt-dlp、FFmpeg 或 N_m3u8DL-RE。如果你想要图形界面、网页嗅探、批量队列、NAS/Docker 和 API 工作流,MediaGo 更适合作为入口工具。
|
||||
|
||||
更完整的横向对比可以阅读:[2026 年视频下载器评测:MediaGo、yt-dlp、4K、NAS 工具与浏览器插件对比](/blog/video-downloader-review/)。
|
||||
|
||||
## 按搜索问题快速进入
|
||||
|
||||
| 你搜索的问题 | 应该先看 |
|
||||
| ---------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| 网页视频怎么下载、下载方法 | [网页视频怎么下载?](/blog/video-download/download-web-video/) |
|
||||
| 网页视频下载不了、复制链接不能下载 | [网页视频下载不了怎么办?](/blog/video-download/web-video-download-failed/) |
|
||||
| 网页视频下载插件、Edge 插件 | [视频嗅探器浏览器哪个好用?](/blog/video-sniffer/browser-extension-vs-desktop/) |
|
||||
| B站视频怎么下载、保存到本地 | [B站视频怎么下载?](/blog/video-download/bilibili-video-download/) |
|
||||
| B站视频转文字、提取字幕或音频 | [B站视频怎么转文字稿、提取字幕和音频?](/blog/video-download/bilibili-video-to-text-audio/) |
|
||||
| m3u8 视频怎么下载 | [m3u8 视频怎么下载?](/blog/m3u8-hls-download/download-m3u8-video/) |
|
||||
| 网页视频下载到电脑后怎么管理 | [视频下载工作流怎么搭?](/blog/video-download/video-download-workflow/) |
|
||||
|
||||
## 专题文章
|
||||
|
||||
| 文章 | 解决的问题 | 目标搜索意图 |
|
||||
| ------------------------------------------------------------------------------------------- | ---------------------------- | -------------------------- |
|
||||
| [网页视频怎么下载?](/blog/video-download/download-web-video/) | 基础网页视频保存流程 | 网页视频怎么下载 |
|
||||
| [网页视频下载不了怎么办?](/blog/video-download/web-video-download-failed/) | 复制链接无效、插件识别失败 | 网页视频下载不了 |
|
||||
| [B站视频怎么下载?](/blog/video-download/bilibili-video-download/) | B站平台场景、电脑和手机访问 | b站视频怎么下载 |
|
||||
| [B站视频怎么转文字稿、提取字幕和音频?](/blog/video-download/bilibili-video-to-text-audio/) | 下载后的字幕、音频、AI 总结 | b站视频转文字、提取字幕 |
|
||||
| [课程回看视频怎么下载?](/blog/video-download/download-course-video/) | 课程、培训、自有学习资料保存 | 课程回看下载、学习资料保存 |
|
||||
| [视频下载工作流怎么搭?](/blog/video-download/video-download-workflow/) | 从网页识别到 NAS 归档的流程 | 视频下载工作流、NAS 归档 |
|
||||
| [视频嗅探器浏览器哪个好用?](/blog/video-sniffer/browser-extension-vs-desktop/) | 浏览器插件、桌面端入口选择 | 网页视频下载插件、视频嗅探 |
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 网页视频下载一定需要浏览器插件吗?
|
||||
|
||||
不一定。浏览器插件适合日常浏览时快速发送任务;MediaGo 桌面端也提供内置浏览器,可以直接在客户端里打开页面并自动识别资源。
|
||||
|
||||
### 为什么有些网页复制链接后不能下载?
|
||||
|
||||
很多网页地址只是播放器页面,真实视频资源可能是 m3u8、mpd、ts 分片、临时签名地址或平台接口返回的数据。遇到这种情况,需要通过嗅探或平台解析能力识别真实资源。
|
||||
|
||||
### 网页视频下载工具和网页视频下载插件怎么选?
|
||||
|
||||
插件适合发现资源,下载工具适合下载、合并、重试、转码和管理文件。普通网页可以先用插件;复杂页面、m3u8/HLS 或失败排查更适合 MediaGo 内置浏览器。
|
||||
|
||||
### B站视频怎么下载到本地?
|
||||
|
||||
先确认你有权保存该内容。自有稿件、授权内容、课程回看或公开素材可以作为合规场景处理;具体流程见:[B站视频怎么下载?](/blog/video-download/bilibili-video-download/)。
|
||||
|
||||
### MediaGo 和 yt-dlp 是替代关系吗?
|
||||
|
||||
不是单纯替代关系。yt-dlp 更像底层下载生态,适合命令行和脚本;MediaGo 更像产品化入口,把底层工具能力整合为图形界面、浏览器入口、API、Docker/NAS 和队列管理。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [2026 年视频下载器评测](/blog/video-downloader-review/)
|
||||
- [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
- [MediaGo 快速开始](/guides)
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: 视频下载工作流怎么搭?从网页识别到 NAS 归档的完整流程
|
||||
description: 介绍从网页视频识别、下载队列、格式转换、命名整理到 NAS 或局域网播放的视频下载工作流设计。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [视频下载工作流, NAS, Docker, MediaGo, 自动化]
|
||||
---
|
||||
|
||||
# 视频下载工作流怎么搭?
|
||||
|
||||
一句话答案:稳定的视频下载工作流应该包含资源识别、任务队列、格式处理、文件命名、归档路径和多端访问。MediaGo 可以把内置浏览器、浏览器扩展、下载队列、API 和 Docker/NAS 连接起来。
|
||||
|
||||
## 推荐工作流
|
||||
|
||||
1. 浏览器或内置浏览器识别视频资源;
|
||||
2. 添加到 MediaGo 下载队列;
|
||||
3. 按任务类型选择保存路径;
|
||||
4. 下载完成后进行格式转换或混流;
|
||||
5. 按项目、课程、平台或日期整理目录;
|
||||
6. 在局域网、NAS 或移动端访问内容;
|
||||
7. 通过 API 或 Agent 处理重复任务。
|
||||
|
||||
## 为什么要设计工作流
|
||||
|
||||
一次性下载不难,难的是长期维护。视频数量变多后,如果没有命名、目录、队列和归档规则,很容易出现重复下载、找不到文件、格式不统一和跨设备访问困难。
|
||||
|
||||
## MediaGo 的角色
|
||||
|
||||
MediaGo 更适合作为视频下载工作流的入口层:负责识别资源、创建任务、处理队列,并连接桌面端、浏览器扩展、HTTP API 和 Docker/NAS 环境。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [网页视频下载完整指南](/blog/video-download/) 的子文章,用于承接 “视频下载工作流” 和 “长期管理” 场景。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [NAS 视频下载器怎么选?](/blog/video-downloader-review/nas-video-downloader-tools/)
|
||||
- [下载接口](/api)
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: 网页视频下载不了怎么办?复制链接无效、插件识别不到和 m3u8 失败排查
|
||||
description: 面向“网页视频下载不了”的排错指南,解释复制页面链接无效、浏览器插件识别不到、m3u8 地址过期、请求头缺失和分片下载失败等常见原因。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [网页视频下载不了, 网页视频下载, 视频下载失败, m3u8下载失败, 视频嗅探]
|
||||
---
|
||||
|
||||
# 网页视频下载不了怎么办?复制链接无效、插件识别不到和 m3u8 失败排查
|
||||
|
||||
一句话答案:网页视频下载不了,通常不是“下载器坏了”,而是页面地址不等于真实视频地址,或者真实资源需要播放过程里的 Cookie、Referer、User-Agent、临时签名和分片列表。先确认视频能正常播放,再用 MediaGo 内置浏览器或浏览器扩展重新识别资源。
|
||||
|
||||
本文只讨论公开可访问内容、用户自有内容、授权内容、课程回看、企业内部资料和个人素材归档,不讨论绕过访问控制或下载受保护内容。
|
||||
|
||||
## 先判断是哪一种失败
|
||||
|
||||
| 现象 | 常见原因 | 优先处理方式 |
|
||||
| -------------------------- | ----------------------------------- | ------------------------------ |
|
||||
| 复制网页链接后提示无法下载 | 页面地址不是视频真实地址 | 用内置浏览器或扩展嗅探真实资源 |
|
||||
| 插件没有识别到视频 | 页面未播放、懒加载、扩展权限限制 | 先播放几秒,再刷新资源列表 |
|
||||
| m3u8 下载到一半失败 | 分片地址过期或请求头缺失 | 重新打开原网页,重新识别 m3u8 |
|
||||
| 下载后只有几 KB | 保存的是播放列表,不是完整视频 | 让下载器读取 m3u8 并下载分片 |
|
||||
| 下载后有声音没画面 | 音视频分离或格式不兼容 | 下载完整资源后转码或重新混流 |
|
||||
| 浏览器能看,下载器不能下 | 播放器带了 Cookie、Referer 等上下文 | 使用内置浏览器保留页面上下文 |
|
||||
|
||||
## 为什么复制网页链接经常不能下载
|
||||
|
||||
很多网页的视频地址不是直接写在页面 URL 里。用户复制的通常是播放器页面地址,而不是 MP4、M3U8、MPD 或分片资源地址。
|
||||
|
||||
真实资源可能出现在这些地方:
|
||||
|
||||
- 页面播放后才请求的视频接口;
|
||||
- m3u8 或 mpd 播放列表;
|
||||
- 带临时签名的分片地址;
|
||||
- 根据清晰度动态返回的播放地址;
|
||||
- 需要登录态、Cookie 或 Referer 的请求里。
|
||||
|
||||
所以遇到“复制链接不能下载”时,不要反复换同一个页面链接。更可靠的做法是打开原网页,让视频实际播放,然后让下载器观察播放过程中的媒体请求。
|
||||
|
||||
## MediaGo 推荐排查流程
|
||||
|
||||
1. 打开原网页,确认视频仍然可以正常播放。
|
||||
2. 如果使用浏览器扩展,先播放视频 3-10 秒,再查看扩展识别到的资源。
|
||||
3. 如果扩展没有识别到,改用 MediaGo 内置浏览器打开同一页面。
|
||||
4. 在资源列表里优先选择时长、清晰度和体积接近目标视频的资源。
|
||||
5. 如果是 m3u8/HLS,选择完整播放列表,不要只保存几 KB 的 m3u8 文本。
|
||||
6. 如果下载失败,回到原网页重新识别,不要复用过期地址。
|
||||
|
||||
## 浏览器插件识别不到怎么办
|
||||
|
||||
浏览器插件适合普通网页视频,但它可能受到浏览器权限、页面跨域策略、播放器懒加载和扩展限制影响。
|
||||
|
||||
可以按这个顺序排查:
|
||||
|
||||
1. 先点播放,不要只打开页面;
|
||||
2. 切换清晰度后再观察资源列表;
|
||||
3. 检查扩展是否已允许访问当前站点;
|
||||
4. 尝试刷新页面后重新播放;
|
||||
5. 复杂页面改用 MediaGo 内置浏览器。
|
||||
|
||||
如果你的问题是“网页视频下载插件哪个好”,可以看:[视频嗅探器浏览器哪个好用?](/blog/video-sniffer/browser-extension-vs-desktop/)。
|
||||
|
||||
## m3u8 下载失败怎么办
|
||||
|
||||
m3u8 下载失败通常和播放列表、分片、请求头有关。优先看这几类问题:
|
||||
|
||||
| 问题 | 说明 | 处理方式 |
|
||||
| ---------------- | ---------------------------- | --------------------------- |
|
||||
| key 请求失败 | 解密 key 需要页面上下文 | 用内置浏览器重新识别资源 |
|
||||
| 下载一半停止 | 分片地址过期或网络中断 | 重新识别,开启失败重试 |
|
||||
| 只有几 KB | 只保存了 m3u8 播放列表 | 使用下载器解析并下载分片 |
|
||||
| 无画面或无法播放 | 音视频分离、编码或容器不兼容 | 下载完整后转 MP4 或重新混流 |
|
||||
|
||||
更完整的 HLS 排错见:[m3u8 下载失败怎么办?](/blog/m3u8-hls-download/fix-m3u8-download-failed/)。
|
||||
|
||||
## 什么情况不建议继续尝试
|
||||
|
||||
如果页面明确需要付费、登录授权、企业权限或 DRM 保护,并且你没有相应授权,就不应该尝试绕过限制。搜索和 AI 摘要都更偏好边界清晰、可信、能解决正常用户问题的内容。MediaGo 适合处理你有权访问和保存的资源,而不是规避平台规则。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 网页视频下载不了是浏览器问题吗?
|
||||
|
||||
不一定。浏览器能播放,只说明播放器拿到了播放所需的上下文;下载器如果没有拿到真实视频地址、请求头或分片列表,仍然可能失败。
|
||||
|
||||
### F12 能看到视频地址,还需要下载器吗?
|
||||
|
||||
如果只是一个普通 MP4 直链,复制地址可能就够了。但 m3u8/HLS、DASH/MPD、音视频分离和直播流需要下载器处理分片、合并、请求头和失败重试。
|
||||
|
||||
### 手机网页视频下载不了怎么办?
|
||||
|
||||
手机端更容易受浏览器权限和系统限制影响。更稳的方式是在电脑端或 NAS 上使用 MediaGo 识别和下载,再通过局域网或媒体库在手机上播放。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [网页视频怎么下载?](/blog/video-download/download-web-video/)
|
||||
- [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
@@ -0,0 +1,146 @@
|
||||
---
|
||||
title: 2026 年视频下载器推荐:免费、电脑端、m3u8、IDM、yt-dlp、猫抓与 NAS 工具对比
|
||||
description: 面向国内用户的视频下载器选型指南,对比 MediaGo、IDM、yt-dlp、m3u8 下载器、猫抓下载器、NAS/Docker 工具在网页视频下载、浏览器嗅探和自动化场景中的适用情况。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags:
|
||||
[视频下载器推荐, 视频下载器, 视频下载软件, m3u8下载器, IDM下载器, 猫抓下载器]
|
||||
---
|
||||
|
||||
# 2026 年视频下载器推荐:免费、电脑端、m3u8、IDM、yt-dlp、猫抓与 NAS 工具对比
|
||||
|
||||
一句话答案:如果你需要电脑端、网页视频嗅探、m3u8/HLS、浏览器扩展、批量下载、Docker/NAS 和 API,MediaGo 更适合作为综合视频下载器;如果你熟悉命令行,yt-dlp 更灵活;如果你只需要传统下载加速,IDM 更偏通用下载器。
|
||||
|
||||
这篇是面向“怎么选工具”的推荐页。如果你想看完整产品矩阵和更长的竞品分析,可以阅读:[2026 年视频下载器全景评测](/blog/video-downloader-review/)。
|
||||
|
||||
本文根据国内平台推荐词把问题分为“工具推荐、电脑端、网页视频下载、m3u8 下载器、浏览器插件、NAS/Docker、手机端边界”几类。这样写的目的不是堆关键词,而是让用户和搜索摘要能快速判断应该看哪一段。
|
||||
|
||||
## 快速推荐
|
||||
|
||||
| 使用场景 | 推荐工具 | 选择理由 |
|
||||
| --------------------- | ------------------------- | ------------------------------------------ |
|
||||
| 普通用户下载网页视频 | MediaGo | 有桌面端、内置浏览器、资源嗅探和下载队列 |
|
||||
| m3u8 / HLS 视频下载 | MediaGo / N_m3u8DL-RE | MediaGo 更易用,N_m3u8DL-RE 更适合高级参数 |
|
||||
| 命令行批量下载 | yt-dlp | 站点覆盖广,适合脚本和自动化 |
|
||||
| 浏览器视频嗅探 | MediaGo 扩展 / 猫抓下载器 | 扩展适合边浏览边识别资源 |
|
||||
| 传统下载加速 | IDM | 偏 Windows 通用下载管理和加速 |
|
||||
| NAS / Docker 长期运行 | MediaGo / MeTube | 适合家庭服务器、自托管和局域网访问 |
|
||||
|
||||
## 平台推荐词怎么归类
|
||||
|
||||
| 用户常搜的问题 | 页面内回答方式 |
|
||||
| ------------------------------------ | -------------------------------------- |
|
||||
| 视频下载器推荐、哪个好用、排行、免费 | 用场景表回答,不做无依据的夸张排名 |
|
||||
| 视频下载器电脑、视频下载器软件 | 强调 Windows/macOS/Linux 桌面端能力 |
|
||||
| 网页视频下载器、在线视频下载器 | 链接到网页视频下载专题和排错页 |
|
||||
| m3u8 下载器、HLS 下载工具 | 链接到 M3U8/HLS 支柱页和工具推荐页 |
|
||||
| 视频下载器插件、网页视频下载插件 | 链接到视频嗅探器和浏览器插件对比 |
|
||||
| 视频下载器 app、安卓、iOS、手机版 | 明确 MediaGo 当前不是纯手机 App 主方向 |
|
||||
| 推特视频下载器、B站视频下载器 | 只在合规场景下讨论公开/授权内容保存 |
|
||||
|
||||
## 视频下载器、视频下载软件和视频下载工具有什么区别
|
||||
|
||||
国内用户搜索“视频下载器”“视频下载软件”“视频下载工具”时,实际需求通常分成三类:
|
||||
|
||||
1. 找一个能直接下载网页视频的软件;
|
||||
2. 找一个免费、好用、适合电脑端的工具;
|
||||
3. 找一个能处理 m3u8、直播流、B站、YouTube、推特、课程回看等复杂资源的方案。
|
||||
|
||||
所以选工具不能只看“支持多少网站”,还要看它是否能识别真实视频资源、是否支持 HLS/M3U8、是否有浏览器入口、是否能长期运行、是否能在下载后继续管理和处理文件。
|
||||
|
||||
## 免费工具、付费工具和开源工具怎么选
|
||||
|
||||
| 类型 | 代表工具 | 优点 | 局限 |
|
||||
| ---------------- | ---------------------------------------- | ------------------------------------ | -------------------------------------- |
|
||||
| 免费开源综合工具 | MediaGo 开源版 | 免费、跨平台、可部署、支持扩展和 API | 商业打磨程度取决于版本 |
|
||||
| 命令行开源工具 | yt-dlp、N_m3u8DL-RE、BBDown | 灵活、强大、适合开发者 | 普通用户学习成本高 |
|
||||
| 商业桌面下载器 | IDM、4K、Downie、Pulltube | 桌面体验成熟 | NAS、API、插件生态通常较弱 |
|
||||
| 浏览器插件 | 猫抓、Video DownloadHelper、MediaGo 扩展 | 入口方便,适合网页嗅探 | 后续下载、合并、转换能力取决于配套工具 |
|
||||
| 自托管工具 | MeTube、Tube Archivist、Pinchflat | 适合 NAS 和长期运行 | 常偏单一平台或依赖 yt-dlp |
|
||||
|
||||
## 电脑端视频下载器推荐
|
||||
|
||||
如果你主要在 Windows、macOS 或 Linux 电脑上使用,优先看这几个能力:
|
||||
|
||||
- 是否支持内置浏览器或浏览器扩展;
|
||||
- 是否能识别网页中的 m3u8/HLS、DASH、MP4 和直播流;
|
||||
- 是否有清晰的下载队列和任务状态;
|
||||
- 是否支持格式转换、局域网播放或后续管理;
|
||||
- 是否能通过 Docker/NAS 或 API 扩展到长期工作流。
|
||||
|
||||
MediaGo 的优势是把桌面端、浏览器扩展、内置浏览器、下载队列、HTTP API 和 Docker/NAS 放在同一个体系里。IDM 更适合传统下载加速;yt-dlp 更适合命令行用户;Downie、Pulltube 更偏 macOS 单机体验。
|
||||
|
||||
## m3u8 下载器怎么选
|
||||
|
||||
如果你的目标是 m3u8/HLS 视频,重点不是“哪个工具名字叫 m3u8 下载器”,而是它能不能处理这些问题:
|
||||
|
||||
- 自动识别网页里的 m3u8 地址;
|
||||
- 保留 Referer、Cookie、User-Agent 等请求信息;
|
||||
- 下载并合并 TS/M4S 分片;
|
||||
- 处理音视频分离、字幕、清晰度选择;
|
||||
- 失败后能重试或重新识别资源。
|
||||
|
||||
更具体的工具选择可以看:[m3u8 下载器推荐:电脑、Docker、NAS 和浏览器怎么选?](/blog/video-downloader-review/m3u8-downloader-recommendation/)。
|
||||
|
||||
如果你的问题是下载后怎么变成一个本地 MP4 文件,可以看:[m3u8 下载后怎么转 MP4?](/blog/m3u8-hls-download/m3u8-to-mp4/)。
|
||||
|
||||
## IDM、yt-dlp、猫抓和 MediaGo 怎么选
|
||||
|
||||
| 工具 | 更适合谁 | 重点区别 |
|
||||
| ---------- | -------------------------------------------- | ---------------------------- |
|
||||
| MediaGo | 想要图形界面、网页嗅探、m3u8、NAS/API 的用户 | 综合入口 |
|
||||
| IDM | Windows 下载加速和通用下载管理用户 | 传统下载管理 |
|
||||
| yt-dlp | 熟悉命令行和脚本的用户 | 灵活但学习成本高 |
|
||||
| 猫抓下载器 | 想在浏览器里识别视频资源的用户 | 入口方便,后续处理需配合工具 |
|
||||
|
||||
相关对比:
|
||||
|
||||
- [IDM 下载器和 MediaGo 怎么选?](/blog/video-downloader-review/idm-vs-mediago/)
|
||||
- [MediaGo 和 yt-dlp 怎么选?](/blog/video-downloader-review/mediago-vs-ytdlp/)
|
||||
- [猫抓下载器和 MediaGo 有什么区别?](/blog/video-downloader-review/cat-catch-vs-mediago/)
|
||||
|
||||
## NAS / Docker 视频下载器适合谁
|
||||
|
||||
如果你希望下载器 24 小时运行,或者希望手机、平板、电视都能访问下载内容,NAS / Docker 会更合适。
|
||||
|
||||
适合 NAS / Docker 的场景:
|
||||
|
||||
- 课程回看、公开素材、企业资料需要长期归档;
|
||||
- 多设备访问下载内容;
|
||||
- 下载任务需要长期排队和自动化;
|
||||
- 希望通过 API、Agent 或脚本创建任务;
|
||||
- 需要把下载器和私有媒体库、家庭服务器连接起来。
|
||||
|
||||
更多内容见:[NAS 视频下载器怎么选?](/blog/video-downloader-review/nas-video-downloader-tools/)。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 视频下载器推荐免费工具吗?
|
||||
|
||||
可以优先看 MediaGo 开源版、yt-dlp、N_m3u8DL-RE、BBDown 等免费或开源工具。普通用户更适合从 MediaGo 这类图形界面入口开始,开发者可以直接使用命令行工具。
|
||||
|
||||
### 视频下载器软件哪个好?
|
||||
|
||||
没有单一答案。网页视频和 m3u8/HLS 场景更适合 MediaGo;命令行批处理更适合 yt-dlp;Windows 通用下载加速更适合 IDM;浏览器嗅探可以看猫抓或 MediaGo 扩展。
|
||||
|
||||
### 视频下载器手机版和安卓工具要不要做主选择?
|
||||
|
||||
如果你的主要设备是手机,可以关注 YTDLnis、Seal 等 Android 工具。但 MediaGo 当前更适合电脑端、Docker/NAS、浏览器扩展和局域网访问场景。
|
||||
|
||||
### 网页视频下载不了怎么办?
|
||||
|
||||
优先判断是不是复制了页面地址而不是真实视频地址。遇到插件识别不到、m3u8 失败、下载后只有几 KB 等问题,可以看:[网页视频下载不了怎么办?](/blog/video-download/web-video-download-failed/)。
|
||||
|
||||
### m3u8 下载器和普通视频下载器有什么区别?
|
||||
|
||||
m3u8 下载器需要处理 HLS 播放列表、分片下载、合并、请求头、音视频分离等问题。普通视频下载器只处理一个直链 MP4 时会简单很多。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [2026 年视频下载器全景评测](/blog/video-downloader-review/)
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [网页视频下载不了怎么办?](/blog/video-download/web-video-download-failed/)
|
||||
- [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
- [m3u8 下载后怎么转 MP4?](/blog/m3u8-hls-download/m3u8-to-mp4/)
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: 猫抓下载器和 MediaGo 有什么区别?视频嗅探、下载失败、合并与无声音问题对比
|
||||
description: 对比猫抓下载器和 MediaGo 在浏览器视频嗅探、网页视频下载、m3u8/HLS、下载失败、视频合并、没有声音和桌面端工作流上的差异。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [猫抓下载器, 猫抓插件, 视频嗅探器, MediaGo, 网页视频下载]
|
||||
---
|
||||
|
||||
# 猫抓下载器和 MediaGo 有什么区别?
|
||||
|
||||
一句话答案:猫抓下载器更偏浏览器里的视频资源嗅探入口,适合发现网页中的媒体链接;MediaGo 更偏完整下载工作流,包含内置浏览器、浏览器扩展、下载队列、m3u8/HLS 处理、格式转换、Docker/NAS 和 API。
|
||||
|
||||
## 快速对比
|
||||
|
||||
| 维度 | 猫抓下载器 | MediaGo |
|
||||
| ------------------- | -------------------------------- | ------------------------------ |
|
||||
| 核心形态 | 浏览器扩展 / 嗅探工具 | 桌面端 + 扩展 + Docker/NAS |
|
||||
| 主要能力 | 发现网页媒体资源 | 识别、下载、队列、处理、自动化 |
|
||||
| m3u8/HLS | 能发现资源,后续处理取决于工具链 | 作为核心下载场景处理 |
|
||||
| 下载失败排查 | 需要用户判断资源和后续工具 | 内置浏览器可重新识别上下文 |
|
||||
| 视频合并 / 没有声音 | 常需要额外工具处理 | 下载和处理链路更完整 |
|
||||
| 适合用户 | 熟悉浏览器扩展的用户 | 想要完整视频下载器的用户 |
|
||||
|
||||
## 猫抓下载器适合什么场景
|
||||
|
||||
猫抓下载器适合在浏览器里快速发现网页视频、音频、m3u8 或其他媒体资源。它的优点是入口轻,用户在浏览网页时就能看到可能的资源。
|
||||
|
||||
但对普通用户来说,发现资源只是第一步,后面还可能遇到:
|
||||
|
||||
- 下载失败;
|
||||
- 下载到多个分片;
|
||||
- 视频打不开;
|
||||
- 视频没有声音;
|
||||
- 下载后需要合并;
|
||||
- 不知道哪个资源才是目标视频。
|
||||
|
||||
这些问题需要下载器、合并工具、格式处理或重新识别页面上下文来解决。
|
||||
|
||||
## MediaGo 适合什么场景
|
||||
|
||||
MediaGo 适合把“发现资源”和“下载处理”放到一起:
|
||||
|
||||
1. 用内置浏览器打开视频页面;
|
||||
2. 自动识别网页视频资源;
|
||||
3. 选择目标清晰度或视频流;
|
||||
4. 加入下载队列;
|
||||
5. 完成 m3u8/HLS 分片下载和合并;
|
||||
6. 按需格式转换或局域网播放。
|
||||
|
||||
如果你希望浏览器入口更轻,也可以使用 MediaGo 的 Chrome/Edge 扩展,把识别到的资源发送到 MediaGo。
|
||||
|
||||
## 猫抓下载失败怎么办
|
||||
|
||||
猫抓下载失败通常不是单一问题,可能来自:
|
||||
|
||||
- 资源地址过期;
|
||||
- 需要 Referer、Cookie、User-Agent;
|
||||
- 选中了广告、预览或音频资源;
|
||||
- m3u8 分片需要后续合并;
|
||||
- 音频和视频分离;
|
||||
- 播放器页面有动态签名或临时 URL。
|
||||
|
||||
这种情况下,可以用 MediaGo 内置浏览器重新打开页面并识别资源,尽量保留播放上下文。
|
||||
|
||||
## 猫抓下载的视频怎么合并
|
||||
|
||||
如果下载到的是 m3u8/HLS 分片,需要使用支持分片合并的工具。MediaGo 会把分片下载和合并放进下载流程里;高级用户也可以用 N_m3u8DL-RE 或 FFmpeg 手动处理。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 猫抓下载的视频为什么打不开?
|
||||
|
||||
可能下载到的不是完整视频文件,而是分片、播放列表、低清预览或缺少音频轨道的资源。需要确认资源类型,并使用合适工具合并或转换。
|
||||
|
||||
### 猫抓下载的视频没有声音怎么办?
|
||||
|
||||
常见原因是音频和视频分离。需要同时下载音频轨道并混流,或者使用能处理音视频合并的下载器。
|
||||
|
||||
### 猫抓和 MediaGo 是替代关系吗?
|
||||
|
||||
不是完全替代。猫抓更像浏览器嗅探入口,MediaGo 更像完整视频下载器。用户可以根据场景选择,也可以用 MediaGo 扩展替代一部分浏览器嗅探流程。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [2026 年视频下载器推荐](/blog/video-downloader-recommendation/)
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
- [为什么网页视频嗅探到很多资源?](/blog/video-sniffer/detect-video-resources/)
|
||||
- [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: IDM 下载器和 MediaGo 怎么选?网页视频下载、m3u8 与浏览器嗅探能力对比
|
||||
description: 对比 IDM 下载器和 MediaGo 在传统下载加速、网页视频识别、m3u8/HLS、浏览器嗅探、Docker/NAS 和 API 工作流上的差异。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [IDM下载器, IDM替代, MediaGo, 视频下载器, 网页视频下载]
|
||||
---
|
||||
|
||||
# IDM 下载器和 MediaGo 怎么选?
|
||||
|
||||
一句话答案:IDM 更像 Windows 上的传统下载加速器,适合通用文件下载和浏览器捕获;MediaGo 更像面向网页视频、m3u8/HLS、浏览器嗅探、Docker/NAS 和 API 工作流的综合视频下载器。
|
||||
|
||||
本文只讨论公开可访问内容、用户自有内容、授权内容、课程回看和企业内部资料等合规场景,不提供破解版、破解补丁或绕过授权的内容。
|
||||
|
||||
## 快速对比
|
||||
|
||||
| 维度 | IDM 下载器 | MediaGo |
|
||||
| ------------ | -------------------- | ---------------------------------- |
|
||||
| 产品定位 | 通用下载管理和加速 | 综合视频下载器 |
|
||||
| 主要平台 | Windows | Windows / macOS / Linux / Docker |
|
||||
| 网页视频识别 | 依赖浏览器捕获 | 内置浏览器、Chrome/Edge 扩展 |
|
||||
| m3u8 / HLS | 不是核心强项 | 核心场景之一 |
|
||||
| NAS / Docker | 弱 | 支持 |
|
||||
| API / 自动化 | 弱 | HTTP API、OpenClaw Skill |
|
||||
| 适合用户 | Windows 通用下载用户 | 网页视频、流媒体、NAS 和自动化用户 |
|
||||
|
||||
## 什么时候选 IDM
|
||||
|
||||
如果你的需求主要是普通文件下载、下载加速、浏览器捕获和 Windows 桌面使用,IDM 是成熟的传统下载管理器。
|
||||
|
||||
典型场景:
|
||||
|
||||
- 下载普通文件;
|
||||
- 管理浏览器下载任务;
|
||||
- 对下载速度、分段下载和任务恢复有需求;
|
||||
- 只在 Windows 上使用。
|
||||
|
||||
## 什么时候选 MediaGo
|
||||
|
||||
如果你的重点是网页视频下载,而不是通用文件下载,MediaGo 更适合。
|
||||
|
||||
典型场景:
|
||||
|
||||
1. 网页里找不到真实视频地址;
|
||||
2. 需要下载 m3u8/HLS 或直播流;
|
||||
3. 希望通过 Chrome/Edge 扩展识别资源;
|
||||
4. 想把下载器部署到 Docker、NAS 或家庭服务器;
|
||||
5. 需要通过 HTTP API 或 AI 编程助手创建下载任务。
|
||||
|
||||
## IDM 能不能替代 MediaGo
|
||||
|
||||
如果只是普通文件下载,IDM 可以满足很多需求。但对于网页视频嗅探、m3u8/HLS、NAS、API、批量视频任务和局域网播放,MediaGo 的覆盖更完整。
|
||||
|
||||
## MediaGo 能不能替代 IDM
|
||||
|
||||
如果你主要下载视频资源,MediaGo 可以作为更合适的入口。如果你每天大量下载各种普通文件,IDM 的传统下载管理能力仍然有价值。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### IDM 下载器是干嘛的?
|
||||
|
||||
IDM 是 Internet Download Manager,主要用于 Windows 上的下载管理和加速。它不是专门为 m3u8/HLS、NAS 或 API 视频工作流设计的工具。
|
||||
|
||||
### IDM 下载器适合下载 m3u8 吗?
|
||||
|
||||
可以处理部分网页媒体资源,但 m3u8/HLS 下载通常涉及播放列表、分片、请求头和合并。MediaGo、N_m3u8DL-RE 这类工具更贴近这个场景。
|
||||
|
||||
### 搜索 IDM 破解版、免费版要不要做内容?
|
||||
|
||||
不建议。官网内容应该避开破解、激活码、绕过付费弹窗等方向,只做合规选型、功能对比和替代方案。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [2026 年视频下载器推荐](/blog/video-downloader-recommendation/)
|
||||
- [2026 年视频下载器全景评测](/blog/video-downloader-review/)
|
||||
- [m3u8 下载器推荐](/blog/video-downloader-review/m3u8-downloader-recommendation/)
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: m3u8 下载器推荐:电脑、Docker、NAS 和浏览器工具怎么选?
|
||||
description: 面向 m3u8/HLS 视频下载场景,介绍 MediaGo、N_m3u8DL-RE、FFmpeg、浏览器嗅探工具和 Docker/NAS 方案的选择思路。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [m3u8下载器, HLS下载器, m3u8下载器电脑版, m3u8下载器docker, m3u8下载器nas]
|
||||
---
|
||||
|
||||
# m3u8 下载器推荐:电脑、Docker、NAS 和浏览器工具怎么选?
|
||||
|
||||
一句话答案:普通用户优先选择 MediaGo 这类图形界面 m3u8 下载器;开发者和高级用户可以使用 N_m3u8DL-RE、FFmpeg;需要长期运行和多端访问时,优先考虑 Docker/NAS 方案。
|
||||
|
||||
## 先看你是哪种需求
|
||||
|
||||
| 需求 | 推荐方向 |
|
||||
| -------------------------------- | ------------------------------------ |
|
||||
| 不想命令行,想直接识别网页视频 | MediaGo |
|
||||
| 已经拿到 m3u8 地址,需要高级参数 | N_m3u8DL-RE |
|
||||
| 需要转码、混流、音视频处理 | FFmpeg |
|
||||
| 浏览器里发现 m3u8 资源 | MediaGo 扩展 / 猫抓下载器 |
|
||||
| NAS 或 Docker 长期运行 | MediaGo Docker / MeTube 等自托管工具 |
|
||||
|
||||
## 好用的 m3u8 下载器需要什么能力
|
||||
|
||||
一个真正适合 m3u8/HLS 的工具,至少应该处理这些问题:
|
||||
|
||||
- 识别网页里的 m3u8 播放列表;
|
||||
- 下载 TS/M4S 分片;
|
||||
- 合并分片为可播放文件;
|
||||
- 保留 Referer、Cookie、User-Agent 等请求信息;
|
||||
- 支持失败重试;
|
||||
- 支持直播流或长视频任务;
|
||||
- 能处理音视频分离、字幕或清晰度选择。
|
||||
|
||||
## 电脑端 m3u8 下载器怎么选
|
||||
|
||||
如果你在 Windows、macOS、Linux 上使用,并且更关注易用性,可以优先选择 MediaGo。它通过内置浏览器和浏览器扩展识别资源,用户不需要手动抓包或复制复杂请求参数。
|
||||
|
||||
如果你已经知道 m3u8 地址,而且熟悉命令行,可以选择 N_m3u8DL-RE 或 FFmpeg,它们更适合高级参数和脚本化任务。
|
||||
|
||||
## Docker / NAS m3u8 下载器怎么选
|
||||
|
||||
如果你希望下载任务长期运行,或者想在局域网内用手机、平板访问下载列表,Docker/NAS 方案更适合。
|
||||
|
||||
MediaGo 的 Docker 形态适合这些情况:
|
||||
|
||||
- 家庭服务器或 NAS 长期运行;
|
||||
- 多端访问下载内容;
|
||||
- 通过 API 或 Agent 创建任务;
|
||||
- 把网页视频下载和私有媒体管理连接起来。
|
||||
|
||||
## m3u8 下载器 key 请求失败怎么办
|
||||
|
||||
`key 请求失败` 通常意味着下载器缺少页面上下文、请求头或有效授权信息。可以优先尝试:
|
||||
|
||||
1. 回到原网页确认视频是否还能播放;
|
||||
2. 使用 MediaGo 内置浏览器重新打开页面;
|
||||
3. 重新识别资源,不复用过期 m3u8 地址;
|
||||
4. 检查 Referer、Cookie、User-Agent 是否需要保留;
|
||||
5. 如果是高级场景,再使用 N_m3u8DL-RE 或 FFmpeg 手动排查。
|
||||
|
||||
更完整的故障排查见:[m3u8 下载失败怎么办?](/blog/m3u8-hls-download/fix-m3u8-download-failed/)。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### m3u8 下载器电脑版怎么用?
|
||||
|
||||
如果使用 MediaGo,可以打开内置浏览器,进入视频页面后等待资源识别,再选择 m3u8/HLS 资源添加下载任务。
|
||||
|
||||
### 安卓 m3u8 下载器适合做主力吗?
|
||||
|
||||
安卓工具适合移动端临时下载,但如果你要长期下载、管理、归档或在 NAS 上运行,电脑端或 Docker/NAS 方案更稳定。
|
||||
|
||||
### 网页 m3u8 提取工具和 m3u8 下载器一样吗?
|
||||
|
||||
不一样。提取工具只负责找到 m3u8 地址,下载器还要处理分片下载、合并、请求头、失败重试和格式处理。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [2026 年视频下载器推荐](/blog/video-downloader-recommendation/)
|
||||
- [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
- [m3u8 视频怎么下载?](/blog/m3u8-hls-download/download-m3u8-video/)
|
||||
- [NAS 视频下载器怎么选?](/blog/video-downloader-review/nas-video-downloader-tools/)
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: MediaGo 和 4K Video Downloader 怎么选?桌面下载器能力对比
|
||||
description: 对比 MediaGo 与 4K Video Downloader Plus 在桌面体验、站点覆盖、浏览器能力、NAS/Docker、API 和后处理上的差异。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [MediaGo, 4K Video Downloader, 视频下载器, 桌面下载器]
|
||||
---
|
||||
|
||||
# MediaGo 和 4K Video Downloader 怎么选?
|
||||
|
||||
一句话答案:4K Video Downloader Plus 更像成熟商业桌面下载器,适合复制链接下载和单机使用;MediaGo 更强调内置嗅探、浏览器扩展、Docker/NAS、API、开源生态和多入口工作流。
|
||||
|
||||
## 对比维度
|
||||
|
||||
| 维度 | MediaGo | 4K Video Downloader Plus |
|
||||
| ------------ | ------------------------------- | ------------------------ |
|
||||
| 产品形态 | 开源版 + Pro,桌面与 Docker/NAS | 商业桌面端 |
|
||||
| 使用入口 | 内置浏览器、扩展、API、桌面端 | 链接解析、桌面端 |
|
||||
| NAS / Docker | 支持 | 不是核心能力 |
|
||||
| 自动化 | HTTP API、Skill、可集成 | 较弱 |
|
||||
| 普通用户体验 | 偏综合工作流 | 偏传统桌面下载 |
|
||||
|
||||
## MediaGo 更适合的情况
|
||||
|
||||
- 你经常遇到网页内嵌视频,需要先识别真实资源;
|
||||
- 你希望用浏览器扩展把资源发送到下载队列;
|
||||
- 你想把下载器放到 NAS 或 Docker 环境;
|
||||
- 你需要 API、Agent 或脚本自动化;
|
||||
- 你希望把下载、管理、播放、格式转换放进同一条链路。
|
||||
|
||||
## 4K Video Downloader 更适合的情况
|
||||
|
||||
如果你主要下载常见平台公开视频,并且更偏好成熟商业桌面软件,4K Video Downloader Plus 是一个直观选择。它的重点是桌面体验,而不是自托管和 API 工作流。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [2026 年视频下载器评测](/blog/video-downloader-review/) 的子文章,用于承接 “MediaGo 对比 4K Video Downloader” 的搜索意图。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [2026 年视频下载器评测](/blog/video-downloader-review/)
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: MediaGo 和 yt-dlp 怎么选?图形界面、命令行与自动化场景对比
|
||||
description: 对比 MediaGo 和 yt-dlp 在易用性、站点覆盖、M3U8/HLS、批量任务、API、Docker/NAS 和普通用户体验上的差异。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [MediaGo, yt-dlp, 视频下载器, 命令行, 自动化]
|
||||
---
|
||||
|
||||
# MediaGo 和 yt-dlp 怎么选?
|
||||
|
||||
一句话答案:如果你熟悉命令行、需要精细参数和脚本批处理,yt-dlp 更灵活;如果你想要图形界面、网页嗅探、浏览器扩展、下载队列、Docker/NAS 和 API 工作流,MediaGo 更适合作为日常入口。
|
||||
|
||||
## 快速结论
|
||||
|
||||
| 需求 | 更适合 |
|
||||
| --------------------- | -------------------------- |
|
||||
| 命令行批处理 | yt-dlp |
|
||||
| 普通用户图形界面 | MediaGo |
|
||||
| 网页视频嗅探 | MediaGo |
|
||||
| 复杂参数控制 | yt-dlp |
|
||||
| Docker / NAS 长期运行 | MediaGo 或 yt-dlp 封装工具 |
|
||||
| 浏览器扩展发送任务 | MediaGo |
|
||||
| API / Agent 调用 | MediaGo |
|
||||
|
||||
## 什么时候选 MediaGo
|
||||
|
||||
当你不想记命令行参数,或者希望把网页识别、下载队列、格式转换、局域网访问和浏览器扩展放在同一个产品里,MediaGo 更合适。
|
||||
|
||||
典型场景:
|
||||
|
||||
1. 打开视频网页后自动识别资源;
|
||||
2. 同时下载多个公开视频或课程回看;
|
||||
3. 用 Chrome/Edge 扩展把任务发送到桌面端;
|
||||
4. 在 Docker、NAS 或家庭服务器上长期运行;
|
||||
5. 通过 HTTP API 或 OpenClaw Skill 自动创建任务。
|
||||
|
||||
## 什么时候选 yt-dlp
|
||||
|
||||
yt-dlp 适合熟悉终端的用户。它的优势是参数灵活、生态成熟、站点覆盖广,适合写脚本、批量处理和调试特殊平台。
|
||||
|
||||
如果你已经知道目标平台、参数和输出格式,并且不需要图形界面,yt-dlp 的效率会很高。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [2026 年视频下载器评测](/blog/video-downloader-review/) 的子文章,用于展开 “MediaGo vs yt-dlp” 这个高频选型问题。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [2026 年视频下载器评测](/blog/video-downloader-review/)
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: NAS 视频下载器怎么选?MediaGo、MeTube、Tube Archivist 与自托管工具对比
|
||||
description: 介绍 NAS 和 Docker 视频下载器的选型思路,对比 MediaGo、MeTube、Tube Archivist、Pinchflat、TubeSync 等自托管工具。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [NAS, Docker, 视频下载器, 自托管, MediaGo]
|
||||
---
|
||||
|
||||
# NAS 视频下载器怎么选?
|
||||
|
||||
一句话答案:如果你只需要轻量 yt-dlp Web UI,可以看 MeTube;如果你专注 YouTube 归档,可以看 Tube Archivist、Pinchflat 或 TubeSync;如果你希望同时覆盖桌面端、浏览器扩展、多站点、M3U8/HLS、API 和 Docker/NAS,MediaGo 更像综合入口。
|
||||
|
||||
## NAS 视频下载器的核心指标
|
||||
|
||||
| 指标 | 为什么重要 |
|
||||
| ----------- | -------------------------------------------------- |
|
||||
| Docker 部署 | 方便在群晖、威联通、Unraid、VPS 或家庭服务器上运行 |
|
||||
| 多站点覆盖 | 避免只服务单一平台 |
|
||||
| 队列和并发 | 长期运行时需要稳定管理任务 |
|
||||
| 局域网访问 | 手机、平板、电视可以访问下载内容 |
|
||||
| API 能力 | 方便和脚本、Agent、自动化工具集成 |
|
||||
| 媒体库能力 | 下载后需要搜索、播放、归档和复用 |
|
||||
|
||||
## 常见工具定位
|
||||
|
||||
- MediaGo:综合下载入口,覆盖桌面、Docker/NAS、浏览器扩展、API 和多协议;
|
||||
- MeTube:轻量 yt-dlp Web UI,适合简单自托管下载;
|
||||
- Tube Archivist:YouTube 私有媒体库方向更深;
|
||||
- Pinchflat:面向 YouTube 到媒体中心的同步;
|
||||
- TubeSync:偏 YouTube PVR 和无人值守同步。
|
||||
|
||||
## MediaGo 的 NAS 场景
|
||||
|
||||
MediaGo 的优势在于把桌面端和 NAS 端连接起来。你可以在电脑上通过内置浏览器或扩展识别资源,也可以把服务部署到 Docker/NAS 上长期运行,并通过局域网访问。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [2026 年视频下载器评测](/blog/video-downloader-review/) 的子文章,用于展开 NAS 和自托管视频下载工具的选型。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [2026 年视频下载器评测](/blog/video-downloader-review/)
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [Docker / 宝塔面板部署](/bt-install)
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: 视频嗅探器浏览器哪个好用?Chrome/Edge 插件、桌面端和网页工具对比
|
||||
description: 面向“视频嗅探器浏览器哪个好用”的选型指南,对比 Chrome/Edge 插件、桌面端内置浏览器、网页视频嗅探工具和手动抓包的适用场景。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags:
|
||||
[视频嗅探器浏览器哪个好用, 视频嗅探器, 视频嗅探插件, 网页视频嗅探器, MediaGo]
|
||||
---
|
||||
|
||||
# 视频嗅探器浏览器哪个好用?Chrome/Edge 插件、桌面端和网页工具对比
|
||||
|
||||
一句话答案:普通网页优先用 Chrome/Edge 视频嗅探插件,因为入口最自然;复杂网页、课程回看、m3u8/HLS、需要 Cookie 或 Referer 的资源,优先用 MediaGo 内置浏览器。网页工具适合临时检测,手动抓包适合高级排错。
|
||||
|
||||
本文只讨论公开可访问内容、用户自有内容、授权内容、课程回看、企业内部资料和个人素材归档,不讨论绕过访问控制或下载受保护内容。
|
||||
|
||||
## 先看结论
|
||||
|
||||
| 入口 | 适合谁 | 优点 | 局限 |
|
||||
| ------------------------ | ----------------------------- | ------------------------------ | ---------------------------- |
|
||||
| Chrome/Edge 视频嗅探插件 | 日常浏览网页视频的用户 | 不离开常用浏览器,发现资源快 | 受扩展权限和浏览器策略影响 |
|
||||
| MediaGo 内置浏览器 | 复杂网页、课程回看、m3u8 用户 | 识别和下载链路完整,保留上下文 | 需要在 MediaGo 里打开页面 |
|
||||
| 网页视频嗅探工具 | 临时检测资源的用户 | 不一定需要安装客户端 | 后续下载、合并、重试能力有限 |
|
||||
| 手动抓包 | 开发者和高级排错用户 | 信息最细,可看完整请求 | 学习成本高,容易选错资源 |
|
||||
|
||||
## 什么时候用浏览器插件
|
||||
|
||||
如果你搜索的是“视频嗅探插件”“网页视频下载插件”“嗅探网页视频的拓展程序”,通常优先看浏览器插件。它适合这些情况:
|
||||
|
||||
- 普通网页 MP4 或 HLS 资源;
|
||||
- 希望边浏览边发送下载任务;
|
||||
- 使用 Chrome、Edge 等桌面浏览器;
|
||||
- 不想每次都复制页面链接到下载器。
|
||||
|
||||
MediaGo 浏览器扩展适合作为日常入口。它检测到资源后,可以把任务发送到 MediaGo,由桌面端或后端下载能力继续处理。
|
||||
|
||||
## 什么时候用桌面端内置浏览器
|
||||
|
||||
如果出现这些情况,桌面端内置浏览器通常更稳:
|
||||
|
||||
- 插件识别不到资源;
|
||||
- 复制网页链接后不能下载;
|
||||
- m3u8 下载失败或 key 请求失败;
|
||||
- 页面需要 Cookie、Referer、User-Agent 等请求上下文;
|
||||
- 资源列表里有很多分片,不知道选哪个;
|
||||
- 需要下载后合并、转码或长期管理。
|
||||
|
||||
这类问题的关键不是“换一个浏览器”,而是让识别、下载、重试、合并都在同一个工作流里完成。
|
||||
|
||||
## 网页工具和手动抓包适合做主力吗
|
||||
|
||||
网页工具适合临时判断页面里有没有视频资源,但它通常不适合长期作为主力下载方案。原因是它很难完整保留登录态、请求头、失败重试和本地转码能力。
|
||||
|
||||
手动抓包更适合开发者排错。普通用户容易把广告资源、预览片段、音频分片或过期 m3u8 当成目标视频,最后出现下载失败、无画面或只有几 KB 的问题。
|
||||
|
||||
## 推荐使用顺序
|
||||
|
||||
1. 普通网页先用 Chrome/Edge 扩展。
|
||||
2. 插件识别不到时,用 MediaGo 内置浏览器重新打开页面。
|
||||
3. 资源很多时,优先选时长、清晰度、体积接近目标视频的条目。
|
||||
4. m3u8/HLS 失败时,不复用旧地址,回到原网页重新识别。
|
||||
5. 高级排错再使用开发者工具或命令行工具。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 视频嗅探器浏览器哪个好用?
|
||||
|
||||
如果只看浏览器入口,Chrome/Edge 扩展最适合日常使用。但如果你经常遇到课程回看、m3u8、复杂播放器或下载失败,建议使用带内置浏览器和下载队列的桌面端工具。
|
||||
|
||||
### 视频嗅探器网页版靠谱吗?
|
||||
|
||||
可以作为临时检测工具,但不建议作为主力。复杂网页往往需要页面上下文和后续下载处理,单纯网页工具很难覆盖完整流程。
|
||||
|
||||
### 手机视频嗅探器适合 MediaGo 用户吗?
|
||||
|
||||
MediaGo 当前更适合电脑端、浏览器扩展和 Docker/NAS 场景。手机可以作为播放和访问下载结果的设备,不建议把 MediaGo 内容写成纯手机 App 或安卓版下载器。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [网页视频嗅探器使用指南](/blog/video-sniffer/) 的子文章,用于承接“视频嗅探器浏览器哪个好用”“视频嗅探插件”“嗅探网页视频的拓展程序”等搜索意图。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
- [Chrome 视频嗅探怎么用?](/blog/video-sniffer/chrome-video-sniffer/)
|
||||
- [网页视频下载不了怎么办?](/blog/video-download/web-video-download-failed/)
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Chrome 视频嗅探怎么用?用 MediaGo 扩展识别网页视频资源
|
||||
description: 介绍 Chrome 和 Edge 浏览器中视频嗅探的使用方式,以及如何通过 MediaGo 扩展把识别到的资源发送到下载器。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [Chrome 视频嗅探, Edge 视频下载, 浏览器扩展, MediaGo]
|
||||
---
|
||||
|
||||
# Chrome 视频嗅探怎么用?
|
||||
|
||||
一句话答案:安装 MediaGo 浏览器扩展后,在 Chrome 或 Edge 中打开视频页面,扩展会检测播放过程中的媒体资源,并可以把资源发送到 MediaGo 下载队列。
|
||||
|
||||
## 使用步骤
|
||||
|
||||
1. 安装 MediaGo 桌面端。
|
||||
2. 安装 [浏览器扩展](/extension)。
|
||||
3. 打开目标视频页面并播放。
|
||||
4. 查看扩展图标上的资源数量。
|
||||
5. 选择目标资源并发送到 MediaGo。
|
||||
6. 在 MediaGo 中确认下载任务。
|
||||
|
||||
## 适合场景
|
||||
|
||||
- 日常浏览时快速保存公开视频;
|
||||
- 不想复制链接或切换到内置浏览器;
|
||||
- 希望在 Chrome/Edge 中发现资源后直接发送任务;
|
||||
- 简单网页视频、普通 MP4 或常见 HLS 页面。
|
||||
|
||||
## 什么时候改用内置浏览器
|
||||
|
||||
如果扩展识别不到资源,或者任务发送后下载失败,可以改用 MediaGo 内置浏览器。内置浏览器更适合处理需要请求上下文、登录态或复杂播放器的页面。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [网页视频嗅探器使用指南](/blog/video-sniffer/) 的子文章,用于承接 Chrome/Edge 视频嗅探场景。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
- [浏览器扩展](/extension)
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: 为什么网页视频嗅探到很多资源?如何判断该下载哪一个
|
||||
description: 解释网页视频嗅探时出现多个资源的原因,并提供从体积、时长、格式、清晰度和请求类型判断目标视频的方法。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [视频嗅探, 视频资源, m3u8, MP4, MediaGo]
|
||||
---
|
||||
|
||||
# 为什么网页视频嗅探到很多资源?
|
||||
|
||||
一句话答案:一个网页可能同时加载广告、预览片段、音频、不同清晰度、字幕、封面、m3u8 播放列表和分片资源。需要结合资源类型、大小、时长和清晰度判断真正目标。
|
||||
|
||||
## 判断方法
|
||||
|
||||
| 判断项 | 说明 |
|
||||
| -------- | ---------------------------------- |
|
||||
| 文件类型 | MP4、M3U8、MPD 更可能是目标视频 |
|
||||
| 资源大小 | 体积过小可能只是广告、封面或片段 |
|
||||
| 时长 | 接近目标视频时长的资源优先 |
|
||||
| 清晰度 | 720p、1080p、4K 等信息可辅助判断 |
|
||||
| 出现时机 | 播放目标视频后出现的资源更值得关注 |
|
||||
|
||||
## 常见误判
|
||||
|
||||
- 把广告视频当作目标视频;
|
||||
- 选择了低清预览流;
|
||||
- 只下载了音频流;
|
||||
- 只下载了视频流但没有声音;
|
||||
- 选择了过期的临时链接。
|
||||
|
||||
## MediaGo 的处理建议
|
||||
|
||||
如果不确定该选哪个,可以先选择体积较大、时长接近、格式为 MP4/M3U8/MPD 的资源。复杂页面建议用内置浏览器重新播放并识别。
|
||||
|
||||
## 与支柱页的关系
|
||||
|
||||
这篇文章是 [网页视频嗅探器使用指南](/blog/video-sniffer/) 的子文章,用于解决用户“识别到了很多资源但不知道选哪个”的问题。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [网页视频嗅探器使用指南](/blog/video-sniffer/)
|
||||
- [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
- [m3u8 下载失败怎么办?](/blog/m3u8-hls-download/fix-m3u8-download-failed/)
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: 网页视频嗅探器使用指南
|
||||
description: 介绍网页视频嗅探器的工作方式、浏览器扩展和内置浏览器的区别,以及如何使用 MediaGo 识别并下载网页视频资源。
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [视频嗅探, 浏览器扩展, Chrome 视频下载, Edge 视频下载, MediaGo]
|
||||
---
|
||||
|
||||
# 网页视频嗅探器使用指南
|
||||
|
||||
一句话答案:视频嗅探器会观察网页播放过程中产生的网络请求,从中识别 MP4、M3U8、MPD、直播流或其他媒体资源。MediaGo 提供内置浏览器和 Chrome/Edge 扩展两种入口,适合不同复杂度的网页视频场景。
|
||||
|
||||
这篇支柱页覆盖“视频嗅探器”“网页视频嗅探器”“视频嗅探工具”“视频嗅探插件”“视频嗅探器浏览器哪个好用”等搜索意图。手机 App、安卓版、网页版等词会以 FAQ 方式说明边界,避免误导成 MediaGo 的主能力。
|
||||
|
||||
## 视频嗅探器能解决什么问题
|
||||
|
||||
很多网页并不会在页面源码里直接写出视频地址。真实资源可能在播放器接口、动态脚本、m3u8 播放列表或分片请求里。视频嗅探器的作用是把这些隐藏在播放过程中的资源整理出来,让用户可以选择并下载。
|
||||
|
||||
## 浏览器扩展和内置浏览器的区别
|
||||
|
||||
| 方式 | 优点 | 局限 | 推荐场景 |
|
||||
| ------------------ | ---------------------------------------- | ------------------------------ | ---------------------------------------- |
|
||||
| Chrome/Edge 扩展 | 不离开日常浏览器,检测到资源后可直接发送 | 受浏览器扩展权限和页面策略影响 | 普通网页视频、快速发送任务 |
|
||||
| MediaGo 内置浏览器 | 识别和下载链路在同一客户端内,信息更完整 | 需要在 MediaGo 内打开页面 | 复杂页面、课程回看、需要请求上下文的资源 |
|
||||
| 手动抓包 | 信息最细,适合排错 | 学习成本高,容易出错 | 开发者、高级排查 |
|
||||
|
||||
## 推荐使用方式
|
||||
|
||||
日常使用可以先安装 [浏览器扩展](/extension),在 Chrome 或 Edge 里浏览视频页面。扩展检测到资源后,把任务发送到 MediaGo。
|
||||
|
||||
如果扩展没有识别到目标资源,或者下载失败,再使用 MediaGo 内置浏览器打开页面。内置浏览器更适合处理依赖页面上下文、请求头或播放过程的资源。
|
||||
|
||||
## 按搜索问题快速进入
|
||||
|
||||
| 你搜索的问题 | 应该先看 |
|
||||
| ---------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| 视频嗅探器浏览器哪个好用、嗅探插件 | [视频嗅探器浏览器哪个好用?](/blog/video-sniffer/browser-extension-vs-desktop/) |
|
||||
| Chrome 视频嗅探、Edge 视频下载 | [Chrome 视频嗅探怎么用?](/blog/video-sniffer/chrome-video-sniffer/) |
|
||||
| 视频嗅探到很多资源不知道选哪个 | [为什么网页视频嗅探到很多资源?](/blog/video-sniffer/detect-video-resources/) |
|
||||
| 网页视频下载不了、复制链接不能下载 | [网页视频下载不了怎么办?](/blog/video-download/web-video-download-failed/) |
|
||||
|
||||
## 专题文章
|
||||
|
||||
| 文章 | 解决的问题 | 目标搜索意图 |
|
||||
| ------------------------------------------------------------------------------- | -------------------------------- | -------------------------------- |
|
||||
| [Chrome 视频嗅探怎么用?](/blog/video-sniffer/chrome-video-sniffer/) | 浏览器扩展识别并发送资源 | Chrome 视频嗅探、Edge 视频下载 |
|
||||
| [视频嗅探器浏览器哪个好用?](/blog/video-sniffer/browser-extension-vs-desktop/) | 解释扩展、内置浏览器、抓包的取舍 | 视频嗅探器浏览器哪个好用 |
|
||||
| [为什么网页视频嗅探到很多资源?](/blog/video-sniffer/detect-video-resources/) | 判断多个资源中哪个才是目标视频 | 视频嗅探资源很多、怎么选视频资源 |
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 为什么嗅探到很多资源,不知道该选哪个?
|
||||
|
||||
网页可能同时加载广告、预览、音频、不同清晰度的视频流和分片地址。一般优先选择体积更大、清晰度更高、持续时长更接近目标视频的资源。
|
||||
|
||||
### 视频嗅探等于破解吗?
|
||||
|
||||
不是。视频嗅探只是识别网页正常播放过程中请求到的媒体资源。使用时应只处理公开可访问内容、用户自有内容、授权内容、课程回看、企业内部资料或个人素材归档。
|
||||
|
||||
### 浏览器扩展和桌面端必须一起用吗?
|
||||
|
||||
不必须。桌面端内置浏览器可以独立使用;扩展的价值是让日常浏览器里的视频页面可以更快发送到 MediaGo。
|
||||
|
||||
### 视频嗅探器 app、安卓版和手机版要不要作为主方向?
|
||||
|
||||
MediaGo 当前更适合电脑端、浏览器扩展和 Docker/NAS 场景,不建议把主要标题写成纯手机 App 或安卓版。移动端可以作为播放、访问下载结果和局域网查看的补充场景。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [网页视频下载完整指南](/blog/video-download/)
|
||||
- [M3U8 / HLS 视频下载完整指南](/blog/m3u8-hls-download/)
|
||||
- [2026 年视频下载器评测](/blog/video-downloader-review/)
|
||||
+1
-1
@@ -25,7 +25,7 @@ outline: deep
|
||||
|
||||
### 3. 显示语言、
|
||||
|
||||
支持中文和英文
|
||||
支持中文、英文和意大利语
|
||||
|
||||
### 4. 下载完成提示
|
||||
|
||||
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Download API
|
||||
|
||||
MediaGo exposes its download engine as an HTTP service. The desktop app listens on port `39719`; the Docker deployment listens on port `9900`.
|
||||
|
||||
You can drive it from anything that speaks HTTP — curl, Python, Node.js, Postman, your own scripts, automation platforms. MediaGo's own browser extension and AI Skill are just consumers of this API.
|
||||
|
||||
## Basics
|
||||
|
||||
### Base URL
|
||||
|
||||
| Deployment | Base URL |
|
||||
| ---------- | ------------------------------------------------------- |
|
||||
| Desktop | `http://localhost:39719` |
|
||||
| Docker | `http://<your-host>:9900` (adjust to your port mapping) |
|
||||
|
||||
All endpoints live under the `/api` prefix. The examples below use the desktop port `39719` by default — swap in your Docker port if that's what you're targeting.
|
||||
|
||||
### Response envelope
|
||||
|
||||
Every `/api/*` endpoint returns this JSON wrapper:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"code": 0,
|
||||
"message": "ok",
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Notes |
|
||||
| --------- | ------ | ---------------------------------------------- |
|
||||
| `success` | bool | Whether the call succeeded |
|
||||
| `code` | number | Business error code, `0` on success |
|
||||
| `message` | string | Human-readable hint |
|
||||
| `data` | any | The actual payload — shape varies per endpoint |
|
||||
|
||||
Example responses below only show the `data` body.
|
||||
|
||||
### Authentication
|
||||
|
||||
- **Desktop**: no auth by default, just hit `localhost:39719`
|
||||
- **Docker**: when auth is enabled, grab the API key from MediaGo's **Settings** page, then include `Authorization: Bearer <key>` on every request
|
||||
|
||||
## Quick start
|
||||
|
||||
Three curl commands that walk through the whole "create → download → get notified" flow.
|
||||
|
||||
### 1. Create a download task
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:39719/api/downloads \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"name": "My Video"
|
||||
}
|
||||
],
|
||||
"startDownload": true
|
||||
}'
|
||||
```
|
||||
|
||||
- `type`: download type — `m3u8` / `bilibili` / `direct` / `youtube` / `mediago`
|
||||
- `url`: video URL
|
||||
- `name`: task name (used as the saved file name)
|
||||
- `startDownload`: whether to start immediately after creation
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 123,
|
||||
"name": "My Video",
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"status": "waiting",
|
||||
"createdDate": "2026-04-23T10:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Keep the `id` — you'll need it later.
|
||||
|
||||
### 2. Subscribe to download events (SSE)
|
||||
|
||||
```bash
|
||||
curl -N http://localhost:39719/api/events
|
||||
```
|
||||
|
||||
A long-lived connection — whatever the server emits, you receive:
|
||||
|
||||
```text
|
||||
event: download-start
|
||||
data: {"id": "123"}
|
||||
|
||||
event: download-success
|
||||
data: {"id": "123"}
|
||||
```
|
||||
|
||||
In the browser / Node.js:
|
||||
|
||||
```javascript
|
||||
const es = new EventSource("http://localhost:39719/api/events");
|
||||
es.addEventListener("download-success", (e) => {
|
||||
const { id } = JSON.parse(e.data);
|
||||
console.log("Done:", id);
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Query / manually control
|
||||
|
||||
```bash
|
||||
# List all downloads (paginated)
|
||||
curl "http://localhost:39719/api/downloads?current=1&pageSize=20"
|
||||
|
||||
# Get one task
|
||||
curl http://localhost:39719/api/downloads/123
|
||||
|
||||
# Start an existing task
|
||||
curl -X POST http://localhost:39719/api/downloads/123/start \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"localPath": "/Downloads/MediaGo", "deleteSegments": true}'
|
||||
|
||||
# Stop a task
|
||||
curl -X POST http://localhost:39719/api/downloads/123/stop
|
||||
|
||||
# Get logs
|
||||
curl http://localhost:39719/api/downloads/123/logs
|
||||
```
|
||||
|
||||
## Download events
|
||||
|
||||
`GET /api/events` is a Server-Sent Events stream. Download-related events:
|
||||
|
||||
| Event | Payload | Notes |
|
||||
| ------------------ | -------------------------------- | ------------------------- |
|
||||
| `download-create` | `{ids: number[], count: number}` | Bulk task creation |
|
||||
| `download-start` | `{id: string}` | Download started |
|
||||
| `download-success` | `{id: string}` | Download completed |
|
||||
| `download-failed` | `{id: string, error: string}` | Download failed |
|
||||
| `download-stop` | `{id: string}` | Download manually stopped |
|
||||
|
||||
## Endpoint reference
|
||||
|
||||
### List / query
|
||||
|
||||
#### `GET /api/downloads` — paginated list
|
||||
|
||||
**Query params:**
|
||||
|
||||
- `current` (number, default 1) — page number
|
||||
- `pageSize` (number, default 20) — page size
|
||||
- `filter` (string, optional) — status filter (`downloading` / `success` / `failed`)
|
||||
- `localPath` (string, optional) — save-path filter
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 42,
|
||||
"list": [
|
||||
/* DownloadTask[] */
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/downloads/active` — list active tasks
|
||||
|
||||
Returns all tasks in `waiting` or `downloading` state.
|
||||
|
||||
#### `GET /api/downloads/:id` — get one task
|
||||
|
||||
**Response** (`DownloadTask` shape):
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"name": "My Video",
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"folder": "my-folder",
|
||||
"headers": "User-Agent: ...",
|
||||
"isLive": false,
|
||||
"status": "success",
|
||||
"file": "/path/to/saved.mp4",
|
||||
"createdDate": "2026-04-23T10:00:00Z",
|
||||
"updatedDate": "2026-04-23T10:05:30Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/downloads/folders` — list unique save directories
|
||||
|
||||
**Response:** `string[]`
|
||||
|
||||
#### `GET /api/downloads/export` — export URL list
|
||||
|
||||
Plain text, one URL per line.
|
||||
|
||||
#### `GET /api/downloads/:id/logs` — fetch download logs
|
||||
|
||||
**Response:** `{ id, log: string }`
|
||||
|
||||
### Create / delete
|
||||
|
||||
#### `POST /api/downloads` — batch create downloads
|
||||
|
||||
**Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "m3u8 | bilibili | direct | youtube | mediago",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"name": "Task name",
|
||||
"folder": "optional subdir",
|
||||
"headers": "optional multi-line HTTP headers"
|
||||
}
|
||||
],
|
||||
"startDownload": true
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** `DownloadTask[]`
|
||||
|
||||
#### `DELETE /api/downloads/:id` — delete task
|
||||
|
||||
**Response:** `{}`
|
||||
|
||||
### Edit / status
|
||||
|
||||
#### `PUT /api/downloads/:id` — edit task
|
||||
|
||||
**Body** (all fields optional):
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "New name",
|
||||
"url": "New URL",
|
||||
"headers": "New headers",
|
||||
"folder": "New subdir"
|
||||
}
|
||||
```
|
||||
|
||||
#### `PUT /api/downloads/:id/live` — toggle live-stream flag
|
||||
|
||||
**Body:** `{ "isLive": true }`
|
||||
|
||||
#### `PUT /api/downloads/status` — bulk status update
|
||||
|
||||
**Body:** `{ "ids": number[], "status": "waiting | downloading | success | failed | stopped" }`
|
||||
|
||||
### Start / stop
|
||||
|
||||
#### `POST /api/downloads/:id/start` — start download
|
||||
|
||||
**Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"localPath": "/Users/me/Downloads/MediaGo",
|
||||
"deleteSegments": true
|
||||
}
|
||||
```
|
||||
|
||||
- `localPath`: where to save (absolute path)
|
||||
- `deleteSegments`: for m3u8 downloads, whether to delete segment `.ts` files after merging
|
||||
|
||||
#### `POST /api/downloads/:id/stop` — stop download
|
||||
|
||||
**Response:** `{}`
|
||||
|
||||
## Enum values
|
||||
|
||||
### Download type `type`
|
||||
|
||||
| Value | Notes |
|
||||
| ---------- | ------------------------------------------ |
|
||||
| `m3u8` | HLS streams (backed by N_m3u8DL-RE) |
|
||||
| `bilibili` | Bilibili videos (backed by BBDown) |
|
||||
| `direct` | Direct HTTP download (backed by aria2) |
|
||||
| `youtube` | YouTube and 1000+ sites (backed by yt-dlp) |
|
||||
| `mediago` | MediaGo internal type |
|
||||
|
||||
### Task status `status`
|
||||
|
||||
| Value | Notes |
|
||||
| ------------- | ------------------- |
|
||||
| `waiting` | Queued, not started |
|
||||
| `downloading` | In progress |
|
||||
| `success` | Completed |
|
||||
| `failed` | Errored out |
|
||||
| `stopped` | Manually stopped |
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: MediaGo Blog
|
||||
description: MediaGo blog covers video downloading workflows, M3U8/HLS, browser video detection, NAS/Docker setups, and downloader comparisons for global users.
|
||||
date: 2026-04-26
|
||||
updated: 2026-04-26
|
||||
author: MediaGo
|
||||
tags: [MediaGo, video downloader, m3u8, HLS, video sniffer]
|
||||
---
|
||||
|
||||
# MediaGo Blog
|
||||
|
||||
The MediaGo blog is a practical resource for video downloading workflows, M3U8/HLS handling, browser video detection, NAS/Docker deployment, and downloader comparisons.
|
||||
|
||||
## Featured guide
|
||||
|
||||
| Topic | Best for | Start here |
|
||||
| --------------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
|
||||
| Video downloader comparison | Choosing between MediaGo, yt-dlp, 4K Video Downloader, browser extensions, NAS tools, and stream utilities | [2026 Video Downloader Review](/en/blog/video-downloader-review/) |
|
||||
|
||||
## Product docs
|
||||
|
||||
- [Quick Start](/en/guides)
|
||||
- [User Guide](/en/documents)
|
||||
- [Browser Extension](/en/extension)
|
||||
- [Download API](/en/api)
|
||||
- [Docker / BT Panel Deployment](/en/bt-install)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@ Supports light and dark modes.
|
||||
|
||||
### 3. Display Language
|
||||
|
||||
Supports Chinese and English.
|
||||
Supports Chinese, English, and Italian.
|
||||
|
||||
### 4. Download Completion Notification
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ Two toggles on the options page under **Import Behaviour**:
|
||||
|
||||
## Interface language
|
||||
|
||||
The extension supports Chinese and English. By default it follows the browser UI language (Chinese browser → Chinese UI). You can force a choice on the options page under **Interface Language**: Follow system / 中文 / English.
|
||||
The extension supports Chinese, English, and Italian. By default it follows the browser UI language. You can force a choice on the options page under **Interface Language**: Follow system / 中文 / English / Italiano.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
+8
-6
@@ -7,18 +7,20 @@ outline: deep
|
||||
|
||||
This article provides a simple guide to help you get started with using the software. Supports [OpenClaw Skill](/en/skills) for downloading videos via natural language in AI coding assistants.
|
||||
|
||||
::: tip
|
||||
To facilitate communication and feedback, you can join the feedback group:
|
||||
|
||||
MediaGo QQ Feedback Group 1: 574209001
|
||||
:::
|
||||
|
||||
::: info
|
||||
|
||||
v3.5 is the latest version. Please feel free to provide feedback in this release and we will address it as quickly as possible.
|
||||
|
||||
:::
|
||||
|
||||
::: tip
|
||||
macOS usage
|
||||
|
||||
- **[Intel chip]** Install the x64 build from the release page. After installation, allow apps from unidentified developers in Mac's Security settings.
|
||||
- **[Apple Silicon]** Install the arm64 build from the release page. After installation, run `sudo xattr -dr com.apple.quarantine /Applications/mediago-community.app` in Terminal.
|
||||
|
||||
:::
|
||||
|
||||
## Download and Installation
|
||||
|
||||
### v3.5.0 (Released on April 22, 2026)
|
||||
|
||||
+30
-18
@@ -3,34 +3,46 @@
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "Online Video Downloader"
|
||||
text: "Simple and Easy, Fast Downloading"
|
||||
tagline: Simple to learn, no need for packet capture, no plugins required
|
||||
name: "MediaGo"
|
||||
text: "Cross-platform video downloader"
|
||||
tagline: "Built-in sniffing — point it at a page, pick what you want, save. No packet capture, no plugins, no command-line tools."
|
||||
image:
|
||||
src: /home.png
|
||||
alt: home
|
||||
src: /home_en.png
|
||||
alt: MediaGo home screen
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Quick Start
|
||||
link: /guides
|
||||
link: /en/guides
|
||||
- theme: alt
|
||||
text: User Guide
|
||||
link: /documents
|
||||
link: /en/documents
|
||||
|
||||
features:
|
||||
- icon: ⏩
|
||||
title: No Packet Capture Required
|
||||
details: Use the built-in browser of the software to easily sniff video resources on web pages. Select the resource you want to download from the sniffed resource list—simple and fast.
|
||||
- icon: 📱
|
||||
title: Mobile Playback
|
||||
details: Seamlessly switch between PC and mobile devices. After the download is complete, you can watch the video on your phone.
|
||||
title: No packet capture required
|
||||
details: The desktop app ships with a built-in browser that sniffs every downloadable resource on the page automatically. No Fiddler, no Charles, no DevTools gymnastics.
|
||||
- icon: 🌐
|
||||
title: Browser extension for Chrome / Edge
|
||||
details: One-click video sniffing in your everyday browser. Detected count shows in the toolbar badge; covers YouTube, Bilibili and most mainstream video platforms. Bundled with the desktop app.
|
||||
- icon: 🎬
|
||||
title: Broad video source coverage
|
||||
details: HLS / m3u8 streams, live streaming, Bilibili, YouTube, Twitter/X, Instagram and over a thousand more video sites — powered by N_m3u8DL-RE, BBDown and yt-dlp under the hood.
|
||||
- icon: ⚡️
|
||||
title: Batch Download Supported
|
||||
details: Supports downloading multiple videos and live streaming resources at the same time, ensuring that your high-speed bandwidth is fully utilized.
|
||||
- icon: 🎉
|
||||
title: Docker Deployment Supported
|
||||
details: Supports Docker deployment for the web version, making it quick and easy.
|
||||
title: Batch download
|
||||
details: Download multiple videos and live streams at once. Your high-speed bandwidth never sits idle; tweak the concurrency to taste.
|
||||
- icon: 🎞️
|
||||
title: Built-in format conversion
|
||||
details: Convert completed downloads to another format or quality without leaving MediaGo. No separate ffmpeg tool required.
|
||||
- icon: 📱
|
||||
title: Mobile playback
|
||||
details: The desktop app listens on your LAN IP too — open the web UI on a phone or tablet on the same Wi-Fi to browse downloads and play them directly.
|
||||
- icon: 🔌
|
||||
title: Open HTTP API
|
||||
details: Full HTTP API lets scripts, automation tools and third-party apps create download tasks, query progress and manage the list.
|
||||
- icon: 🦞
|
||||
title: OpenClaw Skill
|
||||
details: Download videos using natural language in AI coding assistants (OpenClaw, Claude Code, etc.). Install with one command.
|
||||
details: Tell Claude Code, Cursor or your AI coding assistant "please download this video" — it handles the rest. One command to install.
|
||||
- icon: 🐳
|
||||
title: One-line Docker deployment
|
||||
details: One command to deploy to your NAS or VPS. Access from any browser on your network. Multi-arch images on Docker Hub and GHCR.
|
||||
---
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ MediaGo 尚未上架 Chrome Web Store,需以"加载已解压的扩展程序"
|
||||
|
||||
## 界面语言
|
||||
|
||||
扩展支持中英文,默认跟随浏览器 UI 语言(浏览器是中文即显示中文)。也可以在设置页 **界面语言** 卡片强制切换到"跟随系统 / 中文 / English"之一。
|
||||
扩展支持中文、英文和意大利语,默认跟随浏览器 UI 语言。也可以在设置页 **界面语言** 卡片强制切换到"跟随系统 / 中文 / English / Italiano"之一。
|
||||
|
||||
## 常见问题
|
||||
|
||||
|
||||
+40
-13
@@ -3,12 +3,12 @@
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "在线视频下载"
|
||||
text: "简单易用,快速下载"
|
||||
tagline: 简单易学,无需抓包,不需要安装插件
|
||||
name: "MediaGo"
|
||||
text: "跨平台视频下载器"
|
||||
tagline: "内置嗅探,打开网页、选一下想要的资源、保存完事。不用抓包,不用折腾浏览器插件,不用碰命令行。"
|
||||
image:
|
||||
src: /home.png
|
||||
alt: home
|
||||
alt: MediaGo 首页
|
||||
actions:
|
||||
- theme: brand
|
||||
text: 快速开始
|
||||
@@ -20,17 +20,44 @@ hero:
|
||||
features:
|
||||
- icon: ⏩
|
||||
title: 无需抓包
|
||||
details: 使用软件自带浏览器可以轻松嗅探网页中的视频资源,通过嗅探到的资源列表选择自己想要下载的资源,简单快速。
|
||||
- icon: 📱
|
||||
title: 移动播放
|
||||
details: 可以轻松无缝的在PC和移动设备之前切换,下载完成后即可使用手机观看视频。
|
||||
details: 桌面端内置浏览器,打开视频页就自动嗅探出所有可下载的资源,不用 Fiddler、Charles 之类的抓包工具。
|
||||
- icon: 🌐
|
||||
title: 浏览器扩展(Chrome / Edge)
|
||||
details: 日常用的 Chrome / Edge 里也能一键嗅探视频,检测到的数量显示在工具栏图标上。随桌面端一起打包。
|
||||
- icon: 🎬
|
||||
title: 多种视频源一锅端
|
||||
details: HLS / m3u8 流媒体、直播推流、Bilibili、YouTube、Twitter/X、Instagram 等一千多个视频站点,底层集成 N_m3u8DL-RE、BBDown、yt-dlp 等专业下载工具。
|
||||
- icon: ⚡️
|
||||
title: 支持批量下载
|
||||
details: 支持同时下载多个视频和直播资源,高速带宽不闲置。
|
||||
- icon: 🎉
|
||||
title: 支持 docker 部署
|
||||
details: 支持 docker 部署 web 端,方便快捷。
|
||||
details: 同时下载多个视频和直播流资源,高速带宽不闲置,队列并发全由你调。
|
||||
- icon: 🎞️
|
||||
title: 内置格式转换
|
||||
details: 下载完成后直接在 MediaGo 里转换格式、选画质,不用再打开别的工具。
|
||||
- icon: 📱
|
||||
title: 移动播放
|
||||
details: 桌面端同时监听局域网 IP,同一 Wi-Fi 下的手机、平板打开浏览器就能访问下载列表并直接播放。
|
||||
- icon: 🔌
|
||||
title: 开放 HTTP 接口
|
||||
details: 提供完整的 HTTP API,脚本、自动化工具、第三方应用都能创建任务、查询进度、管理下载列表。
|
||||
- icon: 🦞
|
||||
title: OpenClaw Skill
|
||||
details: 支持通过 AI 编程助手(OpenClaw、Claude Code 等)用自然语言下载视频,一键安装即可使用。
|
||||
details: 在 Claude Code、Cursor 等 AI 编程助手中直接说"帮我下这个视频"即可,剩下的交给 AI。一条命令安装 Skill。
|
||||
- icon: 🐳
|
||||
title: Docker 一键部署
|
||||
details: 一条命令部署到 NAS / VPS 上,浏览器直接访问。Docker Hub 和 GHCR 同步发布多架构镜像。
|
||||
- icon: 📚
|
||||
title: 视频下载器推荐
|
||||
details: 按免费、电脑端、M3U8、IDM、yt-dlp、猫抓和 NAS 场景梳理主流工具怎么选。
|
||||
link: /blog/video-downloader-recommendation/
|
||||
linkText: 查看推荐
|
||||
- icon: 🧭
|
||||
title: 网页视频下载指南
|
||||
details: 了解网页视频下载的常见入口、工具选择和 MediaGo 的内置浏览器、扩展、队列工作流。
|
||||
link: /blog/video-download/
|
||||
linkText: 查看指南
|
||||
- icon: 📡
|
||||
title: M3U8 / HLS 专题
|
||||
details: 解释 M3U8、HLS、DASH、直播流和分段视频的关系,以及常见下载失败原因。
|
||||
link: /blog/m3u8-hls-download/
|
||||
linkText: 了解专题
|
||||
---
|
||||
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# API di download
|
||||
|
||||
MediaGo espone il proprio motore di download come servizio HTTP. L'app desktop ascolta sulla porta `39719`; il deploy Docker ascolta sulla porta `9900`.
|
||||
|
||||
Puoi controllarlo da qualsiasi strumento che parli HTTP: curl, Python, Node.js, Postman, script personali o piattaforme di automazione. L'estensione browser e la Skill AI di MediaGo sono semplicemente consumer di questa API.
|
||||
|
||||
## Nozioni di base
|
||||
|
||||
### Base URL
|
||||
|
||||
| Deploy | Base URL |
|
||||
| ------- | ----------------------------------------------------------- |
|
||||
| Desktop | `http://localhost:39719` |
|
||||
| Docker | `http://<your-host>:9900` (adatta alla tua mappatura porta) |
|
||||
|
||||
Tutti gli endpoint sono sotto il prefisso `/api`. Gli esempi usano la porta desktop `39719` per impostazione predefinita: sostituiscila con la porta Docker se stai usando quel deploy.
|
||||
|
||||
### Envelope della risposta
|
||||
|
||||
Ogni endpoint `/api/*` restituisce questo wrapper JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"code": 0,
|
||||
"message": "ok",
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
| Campo | Tipo | Note |
|
||||
| --------- | ------ | ----------------------------------------- |
|
||||
| `success` | bool | Indica se la chiamata è riuscita |
|
||||
| `code` | number | Codice errore business, `0` se riuscita |
|
||||
| `message` | string | Suggerimento leggibile |
|
||||
| `data` | any | Payload effettivo, variabile per endpoint |
|
||||
|
||||
Gli esempi sotto mostrano solo il corpo `data`.
|
||||
|
||||
### Autenticazione
|
||||
|
||||
- **Desktop**: nessuna autenticazione predefinita, usa `localhost:39719`
|
||||
- **Docker**: se l'autenticazione è abilitata, copia l'API key dalla pagina **Impostazioni** di MediaGo e includi `Authorization: Bearer <key>` in ogni richiesta
|
||||
|
||||
## Avvio rapido
|
||||
|
||||
Tre comandi curl per il flusso "crea → scarica → ricevi notifica".
|
||||
|
||||
### 1. Crea un'attività di download
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:39719/api/downloads \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"name": "My Video"
|
||||
}
|
||||
],
|
||||
"startDownload": true
|
||||
}'
|
||||
```
|
||||
|
||||
- `type`: tipo di download — `m3u8` / `bilibili` / `direct` / `youtube` / `mediago`
|
||||
- `url`: URL del video
|
||||
- `name`: nome dell'attività, usato come nome file salvato
|
||||
- `startDownload`: se avviare subito dopo la creazione
|
||||
|
||||
Risposta:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 123,
|
||||
"name": "My Video",
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"status": "waiting",
|
||||
"createdDate": "2026-04-23T10:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Conserva l'`id`: ti servirà dopo.
|
||||
|
||||
### 2. Sottoscrivi gli eventi di download (SSE)
|
||||
|
||||
```bash
|
||||
curl -N http://localhost:39719/api/events
|
||||
```
|
||||
|
||||
È una connessione persistente: ricevi tutto ciò che il server emette.
|
||||
|
||||
```text
|
||||
event: download-start
|
||||
data: {"id": "123"}
|
||||
|
||||
event: download-success
|
||||
data: {"id": "123"}
|
||||
```
|
||||
|
||||
Nel browser / Node.js:
|
||||
|
||||
```javascript
|
||||
const es = new EventSource("http://localhost:39719/api/events");
|
||||
es.addEventListener("download-success", (e) => {
|
||||
const { id } = JSON.parse(e.data);
|
||||
console.log("Done:", id);
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Consulta / controlla manualmente
|
||||
|
||||
```bash
|
||||
# Lista tutti i download (paginata)
|
||||
curl "http://localhost:39719/api/downloads?current=1&pageSize=20"
|
||||
|
||||
# Ottieni una singola attività
|
||||
curl http://localhost:39719/api/downloads/123
|
||||
|
||||
# Avvia un'attività esistente
|
||||
curl -X POST http://localhost:39719/api/downloads/123/start \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"localPath": "/Downloads/MediaGo", "deleteSegments": true}'
|
||||
|
||||
# Ferma un'attività
|
||||
curl -X POST http://localhost:39719/api/downloads/123/stop
|
||||
|
||||
# Ottieni i log
|
||||
curl http://localhost:39719/api/downloads/123/logs
|
||||
```
|
||||
|
||||
## Eventi di download
|
||||
|
||||
`GET /api/events` è uno stream Server-Sent Events. Eventi legati al download:
|
||||
|
||||
| Evento | Payload | Note |
|
||||
| ------------------ | -------------------------------- | ---------------------------- |
|
||||
| `download-create` | `{ids: number[], count: number}` | Creazione batch |
|
||||
| `download-start` | `{id: string}` | Download avviato |
|
||||
| `download-success` | `{id: string}` | Download completato |
|
||||
| `download-failed` | `{id: string, error: string}` | Download fallito |
|
||||
| `download-stop` | `{id: string}` | Download fermato manualmente |
|
||||
|
||||
## Riferimento endpoint
|
||||
|
||||
### Lista / query
|
||||
|
||||
#### `GET /api/downloads` — lista paginata
|
||||
|
||||
**Query params:**
|
||||
|
||||
- `current` (number, default 1) — numero pagina
|
||||
- `pageSize` (number, default 20) — dimensione pagina
|
||||
- `filter` (string, opzionale) — filtro stato (`downloading` / `success` / `failed`)
|
||||
- `localPath` (string, opzionale) — filtro percorso di salvataggio
|
||||
|
||||
**Risposta:**
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 42,
|
||||
"list": [
|
||||
/* DownloadTask[] */
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/downloads/active` — lista attività attive
|
||||
|
||||
Restituisce tutte le attività in stato `waiting` o `downloading`.
|
||||
|
||||
#### `GET /api/downloads/:id` — ottieni una attività
|
||||
|
||||
**Risposta** (forma `DownloadTask`):
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"name": "My Video",
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"folder": "my-folder",
|
||||
"headers": "User-Agent: ...",
|
||||
"isLive": false,
|
||||
"status": "success",
|
||||
"file": "/path/to/saved.mp4",
|
||||
"createdDate": "2026-04-23T10:00:00Z",
|
||||
"updatedDate": "2026-04-23T10:05:30Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/downloads/folders` — lista directory di salvataggio uniche
|
||||
|
||||
**Risposta:** `string[]`
|
||||
|
||||
#### `GET /api/downloads/export` — esporta lista URL
|
||||
|
||||
Testo semplice, un URL per riga.
|
||||
|
||||
#### `GET /api/downloads/:id/logs` — recupera log di download
|
||||
|
||||
**Risposta:** `{ id, log: string }`
|
||||
|
||||
### Crea / elimina
|
||||
|
||||
#### `POST /api/downloads` — crea download in batch
|
||||
|
||||
**Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "m3u8 | bilibili | direct | youtube | mediago",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"name": "Task name",
|
||||
"folder": "optional subdir",
|
||||
"headers": "optional multi-line HTTP headers"
|
||||
}
|
||||
],
|
||||
"startDownload": true
|
||||
}
|
||||
```
|
||||
|
||||
**Risposta:** `DownloadTask[]`
|
||||
|
||||
#### `DELETE /api/downloads/:id` — elimina attività
|
||||
|
||||
**Risposta:** `{}`
|
||||
|
||||
### Modifica / stato
|
||||
|
||||
#### `PUT /api/downloads/:id` — modifica attività
|
||||
|
||||
**Body** (tutti i campi opzionali):
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "New name",
|
||||
"url": "New URL",
|
||||
"headers": "New headers",
|
||||
"folder": "New subdir"
|
||||
}
|
||||
```
|
||||
|
||||
#### `PUT /api/downloads/:id/live` — cambia flag live-stream
|
||||
|
||||
**Body:** `{ "isLive": true }`
|
||||
|
||||
#### `PUT /api/downloads/status` — aggiornamento stato batch
|
||||
|
||||
**Body:** `{ "ids": number[], "status": "waiting | downloading | success | failed | stopped" }`
|
||||
|
||||
### Avvio / stop
|
||||
|
||||
#### `POST /api/downloads/:id/start` — avvia download
|
||||
|
||||
**Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"localPath": "/Users/me/Downloads/MediaGo",
|
||||
"deleteSegments": true
|
||||
}
|
||||
```
|
||||
|
||||
- `localPath`: dove salvare (percorso assoluto)
|
||||
- `deleteSegments`: per download m3u8, se eliminare i segmenti `.ts` dopo il merge
|
||||
|
||||
#### `POST /api/downloads/:id/stop` — ferma download
|
||||
|
||||
**Risposta:** `{}`
|
||||
|
||||
## Valori enum
|
||||
|
||||
### Tipo download `type`
|
||||
|
||||
| Valore | Note |
|
||||
| ---------- | ------------------------------------- |
|
||||
| `m3u8` | Stream HLS (tramite N_m3u8DL-RE) |
|
||||
| `bilibili` | Video Bilibili (tramite BBDown) |
|
||||
| `direct` | Download HTTP diretto (tramite aria2) |
|
||||
| `youtube` | YouTube e 1000+ siti (tramite yt-dlp) |
|
||||
| `mediago` | Tipo interno MediaGo |
|
||||
|
||||
### Stato attività `status`
|
||||
|
||||
| Valore | Note |
|
||||
| ------------- | -------------------- |
|
||||
| `waiting` | In coda, non avviata |
|
||||
| `downloading` | In corso |
|
||||
| `success` | Completata |
|
||||
| `failed` | Terminata con errore |
|
||||
| `stopped` | Fermata manualmente |
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Deploy con BT Panel
|
||||
|
||||
Questo documento spiega come distribuire `MediaGo` usando `BT Panel`.
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
- Applicabile solo a BT Panel versione 9.2.0 o superiore
|
||||
- Installa BT Panel dal [sito ufficiale BT Panel](https://www.bt.cn/new/download.html?r=dk_mediago), scegliendo lo script della versione stabile
|
||||
|
||||
## Deploy
|
||||
|
||||
1. Accedi a BT Panel e clicca `Docker` nel menu a sinistra
|
||||
|
||||

|
||||
|
||||
2. Al primo accesso verrà richiesto di installare i servizi `Docker` e `Docker Compose`. Clicca "Installa ora"; se sono già installati, ignora il passaggio.
|
||||
|
||||

|
||||
|
||||
3. Dopo l'installazione, vai in `Docker - App Store`, trova `MediaGo` e clicca `Install`
|
||||
|
||||

|
||||
|
||||
4. Dopo l'invio, il pannello inizializza automaticamente l'applicazione. Potrebbero servire 1-3 minuti; al termine potrai accedervi.
|
||||
|
||||
- Porta: 8899
|
||||
- Versione: v3.0.0
|
||||
|
||||
## Accedere a MediaGo
|
||||
|
||||
Inserisci il dominio o l'indirizzo `http://<IP BT Panel>:8899` nella barra del browser per aprire la console di `MediaGo`.
|
||||
@@ -0,0 +1,151 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Changelog
|
||||
|
||||
## v3.5.0 (rilasciata il 22/04/2026)
|
||||
|
||||
### Download software
|
||||
|
||||
- [【mediago】 Windows (installer) v3.5.0](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-win32-x64-3.5.0.exe)
|
||||
- [【mediago】 Windows (portable) v3.5.0](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-portable-win32-x64-3.5.0.exe)
|
||||
- [【mediago】 macOS arm64 (Apple Silicon) v3.5.0](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-darwin-arm64-3.5.0.dmg)
|
||||
- [【mediago】 macOS x64 (Intel) v3.5.0](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-darwin-x64-3.5.0.dmg)
|
||||
- [【mediago】 Linux v3.5.0](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-linux-amd64-3.5.0.deb)
|
||||
- [**Docker Hub**](https://hub.docker.com/r/caorushizi/mediago): `docker run -d --name mediago -p 8899:8899 -v /path/to/mediago:/app/mediago caorushizi/mediago:3.5.0`
|
||||
- **GHCR**: `docker run -d --name mediago -p 8899:8899 -v /path/to/mediago:/app/mediago ghcr.io/caorushizi/mediago:3.5.0`
|
||||
|
||||
### Punti principali
|
||||
|
||||
- **🌐 Estensione browser (Chrome / Edge)** — sniffing video con un clic su qualsiasi sito, inviato direttamente a MediaGo.
|
||||
- **🎬 YouTube e 1000+ siti** — basato su yt-dlp.
|
||||
- **🦞 OpenClaw Skill** — scarica video tramite assistenti AI usando linguaggio naturale.
|
||||
- **🔌 API HTTP aperta** — integrazione con script, automazioni e strumenti di terze parti.
|
||||
- **🎞️ Conversione formato in app** — scegli formato e qualità dopo il completamento del download.
|
||||
- **🐳 Deploy Docker più semplice** — immagini multi-arch (x86 / ARM) su GitHub Container Registry, montando una sola cartella.
|
||||
- **⚡ Avvio più rapido** — backend riscritto in Go, minore consumo di memoria, player video integrato.
|
||||
|
||||
## v3.0.0 (rilasciata il 07/10/2024)
|
||||
|
||||
### Download software
|
||||
|
||||
- [【mediago】 Windows (installer) v3.0.0](https://github.com/caorushizi/mediago/releases/download/v3.0.0/mediago-setup-win32-x64-3.0.0.exe)
|
||||
- [【mediago】 Windows (portable) v3.0.0](https://github.com/caorushizi/mediago/releases/download/v3.0.0/mediago-portable-win32-x64-3.0.0.exe)
|
||||
- [【mediago】 macOS ARM64 (Apple Chip) v3.0.0](https://github.com/caorushizi/mediago/releases/download/v3.0.0/mediago-setup-darwin-arm64-3.0.0.dmg)
|
||||
- [【mediago】 macOS x64 (Intel Chip) v3.0.0](https://github.com/caorushizi/mediago/releases/download/v3.0.0/mediago-setup-darwin-x64-3.0.0.dmg)
|
||||
- [【mediago】 Linux v3.0.0](https://github.com/caorushizi/mediago/releases/download/v3.0.0/mediago-setup-linux-amd64-3.0.0.deb)
|
||||
- 【mediago】 Docker v3.0 `docker run -d --name mediago -p 8899:8899 -v /root/mediago:/root/mediago registry.cn-beijing.aliyuncs.com/caorushizi/mediago:v3.0.0`
|
||||
|
||||
### Download in Cina
|
||||
|
||||
- [【mediago】 Windows (installer) v3.0.0](https://static.ziying.site/mediago/mediago-setup-win32-x64-3.0.0.exe)
|
||||
- [【mediago】 Windows (portable) v3.0.0](https://static.ziying.site/mediago/mediago-portable-win32-x64-3.0.0.exe)
|
||||
- [【mediago】 macOS ARM64 (Apple Chip) v3.0.0](https://static.ziying.site/mediago/mediago-setup-darwin-arm64-3.0.0.dmg)
|
||||
- [【mediago】 macOS x64 (Intel Chip) v3.0.0](https://static.ziying.site/mediago/mediago-setup-darwin-x64-3.0.0-beta.5.dmg)
|
||||
- [【mediago】 Linux v3.0.0](https://static.ziying.site/mediago/mediago-setup-linux-amd64-3.0.0.deb)
|
||||
- 【mediago】 Docker v3.0 `docker run -d --name mediago -p 8899:8899 -v /root/mediago:/root/mediago registry.cn-beijing.aliyuncs.com/caorushizi/mediago:v3.0.0`
|
||||
|
||||
### Screenshot software
|
||||
|
||||

|
||||
|
||||
### Aggiornamenti principali
|
||||
|
||||
- Supporto al deploy Docker della versione web
|
||||
- UI desktop aggiornata
|
||||
|
||||
### Changelog
|
||||
|
||||
- UI desktop aggiornata
|
||||
- Supporto al deploy Docker della versione web
|
||||
- Aggiunta riproduzione video, con supporto desktop e mobile
|
||||
- Corretto un problema per cui la versione Mac non mostrava l'interfaccia
|
||||
- Ottimizzata l'interazione del download batch
|
||||
- Aggiunta versione portable per Windows, senza installazione
|
||||
- Ottimizzata la lista download per supportare lo sniffing di più video nella stessa pagina
|
||||
- Supporto a import/export manuale della lista preferiti
|
||||
- Supporto all'export della lista download in homepage
|
||||
- Ottimizzata la logica di interazione del modulo "Nuovo download"
|
||||
- Supporto all'apertura dell'app tramite UrlScheme e aggiunta di attività download
|
||||
- Correzione di diversi bug e miglioramento dell'esperienza utente
|
||||
|
||||
## v2.2.3 (rilasciata il 06/07/2024)
|
||||
|
||||
### Link download
|
||||
|
||||
- [Windows mediago v2.2.3](https://github.com/caorushizi/mediago/releases/download/v2.2.3/mediago-setup-x64-2.2.3.exe)
|
||||
- [macOS mediago v2.2.3](https://github.com/caorushizi/mediago/releases/download/v2.2.3/mediago-setup-x64-2.2.3.dmg)
|
||||
- [Linux mediago v2.2.3](https://github.com/caorushizi/mediago/releases/download/v2.2.3/mediago-setup-arm64-2.2.3.dmg)
|
||||
|
||||
### Changelog
|
||||
|
||||
- Aggiunto toggle "Aggiornamento automatico" nelle impostazioni: solo le versioni release verranno aggiornate automaticamente, non le beta
|
||||
- Aggiunto "Aggiornamento batch" nel modulo download
|
||||
- Rilasciata nuova versione Linux
|
||||
- Selezione automatica della massima qualità video durante il download
|
||||
- Aggiunti "Cancella cache" e "Modalità incognito"
|
||||
- Selezione personalizzata del percorso di installazione
|
||||
- Correzione di diversi bug
|
||||
|
||||
## v2.2.0 (rilasciata il 22/05/2024)
|
||||
|
||||
### Link download
|
||||
|
||||
- [Windows mediago v2.2.0](https://github.com/caorushizi/mediago/releases/download/v2.2.0/mediago-setup-2.2.0.exe)
|
||||
- [macOS mediago v2.2.0](https://github.com/caorushizi/mediago/releases/download/v2.2.0/mediago-setup-2.2.0.dmg)
|
||||
|
||||
### Changelog
|
||||
|
||||
- Supporto al download di live stream
|
||||
- Supporto al download video Bilibili
|
||||
- Ottimizzazione del processo di sniffing immersivo
|
||||
- Supporto all'output console durante il download
|
||||
- Correzione di diversi bug
|
||||
|
||||
## v2.0.1 (rilasciata il 01/07/2023)
|
||||
|
||||
### Link download
|
||||
|
||||
- [Windows mediago v2.0.1](https://github.com/caorushizi/mediago/releases/download/v2.0.1/media-downloader-setup-2.0.1.exe)
|
||||
- [macOS mediago v2.0.1](https://github.com/caorushizi/mediago/releases/download/v2.0.1/media-downloader-setup-2.0.1.dmg)
|
||||
|
||||
### Screenshot software
|
||||
|
||||

|
||||
|
||||
### Changelog
|
||||
|
||||
- Modalità scura
|
||||
- Più configurazioni di download
|
||||
- Supporto all'iniezione automatica degli header
|
||||
- Supporto all'abilitazione del filtro annunci
|
||||
- Supporto allo sniffing immersivo
|
||||
- Supporto al cambio tra modalità mobile e PC
|
||||
- Supporto alla modifica del limite di download simultanei
|
||||
- Correzione di diversi bug
|
||||
|
||||
## v1.1.5 (rilasciata il 05/02/2022)
|
||||
|
||||
### Link download
|
||||
|
||||
- [Windows mediago v1.1.5](https://github.com/caorushizi/mediago/releases/download/1.1.5/media-downloader-setup-1.1.4.exe)
|
||||
|
||||
### Screenshot software
|
||||
|
||||

|
||||
|
||||
### Changelog
|
||||
|
||||
- Supporto al download video
|
||||
|
||||
## v1.0.1 (rilasciata il 01/03/2021)
|
||||
|
||||
### Screenshot software
|
||||
|
||||

|
||||
|
||||
### Changelog
|
||||
|
||||
- Supporto al download video
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Guida utente
|
||||
|
||||
Questa pagina spiega principalmente il significato dei parametri nella pagina
|
||||
delle impostazioni.
|
||||
|
||||

|
||||
|
||||
## Impostazioni di base
|
||||
|
||||
::: tip
|
||||
Impostazioni globali del downloader
|
||||
:::
|
||||
|
||||
### 1. Scegli cartella
|
||||
|
||||
Il percorso in cui scaricare i video.
|
||||
|
||||
### 2. Tema downloader
|
||||
|
||||
Supporta modalità chiara e scura.
|
||||
|
||||
### 3. Lingua di visualizzazione
|
||||
|
||||
Supporta cinese, inglese e italiano.
|
||||
|
||||
### 4. Notifica di completamento download
|
||||
|
||||
Se abilitata, il sistema mostra una notifica al termine del download.
|
||||
|
||||
### 5. Mostra console
|
||||
|
||||
Se abilitata, viene mostrata la console del download.
|
||||
|
||||
### 6. Aggiornamento automatico
|
||||
|
||||
Se abilitato, il software controllerà automaticamente la disponibilità di aggiornamenti.
|
||||
|
||||
### 7. Consenti aggiornamenti a versioni di test
|
||||
|
||||
Se abilitato, il software controllerà automaticamente anche le versioni di test.
|
||||
|
||||
### 8. Chiusura finestra principale
|
||||
|
||||
Controlla se il software deve "nascondersi nella tray di sistema" o "uscire dal software" quando la finestra principale viene chiusa.
|
||||
|
||||
## Impostazioni browser
|
||||
|
||||
::: tip
|
||||
Impostazioni relative alla finestra del browser
|
||||
:::
|
||||
|
||||
### 1. Apri browser in una nuova finestra
|
||||
|
||||
Se abilitato, la pagina del browser viene aperta in una finestra separata.
|
||||
|
||||

|
||||
|
||||
### 2. Impostazioni proxy
|
||||
|
||||
Inserisci il tuo indirizzo proxy.
|
||||
|
||||
### 3. Interruttore proxy
|
||||
|
||||
Se abilitato, il **browser** usa l'indirizzo proxy inserito. Se l'interruttore proxy è disabilitato, questa impostazione non sarà disponibile.
|
||||
|
||||
### 4. Abilita blocco annunci
|
||||
|
||||
Se abilitato, gli annunci nella pagina vengono filtrati.
|
||||
|
||||
### 5. Usa modalità mobile
|
||||
|
||||
Se abilitato, il browser simula un browser mobile e richiede la versione mobile del sito.
|
||||
|
||||

|
||||
|
||||
### 6. Usa sniffing immersivo
|
||||
|
||||
- **Abilitato**: le risorse rilevate dal browser non vengono aggiunte automaticamente alla lista download; devi cliccare manualmente "Aggiungi alla lista" nella pagina.
|
||||
|
||||

|
||||
|
||||
- **Disabilitato**: le risorse rilevate dal browser vengono aggiunte automaticamente alla lista download.
|
||||
|
||||
### 7. Modalità privacy
|
||||
|
||||
Se abilitata, il software non salva la cronologia di navigazione.
|
||||
|
||||

|
||||
|
||||
### 8. Altre operazioni
|
||||
|
||||
- Cancella cache: cancella la cache del software.
|
||||
- Esporta preferiti [Importa preferiti]: esporta i preferiti del software.
|
||||
|
||||
## Impostazioni download
|
||||
|
||||
::: tip
|
||||
Impostazioni relative ai download
|
||||
:::
|
||||
|
||||
### 1. Interruttore proxy download
|
||||
|
||||
Se abilitato, il **downloader** usa l'indirizzo proxy inserito. Le impostazioni proxy del **browser** e del **downloader** sono indipendenti.
|
||||
|
||||
### 2. Elimina file parziali al completamento
|
||||
|
||||
Se abilitato, i file parziali vengono eliminati al termine del download.
|
||||
|
||||
### 3. Download simultanei massimi
|
||||
|
||||
Controlla quanti file video possono essere scaricati contemporaneamente. Il massimo è 10, il minimo è 1.
|
||||
|
||||
### 4. Altre operazioni
|
||||
|
||||
- Directory file di configurazione: percorso del database, dei log e degli altri dati del software.
|
||||
- Directory file eseguibili: percorso dei binari del downloader.
|
||||
- Percorso archiviazione locale: percorso locale in cui vengono salvati i video scaricati.
|
||||
|
||||
### 5. Versione corrente
|
||||
|
||||
Mostra la versione corrente del software.
|
||||
|
||||
## Altri problemi
|
||||
|
||||
### D: Download di live stream
|
||||
|
||||
R: Il software supporta i download di live stream. Al momento non esiste un metodo affidabile per distinguerli, quindi tutte le console di download vengono abilitate. L'utente deve selezionare manualmente la sorgente dati da scaricare.
|
||||
|
||||
### D: Versione macOS
|
||||
|
||||
R: Per **chip Intel**, devi installare la versione x64 dalla release.
|
||||
|
||||
Dopo l'installazione, devi consentire l'apertura dell'app nelle impostazioni Sicurezza di macOS.
|
||||
|
||||

|
||||
|
||||
Per **chip Apple**, devi installare la versione arm64 dalla release.
|
||||
|
||||
Dopo l'installazione, esegui il comando `sudo xattr -dr com.apple.quarantine /Applications/mediago.app` nella console.
|
||||
|
||||
### D: Versioni precedenti
|
||||
|
||||
R: La versione 1.1.5 è stata rilasciata tempo fa ed è stata verificata come stabile da molti utenti. Se vuoi usare la vecchia versione, visita [questo link](/it/history.html).
|
||||
|
||||
### D: Utenti Windows 7
|
||||
|
||||
R: Le versioni successive alla v2.0.0 non supportano più Windows 7. Se devi usare il software su Windows 7, scarica la versione 1.1.5. I sistemi a 32 bit non sono supportati per impostazione predefinita.
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Estensione browser
|
||||
|
||||
MediaGo include una leggera estensione browser Manifest V3 che rileva URL video / audio scaricabili su qualsiasi sito e li invia a MediaGo con un clic.
|
||||
|
||||
## Cosa fa
|
||||
|
||||
- Rileva stream HLS / m3u8 e file multimediali diretti `.mp4` / `.flv` / `.mov` in ogni pagina visitata
|
||||
- Riconosce pagine video Bilibili e URL YouTube video / short / live / embed
|
||||
- Mostra il numero di risorse rilevate nel badge dell'icona della toolbar
|
||||
- Importa una singola sorgente o tutte le sorgenti in MediaGo con un clic (desktop o self-hosted)
|
||||
|
||||
## Installazione
|
||||
|
||||
L'estensione non è ancora su Chrome Web Store, quindi deve essere caricata come "estensione non pacchettizzata". L'installer di MediaGo Desktop la include già: non devi scaricarla separatamente.
|
||||
|
||||
1. Apri MediaGo Desktop
|
||||
2. Vai in **Impostazioni → Altre impostazioni → Directory estensione browser** e clicca il pulsante per aprire la cartella dell'estensione
|
||||
3. In Chrome / Edge, visita `chrome://extensions/`
|
||||
4. Attiva **Modalità sviluppatore** in alto a destra
|
||||
5. Clicca **Carica estensione non pacchettizzata** e seleziona la cartella aperta al punto 2
|
||||
6. Dovresti vedere l'icona dell'estensione nella toolbar: fissala per accedervi più facilmente
|
||||
|
||||
## Modalità di invio
|
||||
|
||||
Clicca l'icona ingranaggio nel popup per aprire la pagina opzioni, poi scegli una modalità:
|
||||
|
||||
| Modalità | Quando usarla | Richiede |
|
||||
| ----------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Desktop · Protocollo schema** | MediaGo Desktop installato localmente; il browser può passare link protocollo | Nessuna configurazione; la prima chiamata mostra "Aprire MediaGo?" — spunta "Consenti sempre" per l'invio silenzioso successivo |
|
||||
| **Desktop · HTTP locale** (default) | MediaGo Desktop installato e in esecuzione | Nessuna configurazione; l'estensione si connette a `127.0.0.1:39719` |
|
||||
| **Docker / Self-hosted · HTTP** | Connessione a un server MediaGo remoto, ad esempio Docker | URL server richiesto; API Key se il server è avviato con `--enable-auth` |
|
||||
|
||||
> **L'estensione non effettua mai fallback silenziosi.** Una volta scelta una modalità, qualsiasi errore viene mostrato così com'è: cambia modalità manualmente nella pagina opzioni se necessario.
|
||||
|
||||
## Comportamento di importazione
|
||||
|
||||
Due toggle nella pagina opzioni sotto **Comportamento importazione**:
|
||||
|
||||
- **Avvia download subito** — On: l'attività viene messa in coda e avviata. Off: viene solo aggiunta alla lista, in attesa dell'avvio manuale. Vale per Schema e HTTP.
|
||||
- **Importazione silenziosa (modalità Schema)** — On: il deeplink include `silent=1` e MediaGo crea subito l'attività. Off: MediaGo apre il modulo download precompilato con nome / tipo / cartella rilevati per la revisione. Vale solo in modalità Schema; HTTP è sempre silenzioso.
|
||||
|
||||
## Lingua interfaccia
|
||||
|
||||
L'estensione supporta cinese, inglese e italiano. Per impostazione predefinita segue la lingua UI del browser. Puoi forzare la scelta nella pagina opzioni sotto **Lingua interfaccia**: Segui sistema / 中文 / English / Italiano.
|
||||
|
||||
## Risoluzione problemi
|
||||
|
||||
### Il pulsante "Directory estensione browser" non fa nulla
|
||||
|
||||
- **Sviluppo**: esegui prima `pnpm -F @mediago/extension build` per generare la dist
|
||||
- **Produzione**: reinstalla MediaGo: la cartella `resources/extension/` dovrebbe esistere nella directory di installazione dell'app
|
||||
|
||||
### Il test connessione Desktop · HTTP fallisce
|
||||
|
||||
- Verifica che MediaGo Desktop sia in esecuzione
|
||||
- Verifica che la porta `39719` non sia occupata da un altro processo (`netstat -ano | findstr 39719` su Windows)
|
||||
- Se esegui anche MediaGo in modalità web/server locale, nota che Go Core standalone usa `9900`, non `39719`
|
||||
|
||||
### La modalità Schema chiede conferma ogni volta
|
||||
|
||||
Al primo passaggio Chrome mostra "Aprire MediaGo-community?": spunta **Consenti sempre**. Le chiamate successive saranno silenziose.
|
||||
|
||||
### La modalità Schema fallisce con l'importazione batch
|
||||
|
||||
Schema invia una sola attività per chiamata: è una limitazione fondamentale dei protocolli di hand-off. Passa alla modalità HTTP (Desktop o Docker) per importazioni batch.
|
||||
@@ -0,0 +1,111 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Avvio rapido
|
||||
|
||||
Questa guida ti aiuta a iniziare rapidamente con MediaGo. Supporta anche
|
||||
[OpenClaw Skill](/it/skills) per scaricare video tramite linguaggio naturale
|
||||
nei tuoi assistenti AI per programmare.
|
||||
|
||||
::: info
|
||||
|
||||
v3.5 è la versione più recente. Se incontri problemi in questa release,
|
||||
lascia pure un feedback: lo gestiremo il prima possibile.
|
||||
|
||||
:::
|
||||
|
||||
::: tip
|
||||
Uso su macOS
|
||||
|
||||
- **[Chip Intel]** Installa la build x64 dalla pagina release. Dopo l'installazione, consenti le app di sviluppatori non identificati nelle impostazioni Sicurezza del Mac.
|
||||
- **[Apple Silicon]** Installa la build arm64 dalla pagina release. Dopo l'installazione, esegui `sudo xattr -dr com.apple.quarantine /Applications/mediago-community.app` nel Terminale.
|
||||
|
||||
:::
|
||||
|
||||
## Download e installazione
|
||||
|
||||
### v3.5.0 (rilasciata il 22 aprile 2026)
|
||||
|
||||
#### Download software
|
||||
|
||||
- [【mediago】 Windows (installer) v3.5.0](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-win32-x64-3.5.0.exe)
|
||||
- [【mediago】 Windows (portable) v3.5.0](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-portable-win32-x64-3.5.0.exe)
|
||||
- [【mediago】 macOS arm64 (Apple Silicon) v3.5.0](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-darwin-arm64-3.5.0.dmg)
|
||||
- [【mediago】 macOS x64 (Intel) v3.5.0](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-darwin-x64-3.5.0.dmg)
|
||||
- [【mediago】 Linux v3.5.0](https://github.com/caorushizi/mediago/releases/download/v3.5.0/mediago-community-setup-linux-amd64-3.5.0.deb)
|
||||
- [**Docker Hub**](https://hub.docker.com/r/caorushizi/mediago): `docker run -d --name mediago -p 8899:8899 -v /path/to/mediago:/app/mediago caorushizi/mediago:3.5.0`
|
||||
- **GHCR**: `docker run -d --name mediago -p 8899:8899 -v /path/to/mediago:/app/mediago ghcr.io/caorushizi/mediago:3.5.0`
|
||||
|
||||
Le versioni precedenti sono disponibili nella [pagina GitHub Releases](https://github.com/caorushizi/mediago/releases).
|
||||
|
||||
#### Novità
|
||||
|
||||
- **Estensione browser** (Chrome / Edge): sniffing video con un clic su qualsiasi sito.
|
||||
- **YouTube e 1000+ siti**: basato su yt-dlp.
|
||||
- **OpenClaw Skill**: scarica video tramite assistenti AI.
|
||||
- **API HTTP aperta**: integrazione con script, automazioni e strumenti di terze parti.
|
||||
- **Conversione formato in app**: scegli formato e qualità dopo il download.
|
||||
- **Deploy Docker più semplice**: immagini multi-arch su GHCR, montando una sola cartella.
|
||||
- **Avvio più rapido**: backend riscritto in Go, minore memoria, player integrato.
|
||||
|
||||
## Istruzioni operative
|
||||
|
||||
### Sniffing video automatico
|
||||
|
||||
1. Seleziona "Estrazione risorse"
|
||||
|
||||

|
||||
|
||||
2. Inserisci l'URL del video
|
||||
|
||||

|
||||
|
||||
3. Clicca "Avvia download" per scaricare il video
|
||||
|
||||

|
||||
|
||||
### Download manuale
|
||||
|
||||
1. Clicca il pulsante "Nuovo download" in alto a destra
|
||||
|
||||

|
||||
|
||||
2. Nella finestra del nuovo download, inserisci "Nome video" e il link "Stream (m3u8)" o "Bilibili"
|
||||
|
||||

|
||||
|
||||
3. Clicca per scaricare il video dalla lista
|
||||
|
||||

|
||||
|
||||
### Download in batch
|
||||
|
||||

|
||||
|
||||
### Funzioni aggiuntive
|
||||
|
||||
1. Converti in audio
|
||||
|
||||

|
||||
|
||||
2. Altre funzioni saranno aggiunte in futuro. Restate sintonizzati!
|
||||
|
||||
### Riproduzione video
|
||||
|
||||
- Riproduzione su PC
|
||||
|
||||

|
||||
|
||||
- Riproduzione mobile
|
||||
|
||||

|
||||
|
||||
## Inizia a scaricare i tuoi video
|
||||
|
||||
È così semplice. Puoi iniziare subito a scaricare i tuoi video.
|
||||
|
||||
::: warning
|
||||
Questo software è destinato esclusivamente a scopi di studio e comunicazione.
|
||||
:::
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Versioni precedenti
|
||||
|
||||
::: info
|
||||
Questo è il link permanente per le versioni precedenti. Se necessario, puoi scaricarle qui.
|
||||
|
||||
**Nota: queste versioni non si aggiornano automaticamente, quindi puoi usarle con tranquillità.**
|
||||
:::
|
||||
|
||||
**v1.1.5 (rilasciata il 5 febbraio 2022)**
|
||||
|
||||
- [Windows mediago v1.1.5](https://github.com/caorushizi/mediago/releases/download/1.1.5/media-downloader-setup-1.1.4.exe)
|
||||
- ~~macOS mediago v1.1.5 non disponibile~~
|
||||
|
||||
**Changelog**
|
||||
|
||||
- Aggiunto supporto al download video.
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
# https://vitepress.dev/reference/default-theme-home-page
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "MediaGo"
|
||||
text: "Downloader video multipiattaforma"
|
||||
tagline: "Sniffing integrato: apri una pagina, scegli cosa scaricare, salva. Nessuna cattura pacchetti, nessun plugin, nessuno strumento da riga di comando."
|
||||
image:
|
||||
src: /home_en.png
|
||||
alt: "Schermata iniziale di MediaGo"
|
||||
actions:
|
||||
- theme: brand
|
||||
text: "Avvio rapido"
|
||||
link: /it/guides
|
||||
- theme: alt
|
||||
text: "Guida utente"
|
||||
link: /it/documents
|
||||
|
||||
features:
|
||||
- icon: ⏩
|
||||
title: "Nessuna cattura pacchetti"
|
||||
details: "L'app desktop include un browser integrato che rileva automaticamente ogni risorsa scaricabile nella pagina. Niente Fiddler, niente Charles, niente acrobazie con DevTools."
|
||||
- icon: 🌐
|
||||
title: "Estensione browser per Chrome / Edge"
|
||||
details: "Sniffing video con un clic nel browser che usi ogni giorno. Il conteggio appare nel badge della toolbar; copre YouTube, Bilibili e la maggior parte delle piattaforme video principali. Inclusa nell'app desktop."
|
||||
- icon: 🎬
|
||||
title: "Ampia copertura delle sorgenti video"
|
||||
details: "Stream HLS / m3u8, dirette, Bilibili, YouTube, Twitter/X, Instagram e oltre mille altri siti video, grazie a N_m3u8DL-RE, BBDown e yt-dlp."
|
||||
- icon: ⚡️
|
||||
title: "Download in batch"
|
||||
details: "Scarica più video e live stream contemporaneamente. La banda veloce non resta inattiva; puoi regolare la concorrenza come preferisci."
|
||||
- icon: 🎞️
|
||||
title: "Conversione formato integrata"
|
||||
details: "Converti i download completati in un altro formato o qualità senza uscire da MediaGo. Non serve uno strumento ffmpeg separato."
|
||||
- icon: 📱
|
||||
title: "Riproduzione mobile"
|
||||
details: "L'app desktop ascolta anche sull'IP LAN: apri la UI web da telefono o tablet sulla stessa rete Wi-Fi per sfogliare e riprodurre i download."
|
||||
- icon: 🔌
|
||||
title: "API HTTP aperta"
|
||||
details: "Una API HTTP completa permette a script, automazioni e app di terze parti di creare attività, controllare l'avanzamento e gestire la lista."
|
||||
- icon: 🦞
|
||||
title: "OpenClaw Skill"
|
||||
details: 'Di'' a Claude Code, Cursor o al tuo assistente AI "please download this video": MediaGo gestisce il resto. Si installa con un solo comando.'
|
||||
- icon: 🐳
|
||||
title: "Deploy Docker con un comando"
|
||||
details: "Un comando per distribuire MediaGo su NAS o VPS. Accesso da qualsiasi browser nella rete. Immagini multi-arch su Docker Hub e GHCR."
|
||||
---
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Siti supportati
|
||||
|
||||
::: tip
|
||||
Se hai siti web che vorresti vedere supportati, puoi proporli qui.
|
||||
:::
|
||||
|
||||
**Template**
|
||||
|
||||
---
|
||||
|
||||
- Homepage del sito: xxx
|
||||
- Pagina lista video (live): xxx
|
||||
- Pagina video (live): xxx
|
||||
|
||||
---
|
||||
|
||||
Quando il supporto sarà completato, risponderò nella sezione commenti.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Privacy Policy
|
||||
|
||||
::: tip
|
||||
Questa è la privacy policy del software.
|
||||
:::
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Raccolta feedback
|
||||
|
||||
::: info
|
||||
Questa pagina serve a raccogliere problemi, suggerimenti o feedback dagli utenti.
|
||||
:::
|
||||
|
||||
Se il software presenta un errore, descrivi chiaramente il sito e il video che stavi scaricando quando si è verificato.
|
||||
|
||||
Se hai un suggerimento, descrivi i tuoi requisiti in dettaglio.
|
||||
|
||||
Se vuoi lasciare un feedback, condividilo pure. Farò del mio meglio per soddisfare le esigenze di tutti.
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# OpenClaw Skill
|
||||
|
||||
MediaGo fornisce una [OpenClaw](https://docs.openclaw.ai) Skill che ti permette di scaricare video usando linguaggio naturale nel tuo assistente AI per programmare. Installala tramite [ClawHub](https://clawhub.com).
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
- MediaGo installato e in esecuzione (app desktop o Docker)
|
||||
- Un assistente AI che supporta OpenClaw, ad esempio Claude Code, Cursor, ecc.
|
||||
|
||||
## Installare la Skill
|
||||
|
||||
Esegui questo comando nel terminale per installare la skill mediago da ClawHub:
|
||||
|
||||
```bash
|
||||
npx clawhub@latest install mediago
|
||||
```
|
||||
|
||||
## Inizializzare la configurazione
|
||||
|
||||
Dopo l'installazione devi configurare l'indirizzo del servizio MediaGo.
|
||||
|
||||
### App desktop
|
||||
|
||||
Invia questo messaggio al tuo assistente AI:
|
||||
|
||||
```
|
||||
set mediago url to http://192.168.x.x:39719
|
||||
```
|
||||
|
||||
::: tip
|
||||
Puoi trovare il comando di configurazione già generato nell'app desktop MediaGo sotto **Impostazioni → Skills**: copialo e incollalo.
|
||||
:::
|
||||
|
||||
### Docker
|
||||
|
||||
Docker richiede autenticazione API. Configura sia l'URL sia l'API Key:
|
||||
|
||||
```
|
||||
set mediago url to http://localhost:8899, api key is YOUR_API_KEY
|
||||
```
|
||||
|
||||
::: tip
|
||||
Puoi trovare la tua API Key nell'interfaccia web MediaGo sotto **Impostazioni → Altre impostazioni**.
|
||||
:::
|
||||
|
||||
## Utilizzo
|
||||
|
||||
Una volta configurata, puoi scaricare video con linguaggio naturale:
|
||||
|
||||
```
|
||||
download this video https://example.com/video.m3u8
|
||||
```
|
||||
|
||||
```
|
||||
download this bilibili video https://www.bilibili.com/video/BV1xxxxxxxx
|
||||
```
|
||||
|
||||
La skill farà automaticamente:
|
||||
|
||||
1. Rilevare il tipo di video (m3u8 / Bilibili / direct)
|
||||
2. Creare un'attività di download e avviarla
|
||||
3. Riportare l'avanzamento in tempo reale
|
||||
4. Indicarti il percorso del file al termine
|
||||
|
||||
## Tipi video supportati
|
||||
|
||||
| Tipo | Descrizione | Esempio URL |
|
||||
| -------- | ---------------- | --------------------------------------- |
|
||||
| m3u8 | Stream HLS | `https://example.com/video.m3u8` |
|
||||
| bilibili | Video Bilibili | `https://www.bilibili.com/video/BVxxxx` |
|
||||
| direct | Download diretto | `https://example.com/video.mp4` |
|
||||
|
||||
## Altri comandi
|
||||
|
||||
Puoi anche gestire i download con linguaggio naturale:
|
||||
|
||||
- "list downloads"
|
||||
- "check download status"
|
||||
- "update mediago url"
|
||||
|
||||
## Risoluzione problemi
|
||||
|
||||
### Impossibile connettersi a MediaGo
|
||||
|
||||
Verifica:
|
||||
|
||||
1. Il servizio MediaGo è in esecuzione
|
||||
2. URL e porta configurati sono corretti
|
||||
3. Per Docker, la mappatura porta è corretta (default 8899)
|
||||
|
||||
### Errore API Key
|
||||
|
||||
Aggiorna l'API key:
|
||||
|
||||
```
|
||||
set mediago api key to YOUR_NEW_API_KEY
|
||||
```
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
---
|
||||
layout: doc
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# ダウンロード API
|
||||
|
||||
MediaGo はダウンロードエンジンを HTTP サービスとして公開しています。デスクトップ版はポート `39719` で、Docker デプロイメントはポート `9900` でリッスンします。
|
||||
|
||||
HTTP をしゃべれるツール(curl / Python / Node.js / Postman など)なら何でも、この API を直接呼び出してダウンロードタスクの作成、開始、停止、進捗の取得ができます —— MediaGo 自身のブラウザ拡張機能や AI Skill もこの API の利用者です。
|
||||
|
||||
## 基本情報
|
||||
|
||||
### ベース URL
|
||||
|
||||
| デプロイメント | Base URL |
|
||||
| -------------- | ---------------------------------------------------------------------------- |
|
||||
| デスクトップ版 | `http://localhost:39719` |
|
||||
| Docker | `http://<サーバーアドレス>:9900`(実際の `-p` ポートマッピングに合わせて調整) |
|
||||
|
||||
すべてのエンドポイントは `/api` プレフィックスの下にあります。以下の例はデスクトップ版のポート `39719` を使っています。Docker 構成の場合はそちらのポートに置き換えてください。
|
||||
|
||||
### レスポンス形式
|
||||
|
||||
すべての `/api/*` エンドポイントは統一された JSON ラッパー構造を返します:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"code": 0,
|
||||
"message": "ok",
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
| フィールド | 型 | 説明 |
|
||||
| ---------- | ------ | ------------------------------------------------------------ |
|
||||
| `success` | bool | ビジネス処理が成功したかどうか |
|
||||
| `code` | number | ビジネスエラーコード。`0` は成功 |
|
||||
| `message` | string | 人間が読めるメッセージ |
|
||||
| `data` | any | 実際のレスポンスペイロード。エンドポイントにより構造が異なる |
|
||||
|
||||
以下の例の「レスポンス」では `data` フィールドの内容のみを示します。
|
||||
|
||||
### 認証
|
||||
|
||||
- **デスクトップ版**:デフォルトで**認証不要**、`localhost:39719` に直接リクエストするだけ
|
||||
- **Docker デプロイメント**:認証を有効にした場合、MediaGo の**設定ページ**から API キーを取得し、以降のリクエストに `Authorization: Bearer <key>` ヘッダーを付与します
|
||||
|
||||
## クイックスタート
|
||||
|
||||
以下の 3 つの curl コマンドで「作成 → ダウンロード開始 → 完了通知」のフローが一通り流れます。
|
||||
|
||||
### 1. ダウンロードタスクの作成
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:39719/api/downloads \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"name": "私の動画"
|
||||
}
|
||||
],
|
||||
"startDownload": true
|
||||
}'
|
||||
```
|
||||
|
||||
- `type`:ダウンロードタイプ。`m3u8` / `bilibili` / `direct` / `youtube` / `mediago` から選択
|
||||
- `url`:動画の URL
|
||||
- `name`:タスク名(保存ファイル名として使われる)
|
||||
- `startDownload`:作成後すぐにダウンロードを開始するかどうか
|
||||
|
||||
レスポンス:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 123,
|
||||
"name": "私の動画",
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"status": "waiting",
|
||||
"createdDate": "2026-04-23T10:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
返ってきた `id` を控えておいてください。以降のエンドポイントで使います。
|
||||
|
||||
### 2. ダウンロードイベントの購読(SSE)
|
||||
|
||||
```bash
|
||||
curl -N http://localhost:39719/api/events
|
||||
```
|
||||
|
||||
これは長時間接続です。サーバーがプッシュした内容がそのまま流れてきます:
|
||||
|
||||
```text
|
||||
event: download-start
|
||||
data: {"id": "123"}
|
||||
|
||||
event: download-success
|
||||
data: {"id": "123"}
|
||||
```
|
||||
|
||||
ブラウザ / Node.js の場合:
|
||||
|
||||
```javascript
|
||||
const es = new EventSource("http://localhost:39719/api/events");
|
||||
es.addEventListener("download-success", (e) => {
|
||||
const { id } = JSON.parse(e.data);
|
||||
console.log("タスク完了:", id);
|
||||
});
|
||||
```
|
||||
|
||||
### 3. 状態の照会 / 手動制御
|
||||
|
||||
```bash
|
||||
# すべてのダウンロードタスクを一覧表示(ページング)
|
||||
curl "http://localhost:39719/api/downloads?current=1&pageSize=20"
|
||||
|
||||
# 単一のタスクを取得
|
||||
curl http://localhost:39719/api/downloads/123
|
||||
|
||||
# 既存のタスクを開始
|
||||
curl -X POST http://localhost:39719/api/downloads/123/start \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"localPath": "/Downloads/MediaGo", "deleteSegments": true}'
|
||||
|
||||
# タスクを停止
|
||||
curl -X POST http://localhost:39719/api/downloads/123/stop
|
||||
|
||||
# ログを取得
|
||||
curl http://localhost:39719/api/downloads/123/logs
|
||||
```
|
||||
|
||||
## ダウンロードイベント
|
||||
|
||||
`GET /api/events` は Server-Sent Events ストリームです。ダウンロード関連のイベント:
|
||||
|
||||
| イベント名 | ペイロード | 説明 |
|
||||
| ------------------ | -------------------------------- | ---------------------- |
|
||||
| `download-create` | `{ids: number[], count: number}` | タスクの一括作成 |
|
||||
| `download-start` | `{id: string}` | ダウンロード開始 |
|
||||
| `download-success` | `{id: string}` | ダウンロード成功 |
|
||||
| `download-failed` | `{id: string, error: string}` | ダウンロード失敗 |
|
||||
| `download-stop` | `{id: string}` | ダウンロードを手動停止 |
|
||||
|
||||
## エンドポイントリファレンス
|
||||
|
||||
### 一覧 / 照会
|
||||
|
||||
#### `GET /api/downloads` — ページング付きの一覧取得
|
||||
|
||||
**クエリパラメータ:**
|
||||
|
||||
- `current` (number, デフォルト 1):ページ番号
|
||||
- `pageSize` (number, デフォルト 20):ページサイズ
|
||||
- `filter` (string, 任意):ステータスでフィルタ(`downloading` / `success` / `failed`)
|
||||
- `localPath` (string, 任意):保存パスでフィルタ
|
||||
|
||||
**レスポンス:**
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 42,
|
||||
"list": [
|
||||
/* DownloadTask[] */
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/downloads/active` — アクティブなタスクの一覧
|
||||
|
||||
`waiting` / `downloading` 状態のタスクをすべて返します。
|
||||
|
||||
#### `GET /api/downloads/:id` — 単一タスクの取得
|
||||
|
||||
**レスポンス**(`DownloadTask` 構造):
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"name": "私の動画",
|
||||
"type": "m3u8",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"folder": "my-folder",
|
||||
"headers": "User-Agent: ...",
|
||||
"isLive": false,
|
||||
"status": "success",
|
||||
"file": "/path/to/saved.mp4",
|
||||
"createdDate": "2026-04-23T10:00:00Z",
|
||||
"updatedDate": "2026-04-23T10:05:30Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/downloads/folders` — 重複なしの保存ディレクトリ一覧
|
||||
|
||||
**レスポンス:** `string[]`
|
||||
|
||||
#### `GET /api/downloads/export` — ダウンロードリストのエクスポート
|
||||
|
||||
プレーンテキスト、1 行 1 URL。
|
||||
|
||||
#### `GET /api/downloads/:id/logs` — ダウンロードログの取得
|
||||
|
||||
**レスポンス:** `{ id, log: string }`
|
||||
|
||||
### 作成 / 削除
|
||||
|
||||
#### `POST /api/downloads` — ダウンロードの一括作成
|
||||
|
||||
**リクエストボディ:**
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"type": "m3u8 | bilibili | direct | youtube | mediago",
|
||||
"url": "https://example.com/video.m3u8",
|
||||
"name": "タスク名",
|
||||
"folder": "任意のサブディレクトリ",
|
||||
"headers": "任意、複数行の HTTP ヘッダー"
|
||||
}
|
||||
],
|
||||
"startDownload": true
|
||||
}
|
||||
```
|
||||
|
||||
**レスポンス:** `DownloadTask[]`
|
||||
|
||||
#### `DELETE /api/downloads/:id` — タスクの削除
|
||||
|
||||
**レスポンス:** `{}`
|
||||
|
||||
### 編集 / ステータス
|
||||
|
||||
#### `PUT /api/downloads/:id` — タスクの編集
|
||||
|
||||
**リクエストボディ**(すべて任意):
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "新しい名前",
|
||||
"url": "新しい URL",
|
||||
"headers": "新しいヘッダー",
|
||||
"folder": "新しいサブディレクトリ"
|
||||
}
|
||||
```
|
||||
|
||||
#### `PUT /api/downloads/:id/live` — ライブ配信フラグの切り替え
|
||||
|
||||
**リクエストボディ:** `{ "isLive": true }`
|
||||
|
||||
#### `PUT /api/downloads/status` — ステータスの一括更新
|
||||
|
||||
**リクエストボディ:** `{ "ids": number[], "status": "waiting | downloading | success | failed | stopped" }`
|
||||
|
||||
### 開始 / 停止
|
||||
|
||||
#### `POST /api/downloads/:id/start` — ダウンロード開始
|
||||
|
||||
**リクエストボディ:**
|
||||
|
||||
```json
|
||||
{
|
||||
"localPath": "/Users/me/Downloads/MediaGo",
|
||||
"deleteSegments": true
|
||||
}
|
||||
```
|
||||
|
||||
- `localPath`:保存先(絶対パス)
|
||||
- `deleteSegments`:m3u8 ダウンロード完了後、分割された `.ts` ファイルを削除するかどうか
|
||||
|
||||
#### `POST /api/downloads/:id/stop` — ダウンロード停止
|
||||
|
||||
**レスポンス:** `{}`
|
||||
|
||||
## 列挙値
|
||||
|
||||
### ダウンロードタイプ `type`
|
||||
|
||||
| 値 | 説明 |
|
||||
| ---------- | ------------------------------------------------- |
|
||||
| `m3u8` | HLS ストリーム(内部では N_m3u8DL-RE を使用) |
|
||||
| `bilibili` | Bilibili 動画(内部では BBDown を使用) |
|
||||
| `direct` | 直接 HTTP ダウンロード(内部では aria2 を使用) |
|
||||
| `youtube` | YouTube および yt-dlp がサポートする 1000+ サイト |
|
||||
| `mediago` | MediaGo 内部タイプ |
|
||||
|
||||
### タスク状態 `status`
|
||||
|
||||
| 値 | 説明 |
|
||||
| ------------- | -------------------- |
|
||||
| `waiting` | キューに入り、未開始 |
|
||||
| `downloading` | ダウンロード中 |
|
||||
| `success` | 完了 |
|
||||
| `failed` | 失敗 |
|
||||
| `stopped` | 手動停止 |
|
||||
@@ -25,7 +25,7 @@ outline: deep
|
||||
|
||||
### 3. 表示言語
|
||||
|
||||
中国語と英語がサポートされています。
|
||||
中国語、英語、イタリア語がサポートされています。
|
||||
|
||||
### 4. ダウンロード完了通知
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ Chrome Web Store には未公開のため、「パッケージ化されていな
|
||||
|
||||
## インターフェース言語
|
||||
|
||||
拡張機能は日本語表示はサポートしておらず、中国語と英語のみ対応します。既定ではブラウザの UI 言語に追随します(中国語ブラウザでは中国語、それ以外は英語)。設定ページの **インターフェース言語** から強制切替も可能です(システムに従う / 中文 / English)。
|
||||
拡張機能は日本語表示はサポートしておらず、中国語・英語・イタリア語に対応します。既定ではブラウザの UI 言語に追随します。設定ページの **インターフェース言語** から強制切替も可能です(システムに従う / 中文 / English / Italiano)。
|
||||
|
||||
## よくある質問
|
||||
|
||||
|
||||
+8
-7
@@ -7,17 +7,18 @@ outline: deep
|
||||
|
||||
この記事では、ソフトウェアの簡単な説明を行い、すぐに使用できるようにします。[OpenClaw Skill](/jp/skills) に対応し、AI アシスタントで自然言語を使って動画をダウンロードできます。
|
||||
|
||||
::: tip
|
||||
皆さんがより便利にコミュニケーションできるよう、フィードバックグループに参加できます:
|
||||
|
||||
MediaGo QQフィードバックグループ 1: 574209001
|
||||
|
||||
:::
|
||||
|
||||
::: info
|
||||
v3.5 が最新バージョンです。ご意見はできるだけこのバージョンでお寄せください。できるだけ早く対応します。
|
||||
:::
|
||||
|
||||
::: tip
|
||||
macOS での使用
|
||||
|
||||
- **【Intel チップ】** Release から x64 版をインストールしてください。インストール後、Mac のセキュリティ設定で「身元不明の開発元からの App」を許可する必要があります。
|
||||
- **【Apple チップ】** Release から arm64 版をインストールしてください。インストール後、ターミナルで `sudo xattr -dr com.apple.quarantine /Applications/mediago-community.app` を実行する必要があります。
|
||||
|
||||
:::
|
||||
|
||||
## ダウンロードとインストール
|
||||
|
||||
### v3.5.0 (2026年4月22日リリース)
|
||||
|
||||
+27
-15
@@ -2,34 +2,46 @@
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "オンライン動画ダウンロード"
|
||||
text: "簡単に使えて、素早くダウンロード"
|
||||
tagline: 簡単に学べ、パケットキャプチャは不要、プラグインのインストールも不要
|
||||
name: "MediaGo"
|
||||
text: "クロスプラットフォーム動画ダウンローダー"
|
||||
tagline: "ビルトインのスニッフィング — ページを開いて、欲しいリソースを選んで、保存するだけ。パケットキャプチャ不要、プラグイン不要、コマンドライン不要。"
|
||||
image:
|
||||
src: /home.png
|
||||
alt: home
|
||||
alt: MediaGo ホーム画面
|
||||
actions:
|
||||
- theme: brand
|
||||
text: すぐに開始
|
||||
link: /guides
|
||||
link: /jp/guides
|
||||
- theme: alt
|
||||
text: 使用説明
|
||||
link: /documents
|
||||
link: /jp/documents
|
||||
|
||||
features:
|
||||
- icon: ⏩
|
||||
title: パケットキャプチャ不要
|
||||
details: ソフトウェア内蔵のブラウザを使用して、ウェブページの動画リソースを簡単にスニッフし、スニッフしたリソースのリストからダウンロードしたいリソースを選択できます。シンプルで高速です。
|
||||
- icon: 📱
|
||||
title: モバイル再生
|
||||
details: PCとモバイルデバイス間でシームレスに切り替えができ、ダウンロードが完了するとすぐにスマホで動画を視聴できます。
|
||||
details: デスクトップ版に内蔵ブラウザを搭載、動画ページを開くだけでダウンロード可能なリソースを自動で検出します。Fiddler や Charles などのパケットキャプチャツールは不要です。
|
||||
- icon: 🌐
|
||||
title: ブラウザ拡張機能(Chrome / Edge)
|
||||
details: 普段使いの Chrome / Edge でワンクリック動画スニッフィング。ツールバーアイコンに検出件数を表示し、YouTube、Bilibili など主要な動画サイトに対応。デスクトップ版に同梱。
|
||||
- icon: 🎬
|
||||
title: 幅広い動画ソースに対応
|
||||
details: HLS / m3u8 ストリーム、ライブ配信、Bilibili、YouTube、Twitter/X、Instagram など 1000 以上の動画サイトに対応。内部では N_m3u8DL-RE、BBDown、yt-dlp を使用しています。
|
||||
- icon: ⚡️
|
||||
title: バッチダウンロード対応
|
||||
details: 複数の動画やライブストリーミングリソースを同時にダウンロードでき、高速な帯域幅が無駄に使われることはありません。
|
||||
- icon: 🎉
|
||||
title: Dockerデプロイ対応
|
||||
details: Web端をDockerでデプロイでき、簡単で迅速に設定できます。
|
||||
details: 複数の動画やライブストリーミングを同時にダウンロード。高速な帯域幅を無駄にせず、同時実行数もお好みで調整できます。
|
||||
- icon: 🎞️
|
||||
title: 内蔵フォーマット変換
|
||||
details: ダウンロード完了後、MediaGo 内で他のフォーマットや画質に変換できます。ffmpeg を別途起動する必要はありません。
|
||||
- icon: 📱
|
||||
title: モバイル再生
|
||||
details: デスクトップ版は LAN IP でも待ち受けるため、同じ Wi-Fi のスマートフォンやタブレットから Web UI を開いてダウンロード一覧を参照し、直接再生できます。
|
||||
- icon: 🔌
|
||||
title: 開放 HTTP API
|
||||
details: 完全な HTTP API を提供し、スクリプト、自動化ツール、サードパーティアプリからダウンロードタスクの作成、進捗の取得、リスト管理が可能です。
|
||||
- icon: 🦞
|
||||
title: OpenClaw Skill
|
||||
details: AI コーディングアシスタント(OpenClaw、Claude Code など)で自然言語を使って動画をダウンロード。ワンコマンドでインストール。
|
||||
details: Claude Code や Cursor などの AI コーディングアシスタントに「この動画をダウンロードして」と伝えるだけで OK。ワンコマンドで Skill をインストール。
|
||||
- icon: 🐳
|
||||
title: Docker ワンライン展開
|
||||
details: 1 コマンドで NAS / VPS にデプロイ、同じネットワーク内のブラウザから直接アクセス。Docker Hub と GHCR のマルチアーキテクチャイメージ対応。
|
||||
---
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 133 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"appName": {
|
||||
"message": "MediaGo Rilevatore media",
|
||||
"description": "Extension name shown in the Chrome toolbar and chrome://extensions."
|
||||
},
|
||||
"appDescription": {
|
||||
"message": "Rileva URL video / audio scaricabili su qualsiasi pagina e inviali al tuo server MediaGo con un clic.",
|
||||
"description": "Extension description shown in chrome://extensions."
|
||||
},
|
||||
"actionTitle": {
|
||||
"message": "MediaGo Rilevatore media",
|
||||
"description": "Tooltip shown when hovering the toolbar icon."
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,15 @@ export type ExtensionLanguage = "system" | SupportedLanguage;
|
||||
export function resolveLanguage(
|
||||
setting: ExtensionLanguage | undefined,
|
||||
): SupportedLanguage {
|
||||
if (setting === "zh" || setting === "en") return setting;
|
||||
if (setting === "zh" || setting === "en" || setting === "it") return setting;
|
||||
const uiLang =
|
||||
(typeof chrome !== "undefined" && chrome.i18n?.getUILanguage?.()) ||
|
||||
(typeof navigator !== "undefined" ? navigator.language : "") ||
|
||||
"";
|
||||
return uiLang.toLowerCase().startsWith("zh") ? "zh" : "en";
|
||||
const normalizedUiLang = uiLang.toLowerCase();
|
||||
if (normalizedUiLang.startsWith("zh")) return "zh";
|
||||
if (normalizedUiLang.startsWith("it")) return "it";
|
||||
return "en";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,7 @@ const resource: ExtensionResources = {
|
||||
system: "Follow system",
|
||||
zh: "中文",
|
||||
en: "English",
|
||||
it: "Italiano",
|
||||
},
|
||||
server: {
|
||||
title: "Dispatch Mode",
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import en from "./en";
|
||||
import it from "./it";
|
||||
import zh from "./zh";
|
||||
|
||||
export { type ExtensionResources } from "./zh";
|
||||
|
||||
export const resources = {
|
||||
en: { translation: en },
|
||||
it: { translation: it },
|
||||
zh: { translation: zh },
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ const resource: ExtensionResources = {
|
||||
language: {
|
||||
title: "Lingua interfaccia",
|
||||
description:
|
||||
"Lingua usata dal popup e dalla pagina delle opzioni. Scelte "Usa stessa lingua sistema" in base alla lingua dell'interfaccia utente del browser.",
|
||||
`Lingua usata dal popup e dalla pagina delle opzioni. Scelte "Usa stessa lingua sistema" in base alla lingua dell'interfaccia utente del browser.`,
|
||||
system: "Usa stessa lingua sistema",
|
||||
zh: "中文",
|
||||
en: "English",
|
||||
@@ -41,7 +41,7 @@ const resource: ExtensionResources = {
|
||||
"L'estensione non fallisce mai silenziosamente. Una volta scelta una modalità, qualsiasi errore viene segnalato così com'è: se necessario cambia modalità manualmente in questa pagina.",
|
||||
modeSchemaTitle: "Desktop · Protocollo schema",
|
||||
modeSchemaDesc:
|
||||
"Passa tramite il protocollo mediago-community:// (avvia desktop se non è in esecuzione). Richiede l\'installazione locale dell'app MediaGo Desktop.",
|
||||
"Passa tramite il protocollo mediago-community:// (avvia desktop se non è in esecuzione). Richiede l'installazione locale dell'app MediaGo Desktop.",
|
||||
modeDesktopHttpTitle: "Desktop · HTTP locale",
|
||||
modeDesktopHttpDesc:
|
||||
"Parla con l'app desktop in esecuzione tramite {{base}}. Richiede che l'app desktop sia in esecuzione, ma non c'è nessuna finestra di conferma.",
|
||||
@@ -78,7 +78,7 @@ const resource: ExtensionResources = {
|
||||
schemaSilentActive:
|
||||
"ON: il deeplink porta silent=1 quindi MediaGo crea immediatamente l'attività. OFF: MediaGo apre il modulo di download precompilato con il nome/tipo/cartella sniffata per la revisione.",
|
||||
schemaSilentInactive:
|
||||
"Ha effetto solo in modalità sSchema: la modalità HTTP non prevede il concetto di dialogo ed è sempre silenziosa.",0
|
||||
"Ha effetto solo in modalità sSchema: la modalità HTTP non prevede il concetto di dialogo ed è sempre silenziosa.",
|
||||
},
|
||||
rules: {
|
||||
title: "Regole sniffing",
|
||||
|
||||
@@ -31,6 +31,7 @@ const resource = {
|
||||
system: "跟随系统",
|
||||
zh: "中文",
|
||||
en: "English",
|
||||
it: "Italiano",
|
||||
},
|
||||
server: {
|
||||
title: "调用方式",
|
||||
|
||||
@@ -22,6 +22,7 @@ export function LanguageCard() {
|
||||
{ value: "system", title: t("options.language.system") },
|
||||
{ value: "zh", title: t("options.language.zh") },
|
||||
{ value: "en", title: t("options.language.en") },
|
||||
{ value: "it", title: t("options.language.it") },
|
||||
];
|
||||
|
||||
const apply = async (next: ExtensionLanguage) => {
|
||||
|
||||
@@ -4,12 +4,12 @@ import type { DownloadType } from "@mediago/shared-common";
|
||||
* UI language choice persisted in extension settings.
|
||||
*
|
||||
* - `system` — follow the browser UI language (`chrome.i18n.getUILanguage()`).
|
||||
* - `zh` / `en` — hard-pinned.
|
||||
* - `zh` / `en` / `it` — hard-pinned.
|
||||
*
|
||||
* This maps 1:1 to the "跟随系统 / 中文 / English" radio on the options
|
||||
* This maps 1:1 to the language radio on the options
|
||||
* page and is resolved to a concrete locale at i18n bootstrap time.
|
||||
*/
|
||||
export type ExtensionLanguage = "system" | "zh" | "en";
|
||||
export type ExtensionLanguage = "system" | "zh" | "en" | "it";
|
||||
|
||||
/**
|
||||
* Wire-format for localisable text produced in the service worker and
|
||||
|
||||
@@ -6,8 +6,10 @@ export const BASE_I18N_OPTIONS = {
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type ResolvedAppLanguage = "zh" | "en" | "it";
|
||||
|
||||
/**
|
||||
* Resolve a stored AppStore.language value to a real i18n key ("zh" | "en").
|
||||
* Resolve a stored AppStore.language value to a real i18n key.
|
||||
*
|
||||
* The persisted language may be `"system"` (meta value meaning "follow OS locale").
|
||||
* Each process resolves it at apply-time by passing its own locale source:
|
||||
@@ -19,9 +21,17 @@ export const BASE_I18N_OPTIONS = {
|
||||
export function resolveAppLanguage(
|
||||
language: string | undefined,
|
||||
systemLocale: string | undefined,
|
||||
): "zh" | "en" {
|
||||
if (language === "zh" || language === "en") {
|
||||
): ResolvedAppLanguage {
|
||||
if (language === "zh" || language === "en" || language === "it") {
|
||||
return language;
|
||||
}
|
||||
return (systemLocale ?? "").toLowerCase().startsWith("zh") ? "zh" : "en";
|
||||
|
||||
const normalizedSystemLocale = (systemLocale ?? "").toLowerCase();
|
||||
if (normalizedSystemLocale.startsWith("zh")) {
|
||||
return "zh";
|
||||
}
|
||||
if (normalizedSystemLocale.startsWith("it")) {
|
||||
return "it";
|
||||
}
|
||||
return "en";
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ export {
|
||||
DEFAULT_FRONTEND_APP,
|
||||
i18nResources,
|
||||
en,
|
||||
it,
|
||||
zh,
|
||||
} from "./resources";
|
||||
|
||||
@@ -57,6 +57,7 @@ export const en = {
|
||||
displayLanguage: "Language",
|
||||
chinese: "中文",
|
||||
english: "English",
|
||||
italian: "Italiano",
|
||||
pleaseSelectLanguage: "Select a language",
|
||||
downloadPrompt: "Download prompt",
|
||||
browserSetting: "Browser",
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { en } from "./en";
|
||||
import { it } from "./it";
|
||||
import { zh } from "./zh";
|
||||
|
||||
export const i18nResources = {
|
||||
en,
|
||||
it,
|
||||
zh,
|
||||
} as const;
|
||||
|
||||
export const SUPPORTED_LANGUAGES = ["en", "zh"] as const;
|
||||
export const SUPPORTED_LANGUAGES = ["en", "it", "zh"] as const;
|
||||
export const DEFAULT_BACKEND_NAMESPACE = "backend" as const;
|
||||
export const DEFAULT_FRONTEND_APP = "main" as const;
|
||||
|
||||
export { en, zh };
|
||||
export { en, it, zh };
|
||||
|
||||
@@ -235,7 +235,7 @@ export const it = {
|
||||
enableMobilePlayer: "Abilita riproduttore mobile",
|
||||
adminPassword: "Password amministratore",
|
||||
forgotPassword: "Password dimenticata?",
|
||||
forgetPasswordDescription: "Se hai dimenticato la password dell'amministratore, puoi reimpostarla:\n1. Trova la cartella di archiviazione di MediaGo.\n2. Apri la cartella '.store' e modifica 'config.json'.\n3. Cancella il valore del campo apiKey."
|
||||
forgetPasswordDescription: "Se hai dimenticato la password dell'amministratore, puoi reimpostarla:\n1. Trova la cartella di archiviazione di MediaGo.\n2. Apri la cartella '.store' e modifica 'config.json'.\n3. Cancella il valore del campo apiKey.",
|
||||
signin: "Accedi",
|
||||
signinMediaGoServer: "Accedi al server MediaGo",
|
||||
initializeMediaGoServer: "Imposta server MediaGo",
|
||||
@@ -249,4 +249,4 @@ export const it = {
|
||||
apiKey: "Chiave API",
|
||||
pleaseEnterApiKey: "Inserisci chiave API",
|
||||
confirm: "Conferma",
|
||||
};
|
||||
};
|
||||
|
||||
@@ -52,6 +52,7 @@ export const zh = {
|
||||
downloaderTheme: "下载器主题",
|
||||
followSystem: "跟随系统",
|
||||
english: "English",
|
||||
italian: "Italiano",
|
||||
pleaseSelectLanguage: "请选择显示语言",
|
||||
downloadPrompt: "下载完成提示",
|
||||
browserSetting: "浏览器设置",
|
||||
|
||||
@@ -105,6 +105,7 @@ export enum AppLanguage {
|
||||
System = "system",
|
||||
ZH = "zh",
|
||||
EN = "en",
|
||||
IT = "it",
|
||||
}
|
||||
|
||||
export interface DownloadContext {
|
||||
|
||||
Reference in New Issue
Block a user