🔧 Added a step to extract comicinfo.xml

This commit is contained in:
2022-02-28 09:51:45 -08:00
parent 36d18d4846
commit c67328c4d6
7 changed files with 217 additions and 153 deletions

View File

@@ -1,5 +1,5 @@
const mongoose = require("mongoose");
var mexp = require('mongoose-elasticsearch-xp').v7;
var mexp = require("mongoose-elasticsearch-xp").v7;
const paginate = require("mongoose-paginate-v2");
const { Client } = require("@elastic/elasticsearch");
@@ -12,77 +12,69 @@ export const eSClient = new Client({
},
});
const ComicSchema = mongoose.Schema({
importStatus: {
isImported: Boolean,
tagged: Boolean,
matchedResult: {
score: String,
const ComicSchema = mongoose.Schema(
{
importStatus: {
isImported: Boolean,
tagged: Boolean,
matchedResult: {
score: String,
},
},
},
userAddedMetadata: {
tags: [],
},
sourcedMetadata: {
comicInfo: {
blackAndWhite: String,
characters: [String],
count: String,
genre: String,
manga: String,
month: String,
number: String,
pageCount: String,
pages: [],
publisher: String,
summary: String,
title: String,
writer: String,
year: String,
userAddedMetadata: {
tags: [],
},
comicvine: {},
shortboxed: {},
gcd: {},
},
rawFileDetails: {
name: { type: String, es_indexed: true },
filePath: String,
fileSize: Number,
extension: String,
containedIn: String,
pageCount: Number,
cover: {
sourcedMetadata: {
comicInfo: { type: mongoose.Schema.Types.Mixed, default: {} },
comicvine: { type: mongoose.Schema.Types.Mixed, default: {} },
shortboxed: {},
gcd: {},
},
rawFileDetails: {
name: { type: String, es_indexed: true },
filePath: String,
stats: Object,
fileSize: Number,
extension: String,
containedIn: String,
pageCount: Number,
cover: {
filePath: String,
stats: Object,
},
calibreMetadata: {
coverWriteResult: String,
},
},
calibreMetadata :{
coverWriteResult: String,
}
},
inferredMetadata: {
issue: {
name: String,
number: { type: Number, es_indexed: true, required: false, default: 0 },
year: String,
subtitle: String,
}
},
acquisition: {
wanted: Boolean,
release: {},
directconnect: Array,
torrent: {
sourceApplication: String,
magnet: String,
tracker: String,
status: String,
inferredMetadata: {
issue: {
name: String,
number: {
type: Number,
es_indexed: true,
required: false,
default: 0,
},
year: String,
subtitle: String,
},
},
usenet: {
sourceApplication: String,
acquisition: {
wanted: Boolean,
release: {},
directconnect: Array,
torrent: {
sourceApplication: String,
magnet: String,
tracker: String,
status: String,
},
usenet: {
sourceApplication: String,
},
},
},
}, { timestamps: true});
{ timestamps: true, minimize: false }
);
ComicSchema.plugin(mexp, {
client: eSClient,

79
package-lock.json generated
View File

@@ -9,14 +9,13 @@
"version": "0.0.1",
"dependencies": {
"@elastic/elasticsearch": "^7.16.0",
"@jorgeferrero/stream-to-buffer": "^2.0.6",
"@root/walk": "^1.1.0",
"@types/axios": "^0.14.0",
"@types/jest": "^25.1.4",
"@types/mkdirp": "^1.0.0",
"@types/node": "^13.9.8",
"@types/string-similarity": "^4.0.0",
"7zip-bin": "^5.1.1",
"7zip-min": "^1.4.0",
"axios": "^0.25.0",
"axios-retry": "^3.2.4",
"bree": "^7.1.5",
@@ -55,9 +54,11 @@
},
"devDependencies": {
"@types/lodash": "^4.14.168",
"@types/node-7z": "^2.1.4",
"@types/unzipper": "^0.10.3",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"7zip-bin": "^5.1.1",
"eslint": "^7.32.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-prefer-arrow": "^1.2.2",
@@ -1685,6 +1686,11 @@
"node": ">=8"
}
},
"node_modules/@jorgeferrero/stream-to-buffer": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@jorgeferrero/stream-to-buffer/-/stream-to-buffer-2.0.6.tgz",
"integrity": "sha512-G7jiBkMwDdYADntdV/qzl9H4Lkch0RyektqXl5+KhktduP4/rE1RaTz0wKZFdC2dKo3S0JaxoUUC3uDeXmI7EQ=="
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1951,6 +1957,15 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz",
"integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ=="
},
"node_modules/@types/node-7z": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@types/node-7z/-/node-7z-2.1.4.tgz",
"integrity": "sha512-SayS5Cld62m7wRsKtBJheNBYRTVmKK+wTmW16EmYf2FuRTSGHKPx/p2OqAVjeKSQss9qcCKDGG9yv7D+Pi+iNg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/prettier": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.3.tgz",
@@ -2151,15 +2166,8 @@
"node_modules/7zip-bin": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz",
"integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ=="
},
"node_modules/7zip-min": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/7zip-min/-/7zip-min-1.4.1.tgz",
"integrity": "sha512-xiK95rSNr6cXB7UXVLJdrxtNrlXBqZrMhcKlq0S5Rsg6vVhh+9OSUyQIba8bSAGgHjpETlWXAYEuVfxYGEfCWA==",
"dependencies": {
"7zip-bin": "^5.1.1"
}
"integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==",
"dev": true
},
"node_modules/abab": {
"version": "2.0.5",
@@ -4805,9 +4813,9 @@
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
"funding": [
{
"type": "individual",
@@ -13526,9 +13534,9 @@
]
},
"node_modules/simple-get": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"dependencies": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
@@ -16242,6 +16250,11 @@
}
}
},
"@jorgeferrero/stream-to-buffer": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@jorgeferrero/stream-to-buffer/-/stream-to-buffer-2.0.6.tgz",
"integrity": "sha512-G7jiBkMwDdYADntdV/qzl9H4Lkch0RyektqXl5+KhktduP4/rE1RaTz0wKZFdC2dKo3S0JaxoUUC3uDeXmI7EQ=="
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -16495,6 +16508,15 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz",
"integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ=="
},
"@types/node-7z": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@types/node-7z/-/node-7z-2.1.4.tgz",
"integrity": "sha512-SayS5Cld62m7wRsKtBJheNBYRTVmKK+wTmW16EmYf2FuRTSGHKPx/p2OqAVjeKSQss9qcCKDGG9yv7D+Pi+iNg==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/prettier": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.3.tgz",
@@ -16621,15 +16643,8 @@
"7zip-bin": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz",
"integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ=="
},
"7zip-min": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/7zip-min/-/7zip-min-1.4.1.tgz",
"integrity": "sha512-xiK95rSNr6cXB7UXVLJdrxtNrlXBqZrMhcKlq0S5Rsg6vVhh+9OSUyQIba8bSAGgHjpETlWXAYEuVfxYGEfCWA==",
"requires": {
"7zip-bin": "^5.1.1"
}
"integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==",
"dev": true
},
"abab": {
"version": "2.0.5",
@@ -18707,9 +18722,9 @@
"dev": true
},
"follow-redirects": {
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ=="
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
},
"for-each": {
"version": "0.3.3",
@@ -25029,9 +25044,9 @@
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"simple-get": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"requires": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",

View File

@@ -21,9 +21,11 @@
"author": "",
"devDependencies": {
"@types/lodash": "^4.14.168",
"@types/node-7z": "^2.1.4",
"@types/unzipper": "^0.10.3",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"7zip-bin": "^5.1.1",
"eslint": "^7.32.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-prefer-arrow": "^1.2.2",
@@ -38,14 +40,13 @@
},
"dependencies": {
"@elastic/elasticsearch": "^7.16.0",
"@jorgeferrero/stream-to-buffer": "^2.0.6",
"@root/walk": "^1.1.0",
"@types/axios": "^0.14.0",
"@types/jest": "^25.1.4",
"@types/mkdirp": "^1.0.0",
"@types/node": "^13.9.8",
"@types/string-similarity": "^4.0.0",
"7zip-bin": "^5.1.1",
"7zip-min": "^1.4.0",
"axios": "^0.25.0",
"axios-retry": "^3.2.4",
"bree": "^7.1.5",

View File

@@ -46,9 +46,11 @@ import BullMQMixin from "moleculer-bull";
import { SandboxedJob } from "moleculer-bull";
import { DbMixin } from "../mixins/db.mixin";
import Comic from "../models/comic.model";
import { extractCoverFromFile2 } from "../utils/uncompression.utils";
import { extractComicInfoXMLFromArchive, extractCoverFromFile2 } from "../utils/uncompression.utils";
import { refineQuery } from "filename-parser";
import { io } from "./api.service";
import { getFileConstituents } from "../utils/file.utils";
import { USERDATA_DIRECTORY } from "../constants/directories";
const REDIS_URI = process.env.REDIS_URI || `redis://0.0.0.0:6379`;
console.log(`REDIS -> ${REDIS_URI}`);
@@ -70,12 +72,23 @@ export default class QueueService extends Service {
const result = await extractCoverFromFile2(
job.data.fileObject
);
const { filePath } = job.data.fileObject;
// get the file constituents and check for comicinfo.xml
// If present, convert the xml into json
// Import it into mongo
const {
extension,
fileNameWithoutExtension,
} = getFileConstituents(filePath);
const targetDirectory = `${USERDATA_DIRECTORY}/covers/${fileNameWithoutExtension}`;
const info = await extractComicInfoXMLFromArchive(filePath, targetDirectory, extension);
// infer any issue-related metadata from the filename
const { inferredIssueDetails } = refineQuery(result.name);
console.log("Issue metadata inferred: ", JSON.stringify(inferredIssueDetails, null, 2));
// write to mongo
console.log("Writing to mongo...")
const dbImportResult = await this.broker.call(
"library.rawImportToDB",
{
@@ -91,12 +104,12 @@ export default class QueueService extends Service {
issue: inferredIssueDetails,
},
sourcedMetadata: {
comicInfo: info,
comicvine: {},
},
},
{}
);
return Promise.resolve({
dbImportResult,
id: job.id,
@@ -156,8 +169,10 @@ export default class QueueService extends Service {
"stalled",
async (job) => {
console.warn(
`The job with the id '${job} got stalled!`
`The job with the id '${job.id} got stalled!`
);
console.log(`${JSON.stringify(job, null, 2)}`);
console.log(`is stalled.`)
}
);
});

View File

@@ -96,5 +96,19 @@ export const constructPaths = (
walkedFolder.extension,
});
export const getFileConstituents = (filePath: string) => {
const extension = path.extname(filePath);
const fileNameWithExtension = path.basename(filePath);
const fileNameWithoutExtension = path.basename(
filePath,
path.extname(filePath)
);
return {
extension,
fileNameWithoutExtension,
fileNameWithExtension,
};
};
const filterOutDotFiles = (entities) =>
entities.filter((ent) => !ent.name.startsWith("."));

View File

@@ -32,7 +32,8 @@ SOFTWARE.
*/
const fse = require("fs-extra");
import path from "path";
import { promises as fs } from "fs";
import path, { parse } from "path";
import {
IExtractComicBookCoverErrorResponse,
@@ -42,11 +43,17 @@ import {
ISharpResizedImageStats,
} from "threetwo-ui-typings";
import { constructPaths, explodePath, walkFolder } from "../utils/file.utils";
import {
explodePath,
getFileConstituents,
walkFolder,
} from "../utils/file.utils";
import { resizeImage } from "./imagetransformation.utils";
import { isNil, isUndefined } from "lodash";
import { convertXMLToJSON } from "./xml.utils";
const sevenZip = require("7zip-min");
import sevenBin from "7zip-bin";
import { extract } from "node-7z";
const pathTo7zip = sevenBin.path7za;
const unrar = require("node-unrar-js");
const { Calibre } = require("node-calibre");
import { USERDATA_DIRECTORY, COMICS_DIRECTORY } from "../constants/directories";
@@ -69,20 +76,17 @@ export const extractCoverFromFile2 = async (
const directoryOptions = {
mode: 0o2775,
};
const extension = path.extname(filePath);
const fileNameWithExtension = path.basename(filePath);
const fileNameWithoutExtension = path.basename(
filePath,
path.extname(filePath)
);
const {
extension,
fileNameWithExtension,
fileNameWithoutExtension,
} = getFileConstituents(filePath);
const targetDirectory = `${USERDATA_DIRECTORY}/covers/${fileNameWithoutExtension}`;
await fse.ensureDir(targetDirectory, directoryOptions);
console.info(`%s was created.`, targetDirectory);
// 2.1 look for comicinfo.xml
extractFileFromArchive(filePath, targetDirectory, extension);
// 3. extract the cover
console.info(`Starting cover extraction...`);
let result: string;
@@ -173,7 +177,7 @@ export const unrarArchive = async (
}
};
export const extractFileFromRar = async (
export const extractComicInfoXMLFromRar = async (
filePath: string,
fileToExtract: string
) => {
@@ -186,55 +190,73 @@ export const extractFileFromRar = async (
data: fileBuffer,
});
const list = extractor.getFileList();
const listArcHeader = list.arcHeader; // archive header
const fileHeaders = [...list.fileHeaders]; // load the file headers
const extracted = extractor.extract({ files: ["ComicInfo.xml"] });
const files = [...extracted.files]; //load the files
console.log("asdas", files[0]);
if(!isUndefined(files[0])) {
const trin = String.fromCharCode.apply(null, files[0].extraction)
const foo = await convertXMLToJSON(trin);
console.log(foo);
if (!isUndefined(files[0])) {
console.log(
`comicinfo.xml detected in ${filePath}, attempting extraction...`
);
const fileContents = String.fromCharCode.apply(
null,
files[0].extraction
);
const parsedJSON = await convertXMLToJSON(fileContents);
console.log(parsedJSON);
return parsedJSON.comicinfo;
}
} catch (error) {
throw new Error(error);
}
};
export const extractFileFromZip = async (
export const extractComicInfoXMLFromZip = async (
filePath: string,
outputDirectory: string
) => {
const foo = sevenZip.cmd(
["e", path.resolve(filePath), outputDirectory, "*.xml"],
(err) => {
console.log(err);
const foo = extract(path.resolve(filePath), outputDirectory, {
$cherryPick: ["*.xml"],
$bin: pathTo7zip,
});
for await (const chunk of foo) {
if (chunk.status === "extracted") {
console.log(
`comicinfo.xml detected in ${filePath}, attempting extraction...`
);
const fileContents = await fs.readFile(
path.resolve(`${outputDirectory}/${chunk.file}`),
"utf8"
);
const parsedJSON = await convertXMLToJSON(
Buffer.from(fileContents)
);
console.log(parsedJSON);
return parsedJSON.comicinfo;
}
);
return foo;
}
};
export const extractFileFromArchive = async (
export const extractComicInfoXMLFromArchive = async (
filePath: string,
outputDirectory: string,
extension: string
) => {
console.log(extension);
switch (extension) {
case ".cbz":
console.log("cbz");
extractFileFromZip(filePath, outputDirectory);
break;
console.log(
"Detected file type is cbz, looking for comicinfo.xml..."
);
return await extractComicInfoXMLFromZip(filePath, outputDirectory);
case ".cbr":
console.log("cbr");
extractFileFromRar(filePath, outputDirectory);
break;
console.log(
"Detected file type is cbr, looking for comicinfo.xml..."
);
return await extractComicInfoXMLFromRar(filePath, outputDirectory);
default:
console.log("error na rao");
console.log(
"Error inferring filetype for comicinfo.xml extraction."
);
break;
}
};

View File

@@ -635,6 +635,11 @@
"@types/yargs" "^16.0.0"
"chalk" "^4.0.0"
"@jorgeferrero/stream-to-buffer@^2.0.6":
"integrity" "sha512-G7jiBkMwDdYADntdV/qzl9H4Lkch0RyektqXl5+KhktduP4/rE1RaTz0wKZFdC2dKo3S0JaxoUUC3uDeXmI7EQ=="
"resolved" "https://registry.npmjs.org/@jorgeferrero/stream-to-buffer/-/stream-to-buffer-2.0.6.tgz"
"version" "2.0.6"
"@nodelib/fs.scandir@2.1.5":
"integrity" "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="
"resolved" "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
@@ -974,6 +979,13 @@
"@types/bson" "*"
"@types/node" "*"
"@types/node-7z@^2.1.4":
"integrity" "sha512-SayS5Cld62m7wRsKtBJheNBYRTVmKK+wTmW16EmYf2FuRTSGHKPx/p2OqAVjeKSQss9qcCKDGG9yv7D+Pi+iNg=="
"resolved" "https://registry.npmjs.org/@types/node-7z/-/node-7z-2.1.4.tgz"
"version" "2.1.4"
dependencies:
"@types/node" "*"
"@types/node@*", "@types/node@^13.9.8", "@types/node@>=10.0.0":
"integrity" "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ=="
"resolved" "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz"
@@ -1095,13 +1107,6 @@
"resolved" "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz"
"version" "5.1.1"
"7zip-min@^1.4.0":
"integrity" "sha512-xiK95rSNr6cXB7UXVLJdrxtNrlXBqZrMhcKlq0S5Rsg6vVhh+9OSUyQIba8bSAGgHjpETlWXAYEuVfxYGEfCWA=="
"resolved" "https://registry.npmjs.org/7zip-min/-/7zip-min-1.4.1.tgz"
"version" "1.4.1"
dependencies:
"7zip-bin" "^5.1.1"
"abab@^2.0.0", "abab@^2.0.3", "abab@^2.0.5":
"integrity" "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
"resolved" "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz"
@@ -3005,9 +3010,9 @@
"version" "3.2.4"
"follow-redirects@^1.14.7":
"integrity" "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ=="
"resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz"
"version" "1.14.7"
"integrity" "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
"resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz"
"version" "1.14.9"
"for-each@^0.3.3":
"integrity" "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw=="
@@ -6299,9 +6304,9 @@
"version" "1.0.1"
"simple-get@^3.0.3", "simple-get@^3.1.0":
"integrity" "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA=="
"resolved" "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz"
"version" "3.1.0"
"integrity" "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA=="
"resolved" "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz"
"version" "3.1.1"
dependencies:
"decompress-response" "^4.2.0"
"once" "^1.3.1"