mirror of
https://github.com/Jerryplusy/rc-plugin.git
synced 2025-10-14 16:19:18 +00:00
✨ feat: 加入acfun解析,解决部分bug
This commit is contained in:
parent
60e84f470e
commit
11e73d2cd7
@ -19,6 +19,10 @@ export class query extends plugin {
|
|||||||
reg: '^#*医药查询 (.*)$',
|
reg: '^#*医药查询 (.*)$',
|
||||||
fnc: 'doctor'
|
fnc: 'doctor'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
reg: '^#*评分 (.*)',
|
||||||
|
fnc: 'videoScore'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
reg: '^#(cat)$',
|
reg: '^#(cat)$',
|
||||||
fnc: 'cat'
|
fnc: 'cat'
|
||||||
@ -77,6 +81,41 @@ export class query extends plugin {
|
|||||||
return !!this.reply(await Bot.makeForwardMsg(msg))
|
return !!this.reply(await Bot.makeForwardMsg(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async videoScore(e) {
|
||||||
|
let keyword = e.msg.split(' ')[1]
|
||||||
|
const api = `https://movie.douban.com/j/subject_suggest?q=${encodeURI(keyword)}`;
|
||||||
|
|
||||||
|
let movieId = 30433417;
|
||||||
|
fetch(api, {
|
||||||
|
Headers: {
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
}).then(resp => resp.json()).then(resp => {
|
||||||
|
if (resp.length === 0 || resp === "") {
|
||||||
|
e.reply("没找到!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
movieId = resp[0].id;
|
||||||
|
const doubanApi = `https://movie.querydata.org/api?id=${movieId}`;
|
||||||
|
fetch(doubanApi, {
|
||||||
|
Headers: {
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
}).then(resp => resp.json()).then(resp => {
|
||||||
|
if (resp.length === 0 || resp === "") {
|
||||||
|
e.reply("没找到!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
e.reply(`识别:${resp.data[0].name}\n烂番茄评分:${resp.imdbRating}\n豆瓣评分:${resp.doubanRating}\n评分:${resp.imdbRating}`);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
async cat (e) {
|
async cat (e) {
|
||||||
const numb = this.catConfig.count
|
const numb = this.catConfig.count
|
||||||
let images = []
|
let images = []
|
||||||
@ -196,13 +235,27 @@ export class query extends plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async buyerShow (e) {
|
async buyerShow (e) {
|
||||||
const urls = ['https://api.vvhan.com/api/tao', 'http://3650000.xyz/api/?type=img']
|
// http://3650000.xyz/api/?type=img
|
||||||
const randomIndex = Math.floor(Math.random() * urls.length);
|
// https://api.vvhan.com/api/tao
|
||||||
const randomElement = urls.splice(randomIndex, 1)[0];
|
// https://api.uomg.com/api/rand.img3?format=json
|
||||||
await fetch(randomElement).then(resp => {
|
// const randomIndex = Math.floor(Math.random() * urls.length);
|
||||||
e.reply(segment.image(resp.url))
|
// const randomElement = urls.splice(randomIndex, 1)[0];
|
||||||
|
const p1 = new Promise((resolve, reject) => {
|
||||||
|
fetch("https://api.vvhan.com/api/tao").then(resp => {
|
||||||
|
return resolve(resp.url)
|
||||||
|
}).catch(err => reject(err))
|
||||||
})
|
})
|
||||||
return true
|
const p2 = new Promise((resolve, reject) => {
|
||||||
|
fetch("https://api.uomg.com/api/rand.img3?format=json").then(resp => resp.json()).then(resp => {
|
||||||
|
return resolve(resp.imgurl)
|
||||||
|
}).catch(err => reject(err))
|
||||||
|
})
|
||||||
|
Promise.all([p1, p2]).then(res => {
|
||||||
|
res.forEach(item => {
|
||||||
|
e.reply(segment.image(item))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除标签
|
// 删除标签
|
||||||
|
@ -11,7 +11,8 @@ import { TwitterApi } from 'twitter-api-v2'
|
|||||||
import HttpProxyAgent from 'https-proxy-agent'
|
import HttpProxyAgent from 'https-proxy-agent'
|
||||||
import { mkdirsSync } from '../utils/file.js'
|
import { mkdirsSync } from '../utils/file.js'
|
||||||
import { downloadBFile, getDownloadUrl, mergeFileToMp4 } from '../utils/bilibili.js'
|
import { downloadBFile, getDownloadUrl, mergeFileToMp4 } from '../utils/bilibili.js'
|
||||||
import { get, remove, add } from "../utils/redisu.js";
|
import { parseUrl, parseM3u8, downloadM3u8Videos, mergeAcFileToMp4 } from '../utils/acfun.js'
|
||||||
|
// import { get, remove, add } from "../utils/redisu.js";
|
||||||
|
|
||||||
const transMap = { "中": "zh", "日": "jp", "文": "wyw", "英": "en" }
|
const transMap = { "中": "zh", "日": "jp", "文": "wyw", "英": "en" }
|
||||||
|
|
||||||
@ -46,6 +47,14 @@ export class tools extends plugin {
|
|||||||
{
|
{
|
||||||
reg: "(.*)(twitter.com)",
|
reg: "(.*)(twitter.com)",
|
||||||
fnc: "twitter",
|
fnc: "twitter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: "https:\/\/(m.)?v.qq.com\/(.*)",
|
||||||
|
fnc: "tx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: "(.*)(acfun.cn)",
|
||||||
|
fnc: "acfun"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -199,24 +208,24 @@ export class tools extends plugin {
|
|||||||
async wiki (e) {
|
async wiki (e) {
|
||||||
const key = e.msg.replace(/#|百科|wiki/g, "").trim();
|
const key = e.msg.replace(/#|百科|wiki/g, "").trim();
|
||||||
const url = `https://xiaoapi.cn/API/bk.php?m=json&type=bd&msg=${ encodeURI(key) }`
|
const url = `https://xiaoapi.cn/API/bk.php?m=json&type=bd&msg=${ encodeURI(key) }`
|
||||||
const url2 = 'https://api.jikipedia.com/go/auto_complete'
|
// const url2 = 'https://api.jikipedia.com/go/auto_complete'
|
||||||
Promise.all([
|
Promise.all([
|
||||||
axios.post(url2, {
|
// axios.post(url2, {
|
||||||
headers: {
|
// headers: {
|
||||||
"User-Agent":
|
// "User-Agent":
|
||||||
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
|
// "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
|
||||||
"Content-Type": "application/json",
|
// "Content-Type": "application/json",
|
||||||
},
|
// },
|
||||||
timeout: 10000,
|
// timeout: 10000,
|
||||||
"phrase": key,
|
// "phrase": key,
|
||||||
})
|
// })
|
||||||
.then(resp => {
|
// .then(resp => {
|
||||||
const data = resp.data.data
|
// const data = resp.data.data
|
||||||
if (_.isEmpty(data)) {
|
// if (_.isEmpty(data)) {
|
||||||
return data;
|
// return data;
|
||||||
}
|
// }
|
||||||
return data[0].entities[0];
|
// return data[0].entities[0];
|
||||||
}),
|
// }),
|
||||||
axios.get(url, {
|
axios.get(url, {
|
||||||
headers: {
|
headers: {
|
||||||
"User-Agent":
|
"User-Agent":
|
||||||
@ -229,13 +238,13 @@ export class tools extends plugin {
|
|||||||
})
|
})
|
||||||
])
|
])
|
||||||
.then(res => {
|
.then(res => {
|
||||||
const data = res[1]
|
const data = res[0]
|
||||||
const data2 = res[0]
|
// const data2 = res[0]
|
||||||
const template = `
|
const template = `
|
||||||
解释:${ _.get(data, 'msg') }\n
|
解释:${ _.get(data, 'msg') }\n
|
||||||
详情:${ _.get(data, 'more') }\n
|
详情:${ _.get(data, 'more') }\n
|
||||||
小鸡解释:${ _.get(data2, 'content') }
|
|
||||||
`;
|
`;
|
||||||
|
// 小鸡解释:${ _.get(data2, 'content') }
|
||||||
e.reply(template)
|
e.reply(template)
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
@ -286,6 +295,27 @@ export class tools extends plugin {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 视频解析
|
||||||
|
async tx( e ) {
|
||||||
|
const url = e.msg
|
||||||
|
const data = await ( await fetch( `https://xian.txma.cn/API/jx_txjx.php?url=${url}` ) )
|
||||||
|
.json()
|
||||||
|
const k = data.url
|
||||||
|
const name = data.title
|
||||||
|
if( k && name ) {
|
||||||
|
e.reply( name + '\n' + k )
|
||||||
|
let forward = await this.makeForwardMsg( url )
|
||||||
|
e.reply( forward )
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
e.reply( '解析腾讯视频失败~\n去浏览器使用拼接接口吧...' )
|
||||||
|
let forward = await this.makeForwardMsg( url )
|
||||||
|
e.reply( forward )
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 请求参数
|
// 请求参数
|
||||||
async douyinRequest (url) {
|
async douyinRequest (url) {
|
||||||
const params = {
|
const params = {
|
||||||
@ -361,6 +391,32 @@ export class tools extends plugin {
|
|||||||
return (idVideo.length > 19) ? idVideo.substring(0, idVideo.indexOf("?")) : idVideo;
|
return (idVideo.length > 19) ? idVideo.substring(0, idVideo.indexOf("?")) : idVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// acfun解析
|
||||||
|
async acfun(e) {
|
||||||
|
const path = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/temp/`
|
||||||
|
if (!fs.existsSync(path)) {
|
||||||
|
mkdirsSync(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputMsg = e.msg;
|
||||||
|
// 适配手机分享:https://m.acfun.cn/v/?ac=32838812&sid=d2b0991bd6ad9c09
|
||||||
|
if (inputMsg.includes("m.acfun.cn")) {
|
||||||
|
inputMsg = `https://www.acfun.cn/v/ac${/ac=([^&?]*)/.exec(inputMsg)[1]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
parseUrl(inputMsg).then(res => {
|
||||||
|
e.reply(`识别:猴山,${res.videoName}`)
|
||||||
|
parseM3u8(res.urlM3u8s[res.urlM3u8s.length-1]).then(res2 => {
|
||||||
|
downloadM3u8Videos(res2.m3u8FullUrls, path).then(_ => {
|
||||||
|
mergeAcFileToMp4( res2.tsNames, path, `${path}out.mp4`).then(_ => {
|
||||||
|
e.reply(segment.video(`${path}out.mp4`))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// 工具:下载哔哩哔哩
|
// 工具:下载哔哩哔哩
|
||||||
async downBili (title, videoUrl, audioUrl) {
|
async downBili (title, videoUrl, audioUrl) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
- icon: android
|
- icon: android
|
||||||
title: "#安卓软件推荐"
|
title: "#安卓软件推荐"
|
||||||
desc: 推荐安卓软件
|
desc: 推荐安卓软件
|
||||||
|
- icon: buyer
|
||||||
|
title: "#买家秀"
|
||||||
|
desc: 淘宝买家秀
|
||||||
- group: 神秘功能合集
|
- group: 神秘功能合集
|
||||||
list:
|
list:
|
||||||
- icon: pic1
|
- icon: pic1
|
||||||
@ -46,6 +49,9 @@
|
|||||||
- icon: bilibili
|
- icon: bilibili
|
||||||
title: "bilibili/b23"
|
title: "bilibili/b23"
|
||||||
desc: 哔哩哔哩分享实时下载
|
desc: 哔哩哔哩分享实时下载
|
||||||
|
- icon: 推特
|
||||||
|
title: "bilibili/b23"
|
||||||
|
desc: 推特学习版分享实时下载
|
||||||
- group: 其他指令[实验]
|
- group: 其他指令[实验]
|
||||||
list:
|
list:
|
||||||
- icon: update
|
- icon: update
|
||||||
|
BIN
resources/img/icon/buyer.png
Normal file
BIN
resources/img/icon/buyer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
resources/img/icon/推特.png
Normal file
BIN
resources/img/icon/推特.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
164
utils/acfun.js
Normal file
164
utils/acfun.js
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'path'
|
||||||
|
import child_process from 'node:child_process'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 去除JSON的一些转义 \\" -> \" ->"
|
||||||
|
* @param str
|
||||||
|
*/
|
||||||
|
function escapeSpecialChars(str) {
|
||||||
|
return str.replace(/\\\\"/g, '\\"').replace(/\\"/g, '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseVideoName = (videoInfo) => {
|
||||||
|
const strAc号 = "ac" + (videoInfo?.dougaId || "");
|
||||||
|
const str标题 = videoInfo?.title;
|
||||||
|
const str作者 = videoInfo?.user.name;
|
||||||
|
const str上传时间 = videoInfo?.createTime;
|
||||||
|
const str描述 = videoInfo?.description;
|
||||||
|
|
||||||
|
const raw = [strAc号, str标题, str作者, str上传时间, str描述]
|
||||||
|
.map((d) => d || "")
|
||||||
|
.join("_")
|
||||||
|
.slice(0, 100);
|
||||||
|
|
||||||
|
return raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseVideoNameFixed = (videoInfo) => {
|
||||||
|
const f = parseVideoName(videoInfo);
|
||||||
|
const t = f.replaceAll(" ", "-");
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function parseUrl(videoUrlAddress) {
|
||||||
|
// eg https://www.acfun.cn/v/ac4621380?quickViewId=videoInfo_new&ajaxpipe=1
|
||||||
|
const urlSuffix = "?quickViewId=videoInfo_new&ajaxpipe=1";
|
||||||
|
const url = videoUrlAddress + urlSuffix;
|
||||||
|
|
||||||
|
const raw = await axios.get(url).then(resp => {
|
||||||
|
return resp.data
|
||||||
|
});
|
||||||
|
// Split
|
||||||
|
const strsRemoveHeader = raw.split("window.pageInfo = window.videoInfo =");
|
||||||
|
const strsRemoveTail = strsRemoveHeader[1].split("</script>");
|
||||||
|
const strJson = strsRemoveTail[0];
|
||||||
|
|
||||||
|
const strJsonEscaped = escapeSpecialChars(strJson);
|
||||||
|
/** Object videoInfo */
|
||||||
|
const videoInfo = JSON.parse(strJsonEscaped);
|
||||||
|
|
||||||
|
const videoName = parseVideoNameFixed(videoInfo);
|
||||||
|
|
||||||
|
const ksPlayJson = videoInfo.currentVideoInfo.ksPlayJson;
|
||||||
|
/** Object ksPlay */
|
||||||
|
const ksPlay = JSON.parse(ksPlayJson);
|
||||||
|
|
||||||
|
const representations = ksPlay.adaptationSet[0].representation;
|
||||||
|
const urlM3u8s = representations.map((d) => d.url);
|
||||||
|
|
||||||
|
return { urlM3u8s, videoName };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function parseM3u8(m3u8Url) {
|
||||||
|
const m3u8File = await axios.get(m3u8Url).then(resp => resp.data);
|
||||||
|
|
||||||
|
/** 分离ts文件链接 */
|
||||||
|
const rawPieces = m3u8File.split(/\n#EXTINF:.{8},\n/);
|
||||||
|
/** 过滤头部 */
|
||||||
|
const m3u8RelativeLinks = rawPieces.slice(1);
|
||||||
|
/** 修改尾部 去掉尾部多余的结束符 */
|
||||||
|
const patchedTail =
|
||||||
|
m3u8RelativeLinks[m3u8RelativeLinks.length - 1].split("\n")[0];
|
||||||
|
m3u8RelativeLinks[m3u8RelativeLinks.length - 1] = patchedTail;
|
||||||
|
|
||||||
|
/** 完整链接,直接加m3u8Url的通用前缀 */
|
||||||
|
const m3u8Prefix = m3u8Url.split("/").slice(0, -1).join("/");
|
||||||
|
const m3u8FullUrls = m3u8RelativeLinks.map((d) => m3u8Prefix + "/" + d);
|
||||||
|
/** aria2c下载的文件名,就是取url最后一段,去掉末尾url参数(?之后是url参数) */
|
||||||
|
const tsNames = m3u8RelativeLinks.map((d) => d.split("?")[0]);
|
||||||
|
/** 文件夹名,去掉文件名末尾分片号 */
|
||||||
|
let outputFolderName = tsNames[0].slice(0, -9);
|
||||||
|
/** 输出最后合并的文件名,加个通用mp4后缀 */
|
||||||
|
const outputFileName = outputFolderName + ".mp4";
|
||||||
|
|
||||||
|
return {
|
||||||
|
m3u8FullUrls,
|
||||||
|
tsNames,
|
||||||
|
outputFolderName,
|
||||||
|
outputFileName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载m3u8
|
||||||
|
async function downloadM3u8Videos(
|
||||||
|
m3u8FullUrls,
|
||||||
|
outputFolderName
|
||||||
|
) {
|
||||||
|
/** 新建下载文件夹 在当前运行目录下 */
|
||||||
|
const outPath = outputFolderName;
|
||||||
|
|
||||||
|
/** 批下载 */
|
||||||
|
const strDownloadParamFiles = m3u8FullUrls
|
||||||
|
.map(async (d, i) => {
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const writer = fs.createWriteStream(outPath + `${i}.ts`);
|
||||||
|
axios.get(d, {
|
||||||
|
headers: {
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
|
||||||
|
},
|
||||||
|
responseType: "stream",
|
||||||
|
}).then(dres => {
|
||||||
|
dres.data.pipe(writer);
|
||||||
|
writer.on("finish", () => resolve(true));
|
||||||
|
writer.on("error", () => reject);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
/** 写入下载链接列表文件 */
|
||||||
|
// fs.writeFileSync(path.resolve(outPath, "urls.txt"), str下载参数文件);
|
||||||
|
return Promise.all(strDownloadParamFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function mergeAcFileToMp4(tsNames, FullFileName, outputFileName, shouldDelete = true) {
|
||||||
|
|
||||||
|
/** 合并参数列表 格式file path */
|
||||||
|
const concatStrs = tsNames.map(
|
||||||
|
(d, i) => `file ${path.resolve(FullFileName, i + ".ts").replace(/\\/g, "/")}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const ffmpegList = path.resolve(FullFileName, 'file.txt');
|
||||||
|
fs.writeFileSync(ffmpegList, concatStrs.join("\n"));
|
||||||
|
const outPath = path.resolve(outputFileName);
|
||||||
|
|
||||||
|
let cmd = 'ffmpeg';
|
||||||
|
const env = {
|
||||||
|
...process.env,
|
||||||
|
PATH: '/usr/local/bin:' + child_process.execSync('echo $PATH').toString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
child_process.exec(
|
||||||
|
`${ cmd } -y -f concat -safe 0 -i "${ ffmpegList }" -c copy "${ outPath }"`,
|
||||||
|
{ env },
|
||||||
|
err => {
|
||||||
|
if (shouldDelete) {
|
||||||
|
fs.unlink(FullFileName, f => f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve({ outputFileName });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { parseUrl, parseM3u8, downloadM3u8Videos, mergeAcFileToMp4 }
|
@ -27,4 +27,22 @@ function mkdirs (dirname, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export { mkdirs, mkdirsSync }
|
/**
|
||||||
|
* 删除文件夹下所有问价及将文件夹下所有文件清空
|
||||||
|
* @param {*} path
|
||||||
|
*/
|
||||||
|
function emptyDir(path) {
|
||||||
|
const files = fs.readdirSync(path);
|
||||||
|
files.forEach(file => {
|
||||||
|
const filePath = `${path}/${file}`;
|
||||||
|
const stats = fs.statSync(filePath);
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
emptyDir(filePath);
|
||||||
|
} else {
|
||||||
|
fs.unlinkSync(filePath);
|
||||||
|
console.log(`删除${file}文件成功`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { mkdirs, mkdirsSync, emptyDir }
|
Loading…
x
Reference in New Issue
Block a user