Commit b155bddd authored by wudizhanche1000's avatar wudizhanche1000

下载安装步骤

parent 6a33d0bc
...@@ -59,13 +59,13 @@ ...@@ -59,13 +59,13 @@
<tr *ngFor="let mod of mods; let i = index"> <tr *ngFor="let mod of mods; let i = index">
<th scope="row">{{i + 1}}</th> <th scope="row">{{i + 1}}</th>
<td>{{mod.name}}</td> <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> <button i18n type="button" (click)="uninstall(mod)" class="btn btn-danger btn-sm">卸载</button>
</td> </td>
<td *ngIf="!mod.isInstalled()"> <td *ngIf="!mod.isInstalled()">
<button i18n (click)="installMod(mod)" type="button" *ngIf="mod.status.status==='init'" class="btn btn-primary btn-sm">安装</button> <button i18n (click)="installMod(mod)" type="button" *ngIf="!mod.isInstalled()" 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> <progress *ngIf="mod.isDownloading()" class="progress progress-striped progress-animated" value="{{mod.status.progress}}" max="{{mod.status.total}}"></progress>
<div i18n *ngIf="mod.status.status==='waiting'">等待安装...</div> <div i18n *ngIf="mod.isWaiting()">等待安装...</div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
......
...@@ -71,19 +71,8 @@ export class AppDetailComponent implements OnInit { ...@@ -71,19 +71,8 @@ export class AppDetailComponent implements OnInit {
let options = this.installOption; 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 { try {
this.appsService.install(this.currentApp, options); await this.appsService.install(targetApp, options);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
new Notification(targetApp.name, {body: "下载失败"}); new Notification(targetApp.name, {body: "下载失败"});
......
...@@ -14,6 +14,7 @@ import Timer = NodeJS.Timer; ...@@ -14,6 +14,7 @@ import Timer = NodeJS.Timer;
import {DownloadService} from "./download.service"; import {DownloadService} from "./download.service";
import {InstallOption} from "./install-option"; import {InstallOption} from "./install-option";
import {InstallService} from "./install.service"; import {InstallService} from "./install.service";
import {ComparableSet} from "./shared/ComparableSet";
const Aria2 = require('aria2'); const Aria2 = require('aria2');
const sudo = require('electron-sudo'); const sudo = require('electron-sudo');
...@@ -116,6 +117,62 @@ export class AppsService { ...@@ -116,6 +117,62 @@ export class AppsService {
return apps; 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) { async install(app: App, option: InstallOption) {
const addDownloadTask = async(app: App, dir: string) => { const addDownloadTask = async(app: App, dir: string) => {
let metalinkUrl = app.download; let metalinkUrl = app.download;
...@@ -145,8 +202,8 @@ export class AppsService { ...@@ -145,8 +202,8 @@ export class AppsService {
let currentUnit = Math.floor(Math.log(currentSpeed) / Math.log(1024)); let currentUnit = Math.floor(Math.log(currentSpeed) / Math.log(1024));
console.log(currentSpeed, currentUnit); console.log(currentSpeed, currentUnit);
app.status.progressMessage = (currentSpeed / 1024 ** currentUnit).toFixed(1) + " " + speedUnit[currentUnit]; app.status.progressMessage = (currentSpeed / 1024 ** currentUnit).toFixed(1) + " " + speedUnit[currentUnit];
}else{ } else {
app.status.progressMessage=''; app.status.progressMessage = '';
} }
this.ref.tick(); this.ref.tick();
}, (error) => { }, (error) => {
...@@ -160,7 +217,9 @@ export class AppsService { ...@@ -160,7 +217,9 @@ export class AppsService {
}; };
try { try {
let apps: App[] = []; let apps: App[] = [];
let dependencies = app.findDependencies(); let dependencies = app.findDependencies().filter((dependency) => {
return !dependency.isInstalled();
});
apps.push(...dependencies, app); apps.push(...dependencies, app);
let downloadPath = path.join(option.installLibrary, 'downloading'); let downloadPath = path.join(option.installLibrary, 'downloading');
let tasks: Promise<any>[] = []; let tasks: Promise<any>[] = [];
...@@ -174,8 +233,6 @@ export class AppsService { ...@@ -174,8 +233,6 @@ export class AppsService {
o.downloadFiles = result.files; o.downloadFiles = result.files;
this.installService.push({app: result.app, option: o}); this.installService.push({app: result.app, option: o});
} }
// this.installService.push({app: app, option: option})
} catch (e) { } catch (e) {
console.log(e); console.log(e);
throw e; throw e;
......
...@@ -12,7 +12,7 @@ import * as fs from "fs"; ...@@ -12,7 +12,7 @@ import * as fs from "fs";
import {EventEmitter} from "events"; import {EventEmitter} from "events";
import {AppLocal} from "./app-local"; import {AppLocal} from "./app-local";
import {Http} from "@angular/http"; import {Http} from "@angular/http";
import {AppsService} from "./apps.service"; import {ComparableSet} from "./shared/ComparableSet"
import ReadableStream = NodeJS.ReadableStream; import ReadableStream = NodeJS.ReadableStream;
import {Observable, Observer} from "rxjs/Rx"; import {Observable, Observer} from "rxjs/Rx";
...@@ -33,7 +33,8 @@ export class InstallService { ...@@ -33,7 +33,8 @@ export class InstallService {
installingId: string = ''; installingId: string = '';
eventEmitter: EventEmitter = new EventEmitter(); 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(); installQueue: Map<string,InstallTask> = new Map();
...@@ -90,19 +91,18 @@ export class InstallService { ...@@ -90,19 +91,18 @@ export class InstallService {
}); });
if (readyForInstall) { if (readyForInstall) {
let option = task.option; let option = task.option;
let installDir = option.installDir;
// if (!app.isInstalled()) { // if (!app.isInstalled()) {
let checksumFile = await this.getChecksumFile(app); let checksumFile = await this.getChecksumFile(app);
if (app.parent) { if (app.parent) {
let conflictFiles = new Set<string>(); // mod需要安装到parent路径
let parentFilesMap = app.parent.local!.files; installDir = app.parent.local!.path;
for (let key of checksumFile.keys()) { let parentFiles = new ComparableSet(Array.from(app.parent.local!.files.keys()));
if (parentFilesMap.has(key)) { let appFiles = new ComparableSet(Array.from(checksumFile.keys()));
conflictFiles.add(key); let conflictFiles = appFiles.intersection(parentFiles);
}
}
if (conflictFiles.size > 0) { if (conflictFiles.size > 0) {
let backupPath = path.join(option.installLibrary, "backup", app.parent.id); 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()); let allFiles = new Set(checksumFile.keys());
...@@ -111,17 +111,14 @@ export class InstallService { ...@@ -111,17 +111,14 @@ export class InstallService {
app.status.progress = 0; app.status.progress = 0;
// let timeNow = new Date().getTime(); // let timeNow = new Date().getTime();
for (let file of option.downloadFiles) { for (let file of option.downloadFiles) {
await this.createDirectory(option.installDir); await this.createDirectory(installDir);
let interval = setInterval(() => { let interval = setInterval(() => {
}, 500); }, 500);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
this.extract(file, option.installDir).subscribe( this.extract(file, installDir).subscribe(
(lastItem: string) => { (lastItem: string) => {
app.status.progress += 1; app.status.progress += 1;
app.status.progressMessage = lastItem; app.status.progressMessage = lastItem;
// if (new Date().getTime() - timeNow > 500) {
// timeNow = new Date().getTime();
// }
}, },
(error) => { (error) => {
reject(error); reject(error);
...@@ -132,9 +129,9 @@ export class InstallService { ...@@ -132,9 +129,9 @@ export class InstallService {
}); });
clearInterval(interval); clearInterval(interval);
} }
await this.postInstall(app, option.installDir); await this.postInstall(app, installDir);
let local = new AppLocal(); let local = new AppLocal();
local.path = option.installDir; local.path = installDir;
local.files = checksumFile; local.files = checksumFile;
local.version = app.version; local.version = app.version;
app.local = local; app.local = local;
...@@ -230,21 +227,35 @@ export class InstallService { ...@@ -230,21 +227,35 @@ export class InstallService {
} }
} }
async backupFiles(dir: string, backupPath: string, files: Iterable<string>) { async backupFiles(dir: string, backupDir: string, files: Iterable<string>) {
await this.createDirectory(backupPath);
for (let file of files) { for (let file of files) {
await new Promise((resolve, reject) => { await new Promise(async(resolve, reject) => {
let oldPath = path.join(dir, file); let srcPath = path.join(dir, file);
let newPath = path.join(backupPath, file); let backupPath = path.join(backupDir, file);
fs.rename(oldPath, newPath, resolve); 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> > { 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)) { 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) return this.http.get(checksumUrl)
.map((response) => { .map((response) => {
...@@ -280,39 +291,29 @@ export class InstallService { ...@@ -280,39 +291,29 @@ export class InstallService {
}) })
} }
async uninstall(app: App, restore = true) { async uninstall(app: App) {
if (!app.parent) { if (app.isReady()) {
// let children = this.appsService.findChildren(app); let appDir = app.local!.path;
// for (let child of children) { let files = Array.from(app.local!.files.keys()).sort().reverse();
// if (child.isInstalled()) {
// await this.uninstall(child); for (let file of files) {
// } this.deleteFile(path.join(appDir, file));
// }
}
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);
} }
if (restore) {
await this.deleteFile(oldFile); if (app.parent) {
if (app.parent) { let backupDir = path.join(path.dirname(appDir), "backup", app.parent.id)
let backFile = path.join((<AppLocal>app.local).path, "backup", file); let fileSet = new ComparableSet(files);
await new Promise((resolve, reject) => { let parentSet = new ComparableSet(Array.from(app.parent.local!.files.keys()));
fs.rename(backFile, oldFile, resolve); 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 { ...@@ -43,9 +43,6 @@ export class LobbyComponent implements OnInit {
params.set('nickname', this.loginService.user.username); params.set('nickname', this.loginService.user.username);
params.set('autojoin', this.currentApp.conference + '@conference.mycard.moe'); params.set('autojoin', this.currentApp.conference + '@conference.mycard.moe');
this.candy_url = url; this.candy_url = url;
// 尝试更新应用
this.updateApp();
} }
chooseApp(app: App) { chooseApp(app: App) {
...@@ -55,56 +52,7 @@ export class LobbyComponent implements OnInit { ...@@ -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() { 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 @@ ...@@ -2071,7 +2071,8 @@
} }
}, },
"version": { "version": {
"darwin": "1.06" "win32": "1.033.C-7",
"darwin": "1.033.C-7"
}, },
"news": [ "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