feat:改了一大堆
This commit is contained in:
148
backend/src/utils/DeEarth.ts
Normal file
148
backend/src/utils/DeEarth.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import fs from "node:fs"
|
||||
import crypto from "node:crypto"
|
||||
import { yauzl_promise } from "./yauzl.promise.js"
|
||||
import got from "got"
|
||||
import { Utils } from "./utils.js"
|
||||
interface IMixins{
|
||||
name: string
|
||||
data: string
|
||||
}
|
||||
|
||||
interface IFile{
|
||||
filename: string
|
||||
hash: string
|
||||
mixins: IMixins[]
|
||||
}
|
||||
|
||||
interface IHashRes{
|
||||
[key:string]:{
|
||||
project_id: string
|
||||
}
|
||||
}
|
||||
interface IPjs{
|
||||
id:string,
|
||||
client_side:string,
|
||||
server_side:string
|
||||
}
|
||||
export class DeEarth{
|
||||
movepath: string
|
||||
modspath: string
|
||||
file: IFile[]
|
||||
utils: Utils
|
||||
constructor(modspath:string) {
|
||||
this.utils = new Utils();
|
||||
this.movepath = "./.rubbish"
|
||||
this.modspath = modspath
|
||||
this.file = []
|
||||
}
|
||||
|
||||
async Main(){
|
||||
if(!fs.existsSync(this.movepath)){
|
||||
fs.mkdirSync(this.movepath,{recursive:true})
|
||||
}
|
||||
await this.getFile()
|
||||
const hash = await this.Check_Hashes()
|
||||
const mixins = await this.Check_Mixins()
|
||||
const result = [...new Set(hash.concat(mixins))]
|
||||
result.forEach(async e=>{
|
||||
await fs.promises.rename(`${this.modspath}/${e}`,`${this.movepath}/${e}`)
|
||||
})
|
||||
}
|
||||
|
||||
async Check_Hashes(){
|
||||
const cmap = new Map<string,string>()
|
||||
const fmap = new Map<string,string>()
|
||||
const hashes:string[] = []
|
||||
const files = this.file.forEach(e=>{
|
||||
hashes.push(e.hash);
|
||||
cmap.set(e.hash,e.filename)
|
||||
})
|
||||
const res = await got.post(this.utils.modrinth_url+"/v2/version_files",{
|
||||
headers:{
|
||||
"User-Agent": "DeEarth",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
json:{
|
||||
hashes,
|
||||
algorithm: "sha1"
|
||||
}
|
||||
}).json<IHashRes>()
|
||||
const x = Object.keys(res)
|
||||
const arr = []
|
||||
const fhashes = []
|
||||
for(let i=0;i<x.length;i++){
|
||||
const e = x[i] //hash
|
||||
const d = res[e] //hash object
|
||||
const result = cmap.get(e)
|
||||
if(result){
|
||||
fmap.set(d.project_id,result)
|
||||
}
|
||||
fhashes.push(e)
|
||||
arr.push(d.project_id)
|
||||
|
||||
}
|
||||
const mpres = await got.get(`${this.utils.modrinth_url}/v2/projects?ids=${JSON.stringify(arr)}`,{
|
||||
headers:{
|
||||
"User-Agent": "DeEarth"
|
||||
}
|
||||
}).json<IPjs[]>()
|
||||
const result = [] //要删除的文件
|
||||
for(let i=0;i<mpres.length;i++){
|
||||
const e = mpres[i]
|
||||
if(e.client_side==="required" && e.server_side==="unsupported"){
|
||||
const f = fmap.get(e.id)
|
||||
result.push(f)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async Check_Mixins(){
|
||||
//const files = await this.getFile()
|
||||
const files = this.file
|
||||
const result:string[] = []
|
||||
for(let i=0;i<files.length;i++){
|
||||
const file = files[i]
|
||||
file.mixins.forEach(e=>{
|
||||
try{
|
||||
const json = JSON.parse(e.data);
|
||||
if(this._isClientMx(file,json)){
|
||||
result.push(file.filename)
|
||||
}
|
||||
}catch(e){}
|
||||
})
|
||||
}
|
||||
const _result = [...new Set(result)]
|
||||
return _result;
|
||||
}
|
||||
async getFile():Promise<IFile[]>{
|
||||
const files = this.getDir()
|
||||
const arr = []
|
||||
for(let i=0;i<files.length;i++){
|
||||
const _file = files[i]
|
||||
const file = `${this.modspath}/${_file}`
|
||||
const data = fs.readFileSync(file)
|
||||
const sha1 = crypto.createHash('sha1').update(data).digest('hex') //Get Hash
|
||||
const mxarr:{name:string,data:string}[] = []
|
||||
const mixins = (await yauzl_promise(data)).forEach(async e=>{ //Get Mixins Info to check
|
||||
if(e.fileName.endsWith(".mixins.json")&&!e.fileName.includes("/")){
|
||||
mxarr.push({name:e.fileName,data:(await e.ReadEntry).toString()})
|
||||
}
|
||||
})
|
||||
arr.push({filename:file,hash:sha1,mixins:mxarr})
|
||||
}
|
||||
this.file = arr
|
||||
return arr;
|
||||
}
|
||||
private getDir():string[]{
|
||||
if(!fs.existsSync(this.movepath)){
|
||||
fs.mkdirSync(this.movepath)
|
||||
}
|
||||
const dirarr = fs.readdirSync(this.modspath).filter(e=>e.endsWith(".jar")).filter(e=>e.concat(this.modspath));
|
||||
return dirarr
|
||||
}
|
||||
|
||||
private _isClientMx(file:IFile,mixins:any){
|
||||
return (!("mixins" in mixins) || mixins.mixins.length === 0)&&(("client" in mixins) && (mixins.client.length !== 0))&&!file.filename.includes("lib")
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import fs from "fs";
|
||||
interface IConfig {
|
||||
import fs from "node:fs";
|
||||
export interface IConfig {
|
||||
mirror: {
|
||||
bmclapi: boolean;
|
||||
mcimirror: boolean;
|
||||
|
||||
@@ -2,7 +2,7 @@ import pMap from "p-map";
|
||||
import config from "./config.js";
|
||||
import got from "got";
|
||||
import pRetry from "p-retry";
|
||||
import fs from "fs";
|
||||
import fs from "node:fs";
|
||||
import fse from "fs-extra";
|
||||
|
||||
export class Utils {
|
||||
@@ -24,18 +24,42 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
export function mavenToUrl(
|
||||
coordinate: { split: (arg0: string) => [any, any, any, any] },
|
||||
base = "maven"
|
||||
) {
|
||||
const [g, a, v, ce] = coordinate.split(":");
|
||||
const [c, e = "jar"] = (ce || "").split("@");
|
||||
return `${base.replace(/\/$/, "")}/${g.replace(
|
||||
/\./g,
|
||||
"/"
|
||||
)}/${a}/${v}/${a}-${v}${c ? "-" + c : ""}.${e}`;
|
||||
}
|
||||
|
||||
export function version_compare(v1: string, v2: string) {
|
||||
const v1_arr = v1.split(".");
|
||||
const v2_arr = v2.split(".");
|
||||
for (let i = 0; i < v1_arr.length; i++) {
|
||||
if (v1_arr[i] !== v2_arr[i]) {
|
||||
return v1_arr[i] > v2_arr[i] ? 1 : -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export async function fastdownload(data: [string, string]) {
|
||||
return await pMap(
|
||||
data,
|
||||
async (e) => {
|
||||
[data],
|
||||
async (e:any) => {
|
||||
try {
|
||||
await pRetry(
|
||||
async () => {
|
||||
if (!fs.existsSync(e[1])) {
|
||||
/*
|
||||
const size: number = await (async () => {
|
||||
const head = (
|
||||
await got.head(
|
||||
e[0] /*.replace("https://mod.mcimirror.top","https://edge.forgecdn.net")*/,
|
||||
e[0],
|
||||
{ headers: { "user-agent": "DeEarthX" } }
|
||||
)
|
||||
).headers["content-length"];
|
||||
@@ -44,7 +68,56 @@ export async function fastdownload(data: [string, string]) {
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
})();
|
||||
})();*/
|
||||
console.log(e)
|
||||
await got
|
||||
.get(e[0], {
|
||||
responseType: "buffer",
|
||||
headers: {
|
||||
"user-agent": "DeEarthX",
|
||||
},
|
||||
})
|
||||
.on("downloadProgress", (progress) => {
|
||||
//bar.update(progress.transferred);
|
||||
})
|
||||
.then((res) => {
|
||||
fse.outputFileSync(e[1], res.rawBody);
|
||||
});
|
||||
}
|
||||
},
|
||||
{ retries: 3 }
|
||||
);
|
||||
} catch (e) {
|
||||
//LOGGER.error({ err: e });
|
||||
}
|
||||
},
|
||||
{ concurrency: 16 }
|
||||
);
|
||||
}
|
||||
|
||||
export async function xfastdownload(data: [string, string][]) {
|
||||
return await pMap(
|
||||
data,
|
||||
async (e:any) => {
|
||||
try {
|
||||
await pRetry(
|
||||
async () => {
|
||||
if (!fs.existsSync(e[1])) {
|
||||
/*
|
||||
const size: number = await (async () => {
|
||||
const head = (
|
||||
await got.head(
|
||||
e[0],
|
||||
{ headers: { "user-agent": "DeEarthX" } }
|
||||
)
|
||||
).headers["content-length"];
|
||||
if (head) {
|
||||
return Number(head);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
})();*/
|
||||
console.log(e)
|
||||
await got
|
||||
.get(e[0], {
|
||||
responseType: "buffer",
|
||||
|
||||
@@ -3,6 +3,7 @@ import Stream from "node:stream"
|
||||
|
||||
export interface IentryP extends yauzl.Entry {
|
||||
openReadStream: Promise<Stream.Readable>;
|
||||
ReadEntry: Promise<Buffer>;
|
||||
}
|
||||
|
||||
export async function yauzl_promise(buffer: Buffer): Promise<IentryP[]>{
|
||||
@@ -25,7 +26,8 @@ export async function yauzl_promise(buffer: Buffer): Promise<IentryP[]>{
|
||||
getLastModDate: entry.getLastModDate,
|
||||
isEncrypted: entry.isEncrypted,
|
||||
isCompressed: entry.isCompressed,
|
||||
openReadStream: _openReadStream(zip,entry)
|
||||
openReadStream: _openReadStream(zip,entry),
|
||||
ReadEntry: _ReadEntry(zip,entry)
|
||||
}
|
||||
entries.push(_entry)
|
||||
if (zip.entryCount === entries.length){
|
||||
@@ -39,6 +41,24 @@ export async function yauzl_promise(buffer: Buffer): Promise<IentryP[]>{
|
||||
});
|
||||
}
|
||||
|
||||
async function _ReadEntry(zip:yauzl.ZipFile,entry:yauzl.Entry): Promise<Buffer>{
|
||||
return new Promise((resolve,reject)=>{
|
||||
zip.openReadStream(entry,(err,stream)=>{
|
||||
if (err){
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
const chunks: Buffer[] = [];
|
||||
stream.on("data",(chunk)=>{
|
||||
chunks.push(chunk);
|
||||
})
|
||||
stream.on("end",()=>{
|
||||
resolve(Buffer.concat(chunks));
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function _openReadStream(zip:yauzl.ZipFile,entry:yauzl.Entry): Promise<Stream.Readable>{
|
||||
return new Promise((resolve,reject)=>{
|
||||
zip.openReadStream(entry,(err,stream)=>{
|
||||
|
||||
Reference in New Issue
Block a user