Skip to content
This repository has been archived by the owner on Feb 12, 2021. It is now read-only.

Commit

Permalink
[Add & Change] 爬虫gui的下载部分也成功完成, 以及其他代码的变动
Browse files Browse the repository at this point in the history
  • Loading branch information
MoonLake committed Aug 1, 2017
1 parent feef07b commit 3fa7d18
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 9 deletions.
23 changes: 23 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>

Expand Down Expand Up @@ -79,6 +80,28 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>jar-with-dependencies</descriptorRefs>
<archive>
<manifest>
<addClasspath>false</addClasspath>
<mainClass>com.minecraft.moonlake.optifinecrawler.Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>mark-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ open class OptifineCrawler {
*
**************************************************************************/

constructor(factory: HttpRequestFactory = optifineFactory()): this("http://optifine.net", factory)
constructor(url: String = "http://optifine.net", factory: HttpRequestFactory = optifineFactory()) {
this.url = url
this.factory = factory
Expand Down Expand Up @@ -104,6 +105,14 @@ open class OptifineCrawler {
*/
@Throws(RuntimeException::class, IllegalStateException::class)
open fun downloadOptifine(optifineVer: OptifineVersion, out: File) {
factory.requestDownload(parseResultFileUrl(optifineVer), out)
}

/**
* 解析目标 Optifine 版本的结果文件链接
*/
@Throws(RuntimeException::class, IllegalStateException::class)
open fun parseResultFileUrl(optifineVer: OptifineVersion): String {
if(optifineVer.isEmpty() || optifineVer.downloadMirror == null)
throw IllegalStateException("目标 Optifine 版本对象信息为空或下载镜像为 null 值.")

Expand All @@ -118,8 +127,7 @@ open class OptifineCrawler {
val matcher = Pattern.compile("\"downloadx\\?f=${if(optifineVer.preview) "preview_" else ""}OptiFine(.*)\"").matcher(content)
while (matcher.find())
target = "http://optifine.net/downloadx?f=${if(optifineVer.preview) "preview_" else ""}OptiFine${matcher.group(1)}"
// 解析成功后完成最后的下载任务
factory.requestDownload(target, out)
return target
} catch (e: Exception) {
throw RuntimeException(e)
}
Expand All @@ -141,7 +149,7 @@ open class OptifineCrawler {
/**
* OptifineCrawler 的默认 Http 请求工厂
*/
private fun optifineFactory(): HttpRequestFactory {
fun optifineFactory(): HttpRequestFactory {
return object: HttpRequestFactory {
@Throws(Exception::class)
override fun requestGet(url: String): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,32 @@

package com.minecraft.moonlake.optifinecrawler.gui

import com.minecraft.moonlake.optifinecrawler.HttpRequestFactory
import com.minecraft.moonlake.optifinecrawler.OptifineCrawler
import com.minecraft.moonlake.optifinecrawler.OptifineVersion
import javafx.application.Application
import javafx.application.Platform
import javafx.beans.binding.Bindings
import javafx.concurrent.ScheduledService
import javafx.concurrent.Task
import javafx.concurrent.Worker
import javafx.geometry.Insets
import javafx.scene.Parent
import javafx.scene.Scene
import javafx.scene.control.Button
import javafx.scene.control.TableColumn
import javafx.scene.control.TableView
import javafx.scene.control.*
import javafx.scene.layout.BorderPane
import javafx.scene.layout.HBox
import javafx.stage.Stage
import javafx.util.Callback
import javafx.util.Duration
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.RandomAccessFile
import java.net.HttpURLConnection
import java.net.URL
import java.text.DecimalFormat
import java.util.*
import java.util.concurrent.Callable

class OptifineCrawlerGui: Application() {
Expand All @@ -57,7 +69,8 @@ class OptifineCrawlerGui: Application() {
val author = "Month_Light"
val title = "OptifineCrawler $version by $author - https://github.com/lgou2w/OptifineCrawler"

val optifineCrawler = OptifineCrawler()
// private member
private val optifineCrawler = OptifineCrawler(GuiRequestFactory()) // 使用 GUI 的请求工厂

/**************************************************************************
*
Expand All @@ -84,8 +97,13 @@ class OptifineCrawlerGui: Application() {
private val tablePreview = TableColumn<OptifineVersion, Boolean>("Preview")
private val tableVersion = TableColumn<OptifineVersion, String>("Version")
private val tableDate = TableColumn<OptifineVersion, String>("Date")
private val requestVerList = Button("Get Version List")
private val downloadSelected = Button("> Download Selected Item <")
private val requestVerList = Button("> Get The Version List <")
private val downloadLabel = Label("No Download")
private val downloadProgress = ProgressBar()
private val downloadGroup = HBox()
private val rootPane = BorderPane()
private val btnGroup = HBox()

/**************************************************************************
*
Expand All @@ -94,13 +112,22 @@ class OptifineCrawlerGui: Application() {
**************************************************************************/

private fun createUserGui(): Parent {
val btnGroup = HBox(10.0, requestVerList)
btnGroup.spacing = 10.0
btnGroup.padding = Insets(10.0, .0, 10.0, 10.0)
downloadGroup.spacing = 10.0
downloadProgress.setPrefSize(120.0, downloadProgress.prefHeight)
downloadGroup.padding = Insets(2.5, .0, 2.5, 5.0)
downloadGroup.children.setAll(downloadLabel, downloadProgress)
btnGroup.children.setAll(requestVerList, downloadSelected, downloadGroup)
tableDownload.cellValueFactory = Callback { it -> Bindings.createStringBinding(Callable { it.value.downloadMirror }) }
tablePreview.cellValueFactory = Callback { it -> Bindings.createBooleanBinding(Callable { it.value.preview }) }
tableVersion.cellValueFactory = Callback { it -> Bindings.createStringBinding(Callable { it.value.version }) }
tableDate.cellValueFactory = Callback { it -> Bindings.createStringBinding(Callable { it.value.date }) }
tableView.columns.addAll(tableVersion, tableDate, tableDownload, tablePreview)
tableDownload.prefWidth = 250.0
tablePreview.prefWidth = 80.0
tableVersion.prefWidth = 200.0
tableDate.prefWidth = 80.0
rootPane.center = tableView
rootPane.bottom = btnGroup
initListener()
Expand All @@ -111,6 +138,176 @@ class OptifineCrawlerGui: Application() {
requestVerList.setOnAction { _ -> run {
val verList = optifineCrawler.requestVersionList()
tableView.items.setAll(verList)
println("Optifine Crawler Version List Size: ${verList.size}")
}}
downloadSelected.setOnAction { _ -> run {
val index = tableView.selectionModel.selectedIndex
if(index == -1) {
showMessage(Alert.AlertType.WARNING, "Please check the download and try again.", "Error:", ButtonType.OK)
} else {
val item = tableView.items[index]
println("Start Download: " + item.version)
downloadVer(item)
}
}}
}

private fun downloadVer(optifineVer: OptifineVersion, disableBtnGroup: Boolean = true) {
val finalFile = File(System.getProperty("user.dir"), "${optifineVer.version}.jar")
val task = (optifineCrawler.factory as GuiRequestFactory).requestDownloadTask(optifineVer, finalFile)
task.stateProperty().addListener { _, _, newValue -> run {
when(newValue) {
Worker.State.SCHEDULED -> disableBtnGroupButton(disableBtnGroup)
Worker.State.CANCELLED, Worker.State.FAILED, Worker.State.SUCCEEDED -> releaseDownloadGroup()
else -> { }
}
}}
downloadProgress.progressProperty().bind(task.progressProperty())
downloadLabel.textProperty().bind(task.messageProperty())
Thread(task).start()
}

private fun releaseDownloadGroup() {
val service = object: ScheduledService<Unit>() {
override fun createTask(): Task<Unit> {
return object: Task<Unit>() {
override fun call() {
Platform.runLater {
disableBtnGroupButton(false)
downloadProgress.progressProperty().unbind()
downloadProgress.progress = -1.0
downloadLabel.textProperty().unbind()
downloadLabel.text = "No Download"
}
}
}
}
}
service.delay = Duration.seconds(3.0)
service.start()
}

private fun disableBtnGroupButton(state: Boolean) {
btnGroup.children.filter { it is Button }.forEach { it.isDisable = state }
}

private fun showMessage(alertType: Alert.AlertType, message: String, title: String, vararg buttons: ButtonType): Optional<ButtonType> {
val alert = Alert(alertType, message, *buttons)
alert.title = title
alert.graphic = null
alert.headerText = null
return alert.showAndWait()
}

private inner class GuiRequestFactory: HttpRequestFactory {
// 持有一个默认的请求工厂
private val defOptifineFactory = OptifineCrawler.optifineFactory()
private val rounding = DecimalFormat("#.00")

@Throws(Exception::class)
override fun requestGet(url: String): String {
return defOptifineFactory.requestGet(url)
}

@Throws(Exception::class)
override fun requestDownload(url: String, out: File) {
defOptifineFactory.requestDownload(url, out)
}

fun requestDownloadTask(optifineVer: OptifineVersion, out: File): Task<Unit> {
// 这个函数的话就是通过自己实现返回一个 task 对象
// 拥有进度属性和消息属性
return object: Task<Unit>() {
override fun call() {
updateMessage("Downloading...")
val url = optifineCrawler.parseResultFileUrl(optifineVer)
var input: InputStream? = null
var access: RandomAccessFile? = null
try {
val connection = URL(url).openConnection() as HttpURLConnection
connection.doInput = true
connection.connectTimeout = 15000
connection.readTimeout = 15000
connection.addRequestProperty("User-Agent", "MoonLake OptifineCrawler by lgou2w")
connection.connect()
if(connection.responseCode / 100 != 2)
throw IOException("请求的目标响应码不为 200, 当前: ${connection.responseCode}.")
val contentLength = connection.contentLength.toLong()
if(contentLength < 1)
throw IOException("请求的目标内容长度无效.")
if(!(out.parentFile.isDirectory || out.mkdirs()))
throw IOException("无法创建目录文件.")
val tmp = File(out.absolutePath + ".mldltmp")
if(!tmp.exists())
tmp.createNewFile()
else if(!tmp.renameTo(tmp))
throw IllegalStateException("临时文件处于锁状态, 请检测是否被其他进程占用.")
input = connection.inputStream
access = RandomAccessFile(tmp, "rw")
val buffer: ByteArray = ByteArray(1024)
var length = 0; var downloaded = 0L
var lastDownloaded = 0L
var lastTime = System.currentTimeMillis()
while (input.read(buffer).apply { length = this } != -1) {
access.write(buffer, 0, length)
downloaded += length
val now = System.currentTimeMillis()
if(now - lastTime >= 1000) {
updateProgress(downloaded, contentLength)
updateMessage(formatDownloadSpeed(downloaded, lastDownloaded))
lastDownloaded = downloaded
lastTime = now
}
}
if(input != null) try {
input.close()
input = null
} catch (e: Exception) {
}
if(access != null) try {
access.close()
access = null
} catch (e: Exception) {
}
if(out.exists())
out.delete()
tmp.renameTo(out)
} catch (e: Exception) {
out.delete()
throw e
} finally {
if(input != null) try {
input.close()
} catch (e: Exception) {
}
if(access != null) try {
access.close()
} catch (e: Exception) {
}
}
}

override fun succeeded() {
updateProgress(1.0, 1.0)
updateMessage("Download completed.")
}

override fun failed() {
updateProgress(.0, 1.0)
updateMessage("Download failed.")
}

override fun done() {
println("Task Done.")
}

private fun formatDownloadSpeed(downloaded: Long, lastDownloaded: Long): String {
val kilobyte = (downloaded - lastDownloaded) / 1024.0
if(kilobyte <= 1024.0)
return "Speed: ${rounding.format(kilobyte)}KB/s"
return "Speed: ${rounding.format(kilobyte / 1024.0)}MB/s"
}
}
}
}
}

0 comments on commit 3fa7d18

Please sign in to comment.