dootask/electron/electron-down.js

688 lines
24 KiB
JavaScript
Vendored
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const {BrowserWindow, screen, shell, ipcMain} = require('electron')
const fs = require('fs');
const path = require('path');
const loger = require("electron-log");
const {default: electronDl, download, CancelError} = require("@dootask/electron-dl");
const utils = require("./lib/utils");
const {DownloadManager, DownloadStore} = require("./lib/download-manager");
const downloadManager = new DownloadManager();
let downloadWindow = null,
downloadLanguageCode = 'zh',
downloadWaiting = false;
function initialize(onStarted = null) {
// 下载配置
electronDl({
showBadge: false,
showProgressBar: false,
onStarted: (item) => {
downloadManager.add(item);
downloadWaiting = false;
syncDownloadItems();
if (typeof onStarted === 'function') {
onStarted(item)
}
},
onCancel: (item) => {
downloadManager.refresh(item.getSavePath())
syncDownloadItems();
},
onInterrupted: (item) => {
downloadManager.refresh(item.getSavePath());
syncDownloadItems();
// 尝试更新下载项的错误信息
downloadManager.updateError(item, {
language: downloadLanguageCode,
}).then(success => {
if (success) {
syncDownloadItems();
}
});
},
onProgress: (item) => {
downloadManager.refresh(item.path);
syncDownloadItems();
},
onCompleted: (item) => {
downloadManager.refresh(item.path);
syncDownloadItems();
}
});
// IPC
ipcMain.handle('downloadManager', async (event, {action, path}) => {
switch (action) {
case "get": {
return {
items: downloadManager.get(),
waiting: downloadWaiting,
};
}
case "pause": {
downloadManager.pause(path);
syncDownloadItems();
return true;
}
case "resume": {
downloadManager.resume(path);
syncDownloadItems();
return true;
}
case "cancel": {
downloadManager.cancel(path);
syncDownloadItems();
return true;
}
case "remove": {
downloadManager.remove(path);
syncDownloadItems();
return true;
}
case "removeAll": {
downloadManager.removeAll();
syncDownloadItems();
return true;
}
case "openFile": {
if (!fs.existsSync(path)) {
throw new Error('file not found');
}
return shell.openPath(path);
}
case "showFolder": {
if (!fs.existsSync(path)) {
throw new Error('file not found');
}
shell.showItemInFolder(path);
return true;
}
}
});
}
async function createDownload(window_, url, options = {}) {
downloadWaiting = true;
syncDownloadItems();
try {
return await download(window_, url, options);
} catch (error) {
// electron-dl rejects with CancelError when a download is cancelled; treat it as expected.
const isCancelError = (typeof CancelError === 'function' && error instanceof CancelError)
|| error?.name === 'CancelError';
if (!isCancelError) {
throw error;
}
return null;
} finally {
downloadWaiting = false;
syncDownloadItems();
}
}
function syncDownloadItems() {
// 同步下载项到渲染进程
if (downloadWindow) {
downloadWindow.webContents.send('download-items', {
items: downloadManager.get(),
waiting: downloadWaiting,
});
}
}
function getLanguageData(code) {
const packs = {
zh: {
// 语言设置
locale: 'zh-CN',
title: '下载管理器',
// 界面文本
searchPlaceholder: '搜索文件名或链接...',
noItems: '暂无任务',
noSearchResult: '未找到匹配的结果',
// 操作按钮
refresh: '刷新',
removeAll: "清空历史",
copyLink: '复制链接',
resume: '继续',
pause: '暂停',
cancel: '取消',
remove: '删除',
showInFolder: '显示在文件夹',
// 状态文本
progressing: '下载中',
completed: '已完成',
cancelled: '已取消',
interrupted: '失败',
paused: '已暂停',
// 成功消息
copied: "已复制",
refreshSuccess: '刷新成功',
// 确认对话框
confirmCancel: '确定要取消此下载任务并删除记录吗?',
confirmRemove: '确定要从历史记录中删除此项吗?',
confirmRemoveAll: '确定要清空下载历史吗?',
// 错误消息
copyFailed: '复制失败: ',
pauseFailed: '暂停失败: ',
resumeFailed: '继续失败: ',
removeFailed: '删除失败: ',
removeAllFailed: '清空失败: ',
openFailed: '打开文件失败: ',
showFailed: '显示文件失败: ',
},
'zh-CHT': {
locale: 'zh-TW',
title: '下載管理器',
// 界面文本
searchPlaceholder: '搜尋檔案名稱或連結...',
noItems: '暫無任務',
noSearchResult: '未找到匹配的結果',
// 操作按钮
refresh: '重新整理',
removeAll: "清空歷史",
copyLink: '複製連結',
resume: '繼續',
pause: '暫停',
cancel: '取消',
remove: '刪除',
showInFolder: '顯示在資料夾',
// 状态文本
progressing: '下載中',
completed: '已完成',
cancelled: '已取消',
interrupted: '失敗',
paused: '已暫停',
// 成功消息
copied: "已複製",
refreshSuccess: '重新整理成功',
// 确认对话框
confirmCancel: '確定要取消此下載任務並刪除記錄嗎?',
confirmRemove: '確定要從歷史記錄中刪除此項嗎?',
confirmRemoveAll: '確定要清空下載歷史嗎?',
// 错误消息
copyFailed: '複製失敗: ',
pauseFailed: '暫停失敗: ',
resumeFailed: '繼續失敗: ',
removeFailed: '刪除失敗: ',
removeAllFailed: '清空失敗: ',
openFailed: '開啟檔案失敗: ',
showFailed: '顯示檔案失敗: ',
},
en: {
locale: 'en-US',
title: 'Download Manager',
// 界面文本
searchPlaceholder: 'Search filename or link...',
noItems: 'No tasks',
noSearchResult: 'No matching results found',
// 操作按钮
refresh: 'Refresh',
removeAll: "Clear History",
copyLink: 'Copy Link',
resume: 'Resume',
pause: 'Pause',
cancel: 'Cancel',
remove: 'Remove',
showInFolder: 'Show in Folder',
// 状态文本
progressing: 'Downloading',
completed: 'Completed',
cancelled: 'Cancelled',
interrupted: 'Failed',
paused: 'Paused',
// 成功消息
copied: "Copied",
refreshSuccess: 'Refresh successful',
// 确认对话框
confirmCancel: 'Are you sure you want to cancel this download task and delete the record?',
confirmRemove: 'Are you sure you want to remove this item from history?',
confirmRemoveAll: 'Are you sure you want to clear download history?',
// 错误消息
copyFailed: 'Copy failed: ',
pauseFailed: 'Pause failed: ',
resumeFailed: 'Resume failed: ',
removeFailed: 'Remove failed: ',
removeAllFailed: 'Clear failed: ',
openFailed: 'Open file failed: ',
showFailed: 'Show file failed: ',
},
ko: {
locale: 'ko-KR',
title: '다운로드 관리자',
// 界面文本
searchPlaceholder: '파일명 또는 링크 검색...',
noItems: '작업 없음',
noSearchResult: '일치하는 결과를 찾을 수 없습니다',
// 操作按钮
refresh: '새로고침',
removeAll: "기록 지우기",
copyLink: '링크 복사',
resume: '계속',
pause: '일시정지',
cancel: '취소',
remove: '삭제',
showInFolder: '폴더에서 보기',
// 状态文本
progressing: '다운로드 중',
completed: '완료됨',
cancelled: '취소됨',
interrupted: '실패',
paused: '일시정지됨',
// 成功消息
copied: "복사됨",
refreshSuccess: '새로고침 성공',
// 确认对话框
confirmCancel: '이 다운로드 작업을 취소하고 기록을 삭제하시겠습니까?',
confirmRemove: '기록에서 이 항목을 삭제하시겠습니까?',
confirmRemoveAll: '다운로드 기록을 지우시겠습니까?',
// 错误消息
copyFailed: '복사 실패: ',
pauseFailed: '일시정지 실패: ',
resumeFailed: '계속 실패: ',
removeFailed: '삭제 실패: ',
removeAllFailed: '지우기 실패: ',
openFailed: '파일 열기 실패: ',
showFailed: '파일 표시 실패: ',
},
ja: {
locale: 'ja-JP',
title: 'ダウンロードマネージャー',
// 界面文本
searchPlaceholder: 'ファイル名またはリンクを検索...',
noItems: 'タスクがありません',
noSearchResult: '一致する結果が見つかりません',
// 操作按钮
refresh: '更新',
removeAll: "履歴をクリア",
copyLink: 'リンクをコピー',
resume: '再開',
pause: '一時停止',
cancel: 'キャンセル',
remove: '削除',
showInFolder: 'フォルダで表示',
// 状态文本
progressing: 'ダウンロード中',
completed: '完了',
cancelled: 'キャンセル済み',
interrupted: '失敗',
paused: '一時停止中',
// 成功消息
copied: "コピーしました",
refreshSuccess: '更新が完了しました',
// 确认对话框
confirmCancel: 'このダウンロードタスクをキャンセルして記録を削除しますか?',
confirmRemove: '履歴からこの項目を削除しますか?',
confirmRemoveAll: 'ダウンロード履歴をクリアしますか?',
// 错误消息
copyFailed: 'コピーに失敗しました: ',
pauseFailed: '一時停止に失敗しました: ',
resumeFailed: '再開に失敗しました: ',
removeFailed: '削除に失敗しました: ',
removeAllFailed: 'クリアに失敗しました: ',
openFailed: 'ファイルを開けませんでした: ',
showFailed: 'ファイルの表示に失敗しました: ',
},
de: {
locale: 'de-DE',
title: 'Download-Manager',
// 界面文本
searchPlaceholder: 'Dateiname oder Link suchen...',
noItems: 'Keine Aufgaben',
noSearchResult: 'Keine übereinstimmenden Ergebnisse gefunden',
// 操作按钮
refresh: 'Aktualisieren',
removeAll: "Verlauf löschen",
copyLink: 'Link kopieren',
resume: 'Fortsetzen',
pause: 'Pause',
cancel: 'Abbrechen',
remove: 'Entfernen',
showInFolder: 'Im Ordner anzeigen',
// 状态文本
progressing: 'Wird heruntergeladen',
completed: 'Abgeschlossen',
cancelled: 'Abgebrochen',
interrupted: 'Fehlgeschlagen',
paused: 'Pausiert',
// 成功消息
copied: "Kopiert",
refreshSuccess: 'Erfolgreich aktualisiert',
// 确认对话框
confirmCancel: 'Sind Sie sicher, dass Sie diese Download-Aufgabe abbrechen und den Eintrag löschen möchten?',
confirmRemove: 'Sind Sie sicher, dass Sie diesen Eintrag aus dem Verlauf entfernen möchten?',
confirmRemoveAll: 'Sind Sie sicher, dass Sie den Download-Verlauf löschen möchten?',
// 错误消息
copyFailed: 'Kopieren fehlgeschlagen: ',
pauseFailed: 'Pause fehlgeschlagen: ',
resumeFailed: 'Fortsetzen fehlgeschlagen: ',
removeFailed: 'Entfernen fehlgeschlagen: ',
removeAllFailed: 'Löschen fehlgeschlagen: ',
openFailed: 'Datei öffnen fehlgeschlagen: ',
showFailed: 'Datei anzeigen fehlgeschlagen: ',
},
fr: {
locale: 'fr-FR',
title: 'Gestionnaire de téléchargements',
// 界面文本
searchPlaceholder: 'Rechercher nom de fichier ou lien...',
noItems: 'Aucune tâche',
noSearchResult: 'Aucun résultat correspondant trouvé',
// 操作按钮
refresh: 'Actualiser',
removeAll: "Effacer l'historique",
copyLink: 'Copier le lien',
resume: 'Reprendre',
pause: 'Pause',
cancel: 'Annuler',
remove: 'Supprimer',
showInFolder: 'Afficher dans le dossier',
// 状态文本
progressing: 'Téléchargement en cours',
completed: 'Terminé',
cancelled: 'Annulé',
interrupted: 'Échoué',
paused: 'En pause',
// 成功消息
copied: "Copié",
refreshSuccess: 'Actualisation réussie',
// 确认对话框
confirmCancel: 'Êtes-vous sûr de vouloir annuler cette tâche de téléchargement et supprimer l\'enregistrement ?',
confirmRemove: 'Êtes-vous sûr de vouloir supprimer cet élément de l\'historique ?',
confirmRemoveAll: 'Êtes-vous sûr de vouloir effacer l\'historique des téléchargements ?',
// 错误消息
copyFailed: 'Échec de la copie : ',
pauseFailed: 'Échec de la pause : ',
resumeFailed: 'Échec de la reprise : ',
removeFailed: 'Échec de la suppression : ',
removeAllFailed: 'Échec de l\'effacement : ',
openFailed: 'Échec de l\'ouverture du fichier : ',
showFailed: 'Échec de l\'affichage du fichier : ',
},
id: {
locale: 'id-ID',
title: 'Manajer Unduhan',
// 界面文本
searchPlaceholder: 'Cari nama file atau tautan...',
noItems: 'Tidak ada tugas',
noSearchResult: 'Tidak ada hasil yang cocok ditemukan',
// 操作按钮
refresh: 'Segarkan',
removeAll: "Hapus Riwayat",
copyLink: 'Salin Tautan',
resume: 'Lanjutkan',
pause: 'Jeda',
cancel: 'Batal',
remove: 'Hapus',
showInFolder: 'Tampilkan di Folder',
// 状态文本
progressing: 'Mengunduh',
completed: 'Selesai',
cancelled: 'Dibatalkan',
interrupted: 'Gagal',
paused: 'Dijeda',
// 成功消息
copied: "Disalin",
refreshSuccess: 'Berhasil disegarkan',
// 确认对话框
confirmCancel: 'Apakah Anda yakin ingin membatalkan tugas unduhan ini dan menghapus catatan?',
confirmRemove: 'Apakah Anda yakin ingin menghapus item ini dari riwayat?',
confirmRemoveAll: 'Apakah Anda yakin ingin menghapus riwayat unduhan?',
// 错误消息
copyFailed: 'Gagal menyalin: ',
pauseFailed: 'Gagal menjeda: ',
resumeFailed: 'Gagal melanjutkan: ',
removeFailed: 'Gagal menghapus: ',
removeAllFailed: 'Gagal menghapus: ',
openFailed: 'Gagal membuka file: ',
showFailed: 'Gagal menampilkan file: ',
},
ru: {
locale: 'ru-RU',
title: 'Менеджер загрузок',
// 界面文本
searchPlaceholder: 'Поиск имени файла или ссылки...',
noItems: 'Нет задач',
noSearchResult: 'Совпадающих результатов не найдено',
// 操作按钮
refresh: 'Обновить',
removeAll: "Очистить историю",
copyLink: 'Копировать ссылку',
resume: 'Возобновить',
pause: 'Пауза',
cancel: 'Отмена',
remove: 'Удалить',
showInFolder: 'Показать в папке',
// 状态文本
progressing: 'Загружается',
completed: 'Завершено',
cancelled: 'Отменено',
interrupted: 'Ошибка',
paused: 'На паузе',
// 成功消息
copied: "Скопировано",
refreshSuccess: 'Успешно обновлено',
// 确认对话框
confirmCancel: 'Вы уверены, что хотите отменить эту задачу загрузки и удалить запись?',
confirmRemove: 'Вы уверены, что хотите удалить этот элемент из истории?',
confirmRemoveAll: 'Вы уверены, что хотите очистить историю загрузок?',
// 错误消息
copyFailed: 'Ошибка копирования: ',
pauseFailed: 'Ошибка паузы: ',
resumeFailed: 'Ошибка возобновления: ',
removeFailed: 'Ошибка удаления: ',
removeAllFailed: 'Ошибка очистки: ',
openFailed: 'Ошибка открытия файла: ',
showFailed: 'Ошибка отображения файла: ',
}
};
downloadLanguageCode = code;
return packs[code] || packs.zh;
}
async function open(language = 'zh', theme = 'light') {
// 获取语言包
const finalLanguage = getLanguageData(language);
// 如果窗口已存在,直接显示
if (downloadWindow) {
// 更新窗口数据
await updateWindow(language, theme)
// 显示窗口并聚焦
downloadWindow.show();
downloadWindow.focus();
return;
}
// 窗口默认参数
const downloadWindowOptions = {
width: 700,
height: 480,
minWidth: 500,
minHeight: 350,
center: true,
show: false,
autoHideMenuBar: true,
title: finalLanguage.title,
backgroundColor: utils.getDefaultBackgroundColor(),
webPreferences: {
preload: path.join(__dirname, 'electron-preload.js'),
webSecurity: true,
nodeIntegration: true,
contextIsolation: true,
}
}
// 恢复窗口位置
const downloadWindowBounds = DownloadStore.get('downloadWindowBounds', {});
if (
downloadWindowBounds.width !== undefined &&
downloadWindowBounds.height !== undefined &&
downloadWindowBounds.x !== undefined &&
downloadWindowBounds.y !== undefined
) {
// 获取所有显示器的可用区域
const displays = screen.getAllDisplays();
// 检查窗口是否在任意一个屏幕内
let isInScreen = false;
for (const display of displays) {
const area = display.workArea;
if (
downloadWindowBounds.x + downloadWindowBounds.width > area.x &&
downloadWindowBounds.x < area.x + area.width &&
downloadWindowBounds.y + downloadWindowBounds.height > area.y &&
downloadWindowBounds.y < area.y + area.height
) {
isInScreen = true;
break;
}
}
// 如果超出所有屏幕,则移动到主屏幕可见区域
if (!isInScreen) {
const primaryArea = screen.getPrimaryDisplay().workArea;
downloadWindowBounds.x = primaryArea.x + 50;
downloadWindowBounds.y = primaryArea.y + 50;
// 防止窗口太大超出屏幕
downloadWindowBounds.width = Math.min(downloadWindowBounds.width, primaryArea.width - 100);
downloadWindowBounds.height = Math.min(downloadWindowBounds.height, primaryArea.height - 100);
}
downloadWindowOptions.center = false;
downloadWindowOptions.width = downloadWindowBounds.width;
downloadWindowOptions.height = downloadWindowBounds.height;
downloadWindowOptions.x = downloadWindowBounds.x;
downloadWindowOptions.y = downloadWindowBounds.y;
}
// 创建窗口
downloadWindow = new BrowserWindow(downloadWindowOptions);
// 禁止修改窗口标题
downloadWindow.on('page-title-updated', (event) => {
event.preventDefault()
})
// 监听窗口关闭保存窗口位置
downloadWindow.on('close', () => {
const bounds = downloadWindow.getBounds();
DownloadStore.set('downloadWindowBounds', bounds);
});
// 监听窗口关闭事件
downloadWindow.on('closed', () => {
downloadWindow = null;
});
// 加载下载管理器页面
const htmlPath = path.join(__dirname, 'render', 'download', 'index.html');
const themeParam = (theme === 'dark' ? 'dark' : 'light');
await downloadWindow.loadFile(htmlPath, {query: {theme: themeParam}});
// 将语言包发送到渲染进程
downloadWindow.webContents.once('dom-ready', () => {
updateWindow(language, theme)
});
// 显示窗口
downloadWindow.show();
}
function close() {
if (downloadWindow) {
downloadWindow.close();
downloadWindow = null;
}
}
function destroy() {
if (downloadWindow) {
downloadWindow.destroy();
downloadWindow = null;
}
}
async function updateWindow(language, theme) {
if (downloadWindow) {
try {
const finalLanguage = getLanguageData(language);
downloadWindow.setTitle(finalLanguage.title);
downloadWindow.webContents.send('download-theme', theme);
downloadWindow.webContents.send('download-language', finalLanguage);
syncDownloadItems()
} catch (error) {
loger.error(error);
}
}
}
module.exports = {
initialize,
createDownload,
open,
close,
destroy,
updateWindow
}