From e16d95eacd295514e6ec46960bbf59a1d8305129 Mon Sep 17 00:00:00 2001
From: Ryan Whytsell
Date: Tue, 21 Oct 2025 19:54:57 -0400
Subject: [PATCH] Use downloads path rather than direct webdav
---
backend/src/db/database.ts | 3 ++-
backend/src/server.ts | 7 ++++---
backend/src/services/ytdlp.ts | 22 +++++++++++++++-------
frontend/src/pages/SettingsPage.tsx | 16 ++++++++++++++++
frontend/src/types/index.ts | 1 +
5 files changed, 38 insertions(+), 11 deletions(-)
diff --git a/backend/src/db/database.ts b/backend/src/db/database.ts
index be52f89..c77522d 100644
--- a/backend/src/db/database.ts
+++ b/backend/src/db/database.ts
@@ -89,7 +89,8 @@ export function initDatabase() {
intervalMinutes: '180', // 180 minutes = 3 hours
varianceMinutes: '30',
maxConcurrentDownloads: '1',
- enabled: 'true'
+ enabled: 'true',
+ downloadsPath: path.join(__dirname, '../../downloads')
};
const insertConfig = db.prepare(
diff --git a/backend/src/server.ts b/backend/src/server.ts
index 1d0398f..ce05767 100644
--- a/backend/src/server.ts
+++ b/backend/src/server.ts
@@ -1,7 +1,7 @@
import express from 'express';
import cors from 'cors';
import path from 'path';
-import { initDatabase } from './db/database';
+import { initDatabase, configOperations } from './db/database';
import { startScheduler } from './services/scheduler';
import channelsRouter from './routes/channels';
import playlistsRouter from './routes/playlists';
@@ -24,8 +24,9 @@ app.use('/api/playlists', playlistsRouter);
app.use('/api/videos', videosRouter);
app.use('/api/config', configRouter);
-// Serve downloaded videos
-app.use('/downloads', express.static(path.join(__dirname, '../downloads')));
+// Serve downloaded videos from configured path
+const downloadsPath = configOperations.get('downloadsPath') || path.join(__dirname, '../downloads');
+app.use('/downloads', express.static(downloadsPath));
// Health check
app.get('/api/health', (req, res) => {
diff --git a/backend/src/services/ytdlp.ts b/backend/src/services/ytdlp.ts
index e14f485..ce8dde3 100644
--- a/backend/src/services/ytdlp.ts
+++ b/backend/src/services/ytdlp.ts
@@ -1,12 +1,19 @@
import { spawn } from 'child_process';
import path from 'path';
import fs from 'fs';
+import { configOperations } from '../db/database';
-const DOWNLOADS_DIR = path.join(__dirname, '../../downloads');
+// Get downloads directory from config
+function getDownloadsDir(): string {
+ const downloadsPath = configOperations.get('downloadsPath');
+ const dir = downloadsPath || path.join(__dirname, '../../downloads');
-// Ensure downloads directory exists
-if (!fs.existsSync(DOWNLOADS_DIR)) {
- fs.mkdirSync(DOWNLOADS_DIR, { recursive: true });
+ // Ensure downloads directory exists
+ if (!fs.existsSync(dir)) {
+ fs.mkdirSync(dir, { recursive: true });
+ }
+
+ return dir;
}
// Path to cookies file (optional, for additional authentication)
@@ -296,7 +303,8 @@ export async function downloadVideo(
options: DownloadOptions = {}
): Promise<{ filePath: string; fileSize: number }> {
return new Promise((resolve, reject) => {
- const outputTemplate = path.join(DOWNLOADS_DIR, `${videoId}.%(ext)s`);
+ const downloadsDir = getDownloadsDir();
+ const outputTemplate = path.join(downloadsDir, `${videoId}.%(ext)s`);
const args = [
...getCommonArgs(),
@@ -360,10 +368,10 @@ export async function downloadVideo(
// Find the downloaded file
if (!outputPath) {
- const files = fs.readdirSync(DOWNLOADS_DIR);
+ const files = fs.readdirSync(downloadsDir);
const videoFile = files.find(f => f.startsWith(videoId) && f.endsWith('.mp4'));
if (videoFile) {
- outputPath = path.join(DOWNLOADS_DIR, videoFile);
+ outputPath = path.join(downloadsDir, videoFile);
}
}
diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx
index 3f47cf7..519c0cd 100644
--- a/frontend/src/pages/SettingsPage.tsx
+++ b/frontend/src/pages/SettingsPage.tsx
@@ -224,6 +224,22 @@ function SettingsPage() {
Number of videos to download simultaneously (recommended: 1)
+
+
+
+
handleChange('downloadsPath', e.target.value)}
+ className="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm focus:border-blue-500 focus:ring-blue-500"
+ placeholder="/var/www/vidrip/downloads"
+ />
+
+ Absolute path where downloaded videos will be saved. Can point to a mounted WebDAV directory.
+
+
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
index ae9e1b5..79897ab 100644
--- a/frontend/src/types/index.ts
+++ b/frontend/src/types/index.ts
@@ -41,6 +41,7 @@ export interface Config {
varianceMinutes: string;
maxConcurrentDownloads: string;
enabled: string;
+ downloadsPath: string;
}
export interface SchedulerStatus {