package com.narrowtux.fmm.io.tasks;
import com.narrowtux.fmm.model.Datastore;
import com.narrowtux.fmm.io.ModpackDetectorVisitor;
import com.narrowtux.fmm.util.Util;
import com.narrowtux.fmm.model.Mod;
import com.narrowtux.fmm.model.Version;
import javafx.concurrent.Task;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Random;
public class ModDownloadTask extends Task<Mod> implements Pauseable {
public static final int DOWNLOAD_BUFFER_SIZE = 4096;
public static final int CONNECT_TIMEOUT = 5000; // 5 seconds
public static final int READ_TIMEOUT = 10 * 60 * 1000; // 10 minutes
private final URL url;
private final String expectedName;
private final Version expectedVersion;
private static Random random = new Random();
private Path downloadingFile;
private long lastUpdate = 0;
private long started, ended;
public ModDownloadTask(URL url, String expectedName, Version expectedVersion) {
this.url = url;
this.expectedName = expectedName;
this.expectedVersion = expectedVersion;
}
@Override
protected Mod call() throws Exception {
try {
updateMessage("Preparing");
updateProgress(-1, 100);
updateMessage("Connecting to server");
if (expectedName != null) {
updateTitle("Download " + expectedName + (expectedVersion != null ? "#" + expectedVersion.toString() : ""));
} else {
updateTitle("Download: " + url.toString());
}
URLConnection conn = url.openConnection();
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.connect();
InputStream stream = conn.getInputStream();
long contentLength = conn.getContentLengthLong();
updateProgress(0, contentLength);
downloadingFile = Datastore.getInstance().getFMMDir().resolve("downloads").resolve("Download-tmp-" + random.nextLong() + ".part");
Files.createDirectories(downloadingFile.getParent());
FileOutputStream writer = new FileOutputStream(downloadingFile.toFile());
byte buf[] = new byte[DOWNLOAD_BUFFER_SIZE];
int read = 0;
long totalRead = 0;
updateMessage("Downloading");
started = System.currentTimeMillis();
int currentReadingSpeed = 32;
do {
if (isCancelled()) {
updateMessage("Cancelled");
failed();
return null;
}
read = stream.read(buf, 0, currentReadingSpeed);
if (currentReadingSpeed < DOWNLOAD_BUFFER_SIZE) {
currentReadingSpeed *= 2;
}
if (read > 0) {
totalRead += read;
writer.write(buf, 0, read);
if (System.currentTimeMillis() - lastUpdate > 200) {
updateMessage("Downloading: " + Util.formatBytes(totalRead) + " / " + Util.formatBytes(contentLength));
updateProgress(totalRead, contentLength);
lastUpdate = System.currentTimeMillis();
}
}
} while (read > 0);
ended = System.currentTimeMillis();
updateMessage("Took " + (ended - started) + " ms to download");
updateProgress(totalRead, contentLength);
updateMessage("Installing");
Mod mod = ModpackDetectorVisitor.parseMod(downloadingFile);
if (mod == null) {
updateMessage("File was not a mod");
failed();
} else {
if (expectedName != null) {
if (!expectedName.equals(mod.getName())) {
updateMessage("This file does not contain the expected mod");
failed();
}
}
if (expectedVersion != null) {
if (!expectedVersion.equals(mod.getVersion())) {
updateMessage("This file does not contain the expected version of the mod");
failed();
}
}
mod.setPath(Datastore.getInstance().getFMMDir().resolve("mods").resolve(mod.getName() + "_" + mod.getVersion().toString() + ".zip"));
updateMessage("Done");
succeeded();
return mod;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void failed() {
super.failed();
// delete part file
try {
Files.deleteIfExists(downloadingFile);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected void updateMessage(String s) {
super.updateMessage(s);
System.out.println("Download task" + " - " + s);
}
}