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
This diff is collapsed.
......@@ -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