forked from Allar/ue4-mp-downloader
-
Notifications
You must be signed in to change notification settings - Fork 3
/
manifests.js
177 lines (155 loc) · 5.6 KB
/
manifests.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
"use strict";
// Warning: Here be dragons
// Normally don't need to do this global bullshit but 'epic_api.js' is used in another JS app that requires it
// So instead of maintaining two copies of this api, we'll just re-use it like this
// @TODO: Learn how to do all this the right way
global.request = (global.request === undefined) ? require('request') : global.request;
global.request = request.defaults({followRedirect: false, followAllRedirects: false});
global.epic_api = (global.epic_api === undefined) ? require('./epic_api.js') : global.epic_api;
const prompt = require('prompt');
const cheerio = require('cheerio');
const menu = require('console-menu');
const fs = require('fs');
const path = require('path');
const mkdirp = require('mkdirp');
// Takes an HTML form from cheerio.serializeArray() and converts it to an object suitable for the 'request' module
function SerializeLoginFormArray(form) {
var result = {};
form.forEach((element) => {
result[element.name] = element.value;
});
return result;
}
// Ask for username/password from the user
var promptSchema = {
properties: {
username: {
required: true,
type: 'string'
},
password: {
required: true,
type:'string',
hidden: true,
replace: '*'
}
}
};
function cleanEmptyFoldersRecursively(folder) {
var isDir = fs.statSync(folder).isDirectory();
if (!isDir) {
return;
}
var files = fs.readdirSync(folder);
if (files.length > 0) {
files.forEach(function(file) {
var fullPath = path.join(folder, file);
cleanEmptyFoldersRecursively(fullPath);
});
// re-evaluate files; after deleting subfolder
// we may have parent folder empty now
files = fs.readdirSync(folder);
}
if (files.length == 0) {
console.log("removing: ", folder);
fs.rmdirSync(folder);
return;
}
}
// Error handling is for smart people
// We are not smart today
function TryLogin() {
// If Epic's login page is down for some reason, we should probably handle it somehow
epic_api.GetWebLoginForm( (body) => {
prompt.start();
prompt.get(promptSchema, (err, result) => {
if (result == undefined || result.username == undefined) {
process.exit(0); // Control+C
}
const $ = cheerio.load(body);
var loginData = SerializeLoginFormArray($('form#loginForm').serializeArray());
loginData.epic_username = result.username;
loginData.password = result.password;
epic_api.WebLogin(loginData, OnLogin);
});
});
}
// Return error codes for WebLogin are retarded and should be hardcoded to sane values
// I was probably drunk when writing epic_api.js
function OnLogin(status, complete) {
if (status === 'Failed') {
console.log("Failed to log in.");
TryLogin();
return;
}
console.log(status);
// If for some reason the login chain fails but doesn't complete, theres no error handling
// The log above *should* log the login chain failure and execution *should* just stop.
// Theres a lot of assumptions being made because my test sample count is 1.
if (complete == true) {
epic_api.GetOwnedAssets( (success) => {
var items = [];
Object.keys(global.global.marketplace_ownedAssets_consolidated).forEach( (key) => {
if (global.marketplace_ownedAssets_consolidated[key].developer == "Epic Games") // Epic examples returning 403?
return;
var isAsset = global.marketplace_ownedAssets_consolidated[key].categories.find ( (cat) => {
return (cat.path == "assets" || cat.path == "projects" || cat.path == "plugins")
});
if (isAsset) {
items.push(global.marketplace_ownedAssets_consolidated[key]);
mkdirp.sync('./dump/buildinfo/' + global.marketplace_ownedAssets_consolidated[key].id)
mkdirp.sync('./dump/manifests/' + global.marketplace_ownedAssets_consolidated[key].id)
}
});
// Sort items alphabetically
items.sort( (a, b) => {
if (a.title < b.title) return -1;
if (a.title > b.title) return 1;
return 0;
});
var itemVersions = [];
items.forEach( (item) => {
item.releaseInfo.forEach( (versionInfo) => {
versionInfo.catalogItemId = item.id;
versionInfo.title = item.title;
itemVersions.push(versionInfo);
});
});
var buildInfos = [];
var failures = 0;
itemVersions.forEach( (version) => {
global.epic_api.GetItemBuildInfo(version.catalogItemId, version.appId, (error, buildinfo) => {
if (error !== null) {
failures++;
} else {
buildinfo.title = version.title;
fs.writeFileSync('./dump/buildinfo/' + version.catalogItemId + '/' + version.appId, JSON.stringify(buildinfo, null, '\t'));
buildInfos.push(buildinfo);
}
if (buildInfos.length + failures == itemVersions.length) {
console.log("All possible build info items fetched.");
var manifestWrites = 0;
buildInfos.forEach( (storedBuildInfo) => {
global.epic_api.GetItemManifest(storedBuildInfo, (manifestError, manifest) => {
manifestWrites++;
if (manifestError == null) {
manifest.catalogItemId = storedBuildInfo.catalogItemId;
manifest.title = storedBuildInfo.title;
fs.writeFileSync('./dump/manifests/' + manifest.catalogItemId + '/' + storedBuildInfo.appName + '.json', JSON.stringify(manifest, null, '\t'));
fs.writeFileSync('./dump/manifests/' + manifest.catalogItemId + '/title.json', JSON.stringify({title: storedBuildInfo.title}, null, '\t'));
}
if (manifestWrites == buildInfos.length) {
cleanEmptyFoldersRecursively('./dump/');
console.log("Dumped all manifests.");
process.exit(0);
return;
}
});
})
}
});
});
});
};
}
TryLogin();