ordigi/todo.md

16 KiB
Executable File

NOW

Media:

  • rewrite set_date...

Test:

  • finish filesystem
  • date_taken
  • geolocation

move elodie to dozo

check for early morning photos: add test

add --folder-path option %Y-%d-%m/%city/%album datetime.today().strftime('%Y-%m-%d')

add %filename add edit_exif command?

Add update command

enhancement

  • acccept Path in get_exiftool
  • Use get_exiftool instead of get metadata: try to do it in get_date_taken... media class:
  • Add self.file_path

Album form folder

  • move to filesystem

    TODO implement album from folder here?

    folder = os.path.basename(os.path.dirname(source))

    album = self.metadata['album']

    if album_from_folder and (album is None or album == ''):

    album = folder

Update

use pathlib instead of os.path

Allow update in sort command in same dir if path is the dest dir

ENhancement: swap hash db key value: for checking file integrity https://github.com/JohannesBuchner/imagehash https://github.com/cw-somil/Duplicate-Remover https://leons.im/posts/a-python-implementation-of-simhash-algorithm/

Visualy check similar image https://www.pluralsight.com/guides/importing-image-data-into-numpy-arrays https://stackoverflow.com/questions/56056054/add-check-boxes-to-scrollable-image-in-python https://wellsr.com/python/python-image-manipulation-with-pillow-library/ kitty gird image? https://fr.wikibooks.org/wiki/PyQt/PyQt_versus_wxPython https://docs.python.org/3/faq/gui.html https://docs.opencv.org/3.4/d3/df2/tutorial_py_basic_ops.html https://stackoverflow.com/questions/52727332/python-tkinter-create-checkbox-list-from-listbox

Image gird method: matplot https://gist.github.com/lebedov/7018889ba47668c64bcf96aee82caec0

Tkinter https://python-forum.io/thread-22700.html https://stackoverflow.com/questions/43326282/how-can-i-use-images-in-a-tkinter-grid

wxwidget https://wxpython.org/Phoenix/docs/html/wx.lib.agw.thumbnailctrl.html

Ability to change metadata to selection

Enhancement: Option to keep existing directory structure

Fix: change versvalidion number to 0.x99 Fix: README

Refactoring: elodie update: update metadata of destination

Fix: update: fix move files...

Refactoring: Move exiftool config

Checksum: FIX: test if checksum remain the same for all files (global check) FIX: if dest file already here and checksum d'ont match change name to prevent overwriting to file with same dest path

Enhancement: media file, do not filter files, only to prevent error when copying fix: Valid file: check for open file error

Enhancement: Add %base_name string key

Refactoring: class get_metadata check if as exiF, check exif type...

Interface: show error and warning interface: less verbose when no error interface: Move default setting to config?

Behavior: Move only by defaut without changing metatdata and filename...

Refactoring: check one time media is valid? Refactoring: Unify source and path Enhancement: allow nested dir Fix: check exclusion for file Refactoring: Import perl as submodule?

Enhancement: # setup arguments to exiftool https://github.com/andrewning/sortphotos/blob/master/src/sortphotos.py

AFTER

Enhancement: add walklevel function Enhancement: change early morning date sort

TODO

Fix: date, make correction in filename if needed Check: date from filename Options: --update-cache|-u --date-from-filename --location --time

--date from folder

--date from file

-f overwrite metadata

Add get tag function Add --copy alternative --auto|-a: a set of option: geolocalisation, best match date, rename, album from folder... defaut: only move

--keep-folder option

--rename

-- no cache mode!! --confirm unsure operation --interactive

TEST

# lat='45.58339'
# lon='4.79823'
    # coordinates ='53.480837, -2.244914'
    # Alger
    # coords=(36.752887, 3.042048)

https://www.gitmemory.com/issue/pallets/click/843/634305917 https://github.com/pallets/click/issues/843

# import unittest
# import pytest

# from thing.__main__ import cli


# class TestCli(unittest.TestCase):

#     @pytest.fixture(autouse=True)
#     def capsys(self, capsys):
#         self.capsys = capsys

#     def test_cli(self):
#         with pytest.raises(SystemExit) as ex:
#             cli(["create", "--name", "test"])
#         self.assertEqual(ex.value.code, 0)
#         out, err = self.capsys.readouterr()
#         self.assertEqual(out, "Succesfully created test\n")

dev

mode ~/.elodie ~/.config/elodie

location selection buggy

TODO:

/home/cedric/src/elodie/elodie/media/photo.py(86)get_date_taken()

85 # TODO potential bu for old photo below 1970...

---> 86 if(seconds_since_epoch == 0):

87 return None

import os

def walklevel(some_dir, level=1): some_dir = some_dir.rstrip(os.path.sep) assert os.path.isdir(some_dir) num_sep = some_dir.count(os.path.sep) for root, dirs, files in os.walk(some_dir): yield root, dirs, files num_sep_this = root.count(os.path.sep) if num_sep + level <= num_sep_this: del dirs[:] 49/2: y=walklevel('/home/cedric', level=1) 49/3: next(y) 49/4: next(y) 49/5: next(y) 49/6: next(y) 49/7: next(y) 49/8: y=walklevel('/home/cedric', level=0) 49/9: next(y) 49/10: next(y) 49/11: y=walklevel('/home/cedric/.test/Nexcloud/', level=0) 49/12: import os

def walklevel(some_dir, level=1): some_dir = some_dir.rstrip(os.path.sep) assert os.path.isdir(some_dir) num_sep = some_dir.count(os.path.sep) for root, dirs, files in os.walk(some_dir): yield root, dirs, files num_sep_this = root.count(os.path.sep) if num_sep + level <= num_sep_this: print dirs, files 49/13: import os

def walklevel(some_dir, level=1): some_dir = some_dir.rstrip(os.path.sep) assert os.path.isdir(some_dir) num_sep = some_dir.count(os.path.sep) for root, dirs, files in os.walk(some_dir): yield root, dirs, files num_sep_this = root.count(os.path.sep) if num_sep + level <= num_sep_this: print(dirs, files) 49/14: y=walklevel('/home/cedric/.test/Nexcloud/', level=0) 49/15: next(y) 49/16: next(y) 49/17: y=walklevel('/home/cedric/.test/Nexcloud/', level=0) 49/18: import os

def walklevel(some_dir, level=1): some_dir = some_dir.rstrip(os.path.sep) assert os.path.isdir(some_dir) num_sep = some_dir.count(os.path.sep) for root, dirs, files in os.walk(some_dir): yield root, dirs, files num_sep_this = root.count(os.path.sep) 49/19: y=walklevel('/home/cedric/.test/Nexcloud/', level=0) 49/20: next(y) 49/21: next(y) 49/22: y=walklevel('/home/cedric/.test/Nexcloud/', level=2) 49/23: next(y) 49/24: next(y) 49/25: y=walklevel('/home/cedric/.test/las canarias 2012/', level=2) 49/26: next(y) 49/27: next(y) 49/28: next(y) 49/29: next(y) 49/30: y=walklevel('/home/cedric/.test/las canarias 2012/', level=0) 49/31: next(y) 49/32: next(y) 49/33: next(y) 49/34: import os

def walklevel(some_dir, level=1): some_dir = some_dir.rstrip(os.path.sep) assert os.path.isdir(some_dir) num_sep = some_dir.count(os.path.sep) for root, dirs, files in os.walk(some_dir): yield root, dirs, files num_sep_this = root.count(os.path.sep) if num_sep + level <= num_sep_this: print('fuck') 49/35: y=walklevel('/home/cedric/.test/las canarias 2012/', level=0) 49/36: next(y) 49/37: next(y) 49/38: next(y) 64/1: a=os.walk('/home/cedric/.test/las canarias 2012') 64/2: import os 64/3: a=os.walk('/home/cedric/.test/las canarias 2012') 64/4: next(a) 64/5: next(a) 64/6: os.path.sep 64/7: os.path.relpath('/home/cedric/.test/las canarias 2012/private', 'private') 64/8: os.path.relpath('/home/cedric/.test/las canarias 2012', 'private') 64/9: os.path.relpath('/home/cedric/.test/las canarias 2012/private', '/home/cedric/.test/las canarias 2012') 64/10: b='test' 64/11: a='private' 64/12: a+b 64/13: os.path.join(a,b,b) 64/14: !True 64/15: not True 64/16: a=TRue 64/17: a=True 64/18: not a 77/1: import os import requests

def get_location(geotags): coords = get_coordinates(geotags)

uri = 'https://revgeocode.search.hereapi.com/v1/revgeocode'
headers = {}
params = {
    'apiKey': os.environ['API_KEY'],
    'at': "%s,%s" % coords,
    'lang': 'en-US',
    'limit': 1,
}

response = requests.get(uri, headers=headers, params=params)
try:
    response.raise_for_status()
    return response.json()

except requests.exceptions.HTTPError as e:
    print(str(e))
    return {}

77/2: cd ~/.test/ 77/3: ls 77/4: cd 2021-02-Feb/ 77/5: ls 77/6: cd Villeurbanne/ 77/7: ls 77/8: ls -l 77/9: exif = get_exif('2021-02-24_09-33-29-20210305_081001_01.mp4') 77/10: from PIL import Image

def get_exif(filename): image = Image.open(filename) image.verify() return image._getexif() 77/11: exif = get_exif('2021-02-24_09-33-29-20210305_081001_01.mp4') 77/12: .. 77/13: cd .. 77/14: ls 77/15: cd .. 77/16: ls 77/17: cd 2021-03-Mar/ 77/18: cd Villeurbanne/ 77/19: ls 77/20: exif = get_exif('2021-03-09_09-58-42-img_20210309_105842.jpg') 77/21: exif 77/22: def get_geotagging(exif): if not exif: raise ValueError("No EXIF metadata found")

geotagging = {}
for (idx, tag) in TAGS.items():
    if tag == 'GPSInfo':
        if idx not in exif:
            raise ValueError("No EXIF geotagging found")

        for (key, val) in GPSTAGS.items():
            if key in exif[idx]:
                geotagging[val] = exif[idx][key]

return geotagging

77/23: get_geotagging(exif) 77/24: from PIL.ExifTags import TAGS 77/25: def get_labeled_exif(exif): labeled = {} for (key, val) in exif.items(): labeled[TAGS.get(key)] = val

return labeled

77/26: get_geotagging(exif) 77/27: from PIL.ExifTags import GPSTAGS 77/28: get_geotagging(exif) 77/29: geotags = get_geotagging(exif) 77/30: get_location(geotags) 77/31: def get_decimal_from_dms(dms, ref):

degrees = dms[0][0] / dms[0][1]
minutes = dms[1][0] / dms[1][1] / 60.0
seconds = dms[2][0] / dms[2][1] / 3600.0

if ref in ['S', 'W']:
    degrees = -degrees
    minutes = -minutes
    seconds = -seconds

return round(degrees + minutes + seconds, 5)

def get_coordinates(geotags): lat = get_decimal_from_dms(geotags['GPSLatitude'], geotags['GPSLatitudeRef'])

lon = get_decimal_from_dms(geotags['GPSLongitude'], geotags['GPSLongitudeRef'])

return (lat,lon)

77/32: get_geotagging(exif) 77/33: get_location(geotags) 77/34: from geopy.geocoders import Here 78/1: from geopy.geocoders import Here 78/3: 78/4: get_exif 78/5: ls 78/6: cd ~/.test 78/7: ls 78/8: cd 2021-03-Mar/ 78/9: ls 78/10: cd Villeurbanne/ 78/11: get_exif('2021-03-04_11-50-32-img_20210304_125032.jpg') 78/12: exif=get_exif('2021-03-04_11-50-32-img_20210304_125032.jpg') 78/13: get_geotagging(exif) 78/14: from PIL.ExifTags import GPSTAGS

def get_geotagging(exif): if not exif: raise ValueError("No EXIF metadata found")

geotagging = {}
for (idx, tag) in TAGS.items():
    if tag == 'GPSInfo':
        if idx not in exif:
            raise ValueError("No EXIF geotagging found")

        for (key, val) in GPSTAGS.items():
            if key in exif[idx]:
                geotagging[val] = exif[idx][key]

return geotagging

78/15: geotags = get_geotagging(exif) 78/17: geotags = get_geotagging(exif) 78/18: get_coordinates(geotags) 78/19:

78/23: get_location(geotags) 78/24: 78/25: get_location(geotags) 78/26: def get_decimal_from_dms(dms, ref):

degrees = dms[0][0] / dms[0][1]
minutes = dms[1][0] / dms[1][1] / 60.0
seconds = dms[2][0] / dms[2][1] / 3600.0

if ref in ['S', 'W']:
    degrees = -degrees
    minutes = -minutes
    seconds = -seconds

return round(degrees + minutes + seconds, 5)

78/27: get_location(geotags) 78/28: def get_decimal_from_dms(dms, ref):

degrees = dms[0]
minutes = dms[1] / 60.0
seconds = dms[2] / 3600.0

if ref in ['S', 'W']:
    degrees = -degrees
    minutes = -minutes
    seconds = -seconds

return round(degrees + minutes + seconds, 5)

78/29: get_location(geotags) 78/30: exif 78/31: get_geotagging(exif) 78/32: geotags = get_geotagging(exif) 78/33: get_coordinates(geotags) 78/34: geotags = get_geotagging(exif) 78/35: get_location(geotags) 78/36: get_coordinates(geotags) 78/37: coords = get_coordinates(geotags) 78/38: coords 78/39: uri = 'https://revgeocode.search.hereapi.com/v1/revgeocode' 78/40: headers = {} params = { 'apiKey': os.environ['API_KEY'], 'at': "%s,%s" % coords, 'lang': 'en-US', 'limit': 1, } 78/41: headers = {} 78/42: params = { 'apiKey': os.environ['API_KEY'], 'at': "%s,%s" % coords, 'lang': 'en-US', 'limit': 1, } 78/43: params = { 'apiKey': os.environ['API_KEY'], 'at': "%s,%s" % coords, 'lang': 'en-US', 'limit': 1, } 78/44: API_KEY=m5aGo8xGe4LLhxeKZYpHr2MPXGN2aDhe 78/45: API_KEY='m5aGo8xGe4LLhxeKZYpHr2MPXGN2aDhe' 78/46: params = { 'apiKey': os.environ['API_KEY'], 'at': "%s,%s" % coords, 'lang': 'en-US', 'limit': 1, } 78/47: API_KEY='m5aGo8xGe4LLhxeKZYpHr2MPXGN2aDhe' 78/48: params = { 'apiKey': os.environ['API_KEY'], 'at': "%s,%s" % coords, 'lang': 'en-US', 'limit': 1, } 78/49: params = { 'apiKey': os.environ['m5aGo8xGe4LLhxeKZYpHr2MPXGN2aDhe'], 'at': "%s,%s" % coords, 'lang': 'en-US', 'limit': 1, } 78/50: %load_ext autotime 78/51: import pandas as pd import geopandas as gpd import geopy from geopy.geocoders import Nominatim from geopy.extra.rate_limiter import RateLimiterimport matplotlib.pyplot as plt import plotly_express as pximport tqdm from tqdm._tqdm_notebook import tqdm_notebook 78/52: import pandas as pd import geopandas as gpd import geopy from geopy.geocoders import Nominatim from geopy.extra.rate_limiter import RateLimiterimport matplotlib.pyplot as plt import plotly_express as px import pandas as pd import geopandas as gpd

from PIL import Image

filename='2021-02-24_09-33-29-20210305_081001_01.mp4' def get_exif(filename): image = Image.open(filename) image.verify() return image._getexif() exif=get_exif(filename)

from PIL.ExifTags import TAGS from PIL.ExifTags import GPSTAGS def get_geotagging(exif): if not exif: raise ValueError("No EXIF metadata found")

geotagging = {}
for (idx, tag) in TAGS.items():
    if tag == 'GPSInfo':
        if idx not in exif:
            raise ValueError("No EXIF geotagging found")

        for (key, val) in GPSTAGS.items():
            if key in exif[idx]:
                geotagging[val] = exif[idx][key]

return geotagging

geotags = get_geotagging(exif) import os import requests

def get_location(geotags): coords = get_coordinates(geotags)

uri = 'https://revgeocode.search.hereapi.com/v1/revgeocode'
headers = {}
params = {
    'apiKey': os.environ['API_KEY'],
    'at': "%s,%s" % coords,
    'lang': 'en-US',
    'limit': 1,
}

response = requests.get(uri, headers=headers, params=params)
try:
    response.raise_for_status()
    return response.json()

except requests.exceptions.HTTPError as e:
    print(str(e))
    return {}

def get_coordinates(geotags): lat = get_decimal_from_dms(geotags['GPSLatitude'], geotags['GPSLatitudeRef'])

lon = get_decimal_from_dms(geotags['GPSLongitude'], geotags['GPSLongitudeRef'])

return (lat,lon)

coords = get_coordinates(geotags) import geopy from geopy.geocoders import Nominatim locator = Nominatim(user_agent='myGeocoder')

coordinates ='53.480837, -2.244914'

lat='45.58339' lon='4.79823' coords = lat + ',' + lon locator.reverse(coords) location =locator.reverse(coords) location.address.split(',') city=location.address.split(',')[1].strip() country=location.address.split(',')[7].strip() location.raw rint country=location.raw['address']['country'] city=location.raw['address']['village']