Commit e001cc3e authored by wudizhanche1000's avatar wudizhanche1000

合并冲突前提交

parent 033e49fc
<h1>{{currentApp.name}}</h1>
<div *ngIf="currentApp.status.status === 'init'">
<button i18n type="button" class="btn btn-primary" data-toggle="modal" (click)="updateInstallConfig(currentApp)" data-target="#install-modal">安装</button>
<button i18n type="button" class="btn btn-primary" data-toggle="modal" (click)="updateInstallOption(currentApp)" data-target="#install-modal">安装</button>
<button i18n type="button" class="btn btn-secondary">导入</button>
</div>
<div i18n *ngIf="currentApp.status.status === 'installing'">正在安装...</div>
<div i18n *ngIf="currentApp.status.status==='waiting'">等待安装...</div>
<progress *ngIf="currentApp.status.status === 'downloading'" class="progress progress-striped progress-animated" value="{{currentApp.status.progress}}" max="{{currentApp.status.total}}"></progress>
<progress *ngIf="currentApp.status.status === 'downloading'||currentApp.status.status==='installing'" class="progress progress-striped progress-animated" value="{{currentApp.status.progress}}" max="{{currentApp.status.total}}"></progress>
<div class="actions" *ngIf="currentApp.status.status==='ready' && (currentApp.id != 'ygopro')">
<button i18n *ngIf="currentApp.runable()" (click)="runApp(currentApp)" type="button" class="btn btn-primary">运行</button>
<button i18n *ngIf="currentApp.runable() && currentApp.actions.get('custom')" (click)="custom(currentApp)" type="button" class="btn btn-secondary">设置</button>
......@@ -63,7 +63,7 @@
<button i18n type="button" class="btn btn-secondary">校验完整性</button>
<button i18n (click)="uninstall(currentApp)" type="button" class="btn btn-secondary">卸载</button>
</div>
<div class="modal fade" id="install-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" *ngIf="installConfig">
<div class="modal fade" id="install-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" *ngIf="installOption">
<div class="modal-dialog" role="document">
<form id="install-form" class="modal-content" (ngSubmit)="install(currentApp)" #theForm="ngForm">
<div class="modal-header">
......@@ -76,23 +76,23 @@
<p i18n>即将开始安装 {{currentApp.name}}</p>
<h4 i18n>安装位置</h4>
<div class="form-group">
<select class="form-control" name="installPath" [(ngModel)]="installConfig.installLibrary" title="path">
<select class="form-control" name="installPath" [(ngModel)]="installOption.installLibrary" title="path">
<option *ngFor="let library of libraries" value="{{library}}"> {{library}}</option>
</select></div>
<h4 i18n>快捷方式</h4>
<div class="checkbox">
<label i18n *ngIf="platform == 'darwin'" for="create_application_shortcut">创建 LaunchPad 快捷方式</label>
<label i18n *ngIf="platform == 'win32'" for="create_application_shortcut">创建开始菜单快捷方式</label>
<input id="create_application_shortcut" type="checkbox" name="application" [(ngModel)]="installConfig.createShortcut">
<input id="create_application_shortcut" type="checkbox" name="application" [(ngModel)]="installOption.createShortcut">
</div>
<div class="checkbox">
<label i18n for="create_desktop_shortcut">创建桌面快捷方式</label>
<input id="create_desktop_shortcut" type="checkbox" name="desktop" [(ngModel)]="installConfig.createDesktopShortcut">
<input id="create_desktop_shortcut" type="checkbox" name="desktop" [(ngModel)]="installOption.createDesktopShortcut">
</div>
<h4 *ngIf="installConfig.references.length">扩展内容</h4>
<div *ngFor="let reference of installConfig.references"><label>
<input type="checkbox" [(ngModel)]="reference.install" name="references" value="{{reference.app.id}}"> {{reference.app.name}}
</label></div>
<!--<h4 *ngIf="installOption.references.length">扩展内容</h4>-->
<!--<div *ngFor="let reference of installOption.references"><label>-->
<!--<input type="checkbox" [(ngModel)]="reference.install" name="references" value="{{reference.app.id}}"> {{reference.app.name}}-->
<!--</label></div>-->
<div *ngIf="currentApp.findDependencies().length">
<span i18n>依赖:</span>
<span class="dependency" *ngFor="let dependency of currentApp.findDependencies()">{{dependency.name}}</span>
......
import {Component, OnInit, Input, ChangeDetectorRef} from "@angular/core";
import {AppsService} from "./apps.service";
import {InstallConfig} from "./install-config";
import {InstallOption} from "./install-option";
import {SettingsService} from "./settings.sevices";
import {App} from "./app";
import {DownloadService} from "./download.service";
......@@ -22,7 +22,7 @@ export class AppDetailComponent implements OnInit {
currentApp: App;
platform = process.platform;
installConfig: InstallConfig;
installOption: InstallOption;
constructor(private appsService: AppsService, private settingsService: SettingsService,
private downloadService: DownloadService, private installService: InstallService,
......@@ -32,13 +32,13 @@ export class AppDetailComponent implements OnInit {
ngOnInit() {
}
updateInstallConfig(app: App) {
this.installConfig = new InstallConfig(app);
this.installConfig.installLibrary = this.settingsService.getDefaultLibrary().path;
this.installConfig.references = [];
for (let reference of app.references.values()) {
this.installConfig.references.push(new InstallConfig(reference))
}
updateInstallOption(app: App) {
this.installOption = new InstallOption(app);
this.installOption.installLibrary = this.settingsService.getDefaultLibrary().path;
// this.installOption.references = [];
// for (let reference of app.references.values()) {
// this.installOption.references.push(new InstallOption(reference))
// }
}
get libraries(): string[] {
......@@ -54,7 +54,7 @@ export class AppDetailComponent implements OnInit {
}
async installMod(mod: App) {
this.updateInstallConfig(mod);
this.updateInstallOption(mod);
await this.install(mod);
}
......@@ -69,68 +69,23 @@ export class AppDetailComponent implements OnInit {
async install(targetApp: App) {
$('#install-modal').modal('hide');
let options = this.installConfig;
let dependencies = targetApp.findDependencies();
let apps = dependencies.concat(targetApp).filter((app) => {
return !app.isInstalled()
});
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()
}))
}
}
}
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()
// }))
// }
// }
// }
let downloadPath = path.join(this.installConfig.installLibrary, "downloading");
try {
let downloadApps = await this.downloadService.addUris(apps, downloadPath);
for (let app of apps) {
await new Promise((resolve, reject) => {
this.downloadService.getProgress(app)
.subscribe((progress) => {
app.status.status = "downloading";
app.status.progress = progress.progress;
app.status.total = progress.total;
this.ref.detectChanges();
resolve();
},
(error) => {
reject(`download error: ${error}`);
},
() => {
// 避免安装过快
if (app.status.status === "downloading") {
app.status.status = "waiting";
this.ref.detectChanges();
}
});
});
}
await Promise.all(downloadApps.map((app) => {
return this.downloadService.getComplete(app)
.then((completeApp: App) => {
return this.installService.add(completeApp, options);
});
}));
for (let app of apps) {
new Promise(async(resolve, reject) => {
await this.installService.getComplete(app);
app.status.status = 'ready';
resolve();
})
}
await this.installService.getComplete(targetApp);
targetApp.status.status = "ready";
this.ref.detectChanges();
this.appsService.install(this.currentApp, options);
} catch (e) {
console.error(e);
new Notification(targetApp.name, {body: "下载失败"});
}
}
......@@ -138,7 +93,7 @@ export class AppDetailComponent implements OnInit {
selectDir() {
let dir = remote.dialog.showOpenDialog({properties: ['openFile', 'openDirectory']});
console.log(dir);
// this.appsService.installConfig.installDir = dir[0];
// this.appsService.installOption.installDir = dir[0];
return dir[0];
}
......
......@@ -3,6 +3,7 @@ import {App} from "./app";
* Created by zh99998 on 16/9/6.
*/
export class AppLocal {
library: string;
path: string;
version: string;
files: Map<string,string>;
......
......@@ -11,7 +11,7 @@ export enum Category {
module
}
// export enum Status{
// export enum DownloadStatus{
// downloading,
// init,
// installing,
......@@ -34,25 +34,33 @@ export class AppStatus {
}
export class App {
id: string;
_name: string; // i18n
get name() {
return this._name;
}
set name(a) {
this._name = a;
}
name: string; // i18n
description: string; //i18n
author: string; // English Only
homepage: string;
category: Category;
parent: App;
get download(): string {
let downloadUrl = "https://thief.mycard.moe/metalinks/";
return downloadUrl + this.id + ".meta4";
}
get checksum(): string {
let checksumUrl = "https://thief.mycard.moe/checksum/";
return checksumUrl + this.id;
}
get update(): string {
let updateUrl = "https://thief.mycard.moe/update/";
return updateUrl + this.id;
}
actions: Map<string,Action>;
references: Map<string,App>;
dependencies: Map<string,App>;
locales: string[];
download: string; // meta4 url
news: {title: string, url: string, image: string}[];
network: any;
tags: string[];
......@@ -81,7 +89,6 @@ export class App {
this.parent = app.parent;
this.references = app.references;
this.locales = app.locales;
this.download = app.download;
this.news = app.news;
this.network = app.network;
this.tags = app.tags;
......
......@@ -11,6 +11,9 @@ import "rxjs/Rx";
import {AppLocal} from "./app-local";
import * as ini from "ini";
import Timer = NodeJS.Timer;
import {DownloadService} from "./download.service";
import {InstallOption} from "./install-option";
import {InstallService} from "./install.service";
const Aria2 = require('aria2');
const sudo = require('electron-sudo');
......@@ -24,10 +27,10 @@ export class AppsService {
private apps: Map<string,App>;
constructor(private http: Http, private settingsService: SettingsService, private ref: ApplicationRef,) {
constructor(private http: Http, private settingsService: SettingsService, private ref: ApplicationRef,
private downloadService: DownloadService, private installService: InstallService) {
}
loadApps() {
return this.http.get('./apps.json')
.toPromise()
......@@ -67,7 +70,7 @@ export class AppsService {
});
// 去除平台无关的内容
['actions', 'dependencies', 'references', 'download', 'version'].forEach((key) => {
['actions', 'dependencies', 'references', 'version'].forEach((key) => {
if (app[key]) {
if (app[key][platform]) {
app[key] = app[key][platform];
......@@ -113,6 +116,56 @@ export class AppsService {
return apps;
};
async install(app: App, option: InstallOption) {
const addDownloadTask = async(app: App, dir: string) => {
let metalinkUrl = app.download;
if (app.id === "ygopro") {
metalinkUrl="https://thief.mycard.moe/metalinks/ygopro-"+process.platform+".meta4";
}
let metalink = await this.http.get(metalinkUrl).map((response) => {
return response.text()
}).toPromise();
app.status.status = "downloading";
let downloadId = await this.downloadService.addMetalink(metalink, dir);
let observable = this.downloadService.downloadProgress(downloadId);
return new Promise((resolve, reject) => {
observable.subscribe((task) => {
app.status.progress = task.completedLength;
app.status.total = task.totalLength;
this.ref.tick();
}, (error) => {
reject(error);
}, async() => {
app.status.status = "waiting";
let files = await this.downloadService.getFile(downloadId);
resolve({app: app, files: files});
})
});
};
try {
let apps: App[] = [];
let dependencies = app.findDependencies();
apps.push(...dependencies, app);
let downloadPath = path.join(option.installLibrary, 'downloading');
let tasks: Promise<any>[] = [];
for (let a of apps) {
tasks.push(addDownloadTask(a, downloadPath));
}
let downloadResults = await Promise.all(tasks);
for (let result of downloadResults) {
console.log(result);
let o = new InstallOption(result.app, option.installLibrary);
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;
}
}
findChildren(app: App): App[] {
let children: App[] = [];
for (let [id,child] of this.apps) {
......
/**
* Created by weijian on 2016/10/26.
*/
import {Injectable, NgZone} from "@angular/core";
import {Injectable, NgZone, EventEmitter} from "@angular/core";
import {Http} from "@angular/http";
import {Observable} from "rxjs/Observable";
import {EventEmitter} from "events";
import {App} from "./app";
import {Observer} from "rxjs";
import Timer = NodeJS.Timer;
const Aria2 = require('aria2');
const MAX_LIST_NUM = 1000;
const ARIA2_INTERVAL = 1000;
export interface DownloadStatus {
completedLength: string;
downloadSpeed: string;
gid: string;
status: string;
totalLength: string;
errorCode: string;
errorMessage: string;
}
@Injectable()
export class DownloadService {
aria2 = new Aria2();
baseURL = 'https://thief.mycard.moe/metalinks/';
appGidMap = new Map<App,string>();
gidAppMap = new Map<string,App>();
eventEmitter = new EventEmitter();
open = this.aria2.open();
updateEmitter = new EventEmitter<string>();
progressList: Map<string,(Observable<any>)> = new Map();
taskList: Map<string,DownloadStatus> = new Map();
map: Map<string,string[]> = new Map();
constructor(private ngZone: NgZone, private http: Http) {
this.aria2.onDownloadComplete = (result: any) => {
let app = this.gidAppMap.get(result.gid);
if (app) {
this.appGidMap.delete(app);
this.gidAppMap.delete(result.gid);
this.eventEmitter.emit(app.id, 'complete');
//
}
if (this.map.get(result.gid)) {
this.map.get(result.gid).complete();
this.map.delete(result.gid);
}
}
ngZone.runOutsideAngular(async() => {
await this.open;
setInterval(async() => {
let activeList = await this.aria2.tellActive();
let waitList = await this.aria2.tellWaiting(0, MAX_LIST_NUM);
let stoppedList = await this.aria2.tellStopped(0, MAX_LIST_NUM);
for (let item of activeList) {
this.taskList.set(item.gid, item);
}
for (let item of waitList) {
this.taskList.set(item.gid, item);
}
for (let item of stoppedList) {
this.taskList.set(item.gid, item);
}
this.updateEmitter.emit("updated");
}, ARIA2_INTERVAL);
})
}
getComplete(app: App): Promise<App> {
if (!this.appGidMap.has(app)) {
throw('nyaa');
private createId(): string {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return new Promise((resolve, reject) => {
this.eventEmitter.once(app.id, (event: Event) => {
resolve(app);
})
});
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
getProgress(app: App): Observable<any> {
let gid = this.appGidMap.get(app);
return Observable.create((observer: Observer<any>) => {
let interval: Timer;
this.ngZone.runOutsideAngular(() => {
interval = setInterval(() => {
this.aria2.tellStatus(gid).then((status: any) => {
if (status.status === 'complete') {
observer.complete();
} else if (status.status === "active") {
observer.next({total: status.totalLength, progress: status.completedLength})
} else if (status.status === "error") {
console.log("error", status.errorCode);
observer.error(status.errorCode)
downloadProgress(id: string): Observable<any> {
let progress = this.progressList.get(id);
if (progress) {
return progress;
} else {
return Observable.create((observer: Observer<any>) => {
let status = '';
let completedLength = 0;
let totalLength = 0;
let gidList = this.map.get(id) !;
this.updateEmitter.subscribe((value: string) => {
let statusList = new Array(gidList.length);
let newCompletedLength = 0;
let newTotalLength = 0;
for (let [index,gid] of gidList.entries()) {
let task = this.taskList.get(gid)!;
statusList[index] = task.status;
newCompletedLength += parseInt(task.completedLength);
newTotalLength += parseInt(task.totalLength);
}
if (newCompletedLength !== completedLength || newTotalLength !== totalLength) {
completedLength = newCompletedLength;
totalLength = newTotalLength;
observer.next({status: status, completedLength: completedLength, totalLength: totalLength});
}
status = statusList.reduce((value, current) => {
if (value === "complete" && current === "complete") {
return "complete";
}
if (current != "complete" && current != "active") {
return "error";
}
});
}, 1000);
if (status === "complete") {
observer.complete();
} else if (status == "error") {
observer.error("Download Error");
}
return () => {
}
}, () => {
}, () => {
})
});
return () => {
clearInterval(interval);
}
});
}
}
async addUris(apps: App[], path: string): Promise<App[]> {
let tasks: App[] = [];
for (let app of apps) {
let task = await this.addUri(app, path);
tasks.push(task);
async getFile(id: string): Promise<string[]> {
let gids = this.map.get(id)!;
console.log('gids ', gids);
let files: string[] = [];
for (let gid of gids) {
let file = await this.aria2.getFiles(gid);
files.push(file[0].path);
}
return tasks;
return files;
}
map: Map<string,any> = new Map();
async addMetalink(metalink: string, library: string) {
let meta4 = new Buffer((metalink)).toString('base64');
let gid = ( await this.aria2.addMetalink(meta4, {dir: library}))[0];
return Observable.create((observer: Observer<any>) => {
this.map.set(gid, observer);
let interval: Timer;
this.ngZone.runOutsideAngular(() => {
interval = setInterval(async() => {
let status = await this.aria2.tellStatus(gid);
this.map.get(gid).next(status);
}, 1000)
});
return () => {
clearInterval(interval);
}
});
async addMetalink(metalink: string, library: string): Promise<string> {
let encodedMeta4 = new Buffer((metalink)).toString('base64');
let gidList = await this.aria2.addMetalink(encodedMeta4, {dir: library});
let taskId = this.createId();
this.map.set(taskId, gidList);
return taskId;
}
async addUri(app: App, path: string): Promise<App> {
let id = app.id;
async addUri(url: string, destination: string): Promise<string> {
await this.open;
if (this.appGidMap.has(app)) {
return app;
} else {
let meta4link = `${this.baseURL}${id}.meta4`;
if (["ygopro", 'desmume'].includes(id)) {
meta4link = `${this.baseURL}${id}-${process.platform}.meta4`
}
let response = await this.http.get(meta4link).toPromise();
let meta4 = new Buffer(response.text()).toString('base64');
let gid = (await this.aria2.addMetalink(meta4, {dir: path}))[0];
this.appGidMap.set(app, gid);
this.gidAppMap.set(gid, app);
return app;
let id = await this.aria2.addUri([url], {dir: destination});
return id;
}
async pause(id: string): Promise<void> {
await this.open;
try {
await this.aria2.pause(id)
} catch (e) {
}
}
}
import {App} from "./app";
import * as path from "path"
/**
* Created by weijian on 2016/10/24.
*/
export class InstallConfig {
export class InstallOption {
app: App;
install: boolean;
downloadFiles: string[];
installLibrary: string;
installDir: string;
get installDir(): string {
return path.join(this.installLibrary, this.app.id);
}
createShortcut: boolean;
createDesktopShortcut: boolean;
references: InstallConfig[];
constructor(app: App, installLibrary = "", installDir = "", install = true, shortcut = false, desktopShortcut = false) {
constructor(app: App, installLibrary = "", shortcut = false, desktopShortcut = false) {
this.app = app;
this.createShortcut = shortcut;
this.createDesktopShortcut = desktopShortcut;
this.install = install;
this.installDir = installDir;
this.installLibrary = installLibrary;
}
}
\ No newline at end of file
/**
* Created by weijian on 2016/11/2.
*/
import {Injectable} from "@angular/core";
import {App} from "./app";
import {InstallConfig} from "./install-config";
import {Injectable, ApplicationRef} from "@angular/core";
import {App, Category} from "./app";
import {InstallOption} from "./install-option";
import * as path from "path";
import * as child_process from "child_process";
import * as mkdirp from "mkdirp";
......@@ -14,18 +14,32 @@ import {AppLocal} from "./app-local";
import {Http} from "@angular/http";
import {AppsService} from "./apps.service";
import ReadableStream = NodeJS.ReadableStream;
import {Observable, Observer} from "rxjs/Rx";
export interface InstallTask {
app: App;
option: InstallOption;
}
export interface InstallStatus {
status: string;
progress: number;
total: number;
lastItem: string;
}
@Injectable()
export class InstallService {
tarPath: string;
installQueue: Map<App,InstallConfig> = new Map();
installingId: string = '';
eventEmitter: EventEmitter = new EventEmitter();
installingQueue: Set<App> = new Set();
checksumUri = "https://thief.mycard.moe/checksums/";
constructor(private http: Http, private appsService: AppsService) {
installQueue: Map<string,InstallTask> = new Map();
map: Map<string,string> = new Map();
constructor(private http: Http, private ref: ApplicationRef) {
if (process.platform === "win32") {
this.tarPath = path.join(process.resourcesPath, 'bin', 'bsdtar.exe');
} else {
......@@ -33,6 +47,100 @@ export class InstallService {
}
}
private createId(): string {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
// installProgress(id: string): Observable<InstallStatus>|undefined {
// let app = this.map.get(id);
// if (app) {
//
// }
// }
push(task: InstallTask): string {
let id = this.createId();
this.installQueue.set(id, task);
if (this.installQueue.size > 0 && this.installingId == '') {
this.doInstall();
}
return id;
}
async doInstall() {
if (this.installQueue.size > 0 && this.installingId == '') {
let [id,task] = this.installQueue.entries().next().value!;
this.installingId = id;
try {
let app = task.app;
let option = task.option;
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);
}
}
if (conflictFiles.size > 0) {
let backupPath = path.join(option.installLibrary, "backup", app.parent.id);
this.backupFiles(option.installDir, backupPath, conflictFiles);
}
}
let allFiles = new Set(checksumFile.keys());
app.status.status = "installing";
app.status.total = allFiles.size;
app.status.progress = 0;
let timeNow = new Date().getTime();
for (let file of option.downloadFiles) {
await this.createDirectory(option.installDir);
await new Promise((resolve, reject) => {
this.extract(file, option.installDir).subscribe(
(lastItem: string) => {
console.log(app.status.progress, app.status.total, lastItem);
app.status.progress += 1;
if (new Date().getTime() - timeNow > 500) {
timeNow = new Date().getTime();
this.ref.tick();
}
},
(error) => {
reject(error);
},
() => {
resolve();
});
});
}
let local = new AppLocal();
local.path = option.installDir;
local.files = checksumFile;
local.version = app.version;
app.local = local;
this.saveAppLocal(app);
app.status.status = "ready";
}
} catch (e) {
throw e;
}
finally {
this.installQueue.delete(id);
this.installingId = '';
if (this.installQueue.size > 0) {
this.doInstall();
}
}
}
}
createDirectory(dir: string) {
return new Promise((resolve, reject) => {
......@@ -48,23 +156,26 @@ export class InstallService {
});
}
extract(file: string, destPath: string) {
return new Promise((resolve, reject) => {
let tarProcess = child_process.spawn(this.tarPath, ['xvf', file, '-C', destPath]);
extract(file: string, dir: string): Observable<string> {
return Observable.create((observer: Observer<string>) => {
let tarProcess = child_process.spawn(this.tarPath, ['xvf', file, '-C', dir]);
let rl = readline.createInterface({
input: <ReadableStream>tarProcess.stderr,
});
rl.on('line', (input) => {
console.log(input);
rl.on('line', (input: string) => {
console.log(input.split(" ", 2));
observer.next(input.split(" ", 2)[1]);
});
tarProcess.on('exit', (code) => {
if (code === 0) {
resolve();
observer.complete();
} else {
reject(code);
observer.error(code);
}
})
});
});
return () => {
}
})
}
async postInstall(app: App, appPath: string) {
......@@ -107,12 +218,11 @@ export class InstallService {
}
}
async backupFiles(app: App, files: Iterable<string>) {
let backupPath = path.join((<AppLocal>app.local).path, "backup");
async backupFiles(dir: string, backupPath: string, files: Iterable<string>) {
await this.createDirectory(backupPath);
for (let file of files) {
await new Promise((resolve, reject) => {
let oldPath = path.join((<AppLocal>app.local).path, file);
let oldPath = path.join(dir, file);
let newPath = path.join(backupPath, file);
fs.rename(oldPath, newPath, resolve);
});
......@@ -130,6 +240,9 @@ export class InstallService {
for (let line of response.text().split('\n')) {
if (line !== "") {
let [checksum,filename]=line.split(' ', 2);
if (filename.endsWith("\\") || filename.endsWith("/")) {
map.set(filename, "");
}
map.set(filename, checksum);
}
}
......@@ -137,59 +250,6 @@ export class InstallService {
}).toPromise();
}
async doInstall() {
for (let app of this.installQueue.keys()) {
let depInstalled = app.findDependencies()
.every((dependency) => dependency.isInstalled());
if (depInstalled && !this.installingQueue.has(app)) {
this.installingQueue.add(app);
let options = <InstallConfig>this.installQueue.get(app);
let checksumMap = await this.getChecksumFile(app);
let packagePath = path.join(options.installLibrary, 'downloading', `${app.id}.tar.xz`);
if (["ygopro", 'desmume'].includes(app.id)) {
packagePath = path.join(options.installLibrary, 'downloading', `${app.id}-${process.platform}.tar.xz`);
}
let destPath: string;
if (app.parent) {
let differenceSet = new Set<string>();
let parentFilesMap = (<AppLocal>app.parent.local).files;
for (let key of checksumMap.keys()) {
if (parentFilesMap.has(key)) {
differenceSet.add(key);
}
}
await this.backupFiles(app.parent, differenceSet);
destPath = (<AppLocal>app.parent.local).path;
} else {
destPath = path.join(options.installLibrary, app.id);
await this.createDirectory(destPath);
}
await this.extract(packagePath, destPath);
await this.postInstall(app, destPath);
let local = new AppLocal();
local.path = destPath;
local.files = checksumMap;
local.version = app.version;
app.local = local;
this.saveAppLocal(app);
this.eventEmitter.emit(app.id, 'install complete');
this.installQueue.delete(app);
this.installingQueue.delete(app);
if (this.installQueue.size > 0) {
await this.doInstall()
}
}
}
}
add(app: App, options: InstallConfig) {
if (!this.installQueue.has(app)) {
this.installQueue.set(app, options);
if (!app.isInstalled()) {
this.doInstall()
}
}
}
deleteFile(file: string): Promise<string> {
return new Promise((resolve, reject) => {
......@@ -210,12 +270,12 @@ 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 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) {
......
......@@ -5,18 +5,30 @@
<ul *ngIf="grouped_apps.installed" class="nav nav-sidebar">
<li *ngFor="let app of grouped_apps.installed" [class.active]="app===currentApp">
<a (click)="chooseApp(app)" href="#">{{app.name}}</a>
<progress *ngIf="app.status.status === 'downloading'"
class="progress progress-striped progress-animated"
value="{{app.status.progress}}"
max="{{app.status.total}}"></progress>
</li>
</ul>
<span *ngIf="grouped_apps.yugioh">游戏王</span>
<ul *ngIf="grouped_apps.yugioh" class="nav nav-sidebar">
<li *ngFor="let app of grouped_apps.yugioh" [class.active]="app===currentApp">
<a (click)="chooseApp(app)" href="#">{{app.name}}</a>
<progress *ngIf="app.status.status === 'downloading'"
class="progress progress-striped progress-animated"
value="{{app.status.progress}}"
max="{{app.status.total}}"></progress>
</li>
</ul>
<span *ngIf="grouped_apps.touhou">东方 Project</span>
<ul *ngIf="grouped_apps.touhou" class="nav nav-sidebar">
<li *ngFor="let app of grouped_apps.touhou" [class.active]="app===currentApp">
<a (click)="chooseApp(app)" href="#">{{app.name}}</a>
<progress *ngIf="app.status.status === 'downloading'"
class="progress progress-striped progress-animated"
value="{{app.status.progress}}"
max="{{app.status.total}}"></progress>
</li>
</ul>
<span *ngIf="grouped_apps.touhou_pc98">东方旧作</span>
......
......@@ -9,7 +9,7 @@ import {DownloadService} from "./download.service";
import {InstallService} from "./install.service";
import {Http, URLSearchParams} from "@angular/http";
import * as path from "path";
import {InstallConfig} from "./install-config";
import {InstallOption} from "./install-option";
import {AppLocal} from "./app-local";
import WebViewElement = Electron.WebViewElement;
......@@ -85,16 +85,6 @@ export class LobbyComponent implements OnInit {
let dir = path.join(path.dirname((<AppLocal>app.local).path), "downloading");
let a = await this.downloadService.addMetalink(metalink, dir);
await new Promise((resolve, reject) => {
a.subscribe((status: any) => {
console.log(status);
}, (err: any) => {
reject()
}, () => {
resolve();
});
});
for (let file of deleteList) {
await this.installService.deleteFile(file);
}
......@@ -106,7 +96,7 @@ export class LobbyComponent implements OnInit {
for (let child of children) {
if (child.isInstalled()) {
await this.installService.uninstall(child, false);
this.installService.add(child, new InstallConfig(child, path.dirname(((<AppLocal>app.local).path))));
// this.installService.add(child, new InstallOption(child, path.dirname(((<AppLocal>app.local).path))));
await this.installService.getComplete(child);
console.log("282828")
}
......
/**
* Created by weijian on 2016/11/6.
*/
export class Task {
downloadUrl: string;
}
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