vidrip/backend/src/services/webdav.ts

266 lines
6.6 KiB
TypeScript

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<void> {
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<void> {
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<Readable> {
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<boolean> {
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<FileStat | null> {
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;
}