Visor de metadata de imágenes para escritorio, escrito en Python con PySide6.
Muestra toda la metadata que un archivo lleva dentro, no solo el EXIF: una fotografía típica de móvil duplica el autor, la fecha y las coordenadas GPS en bloques XMP e IPTC, y la mayoría de visores solo enseñan uno de ellos.
La aplicación nunca modifica archivos. Abre en modo lectura y no escribe.
Requiere Python 3.11 o superior.
pip install -r requirements.txt
python src/main.pydefusedxml no es opcional: sin él, Pillow no puede leer el bloque XMP y lo
omite en silencio.
Arrastra una imagen sobre la ventana, o usa Archivo → Abrir (Ctrl+O).
Formatos aceptados: JPEG, PNG, TIFF y WebP.
La metadata se presenta en dos pestañas:
- Resumen — cuándo, con qué y dónde se tomó la foto. Cada campo se busca en varias fuentes por orden de prioridad, así que el autor aparece aunque solo esté en el XMP. Al pasar el cursor se indica de qué familia salió el valor.
- Todos los tags — la lista completa, agrupada por familia y con un filtro de texto. Una foto de móvil trae más de 150 entradas.
| Familia | Cobertura |
|---|---|
| EXIF (incluidos GPS y miniatura) | completa |
Chunks tEXt de PNG y comentarios JPEG |
completa |
| XMP | propiedades planas; el árbol se aplana al leerlo |
| IPTC | los datasets de uso frecuente, por su nombre |
| MakerNote | solo presencia y tamaño, sin decodificar |
| Perfil ICC | solo presencia y tamaño, sin parsear |
Estas limitaciones vienen del backend: se usa Pillow, que no decodifica el MakerNote por fabricante ni parsea el perfil ICC. Los formatos RAW (CR2, NEF, ARW) no están soportados.
requirements.txt ya incluye pytest y ruff, así que no hace falta
instalar nada más para las pruebas y el linting:
python -m pytest # 87 pruebas
ruff check .Las imágenes de prueba se generan por construcción en tests/conftest.py; no
hay ninguna versionada.
El motor de lectura vive en src/core/ y no importa Qt, así que se puede
probar sin instanciar una ventana:
from core.operations import load_metadata
loaded = load_metadata("foto.jpg")
print(loaded.summary)
for campo in loaded.highlights:
print(f"{campo.field.label}: {campo.value} (vía {campo.source_label})")Este es un proyecto personal y no se aceptan pull requests: están desactivados en el repositorio. Eres libre de hacer un fork y seguir tu propio camino, que para eso está la licencia.
Si encuentras un fallo, puedes abrir un issue.
MIT. Puedes usar el código para lo que quieras; se ofrece sin garantía de ningún tipo.