Project

General

Profile

shotwell2darktable.py

Antoine Beaupré, 06/09/2013 06:58 AM

 
1
#! /usr/bin/python
2

    
3
import os
4
import sqlite3
5
import argparse
6

    
7
parser = argparse.ArgumentParser(description='Add missing metadata to Darktable from Shotwell.')
8
parser.add_argument('--dryrun', '-n', action='store_true')
9
parser.add_argument('--verbose', '-v', action='store_true')
10
parser.add_argument('--debug', '-d', action='store_true')
11
parser.add_argument('--limit', '-l', type=int, default=10)
12

    
13
args = parser.parse_args()
14
if args.dryrun:
15
    print "not doing any changes"
16

    
17
def maybe_print(msg):
18
    if args.verbose:
19
        print msg,
20

    
21
print 'loading shotwell database...'
22
shotwell = sqlite3.connect(os.getenv('HOME', '.') + '/.local/share/shotwell/data/photo.db')
23
shotwell.row_factory = sqlite3.Row
24

    
25
print 'loading darktable database...'
26
darktable = sqlite3.connect(os.getenv('HOME', '.') + '/.config/darktable/library.db')
27
darktable.row_factory = sqlite3.Row
28

    
29
print 'finding images without metadata...'
30
# finding images without tags, excluding darktable-specific ones or the ones i made in the import from shotwell
31
missing = darktable.execute('''
32
SELECT i.id, r.folder || "/" || i.filename AS path, r.folder, i.filename FROM images i
33
    LEFT JOIN tagged_images ti ON ti.imgid = i.id
34
        AND ti.tagid NOT IN 
35
            (SELECT id FROM tags 
36
                WHERE name LIKE 'darktable%' OR 'shotwelll%')
37
    LEFT JOIN film_rolls r ON r.id = i.film_id
38
    WHERE ti.imgid IS NULL
39
    ORDER BY path ASC
40
''')
41

    
42
changed = tags_found = found = total = 0
43

    
44
for image in missing:
45
    total += 1
46
    
47
    c = shotwell.execute('SELECT id, rating, filename FROM PhotoTable p  WHERE p.filename = ?', (image['path'], )).fetchone()
48
    if c is None:
49
        args.debug and maybe_print("thumb0000000000000000\tNaN\tNaN\t\t%s\n" % image['path'])
50
    else:
51
        found += 1
52
        report = "thumb%0.16x\t" % c['id']
53
        report += "%d\t%d\t" % ( c['id'], c['rating'] )
54

    
55
        # Shotwell (from some versions?) stores the photo identifiers
56
        # in a really strange way: it's the thumbnail name, which is
57
        # the hex version of the photo id prefixed with a bunch of
58
        # zeros
59
        #
60
        # example: 4998 => thumb0000000000001386
61
        tags = [t['name'].split('/')[-1] for t in [tag for tag in shotwell.execute('SELECT name FROM TagTable WHERE photo_id_list LIKE ?', ('%%thumb%0.16x%%' % c['id'], ))]]
62
        report += ",".join(tags)
63
        report += "\t%s" % image['path']
64
        if args.debug or len(tags) > 0:
65
            print report
66
        if len(tags) > 0:
67
            tags_found += 1
68
            args.dryrun and maybe_print("would be")
69
            query = "UPDATE images SET flags = ( (flags&4088) + ? ) WHERE id = ?"
70
            maybe_print("executing " + query.replace('?', '%d') % (c['rating'], image['id']) + "\n")
71
            if not args.dryrun:
72
                assert darktable.execute(query, (c['rating'], image['id'])).rowcount == 1
73
                changed += 1
74
            for tag in tags:
75
                args.dryrun and maybe_print("would be")
76
                query = 'INSERT INTO tagged_images SELECT ?, id FROM tags WHERE name = ?'
77
                maybe_print("executing " + query.replace('?', '%s') % (str(image['id']), tag) + "\n")
78
                if not args.dryrun:
79
                    assert darktable.execute(query, (image['id'], tag)).rowcount == 1
80

    
81
            # add tags on the file itself
82
            args.dryrun and maybe_print("would be")
83
            command = u'''exiv2 -eaX  -M 'add Xmp.dc.subject XmpBag "%s"' %s''' % (u", ".join(tags), image['path'])
84
            maybe_print(u"executing %s\n" % command)
85
            good_sidecar = image['path'] + '.xmp'
86
            bad_sidecar = image['path'][:-4] + '.xmp'
87
            args.dryrun and maybe_print("would be")
88
            maybe_print("renaming %s to %s\n" % (bad_sidecar, good_sidecar))
89
            if not args.dryrun:
90
                ret = os.system(command.encode('utf-8'))
91
                if ret != 0:
92
                    print "warning: exiv command return %d, ran: %s" % (ret, command)
93
                else:
94
                    os.rename(bad_sidecar, good_sidecar)
95
            if tags_found >= args.limit:
96
                break
97

    
98
if not args.dryrun:
99
    darktable.commit()
100

    
101
print "%d/%d images found, %d found with metadata, %d changed" % (found, total, tags_found, changed)
Go to top