Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40536d1963 | ||
|
|
5341bf842b | ||
|
|
26184ba3c5 | ||
|
|
1e5884d454 | ||
|
|
04790fdddf |
@@ -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)
|
||||
|
||||
|
||||
14
README.md
14
README.md
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
1
backend/bgutil-ytdlp-pot-provider
Submodule
1
backend/bgutil-ytdlp-pot-provider
Submodule
Submodule backend/bgutil-ytdlp-pot-provider added at 9c3cc1a21d
4
backend/package-lock.json
generated
4
backend/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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": "",
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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)
|
||||
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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
4
package-lock.json
generated
@@ -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"
|
||||
|
||||
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user