feat(media meta): reverse geocoding from mapbox (#2922)

This commit is contained in:
Aaron Liu 2025-09-26 11:27:43 +08:00
parent 30290d774f
commit fc7791cde1
31 changed files with 416 additions and 15 deletions

View File

@ -287,6 +287,13 @@
"exposureValue": "{{num}} Sekunden", "exposureValue": "{{num}} Sekunden",
"exposure": "Belichtung", "exposure": "Belichtung",
"aperture": "Blende", "aperture": "Blende",
"address": "Adresse",
"street": "Straße",
"locality": "Ort",
"place": "Stadt",
"district": "Bezirk",
"region": "Provinz",
"country": "Land",
"mediaInfo": "Medieninformationen", "mediaInfo": "Medieninformationen",
"details": "Details", "details": "Details",
"activity": "Aktivität", "activity": "Aktivität",

View File

@ -167,6 +167,11 @@
"exifBruteForceDes": "Wenn aktiviert, wird die gesamte Datei gescannt, um EXIF-Daten zu finden, falls sie nicht am Standard-Header-Standort gefunden werden können. Dies kann die Verarbeitungszeit erhöhen, aber EXIF-Daten an nicht-standardmäßigen Standorten finden.", "exifBruteForceDes": "Wenn aktiviert, wird die gesamte Datei gescannt, um EXIF-Daten zu finden, falls sie nicht am Standard-Header-Standort gefunden werden können. Dies kann die Verarbeitungszeit erhöhen, aber EXIF-Daten an nicht-standardmäßigen Standorten finden.",
"musicCover": "Musik-Cover", "musicCover": "Musik-Cover",
"musicCoverDes": "Album-Cover aus Musikdateien extrahieren, unterstützt ID3 (v1, 2.2, 2.3 und 2.4) Container. Dieser Generator hängt von einem anderen Bild-Miniaturansichten-Generator ab (Cloudreve eingebaut oder VIPS).", "musicCoverDes": "Album-Cover aus Musikdateien extrahieren, unterstützt ID3 (v1, 2.2, 2.3 und 2.4) Container. Dieser Generator hängt von einem anderen Bild-Miniaturansichten-Generator ab (Cloudreve eingebaut oder VIPS).",
"geocoding": "Geokodierung",
"geocodingDes": "Adressinformationen mit dem Mapbox-Dienst basierend auf den in den Medien-EXIF aufgezeichneten Koordinateninformationen abrufen.",
"mapboxAK": "Mapbox API-Schlüssel",
"mapboxAKDes": "API-Schlüssel, der in der Mapbox-Konsole erstellt wurde.",
"geocodingDependencyWarning": "Der Geokodierungs-Generator hängt vom EXIF-Generator ab, bitte aktivieren Sie den EXIF-Generator.",
"notAppliedToNativeGenerator": "{{prefix}}Nicht anwendbar auf nativen Generator von Speicherrichtlinien.", "notAppliedToNativeGenerator": "{{prefix}}Nicht anwendbar auf nativen Generator von Speicherrichtlinien.",
"notAppliedToOneDriveNativeGenerator": "{{prefix}}Nicht anwendbar auf nativen Generator von OneDrive oder SharePoint Speicherrichtlinien.", "notAppliedToOneDriveNativeGenerator": "{{prefix}}Nicht anwendbar auf nativen Generator von OneDrive oder SharePoint Speicherrichtlinien.",
"fileBlobMargin": "Datei-Blob-URL-Cache-Marge (Sekunden)", "fileBlobMargin": "Datei-Blob-URL-Cache-Marge (Sekunden)",

View File

@ -253,6 +253,13 @@
"exposureValue": "{{num}} s", "exposureValue": "{{num}} s",
"exposure": "Exposure", "exposure": "Exposure",
"aperture": "Aperture", "aperture": "Aperture",
"address": "Address",
"street": "Street",
"locality": "Locality",
"place": "City",
"district": "District",
"region": "Province",
"country": "Country",
"mediaInfo": "Media info", "mediaInfo": "Media info",
"details": "Details", "details": "Details",
"activity": "Activity", "activity": "Activity",

View File

@ -166,6 +166,11 @@
"exifBruteForceDes": "When enabled, the entire file will be scanned to find EXIF data if it cannot be found in the standard header location. This may increase processing time but can find EXIF data in non-standard locations.", "exifBruteForceDes": "When enabled, the entire file will be scanned to find EXIF data if it cannot be found in the standard header location. This may increase processing time but can find EXIF data in non-standard locations.",
"musicCover": "Music cover", "musicCover": "Music cover",
"musicCoverDes": "Extract album cover from music files, supports ID3 (v1, 2.2, 2.3 and 2.4) container. This generator depends on any other image thumbnail generator (Cloudreve built-in or VIPS).", "musicCoverDes": "Extract album cover from music files, supports ID3 (v1, 2.2, 2.3 and 2.4) container. This generator depends on any other image thumbnail generator (Cloudreve built-in or VIPS).",
"geocoding": "Geocoding",
"geocodingDes": "Get address information using Mapbox service based on coordinate information recorded in media EXIF.",
"mapboxAK": "Mapbox API Key",
"mapboxAKDes": "API key created in Mapbox console.",
"geocodingDependencyWarning": "Geocoding generator depends on EXIF generator, please enable EXIF generator.",
"notAppliedToNativeGenerator": "{{prefix}}Not applicable to native generator of storage policies.", "notAppliedToNativeGenerator": "{{prefix}}Not applicable to native generator of storage policies.",
"notAppliedToOneDriveNativeGenerator": "{{prefix}}Not applicable to native generator of OneDrive or SharePoint storage policies.", "notAppliedToOneDriveNativeGenerator": "{{prefix}}Not applicable to native generator of OneDrive or SharePoint storage policies.",
"fileBlobMargin": "File Blob URL Cache Margin (seconds)", "fileBlobMargin": "File Blob URL Cache Margin (seconds)",

View File

@ -287,6 +287,13 @@
"exposureValue": "{{num}} s", "exposureValue": "{{num}} s",
"exposure": "Exposición", "exposure": "Exposición",
"aperture": "Apertura", "aperture": "Apertura",
"address": "Dirección",
"street": "Calle",
"locality": "Localidad",
"place": "Ciudad",
"district": "Distrito",
"region": "Provincia",
"country": "País",
"mediaInfo": "Información multimedia", "mediaInfo": "Información multimedia",
"details": "Detalles", "details": "Detalles",
"activity": "Actividad", "activity": "Actividad",

View File

@ -167,6 +167,11 @@
"exifBruteForceDes": "Cuando está habilitado, todo el archivo será escaneado para encontrar datos EXIF si no puede ser encontrado en la ubicación estándar del encabezado. Esto puede aumentar el tiempo de procesamiento pero puede encontrar datos EXIF en ubicaciones no estándar.", "exifBruteForceDes": "Cuando está habilitado, todo el archivo será escaneado para encontrar datos EXIF si no puede ser encontrado en la ubicación estándar del encabezado. Esto puede aumentar el tiempo de procesamiento pero puede encontrar datos EXIF en ubicaciones no estándar.",
"musicCover": "Portada de música", "musicCover": "Portada de música",
"musicCoverDes": "Extraer portada de álbum de archivos de música, soporta contenedor ID3 (v1, 2.2, 2.3 y 2.4). Este generador depende de cualquier otro generador de miniaturas de imagen (Cloudreve integrado o VIPS).", "musicCoverDes": "Extraer portada de álbum de archivos de música, soporta contenedor ID3 (v1, 2.2, 2.3 y 2.4). Este generador depende de cualquier otro generador de miniaturas de imagen (Cloudreve integrado o VIPS).",
"geocoding": "Geocodificación",
"geocodingDes": "Obtener información de dirección usando el servicio Mapbox basado en la información de coordenadas registrada en EXIF de medios.",
"mapboxAK": "Clave API de Mapbox",
"mapboxAKDes": "Clave API creada en la consola de Mapbox.",
"geocodingDependencyWarning": "El generador de geocodificación depende del generador EXIF, por favor habilite el generador EXIF.",
"notAppliedToNativeGenerator": "{{prefix}}No aplicable al generador nativo de políticas de almacenamiento.", "notAppliedToNativeGenerator": "{{prefix}}No aplicable al generador nativo de políticas de almacenamiento.",
"notAppliedToOneDriveNativeGenerator": "{{prefix}}No aplicable al generador nativo de políticas de almacenamiento OneDrive o SharePoint.", "notAppliedToOneDriveNativeGenerator": "{{prefix}}No aplicable al generador nativo de políticas de almacenamiento OneDrive o SharePoint.",
"fileBlobMargin": "Margen de Cache de URL de Blob de Archivo (segundos)", "fileBlobMargin": "Margen de Cache de URL de Blob de Archivo (segundos)",

View File

@ -290,6 +290,13 @@
"exposureValue": "{{num}} s", "exposureValue": "{{num}} s",
"exposure": "Exposition", "exposure": "Exposition",
"aperture": "Ouverture", "aperture": "Ouverture",
"address": "Adresse",
"street": "Rue",
"locality": "Localité",
"place": "Ville",
"district": "District",
"region": "Province",
"country": "Pays",
"mediaInfo": "Informations média", "mediaInfo": "Informations média",
"details": "Détails", "details": "Détails",
"activity": "Activité", "activity": "Activité",

View File

@ -167,6 +167,11 @@
"exifBruteForceDes": "Lorsqu'activé, l'ensemble du fichier sera scanné pour trouver les données EXIF si elles ne peuvent pas être trouvées dans l'emplacement d'en-tête standard. Cela peut augmenter le temps de traitement mais peut trouver des données EXIF dans des emplacements non standard.", "exifBruteForceDes": "Lorsqu'activé, l'ensemble du fichier sera scanné pour trouver les données EXIF si elles ne peuvent pas être trouvées dans l'emplacement d'en-tête standard. Cela peut augmenter le temps de traitement mais peut trouver des données EXIF dans des emplacements non standard.",
"musicCover": "Pochette musicale", "musicCover": "Pochette musicale",
"musicCoverDes": "Extraire la pochette d'album des fichiers musicaux, prend en charge le conteneur ID3 (v1, 2.2, 2.3 et 2.4). Ce générateur dépend de tout autre générateur de miniatures d'images (intégré à Cloudreve ou VIPS).", "musicCoverDes": "Extraire la pochette d'album des fichiers musicaux, prend en charge le conteneur ID3 (v1, 2.2, 2.3 et 2.4). Ce générateur dépend de tout autre générateur de miniatures d'images (intégré à Cloudreve ou VIPS).",
"geocoding": "Géocodage",
"geocodingDes": "Obtenez des informations d'adresse en utilisant le service Mapbox basé sur les informations de coordonnées enregistrées dans l'EXIF des médias.",
"mapboxAK": "Clé API Mapbox",
"mapboxAKDes": "Clé API créée dans la console Mapbox.",
"geocodingDependencyWarning": "Le générateur de géocodage dépend du générateur EXIF, veuillez activer le générateur EXIF.",
"notAppliedToNativeGenerator": "{{prefix}}Non applicable au générateur natif des politiques de stockage.", "notAppliedToNativeGenerator": "{{prefix}}Non applicable au générateur natif des politiques de stockage.",
"notAppliedToOneDriveNativeGenerator": "{{prefix}}Non applicable au générateur natif des politiques de stockage OneDrive ou SharePoint.", "notAppliedToOneDriveNativeGenerator": "{{prefix}}Non applicable au générateur natif des politiques de stockage OneDrive ou SharePoint.",
"fileBlobMargin": "Marge du cache d'URL Blob de fichier (secondes)", "fileBlobMargin": "Marge du cache d'URL Blob de fichier (secondes)",

View File

@ -290,6 +290,13 @@
"exposureValue": "{{num}} s", "exposureValue": "{{num}} s",
"exposure": "Esposizione", "exposure": "Esposizione",
"aperture": "Apertura", "aperture": "Apertura",
"address": "Indirizzo",
"street": "Via",
"locality": "Località",
"place": "Città",
"district": "Distretto",
"region": "Provincia",
"country": "Paese",
"mediaInfo": "Info media", "mediaInfo": "Info media",
"details": "Dettagli", "details": "Dettagli",
"activity": "Attività", "activity": "Attività",

View File

@ -167,6 +167,11 @@
"exifBruteForceDes": "Quando abilitato, l'intero file verrà scansionato per trovare dati EXIF se non possono essere trovati nella posizione header standard. Questo potrebbe aumentare il tempo di elaborazione ma può trovare dati EXIF in posizioni non standard.", "exifBruteForceDes": "Quando abilitato, l'intero file verrà scansionato per trovare dati EXIF se non possono essere trovati nella posizione header standard. Questo potrebbe aumentare il tempo di elaborazione ma può trovare dati EXIF in posizioni non standard.",
"musicCover": "Copertina musicale", "musicCover": "Copertina musicale",
"musicCoverDes": "Estrai copertina album dai file musicali, supporta container ID3 (v1, 2.2, 2.3 e 2.4). Questo generatore dipende da qualsiasi altro generatore di miniature immagini (Cloudreve integrato o VIPS).", "musicCoverDes": "Estrai copertina album dai file musicali, supporta container ID3 (v1, 2.2, 2.3 e 2.4). Questo generatore dipende da qualsiasi altro generatore di miniature immagini (Cloudreve integrato o VIPS).",
"geocoding": "Geocodifica",
"geocodingDes": "Ottieni informazioni sull'indirizzo utilizzando il servizio Mapbox basato sulle informazioni delle coordinate registrate nell'EXIF dei media.",
"mapboxAK": "Chiave API Mapbox",
"mapboxAKDes": "Chiave API creata nella console Mapbox.",
"geocodingDependencyWarning": "Il generatore di geocodifica dipende dal generatore EXIF, si prega di abilitare il generatore EXIF.",
"notAppliedToNativeGenerator": "{{prefix}}Non applicabile al generatore nativo delle policy di archiviazione.", "notAppliedToNativeGenerator": "{{prefix}}Non applicabile al generatore nativo delle policy di archiviazione.",
"notAppliedToOneDriveNativeGenerator": "{{prefix}}Non applicabile al generatore nativo delle policy di archiviazione OneDrive o SharePoint.", "notAppliedToOneDriveNativeGenerator": "{{prefix}}Non applicabile al generatore nativo delle policy di archiviazione OneDrive o SharePoint.",
"fileBlobMargin": "Margine Cache URL Blob File (secondi)", "fileBlobMargin": "Margine Cache URL Blob File (secondi)",

View File

@ -256,6 +256,13 @@
"exposureValue": "{{num}} 秒", "exposureValue": "{{num}} 秒",
"exposure": "露出", "exposure": "露出",
"aperture": "絞り", "aperture": "絞り",
"address": "住所",
"street": "通り",
"locality": "地区",
"place": "都市",
"district": "区",
"region": "省",
"country": "国",
"mediaInfo": "メディア情報", "mediaInfo": "メディア情報",
"details": "詳細", "details": "詳細",
"activity": "アクティビティ", "activity": "アクティビティ",

View File

@ -166,6 +166,11 @@
"exifBruteForceDes": "有効にすると、標準ヘッダーの位置でEXIFデータが見つからない場合、EXIFデータを見つけるためにファイル全体をスキャンします。処理時間が長くなる可能性がありますが、非標準の位置にあるEXIFデータを見つけることができます。", "exifBruteForceDes": "有効にすると、標準ヘッダーの位置でEXIFデータが見つからない場合、EXIFデータを見つけるためにファイル全体をスキャンします。処理時間が長くなる可能性がありますが、非標準の位置にあるEXIFデータを見つけることができます。",
"musicCover": "曲のジャケット画像", "musicCover": "曲のジャケット画像",
"musicCoverDes": "オーディオファイルからアルバムジャケット画像を抽出します。ID3v1、2.2、2.3、2.4メタデータコンテナをサポートします。このジェネレーターは、他の画像ジェネレーターCloudreve組み込みまたはVIPSに依存します。", "musicCoverDes": "オーディオファイルからアルバムジャケット画像を抽出します。ID3v1、2.2、2.3、2.4メタデータコンテナをサポートします。このジェネレーターは、他の画像ジェネレーターCloudreve組み込みまたはVIPSに依存します。",
"geocoding": "ジオコーディング",
"geocodingDes": "メディアのEXIFに記録された座標情報に基づいて、Mapboxサービスを使用して住所情報を取得します。",
"mapboxAK": "Mapbox APIキー",
"mapboxAKDes": "Mapboxコンソールで作成されたAPIキー。",
"geocodingDependencyWarning": "ジオコーディングジェネレーターはEXIFジェネレーターに依存しています。EXIFジェネレーターを有効にしてください。",
"notAppliedToNativeGenerator": "{{prefix}}はストレージポリシーネイティブジェネレーターには適用されません。", "notAppliedToNativeGenerator": "{{prefix}}はストレージポリシーネイティブジェネレーターには適用されません。",
"notAppliedToOneDriveNativeGenerator": "{{prefix}}はOneDriveまたはSharePointストレージポリシーネイティブジェネレーターには適用されません。", "notAppliedToOneDriveNativeGenerator": "{{prefix}}はOneDriveまたはSharePointストレージポリシーネイティブジェネレーターには適用されません。",
"fileBlobMargin": "ファイルBlob一時URLキャッシュ冗長性", "fileBlobMargin": "ファイルBlob一時URLキャッシュ冗長性",

View File

@ -290,6 +290,13 @@
"exposureValue": "{{num}}초", "exposureValue": "{{num}}초",
"exposure": "노출", "exposure": "노출",
"aperture": "조리개", "aperture": "조리개",
"address": "주소",
"street": "거리",
"locality": "지역",
"place": "도시",
"district": "구",
"region": "성",
"country": "국가",
"mediaInfo": "미디어 정보", "mediaInfo": "미디어 정보",
"details": "상세정보", "details": "상세정보",
"activity": "활동", "activity": "활동",

View File

@ -166,6 +166,11 @@
"exifBruteForceDes": "활성화하면 표준 헤더 위치에서 EXIF 데이터를 찾을 수 없을 때 전체 파일을 스캔하여 EXIF 데이터를 찾습니다. 처리 시간이 증가할 수 있지만 비표준 위치의 EXIF 데이터를 찾을 수 있습니다.", "exifBruteForceDes": "활성화하면 표준 헤더 위치에서 EXIF 데이터를 찾을 수 없을 때 전체 파일을 스캔하여 EXIF 데이터를 찾습니다. 처리 시간이 증가할 수 있지만 비표준 위치의 EXIF 데이터를 찾을 수 있습니다.",
"musicCover": "앨범 커버", "musicCover": "앨범 커버",
"musicCoverDes": "오디오 파일에서 앨범 커버를 추출하며, ID3 (v1, 2.2, 2.3, 2.4) 메타데이터 컨테이너를 지원합니다. 이 생성기는 다른 이미지 생성기(Cloudreve 내장 또는 VIPS) 중 하나에 의존합니다.", "musicCoverDes": "오디오 파일에서 앨범 커버를 추출하며, ID3 (v1, 2.2, 2.3, 2.4) 메타데이터 컨테이너를 지원합니다. 이 생성기는 다른 이미지 생성기(Cloudreve 내장 또는 VIPS) 중 하나에 의존합니다.",
"geocoding": "지오코딩",
"geocodingDes": "미디어 EXIF에 기록된 좌표 정보를 기반으로 Mapbox 서비스를 사용하여 주소 정보를 가져옵니다.",
"mapboxAK": "Mapbox API 키",
"mapboxAKDes": "Mapbox 콘솔에서 생성된 API 키입니다.",
"geocodingDependencyWarning": "지오코딩 생성기는 EXIF 생성기에 의존합니다. EXIF 생성기를 활성화하십시오.",
"notAppliedToNativeGenerator": "{{prefix}}저장소 정책 네이티브 생성기에는 적용되지 않습니다.", "notAppliedToNativeGenerator": "{{prefix}}저장소 정책 네이티브 생성기에는 적용되지 않습니다.",
"notAppliedToOneDriveNativeGenerator": "{{prefix}}OneDrive 또는 SharePoint 저장소 정책 네이티브 생성기에는 적용되지 않습니다.", "notAppliedToOneDriveNativeGenerator": "{{prefix}}OneDrive 또는 SharePoint 저장소 정책 네이티브 생성기에는 적용되지 않습니다.",
"fileBlobMargin": "파일 Blob 임시 URL 캐시 여유분(초)", "fileBlobMargin": "파일 Blob 임시 URL 캐시 여유분(초)",

View File

@ -290,6 +290,13 @@
"exposureValue": "{{num}} s", "exposureValue": "{{num}} s",
"exposure": "Exposição", "exposure": "Exposição",
"aperture": "Abertura", "aperture": "Abertura",
"address": "Endereço",
"street": "Rua",
"locality": "Localidade",
"place": "Cidade",
"district": "Distrito",
"region": "Província",
"country": "País",
"mediaInfo": "Informações de mídia", "mediaInfo": "Informações de mídia",
"details": "Detalhes", "details": "Detalhes",
"activity": "Atividade", "activity": "Atividade",

View File

@ -167,6 +167,11 @@
"exifBruteForceDes": "Quando habilitado, o arquivo inteiro será escaneado para encontrar dados EXIF se não puder ser encontrado no local padrão do cabeçalho. Isso pode aumentar o tempo de processamento, mas pode encontrar dados EXIF em locais não padrão.", "exifBruteForceDes": "Quando habilitado, o arquivo inteiro será escaneado para encontrar dados EXIF se não puder ser encontrado no local padrão do cabeçalho. Isso pode aumentar o tempo de processamento, mas pode encontrar dados EXIF em locais não padrão.",
"musicCover": "Capa da música", "musicCover": "Capa da música",
"musicCoverDes": "Extrair capa do álbum de arquivos de música, suporta contêiner ID3 (v1, 2.2, 2.3 e 2.4). Este gerador depende de qualquer outro gerador de miniatura de imagem (Cloudreve integrado ou VIPS).", "musicCoverDes": "Extrair capa do álbum de arquivos de música, suporta contêiner ID3 (v1, 2.2, 2.3 e 2.4). Este gerador depende de qualquer outro gerador de miniatura de imagem (Cloudreve integrado ou VIPS).",
"geocoding": "Geocodificação",
"geocodingDes": "Obter informações de endereço usando o serviço Mapbox baseado nas informações de coordenadas registradas no EXIF da mídia.",
"mapboxAK": "Chave API do Mapbox",
"mapboxAKDes": "Chave API criada no console do Mapbox.",
"geocodingDependencyWarning": "O gerador de geocodificação depende do gerador EXIF, por favor habilite o gerador EXIF.",
"notAppliedToNativeGenerator": "{{prefix}}Não aplicável ao gerador nativo de políticas de armazenamento.", "notAppliedToNativeGenerator": "{{prefix}}Não aplicável ao gerador nativo de políticas de armazenamento.",
"notAppliedToOneDriveNativeGenerator": "{{prefix}}Não aplicável ao gerador nativo de políticas de armazenamento OneDrive ou SharePoint.", "notAppliedToOneDriveNativeGenerator": "{{prefix}}Não aplicável ao gerador nativo de políticas de armazenamento OneDrive ou SharePoint.",
"fileBlobMargin": "Margem do Cache de URL do Blob de Arquivo (segundos)", "fileBlobMargin": "Margem do Cache de URL do Blob de Arquivo (segundos)",

View File

@ -290,6 +290,13 @@
"exposureValue": "{{num}} сек", "exposureValue": "{{num}} сек",
"exposure": "Экспозиция", "exposure": "Экспозиция",
"aperture": "Диафрагма", "aperture": "Диафрагма",
"address": "Адрес",
"street": "Улица",
"locality": "Местность",
"place": "Город",
"district": "Район",
"region": "Провинция",
"country": "Страна",
"mediaInfo": "Информация о медиа", "mediaInfo": "Информация о медиа",
"details": "Подробности", "details": "Подробности",
"activity": "Активность", "activity": "Активность",

View File

@ -167,6 +167,11 @@
"exifBruteForceDes": "При включении весь файл будет просканирован для поиска данных EXIF, если они не могут быть найдены в стандартном расположении заголовка. Это может увеличить время обработки, но может найти данные EXIF в нестандартных местах.", "exifBruteForceDes": "При включении весь файл будет просканирован для поиска данных EXIF, если они не могут быть найдены в стандартном расположении заголовка. Это может увеличить время обработки, но может найти данные EXIF в нестандартных местах.",
"musicCover": "Обложка музыки", "musicCover": "Обложка музыки",
"musicCoverDes": "Извлекать обложку альбома из музыкальных файлов, поддерживает контейнер ID3 (v1, 2.2, 2.3 и 2.4). Этот генератор зависит от любого другого генератора миниатюр изображений (встроенного Cloudreve или VIPS).", "musicCoverDes": "Извлекать обложку альбома из музыкальных файлов, поддерживает контейнер ID3 (v1, 2.2, 2.3 и 2.4). Этот генератор зависит от любого другого генератора миниатюр изображений (встроенного Cloudreve или VIPS).",
"geocoding": "Геокодирование",
"geocodingDes": "Получить информацию об адресе с помощью сервиса Mapbox на основе информации о координатах, записанной в EXIF медиафайлов.",
"mapboxAK": "API-ключ Mapbox",
"mapboxAKDes": "API-ключ, созданный в консоли Mapbox.",
"geocodingDependencyWarning": "Генератор геокодирования зависит от генератора EXIF, пожалуйста, включите генератор EXIF.",
"notAppliedToNativeGenerator": "{{prefix}}Не применимо к нативному генератору политик хранения.", "notAppliedToNativeGenerator": "{{prefix}}Не применимо к нативному генератору политик хранения.",
"notAppliedToOneDriveNativeGenerator": "{{prefix}}Не применимо к нативному генератору политик хранения OneDrive или SharePoint.", "notAppliedToOneDriveNativeGenerator": "{{prefix}}Не применимо к нативному генератору политик хранения OneDrive или SharePoint.",
"fileBlobMargin": "Запас кэша URL файлового блоба (секунды)", "fileBlobMargin": "Запас кэша URL файлового блоба (секунды)",

View File

@ -253,6 +253,13 @@
"exposureValue": "{{num}} 秒", "exposureValue": "{{num}} 秒",
"exposure": "曝光", "exposure": "曝光",
"aperture": "光圈", "aperture": "光圈",
"address": "地址",
"street": "街道",
"locality": "城市区",
"place": "城市",
"district": "区",
"region": "省",
"country": "国家",
"mediaInfo": "媒体信息", "mediaInfo": "媒体信息",
"details": "详情", "details": "详情",
"activity": "活动", "activity": "活动",

View File

@ -166,6 +166,11 @@
"exifBruteForceDes": "启用后,如果在标准头部位置找不到 EXIF 数据,将扫描整个文件以查找 EXIF 数据。这可能会增加处理时间,但可以找到非标准位置的 EXIF 数据。", "exifBruteForceDes": "启用后,如果在标准头部位置找不到 EXIF 数据,将扫描整个文件以查找 EXIF 数据。这可能会增加处理时间,但可以找到非标准位置的 EXIF 数据。",
"musicCover": "歌曲封面", "musicCover": "歌曲封面",
"musicCoverDes": "提取音频文件中的专辑封面, 支持 ID3 (v1, 2.2, 2.3, 2.4) 元数据容器。这一生成器依赖于任一其他图像生成器Cloudreve 内置 或 VIPS。", "musicCoverDes": "提取音频文件中的专辑封面, 支持 ID3 (v1, 2.2, 2.3, 2.4) 元数据容器。这一生成器依赖于任一其他图像生成器Cloudreve 内置 或 VIPS。",
"geocoding": "地理编码",
"geocodingDes": "根据媒体 EXIF 中记录的坐标信息,使用 Mapbox 服务获取地址信息。",
"mapboxAK": "Mapbox 密钥",
"mapboxAKDes": "在 Mapbox 控制台创建的密钥。",
"geocodingDependencyWarning": "地理编码生成器依赖于 EXIF 生成器,请开启 EXIF 生成器。",
"notAppliedToNativeGenerator": "{{prefix}}不适用于存储策略原生生成器。", "notAppliedToNativeGenerator": "{{prefix}}不适用于存储策略原生生成器。",
"notAppliedToOneDriveNativeGenerator": "{{prefix}}不适用于 OneDrive 或 SharePoint 存储策略原生生成器。", "notAppliedToOneDriveNativeGenerator": "{{prefix}}不适用于 OneDrive 或 SharePoint 存储策略原生生成器。",
"fileBlobMargin": "文件 Blob 临时 URL 缓存冗余(秒)", "fileBlobMargin": "文件 Blob 临时 URL 缓存冗余(秒)",

View File

@ -256,6 +256,13 @@
"exposureValue": "{{num}} 秒", "exposureValue": "{{num}} 秒",
"exposure": "曝光", "exposure": "曝光",
"aperture": "光圈", "aperture": "光圈",
"address": "地址",
"street": "街道",
"locality": "城市區",
"place": "城市",
"district": "區",
"region": "省",
"country": "國家",
"mediaInfo": "媒體資訊", "mediaInfo": "媒體資訊",
"details": "詳情", "details": "詳情",
"activity": "活動", "activity": "活動",

View File

@ -166,6 +166,11 @@
"exifBruteForceDes": "啟用後,如果在標準頭部位置找不到 EXIF 資料,將掃描整個檔案以查詢 EXIF 資料。這可能會增加處理時間,但可以找到非標準位置的 EXIF 資料。", "exifBruteForceDes": "啟用後,如果在標準頭部位置找不到 EXIF 資料,將掃描整個檔案以查詢 EXIF 資料。這可能會增加處理時間,但可以找到非標準位置的 EXIF 資料。",
"musicCover": "歌曲封面", "musicCover": "歌曲封面",
"musicCoverDes": "提取音訊檔案中的專輯封面, 支援 ID3 (v1, 2.2, 2.3, 2.4) 元資料容器。這一生成器依賴於任一其他影象生成器Cloudreve 內建 或 VIPS。", "musicCoverDes": "提取音訊檔案中的專輯封面, 支援 ID3 (v1, 2.2, 2.3, 2.4) 元資料容器。這一生成器依賴於任一其他影象生成器Cloudreve 內建 或 VIPS。",
"geocoding": "地理編碼",
"geocodingDes": "根據媒體 EXIF 中記錄的座標資訊,使用 Mapbox 服務獲取地址資訊。",
"mapboxAK": "Mapbox 密鑰",
"mapboxAKDes": "在 Mapbox 控制台建立的密鑰。",
"geocodingDependencyWarning": "地理編碼產生器依賴於 EXIF 產生器,請開啟 EXIF 產生器。",
"notAppliedToNativeGenerator": "{{prefix}}不適用於儲存策略原生生成器。", "notAppliedToNativeGenerator": "{{prefix}}不適用於儲存策略原生生成器。",
"notAppliedToOneDriveNativeGenerator": "{{prefix}}不適用於 OneDrive 或 SharePoint 儲存策略原生生成器。", "notAppliedToOneDriveNativeGenerator": "{{prefix}}不適用於 OneDrive 或 SharePoint 儲存策略原生生成器。",
"fileBlobMargin": "檔案 Blob 臨時 URL 快取冗餘(秒)", "fileBlobMargin": "檔案 Blob 臨時 URL 快取冗餘(秒)",

View File

@ -211,6 +211,14 @@ export const Metadata = {
stream_indexed_bitrate: "bitrate", stream_indexed_bitrate: "bitrate",
stream_indexed_width: "width", stream_indexed_width: "width",
stream_indexed_height: "height", stream_indexed_height: "height",
// Geocoding
street: "geocoding:street",
locality: "geocoding:locality",
place: "geocoding:place",
district: "geocoding:district",
region: "geocoding:region",
country: "geocoding:country",
}; };
export interface FileThumbResponse { export interface FileThumbResponse {

View File

@ -76,6 +76,18 @@ const extractors: ExtractorRenderProps[] = [
maxSizeLocalSetting: "media_meta_ffprobe_size_local", maxSizeLocalSetting: "media_meta_ffprobe_size_local",
maxSizeRemoteSetting: "media_meta_ffprobe_size_remote", maxSizeRemoteSetting: "media_meta_ffprobe_size_remote",
}, },
{
name: "geocoding",
des: "geocodingDes",
enableFlag: "media_meta_geocoding",
additionalSettings: [
{
name: "media_meta_geocoding_mapbox_ak",
label: "mapboxAK",
des: "mapboxAKDes",
},
],
},
]; ];
const Extractors = ({ values, setSetting }: ExtractorsProps) => { const Extractors = ({ values, setSetting }: ExtractorsProps) => {
@ -88,6 +100,15 @@ const Extractors = ({ values, setSetting }: ExtractorsProps) => {
setSetting({ setSetting({
[name]: e.target.checked ? "1" : "0", [name]: e.target.checked ? "1" : "0",
}); });
const newValues = { ...values, [name]: e.target.checked ? "1" : "0" };
if (isTrueVal(newValues["media_meta_geocoding"]) && !isTrueVal(newValues["media_meta_exif"])) {
enqueueSnackbar({
message: t("settings.geocodingDependencyWarning"),
variant: "warning",
action: DefaultCloseAction,
});
}
}; };
const doTest = (name: string, executable: string) => { const doTest = (name: string, executable: string) => {
@ -215,7 +236,8 @@ const Extractors = ({ values, setSetting }: ExtractorsProps) => {
/> />
) : ( ) : (
<DenseFilledTextField <DenseFilledTextField
required label={t(`settings.${setting.label}`)}
required={isTrueVal(values[e.enableFlag ?? ""])}
value={values[setting.name]} value={values[setting.name]}
onChange={(ev) => onChange={(ev) =>
setSetting({ setSetting({

View File

@ -265,6 +265,8 @@ const Settings = () => {
"media_meta_ffprobe_path", "media_meta_ffprobe_path",
"media_meta_ffprobe_size_local", "media_meta_ffprobe_size_local",
"media_meta_ffprobe_size_remote", "media_meta_ffprobe_size_remote",
"media_meta_geocoding",
"media_meta_geocoding_mapbox_ak",
]} ]}
> >
<Media /> <Media />

View File

@ -46,6 +46,13 @@ const mediaInfoOptions: (ColumType | null)[] = [
ColumType.artist, ColumType.artist,
ColumType.album, ColumType.album,
ColumType.duration, ColumType.duration,
null,
ColumType.street,
ColumType.locality,
ColumType.place,
ColumType.district,
ColumType.region,
ColumType.country,
]; ];
const AddColumn = (props: AddColumnProps) => { const AddColumn = (props: AddColumnProps) => {

View File

@ -30,6 +30,8 @@ import {
getArtist, getArtist,
getCameraMake, getCameraMake,
getCameraModel, getCameraModel,
getCountry,
getDistrict,
getDuration, getDuration,
getExposure, getExposure,
getExposureBias, getExposureBias,
@ -39,8 +41,12 @@ import {
getIso, getIso,
getLensMake, getLensMake,
getLensModel, getLensModel,
getLocality,
getMediaTitle, getMediaTitle,
getPlace,
getRegion,
getSoftware, getSoftware,
getStreet,
takenAt, takenAt,
} from "../../Sidebar/MediaInfo.tsx"; } from "../../Sidebar/MediaInfo.tsx";
import { MediaMetaElements } from "../../Sidebar/MediaMetaCard.tsx"; import { MediaMetaElements } from "../../Sidebar/MediaMetaCard.tsx";
@ -352,6 +358,18 @@ const Cell = memo((props: CellProps) => {
return <MediaElementsCell element={getAlbum(file)} />; return <MediaElementsCell element={getAlbum(file)} />;
case ColumType.duration: case ColumType.duration:
return <MediaElementsCell element={getDuration(file)} />; return <MediaElementsCell element={getDuration(file)} />;
case ColumType.street:
return <MediaElementsCell element={getStreet(file)} />;
case ColumType.locality:
return <MediaElementsCell element={getLocality(file)} />;
case ColumType.place:
return <MediaElementsCell element={getPlace(file)} />;
case ColumType.district:
return <MediaElementsCell element={getDistrict(file)} />;
case ColumType.region:
return <MediaElementsCell element={getRegion(file)} />;
case ColumType.country:
return <MediaElementsCell element={getCountry(file)} />;
case ColumType.custom_props: case ColumType.custom_props:
if (customProp) { if (customProp) {
return getPropsContent(customProp, () => {}, false, true); return getPropsContent(customProp, () => {}, false, true);

View File

@ -54,6 +54,12 @@ export enum ColumType {
artist = 23, artist = 23,
album = 24, album = 24,
duration = 25, duration = 25,
street = 27,
locality = 28,
place = 29,
district = 30,
region = 31,
country = 32,
// Custom props // Custom props
custom_props = 26, custom_props = 26,
@ -179,6 +185,30 @@ export const ColumnTypeDefaults: { [key: number]: ColumTypeDefaults } = {
title: "application:fileManager.duration", title: "application:fileManager.duration",
width: 100, width: 100,
}, },
[ColumType.street]: {
title: "application:fileManager.street",
width: 100,
},
[ColumType.locality]: {
title: "application:fileManager.locality",
width: 100,
},
[ColumType.place]: {
title: "application:fileManager.place",
width: 100,
},
[ColumType.district]: {
title: "application:fileManager.district",
width: 100,
},
[ColumType.region]: {
title: "application:fileManager.region",
width: 100,
},
[ColumType.country]: {
title: "application:fileManager.country",
width: 100,
},
}; };
export const getColumnTypeDefaults = ( export const getColumnTypeDefaults = (

View File

@ -17,7 +17,7 @@ import Tag from "../../../Icons/Tag.tsx";
import TextBulletListSquareEdit from "../../../Icons/TextBulletListSquareEdit.tsx"; import TextBulletListSquareEdit from "../../../Icons/TextBulletListSquareEdit.tsx";
import TextCaseTitle from "../../../Icons/TextCaseTitle.tsx"; import TextCaseTitle from "../../../Icons/TextCaseTitle.tsx";
import { CascadingSubmenu } from "../../ContextMenu/CascadingMenu.tsx"; import { CascadingSubmenu } from "../../ContextMenu/CascadingMenu.tsx";
import { SquareMenuItem } from "../../ContextMenu/ContextMenu.tsx"; import { DenseDivider, SquareMenuItem } from "../../ContextMenu/ContextMenu.tsx";
import { customPropsMetadataPrefix } from "../../Sidebar/CustomProps/CustomProps.tsx"; import { customPropsMetadataPrefix } from "../../Sidebar/CustomProps/CustomProps.tsx";
import { Condition, ConditionType } from "./ConditionBox.tsx"; import { Condition, ConditionType } from "./ConditionBox.tsx";
@ -77,7 +77,7 @@ const options: ConditionOption[] = [
}, },
]; ];
const mediaMetaOptions: ConditionOption[] = [ const mediaMetaOptions: (ConditionOption | null)[] = [
{ {
name: "application:fileManager.title", name: "application:fileManager.title",
condition: { condition: {
@ -105,6 +105,7 @@ const mediaMetaOptions: ConditionOption[] = [
id: Metadata.music_album, id: Metadata.music_album,
}, },
}, },
null, // divider
{ {
name: "application:fileManager.cameraMake", name: "application:fileManager.cameraMake",
condition: { condition: {
@ -141,6 +142,61 @@ const mediaMetaOptions: ConditionOption[] = [
id: Metadata.lens_model, id: Metadata.lens_model,
}, },
}, },
null, // divider
{
name: "application:fileManager.street",
condition: {
type: ConditionType.metadata,
metadata_key_readonly: true,
metadata_key: Metadata.street,
id: Metadata.street,
},
},
{
name: "application:fileManager.locality",
condition: {
type: ConditionType.metadata,
metadata_key_readonly: true,
metadata_key: Metadata.locality,
id: Metadata.locality,
},
},
{
name: "application:fileManager.place",
condition: {
type: ConditionType.metadata,
metadata_key_readonly: true,
metadata_key: Metadata.place,
id: Metadata.place,
},
},
{
name: "application:fileManager.district",
condition: {
type: ConditionType.metadata,
metadata_key_readonly: true,
metadata_key: Metadata.district,
id: Metadata.district,
},
},
{
name: "application:fileManager.region",
condition: {
type: ConditionType.metadata,
metadata_key_readonly: true,
metadata_key: Metadata.region,
id: Metadata.region,
},
},
{
name: "application:fileManager.country",
condition: {
type: ConditionType.metadata,
metadata_key_readonly: true,
metadata_key: Metadata.country,
id: Metadata.country,
},
},
]; ];
const AddCondition = (props: AddConditionProps) => { const AddCondition = (props: AddConditionProps) => {
@ -186,17 +242,21 @@ const AddCondition = (props: AddConditionProps) => {
popupId={"mediaInfo"} popupId={"mediaInfo"}
title={t("application:fileManager.mediaInfo")} title={t("application:fileManager.mediaInfo")}
> >
{mediaMetaOptions.map((option, index) => ( {mediaMetaOptions.map((option, index) =>
<SquareMenuItem key={index} dense onClick={() => onConditionAdd(option.condition)}> option ? (
<ListItemText <SquareMenuItem key={index} dense onClick={() => onConditionAdd(option.condition)}>
slotProps={{ <ListItemText
primary: { variant: "body2" }, slotProps={{
}} primary: { variant: "body2" },
> }}
{t(option.name)} >
</ListItemText> {t(option.name)}
</SquareMenuItem> </ListItemText>
))} </SquareMenuItem>
) : (
<DenseDivider />
),
)}
</CascadingSubmenu> </CascadingSubmenu>
{customPropsOptions && customPropsOptions.length > 0 && ( {customPropsOptions && customPropsOptions.length > 0 && (
<CascadingSubmenu <CascadingSubmenu

View File

@ -13,6 +13,7 @@ import Copyright from "../../Icons/Copyright.tsx";
import DarkTheme from "../../Icons/DarkTheme.tsx"; import DarkTheme from "../../Icons/DarkTheme.tsx";
import Image from "../../Icons/Image.tsx"; import Image from "../../Icons/Image.tsx";
import InfoFilled from "../../Icons/InfoFilled.tsx"; import InfoFilled from "../../Icons/InfoFilled.tsx";
import LocationFilled from "../../Icons/LocationFilled.tsx";
import MusicNote1 from "../../Icons/MusicNote1.tsx"; import MusicNote1 from "../../Icons/MusicNote1.tsx";
import Notepad from "../../Icons/Notepad.tsx"; import Notepad from "../../Icons/Notepad.tsx";
import Person from "../../Icons/Person.tsx"; import Person from "../../Icons/Person.tsx";
@ -236,6 +237,66 @@ export const getDuration = (target: FileResponse): MediaMetaElements | undefined
} }
}; };
export const getStreet = (target: FileResponse): MediaMetaElements | undefined => {
if (target.metadata?.[Metadata.street]) {
return {
display: target.metadata[Metadata.street],
searchKey: Metadata.street,
searchValue: target.metadata[Metadata.street],
};
}
};
export const getLocality = (target: FileResponse): MediaMetaElements | undefined => {
if (target.metadata?.[Metadata.locality]) {
return {
display: target.metadata[Metadata.locality],
searchKey: Metadata.locality,
searchValue: target.metadata[Metadata.locality],
};
}
};
export const getPlace = (target: FileResponse): MediaMetaElements | undefined => {
if (target.metadata?.[Metadata.place]) {
return {
display: target.metadata[Metadata.place],
searchKey: Metadata.place,
searchValue: target.metadata[Metadata.place],
};
}
};
export const getDistrict = (target: FileResponse): MediaMetaElements | undefined => {
if (target.metadata?.[Metadata.district]) {
return {
display: target.metadata[Metadata.district],
searchKey: Metadata.district,
searchValue: target.metadata[Metadata.district],
};
}
};
export const getRegion = (target: FileResponse): MediaMetaElements | undefined => {
if (target.metadata?.[Metadata.region]) {
return {
display: target.metadata[Metadata.region],
searchKey: Metadata.region,
searchValue: target.metadata[Metadata.region],
};
}
};
export const getCountry = (target: FileResponse): MediaMetaElements | undefined => {
if (target.metadata?.[Metadata.country]) {
return {
display: target.metadata[Metadata.country],
searchKey: Metadata.country,
searchValue: target.metadata[Metadata.country],
};
}
};
const MediaInfo = ({ target }: MediaInfoProps) => { const MediaInfo = ({ target }: MediaInfoProps) => {
if (!target.metadata) { if (!target.metadata) {
return undefined; return undefined;
@ -556,6 +617,56 @@ const MediaInfo = ({ target }: MediaInfoProps) => {
return undefined; return undefined;
}, [target, t]); }, [target, t]);
const gpsAddressContent = useMemo(() => {
let content: (MediaMetaElements | string)[] = [];
// Build address components in hierarchical order (most specific to least specific)
const addressComponents: (MediaMetaElements | undefined)[] = [];
const street = getStreet(target);
if (street) {
addressComponents.push(street);
}
const locality = getLocality(target);
if (locality) {
addressComponents.push(locality);
}
const place = getPlace(target);
if (place) {
addressComponents.push(place);
}
const district = getDistrict(target);
if (district) {
addressComponents.push(district);
}
const region = getRegion(target);
if (region) {
addressComponents.push(region);
}
const country = getCountry(target);
if (country) {
addressComponents.push(country);
}
// Filter out undefined components and join with commas
const validComponents = addressComponents.filter(Boolean) as MediaMetaElements[];
if (validComponents.length > 0) {
// Add the first component
content.push(validComponents[0]);
// Add remaining components with comma separators
for (let i = 1; i < validComponents.length; i++) {
content.push(", ");
content.push(validComponents[i]);
}
return { title: [t("fileManager.address")], content };
}
return undefined;
}, [target, t]);
const showExifBasic = exifContents.length > 0; const showExifBasic = exifContents.length > 0;
const showLightInfo = lightInfoContent.length > 0; const showLightInfo = lightInfoContent.length > 0;
const showCopyRight = !!copyRightContent; const showCopyRight = !!copyRightContent;
@ -573,7 +684,8 @@ const MediaInfo = ({ target }: MediaInfoProps) => {
durationContent || durationContent ||
streamFormatContent || streamFormatContent ||
singleStreamContents || singleStreamContents ||
mapBoxGps; mapBoxGps ||
gpsAddressContent;
if (!showMediaInfo) { if (!showMediaInfo) {
return undefined; return undefined;
@ -592,6 +704,7 @@ const MediaInfo = ({ target }: MediaInfoProps) => {
{showCopyRight && <MediaMetaCard icon={Copyright} contents={[copyRightContent]} />} {showCopyRight && <MediaMetaCard icon={Copyright} contents={[copyRightContent]} />}
{softwareContent && <MediaMetaCard icon={WindowApps} contents={[softwareContent]} />} {softwareContent && <MediaMetaCard icon={WindowApps} contents={[softwareContent]} />}
{mapBoxGps && <MapLoader height={200} center={mapBoxGps} />} {mapBoxGps && <MapLoader height={200} center={mapBoxGps} />}
{gpsAddressContent && <MediaMetaCard icon={LocationFilled} contents={[gpsAddressContent]} />}
{mediaTitleContent && <MediaMetaCard icon={Notepad} contents={[mediaTitleContent]} />} {mediaTitleContent && <MediaMetaCard icon={Notepad} contents={[mediaTitleContent]} />}
{musicArtistContent && <MediaMetaCard icon={Person} contents={[musicArtistContent]} />} {musicArtistContent && <MediaMetaCard icon={Person} contents={[musicArtistContent]} />}
{albumContent && <MediaMetaCard icon={MusicNote1} contents={[albumContent]} />} {albumContent && <MediaMetaCard icon={MusicNote1} contents={[albumContent]} />}

View File

@ -0,0 +1,9 @@
import { SvgIcon, SvgIconProps } from "@mui/material";
export default function LocationFilled(props: SvgIconProps) {
return (
<SvgIcon {...props}>
<path d="M5.843 4.57c3.4-3.401 8.913-3.401 12.314 0s3.4 8.912 0 12.313l-1.187 1.173a657 657 0 0 1-3.406 3.313a2.25 2.25 0 0 1-3.128 0l-3.491-3.397q-.658-.646-1.102-1.09a8.707 8.707 0 0 1 0-12.313M12 8a3 3 0 1 0 0 6a3 3 0 0 0 0-6" />
</SvgIcon>
);
}