import { createClient, WebDAVClient, FileStat } from 'webdav'; import { createReadStream, createWriteStream, statSync } from 'fs'; import { Readable } from 'stream'; import { configOperations } from '../db/database'; let webdavClient: WebDAVClient | null = null; export interface WebDAVConfig { url: string; username: string; password: string; path: string; } /** * Get WebDAV configuration from database */ function getWebDAVConfig(): WebDAVConfig | null { const config = configOperations.getAll(); if (config.webdavEnabled !== 'true') { return null; } if (!config.webdavUrl || !config.webdavUsername || !config.webdavPassword) { return null; } return { url: config.webdavUrl, username: config.webdavUsername, password: config.webdavPassword, path: config.webdavPath || '/vidrip/' }; } /** * Create authenticated WebDAV client from config */ export function createWebDAVClient(config?: WebDAVConfig): WebDAVClient | null { const webdavConfig = config || getWebDAVConfig(); if (!webdavConfig) { return null; } try { return createClient(webdavConfig.url, { username: webdavConfig.username, password: webdavConfig.password }); } catch (error) { console.error('Failed to create WebDAV client:', error); return null; } } /** * Test WebDAV connection and permissions */ export async function testConnection(config: WebDAVConfig): Promise<{ success: boolean; message: string }> { try { const client = createWebDAVClient(config); if (!client) { return { success: false, message: 'Failed to create WebDAV client' }; } // Ensure path ends with / const basePath = config.path.endsWith('/') ? config.path : `${config.path}/`; // Test connection by checking if base path exists const exists = await client.exists(basePath); if (!exists) { // Try to create the directory try { await client.createDirectory(basePath, { recursive: true }); return { success: true, message: 'Connected successfully and created directory' }; } catch (createError) { return { success: false, message: `Directory doesn't exist and cannot be created: ${createError}` }; } } // Try to get directory contents to verify read permissions await client.getDirectoryContents(basePath); return { success: true, message: 'Connected successfully' }; } catch (error) { console.error('WebDAV connection test failed:', error); return { success: false, message: error instanceof Error ? error.message : 'Connection failed' }; } } /** * Get full WebDAV path for a video */ function getVideoPath(videoId: string): string { const config = getWebDAVConfig(); if (!config) { throw new Error('WebDAV not configured'); } const basePath = config.path.endsWith('/') ? config.path : `${config.path}/`; return `${basePath}${videoId}.mp4`; } /** * Upload video file to WebDAV */ export async function uploadVideo(localPath: string, videoId: string): Promise { const config = getWebDAVConfig(); if (!config) { throw new Error('WebDAV not configured'); } const client = createWebDAVClient(config); if (!client) { throw new Error('Failed to create WebDAV client'); } try { const remotePath = getVideoPath(videoId); const basePath = config.path.endsWith('/') ? config.path : `${config.path}/`; // Ensure directory exists const dirExists = await client.exists(basePath); if (!dirExists) { await client.createDirectory(basePath, { recursive: true }); } // Upload file const fileStream = createReadStream(localPath); await client.putFileContents(remotePath, fileStream, { overwrite: true }); console.log(`Successfully uploaded ${videoId}.mp4 to WebDAV`); } catch (error) { console.error('Failed to upload to WebDAV:', error); throw new Error(`WebDAV upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Delete video from WebDAV server */ export async function deleteVideo(videoId: string): Promise { const config = getWebDAVConfig(); if (!config) { // If WebDAV not configured, silently return (nothing to delete) return; } const client = createWebDAVClient(config); if (!client) { throw new Error('Failed to create WebDAV client'); } try { const remotePath = getVideoPath(videoId); const exists = await client.exists(remotePath); if (exists) { await client.deleteFile(remotePath); console.log(`Successfully deleted ${videoId}.mp4 from WebDAV`); } } catch (error) { console.error('Failed to delete from WebDAV:', error); throw new Error(`WebDAV delete failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Get readable stream from WebDAV for proxying */ export async function streamVideo(videoId: string): Promise { const config = getWebDAVConfig(); if (!config) { throw new Error('WebDAV not configured'); } const client = createWebDAVClient(config); if (!client) { throw new Error('Failed to create WebDAV client'); } try { const remotePath = getVideoPath(videoId); const stream = client.createReadStream(remotePath); return stream as Readable; } catch (error) { console.error('Failed to stream from WebDAV:', error); throw new Error(`WebDAV stream failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Check if video exists on WebDAV */ export async function getVideoExists(videoId: string): Promise { const config = getWebDAVConfig(); if (!config) { return false; } const client = createWebDAVClient(config); if (!client) { return false; } try { const remotePath = getVideoPath(videoId); return await client.exists(remotePath); } catch (error) { console.error('Failed to check video existence on WebDAV:', error); return false; } } /** * Get video file stats from WebDAV */ export async function getVideoStats(videoId: string): Promise { const config = getWebDAVConfig(); if (!config) { return null; } const client = createWebDAVClient(config); if (!client) { return null; } try { const remotePath = getVideoPath(videoId); const stat = await client.stat(remotePath) as FileStat; return stat; } catch (error) { console.error('Failed to get video stats from WebDAV:', error); return null; } } /** * Check if WebDAV is enabled and configured */ export function isWebDAVEnabled(): boolean { const config = getWebDAVConfig(); return config !== null; }