diff --git a/.github/workflows/ci_cd.yaml b/.github/workflows/ci_cd.yaml index 5525ea3..7830423 100644 --- a/.github/workflows/ci_cd.yaml +++ b/.github/workflows/ci_cd.yaml @@ -4,7 +4,7 @@ on: branches: - main env: - VERSION: 1.4.0 + VERSION: 1.4.1 NAME: BitKip jobs: build-windows: diff --git a/build.gradle b/build.gradle index 96ea490..68d8f6b 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { } group 'ir.darkdeveloper' -version '1.4.0' +version '1.4.1' sourceCompatibility = '21' targetCompatibility = '21' mainClassName = 'ir.darkdeveloper.bitkip.BitKip' diff --git a/builders/linux-installer/application/BitKip.desktop b/builders/linux-installer/application/BitKip.desktop index 9c2b005..142acdc 100644 --- a/builders/linux-installer/application/BitKip.desktop +++ b/builders/linux-installer/application/BitKip.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Name=BitKip -Version=1.4.0 +Version=1.4.1 Comment=Free download manager Keywords=download,java,app Exec=/usr/share/BitKip/bin/BitKip diff --git a/src/main/java/ir/darkdeveloper/bitkip/config/AppConfigs.java b/src/main/java/ir/darkdeveloper/bitkip/config/AppConfigs.java index 27c633f..b55eeb1 100644 --- a/src/main/java/ir/darkdeveloper/bitkip/config/AppConfigs.java +++ b/src/main/java/ir/darkdeveloper/bitkip/config/AppConfigs.java @@ -17,7 +17,7 @@ public class AppConfigs { - public static final String VERSION = "1.4.0"; + public static final String VERSION = "1.4.1"; public static final String dataPath = System.getProperty("user.home") + File.separator + "Documents" @@ -57,6 +57,8 @@ public class AppConfigs { public static boolean downloadImmediately = defaultDownloadImmediately; public static final boolean defaultAddSameDownload = true; public static boolean addSameDownload = defaultAddSameDownload; + public static final boolean defaultLessCpuIntensive = false; + public static boolean lessCpuIntensive = defaultLessCpuIntensive; diff --git a/src/main/java/ir/darkdeveloper/bitkip/controllers/BatchDownload.java b/src/main/java/ir/darkdeveloper/bitkip/controllers/BatchDownload.java index 37707a6..12bf97c 100644 --- a/src/main/java/ir/darkdeveloper/bitkip/controllers/BatchDownload.java +++ b/src/main/java/ir/darkdeveloper/bitkip/controllers/BatchDownload.java @@ -81,8 +81,8 @@ public void initialize(URL location, ResourceBundle resources) { checkBtn.setDisable(true); Validations.prepareLinkFromClipboard(urlField); Validations.validateChunksInput(chunksField); - Validations.validateIntInputCheck(startField, 0L); - Validations.validateIntInputCheck(endField, 0L); + Validations.validateIntInputCheck(startField, 0L, 0, null); + Validations.validateIntInputCheck(endField, 0L, 0, null); var questionBtns = new Button[]{questionBtnUrl, questionBtnChunks}; var contents = new String[]{ "You want to download several files, clarify where urls are different by $ sign." + diff --git a/src/main/java/ir/darkdeveloper/bitkip/controllers/DetailsController.java b/src/main/java/ir/darkdeveloper/bitkip/controllers/DetailsController.java index 0115361..accb02f 100644 --- a/src/main/java/ir/darkdeveloper/bitkip/controllers/DetailsController.java +++ b/src/main/java/ir/darkdeveloper/bitkip/controllers/DetailsController.java @@ -66,7 +66,7 @@ public class DetailsController implements FXMLController { private Button controlBtn, openFolderBtn; private Stage stage; - private DownloadModel downloadModel; + private DownloadModel dm; private boolean isComplete = false; private final BooleanProperty isPaused = new SimpleBooleanProperty(true); private final PopOver linkPopover = new PopOver(); @@ -93,41 +93,41 @@ public Stage getStage() { return stage; } - public void setDownloadModel(DownloadModel downloadModel) { - this.downloadModel = downloadModel; + public void setDownloadModel(DownloadModel dm) { + this.dm = dm; initDownloadData(); initDownloadListeners(); } private void initDownloadData() { - Validations.validateInputChecks(null, bytesField, speedField, downloadModel); - var byteLimit = String.valueOf(downloadModel.getByteLimit()); + Validations.validateInputChecks(null, bytesField, speedField, dm); + var byteLimit = String.valueOf(dm.getByteLimit()); if (byteLimit.equals("0")) - byteLimit = String.valueOf(downloadModel.getSize()); + byteLimit = String.valueOf(dm.getSize()); bytesField.setText(byteLimit); - bytesField.setDisable(downloadModel.getSize() < 0 || !downloadModel.isResumable()); - speedField.setDisable(!downloadModel.isResumable()); - var mbOfBytes = IOUtils.getMbOfBytes(downloadModel.getSpeedLimit()); - speedField.setText(downloadModel.getSpeedLimit() == 0 ? "0.0" : String.valueOf(mbOfBytes)); - downloadedBytes.setText(String.valueOf(downloadModel.getDownloaded())); - link.setText(downloadModel.getUrl()); - locationLbl.setText("Path: " + new File(downloadModel.getFilePath()).getParentFile().getAbsolutePath()); - var end = downloadModel.getName().length(); + bytesField.setDisable(dm.getSize() < 0 || !dm.isResumable()); + speedField.setDisable(!dm.isResumable()); + var mbOfBytes = IOUtils.getMbOfBytes(dm.getSpeedLimit()); + speedField.setText(dm.getSpeedLimit() == 0 ? "0.0" : String.valueOf(mbOfBytes)); + downloadedBytes.setText(String.valueOf(dm.getDownloaded())); + link.setText(dm.getUrl()); + locationLbl.setText("Path: " + new File(dm.getFilePath()).getParentFile().getAbsolutePath()); + var end = dm.getName().length(); if (end > 60) end = 60; - stage.setTitle(downloadModel.getName().substring(0, end)); - nameLbl.setText("Name: " + downloadModel.getName()); - var queues = QueuesRepo.findQueuesOfADownload(downloadModel.getId()).toString(); + stage.setTitle(dm.getName().substring(0, end)); + nameLbl.setText("Name: " + dm.getName()); + var queues = QueuesRepo.findQueuesOfADownload(dm.getId()).toString(); queueLbl.setText("Queues: " + queues.substring(1, queues.length() - 1)); - statusLbl.setText("Status: " + downloadModel.getDownloadStatus().name()); + statusLbl.setText("Status: " + dm.getDownloadStatus().name()); var downloadOf = "%s / %s" - .formatted(IOUtils.formatBytes(downloadModel.getDownloaded()), - IOUtils.formatBytes(downloadModel.getSize())); + .formatted(IOUtils.formatBytes(dm.getDownloaded()), + IOUtils.formatBytes(dm.getSize())); downloadedOfLbl.setText(downloadOf); - progressLbl.setText("Progress: %.2f%%".formatted(downloadModel.getProgress())); - downloadProgress.setProgress(downloadModel.getProgress() / 100); - chunksLbl.setText("Chunks: " + downloadModel.getChunks()); - var resumable = downloadModel.isResumable(); + progressLbl.setText("Progress: %.2f%%".formatted(dm.getProgress())); + downloadProgress.setProgress(dm.getProgress() / 100); + chunksLbl.setText("Chunks: " + dm.getChunks()); + var resumable = dm.isResumable(); if (resumable) { resumableLbl.getStyleClass().add("yes"); resumableLbl.getStyleClass().remove("no"); @@ -138,10 +138,10 @@ private void initDownloadData() { resumableLbl.setText("No"); } controlBtn.setText(isPaused.get() ? (resumable ? "Resume" : "Restart") : "Pause"); - openSwitch.setSelected(downloadModel.isOpenAfterComplete()); - showSwitch.setSelected(downloadModel.isShowCompleteDialog()); - onComplete(downloadModel); - turnOffCombo.getSelectionModel().select(downloadModel.getTurnOffMode()); + openSwitch.setSelected(dm.isOpenAfterComplete()); + showSwitch.setSelected(dm.isShowCompleteDialog()); + onComplete(dm); + turnOffCombo.getSelectionModel().select(dm.getTurnOffMode()); } public void initDownloadListeners() { @@ -156,13 +156,13 @@ public void initDownloadListeners() { private DownloadTask getDownloadTask() { return currentDownloadings.stream() - .filter(dm -> dm.equals(downloadModel)) + .filter(dm -> dm.equals(this.dm)) .findFirst() .map(DownloadModel::getDownloadTask).orElse(null); } private void bytesDownloadedListener(DownloadTask dt) { - if (downloadModel.getSize() != -1) + if (dm.getSize() != -1) dt.valueProperty().addListener((ob, o, bytes) -> { if (!isPaused.get()) { if (o == null) @@ -171,22 +171,22 @@ private void bytesDownloadedListener(DownloadTask dt) { downloadedBytes.setText(String.valueOf(bytes)); if (bytes == 0) { - downloadedBytes.setText(String.valueOf(downloadModel.getDownloaded())); + downloadedBytes.setText(String.valueOf(dm.getDownloaded())); speed = 0; } else - downloadModel.setDownloaded(bytes); + dm.setDownloaded(bytes); - downloadModel.setSpeed(speed); - downloadModel.setDownloadStatus(Downloading); + dm.setSpeed(speed); + dm.setDownloadStatus(Downloading); speedLbl.setText(IOUtils.formatBytes(speed)); statusLbl.setText("Status: " + Downloading); var downloadOf = "%s / %s" .formatted(IOUtils.formatBytes(bytes), - IOUtils.formatBytes(downloadModel.getSize())); + IOUtils.formatBytes(dm.getSize())); downloadedOfLbl.setText(downloadOf); if (speed > 0) { - long delta = downloadModel.getSize() - bytes; + long delta = dm.getSize() - bytes; var remaining = DurationFormatUtils.formatDuration((delta / speed) * 1000, "dd:HH:mm:ss"); remainingLbl.setText("Remaining: " + remaining); } @@ -195,8 +195,8 @@ private void bytesDownloadedListener(DownloadTask dt) { else dt.valueProperty().addListener((ob, o, bytes) -> { if (!isPaused.get()) { - downloadModel.setDownloadStatus(Downloading); - downloadModel.setDownloaded(bytes); + dm.setDownloadStatus(Downloading); + dm.setDownloaded(bytes); statusLbl.setText("Status: " + Downloading); var downloadOf = "%s / %s".formatted(IOUtils.formatBytes(bytes), IOUtils.formatBytes(0)); downloadedOfLbl.setText(downloadOf); @@ -222,12 +222,12 @@ public void initialize(URL location, ResourceBundle resources) { else updatePause(newValue); }); openSwitch.selectedProperty().addListener((o, old, newVal) -> { - downloadModel.setOpenAfterComplete(newVal); - DownloadsRepo.updateDownloadOpenAfterComplete(downloadModel); + dm.setOpenAfterComplete(newVal); + DownloadsRepo.updateDownloadOpenAfterComplete(dm); }); showSwitch.selectedProperty().addListener((o, old, newVal) -> { - downloadModel.setShowCompleteDialog(newVal); - DownloadsRepo.updateDownloadShowCompleteDialog(downloadModel); + dm.setShowCompleteDialog(newVal); + DownloadsRepo.updateDownloadShowCompleteDialog(dm); }); linkPopover.setAnimated(true); linkPopover.setContentNode(new Label("Copied")); @@ -243,22 +243,22 @@ public void initialize(URL location, ResourceBundle resources) { } private void updatePause(Boolean paused) { - if (downloadModel.getDownloadStatus() == DownloadStatus.Completed) + if (dm.getDownloadStatus() == DownloadStatus.Completed) return; controlBtn.setDisable(false); - controlBtn.setText(paused ? (downloadModel.isResumable() ? "Resume" : "Restart") : "Pause"); + controlBtn.setText(paused ? (dm.isResumable() ? "Resume" : "Restart") : "Pause"); if (paused) remainingLbl.setText("Remaining: Paused"); - if (downloadModel.getDownloadStatus() == DownloadStatus.Merging) { + if (dm.getDownloadStatus() == DownloadStatus.Merging) { setPauseButtonDisable(true); updateLabels("Status: " + DownloadStatus.Merging.name(), "Remaining: Merging"); downloadProgress.setProgress(ProgressBar.INDETERMINATE_PROGRESS); } else statusLbl.setText("Status: " + (paused ? DownloadStatus.Paused : Downloading)); var downloadOf = "%s / %s" - .formatted(IOUtils.formatBytes(downloadModel.getDownloaded()), - IOUtils.formatBytes(downloadModel.getSize())); + .formatted(IOUtils.formatBytes(dm.getDownloaded()), + IOUtils.formatBytes(dm.getSize())); downloadedOfLbl.setText(downloadOf); } @@ -271,7 +271,7 @@ private void onClose() { @FXML private void onControl() { if (isComplete) { - openFile(downloadModel); + openFile(dm); stage.close(); return; } @@ -279,7 +279,7 @@ private void onControl() { if (isPaused.get()) { statusLbl.setText("Status: " + DownloadStatus.Trying); controlBtn.setDisable(true); - DownloadOpUtils.resumeDownloads(List.of(downloadModel), + DownloadOpUtils.resumeDownloads(List.of(dm), getBytesFromString(speedField.getText()), Long.parseLong(bytesField.getText())); controlBtn.setDisable(false); isPaused.set(false); @@ -319,8 +319,8 @@ public void onComplete(DownloadModel download) { statusLbl.setText("Status: Complete"); progressLbl.setText("Progress: 100%"); var downloadOf = "%s / %s" - .formatted(IOUtils.formatBytes(downloadModel.getSize()), - IOUtils.formatBytes(downloadModel.getSize())); + .formatted(IOUtils.formatBytes(dm.getSize()), + IOUtils.formatBytes(dm.getSize())); downloadedOfLbl.setText(downloadOf); bytesField.setDisable(true); speedField.setDisable(true); @@ -341,25 +341,25 @@ public void closeStage() { } public DownloadModel getDownloadModel() { - return downloadModel; + return dm; } @FXML private void onFolderOpen() { - DownloadOpUtils.openContainingFolder(downloadModel); + DownloadOpUtils.openContainingFolder(dm); stage.close(); } @FXML private void onTurnOffChanged() { var value = turnOffCombo.getValue(); - downloadModel.setTurnOffMode(value); - var downloadTask = downloadModel.getDownloadTask(); + dm.setTurnOffMode(value); + var downloadTask = dm.getDownloadTask(); if (downloadTask != null) - downloadTask.setDownloadModel(downloadModel); + downloadTask.setDownloadModel(dm); DatabaseHelper.updateCol(COL_TURNOFF_MODE, - value.toString(), DOWNLOADS_TABLE_NAME, downloadModel.getId()); + value.toString(), DOWNLOADS_TABLE_NAME, dm.getId()); if (value != NOTHING) { Notifications.create() .title("Turn off mode changed") @@ -370,11 +370,11 @@ private void onTurnOffChanged() { @FXML private void onSpeedApplied() { - var dmTask = downloadModel.getDownloadTask(); + var dmTask = dm.getDownloadTask(); if (dmTask instanceof ChunksDownloadTask cdt) { var speed = getBytesFromString(speedField.getText()); - downloadModel.setSpeed(speed); - downloadModel.setSpeedLimit(speed); + dm.setSpeed(speed); + dm.setSpeedLimit(speed); cdt.setSpeedLimit(speed); speedApplyBtn.setDisable(true); speedApplyBtn.setVisible(false); @@ -383,7 +383,7 @@ private void onSpeedApplied() { @FXML private void onAllBytes() { - bytesField.setText(String.valueOf(downloadModel.getSize())); + bytesField.setText(String.valueOf(dm.getSize())); allBytesBtn.setVisible(false); allBytesBtn.setDisable(true); } @@ -394,17 +394,17 @@ private void onBytesChanged() { return; if (!text.matches("\\d*")) text = text.replaceAll("\\D", ""); - if (Long.parseLong(text) < downloadModel.getSize()) { + if (Long.parseLong(text) < dm.getSize()) { allBytesBtn.setVisible(true); allBytesBtn.setDisable(false); } - if (Long.parseLong(text) < downloadModel.getDownloaded()) - bytesField.setText(String.valueOf(downloadModel.getDownloaded())); + if (Long.parseLong(text) < dm.getDownloaded()) + bytesField.setText(String.valueOf(dm.getDownloaded())); } private void onSpeedChanged() { - var isDownloading = downloadModel.getDownloadStatus() == Downloading; + var isDownloading = dm.getDownloadStatus() == Downloading; var isZero = speedField.getText().equals("0"); speedApplyBtn.setVisible(isDownloading && !isZero); speedApplyBtn.setDisable(isDownloading && isZero); diff --git a/src/main/java/ir/darkdeveloper/bitkip/controllers/QueueSetting.java b/src/main/java/ir/darkdeveloper/bitkip/controllers/QueueSetting.java index 85b39b5..cd355e4 100644 --- a/src/main/java/ir/darkdeveloper/bitkip/controllers/QueueSetting.java +++ b/src/main/java/ir/darkdeveloper/bitkip/controllers/QueueSetting.java @@ -147,7 +147,7 @@ private void initInputs() { Validations.validateTimePickerInputs(startHourSpinner, startMinuteSpinner, startSecondSpinner); Validations.validateTimePickerInputs(stopHourSpinner, stopMinuteSpinner, stopSecondSpinner); Validations.validateSpeedInput(speedField); - Validations.validateIntInputCheck(simulDownloadSpinner.getEditor(), 1, 1, 5); + Validations.validateIntInputCheck(simulDownloadSpinner.getEditor(), 1L, 1, 5); datePicker.setDayCellFactory(dp -> new DateCell() { diff --git a/src/main/java/ir/darkdeveloper/bitkip/controllers/SettingsController.java b/src/main/java/ir/darkdeveloper/bitkip/controllers/SettingsController.java index ac27e0f..86c0452 100644 --- a/src/main/java/ir/darkdeveloper/bitkip/controllers/SettingsController.java +++ b/src/main/java/ir/darkdeveloper/bitkip/controllers/SettingsController.java @@ -39,7 +39,7 @@ public class SettingsController implements FXMLController { @FXML private CheckBox immediateCheck, triggerOffCheck, agentCheck, addDownCheck, - continueCheck, completeDialogCheck, serverCheck; + continueCheck, completeDialogCheck, serverCheck, lessCpuCheck; @FXML private VBox root, actionArea, queueContainer; @FXML @@ -89,11 +89,11 @@ public void initAfterStage() { @Override public void initialize(URL location, ResourceBundle resources) { - Validations.validateIntInputCheck(portField, (long) serverPort); - Validations.validateIntInputCheck(retryField, (long) downloadRetryCount); - Validations.validateIntInputCheck(rateLimitField, (long) downloadRateLimitCount); - Validations.validateIntInputCheck(connectionField, (long) connectionTimeout); - Validations.validateIntInputCheck(readField, (long) readTimeout); + Validations.validateIntInputCheck(portField, (long) serverPort, 0, null); + Validations.validateIntInputCheck(retryField, (long) downloadRetryCount, 1, null); + Validations.validateIntInputCheck(rateLimitField, (long) downloadRateLimitCount, 1, null); + Validations.validateIntInputCheck(connectionField, (long) connectionTimeout, 0, null); + Validations.validateIntInputCheck(readField, (long) readTimeout, 0, null); agentDesc.setText("Note: If you enter wrong agent, your downloads may not start. Your agent will update when you use extension"); initElements(); } @@ -104,6 +104,7 @@ private void initElements() { triggerOffCheck.setSelected(triggerTurnOffOnEmptyQueue); immediateCheck.setSelected(downloadImmediately); addDownCheck.setSelected(addSameDownload); + lessCpuCheck.setSelected(lessCpuIntensive); portField.setText(String.valueOf(serverPort)); retryField.setText(String.valueOf(downloadRetryCount)); rateLimitField.setText(String.valueOf(downloadRateLimitCount)); @@ -251,6 +252,7 @@ private void onDefaults() { userAgentEnabled = defaultUserAgentEnabled; connectionTimeout = defaultConnectionTimeout; readTimeout = defaultReadTimeout; + lessCpuIntensive = defaultLessCpuIntensive; IOUtils.saveConfigs(); initElements(); showSavedMessage(); @@ -281,6 +283,12 @@ private void onAddDownCheck() { IOUtils.saveConfigs(); } + @FXML + private void onLessCpuCheck() { + lessCpuIntensive = lessCpuCheck.isSelected(); + IOUtils.saveConfigs(); + } + public void setQueue(QueueModel selectedQueue) { tabPane.getSelectionModel().select(2); queueController.setSelectedQueue(selectedQueue); diff --git a/src/main/java/ir/darkdeveloper/bitkip/task/ChunksDownloadTask.java b/src/main/java/ir/darkdeveloper/bitkip/task/ChunksDownloadTask.java index a20a321..30f7c77 100644 --- a/src/main/java/ir/darkdeveloper/bitkip/task/ChunksDownloadTask.java +++ b/src/main/java/ir/darkdeveloper/bitkip/task/ChunksDownloadTask.java @@ -82,9 +82,8 @@ protected Long call() { private void downloadInChunks(long fileSize) throws IOException, InterruptedException, ExecutionException { - var futures = prepareParts(fileSize); -// futures.addFirst(calculateSpeedAndProgress(fileSize)); calculateSpeedAndProgress(fileSize); + var futures = prepareParts(fileSize); if (!futures.isEmpty()) { isCalculating = true; log.info("Downloading : " + downloadModel); @@ -247,7 +246,7 @@ private void performSpeedLimitedDownload(long fromContinue, long from, long to, private void calculateSpeedAndProgress(long fileSize) { - new Thread(() -> { + Runnable runnable = () -> { Thread.currentThread().setName("calculator: " + Thread.currentThread().getName()); try { while (!isCalculating) Thread.onSpinWait(); @@ -266,7 +265,11 @@ private void calculateSpeedAndProgress(long fileSize) { } catch (IOException e) { log.error(e.getLocalizedMessage()); } - }).start(); + }; + if (lessCpuIntensive) + new Thread(runnable).start(); + else + executor.submit(runnable); } @Override diff --git a/src/main/java/ir/darkdeveloper/bitkip/utils/DownloadOpUtils.java b/src/main/java/ir/darkdeveloper/bitkip/utils/DownloadOpUtils.java index 3b55445..af33422 100644 --- a/src/main/java/ir/darkdeveloper/bitkip/utils/DownloadOpUtils.java +++ b/src/main/java/ir/darkdeveloper/bitkip/utils/DownloadOpUtils.java @@ -93,7 +93,8 @@ public static void triggerDownload(DownloadModel dm, long speed, long bytes, boo DownloadsRepo.insertDownload(dm); mainTableUtils.addRow(dm); } - var executor = Executors.newVirtualThreadPerTaskExecutor(); + + var executor = lessCpuIntensive ? Executors.newVirtualThreadPerTaskExecutor() : Executors.newCachedThreadPool(); downloadTask.setExecutor(executor); log.info(("Starting download in " + (blocking ? "blocking" : "non-blocking") + ": %s").formatted(dm)); if (blocking) diff --git a/src/main/java/ir/darkdeveloper/bitkip/utils/FxUtils.java b/src/main/java/ir/darkdeveloper/bitkip/utils/FxUtils.java index f1546f2..050a760 100644 --- a/src/main/java/ir/darkdeveloper/bitkip/utils/FxUtils.java +++ b/src/main/java/ir/darkdeveloper/bitkip/utils/FxUtils.java @@ -530,7 +530,7 @@ public static boolean showFailedToStart(String header, String content) { dialog.getDialogPane().getButtonTypes().addAll(ButtonType.APPLY, ButtonType.CANCEL); var portField = new TextField(); - Validations.validateIntInputCheck(portField, (long) serverPort); + Validations.validateIntInputCheck(portField, (long) serverPort, 0, null); portField.setText(String.valueOf(serverPort)); var contentLabel = new Label(); contentLabel.setText(content); diff --git a/src/main/java/ir/darkdeveloper/bitkip/utils/IOUtils.java b/src/main/java/ir/darkdeveloper/bitkip/utils/IOUtils.java index d43774f..ba21769 100644 --- a/src/main/java/ir/darkdeveloper/bitkip/utils/IOUtils.java +++ b/src/main/java/ir/darkdeveloper/bitkip/utils/IOUtils.java @@ -286,6 +286,7 @@ public static void saveConfigs() { .append("read_timeout=").append(String.valueOf(readTimeout)).append("\n") .append("immediate_download=").append(String.valueOf(downloadImmediately)).append("\n") .append("add_same_download=").append(String.valueOf(addSameDownload)).append("\n") + .append("less_cpu_intensive=").append(String.valueOf(lessCpuIntensive)).append("\n") .append("user_agent_enabled=").append(String.valueOf(userAgentEnabled)).append("\n") .append("user_agent=").append(userAgent); writer.flush(); @@ -321,6 +322,7 @@ public static void readConfig() { case "read_timeout" -> readTimeout = Integer.parseInt(value); case "immediate_download" -> downloadImmediately = value.equals("true"); case "add_same_download" -> addSameDownload = value.equals("true"); + case "less_cpu_intensive" -> lessCpuIntensive = value.equals("true"); case "user_agent" -> userAgent = value; case "user_agent_enabled" -> userAgentEnabled = value.equals("true"); } diff --git a/src/main/java/ir/darkdeveloper/bitkip/utils/MenuUtils.java b/src/main/java/ir/darkdeveloper/bitkip/utils/MenuUtils.java index 7a46af0..c9f5cb1 100644 --- a/src/main/java/ir/darkdeveloper/bitkip/utils/MenuUtils.java +++ b/src/main/java/ir/darkdeveloper/bitkip/utils/MenuUtils.java @@ -215,6 +215,8 @@ public static void disableMenuItems(Label resumeLbl, Label pauseLbl, Label pause menuItems.get(refreshLbl).setDisable(false); menuItems.get(addToQueueLbl).setDisable(false); }); + selectedItems.stream().filter(dm -> dm.getDownloadStatus() == DownloadStatus.Merging) + .findFirst().ifPresent(dm -> menuItems.values().forEach(menuItem -> menuItem.setDisable(true))); } public static void initMoreMenu(Button moreBtn, TableView table) { diff --git a/src/main/java/ir/darkdeveloper/bitkip/utils/Validations.java b/src/main/java/ir/darkdeveloper/bitkip/utils/Validations.java index 8fa4f77..e422dfb 100644 --- a/src/main/java/ir/darkdeveloper/bitkip/utils/Validations.java +++ b/src/main/java/ir/darkdeveloper/bitkip/utils/Validations.java @@ -6,6 +6,7 @@ import javafx.scene.control.Spinner; import javafx.scene.control.TextField; import javafx.scene.input.Clipboard; +import org.apache.commons.lang3.StringUtils; import java.io.IOException; @@ -14,30 +15,45 @@ public class Validations { - public static void validateInputChecks(TextField chunksField, TextField bytesField, - TextField speedField, DownloadModel dm) { + public static void validateInputChecks(TextField chunksField, TextField bytesField, TextField speedField, DownloadModel dm) { validateChunksInput(chunksField); validateSpeedInput(speedField); - validateBytesInput(bytesField, dm.getSize()); + validateBytesInput(bytesField, dm); } - private static void validateBytesInput(TextField bytesField, long fileSize) { + public static void validateBytesInput(TextField bytesField, DownloadModel dm) { if (bytesField == null) return; - bytesField.textProperty().addListener((o, old, newValue) -> { + var fileSize = dm.getSize(); if (!newValue.matches("\\d*")) { if (newValue.equals("-1")) newValue = "0"; newValue = newValue.replaceAll("\\D", ""); bytesField.setText(newValue); + } else { + if (newValue.isBlank() || (fileSize > 0 && Long.parseLong(newValue) > fileSize)) { + bytesField.setText(String.valueOf(fileSize)); + } else + return; + long newV; + if (newValue.length() > 1) { + var stripped = StringUtils.stripStart(newValue, "0"); + if (stripped.isBlank()) { + bytesField.setText(String.valueOf(fileSize)); + return; + } + newV = Long.parseLong(stripped); + } else + newV = Long.parseLong(newValue); + + if (newV > fileSize) + bytesField.setText(String.valueOf(fileSize)); } - if (newValue.isBlank() || (fileSize > 0 && Long.parseLong(newValue) > fileSize)) - bytesField.setText(String.valueOf(fileSize)); }); bytesField.focusedProperty().addListener((o, old, newValue) -> { if (!newValue && bytesField.getText().isBlank()) - bytesField.setText(String.valueOf(fileSize)); + bytesField.setText(String.valueOf(dm.getSize())); }); } @@ -58,41 +74,35 @@ public static void validateChunksInput(TextField chunksField) { if (chunksField == null) return; var threads = maxChunks(Long.MAX_VALUE); + validateIntInputCheck(chunksField, (long) threads, 0, threads); chunksField.setText(String.valueOf(threads)); - chunksField.setDisable(true); } - public static void validateIntInputCheck(TextField field, Long defaultVal) { + public static void validateIntInputCheck(TextField field, Long defaultVal, Integer min, Integer max) { if (field == null) return; field.textProperty().addListener((o, old, newValue) -> { - if (!newValue.matches("\\d*")) { + if (!newValue.matches("\\d*")) field.setText(newValue.replaceAll("\\D", "")); - if (defaultVal != null && field.getText().isBlank()) - field.setText(String.valueOf(defaultVal)); - } - }); - field.focusedProperty().addListener((o, old, newValue) -> { - if (defaultVal != null && !newValue && field.getText().isBlank()) - field.setText(String.valueOf(defaultVal)); - }); - } - - public static void validateIntInputCheck(TextField field, long defaultVal, long minValue, long maxValue) { - if (field == null) - return; - field.textProperty().addListener((o, old, newValue) -> { - if (!newValue.matches("\\d*")) { - field.setText(newValue.replaceAll("\\D", "")); - if (field.getText().isBlank()) - field.setText(String.valueOf(defaultVal)); - } else { - if (newValue.isBlank()) + else { + if (newValue.isBlank()) { + field.setText(String.valueOf(min)); return; - if (Long.parseLong(newValue) > maxValue) - field.setText(String.valueOf(maxValue)); - if (Long.parseLong(newValue) < minValue) - field.setText(String.valueOf(minValue)); + } + var newV = 0; + if (newValue.length() > 1) { + var stripped = StringUtils.stripStart(newValue, "0"); + if (stripped.isBlank()) { + field.setText(String.valueOf(min)); + return; + } + newV = Integer.parseInt(stripped); + } else + newV = Integer.parseInt(newValue); + if (min != null && newV < min) + field.setText(String.valueOf(min)); + else if (max != null && newV > max) + field.setText(String.valueOf(max)); } }); field.focusedProperty().addListener((o, old, newValue) -> { @@ -113,38 +123,18 @@ public static void prepareLinkFromClipboard(TextField urlField) { public static void validateTimePickerInputs(Spinner hourSpinner, Spinner minuteSpinner, Spinner secondSpinner) { - validateIntInputCheck(hourSpinner.getEditor(), null); - validateIntInputCheck(minuteSpinner.getEditor(), null); - validateIntInputCheck(secondSpinner.getEditor(), null); - - hourSpinner.getEditor().textProperty().addListener((o, o2, n) -> { - if (n == null) - return; - if (n.isBlank()) - hourSpinner.getEditor().setText("0"); - if (!n.isBlank() && Integer.parseInt(n) > 23) - hourSpinner.getEditor().setText("23"); - }); - zeroToSixtySpinner(minuteSpinner); - zeroToSixtySpinner(secondSpinner); + validateIntInputCheck(hourSpinner.getEditor(), null, 0, 23); + validateIntInputCheck(minuteSpinner.getEditor(), null, 0, 59); + validateIntInputCheck(secondSpinner.getEditor(), null, 0, 59); } - private static void zeroToSixtySpinner(Spinner minuteSpinner) { - minuteSpinner.getEditor().textProperty().addListener((o, o2, n) -> { - if (n == null) - return; - if (n.isBlank()) - minuteSpinner.getEditor().setText("0"); - if (!n.isBlank() && Integer.parseInt(n) > 59) - minuteSpinner.getEditor().setText("59"); - }); - } - - public static int maxChunks(long fileSize) { if (fileSize < 2_000_000) return 0; - return Runtime.getRuntime().availableProcessors() * 2; + int processors = Runtime.getRuntime().availableProcessors(); + if (processors < 10) + return processors * 2; + return processors; } public static boolean validateUrl(String url) { diff --git a/src/main/resources/ir/darkdeveloper/bitkip/fxml/settings.fxml b/src/main/resources/ir/darkdeveloper/bitkip/fxml/settings.fxml index 9293f29..69a5345 100644 --- a/src/main/resources/ir/darkdeveloper/bitkip/fxml/settings.fxml +++ b/src/main/resources/ir/darkdeveloper/bitkip/fxml/settings.fxml @@ -101,6 +101,11 @@