Skip to content

Instantly share code, notes, and snippets.

@OlegIlyenko
Created April 14, 2012 22:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OlegIlyenko/2388377 to your computer and use it in GitHub Desktop.
Save OlegIlyenko/2388377 to your computer and use it in GitHub Desktop.
package scaldi.keys
import math._
import swing._
import java.io.{Closeable, InputStream, FilterInputStream}
import javax.swing.UIManager
import java.net.URL
object Downloader extends SimpleSwingApplication {
UIManager.getInstalledLookAndFeels
.filter(_.getName contains "Nimbus").headOption
.map(_.getClassName).foreach(UIManager setLookAndFeel _)
val url = new TextField(35)
val progress = new ProgressBar
val info = new Label("Please start download")
def top = new MainFrame {
title = "File Downloader"
contents = new BoxPanel(Orientation.Vertical) {
contents += new FlowPanel(FlowPanel.Alignment.Right)(
new Label("URL:"),
url,
Button("Download") {
import Util._
thread {
val conn = new URL(url.text).openConnection
val tracker = (p: Progress) => Swing.onEDT {
info.text = "<html>" + p.formattedMetrics.mkString("<br>") +"</html>"
progress.value = p.percent
}
withResource(ProgressInputStream(conn.getInputStream, conn.getContentLength, tracker)) { in =>
val buf = new Array[Byte](1024)
while(in.read(buf) != -1) {
println("Reading 1 kb")
}
}
}
}
)
contents += progress
contents += new FlowPanel(FlowPanel.Alignment.Left)(info)
}
size = new Dimension(550, 250)
centerOnScreen()
}
}
object Util {
def thread(fn: => Unit) = {
val thread = new Thread(Swing.Runnable(fn))
thread.start()
thread
}
def withResource[T <: Closeable, R](res: T)(fn: T => R) =
try {
fn(res)
} finally {
try {
res.close()
} catch {
case e: Exception => e.printStackTrace()
}
}
}
class ProgressInputStream(in: InputStream, listener: Long => Unit) extends FilterInputStream(in) {
val NotificationThreshold = 8 * 1024;
var unnotifiedByteCount = 0
override def read() = {
val data = super.read()
if (data != -1) notify(1)
data
}
override def read(b: Array[Byte], off: Int, len: Int) = {
val bytesRead = super.read(b, off, len)
if (bytesRead != -1) notify(bytesRead)
bytesRead
}
override def close() {
if (unnotifiedByteCount > 0) {
listener(unnotifiedByteCount)
unnotifiedByteCount = 0
}
super.close()
}
def notify(bytesRead: Int) {
unnotifiedByteCount += bytesRead
if (unnotifiedByteCount >= NotificationThreshold) {
listener(unnotifiedByteCount)
unnotifiedByteCount = 0
}
}
}
object ProgressInputStream {
def apply(in: InputStream, availableBytes: Long, tracker: Progress => Unit) =
new ProgressInputStream(in, new ProgressListener(availableBytes, tracker))
}
case class Progress(percent: Int, size: Long, remains: Long, done: Long, bps: Long, elapsed: Long, estimated: Long) {
lazy val formattedMetrics= List(
"Progress: " + percent + "%",
"File size: " + formatSize(size),
"Downloaded: " + formatSize(done),
"Remains: " + formatSize(remains),
"Speed: " + (bps / 1024) + " kb/s",
"Elapsed: " + formatTime(elapsed),
"Estimated: " + formatTime(estimated)
)
def formatTime(time: Long) = {
val oneSecond = 1000
val oneMinute = 60 * oneSecond
val oneHour = 60 * oneMinute
val hours = (time / oneHour).toInt
val minutes = ((time - hours * oneHour) / oneMinute).toInt
val seconds = ((time - hours * oneHour - minutes * oneMinute) / oneSecond).toInt
val fmt = "%02d"
List(hours, minutes, seconds) map (fmt format _) mkString ":"
}
def formatSize(bytes: Long) =
if (bytes < 1024)
bytes + " B"
else {
val exp = (log(bytes) / log(1024)).toInt
"%.1f %sB" format (bytes / pow(1024, exp), "KMGTPE" charAt (exp - 1))
}
}
class ProgressListener(availableBytes: Long, tracker: Progress => Unit) extends Function1[Long, Unit] {
var transfered: Long = 0
val startTime = System.currentTimeMillis
def apply(bytesTransfered: Long) {
transfered += bytesTransfered
val elapsed = System.currentTimeMillis - startTime
val bpms = (transfered / max(elapsed, 1000)).toLong
val bps = (transfered / max(elapsed / 1000, 1)).toLong
tracker(Progress(
percent = (transfered * 100 / availableBytes).toInt min 100,
size = availableBytes,
remains = availableBytes - transfered,
done = transfered,
bps = bps,
elapsed = elapsed,
estimated = (availableBytes - transfered) / max(bpms, 1)
))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment