5 Commits

Author SHA1 Message Date
Peifan Li
40536d1963 chore(release): v1.3.7 2025-12-02 13:03:02 -05:00
Peifan Li
5341bf842b docs: Update README with Python and yt-dlp installation instructions 2025-12-02 13:02:58 -05:00
Peifan Li
26184ba3c5 feat: Add bgutil-ytdlp-pot-provider integration 2025-12-02 12:56:12 -05:00
Peifan Li
1e5884d454 refactor: Update character set for sanitizing filename 2025-12-02 12:28:18 -05:00
Peifan Li
04790fdddf fix: Update versions to 1.3.5 and revise features 2025-12-02 00:06:50 -05:00
12 changed files with 53 additions and 18 deletions

View File

@@ -1,6 +1,6 @@
# MyTube
一个 YouTube/Bilibili/MissAV 视频下载和播放应用,允许您将视频及其缩略图本地保存并提供订阅下载功能。将您的视频整理到收藏夹中,以便轻松访问和管理。现已支持[yt-dlp所有网址](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md##)包括微博小红书x.com等。
一个 YouTube/Bilibili/MissAV 视频下载和播放应用,支持频道订阅与自动下载,允许您将视频及其缩略图本地保存。将您的视频整理到收藏夹中,以便轻松访问和管理。现已支持[yt-dlp所有网址](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md##)包括微博小红书x.com等。
[English](README.md)

View File

@@ -1,6 +1,6 @@
# MyTube
A YouTube/Bilibili/MissAV video downloader and player application that allows you to download and save videos locally, along with their thumbnails. Organize your videos into collections for easy access and management. Now supports [yt-dlp sites](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md##), including Weibo, Xiaohongshu, X.com, etc.
A YouTube/Bilibili/MissAV video downloader and player that supports channel subscriptions and auto-downloads, allowing you to save videos and thumbnails locally. Organize your videos into collections for easy access and management. Now supports [yt-dlp sites](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md##), including Weibo, Xiaohongshu, X.com, etc.
[中文](README-zh.md)
@@ -74,6 +74,8 @@ mytube/
- Node.js (v14 or higher)
- npm (v6 or higher)
- Docker (optional, for containerized deployment)
- Python 3.8+ (for yt-dlp and PO Token provider)
- yt-dlp (installed via pip/pipx)
### Installation
@@ -100,6 +102,16 @@ mytube/
cd ../backend && npm install
```
**Note**: The backend installation will automatically build the `bgutil-ytdlp-pot-provider` server. However, you must ensure `yt-dlp` and the `bgutil-ytdlp-pot-provider` python plugin are installed in your environment:
```bash
# Install yt-dlp and the plugin
pip install yt-dlp bgutil-ytdlp-pot-provider
# OR using pipx (recommended)
pipx install yt-dlp
pipx inject yt-dlp bgutil-ytdlp-pot-provider
```
#### Using npm Scripts
You can use npm scripts from the root directory:

View File

@@ -12,6 +12,12 @@ ENV YOUTUBE_DL_SKIP_PYTHON_CHECK=1
RUN npm ci
COPY . .
# Build bgutil-ytdlp-pot-provider
WORKDIR /app/bgutil-ytdlp-pot-provider/server
RUN npm install && npx tsc
WORKDIR /app
RUN npm run build
# Stage 2: Production
@@ -30,6 +36,9 @@ RUN apk add --no-cache \
py3-pip && \
ln -sf python3 /usr/bin/python
# Install yt-dlp and bgutil-ytdlp-pot-provider
RUN pip3 install yt-dlp bgutil-ytdlp-pot-provider --break-system-packages
# Environment variables
ENV NODE_ENV=production
ENV PORT=5551
@@ -44,6 +53,8 @@ RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
# Copy drizzle migrations
COPY --from=builder /app/drizzle ./drizzle
# Copy bgutil-ytdlp-pot-provider
COPY --from=builder /app/bgutil-ytdlp-pot-provider /app/bgutil-ytdlp-pot-provider
# Create necessary directories
RUN mkdir -p uploads/videos uploads/images data

Submodule backend/bgutil-ytdlp-pot-provider added at 9c3cc1a21d

View File

@@ -1,12 +1,12 @@
{
"name": "backend",
"version": "1.3.4",
"version": "1.3.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "backend",
"version": "1.3.4",
"version": "1.3.5",
"license": "ISC",
"dependencies": {
"axios": "^1.8.1",

View File

@@ -1,6 +1,6 @@
{
"name": "backend",
"version": "1.3.5",
"version": "1.3.7",
"main": "server.js",
"scripts": {
"start": "ts-node src/server.ts",
@@ -8,7 +8,8 @@
"build": "tsc",
"generate": "drizzle-kit generate",
"test": "vitest",
"test:coverage": "vitest run --coverage"
"test:coverage": "vitest run --coverage",
"postinstall": "cd bgutil-ytdlp-pot-provider/server && npm install && npx tsc"
},
"keywords": [],
"author": "",

View File

@@ -7,6 +7,9 @@ import { sanitizeFilename } from "../../utils/helpers";
import * as storageService from "../storageService";
import { Video } from "../storageService";
const YT_DLP_PATH = process.env.YT_DLP_PATH || "yt-dlp";
const PROVIDER_SCRIPT = process.env.BGUTIL_SCRIPT_PATH || path.join(process.cwd(), "bgutil-ytdlp-pot-provider/server/build/generate_once.js");
// Helper function to extract author from XiaoHongShu page when yt-dlp doesn't provide it
async function extractXiaoHongShuAuthor(url: string): Promise<string | null> {
try {
@@ -54,7 +57,8 @@ export class YtDlpDownloader {
noWarnings: true,
skipDownload: true,
playlistEnd: 5, // Limit to 5 results
} as any);
extractorArgs: `youtubepot-bgutilscript:script_path=${PROVIDER_SCRIPT}`,
} as any, { execPath: YT_DLP_PATH } as any);
if (!searchResults || !(searchResults as any).entries) {
return [];
@@ -87,7 +91,8 @@ export class YtDlpDownloader {
noWarnings: true,
preferFreeFormats: true,
// youtubeSkipDashManifest: true, // Specific to YT, might want to keep or make conditional
} as any);
extractorArgs: `youtubepot-bgutilscript:script_path=${PROVIDER_SCRIPT}`,
} as any, { execPath: YT_DLP_PATH } as any);
return {
title: info.title || "Video",
@@ -127,7 +132,8 @@ export class YtDlpDownloader {
playlistEnd: 5,
noWarnings: true,
flatPlaylist: true, // We only need the ID/URL, not full info
} as any);
extractorArgs: `youtubepot-bgutilscript:script_path=${PROVIDER_SCRIPT}`,
} as any, { execPath: YT_DLP_PATH } as any);
// If it's a playlist/channel, 'entries' will contain the videos
if ((result as any).entries && (result as any).entries.length > 0) {
@@ -178,7 +184,8 @@ export class YtDlpDownloader {
dumpSingleJson: true,
noWarnings: true,
preferFreeFormats: true,
} as any);
extractorArgs: `youtubepot-bgutilscript:script_path=${PROVIDER_SCRIPT}`,
} as any, { execPath: YT_DLP_PATH } as any);
console.log("Video info:", {
title: info.title,
@@ -245,8 +252,11 @@ export class YtDlpDownloader {
];
}
// Add PO Token provider args
flags.extractorArgs = `youtubepot-bgutilscript:script_path=${PROVIDER_SCRIPT}`;
// Use exec to capture stdout for progress
const subprocess = youtubedl.exec(videoUrl, flags);
const subprocess = youtubedl.exec(videoUrl, flags, { execPath: YT_DLP_PATH } as any);
if (onStart) {
onStart(() => {

View File

@@ -109,7 +109,7 @@ export function sanitizeFilename(filename: string): string {
// Replace only unsafe characters for filesystems
// This preserves non-Latin characters like Chinese, Japanese, Korean, etc.
const sanitized = withoutHashtags
.replace(/[\/\\:*?"<>|]/g, "_") // Replace unsafe filesystem characters
.replace(/[\/\\:*?"<>|%,'!;=+\$@^`{}~\[\]()&]/g, "_") // Replace unsafe filesystem and URL characters
.replace(/\s+/g, "_"); // Replace spaces with underscores
// Truncate to 200 characters to avoid ENAMETOOLONG errors (filesystem limit is usually 255 bytes)

View File

@@ -1,12 +1,12 @@
{
"name": "frontend",
"version": "1.3.4",
"version": "1.3.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "frontend",
"version": "1.3.4",
"version": "1.3.5",
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",

View File

@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
"version": "1.3.5",
"version": "1.3.7",
"type": "module",
"scripts": {
"dev": "vite",

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "mytube",
"version": "1.3.4",
"version": "1.3.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mytube",
"version": "1.3.4",
"version": "1.3.5",
"license": "MIT",
"dependencies": {
"concurrently": "^8.2.2"

View File

@@ -1,6 +1,6 @@
{
"name": "mytube",
"version": "1.3.5",
"version": "1.3.7",
"description": "YouTube video downloader and player application",
"main": "index.js",
"scripts": {