The branch, dharma-pre has been updated
       via  86e2c10721eba4891dd81faab1cb8ff6f938405e (commit)
      from  e55375ea5ea54c4528c36d161b783081dc25983c (commit)

- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=86e2c10721eba4891dd81faab1cb8ff6f938405e

commit 86e2c10721eba4891dd81faab1cb8ff6f938405e
Author: spiff <[email protected]>
Date:   Fri Oct 22 12:42:26 2010 +0200

    [plugin.image.iphoto] updated to version 1.2.0

diff --git a/plugin.image.iphoto/README.txt b/plugin.image.iphoto/README.txt
index b97a2b9..ba24ff1 100644
--- a/plugin.image.iphoto/README.txt
+++ b/plugin.image.iphoto/README.txt
@@ -1,10 +1,12 @@
 iPhoto plugin for XBMC
 ======================
 This plugin imports an iPhoto library into XBMC.  After importing, you will
-see three categories that correspond with their iPhoto counterparts:
+see categories that correspond with their iPhoto counterparts:
 
 * Events
 * Albums
+* Faces
+* Keywords
 * Ratings
 
 Configuration
@@ -40,4 +42,4 @@ Known Issues
 ============
 * Sorting by date is broken and therefore disabled for now.
   See http://trac.xbmc.org/ticket/10519
-* No support for Keywords and Faces (yet).
+* Need icons for Faces and Keywords.
diff --git a/plugin.image.iphoto/addon.py b/plugin.image.iphoto/addon.py
index bfb70ca..2113d8b 100755
--- a/plugin.image.iphoto/addon.py
+++ b/plugin.image.iphoto/addon.py
@@ -42,7 +42,7 @@ def render_media(media):
        if (not caption):
            caption = mediapath
 
-       if caption:
+       if (caption):
            # < r34717 doesn't support unicode thumbnail paths
            try:
                item = gui.ListItem(caption, thumbnailImage=thumbpath)
@@ -65,6 +65,46 @@ def render_media(media):
        plugin.addSortMethod(int(sys.argv[1]), plugin.SORT_METHOD_DATE)
     return n
 
+def list_photos_in_album(params):
+    global db
+
+    albumid = params['albumid']
+    media = db.GetMediaInAlbum(albumid)
+    return render_media(media)
+
+def list_albums(params, ign_empty):
+    global db, BASE_URL
+
+    albumid = 0
+    try:
+       albumid = params['albumid']
+       return list_photos_in_album(params)
+    except Exception, e:
+       print to_str(e)
+       pass
+
+    albums = db.GetAlbums()
+    if (not albums):
+       return
+
+    n = 0
+    for (albumid, name, count) in albums:
+       if (name == "Photos"):
+           continue
+
+       if (not count and ign_empty == "true"):
+           continue
+
+       item = gui.ListItem(name)
+       if (count):
+           item.setInfo(type="pictures", infoLabels={ "count": count })
+       plugin.addDirectoryItem(handle = int(sys.argv[1]), 
url=BASE_URL+"?action=albums&albumid=%s" % (albumid), listitem = item, isFolder 
= True)
+       n += 1
+
+    plugin.addSortMethod(int(sys.argv[1]), plugin.SORT_METHOD_UNSORTED)
+    plugin.addSortMethod(int(sys.argv[1]), plugin.SORT_METHOD_LABEL)
+    return n
+
 def list_photos_in_event(params):
     global db
 
@@ -72,8 +112,8 @@ def list_photos_in_event(params):
     media = db.GetMediaInRoll(rollid)
     return render_media(media)
 
-def list_events(params):
-    global db,BASE_URL
+def list_events(params, ign_empty):
+    global db, BASE_URL
 
     rollid = 0
     try:
@@ -90,6 +130,9 @@ def list_events(params):
     sort_date = False
     n = 0
     for (rollid, name, thumbpath, rolldate, count) in rolls:
+       if (not count and ign_empty == "true"):
+           continue
+
        # < r34717 doesn't support unicode thumbnail paths
        try:
            item = gui.ListItem(name, thumbnailImage=thumbpath)
@@ -103,52 +146,103 @@ def list_events(params):
        except:
            pass
 
-       item.setInfo(type="pictures", infoLabels={ "count": count })
+       if (count):
+           item.setInfo(type="pictures", infoLabels={ "count": count })
        plugin.addDirectoryItem(handle = int(sys.argv[1]), 
url=BASE_URL+"?action=events&rollid=%s" % (rollid), listitem = item, isFolder = 
True)
        n += 1
 
     plugin.addSortMethod(int(sys.argv[1]), plugin.SORT_METHOD_UNSORTED)
     plugin.addSortMethod(int(sys.argv[1]), plugin.SORT_METHOD_LABEL)
-    if sort_date == True:
+    if (sort_date == True):
        plugin.addSortMethod(int(sys.argv[1]), plugin.SORT_METHOD_DATE)
     return n
 
-def list_photos_in_album(params):
+def list_photos_with_face(params):
     global db
 
-    albumid = params['albumid']
-    media = db.GetMediaInAlbum(albumid)
+    faceid = params['faceid']
+    media = db.GetMediaWithFace(faceid)
     return render_media(media)
 
-def list_albums(params):
-    global db,BASE_URL
+def list_faces(params, ign_empty):
+    global db, BASE_URL
 
-    albumid = 0
+    faceid = 0
     try:
-       albumid = params['albumid']
-       return list_photos_in_album(params)
+       faceid = params['faceid']
+       return list_photos_with_face(params)
     except Exception, e:
        print to_str(e)
        pass
 
-    albums = db.GetAlbums()
-    if (not albums):
+    faces = db.GetFaces()
+    if (not faces):
        return
 
     n = 0
-    for (albumid, name, count) in albums:
-       if name == "Photos":
+    for (faceid, name, thumbpath, count) in faces:
+       if (not count and ign_empty == "true"):
            continue
 
-       item = gui.ListItem(name)
-       item.setInfo(type="pictures", infoLabels={ "count": count })
-       plugin.addDirectoryItem(handle = int(sys.argv[1]), 
url=BASE_URL+"?action=albums&albumid=%s" % (albumid), listitem = item, isFolder 
= True)
+       # < r34717 doesn't support unicode thumbnail paths
+       try:
+           item = gui.ListItem(name, thumbnailImage=thumbpath)
+       except:
+           item = gui.ListItem(name)
+
+       if (count):
+           item.setInfo(type="pictures", infoLabels={ "count": count })
+       plugin.addDirectoryItem(handle = int(sys.argv[1]), 
url=BASE_URL+"?action=faces&faceid=%s" % (faceid), listitem = item, isFolder = 
True)
        n += 1
 
     plugin.addSortMethod(int(sys.argv[1]), plugin.SORT_METHOD_UNSORTED)
     plugin.addSortMethod(int(sys.argv[1]), plugin.SORT_METHOD_LABEL)
     return n
 
+def list_photos_with_keyword(params):
+    global db
+
+    keywordid = params['keywordid']
+    media = db.GetMediaWithKeyword(keywordid)
+    return render_media(media)
+
+def list_keywords(params, ign_empty):
+    global db, BASE_URL
+
+    keywordid = 0
+    try:
+       keywordid = params['keywordid']
+       return list_photos_with_keyword(params)
+    except Exception, e:
+       print to_str(e)
+       pass
+
+    keywords = db.GetKeywords()
+    if (not keywords):
+       return
+
+    hidden_keywords = addon.getSetting('hidden_keywords')
+
+    n = 0
+    for (keywordid, name, count) in keywords:
+       if (name in hidden_keywords):
+           continue
+
+       if (not count and ign_empty == "true"):
+           continue
+
+       item = gui.ListItem(name)
+       item.addContextMenuItems([(addon.getLocalizedString(30214), 
"XBMC.RunPlugin(\""+BASE_URL+"?action=hidekeyword&keyword=%s\")" % (name),)])
+       if (count):
+           item.setInfo(type="pictures", infoLabels={ "count": count })
+       plugin.addDirectoryItem(handle = int(sys.argv[1]), 
url=BASE_URL+"?action=keywords&keywordid=%s" % (keywordid), listitem = item, 
isFolder = True)
+       n += 1
+
+    if (n > 0):
+       plugin.addSortMethod(int(sys.argv[1]), plugin.SORT_METHOD_UNSORTED)
+       plugin.addSortMethod(int(sys.argv[1]), plugin.SORT_METHOD_LABEL)
+    return n
+
 def list_photos_with_rating(params):
     global db
 
@@ -157,7 +251,7 @@ def list_photos_with_rating(params):
     return render_media(media)
 
 def list_ratings(params):
-    global db,BASE_URL,ICONS_PATH
+    global db, BASE_URL, ICONS_PATH
 
     albumid = 0
     try:
@@ -190,11 +284,9 @@ def progress_callback(progress_dialog, nphotos, ntotal):
     return nphotos
 
 def import_library(xmlfile):
-    global db_file
+    global db
 
-    db_tmp_file = db_file + ".tmp"
-    db_tmp = IPhotoDB(db_tmp_file)
-    db_tmp.ResetDB()
+    db.ResetDB()
 
     # always ignore Books and currently selected album
     album_ign = []
@@ -223,43 +315,31 @@ def import_library(xmlfile):
     except:
        print traceback.print_exc()
     else:
-       iparser = IPhotoParser(xmlfile, db_tmp.AddAlbumNew, album_ign, 
db_tmp.AddRollNew, db_tmp.AddKeywordNew, db_tmp.AddMediaNew, progress_callback, 
progress_dialog)
+       iparser = IPhotoParser(xmlfile, db.AddAlbumNew, album_ign, 
db.AddRollNew, db.AddFaceNew, db.AddKeywordNew, db.AddMediaNew, 
progress_callback, progress_dialog)
 
        progress_dialog.update(0, addon.getLocalizedString(30212))
        try:
            iparser.Parse()
-           db_tmp.UpdateLastImport()
+           db.UpdateLastImport()
        except:
            print traceback.print_exc()
-       else:
-           if (not progress_dialog.iscanceled()):
-               try:
-                   os.rename(db_tmp_file, db_file)
-               except:
-                   # windows doesn't allow in-place rename
-                   remove_tries = 3
-                   while remove_tries and os.path.isfile(db_file):
-                       try:
-                           os.remove(db_file)
-                       except:
-                           remove_tries -= 1
-                           xbmc.sleep(1000)
-
-                   try:
-                       os.rename(db_tmp_file, db_file)
-                   except:
-                       print traceback.print_exc()
 
     progress_dialog.close()
 
-    del db_tmp
-    remove_tries = 3
-    while remove_tries and os.path.isfile(db_tmp_file):
-       try:
-           os.remove(db_tmp_file)
-       except:
-           remove_tries -= 1
-           xbmc.sleep(1000)
+def hide_keyword(params):
+    try:
+       keyword = params['keyword']
+       hidden_keywords = addon.getSetting('hidden_keywords')
+       if (hidden_keywords != ""):
+           hidden_keywords += " "
+       hidden_keywords += keyword
+       addon.setSetting('hidden_keywords', hidden_keywords)
+       print "JSL: HIDDEN KEYWORDS '%s'" % (hidden_keywords)
+    except Exception, e:
+       print to_str(e)
+       pass
+
+    xbmc.executebuiltin("Container.Refresh")
 
 def get_params(paramstring):
     params = {}
@@ -299,6 +379,16 @@ if (__name__ == "__main__"):
            add_import_lib_context_item(item)
            plugin.addDirectoryItem(int(sys.argv[1]), 
BASE_URL+"?action=albums", item, True)
 
+           item = gui.ListItem(addon.getLocalizedString(30105), 
thumbnailImage=ICONS_PATH+"/faces.png")
+           item.setInfo("Picture", { "Title": "Faces" })
+           add_import_lib_context_item(item)
+           plugin.addDirectoryItem(int(sys.argv[1]), BASE_URL+"?action=faces", 
item, True)
+
+           item = gui.ListItem(addon.getLocalizedString(30104), 
thumbnailImage=ICONS_PATH+"/keywords.png")
+           item.setInfo("Picture", { "Title": "Keywords" })
+           add_import_lib_context_item(item)
+           plugin.addDirectoryItem(int(sys.argv[1]), 
BASE_URL+"?action=keywords", item, True)
+
            item = gui.ListItem(addon.getLocalizedString(30102), 
thumbnailImage=ICONS_PATH+"/star.png")
            item.setInfo("Picture", { "Title": "Ratings" })
            add_import_lib_context_item(item)
@@ -333,15 +423,26 @@ if (__name__ == "__main__"):
                if (xml_mtime > db_mtime):
                    import_library(xmlfile)
     else:
+       # ignore empty albums if configured to do so
+       album_ign_empty = addon.getSetting('album_ignore_empty')
+       if (album_ign_empty == ""):
+           addon.setSetting('album_ignore_empty', 'true')
+           album_ign_empty = "true"
+
        if (action == "events"):
-           items = list_events(params)
+           items = list_events(params, album_ign_empty)
        elif (action == "albums"):
-           items = list_albums(params)
+           items = list_albums(params, album_ign_empty)
+       elif (action == "faces"):
+           items = list_faces(params, album_ign_empty)
+       elif (action == "keywords"):
+           items = list_keywords(params, album_ign_empty)
        elif (action == "ratings"):
            items = list_ratings(params)
        elif (action == "rescan"):
-           del db
            items = import_library(xmlfile)
+       elif (action == "hidekeyword"):
+           items = hide_keyword(params)
 
        if (items):
            plugin.endOfDirectory(int(sys.argv[1]), True)
diff --git a/plugin.image.iphoto/addon.xml b/plugin.image.iphoto/addon.xml
index dbf355b..272e025 100644
--- a/plugin.image.iphoto/addon.xml
+++ b/plugin.image.iphoto/addon.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="plugin.image.iphoto" name="iPhoto" version="1.0.0" 
provider-name="jingai">
+<addon id="plugin.image.iphoto" name="iPhoto" version="1.2.0" 
provider-name="jingai">
     <requires>
        <import addon="xbmc.python" version="1.0"/>
        <import addon="script.module.pysqlite" version="2.5.6"/>
@@ -13,7 +13,7 @@
        <summary lang="en">Imports iPhoto library into XBMC.</summary>
        <summary lang="hu">iPhoto könyvtár importálása az XBMC-be</summary>
        <summary lang="pt">Importa bibliotecas iPhoto para o XBMC.</summary>
-       <description lang="en">Imports iPhoto library into Events, Albums, and 
Ratings categories.</description>
+       <description lang="en">Imports iPhoto library into Events, Albums, 
Faces, Keywords, and Ratings categories.</description>
        <description lang="hu">iPhoto könyvtár importálása az Események, 
Albumok és Besorolás kategóriákba.</description>
        <description lang="pt">Importa bibliotecas iPhoto para Eventos, Álbuns 
e Classificações.</description>
        <disclaimer lang="en">[Fixed in SVN r34733] Thumbnail image paths 
containing unicode characters will be skipped (see 
http://trac.xbmc.org/ticket/10486)</disclaimer>
diff --git a/plugin.image.iphoto/changelog.txt 
b/plugin.image.iphoto/changelog.txt
index b44c876..01a1cff 100644
--- a/plugin.image.iphoto/changelog.txt
+++ b/plugin.image.iphoto/changelog.txt
@@ -1,3 +1,14 @@
+1.2.0 - 20101021
+- Add Faces support.
+- Add context menu item to hide Keywords.
+- Add option to hide empty albums.
+
+1.1.0 - 20101021
+- Add Keywords support.
+
+1.0.1 - 20101019
+- Fix library import on Windows.. again.
+
 1.0.0 - 20101015
 - Enable sort on title.
 - Fix library import on Windows.
diff --git a/plugin.image.iphoto/resources/language/English/strings.xml 
b/plugin.image.iphoto/resources/language/English/strings.xml
index 13f69db..3209bd9 100644
--- a/plugin.image.iphoto/resources/language/English/strings.xml
+++ b/plugin.image.iphoto/resources/language/English/strings.xml
@@ -6,12 +6,15 @@
     <string id="30211">Imported %d items</string>
     <string id="30212">Scanning AlbumData.xml...</string>
     <string id="30213">Import iPhoto Library</string>
+    <string id="30214">Hide Keyword</string>
 
     <!-- Category strings -->
     <string id="30100">Events</string>
     <string id="30101">Albums</string>
     <string id="30102">Ratings</string>
     <string id="30103">&lt;&lt; Import iPhoto Library &gt;&gt;</string>
+    <string id="30104">Keywords</string>
+    <string id="30105">Faces</string>
 
     <!-- Plugin settings strings -->
     <string id="30000">Path to AlbumData.xml</string>
@@ -19,4 +22,6 @@
     <string id="30002">Ignore 'Flagged' album</string>
     <string id="30003">Auto update library</string>
     <string id="30004">Hide Import Library item in main menu</string>
+    <string id="30005">Ignore empty albums</string>
+    <string id="30006">Hidden keywords</string>
 </strings>
diff --git a/plugin.image.iphoto/resources/lib/iphoto_parser.py 
b/plugin.image.iphoto/resources/lib/iphoto_parser.py
index 17fbab6..7cc937d 100755
--- a/plugin.image.iphoto/resources/lib/iphoto_parser.py
+++ b/plugin.image.iphoto/resources/lib/iphoto_parser.py
@@ -16,10 +16,10 @@ import os.path
 import locale
 
 def to_unicode(text):
-    if isinstance(text, unicode):
+    if (isinstance(text, unicode)):
        return text
 
-    if hasattr(text, '__unicode__'):
+    if (hasattr(text, '__unicode__')):
        return text.__unicode__()
 
     text = str(text)
@@ -37,13 +37,13 @@ def to_unicode(text):
     return unicode(text, 'latin1')
 
 def to_str(text):
-    if isinstance(text, str):
+    if (isinstance(text, str)):
        return text
 
-    if hasattr(text, '__unicode__'):
+    if (hasattr(text, '__unicode__')):
        text = text.__unicode__()
 
-    if hasattr(text, '__str__'):
+    if (hasattr(text, '__str__')):
        return text.__str__()
 
     return text.encode('utf-8')
@@ -55,7 +55,7 @@ class IPhotoDB:
        return
 
     def _cleanup_filename(self, filename):
-       if filename.startswith("file://localhost"):
+       if (filename.startswith("file://localhost")):
            return unquote(filename[16:])
        else:
            return unquote(filename)
@@ -102,16 +102,6 @@ class IPhotoDB:
            pass
 
        try:
-           # keywords table
-           self.dbconn.execute("""
-           CREATE TABLE keywords (
-              id integer primary key,
-              name varchar
-           )""")
-       except:
-           pass
-
-       try:
            # mediatypes table
            self.dbconn.execute("""
            CREATE TABLE mediatypes (
@@ -169,6 +159,72 @@ class IPhotoDB:
        except Exception, e:
            pass
 
+       try:
+           # faces table
+           self.dbconn.execute("""
+           CREATE TABLE faces (
+              id integer primary key,
+              name varchar,
+              keyphotoid integer,
+              photocount integer,
+              faceorder integer
+           )""")
+       except:
+           pass
+
+       try:
+           # facesmedia table
+           self.dbconn.execute("""
+           CREATE TABLE facesmedia (
+              faceid integer,
+              mediaid integer,
+              mediaorder integer
+           )""")
+       except Exception, e:
+           pass
+
+       try:
+           # keywords table
+           self.dbconn.execute("""
+           CREATE TABLE keywords (
+              id integer primary key,
+              name varchar,
+              photocount integer
+           )""")
+       except:
+           pass
+
+       try:
+           # keywordmedia table
+           self.dbconn.execute("""
+           CREATE TABLE keywordmedia (
+              keywordid integer,
+              mediaid integer,
+              mediaorder integer
+           )""")
+       except Exception, e:
+           pass
+
+    def ResetDB(self):
+       for table in ['media', 'mediatypes', 'rolls', 'rollmedia', 'albums', 
'albummedia', 'faces', 'facesmedia', 'keywords', 'keywordmedia']:
+           try:
+               self.dbconn.execute("DROP TABLE %s" % table)
+           except Exception, e:
+               print to_str(e)
+               pass
+       try:
+           self.InitDB()
+       except Exception, e:
+           print to_str(e)
+           raise e
+
+    def Commit(self):
+       try:
+           self.dbconn.commit()
+       except Exception, e:
+           print "Commit Error: " + to_str(e)
+           pass
+
     def GetConfig(self, key):
        try:
            cur = self.dbconn.cursor()
@@ -177,52 +233,95 @@ class IPhotoDB:
                           WHERE key = ? LIMIT 1""",
                        (key,))
            row = cur.fetchone()
-           if row:
+           if (row):
                return row[0]
            return None
        except:
            return None
 
     def SetConfig(self, key, value):
-       if self.GetConfig(key)==None:
+       if (self.GetConfig(key) == None):
            cur = self.dbconn.cursor()
-           cur.execute("""INSERT INTO config(key,value)
-                          VALUES(?,?)""",
+           cur.execute("""INSERT INTO config (key, value)
+                          VALUES (?, ?)""",
                        (key, value))
            self.Commit()
        else:
            cur = self.dbconn.cursor()
            cur.execute("""UPDATE config
-                          SET value=?
-                          WHERE key=?""",
+                          SET value = ?
+                          WHERE key = ?""",
                        (value, key))
            self.Commit()
 
     def UpdateLastImport(self):
        self.SetConfig('lastimport', 'dummy')
        self.dbconn.execute("""UPDATE config
-                              SET value=datetime('now')
-                              WHERE key=?""",
+                              SET value = datetime('now')
+                              WHERE key = ?""",
                            ('lastimport',))
        self.Commit()
 
+    def GetTableId(self, table, value, column='name', autoadd=False, 
autoclean=True):
+       try:
+           if (autoclean and not value):
+               value = "Unknown"
+           cur = self.dbconn.cursor()
+
+           # query db for column with specified name
+           cur.execute("SELECT id FROM %s WHERE %s = ?" % (table, column),
+                       (value,))
+           row = cur.fetchone()
+
+           # create named ID if requested
+           if not row and autoadd and value and len(value) > 0:
+               nextid = cur.execute("SELECT MAX(id) FROM %s" % 
table).fetchone()[0]
+               if not nextid:
+                   nextid = 1
+               else:
+                   nextid += 1
+               cur.execute("INSERT INTO %s (id, %s) VALUES (?, ?)" % (table, 
column),
+                           (nextid, value))
+               return nextid # return new id
+           return row[0] # return id
+       except Exception, e:
+           print to_str(e)
+           raise e
+
+    def GetMediaTypeId(self, mediatype, autoadd=False):
+       return self.GetTableId('mediatypes', mediatype, 'name', autoadd)
+
     def GetAlbums(self):
        albums = []
        try:
            cur = self.dbconn.cursor()
-           cur.execute("SELECT id,name,photocount FROM albums")
+           cur.execute("SELECT id, name, photocount FROM albums")
            for tuple in cur:
                albums.append(tuple)
        except:
            pass
        return albums
 
+    def GetMediaInAlbum(self, albumid):
+       media = []
+       try:
+           cur = self.dbconn.cursor()
+           cur.execute("""SELECT M.caption, M.mediapath, M.thumbpath, 
M.originalpath, M.rating, M.mediadate, M.mediasize
+                       FROM albummedia A LEFT JOIN media M ON A.mediaid = M.id
+                       WHERE A.albumid = ?""", (albumid,))
+           for tuple in cur:
+               media.append(tuple)
+       except Exception, e:
+           print to_str(e)
+           pass
+       return media
+
     def GetRolls(self):
        rolls = []
        try:
            cur = self.dbconn.cursor()
-           cur.execute("""SELECT 
R.id,R.name,M.thumbpath,R.rolldate,R.photocount 
-                        FROM rolls R LEFT JOIN media M ON R.keyphotoid=M.id""")
+           cur.execute("""SELECT R.id, R.name, M.thumbpath, R.rolldate, 
R.photocount 
+                        FROM rolls R LEFT JOIN media M ON R.keyphotoid = 
M.id""")
            for tuple in cur:
                rolls.append(tuple)
        except Exception, e:
@@ -243,26 +342,27 @@ class IPhotoDB:
            pass
        return media
 
-    def GetMediaWithRating(self, rating):
-       media = []
+    def GetFaces(self):
+       faces = []
        try:
            cur = self.dbconn.cursor()
-           cur.execute("""SELECT M.caption, M.mediapath, M.thumbpath, 
M.originalpath, M.rating, M.mediadate, M.mediasize
-                       FROM media M WHERE M.rating = ?""", (rating,))
+           cur.execute("""SELECT F.id, F.name, M.thumbpath, F.photocount
+                        FROM faces F LEFT JOIN media M ON F.keyphotoid = M.id
+                        ORDER BY F.faceorder""")
            for tuple in cur:
-               media.append(tuple)
+               faces.append(tuple)
        except Exception, e:
            print to_str(e)
            pass
-       return media
+       return faces
 
-    def GetMediaInAlbum(self, albumid):
+    def GetMediaWithFace(self, faceid):
        media = []
        try:
            cur = self.dbconn.cursor()
            cur.execute("""SELECT M.caption, M.mediapath, M.thumbpath, 
M.originalpath, M.rating, M.mediadate, M.mediasize
-                       FROM albummedia A LEFT JOIN media M ON A.mediaid=M.id
-                       WHERE A.albumid = ?""", (albumid,))
+                       FROM facesmedia A LEFT JOIN media M ON A.mediaid = M.id
+                       WHERE A.faceid = ?""", (faceid,))
            for tuple in cur:
                media.append(tuple)
        except Exception, e:
@@ -271,67 +371,47 @@ class IPhotoDB:
        return media
 
     def GetKeywords(self):
-       genres = []
+       keywords = []
        try:
            cur = self.dbconn.cursor()
-           cur.execute("SELECT id,name FROM keywords")
+           cur.execute("SELECT id, name, photocount FROM keywords")
            for tuple in cur:
-               genres.append(tuple)
+               keywords.append(tuple)
        except Exception, e:
            print to_str(e)
            pass
-       return genres
+       return keywords
 
-    def GetTableId(self, table, value, column='name', autoadd=False, 
autoclean=True):
+    def GetMediaWithKeyword(self, keywordid):
+       media = []
        try:
-           if autoclean and not value:
-               value = "Unknown"
            cur = self.dbconn.cursor()
-
-           # query db for column with specified name
-           cur.execute("SELECT id FROM %s WHERE %s = ?" % (table, column),
-                       (value,))
-           row = cur.fetchone()
-
-           # create named ID if requested
-           if not row and autoadd and value and len(value) > 0:
-               nextid = cur.execute("SELECT MAX(id) FROM %s" % 
table).fetchone()[0]
-               if not nextid:
-                   nextid = 1
-               else:
-                   nextid += 1
-               cur.execute("INSERT INTO %s(id, %s) VALUES (?,?)" % (table, 
column),
-                           (nextid, value))
-               return nextid # return new id
-           return row[0] # return id
+           cur.execute("""SELECT M.caption, M.mediapath, M.thumbpath, 
M.originalpath, M.rating, M.mediadate, M.mediasize
+                       FROM keywordmedia A LEFT JOIN media M ON A.mediaid = 
M.id
+                       WHERE A.keywordid = ?""", (keywordid,))
+           for tuple in cur:
+               media.append(tuple)
        except Exception, e:
            print to_str(e)
-           raise e
-
-    def GetMediaTypeId(self, mediatype, autoadd=False):
-       return self.GetTableId('mediatypes', mediatype, 'name', autoadd)
-
-    def Commit(self):
-       try:
-           self.dbconn.commit()
-       except Exception, e:
-           print "Commit Error: " + to_str(e)
            pass
+       return media
 
-    def ResetDB(self):
-       for table in 
['keywords','rolls','rollmedia','albums','albummedia','media','mediatypes']:
-           try:
-               self.dbconn.execute("DROP TABLE %s" % table)
-           except Exception, e:
-               print to_str(e)
-               pass
+    def GetMediaWithRating(self, rating):
+       media = []
        try:
-           self.InitDB()
+           cur = self.dbconn.cursor()
+           cur.execute("""SELECT M.caption, M.mediapath, M.thumbpath, 
M.originalpath, M.rating, M.mediadate, M.mediasize
+                       FROM media M WHERE M.rating = ?""", (rating,))
+           for tuple in cur:
+               media.append(tuple)
        except Exception, e:
            print to_str(e)
-           raise e
+           pass
+       return media
 
     def AddAlbumNew(self, album, album_ign):
+       #print "AddAlbumNew()", album
+
        try:
            albumid = int(album['AlbumId'])
            albumtype = album['Album Type']
@@ -339,15 +419,13 @@ class IPhotoDB:
            return
 
        # weed out ignored albums
-       if albumtype in album_ign:
+       if (albumtype in album_ign):
            return
 
-       #print "AddAlbumNew()", to_str(album)
-
        try:
            self.dbconn.execute("""
            INSERT INTO albums (id, name, master, guid, photocount)
-           VALUES (?,?,?,?,?)""",
+           VALUES (?, ?, ?, ?, ?)""",
                                (albumid,
                                 album['AlbumName'],
                                 album.has_key('Master'),
@@ -355,25 +433,25 @@ class IPhotoDB:
                                 album['PhotoCount']))
            for media in album['medialist']:
                self.dbconn.execute("""
-               INSERT INTO albummedia ( albumid, mediaid )
-               VALUES (?,?)""", (albumid, media))
+               INSERT INTO albummedia (albumid, mediaid)
+               VALUES (?, ?)""", (albumid, media))
        except sqlite.IntegrityError:
            pass
        except Exception, e:
            raise e
 
     def AddRollNew(self, roll):
+       #print "AddRollNew()", roll
+
        try:
            rollid = int(roll['RollID'])
        except:
            return
 
-       #print "AddRollNew()", to_str(roll)
-
        try:
            self.dbconn.execute("""
            INSERT INTO rolls (id, name, keyphotoid, rolldate, photocount)
-           VALUES (?,?,?,?,?)""",
+           VALUES (?, ?, ?, ?, ?)""",
                                (rollid,
                                 roll['RollName'],
                                 roll['KeyPhotoKey'],
@@ -381,27 +459,49 @@ class IPhotoDB:
                                 roll['PhotoCount']))
            for media in roll['medialist']:
                self.dbconn.execute("""
-               INSERT INTO rollmedia ( rollid, mediaid )
-               VALUES (?,?)""", (rollid, media))
+               INSERT INTO rollmedia (rollid, mediaid)
+               VALUES (?, ?)""", (rollid, media))
        except sqlite.IntegrityError:
            pass
        except Exception, e:
            raise e
 
-    def AddKeywordNew(self, keyword):
+    def AddFaceNew(self, face):
+       #print "AddFaceNew()", face
+
        try:
-           kid = keyword.keys()[0]
-           kword = keyword[kid]
+           faceid = int(face['key'])
        except:
            return
 
-       #print "AddKeywordNew()", to_str(keyword)
+       try:
+           self.dbconn.execute("""
+           INSERT INTO faces (id, name, keyphotoid, photocount, faceorder)
+           VALUES (?, ?, ?, ?, ?)""",
+                               (faceid,
+                                face['name'],
+                                face['key image'],
+                                face['PhotoCount'],
+                                face['Order']))
+       except sqlite.IntegrityError:
+           pass
+       except Exception, e:
+           raise e
+
+    def AddKeywordNew(self, keyword):
+       #print "AddKeywordNew()", keyword
+
+       try:
+           keywordid = keyword.keys()[0]
+           kword = keyword[keywordid]
+       except:
+           return
 
        try:
            self.dbconn.execute("""
            INSERT INTO keywords (id, name)
-           VALUES (?,?)""",
-                               (int(kid),
+           VALUES (?, ?)""",
+                               (int(keywordid),
                                 kword))
        except sqlite.IntegrityError:
            pass
@@ -409,19 +509,19 @@ class IPhotoDB:
            raise e
 
     def AddMediaNew(self, media, archivePath, realPath):
+       #print "AddMediaNew()", media
+
        try:
            mediaid = media['MediaID']
-           if not mediaid:
+           if (not mediaid):
                return
-       except Exception, e:
+       except:
            return
 
-       #print "AddMediaNew()", to_str(media)
-
        # rewrite paths to image files based on configured path.
        # if the iPhoto library is mounted as a share, the paths in
        # AlbumData.xml probably won't be right.
-       if archivePath and realPath:
+       if (archivePath and realPath):
            imagepath = media['ImagePath'].replace(archivePath, realPath)
            thumbpath = media['ThumbPath'].replace(archivePath, realPath)
            originalpath = media['OriginalPath'].replace(archivePath, realPath)
@@ -442,10 +542,8 @@ class IPhotoDB:
 
        try:
            self.dbconn.execute("""
-           INSERT INTO media (id, mediatypeid, rollid, caption, guid,
-                             aspectratio, rating, mediadate, mediasize, 
mediapath,
-                             thumbpath, originalpath)
-           VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""",
+           INSERT INTO media (id, mediatypeid, rollid, caption, guid, 
aspectratio, rating, mediadate, mediasize, mediapath, thumbpath, originalpath)
+           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                                (mediaid,
                                 self.GetMediaTypeId(media['MediaType'], True),
                                 media['Roll'],
@@ -458,6 +556,29 @@ class IPhotoDB:
                                 imagepath,
                                 thumbpath,
                                 originalpath))
+
+           for faceid in media['facelist']:
+               self.dbconn.execute("""
+               INSERT INTO facesmedia (faceid, mediaid)
+               VALUES (?, ?)""", (faceid, mediaid))
+               cur = self.dbconn.cursor()
+
+           for keywordid in media['keywordlist']:
+               self.dbconn.execute("""
+               INSERT INTO keywordmedia (keywordid, mediaid)
+               VALUES (?, ?)""", (keywordid, mediaid))
+               cur = self.dbconn.cursor()
+               cur.execute("""SELECT id, photocount
+                           FROM keywords
+                           WHERE id = ?""", (keywordid,))
+               for tuple in cur:
+                   if (tuple[1]):
+                       photocount = int(tuple[1]) + 1
+                   else:
+                       photocount = 1
+                   self.dbconn.execute("""
+                   UPDATE keywords SET photocount = ?
+                   WHERE id = ?""", (photocount, keywordid))
        except sqlite.IntegrityError:
            pass
        except Exception, e:
@@ -481,6 +602,8 @@ class IPhotoParserState:
        self.inalbum = 0
        self.rolls = False
        self.inroll = 0
+       self.faces = False
+       self.inface = 0
        self.keywords = False
        self.inkeyword = 0
        self.master = False
@@ -493,7 +616,7 @@ class IPhotoParserState:
 
 class IPhotoParser:
     def __init__(self, xmlfile="", album_callback=None, album_ign=[],
-                roll_callback=None, keyword_callback=None, photo_callback=None,
+                roll_callback=None, face_callback=None, keyword_callback=None, 
photo_callback=None,
                 progress_callback=None, progress_dialog=None):
        self.xmlfile = xmlfile
        self.imagePath = ""
@@ -506,14 +629,17 @@ class IPhotoParser:
        self.currentPhoto = {}
        self.currentAlbum = {}
        self.currentRoll = {}
+       self.currentFace = {}
        self.currentKeyword = {}
        self.photoList = []
        self.albumList = []
        self.rollList = []
+       self.faceList = []
        self.keywordList = []
        self.AlbumCallback = album_callback
        self.albumIgn = album_ign
        self.RollCallback = roll_callback
+       self.FaceCallback = face_callback
        self.KeywordCallback = keyword_callback
        self.PhotoCallback = photo_callback
        self.ProgressCallback = progress_callback
@@ -522,6 +648,7 @@ class IPhotoParser:
        self._reset_photo()
        self._reset_album()
        self._reset_roll()
+       self._reset_face()
        self._reset_keyword()
 
     def _reset_photo(self):
@@ -531,6 +658,8 @@ class IPhotoParser:
            self.currentPhoto[a] = ""
        self.currentPhoto['Aspect Ratio'] = '0'
        self.currentPhoto['DateAsTimerInterval'] = '0'
+       self.currentPhoto['facelist'] = []
+       self.currentPhoto['keywordlist'] = []
 
     def _reset_album(self):
        self.currentAlbum = {}
@@ -546,6 +675,11 @@ class IPhotoParser:
            self.currentRoll[a] = ""
        self.currentRoll['medialist'] = []
 
+    def _reset_face(self):
+       self.currentFace = {}
+       for a in self.currentFace.keys():
+           self.currentFace[a] = ""
+
     def _reset_keyword(self):
        self.currentKeyword = {}
        for a in self.currentKeyword.keys():
@@ -557,13 +691,13 @@ class IPhotoParser:
 
        state = self.state
        state.nphotos = self.ProgressCallback(self.ProgressDialog, 
state.nphotos, state.nphotostotal)
-       if (state.nphotos is None):
+       if (state.nphotos == None):
            raise ParseCanceled(0)
 
     def commitAll(self):
        state = self.state
 
-       state.nphotostotal = len(self.albumList) + len(self.rollList) + 
len(self.keywordList) + len(self.photoList)
+       state.nphotostotal = len(self.albumList) + len(self.rollList) + 
len(self.faceList) + len(self.keywordList) + len(self.photoList)
 
        try:
            realPath = os.path.dirname(self.xmlfile)
@@ -571,22 +705,27 @@ class IPhotoParser:
            pass
 
        try:
-           if self.AlbumCallback and len(self.albumList) > 0:
+           if (self.AlbumCallback and len(self.albumList) > 0):
                for a in self.albumList:
                    self.AlbumCallback(a, self.albumIgn)
                    self.updateProgress()
 
-           if self.RollCallback and len(self.rollList) > 0:
+           if (self.RollCallback and len(self.rollList) > 0):
                for a in self.rollList:
                    self.RollCallback(a)
                    self.updateProgress()
 
-           if self.KeywordCallback and len(self.keywordList) > 0:
+           if (self.FaceCallback and len(self.faceList) > 0):
+               for a in self.faceList:
+                   self.FaceCallback(a)
+                   self.updateProgress()
+
+           if (self.KeywordCallback and len(self.keywordList) > 0):
                for a in self.keywordList:
                    self.KeywordCallback(a)
                    self.updateProgress()
 
-           if self.PhotoCallback and len(self.photoList) > 0:
+           if (self.PhotoCallback and len(self.photoList) > 0):
                for a in self.photoList:
                    self.PhotoCallback(a, self.imagePath, realPath)
                    self.updateProgress()
@@ -623,27 +762,30 @@ class IPhotoParser:
     def StartElement(self, name, attrs):
        state = self.state
        self.lastdata = False
-       if state.archivepath:
+       if (state.archivepath):
            state.inarchivepath += 1
            state.key = name
-       elif state.albums:
+       elif (state.albums):
            state.inalbum += 1
            state.key = name
-       elif state.rolls:
+       elif (state.rolls):
            state.inroll += 1
            state.key = name
-       elif state.keywords:
+       elif (state.faces):
+           state.inface += 1
+           state.key = name
+       elif (state.keywords):
            state.inkeyword += 1
            state.key = name
-       elif state.master:
+       elif (state.master):
            state.inmaster += 1
            state.key = name
 
-       if name == "key":
+       if (name == "key"):
            state.key = True
            #print "Got key type " + to_str(name)
        else:
-           if state.key:
+           if (state.key):
                state.valueType = name
                #print "Got value type " + to_str(name)
            else:
@@ -657,8 +799,8 @@ class IPhotoParser:
        self.lastdata = False
        state = self.state
 
-       if state.archivepath:
-           if not state.key:
+       if (state.archivepath):
+           if (not state.key):
                self.imagePath = state.value
                print "Rewriting iPhoto archive path '%s'" % 
(to_str(self.imagePath))
                print "as '%s'" % (to_str(os.path.dirname(self.xmlfile)))
@@ -666,51 +808,66 @@ class IPhotoParser:
            state.inarchivepath -= 1
 
        # Albums
-       elif state.albums:
-           if state.inalbum == 3 and self.currentAlbum.has_key('AlbumId'):
+       elif (state.albums):
+           if (state.inalbum == 3 and self.currentAlbum.has_key('AlbumId')):
                self.currentAlbum['medialist'].append(state.value)
-           elif state.inalbum == 2 and not state.key:
+           elif (state.inalbum == 2 and not state.key):
                #print "Mapping %s => %s" % ( to_str(state.keyValue), 
to_str(state.value))
                self.currentAlbum[state.keyValue] = state.value
            state.inalbum -= 1
-           if state.inalbum == 0 and self.currentAlbum.has_key('AlbumId'):
+           if (state.inalbum == 0 and self.currentAlbum.has_key('AlbumId')):
                # Finished reading album
                self.albumList.append(self.currentAlbum)
                self._reset_album()
 
        # Rolls
-       elif state.rolls:
-           if state.inroll == 3 and self.currentRoll.has_key('RollID'):
+       elif (state.rolls):
+           if (state.inroll == 3 and self.currentRoll.has_key('RollID')):
                self.currentRoll['medialist'].append(state.value)
-           elif state.inroll == 2 and not state.key:
+           elif (state.inroll == 2 and not state.key):
                #print "Mapping %s => %s" % ( to_str(state.keyValue), 
to_str(state.value))
                self.currentRoll[state.keyValue] = state.value
            state.inroll -= 1
-           if state.inroll == 0 and self.currentRoll.has_key('RollID'):
+           if (state.inroll == 0 and self.currentRoll.has_key('RollID')):
                # Finished reading roll
                self.rollList.append(self.currentRoll)
                self._reset_roll()
 
+       # Faces
+       elif (state.faces):
+           if (state.inface == 2 and not state.key):
+               #print "Mapping %s => %s" % ( to_str(state.keyValue), 
to_str(state.value))
+               self.currentFace[state.keyValue] = state.value
+           state.inface -= 1
+           if (state.inface == 0 and not state.key):
+               # Finished reading faces
+               self.faceList.append(self.currentFace)
+               self._reset_face()
+
        # Keywords
-       elif state.keywords:
-           if state.inkeyword == 1 and not state.key:
+       elif (state.keywords):
+           if (state.inkeyword == 1 and not state.key):
                #print "Mapping %s => %s" % ( to_str(state.keyValue), 
to_str(state.value))
                self.currentKeyword[state.keyValue] = state.value
            state.inkeyword -= 1
-           if state.inkeyword == 0 and not state.key:
+           if (state.inkeyword == 0 and not state.key):
                # Finished reading keywords
                self.keywordList.append(self.currentKeyword)
                self._reset_keyword()
 
        # Master Image List
-       elif state.master:
-           if state.inmaster == 1 and state.key:
+       elif (state.master):
+           if (state.inmaster == 1 and state.key):
                self.currentPhoto['MediaID'] = state.keyValue
-           elif state.inmaster == 2 and not state.key:
+           elif (state.inmaster == 3 and not state.key and state.keyValue == 
"Keywords"):
+               self.currentPhoto['keywordlist'].append(state.value)
+           elif (state.inmaster == 4 and not state.key and state.keyValue == 
"face key"):
+               self.currentPhoto['facelist'].append(state.value)
+           elif (state.inmaster == 2 and not state.key):
                #print "Mapping %s => %s" % ( to_str(state.keyValue), 
to_str(state.value))
                self.currentPhoto[state.keyValue] = state.value
            state.inmaster -= 1
-           if state.inmaster == 0 and self.currentPhoto.has_key('GUID') and 
self.currentPhoto['GUID']:
+           if (state.inmaster == 0 and self.currentPhoto.has_key('GUID') and 
self.currentPhoto['GUID']):
                # Finished reading master photo list
                self.photoList.append(self.currentPhoto)
                self._reset_photo()
@@ -719,47 +876,59 @@ class IPhotoParser:
 
     def CharData(self, data):
        state = self.state
-       if self.lastdata:
+       if (self.lastdata):
            data = self.lastdata + data
        self.lastdata = data
 
        data = data.strip()
 
-       if state.key and data:
+       if (state.key and data):
            state.keyValue = data
        else:
            state.value = data
 
        # determine which section we are in
-       if state.key and state.level == 3:
-           if data == "Archive Path":
+       if (state.key and state.level == 3):
+           if (data == "Archive Path"):
                state.archivepath = True
                state.albums = False
-               state.rolls  = False
+               state.rolls = False
+               state.faces = False
                state.keywords = False
                state.master = False
-           elif data == "List of Albums":
+           elif (data == "List of Albums"):
                state.archivepath = False
                state.albums = True
-               state.rolls  = False
+               state.rolls = False
+               state.faces = False
+               state.keywords = False
+               state.master = False
+           elif (data == "List of Rolls"):
+               state.archivepath = False
+               state.albums = False
+               state.rolls = True
+               state.faces = False
                state.keywords = False
                state.master = False
-           elif data == "List of Rolls":
+           elif (data == "List of Faces"):
                state.archivepath = False
                state.albums = False
-               state.rolls  = True
+               state.rolls = False
+               state.faces = True
                state.keywords = False
                state.master = False
-           elif data == "List of Keywords":
+           elif (data == "List of Keywords"):
                state.archivepath = False
                state.albums = False
-               state.rolls  = False
+               state.rolls = False
+               state.faces = False
                state.keywords = True
                state.master = False
-           elif data == "Master Image List":
+           elif (data == "Master Image List"):
                state.archivepath = False
                state.albums = False
-               state.rolls  = False
+               state.rolls = False
+               state.faces = False
                state.keywords = False
                state.master = True
 
@@ -785,7 +954,7 @@ def main():
 
     db = IPhotoDB("iphoto.db")
     db.ResetDB()
-    iparser = IPhotoParser(xmlfile, db.AddAlbumNew, "", db.AddRollNew, 
db.AddKeywordNew, db.AddMediaNew)
+    iparser = IPhotoParser(xmlfile, db.AddAlbumNew, "", db.AddRollNew, 
db.AddFaceNew, db.AddKeywordNew, db.AddMediaNew)
     try:
        iparser.Parse()
     except:
diff --git a/plugin.image.iphoto/resources/settings.xml 
b/plugin.image.iphoto/resources/settings.xml
index 127a016..69fb62f 100644
--- a/plugin.image.iphoto/resources/settings.xml
+++ b/plugin.image.iphoto/resources/settings.xml
@@ -2,7 +2,10 @@
 <settings>   
     <setting id="albumdata_xml_path" type="file" label="30000" default=""/>
     <setting id="auto_update_lib" type="bool" label="30003" default="false"/>
+    <setting type="sep" />
     <setting id="hide_import_lib" type="bool" label="30004" default="false"/>
+    <setting id="album_ignore_empty" type="bool" label="30005" default="true"/>
     <setting id="album_ignore_published" type="bool" label="30001" 
default="true"/>
     <setting id="album_ignore_flagged" type="bool" label="30002" 
default="true"/>
+    <setting id="hidden_keywords" type="text" label="30006" default=""/>
 </settings>

-----------------------------------------------------------------------

Summary of changes:
 plugin.image.iphoto/README.txt                     |    6 +-
 plugin.image.iphoto/addon.py                       |  213 +++++++---
 plugin.image.iphoto/addon.xml                      |    4 +-
 plugin.image.iphoto/changelog.txt                  |   11 +
 .../resources/language/English/strings.xml         |    5 +
 plugin.image.iphoto/resources/lib/iphoto_parser.py |  469 +++++++++++++-------
 plugin.image.iphoto/resources/settings.xml         |    3 +
 7 files changed, 501 insertions(+), 210 deletions(-)


hooks/post-receive
-- 
Plugins

------------------------------------------------------------------------------
Nokia and AT&T present the 2010 Calling All Innovators-North America contest
Create new apps & games for the Nokia N8 for consumers in  U.S. and Canada
$10 million total in prizes - $4M cash, 500 devices, nearly $6M in marketing
Develop with Nokia Qt SDK, Web Runtime, or Java and Publish to Ovi Store 
http://p.sf.net/sfu/nokia-dev2dev
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons

Reply via email to