Commit 73ceaafc authored by 神楽坂玲奈's avatar 神楽坂玲奈

fix

parents 71684161 a20ada6d
/bin/ /bin/
/app/*.js /app/**/*.js
/app/*.js.map /app/**/*.js.map
/app/*.metadata.json /app/*.metadata.json
/node_modules/ /node_modules/
/dist/ /dist/
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
<div> <div>
<span *ngIf="currentApp.isDownloading()">正在下载</span> <span *ngIf="currentApp.isDownloading()">正在下载</span>
<span *ngIf="currentApp.isInstalling()">正在安装...</span> <span *ngIf="currentApp.isInstalling()">正在安装...</span>
<span *ngIf="currentApp.isUninstalling()">正在卸载...</span>
<span *ngIf="currentApp.isWaiting()">等待安装...</span> <span *ngIf="currentApp.isWaiting()">等待安装...</span>
<span *ngIf="currentApp.status.total">{{(currentApp.status.progress/currentApp.status.total * 100).toFixed()}}%</span> <span *ngIf="currentApp.status.total">{{(currentApp.status.progress/currentApp.status.total * 100).toFixed()}}%</span>
<span>{{currentApp.progressMessage()}}</span> <span>{{currentApp.progressMessage()}}</span>
...@@ -59,13 +60,15 @@ ...@@ -59,13 +60,15 @@
<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> </td>
<div i18n *ngIf="mod.status.status==='waiting'">等待安装...</div> <td *ngIf="mod.isInstalled()&&!mod.isReady()">
<progress class="progress progress-striped progress-animated" value="{{mod.status.progress}}" max="{{mod.status.total}}"></progress>
<!--<div i18n *ngIf="mod.isWaiting()">等待安装...</div>-->
</td> </td>
</tr> </tr>
</tbody> </tbody>
...@@ -91,8 +94,9 @@ ...@@ -91,8 +94,9 @@
<p i18n>即将开始安装 {{currentApp.name}}</p> <p i18n>即将开始安装 {{currentApp.name}}</p>
<h4 i18n>安装位置</h4> <h4 i18n>安装位置</h4>
<div class="form-group"> <div class="form-group">
<select class="form-control" name="installPath" [(ngModel)]="installOption.installLibrary" title="path"> <select class="form-control" name="installPath" (change)="selectLibrary()" [(ngModel)]="installOption.installLibrary" title="path">
<option *ngFor="let library of libraries" value="{{library}}"> {{library}}</option> <option *ngFor="let library of libraries" value="{{library}}"> {{library}}</option>
<option *ngFor="let library of availableLibraries" value="create_{{library}}">在 {{library}}\ 盘新建 MyCard 库</option>
</select></div> </select></div>
<h4 i18n>快捷方式</h4> <h4 i18n>快捷方式</h4>
<div class="checkbox"> <div class="checkbox">
...@@ -104,10 +108,10 @@ ...@@ -104,10 +108,10 @@
<input id="create_desktop_shortcut" type="checkbox" name="desktop" [(ngModel)]="installOption.createDesktopShortcut"> <input id="create_desktop_shortcut" type="checkbox" name="desktop" [(ngModel)]="installOption.createDesktopShortcut">
<label i18n for="create_desktop_shortcut">创建桌面快捷方式</label> <label i18n for="create_desktop_shortcut">创建桌面快捷方式</label>
</div> </div>
<!--<h4 *ngIf="installOption.references.length">扩展内容</h4>--> <h4 *ngIf="references.length>0">扩展内容</h4>
<!--<div *ngFor="let reference of installOption.references"><label>--> <div *ngFor="let reference of references"><label>
<!--<input type="checkbox" [(ngModel)]="reference.install" name="references" value="{{reference.app.id}}"> {{reference.app.name}}--> <input type="checkbox" [(ngModel)]="referencesInstall[reference.id]" name="references"> {{reference.name}}
<!--</label></div>--> </label></div>
<div *ngIf="currentApp.findDependencies().length"> <div *ngIf="currentApp.findDependencies().length">
<span i18n>依赖:</span> <span i18n>依赖:</span>
<span class="dependency" *ngFor="let dependency of currentApp.findDependencies()">{{dependency.name}}</span> <span class="dependency" *ngFor="let dependency of currentApp.findDependencies()">{{dependency.name}}</span>
......
...@@ -6,7 +6,9 @@ import {App} from "./app"; ...@@ -6,7 +6,9 @@ import {App} from "./app";
import {DownloadService} from "./download.service"; import {DownloadService} from "./download.service";
import {clipboard, remote} from "electron"; import {clipboard, remote} from "electron";
import * as path from "path"; import * as path from "path";
import * as fs from 'fs';
import {InstallService} from "./install.service"; import {InstallService} from "./install.service";
import mkdirp = require("mkdirp");
declare const Notification: any; declare const Notification: any;
declare const $: any; declare const $: any;
...@@ -23,22 +25,61 @@ export class AppDetailComponent implements OnInit { ...@@ -23,22 +25,61 @@ export class AppDetailComponent implements OnInit {
platform = process.platform; platform = process.platform;
installOption: InstallOption; installOption: InstallOption;
availableLibraries: string[] = [];
references: App[];
referencesInstall: {[id: string]: boolean};
constructor(private appsService: AppsService, private settingsService: SettingsService, constructor(private appsService: AppsService, private settingsService: SettingsService,
private downloadService: DownloadService, private installService: InstallService, private downloadService: DownloadService, private installService: InstallService,
private ref: ChangeDetectorRef) { private ref: ChangeDetectorRef) {
} }
// public File[] listRoots() {
// int ds = listRoots0();
// int n = 0;
// for (int i = 0; i < 26; i++) {
// if (((ds >> i) & 1) != 0) {
// if (!access((char)('A' + i) + ":" + slash))
// ds &= ~(1 << i);
// else
// n++;
// }
// }
// File[] fs = new File[n];
// int j = 0;
// char slash = this.slash;
// for (int i = 0; i < 26; i++) {
// if (((ds >> i) & 1) != 0)
// fs[j++] = new File((char)('A' + i) + ":" + slash);
// }
// return fs;
// }
ngOnInit() { ngOnInit() {
let volume = 'A';
for (let i = 0; i < 26; i++) {
new Promise((resolve, reject) => {
let currentVolume = String.fromCharCode(volume.charCodeAt(0) + i) + ":";
fs.access(currentVolume, (err) => {
if (!err) {
//判断是否已经存在Library
if (this.libraries.every((library) => !library.startsWith(currentVolume))) {
this.availableLibraries.push(currentVolume);
}
}
})
})
}
} }
updateInstallOption(app: App) { updateInstallOption(app: App) {
this.installOption = new InstallOption(app); this.installOption = new InstallOption(app);
this.installOption.installLibrary = this.settingsService.getDefaultLibrary().path; this.installOption.installLibrary = this.settingsService.getDefaultLibrary().path;
// this.installOption.references = []; this.references = Array.from(app.references.values());
// for (let reference of app.references.values()) { console.log(this.references);
// this.installOption.references.push(new InstallOption(reference)) this.referencesInstall = {};
// } for (let reference of this.references) {
this.referencesInstall[reference.id] = true;
}
} }
get libraries(): string[] { get libraries(): string[] {
...@@ -71,25 +112,39 @@ export class AppDetailComponent implements OnInit { ...@@ -71,25 +112,39 @@ 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);
if (this.references.length > 0) {
for (let [id,isInstalled] of Object.entries(this.referencesInstall)) {
if (isInstalled) {
let reference = targetApp.references.get(id)!;
await this.appsService.install(reference, options);
}
}
}
} catch (e) { } catch (e) {
console.error(e); console.error(e);
new Notification(targetApp.name, {body: "下载失败"}); new Notification(targetApp.name, {body: "下载失败"});
} }
} }
async selectLibrary() {
if (this.installOption.installLibrary.startsWith('create_')) {
let volume = this.installOption.installLibrary.slice(7);
let library = path.join(volume, "MyCardLibrary");
try {
await this.installService.createDirectory(library);
this.installOption.installLibrary = library;
this.settingsService.addLibrary(library, true);
} catch (e) {
this.installOption.installLibrary = this.settingsService.getDefaultLibrary().path;
alert("无法创建指定目录");
}
} else {
this.settingsService.setDefaultLibrary({path: this.installOption.installLibrary, "default": true})
}
}
selectDir() { selectDir() {
let dir = remote.dialog.showOpenDialog({properties: ['openFile', 'openDirectory']}); let dir = remote.dialog.showOpenDialog({properties: ['openFile', 'openDirectory']});
console.log(dir); console.log(dir);
......
...@@ -100,6 +100,9 @@ export class App { ...@@ -100,6 +100,9 @@ export class App {
isDownloading(): boolean { isDownloading(): boolean {
return this.status.status === "downloading"; return this.status.status === "downloading";
} }
isUninstalling():boolean{
return this.status.status==="uninstalling";
}
runable(): boolean { runable(): boolean {
return [Category.game].includes(this.category); return [Category.game].includes(this.category);
......
...@@ -10,10 +10,11 @@ import {remote} from "electron"; ...@@ -10,10 +10,11 @@ import {remote} from "electron";
import "rxjs/Rx"; import "rxjs/Rx";
import {AppLocal} from "./app-local"; import {AppLocal} from "./app-local";
import * as ini from "ini"; import * as ini from "ini";
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 Timer = NodeJS.Timer; 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;
...@@ -158,27 +215,30 @@ export class AppsService { ...@@ -158,27 +215,30 @@ export class AppsService {
}) })
}); });
}; };
try { if (!app.isInstalled()) {
let apps: App[] = []; try {
let dependencies = app.findDependencies(); let apps: App[] = [];
apps.push(...dependencies, app); let dependencies = app.findDependencies().filter((dependency) => {
let downloadPath = path.join(option.installLibrary, 'downloading'); return !dependency.isInstalled();
let tasks: Promise<any>[] = []; });
for (let a of apps) { apps.push(...dependencies, app);
tasks.push(addDownloadTask(a, downloadPath)); let downloadPath = path.join(option.installLibrary, 'downloading');
} let tasks: Promise<any>[] = [];
let downloadResults = await Promise.all(tasks); for (let a of apps) {
for (let result of downloadResults) { tasks.push(addDownloadTask(a, downloadPath));
console.log(result); }
let o = new InstallOption(result.app, option.installLibrary); let downloadResults = await Promise.all(tasks);
o.downloadFiles = result.files; for (let result of downloadResults) {
this.installService.push({app: result.app, option: o}); console.log(result);
let o = new InstallOption(result.app, option.installLibrary);
o.downloadFiles = result.files;
this.installService.push({app: result.app, option: o});
}
} catch (e) {
app.status.status = 'init';
console.log(e);
throw e;
} }
// this.installService.push({app: app, option: option})
} catch (e) {
console.log(e);
throw e;
} }
} }
...@@ -231,12 +291,12 @@ export class AppsService { ...@@ -231,12 +291,12 @@ export class AppsService {
} }
if (action.open) { if (action.open) {
let np2 = <App>action.open; let np2 = action.open;
let openAction: Action; let openAction: Action;
openAction = <Action>np2.actions.get('main'); openAction = np2.actions.get('main')!;
let openPath = (<AppLocal>np2.local).path; let openPath = np2.local!.path;
if (action.open.id == 'np2fmgen') { if (action.open.id == 'np2fmgen') {
const config_file = path.join((<AppLocal>(<App>action.open).local).path, 'np21nt.ini'); const config_file = path.join(action.open!.local!.path, 'np21nt.ini');
let config = await new Promise((resolve, reject) => { let config = await new Promise((resolve, reject) => {
fs.readFile(config_file, {encoding: 'utf-8'}, (error, data) => { fs.readFile(config_file, {encoding: 'utf-8'}, (error, data) => {
if (error) return reject(error); if (error) return reject(error);
...@@ -252,7 +312,7 @@ export class AppsService { ...@@ -252,7 +312,7 @@ export class AppsService {
windtype: '0' windtype: '0'
}; };
config['NekoProject21'] = Object.assign({}, default_config, config['NekoProject21']); config['NekoProject21'] = Object.assign({}, default_config, config['NekoProject21']);
config['NekoProject21']['HDD1FILE'] = path.win32.join(process.platform == 'win32' ? '' : 'Z:', (<AppLocal>app.local).path, action.execute); config['NekoProject21']['HDD1FILE'] = path.win32.join(process.platform == 'win32' ? '' : 'Z:', app.local!.path, action.execute);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
fs.writeFile(config_file, ini.stringify(config), (error) => { fs.writeFile(config_file, ini.stringify(config), (error) => {
if (error) { if (error) {
...@@ -262,12 +322,15 @@ export class AppsService { ...@@ -262,12 +322,15 @@ export class AppsService {
} }
}) })
}); });
args.push(openAction.execute);
args = args.concat(openAction.args); if (process.platform != 'win32') {
let wine = <App>openAction.open; args.push(openAction.execute);
openPath = (<AppLocal>wine.local).path; args = args.concat(openAction.args);
openAction = <Action>(<App>openAction.open).actions.get('main'); let wine = openAction.open!;
cwd = (<AppLocal>np2.local).path; openPath = wine.local!.path;
openAction = openAction!.open!.actions.get('main')!;
}
cwd = np2.local!.path;
} }
args = args.concat(openAction.args); args = args.concat(openAction.args);
args.push(action.execute); args.push(action.execute);
......
...@@ -83,10 +83,12 @@ export class DownloadService { ...@@ -83,10 +83,12 @@ export class DownloadService {
let newDownloadSpeed = 0; let newDownloadSpeed = 0;
for (let [index,gid] of gidList.entries()) { for (let [index,gid] of gidList.entries()) {
let task = this.taskList.get(gid)!; let task = this.taskList.get(gid)!;
statusList[index] = task.status; if (task) {
newCompletedLength += parseInt(task.completedLength); statusList[index] = task.status;
newTotalLength += parseInt(task.totalLength); newCompletedLength += parseInt(task.completedLength);
newDownloadSpeed += parseInt(task.downloadSpeed); newTotalLength += parseInt(task.totalLength);
newDownloadSpeed += parseInt(task.downloadSpeed);
}
} }
if (newCompletedLength !== completedLength || newTotalLength !== totalLength) { if (newCompletedLength !== completedLength || newTotalLength !== totalLength) {
completedLength = newCompletedLength; completedLength = newCompletedLength;
......
...@@ -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,33 @@ export class InstallService { ...@@ -280,39 +291,33 @@ 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); app.status.status = "uninstalling";
// for (let child of children) { let appDir = app.local!.path;
// if (child.isInstalled()) { let files = Array.from(app.local!.files.keys()).sort().reverse();
// await this.uninstall(child);
// } app.status.total = files.length;
// }
} for (let file of files) {
let files = Array.from((<AppLocal>app.local).files.keys()).sort().reverse(); app.status.progress += 1;
for (let file of files) { await this.deleteFile(path.join(appDir, file));
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() {
......
...@@ -36,6 +36,28 @@ export class SettingsService { ...@@ -36,6 +36,28 @@ export class SettingsService {
return this.libraries; return this.libraries;
} }
addLibrary(libraryPath: string, isDefault: boolean) {
let libraries = this.getLibraries();
if (isDefault) {
libraries.forEach((l) => {
l.default = false;
});
}
libraries.push({"default": isDefault, path: libraryPath});
this.libraries = libraries;
localStorage.setItem(SettingsService.SETTING_LIBRARY, JSON.stringify(libraries));
}
setDefaultLibrary(library: Library) {
let libraries = this.getLibraries();
libraries.forEach((l) => {
l.default = library.path == l.path;
});
this.libraries = libraries;
localStorage.setItem(SettingsService.SETTING_LIBRARY, JSON.stringify(libraries));
}
getDefaultLibrary(): Library { getDefaultLibrary(): Library {
if (!this.libraries) { if (!this.libraries) {
this.getLibraries() this.getLibraries()
......
/**
* 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;
}
}
...@@ -2070,7 +2070,8 @@ ...@@ -2070,7 +2070,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