유튜브 재생목록 다운로드 과정을 순차적으로 설명

  1. 프론트엔드 (DownloadButton 컴포넌트)에서 시작:
// components/DownloadButton.tsx
const handleDownload = () => {
    // URL 유효성 검사
    if (!isValidYouTubeUrl(url)) {
        setError("올바른 YouTube 재생목록 URL을 입력해주세요.");
        return;
    }

    // 설정 정보와 함께 IPC 메시지 전송
    if (window.electron) {
        window.electron.ipcRenderer.send('download-playlist', {
            url,
            settings: {
                preferredCodec: 'mp3',      // 설정에서 가져온 값
                preferredQuality: '192',     // 설정에서 가져온 값
                downloadPath: './downloads'  // 설정에서 가져온 값
            }
        });
    }
};
  1. Electron의 preload.js를 통한 IPC 통신 설정:
// preload.js
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electron', {
    ipcRenderer: {
        send: (channel, data) => ipcRenderer.send(channel, data),
        on: (channel, func) => ipcRenderer.on(channel, func),
        once: (channel, func) => ipcRenderer.once(channel, func),
    }
});
  1. Electron 메인 프로세스(main.js)에서 IPC 메시지 수신 및 처리:
// main.js
ipcMain.on('download-playlist', (event, { url, settings }) => {
    // pydownloader 실행 파일 경로 설정
    const pythonScriptPath = path.join(__dirname, 'dist', 'pydownloader');
    
    // pydownloader 프로세스 실행
    const downloadProcess = spawn(pythonScriptPath, [
        url,                        // 유튜브 URL
        settings.preferredCodec,    // 선호 코덱
        settings.preferredQuality,  // 음질
        settings.downloadPath       // 저장 경로
    ]);

    // 표준 출력 처리
    downloadProcess.stdout.on('data', (data) => {
        const message = data.toString();
        
        if (message.startsWith("progress:")) {
            // 진행률 업데이트
            const progress = parseFloat(message.replace("progress:", "").trim());
            event.sender.send('download-progress', { progress });
        } else if (message.startsWith("file:")) {
            // 파일 다운로드 완료
            const filePath = message.replace("file:", "").trim();
            event.sender.send('file-downloaded', { filePath });
        }
    });

    // 에러 출력 처리
    downloadProcess.stderr.on('data', (data) => {
        event.sender.send('download-error', {
            error: data.toString()
        });
    });

    // 프로세스 종료 처리
    downloadProcess.on('close', (code) => {
        event.sender.send('download-complete', { 
            success: code === 0 
        });
    });
});
  1. pydownloader 실행 파일 실행:
# downloader.py (pyinstaller로 변환됨)
def download_playlist(url, preferred_codec='mp3', preferred_quality='192', download_directory='./downloads'):
    try:
        # URL 유효성 검사
        if not is_valid_youtube_url(url):
            print(f"error:Invalid YouTube URL: {url}")
            return

        # yt-dlp 설정
        ydl_opts = {
            'format': 'bestaudio/best',
            'outtmpl': os.path.join(download_directory, '%(title)s.%(ext)s'),
            'progress_hooks': [progress_hook],
            'postprocessors': [{
                'key': 'FFmpegExtractAudio',
                'preferredcodec': preferred_codec,
                'preferredquality': preferred_quality,
            }],
        }

        # 다운로드 실행
        with YoutubeDL(ydl_opts) as ydl:
            ydl.download([url])

    except Exception as e:
        print(f"error:{str(e)}")
  1. 다운로드 진행 상황은 다시 프론트엔드로 전달:
// DownloadButton.tsx
useEffect(() => {
    if (window.electron) {
        // 진행률 업데이트 리스너
        window.electron.ipcRenderer.on('download-progress', (event, { progress }) => {
            setProgress(progress);
        });

        // 다운로드 완료 리스너
        window.electron.ipcRenderer.on('download-complete', (event, { success }) => {
            if (success) {
                setStatus('완료');
            }
        });

        // 에러 리스너
        window.electron.ipcRenderer.on('download-error', (event, { error }) => {
            setError(error);
        });
    }
}, []);

전체 프로세스 흐름:

  1. 사용자가 다운로드 버튼 클릭
  2. 프론트엔드에서 URL 검증 후 IPC 메시지 전송
  3. preload.js를 통해 메시지가 메인 프로세스로 전달
  4. 메인 프로세스에서 pydownloader 실행 파일 실행
  5. pydownloader가 yt-dlp를 사용하여 실제 다운로드 수행
  6. 다운로드 진행 상황이 표준 출력을 통해 메인 프로세스로 전달
  7. 메인 프로세스가 IPC를 통해 프론트엔드로 상태 업데이트
  8. 프론트엔드에서 진행 상황 표시