Fix: ui, set a global progress on the download

This commit is contained in:
Laurent Clouet
2024-04-11 15:24:50 +00:00
parent 50ddc9ae3f
commit 74b39797ce
8 changed files with 97 additions and 58 deletions

View File

@@ -24,7 +24,6 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.sql.Time;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
@@ -38,11 +37,9 @@ import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -795,6 +792,8 @@ import okhttp3.HttpUrl;
ExecutorService executor = Executors.newFixedThreadPool(total); ExecutorService executor = Executors.newFixedThreadPool(total);
ArrayList<Callable<Error.Type>> tasks = new ArrayList<>(); ArrayList<Callable<Error.Type>> tasks = new ArrayList<>();
this.gui.getDownloadProgress().reset("Downloading project");
for (int i = 0; i < total; i++) { for (int i = 0; i < total; i++) {
Chunk chunk = ajob_.getArchiveChunks().get(i); Chunk chunk = ajob_.getArchiveChunks().get(i);
@@ -804,7 +803,6 @@ import okhttp3.HttpUrl;
this.server, this.server,
this.gui, this.gui,
this.log, this.log,
String.format(LOCALE, "chunk %d/%d", finalI + 1, total),
this.directoryManager.getActualStoragePathFor(chunk), this.directoryManager.getActualStoragePathFor(chunk),
chunk.getMd5(), chunk.getMd5(),
String.format(LOCALE, "%s?chunk=%s", this.server.getPage("download-chunk"), chunk.getId()) String.format(LOCALE, "%s?chunk=%s", this.server.getPage("download-chunk"), chunk.getId())
@@ -839,11 +837,11 @@ import okhttp3.HttpUrl;
} }
protected Error.Type downloadExecutable(Job ajob) throws SheepItException { protected Error.Type downloadExecutable(Job ajob) throws SheepItException {
this.gui.getDownloadProgress().reset("Downloading Blender");
return (new DownloadManager( return (new DownloadManager(
this.server, this.server,
this.gui, this.gui,
this.log, this.log,
"renderer",
this.directoryManager.getActualStorageBinaryPathFor(ajob), this.directoryManager.getActualStorageBinaryPathFor(ajob),
ajob.getRendererMD5(), ajob.getRendererMD5(),
String.format(LOCALE, "%s?job=%s", this.server.getPage("download-binary"), ajob.getId()) String.format(LOCALE, "%s?job=%s", this.server.getPage("download-binary"), ajob.getId())

View File

@@ -36,15 +36,13 @@ public class DownloadManager {
private Log log; private Log log;
// task specific objects // task specific objects
private String gui_text; // what do display on the gui
private String local_target; private String local_target;
private String md5; // expected md5 of the file, for check purpose private String md5; // expected md5 of the file, for check purpose
private String remote; // remote url private String remote; // remote url
public DownloadManager(Server server, Gui gui, Log log, String gui_text, String local_target, String md5, String remote) { public DownloadManager(Server server, Gui gui, Log log, String local_target, String md5, String remote) {
this.server = server; this.server = server;
this.gui = gui; this.gui = gui;
this.gui_text = gui_text;
this.log = log; this.log = log;
this.local_target = local_target; this.local_target = local_target;
this.md5 = md5; this.md5 = md5;
@@ -61,15 +59,14 @@ public class DownloadManager {
do { do {
// if the binary or scene already exists in the cache // if the binary or scene already exists in the cache
if (local_path_file.exists()) { if (local_path_file.exists()) {
this.gui.status("Reusing cached " + this.gui_text); this.gui.status("Reusing cached");
return Error.Type.OK; return Error.Type.OK;
} }
// if the binary or scene is being downloaded by another client // if the binary or scene is being downloaded by another client
else if (this.lockExists()) { else if (this.lockExists()) {
// Wait and check every second for file download completion but only update the GUI every 10 seconds to minimise CPU load // Wait and check every second for file download completion but only update the GUI every 10 seconds to minimise CPU load
if (remaining % 10000 == 0) { if (remaining % 10000 == 0) {
this.gui.status(String.format("Another client is downloading the %s. Cancel in %dmin %ds", this.gui.status(String.format("Another client is downloading. Cancel in %dmin %ds",
this.gui_text,
TimeUnit.MILLISECONDS.toMinutes(remaining), TimeUnit.MILLISECONDS.toMinutes(remaining),
TimeUnit.MILLISECONDS.toSeconds(remaining) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(remaining)) TimeUnit.MILLISECONDS.toSeconds(remaining) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(remaining))
)); ));
@@ -100,16 +97,14 @@ public class DownloadManager {
} }
} }
this.gui.status(String.format("Downloading %s", this.gui_text)); this.gui.status(String.format("Downloading"));
return this.downloadActual(); return this.downloadActual();
} }
private Error.Type downloadActual() throws SheepItException { private Error.Type downloadActual() throws SheepItException {
String update_ui = "Downloading " + this.gui_text;
// must download the archive // must download the archive
Error.Type ret = this.server.HTTPGetFile(this.remote, this.local_target, this.gui, update_ui); Error.Type ret = this.server.HTTPGetFile(this.remote, this.local_target, this.gui);
if (ret == Error.Type.RENDERER_KILLED_BY_SERVER || ret == Error.Type.RENDERER_KILLED_BY_USER_OVER_TIME || ret == Error.Type.RENDERER_KILLED_BY_USER) { if (ret == Error.Type.RENDERER_KILLED_BY_SERVER || ret == Error.Type.RENDERER_KILLED_BY_USER_OVER_TIME || ret == Error.Type.RENDERER_KILLED_BY_USER) {
return ret; return ret;
@@ -121,11 +116,11 @@ public class DownloadManager {
while ((ret != Error.Type.OK || md5_check == false) && attempts < this.maxDownloadFileAttempts) { while ((ret != Error.Type.OK || md5_check == false) && attempts < this.maxDownloadFileAttempts) {
if (ret != Error.Type.OK) { if (ret != Error.Type.OK) {
this.gui.error(String.format("Unable to download %s (error %s). Retrying now", this.gui_text, ret)); this.gui.error(String.format("Unable to download (error %s). Retrying now", ret));
this.log.debug("DownloadManager::downloadActual problem with Server.HTTPGetFile (return: " + ret + ") removing local file (path: " + this.local_target + ")"); this.log.debug("DownloadManager::downloadActual problem with Server.HTTPGetFile (return: " + ret + ") removing local file (path: " + this.local_target + ")");
} }
else if (md5_check == false) { else if (md5_check == false) {
this.gui.error(String.format("Verification of downloaded %s has failed. Retrying now", this.gui_text)); this.gui.error(String.format("Verification of downloaded %s has failed. Retrying now"));
this.log.debug("DownloadManager::downloadActual problem with Client::checkFile mismatch on md5, removing local file (path: " + this.local_target + ")"); this.log.debug("DownloadManager::downloadActual problem with Client::checkFile mismatch on md5, removing local file (path: " + this.local_target + ")");
} }
@@ -134,7 +129,7 @@ public class DownloadManager {
this.log.debug("DownloadManager::downloadActual failed, let's try again (" + (attempts + 1) + "/" + this.maxDownloadFileAttempts + ") ..."); this.log.debug("DownloadManager::downloadActual failed, let's try again (" + (attempts + 1) + "/" + this.maxDownloadFileAttempts + ") ...");
String partial_target = this.local_target + ".partial"; String partial_target = this.local_target + ".partial";
ret = this.server.HTTPGetFile(this.remote, partial_target, this.gui, update_ui); ret = this.server.HTTPGetFile(this.remote, partial_target, this.gui);
md5_check = this.check(); md5_check = this.check();
attempts++; attempts++;

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2024 Laurent CLOUET
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.sheepit.client;
public class DownloadProgress {
// global objects
private Gui gui;
private String guiPattern = "";
private long total = 0;
private long partial = 0;
public DownloadProgress(Gui gui) {
this.gui = gui;
}
public synchronized void reset(String pattern) {
this.guiPattern = pattern;
this.total = 0;
this.partial = 0;
}
public synchronized void addTotal(long size) {
this.total += size;
}
public synchronized void addProgress(long progress) {
this.partial += progress;
gui.status(String.format(this.guiPattern + " %.0f %%", 100.0f * this.partial / this.total));
}
}

View File

@@ -30,8 +30,6 @@ public interface Gui {
public void status(String msg_, int progress); public void status(String msg_, int progress);
public void status(String msg_, int progress, long size);
public void updateTrayIcon(Integer percentage_); public void updateTrayIcon(Integer percentage_);
public void setRenderingProjectName(String name_); public void setRenderingProjectName(String name_);
@@ -57,4 +55,6 @@ public interface Gui {
public void setComputeMethod(String computeMethod_); public void setComputeMethod(String computeMethod_);
public Client getClient(); public Client getClient();
public DownloadProgress getDownloadProgress();
} }

View File

@@ -491,7 +491,7 @@ public class Server extends Thread {
} }
} }
public Error.Type HTTPGetFile(String url_, String destination_, Gui gui_, String status_) throws SheepItException { public Error.Type HTTPGetFile(String url_, String destination_, Gui gui_) throws SheepItException {
this.log.debug("Server::HTTPGetFile destination: " + destination_); this.log.debug("Server::HTTPGetFile destination: " + destination_);
InputStream is = null; InputStream is = null;
OutputStream output = null; OutputStream output = null;
@@ -508,10 +508,14 @@ public class Server extends Thread {
output = new FileOutputStream(destination_ + ".partial"); output = new FileOutputStream(destination_ + ".partial");
long size = response.body().contentLength(); long size = response.body().contentLength();
byte[] buffer = new byte[8 * 1024]; // only update the gui every 1MB
byte[] buffer = new byte[1024 * 1024];
int len = 0; int len = 0;
long written = 0; long written = 0;
long lastUpd = 0; // last GUI progress update
if (size != -1) {
gui_.getDownloadProgress().addTotal(size);
}
this.log.debug("Downloading file from " + response.request().url().host()); this.log.debug("Downloading file from " + response.request().url().host());
LocalDateTime startRequestTime = LocalDateTime.now(); LocalDateTime startRequestTime = LocalDateTime.now();
@@ -526,20 +530,13 @@ public class Server extends Thread {
output.write(buffer, 0, len); output.write(buffer, 0, len);
written += len; written += len;
gui_.getDownloadProgress().addProgress(len);
if ((written - lastUpd) > 1000000) { // only update the gui every 1MB
if (size != -1) { // no header for contentlength
gui_.status(status_, (int) (100.0 * written / size), written);
}
lastUpd = written;
}
} }
LocalDateTime endRequestTime = LocalDateTime.now(); LocalDateTime endRequestTime = LocalDateTime.now();
Duration duration = Duration.between(startRequestTime, endRequestTime); Duration duration = Duration.between(startRequestTime, endRequestTime);
this.dlStats.calc(written, ((duration.getSeconds() * 1000) + (duration.getNano() / 1000000))); this.dlStats.calc(written, ((duration.getSeconds() * 1000) + (duration.getNano() / 1000000)));
gui_.displayTransferStats(dlStats, ulStats); gui_.displayTransferStats(dlStats, ulStats);
gui_.status(status_, 100, size);
this.log.debug(String.format("File downloaded at %s/s, written %d bytes", new TransferStats(size, duration.toMillis() + 1).getAverageSessionSpeed(), written)); this.log.debug(String.format("File downloaded at %s/s, written %d bytes", new TransferStats(size, duration.toMillis() + 1).getAverageSessionSpeed(), written));

View File

@@ -24,6 +24,7 @@ import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.FlatLightLaf;
import com.sheepit.client.Client; import com.sheepit.client.Client;
import com.sheepit.client.Configuration; import com.sheepit.client.Configuration;
import com.sheepit.client.DownloadProgress;
import com.sheepit.client.Gui; import com.sheepit.client.Gui;
import com.sheepit.client.SettingsLoader; import com.sheepit.client.SettingsLoader;
import com.sheepit.client.Stats; import com.sheepit.client.Stats;
@@ -132,6 +133,7 @@ public class GuiSwing extends JFrame implements Gui {
private boolean waitingForAuthentication; private boolean waitingForAuthentication;
private Client client; private Client client;
private DownloadProgress downloadProgress;
private BufferedImage iconSprites; private BufferedImage iconSprites;
private BufferedImage[] trayIconSprites; private BufferedImage[] trayIconSprites;
@@ -141,6 +143,7 @@ public class GuiSwing extends JFrame implements Gui {
private ThreadClient threadClient; private ThreadClient threadClient;
public GuiSwing(boolean useSysTray_, String title_) { public GuiSwing(boolean useSysTray_, String title_) {
downloadProgress = new DownloadProgress(this);
framesRendered = 0; framesRendered = 0;
useSysTray = useSysTray_; useSysTray = useSysTray_;
title = title_; title = title_;
@@ -272,10 +275,6 @@ public class GuiSwing extends JFrame implements Gui {
} }
} }
@Override public void status(String msg, int progress, long size) {
this.status(msg, progress);
}
@Override public void setRenderingProjectName(String name_) { @Override public void setRenderingProjectName(String name_) {
if (activityWorking != null) { if (activityWorking != null) {
this.activityWorking.setRenderingProjectName(name_); this.activityWorking.setRenderingProjectName(name_);
@@ -329,6 +328,10 @@ public class GuiSwing extends JFrame implements Gui {
return client; return client;
} }
@Override public DownloadProgress getDownloadProgress() {
return downloadProgress;
}
@Override public void setClient(Client cli) { @Override public void setClient(Client cli) {
client = cli; client = cli;
} }
@@ -496,5 +499,4 @@ public class GuiSwing extends JFrame implements Gui {
} }
} }
} }
} }

View File

@@ -20,6 +20,7 @@
package com.sheepit.client.standalone; package com.sheepit.client.standalone;
import com.sheepit.client.Client; import com.sheepit.client.Client;
import com.sheepit.client.DownloadProgress;
import com.sheepit.client.Gui; import com.sheepit.client.Gui;
import com.sheepit.client.Log; import com.sheepit.client.Log;
import com.sheepit.client.Stats; import com.sheepit.client.Stats;
@@ -46,8 +47,10 @@ public class GuiText implements Gui {
private String eta; private String eta;
private Client client; private Client client;
private DownloadProgress downloadProgress;
public GuiText() { public GuiText() {
this.downloadProgress = new DownloadProgress(this);
this.framesRendered = 0; this.framesRendered = 0;
this.log = Log.getInstance(null); this.log = Log.getInstance(null);
this.df = new SimpleDateFormat("MMM dd HH:mm:ss"); this.df = new SimpleDateFormat("MMM dd HH:mm:ss");
@@ -117,12 +120,8 @@ public class GuiText implements Gui {
} }
@Override public void status(String msg, int progress) { @Override public void status(String msg, int progress) {
this.status(msg, progress, 0);
}
@Override public void status(String msg, int progress, long size) {
System.out.print("\r"); System.out.print("\r");
System.out.print(String.format("%s %s", this.df.format(new Date()), showProgress(msg, progress, size))); System.out.print(String.format("%s %s", this.df.format(new Date()), showProgress(msg, progress)));
} }
@Override public void error(String err_) { @Override public void error(String err_) {
@@ -178,11 +177,15 @@ public class GuiText implements Gui {
return client; return client;
} }
@Override public DownloadProgress getDownloadProgress() {
return downloadProgress;
}
@Override public void successfulAuthenticationEvent(String publickey) { @Override public void successfulAuthenticationEvent(String publickey) {
} }
private String showProgress(String message, int progress, long size) { private String showProgress(String message, int progress) {
StringBuilder progressBar = new StringBuilder(140); StringBuilder progressBar = new StringBuilder(140);
if (progress < 100) { if (progress < 100) {
@@ -196,10 +199,6 @@ public class GuiText implements Gui {
.append(String.join("", Collections.nCopies(20 - (int)(progress / 5), " "))) .append(String.join("", Collections.nCopies(20 - (int)(progress / 5), " ")))
.append(']'); .append(']');
if (size > 0) {
progressBar.append(String.format(" %dMB", (size / 1024 / 1024)));
}
if (!this.eta.equals("")) { if (!this.eta.equals("")) {
progressBar.append(String.format(" ETA %s", this.eta)); progressBar.append(String.format(" ETA %s", this.eta));
} }

View File

@@ -20,6 +20,7 @@
package com.sheepit.client.standalone; package com.sheepit.client.standalone;
import com.sheepit.client.Client; import com.sheepit.client.Client;
import com.sheepit.client.DownloadProgress;
import com.sheepit.client.Gui; import com.sheepit.client.Gui;
import com.sheepit.client.Stats; import com.sheepit.client.Stats;
import com.sheepit.client.TransferStats; import com.sheepit.client.TransferStats;
@@ -55,8 +56,10 @@ public class GuiTextOneLine implements Gui {
private boolean exiting = false; private boolean exiting = false;
private Client client; private Client client;
private DownloadProgress downloadProgress;
public GuiTextOneLine() { public GuiTextOneLine() {
downloadProgress = new DownloadProgress(this);
project = ""; project = "";
rendered = 0; rendered = 0;
remaining = 0; remaining = 0;
@@ -118,22 +121,19 @@ public class GuiTextOneLine implements Gui {
@Override public void status(String msg_, boolean overwriteSuspendedMsg) { @Override public void status(String msg_, boolean overwriteSuspendedMsg) {
if (client != null && client.isSuspended()) { if (client != null && client.isSuspended()) {
if (overwriteSuspendedMsg) { if (overwriteSuspendedMsg) {
status = msg_; status = msg_.replace("%", "%%"); // escape %
updateLine(); updateLine();
} }
} }
else { else {
status = msg_; status = msg_.replace("%", "%%"); // escape %
updateLine(); updateLine();
} }
} }
@Override public void status(String msg, int progress) { @Override public void status(String msg, int progress) {
this.status(msg, progress, 0); status = showProgress(msg, progress);
}
@Override public void status(String msg, int progress, long size) {
status = showProgress(msg, progress, size);
updateLine(); updateLine();
} }
@@ -186,6 +186,10 @@ public class GuiTextOneLine implements Gui {
client = cli; client = cli;
} }
@Override public DownloadProgress getDownloadProgress() {
return downloadProgress;
}
@Override public void setComputeMethod(String computeMethod_) { @Override public void setComputeMethod(String computeMethod_) {
computeMethod = computeMethod_; computeMethod = computeMethod_;
} }
@@ -224,7 +228,7 @@ public class GuiTextOneLine implements Gui {
} }
} }
private String showProgress(String message, int progress, long size) { private String showProgress(String message, int progress) {
StringBuilder progressBar = new StringBuilder(140); StringBuilder progressBar = new StringBuilder(140);
progressBar progressBar
.append(message) .append(message)
@@ -235,10 +239,6 @@ public class GuiTextOneLine implements Gui {
.append(String.join("", Collections.nCopies(10 - (int) (progress / 10), " "))) .append(String.join("", Collections.nCopies(10 - (int) (progress / 10), " ")))
.append(']'); .append(']');
if (size > 0) {
progressBar.append(String.format(" %dMB", (size / 1024 / 1024)));
}
if (!this.eta.equals("")) { if (!this.eta.equals("")) {
progressBar.append(String.format(" ETA %s", this.eta)); progressBar.append(String.format(" ETA %s", this.eta));
} }