Skip to content

Commit e5d0602

Browse files
committed
feat: add position filling stage in manifest building
1 parent f3221f4 commit e5d0602

File tree

1 file changed

+83
-1
lines changed

1 file changed

+83
-1
lines changed

packages/builder/src/builder/builder.ts

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import { thumbnailExists } from '../image/thumbnail.js'
44
import { logger } from '../logger/index.js'
55
import { handleDeletedPhotos, loadExistingManifest, needsUpdate, saveManifest } from '../manifest/manager.js'
66
import { CURRENT_MANIFEST_VERSION } from '../manifest/version.js'
7+
import { createStorageKeyNormalizer, runWithPhotoExecutionContext } from '../photo/execution-context.js'
8+
import { createGeocodingProvider, parseGPSCoordinates } from '../photo/geocoding.js'
9+
import { createPhotoProcessingLoggers } from '../photo/logger-adapter.js'
710
import type { PhotoProcessorOptions } from '../photo/processor.js'
811
import { processPhoto } from '../photo/processor.js'
912
import type { PluginRunState } from '../plugins/manager.js'
@@ -18,7 +21,7 @@ import type { StorageConfig } from '../storage/index.js'
1821
import { StorageFactory, StorageManager } from '../storage/index.js'
1922
import type { BuilderConfig, UserBuilderSettings } from '../types/config.js'
2023
import type { AfilmoryManifest, CameraInfo, LensInfo } from '../types/manifest.js'
21-
import type { PhotoManifestItem, ProcessPhotoResult } from '../types/photo.js'
24+
import type { LocationInfo, PhotoManifestItem, ProcessPhotoResult } from '../types/photo.js'
2225
import { ClusterPool } from '../worker/cluster-pool.js'
2326
import type { TaskCompletedPayload } from '../worker/pool.js'
2427
import { WorkerPool } from '../worker/pool.js'
@@ -417,6 +420,13 @@ export class AfilmoryBuilder {
417420
})
418421
}
419422

423+
const locationRetryStats = await this.retryMissingLocations(manifest)
424+
if (locationRetryStats.attempted > 0) {
425+
logger.main.info(
426+
`📍 为 ${locationRetryStats.attempted} 张缺失位置信息的照片尝试补全,成功 ${locationRetryStats.updated} 张`,
427+
)
428+
}
429+
420430
await this.emitPluginEvent(runState, 'afterProcessTasks', {
421431
options,
422432
tasks: tasksToProcess,
@@ -669,6 +679,78 @@ export class AfilmoryBuilder {
669679
return this.storageManager
670680
}
671681

682+
private async retryMissingLocations(manifest: PhotoManifestItem[]): Promise<{ attempted: number; updated: number }> {
683+
const processingSettings = this.config.system.processing
684+
if (!processingSettings.enableGeocoding) {
685+
return { attempted: 0, updated: 0 }
686+
}
687+
688+
const provider = createGeocodingProvider(
689+
processingSettings.geocodingProvider || 'auto',
690+
processingSettings.mapboxToken,
691+
processingSettings.nominatimBaseUrl,
692+
)
693+
694+
if (!provider) {
695+
return { attempted: 0, updated: 0 }
696+
}
697+
698+
const hasCandidate = manifest.some(
699+
(item) =>
700+
!item.location && item.exif && item.exif.GPSLatitude !== undefined && item.exif.GPSLongitude !== undefined,
701+
)
702+
703+
if (!hasCandidate) {
704+
return { attempted: 0, updated: 0 }
705+
}
706+
707+
const storageManager = this.ensureStorageManager()
708+
const storageConfig = this.getStorageConfig()
709+
const normalizeStorageKey = createStorageKeyNormalizer(storageConfig)
710+
const loggers = createPhotoProcessingLoggers(0, logger)
711+
712+
return await runWithPhotoExecutionContext(
713+
{
714+
builder: this,
715+
storageManager,
716+
storageConfig,
717+
normalizeStorageKey,
718+
loggers,
719+
},
720+
async () => {
721+
const coordinateCache = new Map<string, LocationInfo | null>()
722+
let attempted = 0
723+
let updated = 0
724+
725+
for (const item of manifest) {
726+
if (item.location || !item.exif) {
727+
continue
728+
}
729+
730+
const { latitude, longitude } = parseGPSCoordinates(item.exif)
731+
if (latitude === undefined || longitude === undefined) {
732+
continue
733+
}
734+
735+
const cacheKey = `${latitude.toFixed(4)},${longitude.toFixed(4)}`
736+
let locationInfo = coordinateCache.get(cacheKey)
737+
if (locationInfo === undefined) {
738+
locationInfo = await provider.reverseGeocode(latitude, longitude)
739+
coordinateCache.set(cacheKey, locationInfo ?? null)
740+
}
741+
742+
attempted++
743+
if (locationInfo) {
744+
item.location = locationInfo
745+
updated++
746+
}
747+
}
748+
749+
return { attempted, updated }
750+
},
751+
)
752+
}
753+
672754
private getUserSettings(): UserBuilderSettings {
673755
if (!this.config.user) {
674756
throw new Error('User configuration is missing. 请配置 system/user 设置。')

0 commit comments

Comments
 (0)