UNPKG

10.1 kBPlain TextView Raw
1import { getIfUpdate, UpdatedObject } from "@xmcl/net";
2import * as parser from "fast-html-parser";
3import { ForgeInstaller } from "./index";
4
5export namespace ForgeWebPage {
6
7 export interface Download {
8 md5: string;
9 sha1: string;
10 path: string;
11 }
12
13 /**
14 * Parse the html string of forge webpage
15 */
16 export function parse(content: string): ForgeWebPage {
17 const dom = parser.parse(content);
18 const selected = dom.querySelector(".elem-active");
19 const mcversion = selected.text;
20 return {
21 timestamp: "",
22 mcversion,
23 versions: dom.querySelector(".download-list").querySelector("tbody").querySelectorAll("tr")
24 .map((e) => {
25 const links = e.querySelector(".download-links").childNodes
26 .filter((elem) => elem.tagName === "li")
27 .map((elem) => {
28 elem = elem.removeWhitespace();
29 /*
30 * <div class="info-tooltip">
31 * <strong>MD5:</strong> 31742b6c996f53af96f606b7a0c46e2a<br>
32 * <strong>SHA1:</strong> 8d6a23554839d6f6014fbdb7991e3cd8af7eca80
33 * </div>
34 */
35 const tooltipInfo = elem.querySelector(".info-tooltip");
36 const url = tooltipInfo.querySelector("a") || elem.querySelector("a");
37 // href is like /maven/net/minecraftforge/forge/1.14.4-28.1.70/forge-1.14.4-28.1.70-changelog.txt
38 const href = url.attributes.href.trim();
39 const matched = /forge-.+-.+-(\w+)\.\w+/.exec(href);
40 let name = "", sha1 = "", md5 = "";
41 if (matched) { name = matched[1]; }
42 if (!name) {
43 throw new Error(`Cannot determine name for forge url "${href}". Maybe the forge webisite changed?`);
44 }
45 try {
46 md5 = tooltipInfo.childNodes[1].text.trim();
47 sha1 = tooltipInfo.childNodes[4].text.trim();
48 } catch {
49 console.warn(`Error during fetching the sha1 and md5 for the forge "${href}". The result might be wrong.`);
50 }
51 const isSha1 = /\b([a-f0-9]{40})\b/i
52 const isMd5 = /\b[a-f0-9]{32}\b/i;
53 if (!isMd5.test(md5.trim())) {
54 console.warn(`Illegal Md5 for "${href}": ${md5}`)
55 md5 = "";
56 }
57 if (!isSha1.test(sha1.trim())) {
58 console.warn(`Illegal Sha1 for "${href}": ${sha1}`)
59 sha1 = "";
60 }
61 return {
62 name,
63 md5,
64 sha1,
65 path: href,
66 };
67 });
68 const downloadVersionElem = e.querySelector(".download-version");
69 let version;
70 let type: ForgeWebPage.Version["type"] = "common";
71 const icon = downloadVersionElem.querySelector("i");
72 if (icon) {
73 if (icon.classNames.indexOf("promo-recommended") !== -1) {
74 type = "recommended";
75 } else if (icon.classNames.indexOf("promo-latest") !== -1) {
76 type = "latest";
77 } else if (icon.classNames.indexOf("fa-bug") !== -1) {
78 type = "buggy";
79 }
80 version = downloadVersionElem.firstChild.text.trim();
81 } else {
82 version = downloadVersionElem.text.trim();
83 }
84 const installer = links.find((l) => l.name === "installer");
85 const universal = links.find((l) => l.name === "universal");
86 const changelog = links.find((l) => l.name === "changelog");
87 const installerWin = links.find((l) => l.name === "installer-win");
88 const source = links.find((l) => l.name === "source");
89 const launcher = links.find((l) => l.name === "launcher");
90
91 const mdk = links.find((l) => l.name === "mdk");
92
93 if (installer === undefined || universal === undefined) {
94 throw new Error("Cannot parse forge web since it missing installer and universal jar info.");
95 }
96 const result = {
97 version,
98 "date": e.querySelector(".download-time").text.trim(),
99 changelog,
100 installer,
101 mdk,
102 universal,
103 source,
104 launcher,
105 "installer-win": installerWin,
106 "mcversion": mcversion,
107 type,
108 };
109
110 return result;
111 }),
112 };
113 }
114
115 /**
116 * Query the webpage content from files.minecraftforge.net.
117 *
118 * You can put the last query result to the fallback option. It will check if your old result is up-to-date.
119 * It will request a new page only when the fallback option is outdated.
120 *
121 * @param option The option can control querying minecraft version, and page caching.
122 */
123 export function getWebPage(): Promise<ForgeWebPage | undefined>;
124 /**
125 * Query the webpage content from files.minecraftforge.net.
126 *
127 * You can put the last query result to the fallback option. It will check if your old result is up-to-date.
128 * It will request a new page only when the fallback option is outdated.
129 *
130 * @param option The option can control querying minecraft version, and page caching.
131 */
132 export function getWebPage(option?: {
133 mcversion?: string;
134 }): Promise<ForgeWebPage | undefined>;
135 /**
136 * Query the webpage content from files.minecraftforge.net.
137 *
138 * You can put the last query result to the fallback option. It will check if your old result is up-to-date.
139 * It will request a new page only when the fallback option is outdated.
140 *
141 * @param option The option can control querying minecraft version, and page caching.
142 */
143 export function getWebPage(option?: {
144 mcversion?: string;
145 fallback?: ForgeWebPage;
146 }): Promise<ForgeWebPage | undefined>;
147 /**
148 * Query the webpage content from files.minecraftforge.net.
149 *
150 * You can put the last query result to the fallback option. It will check if your old result is up-to-date.
151 * It will request a new page only when the fallback option is outdated.
152 *
153 * @param option The option can control querying minecraft version, and page caching.
154 */
155 export function getWebPage(option?: {
156 mcversion?: string;
157 fallback: ForgeWebPage;
158 }): Promise<ForgeWebPage>;
159
160 /**
161 * Query the webpage content from files.minecraftforge.net.
162 *
163 * You can put the last query result to the fallback option. It will check if your old result is up-to-date.
164 * It will request a new page only when the fallback option is outdated.
165 *
166 * @param option The option can control querying minecraft version, and page caching.
167 */
168 export async function getWebPage(option: {
169 mcversion?: string,
170 fallback?: ForgeWebPage,
171 } = {}): Promise<ForgeWebPage | undefined> {
172 const mcversion = option.mcversion || "";
173 const url = mcversion === "" ? "http://files.minecraftforge.net/maven/net/minecraftforge/forge/index.html" : `http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_${mcversion}.html`;
174 const page = await getIfUpdate(url, parse, option.fallback);
175 return page;
176 }
177
178 /**
179 * A richer version info than forge installer required
180 */
181 export interface Version extends ForgeInstaller.VersionMeta {
182 /**
183 * The minecraft version
184 */
185 mcversion: string;
186 /**
187 * The version of forge
188 */
189 version: string;
190 date: string;
191 /**
192 * The changelog info
193 */
194 changelog?: ForgeWebPage.Download;
195 installer: ForgeWebPage.Download;
196 mdk?: ForgeWebPage.Download;
197 universal: ForgeWebPage.Download;
198 source?: ForgeWebPage.Download;
199 launcher?: ForgeWebPage.Download;
200
201 /**
202 * The type of the forge release. The `common` means the normal release.
203 */
204 type: "buggy" | "recommended" | "common" | "latest";
205 }
206
207 export namespace Version {
208 export function to(webPageVersion: ForgeWebPage.Version): ForgeInstaller.VersionMeta {
209 return {
210 universal: webPageVersion.universal,
211 installer: webPageVersion.installer,
212 mcversion: webPageVersion.mcversion,
213 version: webPageVersion.version,
214 };
215 }
216 }
217}
218
219declare module "./index" {
220 export namespace VersionMeta {
221 export function from(webPageVersion: ForgeWebPage.Version): ForgeInstaller.VersionMeta;
222 }
223}
224
225(ForgeInstaller as any).VersionMeta = (ForgeInstaller as any).VersionMeta || {};
226(ForgeInstaller as any).VersionMeta.from = ForgeWebPage.Version.to;
227
228export interface ForgeWebPage extends UpdatedObject {
229 versions: ForgeWebPage.Version[];
230 mcversion: string;
231}
232
233
234