Files
beStream/src/hooks/useDownloadManager.ts

174 lines
4.9 KiB
TypeScript

import { useCallback, useEffect, useRef } from 'react';
import { useDownloadStore } from '../stores/downloadStore';
import { downloadService, type DownloadSession } from '../services/download/downloadService';
import type { Movie, Torrent } from '../types';
import { logger } from '../utils/logger';
/**
* Hook to manage downloads with actual torrent downloading
* This combines the download store state with the download service
*/
export function useDownloadManager() {
const {
items,
addDownload: addToStore,
updateDownload,
removeDownload: removeFromStore,
pauseDownload: pauseInStore,
resumeDownload: resumeInStore,
getDownload,
getDownloadByMovieId,
clearCompleted,
} = useDownloadStore();
// Track active download sessions
const activeSessionsRef = useRef<Map<string, string>>(new Map());
/**
* Start a new download
*/
const startDownload = useCallback(async (movie: Movie, torrent: Torrent): Promise<string> => {
// Add to store first to get the ID
const id = addToStore(movie, torrent);
try {
// Update status to downloading
updateDownload(id, { status: 'downloading' });
// Start the actual download
const result = await downloadService.startDownload(
id,
movie,
torrent,
(data: DownloadSession) => {
// Update progress in store
updateDownload(id, {
progress: data.progress,
downloadSpeed: data.downloadSpeed,
uploadSpeed: data.uploadSpeed,
peers: data.peers,
status: data.status === 'ready'
? 'completed'
: data.status === 'error'
? 'error'
: 'downloading',
});
// Log progress periodically
if (Math.floor(data.progress * 100) % 10 === 0) {
logger.debug('Download progress', {
id,
progress: `${(data.progress * 100).toFixed(1)}%`,
speed: `${(data.downloadSpeed / 1024 / 1024).toFixed(2)} MB/s`
});
}
}
);
// Store the session ID
activeSessionsRef.current.set(id, result.sessionId);
logger.info('Download started successfully', { id, movie: movie.title });
return id;
} catch (error) {
// Update status to error
updateDownload(id, {
status: 'error',
});
logger.error('Failed to start download', error);
throw error;
}
}, [addToStore, updateDownload]);
/**
* Stop and remove a download
*/
const removeDownload = useCallback(async (id: string): Promise<void> => {
try {
// Stop the download on the server
await downloadService.stopDownload(id);
activeSessionsRef.current.delete(id);
} catch (error) {
logger.error('Failed to stop download', error);
}
// Remove from store
removeFromStore(id);
}, [removeFromStore]);
/**
* Pause a download
*/
const pauseDownload = useCallback(async (id: string): Promise<void> => {
// For now, pausing just updates the UI state
// A full implementation would need server support for pausing torrents
pauseInStore(id);
logger.info('Download paused', { id });
}, [pauseInStore]);
/**
* Resume a download
*/
const resumeDownload = useCallback(async (id: string): Promise<void> => {
const download = getDownload(id);
if (!download) return;
// If the download was never started or needs to be restarted
if (!activeSessionsRef.current.has(id)) {
try {
resumeInStore(id);
// Restart the download
await downloadService.startDownload(
id,
download.movie,
download.torrent,
(data: DownloadSession) => {
updateDownload(id, {
progress: data.progress,
downloadSpeed: data.downloadSpeed,
uploadSpeed: data.uploadSpeed,
peers: data.peers,
status: data.status === 'ready'
? 'completed'
: data.status === 'error'
? 'error'
: 'downloading',
});
}
);
} catch (error) {
updateDownload(id, { status: 'error' });
logger.error('Failed to resume download', error);
}
} else {
resumeInStore(id);
}
}, [getDownload, resumeInStore, updateDownload]);
/**
* Cleanup on unmount
*/
useEffect(() => {
return () => {
// Don't disconnect on unmount - downloads should continue in background
// downloadService.disconnect();
};
}, []);
return {
downloads: items,
startDownload,
removeDownload,
pauseDownload,
resumeDownload,
getDownload,
getDownloadByMovieId,
clearCompleted,
activeCount: items.filter(d => d.status === 'downloading').length,
completedCount: items.filter(d => d.status === 'completed').length,
};
}
export default useDownloadManager;