diff --git a/core/src/main/groovy/com/muwire/core/download/Downloader.groovy b/core/src/main/groovy/com/muwire/core/download/Downloader.groovy index 596db7cb..1af33ea4 100644 --- a/core/src/main/groovy/com/muwire/core/download/Downloader.groovy +++ b/core/src/main/groovy/com/muwire/core/download/Downloader.groovy @@ -276,6 +276,57 @@ public class Downloader { activeWorkers.put(d, newWorker) executorService.submit(newWorker) } + + boolean isSequential() { + pieces.ratio == 0f + } + + File generatePreview() { + int lastCompletePiece = pieces.firstIncomplete() - 1 + if (lastCompletePiece == -1) + return null + if (lastCompletePiece < -1) + return file + long previewableLength = (lastCompletePiece + 1) * ((long)pieceSize) + + // generate name + long now = System.currentTimeMillis() + File previewFile + File parentFile = file.getParentFile() + int lastDot = file.getName().lastIndexOf('.') + if (lastDot < 0) + previewFile = new File(parentFile, file.getName() + "." + String.valueOf(now) + ".mwpreview") + else { + String name = file.getName().substring(0, lastDot) + String extension = file.getName().substring(lastDot + 1) + String previewName = name + "." + String.valueOf(now) + ".mwpreview."+extension + previewFile = new File(parentFile, previewName) + } + + // copy + InputStream is = null + OutputStream os = null + try { + is = new BufferedInputStream(new FileInputStream(incompleteFile)) + os = new BufferedOutputStream(new FileOutputStream(previewFile)) + byte [] tmp = new byte[0x1 << 13] + long totalCopied = 0 + while(totalCopied < previewableLength) { + int read = is.read(tmp, 0, (int)Math.min(tmp.length, previewableLength - totalCopied)) + if (read < 0) + throw new IOException("EOF?") + os.write(tmp, 0, read) + totalCopied += read + } + return previewFile + } catch (IOException bad) { + log.log(Level.WARNING,"Preview failed",bad) + return null + } finally { + try {is?.close() } catch (IOException ignore) {} + try {os?.close() } catch (IOException ignore) {} + } + } class DownloadWorker implements Runnable { private final Destination destination diff --git a/core/src/main/groovy/com/muwire/core/download/Pieces.groovy b/core/src/main/groovy/com/muwire/core/download/Pieces.groovy index ad81f2a0..4d4a9d7e 100644 --- a/core/src/main/groovy/com/muwire/core/download/Pieces.groovy +++ b/core/src/main/groovy/com/muwire/core/download/Pieces.groovy @@ -108,6 +108,10 @@ class Pieces { partials.clear() } + synchronized int firstIncomplete() { + done.nextClearBit(0) + } + synchronized void write(PrintWriter writer) { for (int i = done.nextSetBit(0); i >= 0; i = done.nextSetBit(i+1)) { writer.println(i) diff --git a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy index 6e40d929..53ab0f1a 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy @@ -204,6 +204,16 @@ class MainFrameController { downloader.pause() core.eventBus.publish(new UIDownloadPausedEvent()) } + + @ControllerAction + void preview() { + def downloader = model.downloads[selectedDownload()].downloader + File previewFile = downloader.generatePreview() + if (previewFile != null) + Desktop.getDesktop().open(previewFile) + else + JOptionPane.showMessageDialog(null, "Failed to generate preview", "Failed to generate preveiw", JOptionPane.ERROR_MESSAGE) + } @ControllerAction void clear() { diff --git a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy index d9ad5d40..74fb4bb4 100644 --- a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy @@ -216,6 +216,7 @@ class MainFrameView { button(text: "Pause", enabled : bind {model.pauseButtonEnabled}, pauseAction) button(text: bind { model.resumeButtonText }, enabled : bind {model.retryButtonEnabled}, resumeAction) button(text: "Cancel", enabled : bind {model.cancelButtonEnabled }, cancelAction) + button(text: "Preview", previewAction) button(text: "Clear Done", enabled : bind {model.clearButtonEnabled}, clearAction) } }