Переглянути джерело

feat: 图片整理
1. 图片转移工具制作

kindring 3 місяців тому
батько
коміт
ad3be318a8

+ 0 - 1
build/app.js

@@ -27,7 +27,6 @@ function main() {
     app.use('/api', apiIndex_1.default);
     app.use(function (req, res) {
         res.sendStatus(404);
-        // res.send("404");
     });
     app.listen(serverConfig.webPort, function () {
         logger_1.default.info("web\u670D\u52A1\u4EE5\u542F\u52A8,\u76D1\u542C\u7AEF\u53E3\u4E3A: 0.0.0.0:".concat(serverConfig.webPort));

+ 1 - 1
build/app.js.map

@@ -1 +1 @@
-{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":";;;;;AAAA,oDAA+E;AAC/E,4DAAqC;AACrC,iDAAmD;AACnD,wDAAiC;AACjC,mFAA4D;AAE5D,kEAA4C;AAC5C,IAAI,EAAE,CAAC;AACP,SAAS,IAAI;IACT,IAAI,YAAY,GAAG,IAAA,6BAAgB,EAAC,sBAAsB,CAAC,CAAA;IAC3D,IAAG,YAAY,KAAK,IAAI,EAAC;QACrB,gBAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;KAClC;IACD,IAAM,GAAG,GAAe,IAAA,iBAAO,GAAE,CAAC;IAClC,GAAG,CAAC,GAAG,CAAC,0BAAgB,CAAC,CAAC;IAC1B,GAAG,CAAC,GAAG,CAAC,SAAS,EAAC,iBAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;IAC/C,GAAG,CAAC,GAAG,CAAC,qBAAU,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAClE,GAAG,CAAC,GAAG,CAAC,qBAAU,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5C,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,UAAC,GAAW,EAAE,GAAY,EAAE,IAAiB;QACtD,GAAG,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,CAAC,MAAM,EAAC,kBAAQ,CAAC,CAAC;IACzB,GAAG,CAAC,GAAG,CAAC,UAAC,GAAG,EAAC,GAAG;QACZ,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACpB,mBAAmB;IACvB,CAAC,CAAC,CAAA;IACF,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE;QAC7B,gBAAM,CAAC,IAAI,CAAC,oFAA2B,YAAY,CAAC,OAAO,CAAE,CAAC,CAAA;QAC9D,yEAAyE;IAC7E,CAAC,CAAC,CAAC;AACP,CAAC"}
+{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":";;;;;AAAA,oDAA+E;AAC/E,4DAAqC;AACrC,iDAAmD;AACnD,wDAAiC;AACjC,mFAA4D;AAE5D,kEAA4C;AAC5C,IAAI,EAAE,CAAC;AACP,SAAS,IAAI;IACT,IAAI,YAAY,GAAG,IAAA,6BAAgB,EAAC,sBAAsB,CAAC,CAAA;IAC3D,IAAG,YAAY,KAAK,IAAI,EAAC;QACrB,gBAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;KAClC;IAED,IAAM,GAAG,GAAe,IAAA,iBAAO,GAAE,CAAC;IAClC,GAAG,CAAC,GAAG,CAAC,0BAAgB,CAAC,CAAC;IAC1B,GAAG,CAAC,GAAG,CAAC,SAAS,EAAC,iBAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;IAC/C,GAAG,CAAC,GAAG,CAAC,qBAAU,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAClE,GAAG,CAAC,GAAG,CAAC,qBAAU,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5C,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,UAAC,GAAW,EAAE,GAAY,EAAE,IAAiB;QACtD,GAAG,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,CAAC,MAAM,EAAC,kBAAQ,CAAC,CAAC;IACzB,GAAG,CAAC,GAAG,CAAC,UAAC,GAAG,EAAC,GAAG;QACZ,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC,CAAA;IACF,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE;QAC7B,gBAAM,CAAC,IAAI,CAAC,oFAA2B,YAAY,CAAC,OAAO,CAAE,CAAC,CAAA;QAC9D,yEAAyE;IAC7E,CAAC,CAAC,CAAC;AACP,CAAC"}

+ 1 - 1
build/until/loadConfig.js.map

@@ -1 +1 @@
-{"version":3,"file":"loadConfig.js","sourceRoot":"","sources":["../../src/until/loadConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qCAAyB;AAKzB,SAAgB,gBAAgB,CAAC,UAAiB;IAC9C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,WAAW;KAC1C;QACI,mBAAmB;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;KAC1D;SAAI;QACD,YAAY;QACZ,OAAO,IAAI,CAAC;KACf;AACL,CAAC;AATD,4CASC;AAKD,SAAgB,cAAc,CAAC,UAAiB;IAC5C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,WAAW;KAC1C;QACI,mBAAmB;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;KAC1D;SAAI;QACD,YAAY;QACZ,OAAO,IAAI,CAAC;KACf;AACL,CAAC;AATD,wCASC;AAOD,SAAgB,gBAAgB,CAAC,UAAiB;IAC9C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,WAAW;KAC1C;QACI,mBAAmB;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;KAC1D;SAAI;QACD,YAAY;QACZ,OAAO,IAAI,CAAC;KACf;AACL,CAAC;AATD,4CASC"}
+{"version":3,"file":"loadConfig.js","sourceRoot":"","sources":["../../src/until/loadConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qCAAyB;AAKzB,SAAgB,gBAAgB,CAAC,UAAiB;IAC9C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,WAAW;KAC1C;QACI,mBAAmB;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;KAC1D;SAAI;QACD,YAAY;QACZ,OAAO,IAAI,CAAC;KACf;AACL,CAAC;AATD,4CASC;AAKD,SAAgB,cAAc,CAAC,UAAiB;IAC5C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,WAAW;KAC1C;QACI,mBAAmB;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;KAC1D;SAAI;QACD,YAAY;QACZ,OAAO,IAAI,CAAC;KACf;AACL,CAAC;AATD,wCASC;AAQD,SAAgB,gBAAgB,CAAC,UAAiB;IAC9C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,WAAW;KAC1C;QACI,mBAAmB;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;KAC1D;SAAI;QACD,YAAY;QACZ,OAAO,IAAI,CAAC;KACf;AACL,CAAC;AATD,4CASC"}

+ 2 - 1
config/Custom.json

@@ -1,5 +1,6 @@
 {
   "mainPc": {
     "mac": "00E269589A9E"
-  }
+  },
+  "imgDir": "/home/fc/imageDir"
 }

+ 177 - 0
src/control/imgManager.ts

@@ -0,0 +1,177 @@
+import path from "path";
+import logger from "../lib/logger";
+import * as fs from "fs";
+import {loadCustomConfig} from "../until/loadConfig";
+
+export enum MvMode{
+    move,
+    copy
+}
+export interface MvOptions{
+    checkPath:string,
+    targetPath:string,
+    extName: string,
+    mvMode: MvMode,
+    // 检查子文件夹
+    checkSubDir: boolean,
+    minRating: number,// 最小评级
+}
+
+interface FileCount {
+    child: string[], // 文件名
+    rating: number, // 文件评级
+}
+
+
+
+const customConfig = loadCustomConfig("./config/Custom.json");
+
+export function showDirs ():string[]{
+    if (!customConfig){
+        return [];
+    }
+    let imagePath = customConfig.imgDir;
+    let dirs = fs.readdirSync(imagePath);
+    return dirs;
+}
+
+/**
+ * <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 7.0-c000 1.000000, 0000/00/00-00:00:00        ">
+ *  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ *   <rdf:Description rdf:about=""
+ *     xmlns:xmp="http://ns.adobe.com/xap/1.0/"
+ *     xmlns:tiff="http://ns.adobe.com/tiff/1.0/"
+ *     xmlns:exif="http://ns.adobe.com/exif/1.0/"
+ *     xmlns:aux="http://ns.adobe.com/exif/1.0/aux/"
+ *     xmlns:exifEX="http://cipa.jp/exif/1.0/"
+ *     xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/"
+ *     xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
+ *     xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
+ *     xmlns:dc="http://purl.org/dc/elements/1.1/"
+ *     xmlns:crd="http://ns.adobe.com/camera-raw-defaults/1.0/"
+ *    xmp:Rating="4"
+ *    xmp:CreatorTool="ILCE-7C v2.00"
+ *    xmp:ModifyDate="2024-12-15T14:17:28.732+08:00"
+ *    xmp:CreateDate="2024-12-15T14:17:28.732+08:00"
+ * @param filePath
+ */
+function _parseXmpFile(filePath: string): number
+{
+    // 读取xmp文件 获取 Rating
+    let xmpFile = fs.readFileSync(filePath)
+    let rating: number = 0;
+    // logger.info(xmpFile.toString());
+    xmpFile.toString().split('\n').forEach(line => {
+        // 判断xmp:Rating是否存在
+        if (line.includes('xmp:Rating')){
+            // 获取xmp:Rating的值
+            rating = parseInt(line.split('"')[1]);
+            // logger.info(`filepath: ${filePath} rating: ${rating}`);
+        }
+    })
+    return rating
+}
+
+
+function getImgRating(filePath: string): number
+{
+    let rating: number = 0;
+    // 获取文件后缀
+    let ext = path.extname(filePath);
+    console.log(ext);
+    switch (ext) {
+        case '.xmp':
+            rating = _parseXmpFile(filePath);
+            break;
+        default:
+            logger.info("this file not support");
+            break;
+    }
+    return rating;
+}
+
+export function mvImgFile(mvOptions: MvOptions): number
+{
+    if (!customConfig){
+        return -1;
+    }
+    console.log(mvOptions);
+    let imagePath = customConfig.imgDir;
+    let baseDirPath = path.join(imagePath, mvOptions.checkPath);
+    let targetDirPath = path.join(imagePath, mvOptions.targetPath);
+    let files = fs.readdirSync(baseDirPath);
+    let len = files.length;
+    let fileNameCount: {[key: string]:FileCount} = {};
+    let movedTotal = 0;
+    // 获取文件评分
+    for (let i = 0; i < len; i++)
+    {
+        let dir = files[i];
+        let baseName = path.basename(dir);
+        // 获取文件名 无后缀
+        baseName = baseName.split('.')[0];
+        let dirPath = path.join(baseDirPath, dir);
+        if (!fileNameCount[baseName]){
+            fileNameCount[baseName] = {
+                child: [],
+                rating: -1
+            };
+        }
+        fileNameCount[baseName].child.push(dir)
+        // 添加至其中
+        if (mvOptions.checkSubDir && fs.statSync(dirPath).isDirectory()) {
+            // 添加至dirs后方
+            files.push(...fs.readdirSync(dirPath));
+            len += files.length;
+        }
+        // 判断文件后缀是否为mvOptions.extName
+        if (mvOptions.extName !== undefined && !dir.endsWith(mvOptions.extName)) {
+            continue;
+        }
+        // 获取文件评分
+        let rating = getImgRating(dirPath);
+        // logger.info(`${baseName} 评分为 ${rating}`);
+
+        if (rating >= mvOptions.minRating) {
+            fileNameCount[baseName].rating = rating;
+        }
+    }
+
+    // 评分筛选
+    // let passFile: FileCount[] = [];
+    // for (let key in fileNameCount) {
+    //     if (fileNameCount[key].rating >= mvOptions.minRating) {
+    //         passFile.push(fileNameCount[key]);
+    //     }
+    // }
+    // 创建目标文件夹
+    if (!fs.existsSync(targetDirPath)) {
+        fs.mkdirSync(targetDirPath);
+    }
+    for (let key in fileNameCount) {
+        if (fileNameCount[key].rating < mvOptions.minRating)
+        {
+            continue;
+        }
+        movedTotal ++;
+        let passFile = fileNameCount[key];
+        let tmpFn = fs.copyFileSync;
+        if (mvOptions.mvMode === MvMode.move)
+        {
+            tmpFn = fs.renameSync;
+        }
+        else {
+            tmpFn = fs.copyFileSync;
+        }
+        console.log(passFile)
+        logger.info(`移动文件 file ${key}`);
+
+        for (let j = 0; j < passFile.child.length; j++)
+        {
+            logger.info(`正在转移文件 ${passFile.child[j]}`)
+
+            tmpFn(path.join(baseDirPath, passFile.child[j]), path.join(targetDirPath, passFile.child[j]))
+        }
+    }
+    return movedTotal;
+}

+ 2 - 1
src/until/loadConfig.ts

@@ -31,7 +31,8 @@ export function loadFileConfig(configPath:string):FileConf{
 interface CustomConf{
     mainPc: {
         mac: string
-    }
+    },
+    imgDir: string
 }
 export function loadCustomConfig(configPath:string):CustomConf{
     if (fs.existsSync(configPath)) //判断是否存在此文件

+ 3 - 0
src/webRouter/apiIndex.ts

@@ -3,8 +3,11 @@ import {Router} from "express"
 import fs from "./fs"
 import qqHook from "./qqHook";
 import pcControl from "./pcControl";
+import imgControl from "./imgControl";
 const router = Router();
 router.use("/fs",fs);
 router.use("/iq",qqHook);
 router.use("/pcc",pcControl);
+router.use("/img", imgControl);
+
 export default router;

+ 46 - 0
src/webRouter/imgControl.ts

@@ -0,0 +1,46 @@
+import logger from "../lib/logger";
+import {openPc} from "../lib/wol";
+import router from "./pcControl";
+import {mvImgFile, MvMode, showDirs} from "../control/imgManager";
+
+router.get('/showDir',(req, res)=> {
+    res.json({
+        code:0,
+        data: showDirs()
+    });
+    res.json({
+        code:0,
+        msg:'ok'
+    });
+})
+
+router.get('/moveFile',(req, res)=> {
+    try {
+        const checkPath = req.query.src as string;
+        const destPath = req.query.dest as string;
+        const extName = req.query.extName as string;
+        const minRating = req.query.rating ? Number(req.query.rating) : 1;
+        const checkSubDir = req.query.subDir === '1';
+        const mvModeValue = req.query.mode as string;
+        const mvMode = mvModeValue === 'move' ? MvMode.move : MvMode.copy;
+
+        if (!checkPath || !destPath || !extName)
+            return res.status(400).json({ code: -1, msg: '参数错误: src: images, dest: target, extName: xmp' });
+        const result = mvImgFile({
+            checkPath: checkPath,
+            targetPath: destPath,
+            checkSubDir: checkSubDir,
+            extName: extName,
+            minRating: minRating,
+            mvMode: mvMode,
+        });
+        res.json({ code: 0, data: { moved: result } });
+    } catch (e)
+    {
+        logger.error(e);
+        res.status(500).json({ code: -1 });
+    }
+})
+
+
+export default router;