Skip to content

Commit c169327

Browse files
authored
Merge pull request #21 from BeanVortex/1.5.1
1.5.1
2 parents 8ab6587 + 9086101 commit c169327

40 files changed

+642
-191
lines changed

.github/workflows/ci_cd.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
branches:
55
- main
66
env:
7-
VERSION: 1.5.0
7+
VERSION: 1.5.1
88
EXT_VERSION: 1.2
99
NAME: BitKip
1010
jobs:

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
- [x] Download any file based on http/https protocol
77
- [x] Extremely fast download in chunk mode
8+
- [x] Authenticated downloads (providing username and password)
9+
- [x] User's credentials are encrypted (not usable after terminating the application session)
810
- [x] Change download speed while download is in progress
911
- [x] Ability to turn off/sleep pc after a single download is done
1012
- [x] Ability to limit download speed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ plugins {
88
}
99

1010
group 'io.beanvortex'
11-
version '1.5.0'
11+
version '1.5.1'
1212
sourceCompatibility = '21'
1313
targetCompatibility = '21'
1414
mainClassName = 'io.beanvortex.bitkip.BitKip'

builders/linux-installer/application/BitKip.desktop

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[Desktop Entry]
22
Name=BitKip
3-
Version=1.5.0
3+
Version=1.5.1
44
Comment=Free download manager
55
Keywords=download,java,app
66
Exec=/usr/share/BitKip/bin/BitKip

src/main/java/io/beanvortex/bitkip/api/BatchService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ private void doPost(ServerRequest req, ServerResponse res) {
3737
res.status(400).send("Empty data sent by extension");
3838
return;
3939
}
40-
Platform.runLater(() -> FxUtils.newBatchListStage(links));
40+
Platform.runLater(() -> FxUtils.newBatchListStage(links, null));
4141
res.status(200).send();
4242
});
4343
}

src/main/java/io/beanvortex/bitkip/config/AppConfigs.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
public class AppConfigs {
1919

20-
public static final String VERSION = "1.5.0";
20+
public static final String VERSION = "1.5.1";
2121

2222
public static final String dataPath = System.getProperty("user.home")
2323
+ File.separator + "Documents"

src/main/java/io/beanvortex/bitkip/controllers/BatchDownload.java

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.beanvortex.bitkip.controllers;
22

33
import io.beanvortex.bitkip.config.AppConfigs;
4+
import io.beanvortex.bitkip.models.Credentials;
45
import io.beanvortex.bitkip.utils.FxUtils;
56
import io.beanvortex.bitkip.config.observers.QueueObserver;
67
import io.beanvortex.bitkip.config.observers.QueueSubject;
@@ -34,17 +35,17 @@
3435
public class BatchDownload implements QueueObserver {
3536

3637
@FXML
37-
private Label errorLabel;
38+
private PasswordField passwordField;
3839
@FXML
39-
private Button questionBtnUri, checkBtn, cancelBtn, questionBtnChunks, openLocation, newQueue;
40+
private Label errorLabel;
4041
@FXML
41-
private TextField startField, locationField, endField;
42+
private Button questionBtnUri, refreshBtn, checkBtn, cancelBtn, questionBtnChunks, openLocation, newQueue;
4243
@FXML
4344
private ComboBox<QueueModel> queueCombo;
4445
@FXML
45-
private TextField chunksField, urlField;
46+
private TextField chunksField, urlField, usernameField, startField, locationField, endField;
4647
@FXML
47-
private CheckBox lastLocationCheck;
48+
private CheckBox lastLocationCheck, authorizedCheck;
4849

4950
private LinkModel tempLink;
5051
private Stage stage;
@@ -77,6 +78,12 @@ public void initialize(URL location, ResourceBundle resources) {
7778
queueCombo.setValue(queues.get(0));
7879
errorLabel.setVisible(false);
7980
checkBtn.setDisable(true);
81+
usernameField.getParent().setManaged(false);
82+
usernameField.getParent().setVisible(false);
83+
passwordField.getParent().setManaged(false);
84+
passwordField.getParent().setVisible(false);
85+
refreshBtn.setGraphic(new FontIcon());
86+
refreshBtn.setOnAction(e -> autoFillLocation());
8087
if (AppConfigs.lastSavedDir == null)
8188
lastLocationCheck.setDisable(true);
8289
Validations.prepareLinkFromClipboard(urlField);
@@ -96,20 +103,20 @@ public void initialize(URL location, ResourceBundle resources) {
96103
endField.textProperty().addListener(o -> autoFillLocation());
97104
urlField.textProperty().addListener((o, ol, n) -> {
98105
if (n.isBlank())
99-
DownloadUtils.disableControlsAndShowError("URL is blank", errorLabel, checkBtn, null, null);
106+
DownloadUtils.disableControlsAndShowError("URL is blank", errorLabel, checkBtn, null);
100107
else autoFillLocation();
101108
});
102109
locationField.textProperty().addListener((o, ol, n) -> {
103110
if (n.isBlank())
104-
DownloadUtils.disableControlsAndShowError("Location is blank", errorLabel, checkBtn, null, null);
111+
DownloadUtils.disableControlsAndShowError("Location is blank", errorLabel, checkBtn, null);
105112
else onOfflineFieldsChanged();
106113
});
107114
}
108115

109116
private void onOfflineFieldsChanged() {
110117
if (tempLink != null)
111118
handleError(() -> DownloadUtils.onOfflineFieldsChanged(locationField, tempLink.getName(),
112-
null, queueCombo, null, checkBtn, openLocation, null, lastLocationCheck), errorLabel);
119+
null, queueCombo, null, checkBtn, openLocation, lastLocationCheck), errorLabel);
113120

114121
}
115122

@@ -122,26 +129,27 @@ private void autoFillLocation() {
122129
var links = generateLinks(url, start, end, Integer.parseInt(chunksField.getText()), true);
123130
var link = links.get(0);
124131
tempLink = link;
125-
var connection = DownloadUtils.connect(link.getUri());
132+
var credential = new Credentials(usernameField.getText(), passwordField.getText());
133+
var connection = DownloadUtils.connect(link.getUri(), credential);
126134
var fileNameLocationFuture = CompletableFuture.supplyAsync(() -> DownloadUtils.extractFileName(link.getUri(), connection))
127135
.thenAccept(this::setLocation);
128136
fileNameLocationFuture
129137
.whenComplete((unused, throwable) ->
130138
handleError(() -> DownloadUtils.checkIfFileIsOKToSave(locationField.getText(),
131-
tempLink.getName(), null, checkBtn, null, lastLocationCheck), errorLabel))
139+
tempLink.getName(), null, checkBtn, lastLocationCheck), errorLabel))
132140
.exceptionally(throwable -> {
133141
var errorMsg = throwable.getCause().getLocalizedMessage();
134142
Platform.runLater(() ->
135143
DownloadUtils.disableControlsAndShowError(errorMsg, errorLabel,
136-
checkBtn, null, null));
144+
checkBtn, null));
137145
return null;
138146
});
139147
} catch (NumberFormatException ignore) {
140148
} catch (Exception e) {
141149
var errorMsg = e.getLocalizedMessage();
142150
if (e instanceof IndexOutOfBoundsException)
143151
errorMsg = "No URLs found";
144-
DownloadUtils.disableControlsAndShowError(errorMsg, errorLabel, checkBtn, null, null);
152+
DownloadUtils.disableControlsAndShowError(errorMsg, errorLabel, checkBtn, null);
145153
}
146154
}
147155

@@ -231,7 +239,7 @@ private void onSelectLocation(ActionEvent e) {
231239
locationField.setText(path);
232240
if (tempLink != null)
233241
handleError(() -> DownloadUtils.checkIfFileIsOKToSave(locationField.getText(),
234-
tempLink.getName(), null, checkBtn, null, lastLocationCheck), errorLabel);
242+
tempLink.getName(), null, checkBtn, lastLocationCheck), errorLabel);
235243
}
236244

237245
@FXML
@@ -244,12 +252,12 @@ private void onCheck() {
244252
var finalPath = path;
245253
if (url.isBlank()) {
246254
log.warn("URL is blank");
247-
DownloadUtils.disableControlsAndShowError("URL is blank", errorLabel, checkBtn, null, null);
255+
DownloadUtils.disableControlsAndShowError("URL is blank", errorLabel, checkBtn, null);
248256
return;
249257
}
250258
if (path.isBlank()) {
251259
log.warn("Location is blank");
252-
DownloadUtils.disableControlsAndShowError("Location is blank", errorLabel, checkBtn, null, null);
260+
DownloadUtils.disableControlsAndShowError("Location is blank", errorLabel, checkBtn, null);
253261
return;
254262
}
255263
var start = Integer.parseInt(startField.getText());
@@ -270,7 +278,7 @@ private void onCheck() {
270278
var msg = "At least one URL exists for this location. Change location or change start, end.\n"
271279
+ found.get().getUri();
272280
log.warn(msg);
273-
DownloadUtils.disableControlsAndShowError(msg, errorLabel, checkBtn, null, null);
281+
DownloadUtils.disableControlsAndShowError(msg, errorLabel, checkBtn, null);
274282
return;
275283
}
276284
}
@@ -286,14 +294,17 @@ private void onCheck() {
286294
lm.setPath(finalPath);
287295
lm.setSelectedPath(finalPath);
288296
});
289-
FxUtils.newBatchListStage(links);
297+
Credentials credentials = null;
298+
if (!usernameField.getText().isBlank() && !passwordField.getText().isBlank())
299+
credentials = new Credentials(usernameField.getText(), passwordField.getText());
300+
FxUtils.newBatchListStage(links, credentials);
290301
getQueueSubject().removeObserver(this);
291302
stage.close();
292303
} catch (IllegalArgumentException e) {
293304
if (e instanceof NumberFormatException)
294305
return;
295306
log.error(e.getLocalizedMessage());
296-
DownloadUtils.disableControlsAndShowError(e.getLocalizedMessage(), errorLabel, checkBtn, null, null);
307+
DownloadUtils.disableControlsAndShowError(e.getLocalizedMessage(), errorLabel, checkBtn, null);
297308
}
298309
}
299310

@@ -351,6 +362,16 @@ private void onLastLocationCheck() {
351362
else
352363
setLocation(tempLink.getName());
353364
}
365+
366+
@FXML
367+
private void onAuthorizedCheck() {
368+
usernameField.getParent().setManaged(authorizedCheck.isSelected());
369+
usernameField.getParent().setVisible(authorizedCheck.isSelected());
370+
passwordField.getParent().setManaged(authorizedCheck.isSelected());
371+
passwordField.getParent().setVisible(authorizedCheck.isSelected());
372+
usernameField.setText("");
373+
passwordField.setText("");
374+
}
354375
}
355376

356377

src/main/java/io/beanvortex/bitkip/controllers/BatchList.java

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.beanvortex.bitkip.utils.FxUtils;
1111
import io.beanvortex.bitkip.utils.LinkTableUtils;
1212
import io.beanvortex.bitkip.config.observers.QueueObserver;
13+
import javafx.application.Platform;
1314
import javafx.collections.FXCollections;
1415
import javafx.event.ActionEvent;
1516
import javafx.fxml.FXML;
@@ -30,6 +31,7 @@
3031
import java.util.ResourceBundle;
3132
import java.util.concurrent.CopyOnWriteArrayList;
3233
import java.util.concurrent.Executors;
34+
import java.util.function.Consumer;
3335

3436
import static io.beanvortex.bitkip.config.AppConfigs.log;
3537
import static io.beanvortex.bitkip.config.AppConfigs.mainTableUtils;
@@ -38,20 +40,43 @@
3840
public class BatchList implements QueueObserver {
3941

4042
@FXML
41-
private TextField locationField;
43+
private PasswordField passwordField;
4244
@FXML
43-
private CheckBox lastLocationCheck;
45+
private TextField locationField, usernameField;
46+
@FXML
47+
private CheckBox lastLocationCheck, authorizedCheck;
4448
@FXML
4549
private ComboBox<QueueModel> comboQueue;
4650
@FXML
47-
private Button addBtn, newQueue, openLocation;
51+
private Button addBtn, newQueue, openLocation, refreshBtn, stopBtn;
4852
@FXML
4953
private TableView<LinkModel> linkTable;
5054

55+
private volatile boolean fetchingStopped;
56+
5157
private Stage stage;
5258
private LinkTableUtils linkTableUtils;
5359
private List<LinkModel> links;
5460
private static final QueueModel customQueue = new QueueModel("CUSTOM", false);
61+
private Credentials credentials;
62+
63+
@FXML
64+
private void onAuthorizedCheck() {
65+
toggleViewOfAuthorizeFields(authorizedCheck.isSelected());
66+
usernameField.setText("");
67+
passwordField.setText("");
68+
}
69+
70+
public void setCredential(Credentials credentials) {
71+
this.credentials = credentials;
72+
if (credentials != null) {
73+
authorizedCheck.setSelected(true);
74+
authorizedCheck.setDisable(true);
75+
usernameField.setText("***");
76+
passwordField.setText("***");
77+
toggleViewOfAuthorizeFields(false);
78+
}
79+
}
5580

5681

5782
@AllArgsConstructor
@@ -75,9 +100,23 @@ public void initialize(URL l, ResourceBundle resources) {
75100
addBtn.setDisable(true);
76101
comboQueue.setDisable(true);
77102
newQueue.setGraphic(new FontIcon());
103+
refreshBtn.setGraphic(new FontIcon());
104+
stopBtn.setGraphic(new FontIcon());
105+
stopBtn.setVisible(false);
106+
stopBtn.setDisable(true);
107+
stopBtn.setOnAction(e -> fetchingStopped = true);
108+
refreshBtn.setOnAction(e -> fetchLinksData(this.links));
109+
toggleViewOfAuthorizeFields(false);
78110
location = new LocationData(locationField, null, true);
79111
}
80112

113+
private void toggleViewOfAuthorizeFields(boolean visible) {
114+
usernameField.getParent().setManaged(visible);
115+
usernameField.getParent().setVisible(visible);
116+
passwordField.getParent().setManaged(visible);
117+
passwordField.getParent().setVisible(visible);
118+
}
119+
81120

82121
@Override
83122
public void setStage(Stage stage) {
@@ -87,7 +126,13 @@ public void setStage(Stage stage) {
87126

88127
public void setData(List<LinkModel> links) {
89128
this.links = links;
90-
linkTableUtils = new LinkTableUtils(linkTable, links, comboQueue, stage);
129+
var refreshLinks = new Consumer<List<LinkModel>>() {
130+
@Override
131+
public void accept(List<LinkModel> links) {
132+
fetchLinksData(links);
133+
}
134+
};
135+
linkTableUtils = new LinkTableUtils(linkTable, links, refreshLinks, comboQueue, stage);
91136
linkTableUtils.tableInits();
92137
location.setFirstPath(links.get(0).getPath());
93138
locationField.setText(location.getFirstPath());
@@ -103,24 +148,39 @@ public void setData(List<LinkModel> links) {
103148

104149

105150
private void fetchLinksData(List<LinkModel> links) {
151+
fetchingStopped = false;
106152
var executor = Executors.newCachedThreadPool();
107-
var linkTask = new LinkDataTask(links);
153+
if (credentials == null || credentials.username().isBlank() || credentials.password().isBlank())
154+
credentials = new Credentials(usernameField.getText(), passwordField.getText());
155+
var linkTask = new LinkDataTask(links, credentials);
108156
linkTask.valueProperty().addListener((o, ol, linkFlux) ->
109157
executor.submit(() -> {
110-
linkFlux.subscribe(
111-
lm -> {
112-
linkTableUtils.updateLink(lm);
113-
if (!stage.isShowing())
114-
linkTask.setCancel(true);
115-
116-
},
117-
this::errorLog,
118-
() -> {
119-
addBtn.setDisable(false);
120-
comboQueue.setDisable(false);
121-
executor.shutdown();
122-
}
123-
);
158+
Platform.runLater(() -> {
159+
refreshBtn.setDisable(true);
160+
refreshBtn.setVisible(false);
161+
stopBtn.setDisable(false);
162+
stopBtn.setVisible(true);
163+
});
164+
linkFlux.takeUntil(lm -> fetchingStopped)
165+
.subscribe(
166+
lm -> {
167+
linkTableUtils.updateLink(lm);
168+
if (!stage.isShowing())
169+
linkTask.setCancel(true);
170+
},
171+
this::errorLog,
172+
() -> {
173+
addBtn.setDisable(false);
174+
comboQueue.setDisable(false);
175+
Platform.runLater(() -> {
176+
refreshBtn.setDisable(false);
177+
refreshBtn.setVisible(true);
178+
stopBtn.setDisable(true);
179+
stopBtn.setVisible(false);
180+
});
181+
executor.shutdown();
182+
}
183+
);
124184
}));
125185
executor.submit(linkTask);
126186
}

0 commit comments

Comments
 (0)