Compare commits
3 Commits
v1.2.0
...
113-downlo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cab86fe71c | ||
|
|
129899ed61 | ||
|
|
1f821b67dd |
68
README.md
68
README.md
@@ -26,48 +26,7 @@ VidBee is a modern, open-source video downloader that lets you download videos a
|
||||
|
||||
VidBee is currently under active development, and feedback is welcome for any [issue](https://github.com/nexmoe/VidBee/issues) encountered.
|
||||
|
||||
Feel free to try it using the following methods:
|
||||
|
||||
<a href="https://vidbee.org/download/" target="_blank"><img src="https://img.shields.io/badge/Download-VidBee-369eff?style=flat-square&logo=github&logoColor=white&labelColor=black" height="55"/></a>
|
||||
|
||||
### 🍎 macOS Installation Notes
|
||||
|
||||
After downloading and installing VidBee on macOS, you may encounter a "file is damaged" error when trying to run the application. This is due to macOS security restrictions on applications downloaded from the internet.
|
||||
|
||||
```bash
|
||||
xattr -rd com.apple.quarantine /Applications/VidBee.app/
|
||||
```
|
||||
|
||||
This command removes the quarantine attribute that macOS applies to applications downloaded from the internet, allowing VidBee to run properly without the "file is damaged" error.
|
||||
|
||||
### 🌐 Browser Script (Quick Download)
|
||||
|
||||
For a more convenient downloading experience, you can install the VidBee browser script to add a quick download button directly on supported video websites.
|
||||
|
||||

|
||||
|
||||
**Installation:**
|
||||
|
||||
1. Install a userscript manager extension:
|
||||
- [Tampermonkey](https://www.tampermonkey.net/) (Recommended)
|
||||
- [Violentmonkey](https://violentmonkey.github.io/)
|
||||
- [Greasemonkey](https://www.greasespot.net/)
|
||||
|
||||
2. Install the VidBee Quick Download script:
|
||||
- [Install from Greasy Fork](https://greasyfork.org/zh-CN/scripts/559595-vidbee-quick-download)
|
||||
|
||||
**Usage:**
|
||||
|
||||
After installation, when you visit supported video websites (YouTube, Bilibili, TikTok, Instagram, Twitter, etc.), a download button will appear on the page. Click the button to quickly send the video URL to VidBee desktop app for downloading.
|
||||
|
||||
**Supported Sites:**
|
||||
|
||||
The script works on popular video platforms including:
|
||||
|
||||
- YouTube, Bilibili, TikTok, Vimeo, Dailymotion
|
||||
- Twitch, Twitter/X, Instagram, Facebook
|
||||
- Reddit, SoundCloud, Niconico, Kick
|
||||
- Bandcamp, Mixcloud, and more
|
||||
[📥 Download VidBee](https://vidbee.org/download/) | [📚 Documentation](https://docs.vidbee.org)
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
@@ -102,29 +61,14 @@ Automatically subscribe to RSS feeds and auto-download new videos in the backgro
|
||||
|
||||
## 🌐 Supported Sites
|
||||
|
||||
VidBee supports hundreds of video and audio platforms through yt-dlp. Here are the most popular platforms:
|
||||
|
||||
| Video Platforms | Audio & Other Platforms |
|
||||
| :--------------- | :---------------------- |
|
||||
| YouTube | YouTube Music |
|
||||
| TikTok | SoundCloud |
|
||||
| Facebook | Mixcloud |
|
||||
| Instagram | Bandcamp |
|
||||
| X (Twitter) | Reddit |
|
||||
| Vimeo | |
|
||||
| Dailymotion | |
|
||||
| Twitch | |
|
||||
| LinkedIn | |
|
||||
| Pinterest | |
|
||||
| Tumblr | |
|
||||
| Niconico | |
|
||||
| Kick | |
|
||||
|
||||
> **💡 Note:** VidBee uses [yt-dlp](https://github.com/yt-dlp/yt-dlp) under the hood, which supports 1000+ sites. For the complete list, visit the [yt-dlp supported sites documentation](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md).
|
||||
VidBee supports 1000+ video and audio platforms through yt-dlp. For the complete list of supported sites, visit [https://vidbee.org/supported-sites/](https://vidbee.org/supported-sites/)
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
You are welcome to join the open source community to build together. Please check our [Contributing Guide](./CONTRIBUTING.md) for more details.
|
||||
You are welcome to join the open source community to build together. For more details, check out:
|
||||
|
||||
- [Contributing Guide](./CONTRIBUTING.md)
|
||||
- [DeepWiki Documentation](https://deepwiki.com/nexmoe/VidBee)
|
||||
|
||||
## 📄 License
|
||||
|
||||
|
||||
@@ -45,12 +45,13 @@
|
||||
"**/*.js",
|
||||
"**/*.jsx",
|
||||
"**/*.json",
|
||||
"!docs",
|
||||
"!monkey/dist",
|
||||
"!dist",
|
||||
"!out",
|
||||
"!build"
|
||||
],
|
||||
"experimentalScannerIgnores": ["monkey/dist/**", "dist/**", "out/**", "build/**"]
|
||||
"experimentalScannerIgnores": ["docs/**", "monkey/dist/**", "dist/**", "out/**", "build/**"]
|
||||
},
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
|
||||
26
docs/.gitignore
vendored
Normal file
26
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# deps
|
||||
/node_modules
|
||||
|
||||
# generated content
|
||||
.source
|
||||
|
||||
# test & build
|
||||
/coverage
|
||||
/.next/
|
||||
/out/
|
||||
/build
|
||||
*.tsbuildinfo
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
/.pnp
|
||||
.pnp.js
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# others
|
||||
.env*.local
|
||||
.vercel
|
||||
next-env.d.ts
|
||||
45
docs/README.md
Normal file
45
docs/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# docs
|
||||
|
||||
This is a Next.js application generated with
|
||||
[Create Fumadocs](https://github.com/fuma-nama/fumadocs).
|
||||
|
||||
Run development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open http://localhost:3000 with your browser to see the result.
|
||||
|
||||
## Explore
|
||||
|
||||
In the project, you can see:
|
||||
|
||||
- `lib/source.ts`: Code for content source adapter, [`loader()`](https://fumadocs.dev/docs/headless/source-api) provides the interface to access your content.
|
||||
- `lib/layout.shared.tsx`: Shared options for layouts, optional but preferred to keep.
|
||||
|
||||
| Route | Description |
|
||||
| ------------------------- | ------------------------------------------------------ |
|
||||
| `app/(home)` | The route group for your landing page and other pages. |
|
||||
| `app/docs` | The documentation layout and pages. |
|
||||
| `app/api/search/route.ts` | The Route Handler for search. |
|
||||
|
||||
### Fumadocs MDX
|
||||
|
||||
A `source.config.ts` config file has been included, you can customise different options like frontmatter schema.
|
||||
|
||||
Read the [Introduction](https://fumadocs.dev/docs/mdx) for further details.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js and Fumadocs, take a look at the following
|
||||
resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js
|
||||
features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
- [Fumadocs](https://fumadocs.dev) - learn about Fumadocs
|
||||
41
docs/biome.config.json
Normal file
41
docs/biome.config.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.2.0/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": true,
|
||||
"includes": [
|
||||
"**",
|
||||
"!node_modules",
|
||||
"!.next",
|
||||
"!dist",
|
||||
"!build",
|
||||
"!.source"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
},
|
||||
"domains": {
|
||||
"next": "recommended",
|
||||
"react": "recommended"
|
||||
}
|
||||
},
|
||||
"assist": {
|
||||
"actions": {
|
||||
"source": {
|
||||
"organizeImports": "on"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
docs/content/cookies.mdx
Normal file
59
docs/content/cookies.mdx
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: Cookies
|
||||
description: Signed-in downloads, restricted content, and cookie setup
|
||||
---
|
||||
|
||||
Cookies reuse your browser's signed-in session so VidBee can download content that requires login or verification, such as subscriber-only items, age-gated pages, or private links.
|
||||
|
||||
## When you need cookies
|
||||
|
||||
- Content that requires an account to view or download.
|
||||
- Age-gated or region-locked pages.
|
||||
- Private links or lists visible only to signed-in users.
|
||||
|
||||
## Two ways to use cookies in VidBee
|
||||
|
||||
### Option 1: Read cookies from your browser
|
||||
|
||||
In **Settings**, select your browser. VidBee will try to detect the browser profile path automatically. You can also enter the profile path manually.
|
||||
|
||||
**Supported browsers (platform dependent):**
|
||||
|
||||
- **Windows: Firefox only. Other browsers cannot be used for cookie reading.**
|
||||
- macOS: Safari, Chrome, Edge, Brave, Firefox, and more.
|
||||
- Linux: Chrome, Chromium, Brave, Firefox, and more.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Open VidBee → Settings → Cookies.
|
||||
2. Select a browser and confirm the profile path.
|
||||
3. Go back and start the download again.
|
||||
|
||||
If detection fails or the path is invalid, manually choose the actual browser profile directory.
|
||||
On Windows, if you are not using Firefox, switch to the cookies file method.
|
||||
|
||||
### Option 2: Import a cookies file
|
||||
|
||||
You can import a **Netscape-formatted** cookies file. This is useful when reading the browser profile is not possible.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Export a Netscape cookies file using a browser extension.
|
||||
2. Open VidBee → Settings → Cookies file, and select the exported file.
|
||||
3. Click Clear to disable it.
|
||||
|
||||
## Recommendations
|
||||
|
||||
- Prefer browser-based cookies when possible, because it's easier to maintain.
|
||||
- If browser reading fails, switch to a cookies file.
|
||||
- Cookies files expire. Re-export when your account state changes.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Profile path is invalid**: Ensure the folder exists and points to the real browser profile directory.
|
||||
- **Still asks for login**: Confirm your browser is signed in and re-save the setting.
|
||||
- **Browser read failed**: Close the browser and retry, or switch to a cookies file.
|
||||
|
||||
## Privacy and security
|
||||
|
||||
Cookies are equivalent to your login session. Keep them private and never share or upload them. If you suspect leakage, sign out and change your password.
|
||||
36
docs/content/faq.mdx
Normal file
36
docs/content/faq.mdx
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: FAQ
|
||||
description: Common questions and troubleshooting for VidBee
|
||||
---
|
||||
|
||||
## What if a download fails or shows an error?
|
||||
|
||||
- Confirm the link is valid and opens in your browser.
|
||||
- Update VidBee to the latest version and retry.
|
||||
- If the content requires login or age verification, configure cookies.
|
||||
|
||||
## Why does the same link sometimes work and sometimes fail?
|
||||
|
||||
Some sites frequently change page structure or limit request rates. Suggested steps:
|
||||
|
||||
- Make sure VidBee is up to date.
|
||||
- Reduce the number of concurrent downloads.
|
||||
- Use cookies to reuse your signed-in session when needed.
|
||||
|
||||
## Which sites are supported?
|
||||
|
||||
VidBee uses the yt-dlp extractor system and supports many sites. Try the download first; if it fails, submit feedback with the link and error details.
|
||||
|
||||
## Why is the download speed slow?
|
||||
|
||||
- Check your network and proxy settings.
|
||||
- Avoid starting too many tasks at once.
|
||||
- Some sites limit bandwidth on their side.
|
||||
|
||||
## macOS says “file is damaged”
|
||||
|
||||
Remove the quarantine attribute and retry:
|
||||
|
||||
```bash
|
||||
xattr -rd com.apple.quarantine /Applications/VidBee.app/
|
||||
```
|
||||
19
docs/content/index.mdx
Normal file
19
docs/content/index.mdx
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: VidBee Docs
|
||||
description: VidBee desktop downloader documentation and FAQ
|
||||
---
|
||||
|
||||
VidBee is a desktop downloader built with Electron and powered by yt-dlp. It provides a clean interface and queue management for downloading video and audio.
|
||||
|
||||
These docs focus on real-world usage and settings, especially signed-in downloads and cookie configuration.
|
||||
|
||||
## Start here
|
||||
|
||||
- [Cookies](./cookies.mdx): Configure signed-in sessions and restricted content.
|
||||
- [FAQ](./faq.mdx): Common questions and troubleshooting.
|
||||
|
||||
## Quick links
|
||||
|
||||
- [VidBee website](https://vidbee.org/)
|
||||
- [Supported sites](https://vidbee.org/supported-sites/)
|
||||
- [Features](https://vidbee.org/features/)
|
||||
4
docs/content/meta.json
Normal file
4
docs/content/meta.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "VidBee Docs",
|
||||
"pages": ["index", "cookies", "faq"]
|
||||
}
|
||||
59
docs/content/zh/cookies.mdx
Normal file
59
docs/content/zh/cookies.mdx
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: Cookie 使用说明
|
||||
description: 登录下载、受限内容与 Cookie 配置
|
||||
---
|
||||
|
||||
Cookie 用于复用浏览器的登录态,帮助 VidBee 下载需要登录或验证的内容,例如订阅内容、年龄限制或私密链接。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- 需要账号登录才能观看或下载的内容。
|
||||
- 年龄限制或地区限制页面。
|
||||
- 仅对登录用户可见的私密链接或列表。
|
||||
|
||||
## VidBee 支持的两种方式
|
||||
|
||||
### 方式一:读取浏览器 Cookie
|
||||
|
||||
在 **设置** 中选择你的浏览器,VidBee 会尝试自动识别浏览器配置文件路径。你也可以手动填写配置文件路径。
|
||||
|
||||
**支持的浏览器(按平台差异显示):**
|
||||
|
||||
- **Windows:仅支持 Firefox。其他浏览器无法读取 Cookie。**
|
||||
- macOS:Safari、Chrome、Edge、Brave、Firefox 等。
|
||||
- Linux:Chrome、Chromium、Brave、Firefox 等。
|
||||
|
||||
**使用步骤:**
|
||||
|
||||
1. 打开 VidBee → 设置 → Cookie。
|
||||
2. 选择浏览器并确认配置文件路径。
|
||||
3. 返回下载页面,重新开始下载任务。
|
||||
|
||||
如果识别失败或路径无效,请手动选择实际的浏览器配置文件目录。
|
||||
在 Windows 上,如果不是 Firefox,请改用 cookies 文件方式。
|
||||
|
||||
### 方式二:导入 Cookies 文件
|
||||
|
||||
你也可以导入 **Netscape 格式** 的 cookies 文件。这个方式适合在不方便读取浏览器配置文件时使用。
|
||||
|
||||
**使用步骤:**
|
||||
|
||||
1. 使用浏览器扩展导出 Netscape cookies 文件。
|
||||
2. 打开 VidBee → 设置 → Cookies 文件,选择导出的文件。
|
||||
3. 如需停用,可点击“清除”。
|
||||
|
||||
## 使用建议
|
||||
|
||||
- 优先使用浏览器读取方式,维护成本更低。
|
||||
- 如果浏览器读取失败,再切换到 cookies 文件方式。
|
||||
- cookies 文件会过期,账号状态变化时需要重新导出。
|
||||
|
||||
## 常见问题
|
||||
|
||||
- **提示配置文件路径无效**:请确认路径存在,并指向实际的浏览器配置文件目录。
|
||||
- **下载仍提示需要登录**:确认当前浏览器已登录对应账号,并重新保存设置。
|
||||
- **浏览器读取失败**:关闭浏览器后重试,或改用 cookies 文件方式。
|
||||
|
||||
## 隐私与安全
|
||||
|
||||
Cookie 等同于登录态,请妥善保管,不要共享或上传。若怀疑泄露,请及时在网站上退出登录并更新密码。
|
||||
36
docs/content/zh/faq.mdx
Normal file
36
docs/content/zh/faq.mdx
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: 常见问题 (FAQ)
|
||||
description: VidBee 使用过程中的常见问题与建议
|
||||
---
|
||||
|
||||
## 下载失败或报错怎么办?
|
||||
|
||||
- 先确认链接是否有效,并尝试在浏览器中打开。
|
||||
- 升级 VidBee 到最新版本后重试。
|
||||
- 如果是登录或年龄限制内容,请配置 Cookie。
|
||||
|
||||
## 为什么同一个链接有时可以、有时不行?
|
||||
|
||||
部分站点会频繁更新页面结构或限制请求频率。建议:
|
||||
|
||||
- 确保 VidBee 为最新版本。
|
||||
- 适当降低同时下载任务数量。
|
||||
- 必要时使用 Cookie 复用登录态。
|
||||
|
||||
## 支持哪些网站?
|
||||
|
||||
VidBee 基于 yt-dlp 解析器体系,覆盖大量站点。可以先尝试下载;若失败,请提交反馈并附上链接与错误提示。
|
||||
|
||||
## 下载速度慢怎么办?
|
||||
|
||||
- 检查本地网络与代理设置。
|
||||
- 避免同时发起过多任务。
|
||||
- 某些站点本身带宽较低,速度受限。
|
||||
|
||||
## macOS 提示“文件已损坏”
|
||||
|
||||
请执行以下命令移除隔离标记后重试:
|
||||
|
||||
```bash
|
||||
xattr -rd com.apple.quarantine /Applications/VidBee.app/
|
||||
```
|
||||
19
docs/content/zh/index.mdx
Normal file
19
docs/content/zh/index.mdx
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: VidBee 文档
|
||||
description: VidBee 桌面下载器使用说明与常见问题
|
||||
---
|
||||
|
||||
VidBee 是一款基于 Electron 的桌面下载器,内置 yt-dlp 引擎,提供清爽的界面与队列管理能力,用于下载视频与音频内容。
|
||||
|
||||
这份文档聚焦 VidBee 的实际使用场景与设置说明,尤其是登录下载与 Cookie 相关配置。
|
||||
|
||||
## 从这里开始
|
||||
|
||||
- [Cookie 使用](./cookies.mdx):登录态与限制内容的下载配置。
|
||||
- [常见问题](./faq.mdx):常见问题与排查思路。
|
||||
|
||||
## 快捷链接
|
||||
|
||||
- [VidBee 官网](https://vidbee.org/)
|
||||
- [支持站点](https://vidbee.org/supported-sites/)
|
||||
- [功能介绍](https://vidbee.org/features/)
|
||||
4
docs/content/zh/meta.json
Normal file
4
docs/content/zh/meta.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "VidBee 文档",
|
||||
"pages": ["index", "cookies", "faq"]
|
||||
}
|
||||
15
docs/next.config.mjs
Normal file
15
docs/next.config.mjs
Normal file
@@ -0,0 +1,15 @@
|
||||
import { createMDX } from 'fumadocs-mdx/next';
|
||||
|
||||
const withMDX = createMDX();
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const config = {
|
||||
output: 'export',
|
||||
reactStrictMode: true,
|
||||
// Use trailing slashes to avoid conflicts with route handlers that have file extensions
|
||||
trailingSlash: true,
|
||||
// Note: rewrites are not supported with static export
|
||||
// The /llms.mdx route will be pre-rendered as static files
|
||||
};
|
||||
|
||||
export default withMDX(config);
|
||||
36
docs/package.json
Normal file
36
docs/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "docs",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "next build && node scripts/post-export.js",
|
||||
"dev": "next dev",
|
||||
"start": "next start",
|
||||
"types:check": "fumadocs-mdx && next typegen && tsc --noEmit",
|
||||
"postinstall": "fumadocs-mdx",
|
||||
"lint": "biome check --config-path biome.config.json",
|
||||
"format": "biome format --write --config-path biome.config.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"fumadocs-core": "16.4.7",
|
||||
"fumadocs-mdx": "14.2.5",
|
||||
"fumadocs-ui": "16.4.7",
|
||||
"lucide-react": "^0.562.0",
|
||||
"next": "16.1.1",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"tailwind-merge": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.18",
|
||||
"@types/mdx": "^2.0.13",
|
||||
"@types/node": "^25.0.5",
|
||||
"@types/react": "^19.2.8",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"typescript": "^5.9.3",
|
||||
"@biomejs/biome": "^2.3.11"
|
||||
}
|
||||
}
|
||||
3989
docs/pnpm-lock.yaml
generated
Normal file
3989
docs/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
5
docs/postcss.config.mjs
Normal file
5
docs/postcss.config.mjs
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
},
|
||||
};
|
||||
46
docs/scripts/post-export.js
Executable file
46
docs/scripts/post-export.js
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Post-export script to copy English content to root directory
|
||||
* This allows the default language (en) to be accessible without language prefix
|
||||
*/
|
||||
|
||||
import { cpSync, existsSync, mkdirSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
const outDir = join(__dirname, '../out');
|
||||
const enDir = join(outDir, 'en');
|
||||
|
||||
console.log('Copying English content to root directory...');
|
||||
|
||||
if (!existsSync(enDir)) {
|
||||
console.error('Error: /en directory not found in output');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Get all items in /en directory
|
||||
const fs = await import('node:fs/promises');
|
||||
const items = await fs.readdir(enDir);
|
||||
|
||||
// Copy each item to root, excluding already existing root items
|
||||
for (const item of items) {
|
||||
const source = join(enDir, item);
|
||||
const dest = join(outDir, item);
|
||||
|
||||
// Skip if item already exists at root (like _next, api, etc.)
|
||||
if (existsSync(dest)) {
|
||||
console.log(`Skipping ${item} (already exists at root)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
cpSync(source, dest, { recursive: true });
|
||||
console.log(`Copied ${item}`);
|
||||
} catch (error) {
|
||||
console.error(`Error copying ${item}:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('English content copied to root directory successfully!');
|
||||
22
docs/source.config.ts
Normal file
22
docs/source.config.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { defineConfig, defineDocs, frontmatterSchema, metaSchema } from 'fumadocs-mdx/config';
|
||||
|
||||
// You can customise Zod schemas for frontmatter and `meta.json` here
|
||||
// see https://fumadocs.dev/docs/mdx/collections
|
||||
export const docs = defineDocs({
|
||||
dir: 'content',
|
||||
docs: {
|
||||
schema: frontmatterSchema,
|
||||
postprocess: {
|
||||
includeProcessedMarkdown: true,
|
||||
},
|
||||
},
|
||||
meta: {
|
||||
schema: metaSchema,
|
||||
},
|
||||
});
|
||||
|
||||
export default defineConfig({
|
||||
mdxOptions: {
|
||||
// MDX options
|
||||
},
|
||||
});
|
||||
67
docs/src/app/[lang]/(docs)/[[...slug]]/page.tsx
Normal file
67
docs/src/app/[lang]/(docs)/[[...slug]]/page.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { getPageImage, source } from '@/lib/source';
|
||||
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { getMDXComponents } from '@/mdx-components';
|
||||
import type { Metadata } from 'next';
|
||||
import { createRelativeLink } from 'fumadocs-ui/mdx';
|
||||
import { GitHubEditButton, LLMCopyButton, ViewOptions } from '@/components/ai/page-actions';
|
||||
|
||||
export default async function Page(props: PageProps<'/[lang]/[[...slug]]'>) {
|
||||
const params = await props.params;
|
||||
const page = source.getPage(params.slug, params.lang);
|
||||
if (!page) notFound();
|
||||
|
||||
const MDX = page.data.body;
|
||||
const gitConfig = {
|
||||
user: 'nexmoe',
|
||||
repo: 'VidBee',
|
||||
branch: 'main',
|
||||
};
|
||||
|
||||
const githubFilePath = `docs/content/${page.path}`;
|
||||
const githubBlobUrl = `https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/${githubFilePath}`;
|
||||
const githubEditUrl = `https://github.com/${gitConfig.user}/${gitConfig.repo}/edit/${gitConfig.branch}/${githubFilePath}`;
|
||||
|
||||
return (
|
||||
<DocsPage toc={page.data.toc} full={page.data.full}>
|
||||
<DocsTitle>{page.data.title}</DocsTitle>
|
||||
<DocsDescription className="mb-0">{page.data.description}</DocsDescription>
|
||||
<div className="flex flex-row gap-2 items-center border-b pb-6">
|
||||
<GitHubEditButton href={githubEditUrl} />
|
||||
<LLMCopyButton markdownUrl={`${page.url}.mdx`} />
|
||||
<ViewOptions
|
||||
markdownUrl={`${page.url}.mdx`}
|
||||
githubUrl={githubBlobUrl}
|
||||
/>
|
||||
</div>
|
||||
<DocsBody>
|
||||
<MDX
|
||||
components={getMDXComponents({
|
||||
// this allows you to link to other pages with relative file paths
|
||||
a: createRelativeLink(source, page),
|
||||
})}
|
||||
/>
|
||||
</DocsBody>
|
||||
</DocsPage>
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return source.generateParams();
|
||||
}
|
||||
|
||||
export async function generateMetadata(
|
||||
props: PageProps<'/[lang]/[[...slug]]'>,
|
||||
): Promise<Metadata> {
|
||||
const params = await props.params;
|
||||
const page = source.getPage(params.slug, params.lang);
|
||||
if (!page) notFound();
|
||||
|
||||
return {
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
openGraph: {
|
||||
images: getPageImage(page).url,
|
||||
},
|
||||
};
|
||||
}
|
||||
20
docs/src/app/[lang]/(docs)/layout.tsx
Normal file
20
docs/src/app/[lang]/(docs)/layout.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { source } from '@/lib/source';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
|
||||
export default async function Layout({
|
||||
children,
|
||||
params,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
params: Promise<{ lang: string; slug?: string[] }>;
|
||||
}) {
|
||||
const resolvedParams = await params;
|
||||
const locale = resolvedParams.lang;
|
||||
return (
|
||||
<DocsLayout tree={source.getPageTree(locale)} {...baseOptions(locale)}>
|
||||
{children}
|
||||
</DocsLayout>
|
||||
);
|
||||
}
|
||||
39
docs/src/app/[lang]/layout.tsx
Normal file
39
docs/src/app/[lang]/layout.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { RootProvider } from 'fumadocs-ui/provider/next';
|
||||
import { defineI18nUI } from 'fumadocs-ui/i18n';
|
||||
import { i18n, isLocale } from '@/lib/i18n';
|
||||
|
||||
const { provider } = defineI18nUI(i18n, {
|
||||
translations: {
|
||||
en: {
|
||||
displayName: 'English',
|
||||
},
|
||||
zh: {
|
||||
displayName: '中文',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default async function Layout({
|
||||
children,
|
||||
params,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
params: Promise<{ lang: string }>;
|
||||
}) {
|
||||
const { lang } = await params;
|
||||
const locale = isLocale(lang) ? lang : i18n.defaultLanguage;
|
||||
|
||||
return (
|
||||
<RootProvider
|
||||
i18n={provider(locale)}
|
||||
search={{
|
||||
options: {
|
||||
type: 'static',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RootProvider>
|
||||
);
|
||||
}
|
||||
14
docs/src/app/api/search/route.ts
Normal file
14
docs/src/app/api/search/route.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { source } from '@/lib/source';
|
||||
import { createFromSource } from 'fumadocs-core/search/server';
|
||||
|
||||
// statically cached for static export
|
||||
export const revalidate = false;
|
||||
|
||||
// Configure language support for both English and Chinese
|
||||
export const { staticGET: GET } = createFromSource(source, {
|
||||
localeMap: {
|
||||
en: { language: 'english' },
|
||||
// Chinese is not natively supported by Orama, use English tokenizer for zh
|
||||
zh: { language: 'english' },
|
||||
},
|
||||
});
|
||||
3
docs/src/app/global.css
Normal file
3
docs/src/app/global.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@import 'tailwindcss';
|
||||
@import 'fumadocs-ui/css/neutral.css';
|
||||
@import 'fumadocs-ui/css/preset.css';
|
||||
26
docs/src/app/layout.tsx
Normal file
26
docs/src/app/layout.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import './global.css';
|
||||
import { Inter } from 'next/font/google';
|
||||
import type { ReactNode } from 'react';
|
||||
import { i18n, isLocale } from '@/lib/i18n';
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
});
|
||||
|
||||
export default async function Layout({
|
||||
children,
|
||||
params,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
params?: Promise<{ lang?: string }>;
|
||||
}) {
|
||||
const resolvedParams = params ? await params : undefined;
|
||||
const lang = isLocale(resolvedParams?.lang) ? resolvedParams.lang : i18n.defaultLanguage;
|
||||
return (
|
||||
<html lang={lang} className={inter.className} suppressHydrationWarning>
|
||||
<body className="flex flex-col min-h-screen">
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
250
docs/src/components/ai/page-actions.tsx
Normal file
250
docs/src/components/ai/page-actions.tsx
Normal file
@@ -0,0 +1,250 @@
|
||||
'use client';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Check, ChevronDown, Copy, ExternalLinkIcon, MessageCircleIcon } from 'lucide-react';
|
||||
import { cn } from '@/lib/cn';
|
||||
import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button';
|
||||
import { buttonVariants } from 'fumadocs-ui/components/ui/button';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from 'fumadocs-ui/components/ui/popover';
|
||||
|
||||
const cache = new Map<string, string>();
|
||||
|
||||
export function LLMCopyButton({
|
||||
/**
|
||||
* A URL to fetch the raw Markdown/MDX content of page
|
||||
*/
|
||||
markdownUrl,
|
||||
}: {
|
||||
markdownUrl: string;
|
||||
}) {
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const [checked, onClick] = useCopyButton(async () => {
|
||||
const cached = cache.get(markdownUrl);
|
||||
if (cached) return navigator.clipboard.writeText(cached);
|
||||
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
await navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
'text/plain': fetch(markdownUrl).then(async (res) => {
|
||||
const content = await res.text();
|
||||
cache.set(markdownUrl, content);
|
||||
|
||||
return content;
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<button
|
||||
disabled={isLoading}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
color: 'secondary',
|
||||
size: 'sm',
|
||||
className: 'gap-2 [&_svg]:size-3.5 [&_svg]:text-fd-muted-foreground',
|
||||
}),
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{checked ? <Check /> : <Copy />}
|
||||
Copy Markdown
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function ViewOptions({
|
||||
markdownUrl,
|
||||
githubUrl,
|
||||
}: {
|
||||
/**
|
||||
* A URL to the raw Markdown/MDX content of page
|
||||
*/
|
||||
markdownUrl: string;
|
||||
|
||||
/**
|
||||
* Source file URL on GitHub
|
||||
*/
|
||||
githubUrl: string;
|
||||
}) {
|
||||
const items = useMemo(() => {
|
||||
const fullMarkdownUrl =
|
||||
typeof window !== 'undefined' ? new URL(markdownUrl, window.location.origin) : 'loading';
|
||||
const q = `Read ${fullMarkdownUrl}, I want to ask questions about it.`;
|
||||
|
||||
return [
|
||||
{
|
||||
title: 'Open in GitHub',
|
||||
href: githubUrl,
|
||||
icon: (
|
||||
<svg fill="currentColor" role="img" viewBox="0 0 24 24">
|
||||
<title>GitHub</title>
|
||||
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Open in Scira AI',
|
||||
href: `https://scira.ai/?${new URLSearchParams({
|
||||
q,
|
||||
})}`,
|
||||
icon: (
|
||||
<svg
|
||||
width="910"
|
||||
height="934"
|
||||
viewBox="0 0 910 934"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Scira AI</title>
|
||||
<path
|
||||
d="M647.664 197.775C569.13 189.049 525.5 145.419 516.774 66.8849C508.048 145.419 464.418 189.049 385.884 197.775C464.418 206.501 508.048 250.131 516.774 328.665C525.5 250.131 569.13 206.501 647.664 197.775Z"
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
strokeWidth="8"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M516.774 304.217C510.299 275.491 498.208 252.087 480.335 234.214C462.462 216.341 439.058 204.251 410.333 197.775C439.059 191.3 462.462 179.209 480.335 161.336C498.208 143.463 510.299 120.06 516.774 91.334C523.25 120.059 535.34 143.463 553.213 161.336C571.086 179.209 594.49 191.3 623.216 197.775C594.49 204.251 571.086 216.341 553.213 234.214C535.34 252.087 523.25 275.491 516.774 304.217Z"
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
strokeWidth="8"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M857.5 508.116C763.259 497.644 710.903 445.288 700.432 351.047C689.961 445.288 637.605 497.644 543.364 508.116C637.605 518.587 689.961 570.943 700.432 665.184C710.903 570.943 763.259 518.587 857.5 508.116Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="20"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M700.432 615.957C691.848 589.05 678.575 566.357 660.383 548.165C642.191 529.973 619.499 516.7 592.593 508.116C619.499 499.533 642.191 486.258 660.383 468.066C678.575 449.874 691.848 427.181 700.432 400.274C709.015 427.181 722.289 449.874 740.481 468.066C758.673 486.258 781.365 499.533 808.271 508.116C781.365 516.7 758.673 529.973 740.481 548.165C722.289 566.357 709.015 589.05 700.432 615.957Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="20"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M889.949 121.237C831.049 114.692 798.326 81.9698 791.782 23.0692C785.237 81.9698 752.515 114.692 693.614 121.237C752.515 127.781 785.237 160.504 791.782 219.404C798.326 160.504 831.049 127.781 889.949 121.237Z"
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
strokeWidth="8"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M791.782 196.795C786.697 176.937 777.869 160.567 765.16 147.858C752.452 135.15 736.082 126.322 716.226 121.237C736.082 116.152 752.452 107.324 765.16 94.6152C777.869 81.9065 786.697 65.5368 791.782 45.6797C796.867 65.5367 805.695 81.9066 818.403 94.6152C831.112 107.324 847.481 116.152 867.338 121.237C847.481 126.322 831.112 135.15 818.403 147.858C805.694 160.567 796.867 176.937 791.782 196.795Z"
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
strokeWidth="8"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M760.632 764.337C720.719 814.616 669.835 855.1 611.872 882.692C553.91 910.285 490.404 924.255 426.213 923.533C362.022 922.812 298.846 907.419 241.518 878.531C184.19 849.643 134.228 808.026 95.4548 756.863C56.6815 705.7 30.1238 646.346 17.8129 583.343C5.50207 520.339 7.76433 455.354 24.4266 393.359C41.089 331.364 71.7099 274.001 113.947 225.658C156.184 177.315 208.919 139.273 268.117 114.442"
|
||||
stroke="currentColor"
|
||||
strokeWidth="30"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Open in ChatGPT',
|
||||
href: `https://chatgpt.com/?${new URLSearchParams({
|
||||
hints: 'search',
|
||||
q,
|
||||
})}`,
|
||||
icon: (
|
||||
<svg
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>OpenAI</title>
|
||||
<path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Open in Claude',
|
||||
href: `https://claude.ai/new?${new URLSearchParams({
|
||||
q,
|
||||
})}`,
|
||||
icon: (
|
||||
<svg
|
||||
fill="currentColor"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Anthropic</title>
|
||||
<path d="M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Open in T3 Chat',
|
||||
href: `https://t3.chat/new?${new URLSearchParams({
|
||||
q,
|
||||
})}`,
|
||||
icon: <MessageCircleIcon />,
|
||||
},
|
||||
];
|
||||
}, [githubUrl, markdownUrl]);
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
color: 'secondary',
|
||||
size: 'sm',
|
||||
className: 'gap-2',
|
||||
}),
|
||||
)}
|
||||
>
|
||||
Open
|
||||
<ChevronDown className="size-3.5 text-fd-muted-foreground" />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="flex flex-col">
|
||||
{items.map((item) => (
|
||||
<a
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
className="text-sm p-2 rounded-lg inline-flex items-center gap-2 hover:text-fd-accent-foreground hover:bg-fd-accent [&_svg]:size-4"
|
||||
>
|
||||
{item.icon}
|
||||
{item.title}
|
||||
<ExternalLinkIcon className="text-fd-muted-foreground size-3.5 ms-auto" />
|
||||
</a>
|
||||
))}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
export function GitHubEditButton({ href }: { href: string }) {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
color: 'secondary',
|
||||
size: 'sm',
|
||||
className: 'gap-2',
|
||||
})
|
||||
)}
|
||||
>
|
||||
<ExternalLinkIcon className="size-3.5 text-fd-muted-foreground" />
|
||||
Edit on GitHub
|
||||
</a>
|
||||
)
|
||||
}
|
||||
1
docs/src/lib/cn.ts
Normal file
1
docs/src/lib/cn.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { twMerge as cn } from 'tailwind-merge';
|
||||
34
docs/src/lib/i18n.ts
Normal file
34
docs/src/lib/i18n.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { defineI18n } from 'fumadocs-core/i18n';
|
||||
|
||||
export const i18n = defineI18n({
|
||||
languages: ['en', 'zh'],
|
||||
defaultLanguage: 'en',
|
||||
// Hide locale prefix for default language (en) so English content appears at root
|
||||
hideLocale: 'default-locale',
|
||||
parser: 'dir'
|
||||
});
|
||||
|
||||
export type Locale = (typeof i18n.languages)[number];
|
||||
|
||||
const localeSet = new Set(i18n.languages);
|
||||
|
||||
export function isLocale(value?: string): value is Locale {
|
||||
return Boolean(value && localeSet.has(value as Locale));
|
||||
}
|
||||
|
||||
export function resolveLocaleFromSlug(slug?: string[]): Locale {
|
||||
if (slug && slug.length > 0 && isLocale(slug[0])) {
|
||||
return slug[0];
|
||||
}
|
||||
return i18n.defaultLanguage;
|
||||
}
|
||||
|
||||
export function stripLocaleFromSlug(slug?: string[]): string[] {
|
||||
if (!slug || slug.length === 0) {
|
||||
return [];
|
||||
}
|
||||
if (isLocale(slug[0])) {
|
||||
return slug.slice(1);
|
||||
}
|
||||
return slug;
|
||||
}
|
||||
11
docs/src/lib/layout.shared.tsx
Normal file
11
docs/src/lib/layout.shared.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
import { i18n } from '@/lib/i18n';
|
||||
|
||||
export function baseOptions(_locale: string): BaseLayoutProps {
|
||||
return {
|
||||
nav: {
|
||||
title: 'VidBee',
|
||||
},
|
||||
i18n,
|
||||
};
|
||||
}
|
||||
31
docs/src/lib/source.ts
Normal file
31
docs/src/lib/source.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { docs } from 'fumadocs-mdx:collections/server';
|
||||
import { type InferPageType, loader } from 'fumadocs-core/source';
|
||||
import { lucideIconsPlugin } from 'fumadocs-core/source/lucide-icons';
|
||||
import { i18n } from '@/lib/i18n';
|
||||
|
||||
// See https://fumadocs.dev/docs/headless/source-api for more info
|
||||
export const source = loader({
|
||||
baseUrl: '/',
|
||||
i18n,
|
||||
source: docs.toFumadocsSource(),
|
||||
plugins: [lucideIconsPlugin()],
|
||||
});
|
||||
|
||||
export function getPageImage(page: InferPageType<typeof source>) {
|
||||
const localePrefix =
|
||||
page.locale && page.locale !== i18n.defaultLanguage ? [page.locale] : [];
|
||||
const segments = [...localePrefix, ...page.slugs, 'image.png'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `/og/docs/${segments.join('/')}`,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getLLMText(page: InferPageType<typeof source>) {
|
||||
const processed = await page.data.getText('processed');
|
||||
|
||||
return `# ${page.data.title}
|
||||
|
||||
${processed}`;
|
||||
}
|
||||
9
docs/src/mdx-components.tsx
Normal file
9
docs/src/mdx-components.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import defaultMdxComponents from 'fumadocs-ui/mdx';
|
||||
import type { MDXComponents } from 'mdx/types';
|
||||
|
||||
export function getMDXComponents(components?: MDXComponents): MDXComponents {
|
||||
return {
|
||||
...defaultMdxComponents,
|
||||
...components,
|
||||
};
|
||||
}
|
||||
9
docs/src/middleware.ts
Normal file
9
docs/src/middleware.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createI18nMiddleware } from 'fumadocs-core/i18n/middleware';
|
||||
import { i18n } from '@/lib/i18n';
|
||||
|
||||
export default createI18nMiddleware(i18n);
|
||||
|
||||
export const config = {
|
||||
// Note: Middleware doesn't run in static export, but kept for development
|
||||
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
|
||||
};
|
||||
46
docs/tsconfig.json
Normal file
46
docs/tsconfig.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"target": "ESNext",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
],
|
||||
"fumadocs-mdx:collections/*": [
|
||||
".source/*"
|
||||
]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -665,7 +665,6 @@ export function DownloadDialog({
|
||||
addDownload(downloadItem)
|
||||
})
|
||||
|
||||
toast.success(t('playlist.downloadStarted', { count: result.totalCount }))
|
||||
setOpen(false) // Close dialog after download starts
|
||||
} catch (error) {
|
||||
console.error('Failed to start playlist download:', error)
|
||||
@@ -748,7 +747,6 @@ export function DownloadDialog({
|
||||
createdAt: Date.now()
|
||||
})
|
||||
|
||||
toast.success(t('notifications.downloadStarted'))
|
||||
setOpen(false) // Close dialog after download starts
|
||||
} catch (error) {
|
||||
console.error('Failed to start download:', error)
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "البحث عن التحديثات...",
|
||||
"downloadError": "فشل في تنزيل التحديث",
|
||||
"downloadStarted": "بدأ التحميل...",
|
||||
"downloadUpdate": "تنزيل وتثبيت التحديث {{version}}؟",
|
||||
"manualDownloadAction": "تنزيل الآن",
|
||||
"noUpdatesAvailable": "أنت تستخدم أحدث إصدار",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "خطأ",
|
||||
"fetch": "جلب",
|
||||
"fetchingVideoInfo": "جلب معلومات الفيديو...",
|
||||
"feedback": {
|
||||
"title": "أبلغ عن هذا الخطأ:",
|
||||
"githubUrlTooLong": "رابط GitHub هذا طويل جدًا. إذا لم يفتح، فالرجاء فتح صفحة المشكلة ولصق السجلات يدويًا."
|
||||
},
|
||||
"history": "السجل",
|
||||
"imageLoadError": "فشل تحميل الصورة",
|
||||
"imagePlaceholder": "لا توجد صورة متاحة",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "ملاحظة التنسيق",
|
||||
"protocol": "البروتوكول",
|
||||
"subscription": "الاشتراك"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "رابط GitHub هذا طويل جدًا. إذا لم يفتح، فالرجاء فتح صفحة المشكلة ولصق السجلات يدويًا."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "فشل في النسخ إلى الحافظة",
|
||||
"downloadCompleted": "اكتمل التحميل",
|
||||
"downloadFailed": "فشل التحميل",
|
||||
"downloadStarted": "بدأ التحميل",
|
||||
"historyCleared": "تم مسح السجل",
|
||||
"historyClearFailed": "فشل مسح السجل",
|
||||
"itemRemoved": "تم إزالة العنصر",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "تحميل جميع الفيديوهات من قائمة تشغيل أو قناة YouTube",
|
||||
"downloadFailed": "فشل في بدء تحميل قائمة التشغيل",
|
||||
"downloadPlaylist": "تحميل قائمة التشغيل",
|
||||
"downloadStarted": "بدأ تحميل {{count}} فيديو من قائمة التشغيل",
|
||||
"downloadType": "نوع التحميل",
|
||||
"downloading": "جاري تحميل قائمة التشغيل:",
|
||||
"endIndex": "النهاية",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "المنصات الرئيسية",
|
||||
"viewAll": "عرض جميع المواقع المدعومة"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "Suche nach Updates...",
|
||||
"downloadError": "Update konnte nicht heruntergeladen werden",
|
||||
"downloadStarted": "Download gestartet...",
|
||||
"downloadUpdate": "Update {{version}} herunterladen und installieren?",
|
||||
"manualDownloadAction": "Jetzt herunterladen",
|
||||
"noUpdatesAvailable": "Sie verwenden die neueste Version",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "Fehler",
|
||||
"fetch": "Abrufen",
|
||||
"fetchingVideoInfo": "Video-Informationen werden abgerufen...",
|
||||
"feedback": {
|
||||
"title": "Fehler melden:",
|
||||
"githubUrlTooLong": "Dieser GitHub-Link ist sehr lang. Wenn er sich nicht öffnet, öffnen Sie bitte die Issue-Seite und fügen Sie die Logs manuell ein."
|
||||
},
|
||||
"history": "Verlauf",
|
||||
"imageLoadError": "Bild konnte nicht geladen werden",
|
||||
"imagePlaceholder": "Kein Bild verfügbar",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "Format-Hinweis",
|
||||
"protocol": "Protokoll",
|
||||
"subscription": "Abonnement"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "Dieser GitHub-Link ist sehr lang. Wenn er sich nicht öffnet, öffnen Sie bitte die Issue-Seite und fügen Sie die Logs manuell ein."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "Kopieren in Zwischenablage fehlgeschlagen",
|
||||
"downloadCompleted": "Download abgeschlossen",
|
||||
"downloadFailed": "Download fehlgeschlagen",
|
||||
"downloadStarted": "Download gestartet",
|
||||
"historyCleared": "Verlauf gelöscht",
|
||||
"historyClearFailed": "Verlauf konnte nicht gelöscht werden",
|
||||
"itemRemoved": "Element entfernt",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "Alle Videos aus einer YouTube-Playlist oder einem Kanal herunterladen",
|
||||
"downloadFailed": "Playlist-Download konnte nicht gestartet werden",
|
||||
"downloadPlaylist": "Playlist herunterladen",
|
||||
"downloadStarted": "Download von {{count}} Videos aus Playlist gestartet",
|
||||
"downloadType": "Download-Typ",
|
||||
"downloading": "Playlist wird heruntergeladen:",
|
||||
"endIndex": "Ende",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "Hauptplattformen",
|
||||
"viewAll": "Alle unterstützten Websites anzeigen"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "Looking for updates...",
|
||||
"downloadError": "Failed to download update",
|
||||
"downloadStarted": "Download started...",
|
||||
"downloadUpdate": "Download and install update {{version}}?",
|
||||
"manualDownloadAction": "Download now",
|
||||
"noUpdatesAvailable": "You're using the latest version",
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "Failed to copy to clipboard",
|
||||
"downloadCompleted": "Download completed",
|
||||
"downloadFailed": "Download failed",
|
||||
"downloadStarted": "Download started",
|
||||
"historyCleared": "History cleared",
|
||||
"historyClearFailed": "Failed to clear history",
|
||||
"itemRemoved": "Item removed",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "Download all videos from a YouTube playlist or channel",
|
||||
"downloadFailed": "Failed to start playlist download",
|
||||
"downloadPlaylist": "Download Playlist",
|
||||
"downloadStarted": "Started downloading {{count}} videos from playlist",
|
||||
"downloadType": "Download Type",
|
||||
"downloading": "Downloading playlist:",
|
||||
"endIndex": "End",
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "Buscando actualizaciones...",
|
||||
"downloadError": "Error al descargar la actualización",
|
||||
"downloadStarted": "Descarga iniciada...",
|
||||
"downloadUpdate": "¿Descargar e instalar la actualización {{version}}?",
|
||||
"manualDownloadAction": "Descargar ahora",
|
||||
"noUpdatesAvailable": "Estás usando la última versión",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "Error",
|
||||
"fetch": "Obtener",
|
||||
"fetchingVideoInfo": "Obteniendo información del video...",
|
||||
"feedback": {
|
||||
"title": "Informe este error:",
|
||||
"githubUrlTooLong": "Este enlace de GitHub es muy largo. Si no se abre, abre la página de la incidencia y pega los registros manualmente."
|
||||
},
|
||||
"history": "Historial",
|
||||
"imageLoadError": "Error al cargar la imagen",
|
||||
"imagePlaceholder": "No hay imagen disponible",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "Nota de formato",
|
||||
"protocol": "Protocolo",
|
||||
"subscription": "Suscripción"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "Este enlace de GitHub es muy largo. Si no se abre, abre la página de la incidencia y pega los registros manualmente."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "Error al copiar al portapapeles",
|
||||
"downloadCompleted": "Descarga completada",
|
||||
"downloadFailed": "Error en la descarga",
|
||||
"downloadStarted": "Descarga iniciada",
|
||||
"historyCleared": "Historial borrado",
|
||||
"historyClearFailed": "No se pudo borrar el historial",
|
||||
"itemRemoved": "Elemento eliminado",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "Descarga todos los videos de una lista de reproducción o canal de YouTube",
|
||||
"downloadFailed": "Error al iniciar la descarga de la lista de reproducción",
|
||||
"downloadPlaylist": "Descargar Lista de Reproducción",
|
||||
"downloadStarted": "Comenzó la descarga de {{count}} videos de la lista de reproducción",
|
||||
"downloadType": "Tipo de Descarga",
|
||||
"downloading": "Descargando lista de reproducción:",
|
||||
"endIndex": "Fin",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "Plataformas principales",
|
||||
"viewAll": "Ver todos los sitios soportados"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "Recherche de mises à jour...",
|
||||
"downloadError": "Échec du téléchargement de la mise à jour",
|
||||
"downloadStarted": "Téléchargement démarré...",
|
||||
"downloadUpdate": "Télécharger et installer la mise à jour {{version}} ?",
|
||||
"manualDownloadAction": "Téléchargez maintenant",
|
||||
"noUpdatesAvailable": "Vous utilisez la dernière version",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "Erreur",
|
||||
"fetch": "Récupérer",
|
||||
"fetchingVideoInfo": "Récupération des informations vidéo...",
|
||||
"feedback": {
|
||||
"title": "Signalez cette erreur :",
|
||||
"githubUrlTooLong": "Ce lien GitHub est très long. S'il ne s'ouvre pas, ouvrez la page de l'issue et collez les logs manuellement."
|
||||
},
|
||||
"history": "Historique",
|
||||
"imageLoadError": "Échec du chargement de l'image",
|
||||
"imagePlaceholder": "Aucune image disponible",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "Remarque sur le format",
|
||||
"protocol": "Protocole",
|
||||
"subscription": "Abonnement"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "Ce lien GitHub est très long. S'il ne s'ouvre pas, ouvrez la page de l'issue et collez les logs manuellement."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "Échec de la copie dans le presse-papiers",
|
||||
"downloadCompleted": "Téléchargement terminé",
|
||||
"downloadFailed": "Échec du téléchargement",
|
||||
"downloadStarted": "Téléchargement démarré",
|
||||
"historyCleared": "Historique effacé",
|
||||
"historyClearFailed": "Échec de l'effacement de l'historique",
|
||||
"itemRemoved": "Élément supprimé",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "Télécharger toutes les vidéos d'une playlist ou chaîne YouTube",
|
||||
"downloadFailed": "Échec du démarrage du téléchargement de playlist",
|
||||
"downloadPlaylist": "Télécharger la Playlist",
|
||||
"downloadStarted": "Démarré le téléchargement de {{count}} vidéos de la playlist",
|
||||
"downloadType": "Type de Téléchargement",
|
||||
"downloading": "Téléchargement de la playlist :",
|
||||
"endIndex": "Fin",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "Plateformes principales",
|
||||
"viewAll": "Voir tous les sites supportés"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "Mencari pembaruan...",
|
||||
"downloadError": "Gagal mengunduh pembaruan",
|
||||
"downloadStarted": "Unduhan dimulai...",
|
||||
"downloadUpdate": "Unduh dan instal pembaruan {{version}}?",
|
||||
"manualDownloadAction": "Unduh sekarang",
|
||||
"noUpdatesAvailable": "Anda menggunakan versi terbaru",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "Kesalahan",
|
||||
"fetch": "Ambil",
|
||||
"fetchingVideoInfo": "Mengambil info video...",
|
||||
"feedback": {
|
||||
"title": "Laporkan kesalahan ini:",
|
||||
"githubUrlTooLong": "Tautan GitHub ini sangat panjang. Jika tidak terbuka, buka halaman issue dan tempel log secara manual."
|
||||
},
|
||||
"history": "Riwayat",
|
||||
"imageLoadError": "Gagal memuat gambar",
|
||||
"imagePlaceholder": "Tidak ada gambar tersedia",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "Catatan format",
|
||||
"protocol": "Protokol",
|
||||
"subscription": "Berlangganan"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "Tautan GitHub ini sangat panjang. Jika tidak terbuka, buka halaman issue dan tempel log secara manual."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "Gagal menyalin ke clipboard",
|
||||
"downloadCompleted": "Unduhan selesai",
|
||||
"downloadFailed": "Unduhan gagal",
|
||||
"downloadStarted": "Unduhan dimulai",
|
||||
"historyCleared": "Riwayat dihapus",
|
||||
"historyClearFailed": "Gagal menghapus riwayat",
|
||||
"itemRemoved": "Item dihapus",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "Unduh semua video dari playlist atau saluran YouTube",
|
||||
"downloadFailed": "Gagal memulai unduhan playlist",
|
||||
"downloadPlaylist": "Unduh Playlist",
|
||||
"downloadStarted": "Mulai mengunduh {{count}} video dari playlist",
|
||||
"downloadType": "Jenis Unduhan",
|
||||
"downloading": "Mengunduh playlist:",
|
||||
"endIndex": "Akhir",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "Platform utama",
|
||||
"viewAll": "Lihat semua situs yang didukung"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "Ricerca aggiornamenti...",
|
||||
"downloadError": "Errore nel download dell'aggiornamento",
|
||||
"downloadStarted": "Download iniziato...",
|
||||
"downloadUpdate": "Scarica e installa l'aggiornamento {{version}}?",
|
||||
"manualDownloadAction": "Scarica ora",
|
||||
"noUpdatesAvailable": "Stai usando l'ultima versione",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "Errore",
|
||||
"fetch": "Recupera",
|
||||
"fetchingVideoInfo": "Recupero informazioni video...",
|
||||
"feedback": {
|
||||
"title": "Segnala questo errore:",
|
||||
"githubUrlTooLong": "Questo link GitHub è molto lungo. Se non si apre, apri la pagina dell'issue e incolla i log manualmente."
|
||||
},
|
||||
"history": "Cronologia",
|
||||
"imageLoadError": "Errore nel caricamento dell'immagine",
|
||||
"imagePlaceholder": "Nessuna immagine disponibile",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "Nota sul formato",
|
||||
"protocol": "Protocollo",
|
||||
"subscription": "Sottoscrizione"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "Questo link GitHub è molto lungo. Se non si apre, apri la pagina dell'issue e incolla i log manualmente."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "Errore nella copia negli appunti",
|
||||
"downloadCompleted": "Download completato",
|
||||
"downloadFailed": "Download fallito",
|
||||
"downloadStarted": "Download iniziato",
|
||||
"historyCleared": "Cronologia cancellata",
|
||||
"historyClearFailed": "Impossibile cancellare la cronologia",
|
||||
"itemRemoved": "Elemento rimosso",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "Scarica tutti i video da una playlist o canale YouTube",
|
||||
"downloadFailed": "Errore nell'avvio del download playlist",
|
||||
"downloadPlaylist": "Scarica Playlist",
|
||||
"downloadStarted": "Iniziato download di {{count}} video dalla playlist",
|
||||
"downloadType": "Tipo Download",
|
||||
"downloading": "Scaricando playlist:",
|
||||
"endIndex": "Fine",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "Piattaforme principali",
|
||||
"viewAll": "Visualizza tutti i siti supportati"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "アップデートを検索中...",
|
||||
"downloadError": "アップデートのダウンロードに失敗",
|
||||
"downloadStarted": "ダウンロード開始...",
|
||||
"downloadUpdate": "アップデート{{version}}をダウンロードしてインストールしますか?",
|
||||
"manualDownloadAction": "今すぐダウンロード",
|
||||
"noUpdatesAvailable": "最新バージョンを使用しています",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "エラー",
|
||||
"fetch": "取得",
|
||||
"fetchingVideoInfo": "ビデオ情報を取得中...",
|
||||
"feedback": {
|
||||
"title": "このエラーを報告:",
|
||||
"githubUrlTooLong": "このGitHubリンクは非常に長いです。開けない場合は、issueページを開いてログを手動で貼り付けてください。"
|
||||
},
|
||||
"history": "履歴",
|
||||
"imageLoadError": "画像の読み込みに失敗",
|
||||
"imagePlaceholder": "利用可能な画像なし",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "メモのフォーマット",
|
||||
"protocol": "プロトコル",
|
||||
"subscription": "サブスクリプション"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "このGitHubリンクは非常に長いです。開けない場合は、issueページを開いてログを手動で貼り付けてください。"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "クリップボードへのコピーに失敗",
|
||||
"downloadCompleted": "ダウンロード完了",
|
||||
"downloadFailed": "ダウンロードに失敗",
|
||||
"downloadStarted": "ダウンロード開始",
|
||||
"historyCleared": "履歴をクリアしました",
|
||||
"historyClearFailed": "履歴のクリアに失敗しました",
|
||||
"itemRemoved": "アイテムが削除されました",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "YouTubeプレイリストまたはチャンネルからすべてのビデオをダウンロード",
|
||||
"downloadFailed": "プレイリストダウンロードの開始に失敗",
|
||||
"downloadPlaylist": "プレイリストをダウンロード",
|
||||
"downloadStarted": "プレイリストから{{count}}個のビデオのダウンロードを開始",
|
||||
"downloadType": "ダウンロードタイプ",
|
||||
"downloading": "プレイリストをダウンロード中:",
|
||||
"endIndex": "終了",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "主要プラットフォーム",
|
||||
"viewAll": "サポートされているすべてのサイトを表示"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "업데이트 검색 중...",
|
||||
"downloadError": "업데이트 다운로드 실패",
|
||||
"downloadStarted": "다운로드 시작...",
|
||||
"downloadUpdate": "업데이트 {{version}}을(를) 다운로드하고 설치하시겠습니까?",
|
||||
"manualDownloadAction": "지금 다운로드",
|
||||
"noUpdatesAvailable": "최신 버전을 사용 중입니다",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "오류",
|
||||
"fetch": "가져오기",
|
||||
"fetchingVideoInfo": "비디오 정보 가져오는 중...",
|
||||
"feedback": {
|
||||
"title": "이 오류를 보고:",
|
||||
"githubUrlTooLong": "이 GitHub 링크가 매우 깁니다. 열리지 않으면 이슈 페이지를 열고 로그를 수동으로 붙여 넣어 주세요."
|
||||
},
|
||||
"history": "기록",
|
||||
"imageLoadError": "이미지 로드 실패",
|
||||
"imagePlaceholder": "사용 가능한 이미지 없음",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "메모 형식",
|
||||
"protocol": "규약",
|
||||
"subscription": "신청"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "이 GitHub 링크가 매우 깁니다. 열리지 않으면 이슈 페이지를 열고 로그를 수동으로 붙여 넣어 주세요."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "클립보드에 복사 실패",
|
||||
"downloadCompleted": "다운로드 완료",
|
||||
"downloadFailed": "다운로드 실패",
|
||||
"downloadStarted": "다운로드 시작됨",
|
||||
"historyCleared": "기록이 지워졌습니다",
|
||||
"historyClearFailed": "기록을 지우지 못했습니다",
|
||||
"itemRemoved": "항목 제거됨",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "YouTube 재생목록 또는 채널의 모든 비디오 다운로드",
|
||||
"downloadFailed": "재생목록 다운로드 시작 실패",
|
||||
"downloadPlaylist": "재생목록 다운로드",
|
||||
"downloadStarted": "재생목록에서 {{count}}개 비디오 다운로드 시작됨",
|
||||
"downloadType": "다운로드 유형",
|
||||
"downloading": "재생목록 다운로드 중:",
|
||||
"endIndex": "끝",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "주요 플랫폼",
|
||||
"viewAll": "지원되는 모든 사이트 보기"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "Procurando atualizações...",
|
||||
"downloadError": "Falha ao baixar atualização",
|
||||
"downloadStarted": "Download iniciado...",
|
||||
"downloadUpdate": "Baixar e instalar atualização {{version}}?",
|
||||
"manualDownloadAction": "Baixe agora",
|
||||
"noUpdatesAvailable": "Você está usando a versão mais recente",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "Erro",
|
||||
"fetch": "Buscar",
|
||||
"fetchingVideoInfo": "Buscando informações do vídeo...",
|
||||
"feedback": {
|
||||
"title": "Relatar este erro:",
|
||||
"githubUrlTooLong": "Este link do GitHub é muito longo. Se não abrir, abra a página da issue e cole os logs manualmente."
|
||||
},
|
||||
"history": "Histórico",
|
||||
"imageLoadError": "Falha ao carregar imagem",
|
||||
"imagePlaceholder": "Nenhuma imagem disponível",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "Formatar nota",
|
||||
"protocol": "Protocolo",
|
||||
"subscription": "Subscrição"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "Este link do GitHub é muito longo. Se não abrir, abra a página da issue e cole os logs manualmente."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "Falha ao copiar para área de transferência",
|
||||
"downloadCompleted": "Download concluído",
|
||||
"downloadFailed": "Download falhou",
|
||||
"downloadStarted": "Download iniciado",
|
||||
"historyCleared": "Histórico limpo",
|
||||
"historyClearFailed": "Falha ao limpar histórico",
|
||||
"itemRemoved": "Item removido",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "Baixar todos os vídeos de uma playlist ou canal do YouTube",
|
||||
"downloadFailed": "Falha ao iniciar download da playlist",
|
||||
"downloadPlaylist": "Baixar Playlist",
|
||||
"downloadStarted": "Iniciado download de {{count}} vídeos da playlist",
|
||||
"downloadType": "Tipo de Download",
|
||||
"downloading": "Baixando playlist:",
|
||||
"endIndex": "Fim",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "Plataformas principais",
|
||||
"viewAll": "Ver todos os sites suportados"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "Поиск обновлений...",
|
||||
"downloadError": "Не удалось загрузить обновление",
|
||||
"downloadStarted": "Загрузка началась...",
|
||||
"downloadUpdate": "Загрузить и установить обновление {{version}}?",
|
||||
"manualDownloadAction": "Скачать сейчас",
|
||||
"noUpdatesAvailable": "Вы используете последнюю версию",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "Ошибка",
|
||||
"fetch": "Получить",
|
||||
"fetchingVideoInfo": "Получение информации о видео...",
|
||||
"feedback": {
|
||||
"title": "Сообщить об этой ошибке:",
|
||||
"githubUrlTooLong": "Эта ссылка GitHub очень длинная. Если она не открывается, откройте страницу issue и вставьте логи вручную."
|
||||
},
|
||||
"history": "История",
|
||||
"imageLoadError": "Не удалось загрузить изображение",
|
||||
"imagePlaceholder": "Изображение недоступно",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "Примечание формата",
|
||||
"protocol": "Протокол",
|
||||
"subscription": "Подписка"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "Эта ссылка GitHub очень длинная. Если она не открывается, откройте страницу issue и вставьте логи вручную."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "Не удалось скопировать в буфер обмена",
|
||||
"downloadCompleted": "Загрузка завершена",
|
||||
"downloadFailed": "Загрузка не удалась",
|
||||
"downloadStarted": "Загрузка началась",
|
||||
"historyCleared": "История очищена",
|
||||
"historyClearFailed": "Не удалось очистить историю",
|
||||
"itemRemoved": "Элемент удалён",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "Загрузить все видео из плейлиста или канала YouTube",
|
||||
"downloadFailed": "Не удалось начать загрузку плейлиста",
|
||||
"downloadPlaylist": "Загрузить плейлист",
|
||||
"downloadStarted": "Начата загрузка {{count}} видео из плейлиста",
|
||||
"downloadType": "Тип загрузки",
|
||||
"downloading": "Загрузка плейлиста:",
|
||||
"endIndex": "Конец",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "Основные платформы",
|
||||
"viewAll": "Просмотреть все поддерживаемые сайты"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "正在搜尋更新...",
|
||||
"downloadError": "下載更新失敗",
|
||||
"downloadStarted": "開始下載...",
|
||||
"downloadUpdate": "下載並安裝更新 {{version}}?",
|
||||
"manualDownloadAction": "立即下載",
|
||||
"noUpdatesAvailable": "您正在使用最新版本",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "錯誤",
|
||||
"fetch": "取得",
|
||||
"fetchingVideoInfo": "正在取得影片資訊...",
|
||||
"feedback": {
|
||||
"title": "報告此錯誤:",
|
||||
"githubUrlTooLong": "這個 GitHub 連結很長。如果無法開啟,請開啟 issue 頁面並手動貼上日誌。"
|
||||
},
|
||||
"history": "歷史",
|
||||
"imageLoadError": "圖片載入失敗",
|
||||
"imagePlaceholder": "暫無圖片",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "格式註釋",
|
||||
"protocol": "協定",
|
||||
"subscription": "訂閱"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "這個 GitHub 連結很長。如果無法開啟,請開啟 issue 頁面並手動貼上日誌。"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "複製到剪貼簿失敗",
|
||||
"downloadCompleted": "下載完成",
|
||||
"downloadFailed": "下載失敗",
|
||||
"downloadStarted": "下載已開始",
|
||||
"historyCleared": "歷史紀錄已清除",
|
||||
"historyClearFailed": "清除歷史紀錄失敗",
|
||||
"itemRemoved": "項目已移除",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "下載 YouTube 播放清單或頻道中的全部影片",
|
||||
"downloadFailed": "啟動播放清單下載失敗",
|
||||
"downloadPlaylist": "下載播放清單",
|
||||
"downloadStarted": "已開始下載播放清單中的 {{count}} 個影片",
|
||||
"downloadType": "下載類型",
|
||||
"downloading": "正在下載播放清單:",
|
||||
"endIndex": "結束",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "主流平台",
|
||||
"viewAll": "檢視全部支援的網站"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"notifications": {
|
||||
"checkingUpdates": "正在查找更新...",
|
||||
"downloadError": "下载更新失败",
|
||||
"downloadStarted": "开始下载...",
|
||||
"downloadUpdate": "下载并安装更新 {{version}}?",
|
||||
"manualDownloadAction": "立即下载",
|
||||
"noUpdatesAvailable": "您正在使用最新版本",
|
||||
@@ -130,6 +129,10 @@
|
||||
"error": "错误",
|
||||
"fetch": "获取",
|
||||
"fetchingVideoInfo": "正在获取视频信息...",
|
||||
"feedback": {
|
||||
"title": "报告此错误:",
|
||||
"githubUrlTooLong": "这个 GitHub 链接很长。如果无法打开,请打开 issue 页面并手动粘贴日志。"
|
||||
},
|
||||
"history": "历史",
|
||||
"imageLoadError": "图像加载失败",
|
||||
"imagePlaceholder": "暂无图像",
|
||||
@@ -195,10 +198,6 @@
|
||||
"formatNote": "格式注释",
|
||||
"protocol": "协议",
|
||||
"subscription": "订阅"
|
||||
},
|
||||
"feedback": {
|
||||
"title": "Report this error:",
|
||||
"githubUrlTooLong": "这个 GitHub 链接很长。如果无法打开,请打开 issue 页面并手动粘贴日志。"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -303,7 +302,6 @@
|
||||
"copyFailed": "复制到剪贴板失败",
|
||||
"downloadCompleted": "下载完成",
|
||||
"downloadFailed": "下载失败",
|
||||
"downloadStarted": "下载已开始",
|
||||
"historyCleared": "历史记录已清除",
|
||||
"historyClearFailed": "清除历史记录失败",
|
||||
"itemRemoved": "项目已移除",
|
||||
@@ -327,7 +325,6 @@
|
||||
"description": "下载 YouTube 播放列表或频道中的全部视频",
|
||||
"downloadFailed": "启动播放列表下载失败",
|
||||
"downloadPlaylist": "下载播放列表",
|
||||
"downloadStarted": "已开始下载播放列表中的 {{count}} 个视频",
|
||||
"downloadType": "下载类型",
|
||||
"downloading": "正在下载播放列表:",
|
||||
"endIndex": "结束",
|
||||
@@ -663,4 +660,4 @@
|
||||
"popularSection": "主流平台",
|
||||
"viewAll": "查看全部支持的网站"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user