Commit b155bddd authored by wudizhanche1000's avatar wudizhanche1000

下载安装步骤

parent 6a33d0bc
......@@ -59,13 +59,13 @@
<tr *ngFor="let mod of mods; let i = index">
<th scope="row">{{i + 1}}</th>
<td>{{mod.name}}</td>
<td *ngIf="mod.isInstalled()">
<td *ngIf="mod.isReady()">
<button i18n type="button" (click)="uninstall(mod)" class="btn btn-danger btn-sm">卸载</button>
</td>
<td *ngIf="!mod.isInstalled()">
<button i18n (click)="installMod(mod)" type="button" *ngIf="mod.status.status==='init'" class="btn btn-primary btn-sm">安装</button>
<progress *ngIf="mod.status.status==='downloading'" class="progress progress-striped progress-animated" value="{{mod.status.progress}}" max="{{mod.status.total}}"></progress>
<div i18n *ngIf="mod.status.status==='waiting'">等待安装...</div>
<button i18n (click)="installMod(mod)" type="button" *ngIf="!mod.isInstalled()" class="btn btn-primary btn-sm">安装</button>
<progress *ngIf="mod.isDownloading()" class="progress progress-striped progress-animated" value="{{mod.status.progress}}" max="{{mod.status.total}}"></progress>
<div i18n *ngIf="mod.isWaiting()">等待安装...</div>
</td>
</tr>
</tbody>
......
......@@ -71,19 +71,8 @@ export class AppDetailComponent implements OnInit {
let options = this.installOption;
// if (options) {
// for (let reference of options.references) {
// if (reference.install && !reference.app.isInstalled()) {
// apps.push(reference.app);
// apps.push(...reference.app.findDependencies().filter((app) => {
// return !app.isInstalled()
// }))
// }
// }
// }
try {
this.appsService.install(this.currentApp, options);
await this.appsService.install(targetApp, options);
} catch (e) {
console.error(e);
new Notification(targetApp.name, {body: "下载失败"});
......
......@@ -14,6 +14,7 @@ import Timer = NodeJS.Timer;
import {DownloadService} from "./download.service";
import {InstallOption} from "./install-option";
import {InstallService} from "./install.service";
import {ComparableSet} from "./shared/ComparableSet";
const Aria2 = require('aria2');
const sudo = require('electron-sudo');
......@@ -116,6 +117,62 @@ export class AppsService {
return apps;
};
// async update(app: App) {
// const updateServer = "https://thief.mycard.moe/update/metalinks/";
//
// if (app.isReady() && app.local!.version != app.version) {
// let checksumMap = await this.installService.getChecksumFile(app)
//
// let latestFiles = new ComparableSet();
//
// }
//
// if (app.isInstalled() && app.version != (<AppLocal>app.local).version) {
// let checksumMap = await this.installService.getChecksumFile(app);
// let filesMap = (<AppLocal>app.local).files;
// let deleteList: string[] = [];
// let addList: string[] = [];
// let changeList: string[] = [];
// for (let [file,checksum] of filesMap) {
// let t = checksumMap.get(file);
// if (!t) {
// deleteList.push(file);
// } else if (t !== checksum) {
// changeList.push(file);
// }
// }
// for (let file of checksumMap.keys()) {
// if (!filesMap.has(file)) {
// changeList.push(file);
// }
// }
// let metalink = await this.http.post(updateServer + app.id, changeList).map((response) => response.text())
// .toPromise();
// let meta = new DOMParser().parseFromString(metalink, "text/xml");
// let filename = meta.getElementsByTagName('file')[0].getAttribute('name');
// let dir = path.join(path.dirname((<AppLocal>app.local).path), "downloading");
// let a = await this.downloadService.addMetalink(metalink, dir);
//
// for (let file of deleteList) {
// await this.installService.deleteFile(file);
// }
// (<AppLocal>app.local).version = app.version;
// (<AppLocal>app.local).files = checksumMap;
// localStorage.setItem(app.id, JSON.stringify(app.local));
// await this.installService.extract(path.join(dir, filename), (<AppLocal>app.local).path);
// let children = this.appsService.findChildren(app);
// for (let child of children) {
// if (child.isInstalled()) {
// await this.installService.uninstall(child, false);
// // this.installService.add(child, new InstallOption(child, path.dirname(((<AppLocal>app.local).path))));
// await this.installService.getComplete(child);
// console.log("282828")
// }
// }
//
// }
// }
async install(app: App, option: InstallOption) {
const addDownloadTask = async(app: App, dir: string) => {
let metalinkUrl = app.download;
......@@ -145,8 +202,8 @@ export class AppsService {
let currentUnit = Math.floor(Math.log(currentSpeed) / Math.log(1024));
console.log(currentSpeed, currentUnit);
app.status.progressMessage = (currentSpeed / 1024 ** currentUnit).toFixed(1) + " " + speedUnit[currentUnit];
}else{
app.status.progressMessage='';
} else {
app.status.progressMessage = '';
}
this.ref.tick();
}, (error) => {
......@@ -160,7 +217,9 @@ export class AppsService {
};
try {
let apps: App[] = [];
let dependencies = app.findDependencies();
let dependencies = app.findDependencies().filter((dependency) => {
return !dependency.isInstalled();
});
apps.push(...dependencies, app);
let downloadPath = path.join(option.installLibrary, 'downloading');
let tasks: Promise<any>[] = [];
......@@ -174,8 +233,6 @@ export class AppsService {
o.downloadFiles = result.files;
this.installService.push({app: result.app, option: o});
}
// this.installService.push({app: app, option: option})
} catch (e) {
console.log(e);
throw e;
......
......@@ -12,7 +12,7 @@ import * as fs from "fs";
import {EventEmitter} from "events";
import {AppLocal} from "./app-local";
import {Http} from "@angular/http";
import {AppsService} from "./apps.service";
import {ComparableSet} from "./shared/ComparableSet"
import ReadableStream = NodeJS.ReadableStream;
import {Observable, Observer} from "rxjs/Rx";
......@@ -33,7 +33,8 @@ export class InstallService {
installingId: string = '';
eventEmitter: EventEmitter = new EventEmitter();
checksumUri = "https://thief.mycard.moe/checksums/";
readonly checksumURL = "https://thief.mycard.moe/checksums/";
readonly updateServerURL = 'https://thief.mycard.moe/update/metalinks';
installQueue: Map<string,InstallTask> = new Map();
......@@ -90,19 +91,18 @@ export class InstallService {
});
if (readyForInstall) {
let option = task.option;
let installDir = option.installDir;
// if (!app.isInstalled()) {
let checksumFile = await this.getChecksumFile(app);
if (app.parent) {
let conflictFiles = new Set<string>();
let parentFilesMap = app.parent.local!.files;
for (let key of checksumFile.keys()) {
if (parentFilesMap.has(key)) {
conflictFiles.add(key);
}
}
// mod需要安装到parent路径
installDir = app.parent.local!.path;
let parentFiles = new ComparableSet(Array.from(app.parent.local!.files.keys()));
let appFiles = new ComparableSet(Array.from(checksumFile.keys()));
let conflictFiles = appFiles.intersection(parentFiles);
if (conflictFiles.size > 0) {
let backupPath = path.join(option.installLibrary, "backup", app.parent.id);
this.backupFiles(option.installDir, backupPath, conflictFiles);
await this.backupFiles(app.parent.local!.path, backupPath, conflictFiles);
}
}
let allFiles = new Set(checksumFile.keys());
......@@ -111,17 +111,14 @@ export class InstallService {
app.status.progress = 0;
// let timeNow = new Date().getTime();
for (let file of option.downloadFiles) {
await this.createDirectory(option.installDir);
await this.createDirectory(installDir);
let interval = setInterval(() => {
}, 500);
await new Promise((resolve, reject) => {
this.extract(file, option.installDir).subscribe(
this.extract(file, installDir).subscribe(
(lastItem: string) => {
app.status.progress += 1;
app.status.progressMessage = lastItem;
// if (new Date().getTime() - timeNow > 500) {
// timeNow = new Date().getTime();
// }
},
(error) => {
reject(error);
......@@ -132,9 +129,9 @@ export class InstallService {
});
clearInterval(interval);
}
await this.postInstall(app, option.installDir);
await this.postInstall(app, installDir);
let local = new AppLocal();
local.path = option.installDir;
local.path = installDir;
local.files = checksumFile;
local.version = app.version;
app.local = local;
......@@ -230,21 +227,35 @@ export class InstallService {
}
}
async backupFiles(dir: string, backupPath: string, files: Iterable<string>) {
await this.createDirectory(backupPath);
async backupFiles(dir: string, backupDir: string, files: Iterable<string>) {
for (let file of files) {
await new Promise((resolve, reject) => {
let oldPath = path.join(dir, file);
let newPath = path.join(backupPath, file);
fs.rename(oldPath, newPath, resolve);
await new Promise(async(resolve, reject) => {
let srcPath = path.join(dir, file);
let backupPath = path.join(backupDir, file);
await this.createDirectory(path.dirname(backupPath));
fs.unlink(backupPath, (err) => {
fs.rename(srcPath, backupPath, resolve);
});
});
}
}
async restoreFiles(dir: string, backupDir: string, files: Iterable<string>) {
for (let file of files) {
await new Promise((resolve, reject) => {
let backupPath = path.join(backupDir, file);
let srcPath = path.join(dir, file);
fs.unlink(srcPath, (err) => {
fs.rename(backupPath, srcPath, resolve);
})
})
}
}
async getChecksumFile(app: App): Promise<Map<string,string> > {
let checksumUrl = this.checksumUri + app.id;
let checksumUrl = this.checksumURL + app.id;
if (["ygopro", 'desmume'].includes(app.id)) {
checksumUrl = this.checksumUri + app.id + "-" + process.platform;
checksumUrl = this.checksumURL + app.id + "-" + process.platform;
}
return this.http.get(checksumUrl)
.map((response) => {
......@@ -280,39 +291,29 @@ export class InstallService {
})
}
async uninstall(app: App, restore = true) {
if (!app.parent) {
// let children = this.appsService.findChildren(app);
// for (let child of children) {
// if (child.isInstalled()) {
// await this.uninstall(child);
// }
// }
}
let files = Array.from((<AppLocal>app.local).files.keys()).sort().reverse();
for (let file of files) {
let oldFile = file;
if (!path.isAbsolute(file)) {
oldFile = path.join((<AppLocal>app.local).path, file);
async uninstall(app: App) {
if (app.isReady()) {
let appDir = app.local!.path;
let files = Array.from(app.local!.files.keys()).sort().reverse();
for (let file of files) {
this.deleteFile(path.join(appDir, file));
}
if (restore) {
await this.deleteFile(oldFile);
if (app.parent) {
let backFile = path.join((<AppLocal>app.local).path, "backup", file);
await new Promise((resolve, reject) => {
fs.rename(backFile, oldFile, resolve);
});
if (app.parent) {
let backupDir = path.join(path.dirname(appDir), "backup", app.parent.id)
let fileSet = new ComparableSet(files);
let parentSet = new ComparableSet(Array.from(app.parent.local!.files.keys()));
let difference = parentSet.intersection(fileSet);
if (difference) {
this.restoreFiles(appDir, backupDir, Array.from(difference))
}
}
app.local = null;
localStorage.removeItem(app.id);
}
if (app.parent) {
await this.deleteFile(path.join((<AppLocal>app.local).path, "backup"));
} else {
await this.deleteFile((<AppLocal>app.local).path);
}
app.local = null;
localStorage.removeItem(app.id);
}
}
\ No newline at end of file
......@@ -43,9 +43,6 @@ export class LobbyComponent implements OnInit {
params.set('nickname', this.loginService.user.username);
params.set('autojoin', this.currentApp.conference + '@conference.mycard.moe');
this.candy_url = url;
// 尝试更新应用
this.updateApp();
}
chooseApp(app: App) {
......@@ -55,56 +52,7 @@ export class LobbyComponent implements OnInit {
}
}
async updateApp() {
let updateServer = "https://thief.mycard.moe/update/metalinks/";
let checksumServer = "https://thief.mycard.moe/checksums/";
for (let app of this.apps.values()) {
if (app.isInstalled() && app.version != (<AppLocal>app.local).version) {
let checksumMap = await this.installService.getChecksumFile(app);
let filesMap = (<AppLocal>app.local).files;
let deleteList: string[] = [];
let addList: string[] = [];
let changeList: string[] = [];
for (let [file,checksum] of filesMap) {
let t = checksumMap.get(file);
if (!t) {
deleteList.push(file);
} else if (t !== checksum) {
changeList.push(file);
}
}
for (let file of checksumMap.keys()) {
if (!filesMap.has(file)) {
changeList.push(file);
}
}
let metalink = await this.http.post(updateServer + app.id, changeList).map((response) => response.text())
.toPromise();
let meta = new DOMParser().parseFromString(metalink, "text/xml");
let filename = meta.getElementsByTagName('file')[0].getAttribute('name');
let dir = path.join(path.dirname((<AppLocal>app.local).path), "downloading");
let a = await this.downloadService.addMetalink(metalink, dir);
for (let file of deleteList) {
await this.installService.deleteFile(file);
}
(<AppLocal>app.local).version = app.version;
(<AppLocal>app.local).files = checksumMap;
localStorage.setItem(app.id, JSON.stringify(app.local));
await this.installService.extract(path.join(dir, filename), (<AppLocal>app.local).path);
let children = this.appsService.findChildren(app);
for (let child of children) {
if (child.isInstalled()) {
await this.installService.uninstall(child, false);
// this.installService.add(child, new InstallOption(child, path.dirname(((<AppLocal>app.local).path))));
await this.installService.getComplete(child);
console.log("282828")
}
}
}
}
}
get grouped_apps() {
......
/**
* Created by weijian on 2016/12/5.
*/
export class ComparableSet<T> extends Set<T> {
constructor(values?: Iterable<T>) {
if (values) {
super(values);
} else {
super();
}
}
isSuperset(subset: Set<T>) {
for (var elem of subset) {
if (!this.has(elem)) {
return false;
}
}
return true;
}
union(setB: Set<T>): Set<T> {
var union = new Set(this);
for (var elem of setB) {
union.add(elem);
}
return union;
}
intersection(setB: Set<T>): Set<T> {
var intersection = new Set();
for (var elem of setB) {
if (this.has(elem)) {
intersection.add(elem);
}
}
return intersection;
}
difference(setB: Set<T>): Set<T> {
var difference = new Set(this);
for (var elem of setB) {
difference.delete(elem);
}
return difference;
}
}
......@@ -2071,7 +2071,8 @@
}
},
"version": {
"darwin": "1.06"
"win32": "1.033.C-7",
"darwin": "1.033.C-7"
},
"news": [
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment