mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-06 20:42:57 +08:00
🚧 WIP(custom): optimize gallery db sync logic and force refresh gallery
This commit is contained in:
@@ -107,8 +107,8 @@ class GalleryDB {
|
||||
console.log('init gallery db')
|
||||
}
|
||||
|
||||
static getInstance(): DBStore {
|
||||
if (!GalleryDB.#instance) {
|
||||
static getInstance(forceRefresh: boolean = false): DBStore {
|
||||
if (!GalleryDB.#instance || forceRefresh) {
|
||||
GalleryDB.#instance = new DBStore(DB_PATH, 'gallery')
|
||||
}
|
||||
return GalleryDB.#instance
|
||||
|
||||
@@ -100,6 +100,8 @@ export interface IConfigStruct {
|
||||
autoImport: boolean
|
||||
autoImportPicBed: string[]
|
||||
galleryPicBedFilter: string[]
|
||||
enableSecondUploader?: boolean
|
||||
lastSyncTime?: number
|
||||
}
|
||||
needReload: boolean
|
||||
picgoPlugins: IPicGoPlugins
|
||||
@@ -189,6 +191,7 @@ export const configPaths = {
|
||||
autoImportPicBed: 'settings.autoImportPicBed',
|
||||
galleryPicBedFilter: 'settings.galleryPicBedFilter',
|
||||
enableSecondUploader: 'settings.enableSecondUploader',
|
||||
lastSyncTime: 'settings.lastSyncTime',
|
||||
},
|
||||
needReload: 'needReload',
|
||||
picgoPlugins: 'picgoPlugins',
|
||||
|
||||
@@ -2,6 +2,7 @@ import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
|
||||
import db from '@core/datastore'
|
||||
import { GalleryDB } from '@core/datastore'
|
||||
import logger from '@core/picgo/logger'
|
||||
import { Octokit } from '@octokit/rest'
|
||||
import axios from 'axios'
|
||||
@@ -10,15 +11,15 @@ import fs from 'fs-extra'
|
||||
import { HttpsProxyAgent } from 'hpagent'
|
||||
import { AuthType, createClient, WebDAVClientOptions } from 'webdav'
|
||||
|
||||
import type { ISyncConfig } from '#/types/types'
|
||||
import type { IGalleryDBFile, IGalleryDBGalleryItem, ISyncConfig } from '#/types/types'
|
||||
import { extractData, zipData } from '~/utils/common'
|
||||
import { formatEndpoint } from '~/utils/common'
|
||||
import { configPaths } from '~/utils/configPaths'
|
||||
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
const tempDir = path.join(os.tmpdir(), `piclist-sync-tmp`)
|
||||
const db1 = path.join(tempDir, 'db1')
|
||||
const db2 = path.join(tempDir, 'db2')
|
||||
const localDBPath = path.join(tempDir, 'db1')
|
||||
const remoteDBPath = path.join(tempDir, 'db2')
|
||||
const dbMerged = path.join(tempDir, 'db-merged')
|
||||
const galleryDBList = ['piclist.db', 'piclist.bak.db']
|
||||
|
||||
@@ -30,37 +31,50 @@ const uploadOrUpdateMsg = (fileName: string, isUpdate: boolean = true) =>
|
||||
isUpdate ? `update ${fileName} from PicList` : `upload ${fileName} from PicList`
|
||||
|
||||
const emptyDir = async (): Promise<void> => {
|
||||
await fs.emptyDir(tempDir)
|
||||
await fs.emptyDir(db1)
|
||||
await fs.emptyDir(db2)
|
||||
await fs.emptyDir(dbMerged)
|
||||
for (const dir of [tempDir, localDBPath, remoteDBPath, dbMerged]) {
|
||||
await fs.emptyDir(dir)
|
||||
}
|
||||
}
|
||||
|
||||
const mergeGalleryDB = async (targetFile: string) => {
|
||||
const lastSyncTime = db.get(configPaths.settings.lastSyncTime) || 0
|
||||
try {
|
||||
const db1Data = await extractData(path.join(db1, targetFile))
|
||||
const db2Data = await extractData(path.join(db2, targetFile))
|
||||
const mergedData: any = {
|
||||
gallery: [],
|
||||
__gallery_KEY__: {},
|
||||
}
|
||||
const db1Ids = new Set<string>(Object.keys(db1Data.__gallery_KEY__ || {}))
|
||||
const db2Ids = new Set<string>(Object.keys(db2Data.__gallery_KEY__ || {}))
|
||||
const idSet = new Set<string>([...db1Ids, ...db2Ids])
|
||||
for (const id of idSet) {
|
||||
if (db2Ids.has(id)) {
|
||||
mergedData.gallery.push(db2Data.gallery.find((item: any) => item.id === id))
|
||||
} else if (db1Ids.has(id)) {
|
||||
mergedData.gallery.push(db1Data.gallery.find((item: any) => item.id === id))
|
||||
const localDBData = (await extractData(path.join(localDBPath, targetFile))) as IGalleryDBFile
|
||||
const remoteDBData = (await extractData(path.join(remoteDBPath, targetFile))) as IGalleryDBFile
|
||||
const localMap = new Map(localDBData.gallery.map((item: IGalleryDBGalleryItem) => [item.id, item]))
|
||||
const remoteMap = new Map(remoteDBData.gallery.map((item: IGalleryDBGalleryItem) => [item.id, item]))
|
||||
const mergedGalleryMap = new Map<string, any>()
|
||||
|
||||
for (const [id, localItem] of localMap) {
|
||||
const remoteItem = remoteMap.get(id)
|
||||
if (!remoteItem) {
|
||||
mergedGalleryMap.set(id, localItem)
|
||||
} else {
|
||||
const newest = (localItem.updatedAt || 0) >= (remoteItem.updatedAt || 0) ? localItem : remoteItem
|
||||
mergedGalleryMap.set(id, newest)
|
||||
}
|
||||
}
|
||||
for (const item of mergedData.gallery) {
|
||||
mergedData.__gallery_KEY__[item.id] = 1
|
||||
for (const [id, remoteItem] of remoteMap) {
|
||||
if (!localMap.has(id) && (remoteItem.updatedAt || 0) >= lastSyncTime) {
|
||||
console.log('newer in remote:', JSON.stringify(remoteItem))
|
||||
mergedGalleryMap.set(id, remoteItem)
|
||||
}
|
||||
}
|
||||
await zipData(mergedData, path.join(dbMerged, targetFile))
|
||||
await fs.copyFile(path.join(dbMerged, targetFile), path.join(STORE_PATH, targetFile))
|
||||
|
||||
const galleryKeyObj: Record<string, number> = {}
|
||||
mergedGalleryMap.forEach((_, id) => {
|
||||
galleryKeyObj[id] = 1
|
||||
})
|
||||
const mergedData = {
|
||||
gallery: Array.from(mergedGalleryMap.values()),
|
||||
__gallery_KEY__: galleryKeyObj,
|
||||
}
|
||||
const targetFilePath = path.join(dbMerged, targetFile)
|
||||
await zipData(mergedData, targetFilePath)
|
||||
await fs.copyFile(targetFilePath, path.join(STORE_PATH, targetFile))
|
||||
} catch (err: any) {
|
||||
logger.error('merge gallery db failed:', String(err))
|
||||
throw new Error('merge gallery db failed')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,7 +367,7 @@ async function downloadAndWriteFile(url: string, localFilePath: string, config:
|
||||
}
|
||||
|
||||
async function downloadRemoteToLocal(syncConfig: ISyncConfig, fileName: string, galleryMode = false) {
|
||||
const storePath = galleryMode ? db2 : STORE_PATH
|
||||
const storePath = galleryMode ? remoteDBPath : STORE_PATH
|
||||
const localFilePath = path.join(storePath, fileName)
|
||||
const { username, repo, branch, token, proxy, type } = syncConfig
|
||||
try {
|
||||
@@ -582,6 +596,8 @@ async function syncGallery(): Promise<number> {
|
||||
await uploadLocalToRemote(syncConfig, file)
|
||||
logger.info(`gallery db ${file} not exist in cloud, upload local file instead`)
|
||||
successCount++
|
||||
db.set(configPaths.settings.lastSyncTime, Date.now())
|
||||
GalleryDB.getInstance(true)
|
||||
continue
|
||||
}
|
||||
} catch (err: any) {
|
||||
@@ -589,9 +605,11 @@ async function syncGallery(): Promise<number> {
|
||||
continue
|
||||
}
|
||||
await downloadRemoteToLocal(syncConfig, file, true)
|
||||
await fs.copyFile(path.join(STORE_PATH, file), path.join(db1, file))
|
||||
await fs.copyFile(path.join(STORE_PATH, file), path.join(localDBPath, file))
|
||||
await mergeGalleryDB(file)
|
||||
await updateLocalToRemote(syncConfig, file)
|
||||
db.set(configPaths.settings.lastSyncTime, Date.now())
|
||||
GalleryDB.getInstance(true) // refresh gallery db instance
|
||||
logger.info(`sync gallery db ${file} success`)
|
||||
successCount++
|
||||
}
|
||||
|
||||
@@ -717,10 +717,10 @@
|
||||
"syncConfigNote": "The files to be synced are configuration files",
|
||||
"syncConfigProxy": "Proxy",
|
||||
"syncConfiguration": "Sync Configuration",
|
||||
"syncEndpointConfig": "Sync Endpoint Configuration",
|
||||
"syncEndpointConfig": "Endpoint Configuration",
|
||||
"syncResult": { "failed": "Sync Failed", "success": "Sync Successful" },
|
||||
"title": "Sync",
|
||||
"upDownloadSettings": "Upload and Download Settings",
|
||||
"upDownloadSettings": "Sync Config & Gallery",
|
||||
"uploadSettings": "Upload Settings",
|
||||
"webdav": {
|
||||
"authType": "WebDAV Auth Type",
|
||||
|
||||
@@ -712,10 +712,10 @@
|
||||
"syncConfigNote": "同步的文件为配置文件",
|
||||
"syncConfigProxy": "代理",
|
||||
"syncConfiguration": "同步配置",
|
||||
"syncEndpointConfig": "同步方案配置",
|
||||
"syncEndpointConfig": "平台设置",
|
||||
"syncResult": { "failed": "同步失败", "success": "同步成功" },
|
||||
"title": "配置/同步",
|
||||
"upDownloadSettings": "上传下载配置文件",
|
||||
"title": "配置/相册同步",
|
||||
"upDownloadSettings": "同步配置和相册",
|
||||
"uploadSettings": "上传配置",
|
||||
"webdav": {
|
||||
"authType": "WebDAV 认证类型",
|
||||
|
||||
@@ -712,10 +712,10 @@
|
||||
"syncConfigNote": "同步的文件為配置文件",
|
||||
"syncConfigProxy": "代理",
|
||||
"syncConfiguration": "同步配置",
|
||||
"syncEndpointConfig": "同步方案配置",
|
||||
"syncEndpointConfig": "平台配置",
|
||||
"syncResult": { "failed": "同步失敗", "success": "同步成功" },
|
||||
"title": "配置/同步",
|
||||
"upDownloadSettings": "上傳下載配置文件",
|
||||
"title": "配置/相冊同步",
|
||||
"upDownloadSettings": "同步配置和相冊",
|
||||
"uploadSettings": "上傳配置",
|
||||
"webdav": {
|
||||
"authType": "WebDAV 認證類型",
|
||||
|
||||
@@ -523,3 +523,13 @@ export interface IHTTPProxy {
|
||||
port: number
|
||||
protocol: string
|
||||
}
|
||||
|
||||
export interface IGalleryDBGalleryItem {
|
||||
id: string
|
||||
updatedAt?: number
|
||||
[propName: string]: any
|
||||
}
|
||||
export interface IGalleryDBFile {
|
||||
gallery: IGalleryDBGalleryItem[]
|
||||
__gallery_KEY__: Record<string, number>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user