修复注册扫描识别失败问题
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const { defaultLogger } = require('../utils/logger');
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
const ConfigManager = require('../config/config-manager');
|
||||||
|
const artworkUtils = require('../utils/artwork-utils');
|
||||||
|
|
||||||
// 创建logger实例
|
// 创建logger实例
|
||||||
const logger = defaultLogger.child('DownloadRegistry');
|
const logger = defaultLogger.child('DownloadRegistry');
|
||||||
@@ -12,14 +14,14 @@ const logger = defaultLogger.child('DownloadRegistry');
|
|||||||
class DownloadRegistry {
|
class DownloadRegistry {
|
||||||
constructor(dataPath) {
|
constructor(dataPath) {
|
||||||
this.dataPath = dataPath;
|
this.dataPath = dataPath;
|
||||||
this.registryPath = path.join(dataPath, 'download_registry.json');
|
this.registryPath = path.join(dataPath, 'download-registry.json');
|
||||||
this.registry = {
|
this.registry = {
|
||||||
version: '1.0.5',
|
version: '1.0.5',
|
||||||
created_at: new Date().toISOString(),
|
artists: {},
|
||||||
updated_at: new Date().toISOString(),
|
lastUpdated: null
|
||||||
artists: {} // 格式: { artistName: { artworks: [artworkId1, artworkId2, ...] } }
|
|
||||||
};
|
};
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
this.configManager = new ConfigManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,6 +187,44 @@ class DownloadRegistry {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从作品目录名中提取作品ID
|
||||||
|
* @param {string} artworkDir - 作品目录名
|
||||||
|
* @returns {number|null} 作品ID,如果无法提取则返回null
|
||||||
|
*/
|
||||||
|
async extractArtworkIdFromDir(artworkDir) {
|
||||||
|
return await artworkUtils.extractArtworkIdFromDir(artworkDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查作品是否已在注册表中注册
|
||||||
|
* @param {string} artistName - 作者名称
|
||||||
|
* @param {string} artworkDir - 作品目录名
|
||||||
|
* @returns {boolean} 是否已注册
|
||||||
|
*/
|
||||||
|
async isArtworkRegistered(artistName, artworkDir) {
|
||||||
|
if (!this.loaded) {
|
||||||
|
await this.loadRegistry();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用动态方法从作品目录名中提取作品ID
|
||||||
|
const artworkId = await this.extractArtworkIdFromDir(artworkDir);
|
||||||
|
if (!artworkId) {
|
||||||
|
logger.warn(`无法从作品目录名中提取作品ID: ${artworkDir}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedArtistName = this.normalizeArtistName(artistName);
|
||||||
|
|
||||||
|
// 检查艺术家是否存在
|
||||||
|
if (!this.registry.artists[normalizedArtistName]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查作品是否在艺术家的作品列表中
|
||||||
|
return this.registry.artists[normalizedArtistName].artworks.includes(artworkId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取已下载的作品ID列表
|
* 获取已下载的作品ID列表
|
||||||
* @returns {number[]} 作品ID数组
|
* @returns {number[]} 作品ID数组
|
||||||
@@ -387,10 +427,10 @@ class DownloadRegistry {
|
|||||||
const isRegistered = await this.isArtworkRegistered(artistDir, artworkDir);
|
const isRegistered = await this.isArtworkRegistered(artistDir, artworkDir);
|
||||||
|
|
||||||
if (!isRegistered) {
|
if (!isRegistered) {
|
||||||
// 获取作品信息并添加到注册表
|
// 从作品目录名中提取作品ID并添加到注册表
|
||||||
const artworkInfo = await fileManager.getArtworkInfo(artistDir, artworkDir);
|
const artworkId = await this.extractArtworkIdFromDir(artworkDir);
|
||||||
if (artworkInfo) {
|
if (artworkId) {
|
||||||
await this.addArtwork(artistDir, artworkDir, artworkInfo);
|
await this.addArtwork(artistDir, artworkId);
|
||||||
stats.addedArtworks++;
|
stats.addedArtworks++;
|
||||||
logger.debug(`添加作品到注册表: ${artistDir}/${artworkDir}`);
|
logger.debug(`添加作品到注册表: ${artistDir}/${artworkDir}`);
|
||||||
}
|
}
|
||||||
@@ -451,8 +491,8 @@ class DownloadRegistry {
|
|||||||
const artworkEntries = await fileManager.listDirectory(artistPath);
|
const artworkEntries = await fileManager.listDirectory(artistPath);
|
||||||
|
|
||||||
for (const entry of artworkEntries) {
|
for (const entry of artworkEntries) {
|
||||||
const match = entry.match(/^(\d+)_(.+)$/);
|
const extractedArtworkId = await artworkUtils.extractArtworkIdFromDir(entry);
|
||||||
if (match && parseInt(match[1]) === artworkId) {
|
if (extractedArtworkId && extractedArtworkId === artworkId) {
|
||||||
const artworkPath = path.join(artistPath, entry);
|
const artworkPath = path.join(artistPath, entry);
|
||||||
const infoPath = path.join(artworkPath, 'artwork_info.json');
|
const infoPath = path.join(artworkPath, 'artwork_info.json');
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const DownloadRegistry = require('./download-registry');
|
|||||||
const CacheConfigManager = require('../config/cache-config');
|
const CacheConfigManager = require('../config/cache-config');
|
||||||
const fs = require('fs-extra'); // Added for fs-extra
|
const fs = require('fs-extra'); // Added for fs-extra
|
||||||
const { defaultLogger } = require('../utils/logger');
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
const artworkUtils = require('../utils/artwork-utils');
|
||||||
|
|
||||||
// 创建logger实例
|
// 创建logger实例
|
||||||
const logger = defaultLogger.child('DownloadService');
|
const logger = defaultLogger.child('DownloadService');
|
||||||
@@ -784,11 +785,9 @@ class DownloadService {
|
|||||||
const artworks = await this.fileManager.listDirectory(artistPath);
|
const artworks = await this.fileManager.listDirectory(artistPath);
|
||||||
|
|
||||||
for (const artwork of artworks) {
|
for (const artwork of artworks) {
|
||||||
// 检查是否是作品目录(包含数字ID)
|
// 使用工具函数检查是否是作品目录并提取ID
|
||||||
const artworkMatch = artwork.match(/^(\d+)_(.+)$/);
|
const artworkId = await artworkUtils.extractArtworkIdFromDir(artwork);
|
||||||
if (artworkMatch) {
|
if (artworkId) {
|
||||||
const artworkId = artworkMatch[1];
|
|
||||||
|
|
||||||
// 检查作品目录是否包含图片文件
|
// 检查作品目录是否包含图片文件
|
||||||
const artworkPath = path.join(artistPath, artwork);
|
const artworkPath = path.join(artistPath, artwork);
|
||||||
const artworkStat = await this.fileManager.getFileInfo(artworkPath);
|
const artworkStat = await this.fileManager.getFileInfo(artworkPath);
|
||||||
@@ -797,7 +796,7 @@ class DownloadService {
|
|||||||
const files = await this.fileManager.listDirectory(artworkPath);
|
const files = await this.fileManager.listDirectory(artworkPath);
|
||||||
const imageFiles = files.filter(file => /\.(jpg|jpeg|png|gif|webp)$/i.test(file));
|
const imageFiles = files.filter(file => /\.(jpg|jpeg|png|gif|webp)$/i.test(file));
|
||||||
if (imageFiles.length > 0) {
|
if (imageFiles.length > 0) {
|
||||||
downloadedIds.add(parseInt(artworkId));
|
downloadedIds.add(artworkId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const { exec } = require('child_process')
|
|||||||
const ConfigManager = require('../config/config-manager')
|
const ConfigManager = require('../config/config-manager')
|
||||||
const execAsync = promisify(exec)
|
const execAsync = promisify(exec)
|
||||||
const { defaultLogger } = require('../utils/logger');
|
const { defaultLogger } = require('../utils/logger');
|
||||||
|
const artworkUtils = require('../utils/artwork-utils');
|
||||||
|
|
||||||
// 创建logger实例
|
// 创建logger实例
|
||||||
const logger = defaultLogger.child('RepositoryService');
|
const logger = defaultLogger.child('RepositoryService');
|
||||||
@@ -465,9 +466,9 @@ class RepositoryService {
|
|||||||
for (const artworkEntry of artworkEntries) {
|
for (const artworkEntry of artworkEntries) {
|
||||||
if (!artworkEntry.isDirectory()) continue
|
if (!artworkEntry.isDirectory()) continue
|
||||||
|
|
||||||
// 检查是否是目标作品目录(包含数字ID)
|
// 使用工具函数检查是否是目标作品目录
|
||||||
const artworkMatch = artworkEntry.name.match(/^(\d+)_(.+)$/)
|
const extractedArtworkId = await artworkUtils.extractArtworkIdFromDir(artworkEntry.name);
|
||||||
if (artworkMatch && artworkMatch[1] === artworkId.toString()) {
|
if (extractedArtworkId && extractedArtworkId === parseInt(artworkId)) {
|
||||||
const artworkPath = path.join(artistPath, artworkEntry.name)
|
const artworkPath = path.join(artistPath, artworkEntry.name)
|
||||||
|
|
||||||
// 检查作品信息文件 - 这是最可靠的判断标准
|
// 检查作品信息文件 - 这是最可靠的判断标准
|
||||||
@@ -769,9 +770,10 @@ class RepositoryService {
|
|||||||
const artistDir = artwork.artistPath
|
const artistDir = artwork.artistPath
|
||||||
try {
|
try {
|
||||||
const artistEntries = await fs.readdir(artistDir, { withFileTypes: true })
|
const artistEntries = await fs.readdir(artistDir, { withFileTypes: true })
|
||||||
const hasArtworks = artistEntries.some(entry =>
|
const hasArtworks = artistEntries.some(async (entry) => {
|
||||||
entry.isDirectory() && entry.name.match(/^\d+_/)
|
if (!entry.isDirectory()) return false;
|
||||||
)
|
return await artworkUtils.isArtworkDirectory(entry.name);
|
||||||
|
});
|
||||||
|
|
||||||
if (!hasArtworks) {
|
if (!hasArtworks) {
|
||||||
await fs.rmdir(artistDir)
|
await fs.rmdir(artistDir)
|
||||||
@@ -816,11 +818,11 @@ class RepositoryService {
|
|||||||
for (const artworkEntry of artworkEntries) {
|
for (const artworkEntry of artworkEntries) {
|
||||||
if (!artworkEntry.isDirectory()) continue
|
if (!artworkEntry.isDirectory()) continue
|
||||||
|
|
||||||
// 检查是否是目标作品目录(包含数字ID)
|
// 使用工具函数检查是否是目标作品目录
|
||||||
const artworkMatch = artworkEntry.name.match(/^(\d+)_(.+)$/)
|
const extractedArtworkId = await artworkUtils.extractArtworkIdFromDir(artworkEntry.name);
|
||||||
if (artworkMatch && artworkMatch[1] === artworkId.toString()) {
|
if (extractedArtworkId && extractedArtworkId === parseInt(artworkId)) {
|
||||||
const artworkPath = path.join(artistPath, artworkEntry.name)
|
const artworkPath = path.join(artistPath, artworkEntry.name)
|
||||||
const title = artworkMatch[2]
|
const title = await artworkUtils.extractTitleFromDir(artworkEntry.name) || 'Unknown Title';
|
||||||
|
|
||||||
// 找到目标作品,返回基本信息(不需要扫描文件详情)
|
// 找到目标作品,返回基本信息(不需要扫描文件详情)
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
const ConfigManager = require('../config/config-manager');
|
||||||
|
const { defaultLogger } = require('./logger');
|
||||||
|
|
||||||
|
const logger = defaultLogger.child('ArtworkUtils');
|
||||||
|
|
||||||
|
class ArtworkUtils {
|
||||||
|
constructor() {
|
||||||
|
this.configManager = new ConfigManager();
|
||||||
|
this._cachedConfig = null;
|
||||||
|
this._configCacheTime = 0;
|
||||||
|
this.CACHE_DURATION = 30000; // 30秒缓存
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取配置(带缓存)
|
||||||
|
* @returns {Promise<Object>} 配置对象
|
||||||
|
*/
|
||||||
|
async getConfig() {
|
||||||
|
const now = Date.now();
|
||||||
|
if (this._cachedConfig && (now - this._configCacheTime) < this.CACHE_DURATION) {
|
||||||
|
return this._cachedConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cachedConfig = await this.configManager.readConfig();
|
||||||
|
this._configCacheTime = now;
|
||||||
|
return this._cachedConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从作品目录名中提取作品ID
|
||||||
|
* @param {string} artworkDir - 作品目录名
|
||||||
|
* @returns {Promise<number|null>} 作品ID,如果无法提取则返回null
|
||||||
|
*/
|
||||||
|
async extractArtworkIdFromDir(artworkDir) {
|
||||||
|
try {
|
||||||
|
// 获取配置中的命名模式
|
||||||
|
const config = await this.getConfig();
|
||||||
|
const namingPattern = config.namingPattern || "{artist_name}/{artwork_id}_{title}";
|
||||||
|
|
||||||
|
// 从命名模式中提取作品目录部分(去掉 {artist_name}/ 前缀)
|
||||||
|
let artworkPattern = namingPattern;
|
||||||
|
if (artworkPattern.includes('/')) {
|
||||||
|
artworkPattern = artworkPattern.split('/').pop(); // 取最后一部分
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将命名模式转换为正则表达式
|
||||||
|
// 替换占位符为对应的正则表达式组
|
||||||
|
let regexPattern = artworkPattern
|
||||||
|
.replace(/\{artwork_id\}/g, '(\\d+)') // artwork_id 匹配数字
|
||||||
|
.replace(/\{title\}/g, '(.+)') // title 匹配任意字符
|
||||||
|
.replace(/\{artist_name\}/g, '(.+)') // artist_name 匹配任意字符(虽然在这里不应该出现)
|
||||||
|
.replace(/\{[^}]+\}/g, '(.+)'); // 其他占位符匹配任意字符
|
||||||
|
|
||||||
|
// 转义特殊字符
|
||||||
|
regexPattern = regexPattern.replace(/[.*+?^${}()|[\]\\]/g, (match) => {
|
||||||
|
// 不转义我们添加的正则表达式组
|
||||||
|
if (match === '(' || match === ')' || match === '\\') {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
return '\\' + match;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建正则表达式
|
||||||
|
const regex = new RegExp(`^${regexPattern}$`);
|
||||||
|
const match = artworkDir.match(regex);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
// 找到 artwork_id 在模式中的位置
|
||||||
|
const placeholders = artworkPattern.match(/\{[^}]+\}/g) || [];
|
||||||
|
const artworkIdIndex = placeholders.findIndex(placeholder => placeholder === '{artwork_id}');
|
||||||
|
|
||||||
|
if (artworkIdIndex !== -1 && match[artworkIdIndex + 1]) {
|
||||||
|
return parseInt(match[artworkIdIndex + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果动态解析失败,回退到默认格式 {artwork_id}_{title}
|
||||||
|
const fallbackMatch = artworkDir.match(/^(\d+)_(.+)$/);
|
||||||
|
if (fallbackMatch) {
|
||||||
|
logger.debug(`使用回退模式解析作品目录: ${artworkDir}`);
|
||||||
|
return parseInt(fallbackMatch[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(`解析作品目录名失败: ${artworkDir}`, error.message);
|
||||||
|
|
||||||
|
// 发生错误时回退到默认格式
|
||||||
|
const fallbackMatch = artworkDir.match(/^(\d+)_(.+)$/);
|
||||||
|
if (fallbackMatch) {
|
||||||
|
return parseInt(fallbackMatch[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查目录名是否匹配作品目录格式
|
||||||
|
* @param {string} dirName - 目录名
|
||||||
|
* @returns {Promise<boolean>} 是否匹配作品目录格式
|
||||||
|
*/
|
||||||
|
async isArtworkDirectory(dirName) {
|
||||||
|
const artworkId = await this.extractArtworkIdFromDir(dirName);
|
||||||
|
return artworkId !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从作品目录名中提取标题
|
||||||
|
* @param {string} artworkDir - 作品目录名
|
||||||
|
* @returns {Promise<string|null>} 作品标题,如果无法提取则返回null
|
||||||
|
*/
|
||||||
|
async extractTitleFromDir(artworkDir) {
|
||||||
|
try {
|
||||||
|
// 获取配置中的命名模式
|
||||||
|
const config = await this.getConfig();
|
||||||
|
const namingPattern = config.namingPattern || "{artist_name}/{artwork_id}_{title}";
|
||||||
|
|
||||||
|
// 从命名模式中提取作品目录部分(去掉 {artist_name}/ 前缀)
|
||||||
|
let artworkPattern = namingPattern;
|
||||||
|
if (artworkPattern.includes('/')) {
|
||||||
|
artworkPattern = artworkPattern.split('/').pop(); // 取最后一部分
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将命名模式转换为正则表达式
|
||||||
|
let regexPattern = artworkPattern
|
||||||
|
.replace(/\{artwork_id\}/g, '(\\d+)') // artwork_id 匹配数字
|
||||||
|
.replace(/\{title\}/g, '(.+)') // title 匹配任意字符
|
||||||
|
.replace(/\{artist_name\}/g, '(.+)') // artist_name 匹配任意字符
|
||||||
|
.replace(/\{[^}]+\}/g, '(.+)'); // 其他占位符匹配任意字符
|
||||||
|
|
||||||
|
// 转义特殊字符
|
||||||
|
regexPattern = regexPattern.replace(/[.*+?^${}()|[\]\\]/g, (match) => {
|
||||||
|
if (match === '(' || match === ')' || match === '\\') {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
return '\\' + match;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建正则表达式
|
||||||
|
const regex = new RegExp(`^${regexPattern}$`);
|
||||||
|
const match = artworkDir.match(regex);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
// 找到 title 在模式中的位置
|
||||||
|
const placeholders = artworkPattern.match(/\{[^}]+\}/g) || [];
|
||||||
|
const titleIndex = placeholders.findIndex(placeholder => placeholder === '{title}');
|
||||||
|
|
||||||
|
if (titleIndex !== -1 && match[titleIndex + 1]) {
|
||||||
|
return match[titleIndex + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果动态解析失败,回退到默认格式 {artwork_id}_{title}
|
||||||
|
const fallbackMatch = artworkDir.match(/^(\d+)_(.+)$/);
|
||||||
|
if (fallbackMatch) {
|
||||||
|
return fallbackMatch[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(`解析作品标题失败: ${artworkDir}`, error.message);
|
||||||
|
|
||||||
|
// 发生错误时回退到默认格式
|
||||||
|
const fallbackMatch = artworkDir.match(/^(\d+)_(.+)$/);
|
||||||
|
if (fallbackMatch) {
|
||||||
|
return fallbackMatch[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建单例实例
|
||||||
|
const artworkUtils = new ArtworkUtils();
|
||||||
|
|
||||||
|
module.exports = artworkUtils;
|
||||||
Reference in New Issue
Block a user