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"><< Import iPhoto Library >></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