const { app, BrowserWindow, ipcMain, dialog, session } = require('electron'); require('@electron/remote/main').initialize(); const path = require('path'); const fs = require('fs'); const https = require('https'); let store; let mainWindow; let fetch; // Initialize store and fetch using dynamic import (async () => { const [Store, { default: nodeFetch }] = await Promise.all([ import('electron-store'), import('node-fetch') ]); store = new Store.default(); fetch = nodeFetch; console.log('Store and fetch initialized'); })(); function createWindow() { console.log('Creating main window...'); mainWindow = new BrowserWindow({ width: 1200, height: 800, webPreferences: { nodeIntegration: true, contextIsolation: false, webviewTag: true, enableRemoteModule: true } }); // Enable remote module for this window require('@electron/remote/main').enable(mainWindow.webContents); // Set up network request monitoring console.log('Setting up network request monitoring...'); session.defaultSession.webRequest.onBeforeRequest( { urls: ['*://*.onedrive.com/*', '*://*.1drv.com/*', '*://*.microsoftpersonalcontent.com/*'] }, (details, callback) => { console.log('Intercepted request:', details.url); // Check if this is a video request (typically .mov for Live Photos) if (details.url.toLowerCase().includes('.mov') || details.url.toLowerCase().includes('mp4')) { console.log('✓ Detected video URL:', details.url); if (store) { const destFolder = store.get('destFolder', ''); if (destFolder) { // Extract item ID from URL const urlObj = new URL(details.url); const pathParts = urlObj.pathname.split('/'); const itemIndex = pathParts.findIndex(part => part === 'items'); const itemId = itemIndex !== -1 ? pathParts[itemIndex + 1] : null; // Create filename with item ID and extension const extension = details.url.toLowerCase().includes('.mov') ? '.mov' : '.mp4'; const filename = itemId ? `${itemId}${extension}` : `video_${Date.now()}${extension}`; console.log('Downloading video as:', filename); console.log('To folder:', destFolder); console.log("cookies:", session.defaultSession.cookies); // Print all cookies session.defaultSession.cookies.get({}) .then((cookies) => { console.log("cookies:", cookies); }) .catch((error) => { console.error('Failed to load cookies:', error); }); downloadVideo(details.url, path.join(destFolder, filename)); } else { console.warn('⚠ No destination folder configured'); } } else { console.error('⚠ Store not initialized'); } } callback({ cancel: false }); } ); mainWindow.loadFile(path.join(__dirname, 'renderer/index.html')); mainWindow.setTitle('OneDrive Photos'); console.log('Main window created and loaded'); } function downloadVideo(url, filepath) { console.log('Starting video download:', url); // Get cookies from all relevant domains Promise.all([ session.defaultSession.cookies.get({domain: '.onedrive.com'}), session.defaultSession.cookies.get({domain: '.1drv.com'}), session.defaultSession.cookies.get({domain: '.microsoftpersonalcontent.com'}) ]) .then((cookieArrays) => { // Flatten and combine all cookies const allCookies = [].concat(...cookieArrays); console.log('All cookies found:', allCookies); const cookieHeader = allCookies .map(cookie => `${cookie.name}=${cookie.value}`) .join('; '); const options = { headers: { 'Cookie': cookieHeader, 'Accept': '*/*', 'User-Agent': 'Mozilla/5.0', 'Authorization': `Bearer ${allCookies.find(c => c.name === 'AccessToken-OneDrive.ReadWrite')?.value || ''}`, 'Origin': 'https://photos.onedrive.com', 'Referer': 'https://photos.onedrive.com/' } }; console.log('Request headers:', options.headers); // Rest of the download code... const file = fs.createWriteStream(filepath); https.get(url, options, (response) => { console.log('Download started, status:', response.statusCode); console.log('Response headers:', response.headers); if (response.statusCode === 401 || response.statusCode === 403) { console.error('Authentication failed:', response.statusCode); mainWindow.webContents.send('video-save-error', 'Authentication failed'); return; } response.pipe(file); file.on('finish', () => { file.close(); console.log('✓ Video saved successfully:', filepath); mainWindow.webContents.send('video-saved', { filename: path.basename(filepath), path: filepath }); }); }).on('error', (err) => { console.error('⚠ Download error:', err); fs.unlink(filepath, () => { console.log('Cleaned up failed download file'); }); mainWindow.webContents.send('video-save-error', err.message); }); }) .catch((error) => { console.error('Failed to load cookies for download:', error); mainWindow.webContents.send('video-save-error', 'Failed to load authentication cookies'); }); } app.whenReady().then(createWindow); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); // IPC Handlers with debug logs ipcMain.on('toggle-config', () => { console.log('Toggle config panel requested'); mainWindow.webContents.send('toggle-config'); }); ipcMain.on('set-config', (event, config) => { console.log('Saving configuration:', config); if (store) { store.set('destFolder', config.destFolder); store.set('onedriveSource', config.onedriveSource); console.log('✓ Configuration saved successfully'); } else { console.error('⚠ Store not initialized, cannot save config'); } }); ipcMain.on('load-config', (event) => { console.log('Loading configuration...'); if (store) { const config = { destFolder: store.get('destFolder', ''), onedriveSource: store.get('onedriveSource', '') }; console.log('Configuration loaded:', config); event.reply('config-loaded', config); } else { console.error('⚠ Store not initialized, cannot load config'); } }); ipcMain.on('select-folder', (event) => { dialog.showOpenDialog({ properties: ['openDirectory'] }).then(result => { if (!result.canceled) { event.reply('folder-selected', result.filePaths[0]); } }).catch(err => { console.log(err); }); }); ipcMain.on('start-sync', async (event, { destFolder, onedriveSource, items }) => { console.log('Starting sync with config:', { destFolder, onedriveSource }); console.log(`Processing ${items.length} items...`); try { // Filter for video files (Live Photos) const videoItems = items.filter(item => item.name.toLowerCase().endsWith('.mov') || item.name.toLowerCase().endsWith('.mp4') ); console.log(`Found ${videoItems.length} video items`); // Process each video item for (const item of videoItems) { if (item['@microsoft.graph.downloadUrl']) { const filename = `${item.id}${path.extname(item.name)}`; const filepath = path.join(destFolder, filename); console.log(`Downloading: ${item.name} as ${filename}`); await downloadVideo(item['@microsoft.graph.downloadUrl'], filepath); } else { console.warn(`No download URL for item: ${item.name}`); } } event.reply('sync-complete', { totalItems: items.length, videoItems: videoItems.length }); } catch (error) { console.error('Sync error:', error); event.reply('sync-error', error.message); } });