diff options
author | Todd Zullinger <tmzullinger@users.sourceforge.net> | 2007-07-31 20:42:54 +0000 |
---|---|---|
committer | Todd Zullinger <tmzullinger@users.sourceforge.net> | 2007-07-31 20:42:54 +0000 |
commit | 6dda4b59bb0f788da38823a035f28d4c515a38be (patch) | |
tree | 16f02c91b9efa543e3f4daab4b1bbcf3d88859f6 /bindings | |
parent | bd2fc5d392af67e46ee0fe8bf31b4f9e457f2da3 (diff) | |
download | libgpod-6dda4b59bb0f788da38823a035f28d4c515a38be.tar.gz libgpod-6dda4b59bb0f788da38823a035f28d4c515a38be.tar.xz libgpod-6dda4b59bb0f788da38823a035f28d4c515a38be.zip |
merge changes from the bug-1723660 branch
git-svn-id: https://gtkpod.svn.sf.net/svnroot/gtkpod/libgpod/trunk@1662 f01d2545-417e-4e96-918e-98f8d0dbbcb6
Diffstat (limited to 'bindings')
-rwxr-xr-x | bindings/python/examples/coverart_fetch.py | 84 | ||||
-rwxr-xr-x | bindings/python/examples/save_photos.py | 6 | ||||
-rw-r--r-- | bindings/python/gpod.i.in | 43 | ||||
-rw-r--r-- | bindings/python/ipod.py | 97 | ||||
-rw-r--r-- | bindings/python/tests/resources/tiny.png | bin | 0 -> 155 bytes | |||
-rw-r--r-- | bindings/python/tests/tests.py | 104 |
6 files changed, 257 insertions, 77 deletions
diff --git a/bindings/python/examples/coverart_fetch.py b/bindings/python/examples/coverart_fetch.py index f5bfa92..13b7929 100755 --- a/bindings/python/examples/coverart_fetch.py +++ b/bindings/python/examples/coverart_fetch.py @@ -27,14 +27,16 @@ import gpod import sys import amazon import urllib -import Image -import tempfile +import gtk +from optparse import OptionParser -ipod_mount = '/mnt/ipod' -itdb = gpod.itdb_parse(ipod_mount, None) -if not itdb: - print "Failed to read ipod at mountpoint %s" % ipod_mount - sys.exit(2) +parser = OptionParser() +parser.add_option("-m", "--mountpoint", dest="mountpoint", + default="/mnt/ipod", + help="use iPod at MOUNTPOINT", metavar="MOUNTPOINT") +(options, args) = parser.parse_args() + +db = gpod.Database(options.mountpoint) # set your key here, or see amazon.py for a list of other places to # store it. @@ -42,22 +44,21 @@ amazon.setLicense('') images = {} -for track in gpod.sw_get_tracks(itdb): - print track.artist, track.album, track.title, " :", - - #gpod.itdb_track_remove_thumbnails(track) - - if track.artwork.artwork_size: - print "Already has artwork, skipping." +for track in db: + if track.get_coverart().thumbnails: + #print " Already has artwork, skipping." + # note we could remove it with track.set_coverart(None) continue - if not (track.artist and track.album): - print "Need an artist AND album name, skipping." + print "%(artist)s, %(album)s, %(title)s" % track + + if not (track['artist'] and track['album']): + print " Need an artist AND album name, skipping." continue # avoid fetching again if we already had a suitable image - if not images.has_key((track.album,track.artist)): - query = "%s + %s" % (track.artist, track.album) + if not images.has_key((track['album'],track['artist'])): + query = "%(album)s + %(artist)s" % track # nasty hacks to get better hits. Is there a library out there # for this? Note we take out double quotes too: Amazon place # this string literally into their XML response, so can end up @@ -65,10 +66,11 @@ for track in gpod.sw_get_tracks(itdb): # name="KeywordSearch"> which is not well formed :-( for term in ["Disk 1", "Disk 2", '12"', '12 "','"','&']: query = query.replace(term,"") - print "Searching for %s: " % query, + print " Searching for %s: " % query try: albums = amazon.searchByKeyword(query, - type="lite",product_line="music") + type="lite", + product_line="music") except amazon.AmazonError, e: print e albums = [] @@ -77,32 +79,26 @@ for track in gpod.sw_get_tracks(itdb): continue album = albums[0] - hdle, filename = tempfile.mkstemp() - i = urllib.urlopen(album.ImageUrlLarge) - open(filename,"w").write(i.read()) - img = Image.open(filename) - if not (img.size[0] > 10 or img.size[1] > 10): - os.unlink(filename) - else: - print "Fetched image for %s, %s" % (track.album,track.artist) - images[(track.album,track.artist)] = filename + try: + image_data = urllib.urlopen(album.ImageUrlLarge).read() + except: + print " Failed to download from %s" % album.ImageUrlLarge + continue + loader = gtk.gdk.PixbufLoader() + loader.write(image_data) + loader.close() + pixbuf = loader.get_pixbuf() + if (pixbuf.get_width() > 10 or pixbuf.get_height() > 10): + print " Fetched image" + images[(track['album'],track['artist'])] = pixbuf try: - r = gpod.itdb_track_set_thumbnails(track,images[(track.album,track.artist)]) - if r != 1: - print "Failed to save image thumbnail to ipod." - else: - print "Added thumbnails for %s, %s" % (track.album,track.artist) + track.set_coverart(images[(track['album'],track['artist'])]) + print " Added thumbnails" except KeyError: - print "No image available for %s, %s" % (track.album,track.artist) - - -print "Writing ipod database..." -gpod.itdb_write(itdb, None) + print " No image available" -print "Cleaning up downloaded images..." -# really, we should do this if any of the real work threw an exception -# too. This is just a demo script :-) -for filename in images.values(): - os.unlink(filename) +print "Saving database" +db.close() +print "Saved db" diff --git a/bindings/python/examples/save_photos.py b/bindings/python/examples/save_photos.py index abf23ed..ec8079b 100755 --- a/bindings/python/examples/save_photos.py +++ b/bindings/python/examples/save_photos.py @@ -24,6 +24,10 @@ import gpod +if not hasattr(gpod.Thumbnail, 'get_pixbuf'): + print 'Sorry, gpod was built without pixbuf support.' + raise SystemExit + photodb = gpod.PhotoDatabase("/mnt/ipod") print photodb @@ -34,6 +38,6 @@ for album in photodb.PhotoAlbums: for thumbnail, n in zip(photo.thumbnails, range(0,len(photo.thumbnails))): print " ", thumbnail - thumbnail.save_image("/tmp/%d-%d.png" % (photo['id'],n)) + thumbnail.get_pixbuf().save("/tmp/%d-%d.png" % (photo['id'],n),"png") photodb.close() diff --git a/bindings/python/gpod.i.in b/bindings/python/gpod.i.in index f8364eb..b7da152 100644 --- a/bindings/python/gpod.i.in +++ b/bindings/python/gpod.i.in @@ -47,6 +47,7 @@ version = '.'.join(map(str, version_info)) %module(docstring=DOCSTRING) gpod %{ +#include "@top_builddir@/config.h" #include "db-artwork-debug.h" #include "db-artwork-parser.h" #include "db-image-parser.h" @@ -55,7 +56,12 @@ version = '.'.join(map(str, version_info)) #include "itdb.h" #include "itdb_device.h" #include "itdb_private.h" +#ifdef HAVE_GDKPIXBUF +#ifdef HAVE_PYGOBJECT #include <gdk-pixbuf/gdk-pixbuf.h> +#include <pygobject.h> +#endif +#endif /* include prototypes for all functions so builds using @@ -77,7 +83,6 @@ PyObject* sw_get_photo(GList *list, gint index); PyObject* sw_get_artwork_thumbnails(Itdb_Artwork *artwork); PyObject* sw_get_photoalbum_members(Itdb_PhotoAlbum *album); PyObject* sw_ipod_device_to_dict(Itdb_Device *device); -PyObject* sw_save_itdb_thumb(Itdb_PhotoDB *itdb, Itdb_Thumb *thumb, const gchar *filename); void sw__track_extra_destroy (PyObject *data); void hash_table_to_pydict(gpointer key, gpointer value, gpointer user_data); void SWIG_init(void); @@ -287,29 +292,19 @@ PyObject* sw_get_photo(GList *list, gint index) { } } - PyObject* sw_save_itdb_thumb(Itdb_PhotoDB *itdb, Itdb_Thumb *thumb, const gchar *filename) { - GdkPixbuf *pixbuf; - - pixbuf = itdb_thumb_get_gdk_pixbuf (itdb->device, thumb); - - if (pixbuf != NULL) { - gdk_pixbuf_save (pixbuf, filename, "png", NULL, NULL); - gdk_pixbuf_unref (pixbuf); - Py_INCREF(Py_True); - return Py_True; - } else { - Py_INCREF(Py_False); - return Py_False; - } - } - %} %init %{ +#ifdef HAVE_GDKPIXBUF +#ifdef HAVE_PYGOBJECT g_type_init (); + init_pygobject (); +#endif +#endif %} %include "gpod_doc.i" +%include "@top_builddir@/config.h" # be nicer to decode these utf8 strings into Unicode objects in the C # layer. Here we are leaving it to the Python side, and just giving @@ -436,6 +431,19 @@ typedef long time_t; typedef int gboolean; typedef int gint; +#ifdef HAVE_GDKPIXBUF +#ifdef HAVE_PYGOBJECT +%typemap(out) gpointer itdb_thumb_get_gdk_pixbuf { + $result = pygobject_new((GObject *)$1); + g_object_unref($1); +} + +%typemap(in) gpointer pixbuf { + $1 = GDK_PIXBUF(pygobject_get($input)); +} +#endif +#endif + #define G_BEGIN_DECLS #define G_END_DECLS @@ -455,6 +463,5 @@ PyObject* sw_get_photo(GList *list, gint index); PyObject* sw_get_photoalbum_members(Itdb_PhotoAlbum *album); PyObject* sw_get_artwork_thumbnails(Itdb_Artwork *artwork); PyObject* sw_ipod_device_to_dict(Itdb_Device *device); -PyObject* sw_save_itdb_thumb(Itdb_PhotoDB *itdb, Itdb_Thumb *thumb, const gchar *filename); %include "@top_srcdir@/src/itdb.h" diff --git a/bindings/python/ipod.py b/bindings/python/ipod.py index 64e7660..d088eba 100644 --- a/bindings/python/ipod.py +++ b/bindings/python/ipod.py @@ -15,6 +15,15 @@ import locale import socket import datetime +if hasattr(gpod, 'HAVE_GDKPIXBUF') and hasattr(gpod, 'HAVE_PYGOBJECT'): + try: + import gtk + pixbuf_support = True + except ImportError: + pixbuf_support = False +else: + pixbuf_support = False + defaultencoding = locale.getpreferredencoding() class DatabaseException(RuntimeError): @@ -364,10 +373,23 @@ class Track: unicode_value = self['userdata']['%s_locale' % key].decode(self['userdata']['charset']) return unicode_value.encode(defaultencoding) - def set_thumbnail(self, filename): + def set_coverart_from_file(self, filename): gpod.itdb_track_set_thumbnails(self._track, filename) self._set_userdata_utf8('thumbnail', filename) + def set_coverart(self, pixbuf): + if pixbuf == None: + gpod.itdb_track_remove_thumbnails(self._track) + elif isinstance(pixbuf, Photo): + raise NotImplemented("Can't set coverart from existing coverart yet") + else: + gpod.itdb_track_set_thumbnails_from_pixbuf(self._track, + pixbuf) + + def get_coverart(self): + return Photo(proxied_photo=self._track.artwork, + ownerdb=self._track.itdb) + def copy_to_ipod(self): """Copy the track to the iPod.""" self['userdata']['sha1_hash'] = gtkpod.sha1_hash(self._userdata_into_default_locale('filename')) @@ -716,7 +738,9 @@ class PhotoDatabase: def __init__(self, mountpoint="/mnt/ipod"): """Create a Photo database object""" self._itdb = gpod.itdb_photodb_parse(mountpoint, None) - + if self._itdb == None: + self._itdb = gpod.itdb_photodb_create(mountpoint) + def __str__(self): return self.__repr__() @@ -727,7 +751,8 @@ class PhotoDatabase: len(self)) def close(self): - pass + gpod.itdb_photodb_write(self._itdb, None) + gpod.itdb_photodb_free(self._itdb) def __len__(self): return gpod.sw_get_list_len(self._itdb.photos) @@ -740,6 +765,33 @@ class PhotoDatabase: index += len(self) return Photo(proxied_photo=gpod.sw_get_photo(self._itdb.photos, index), ownerdb=self) + + def new_PhotoAlbum(self,**kwargs): + """Create a new PhotoAlbum. + """ + album = PhotoAlbum(self, **kwargs) + return album + + def new_Photo(self,**kwargs): + """Create a new Photo. + """ + kwargs['ownerdb'] = self + photo = Photo(**kwargs) + return photo + + def remove(self, item): + """Remove a photo or album from a database. + + item is either a Photo or PhotoAlbum object. + """ + + if isinstance(item, PhotoAlbum): + gpod.itdb_photodb_photoalbum_remove(self._itdb, item._pa, False) + elif isinstance(item, Photo): + gpod.itdb_photodb_remove_photo(self._itdb, None, item._photo) + else: + raise DatabaseException("Unable to remove a %s from database" % type(item)) + def get_device(self): return gpod.sw_ipod_device_to_dict(self._itdb.device) @@ -796,7 +848,14 @@ class PhotoAlbum: if proxied_photoalbum: self._pa = proxied_photoalbum else: - raise NotImplemented("Can't create new Photo Albums yet") + self._pa = gpod.itdb_photodb_photoalbum_create(self._db._itdb, title, pos) + + def add(self, photo): + """Add photo to photo album.""" + gpod.itdb_photodb_photoalbum_add_photo(self._db._itdb, self._pa, photo._photo, -1) + + def remove(self, photo): + gpod.itdb_photodb_remove_photo(self._db._itdb, self._pa, photo._photo) def get_name(self): """Get the name of the photo album.""" @@ -844,10 +903,11 @@ class Photo: def __init__(self, filename=None, proxied_photo=None, ownerdb=None): """Create a Photo object.""" + error = None if filename: - # maybe use itdb_photodb_add_photo ? - raise NotImplemented("Can't create new Photos from files yet") + self._photo = gpod.itdb_photodb_add_photo(ownerdb._itdb, filename, -1, 0, error) + self._database = ownerdb elif proxied_photo: self._photo = proxied_photo self._database = ownerdb @@ -895,8 +955,10 @@ class Photo: raise KeyError('No such key: %s' % item) def get_thumbnails(self): - return [Thumbnail(proxied_thumbnail=t, ownerphoto=self) for t in gpod.sw_get_artwork_thumbnails(self._photo)] - + return [Thumbnail(proxied_thumbnail=t, + ownerobject=self) for t in gpod.sw_get_artwork_thumbnails( + self._photo)] + thumbnails = property(get_thumbnails) class Thumbnail: @@ -904,14 +966,14 @@ class Thumbnail: _proxied_attributes = [k for k in gpod._Itdb_Thumb.__dict__.keys() if not k.startswith("_")] - def __init__(self, proxied_thumbnail=None, ownerphoto=None): + def __init__(self, proxied_thumbnail=None, ownerobject=None): """Create a thumbnail object.""" if not proxied_thumbnail: raise NotImplemented("Can't create new Thumbnails from scratch, create Photos instead") self._thumbnail = proxied_thumbnail - self.__photo = ownerphoto # so the photo doesn't get gc'd + self.__ownerobject = ownerobject def __str__(self): return self.__repr__() @@ -946,7 +1008,14 @@ class Thumbnail: else: raise KeyError('No such key: %s' % item) - def save_image(self,filename): - return gpod.sw_save_itdb_thumb( - self.__photo._database._itdb, - self._thumbnail,filename) + if pixbuf_support: + def get_pixbuf(self): + # this deals with coverart and photo albums + if hasattr(self.__ownerobject._database,"_itdb"): + return gpod.itdb_thumb_get_gdk_pixbuf( + self.__ownerobject._database._itdb.device, + self._thumbnail) + else: + return gpod.itdb_thumb_get_gdk_pixbuf( + self.__ownerobject._database.device, + self._thumbnail) diff --git a/bindings/python/tests/resources/tiny.png b/bindings/python/tests/resources/tiny.png Binary files differnew file mode 100644 index 0000000..0b34c35 --- /dev/null +++ b/bindings/python/tests/resources/tiny.png diff --git a/bindings/python/tests/tests.py b/bindings/python/tests/tests.py index bdae7cb..09c89bf 100644 --- a/bindings/python/tests/tests.py +++ b/bindings/python/tests/tests.py @@ -66,5 +66,109 @@ class TestiPodFunctions(unittest.TestCase): def testVersion(self): self.assertEqual(type(gpod.version_info), types.TupleType) + +class TestPhotoDatabase(unittest.TestCase): + def setUp(self): + self.mp = tempfile.mkdtemp() + control_dir = os.path.join(self.mp,'iPod_Control') + photo_dir = os.path.join(control_dir, 'Photos') + shutil.copytree('resources', + control_dir) + os.mkdir(photo_dir) + self.db = gpod.PhotoDatabase(self.mp) + gpod.itdb_device_set_sysinfo (self.db._itdb.device, "ModelNumStr", "MA450"); + + def tearDown(self): + shutil.rmtree(self.mp) + + def testClose(self): + self.db.close() + + def testAddPhotoAlbum(self): + """ Test adding 5 photo albums to the database """ + for i in range(0, 5): + count = len(self.db.PhotoAlbums) + album = self.db.new_PhotoAlbum(title="Test %s" % i) + self.failUnless(len(self.db.PhotoAlbums) == (count + 1)) + + def testAddRemovePhotoAlbum(self): + """ Test removing all albums but "Photo Library" """ + self.testAddPhotoAlbum() + pas = [x for x in self.db.PhotoAlbums if x.name != "Photo Library"] + for pa in pas: + self.db.remove(pa) + self.assertEqual(len(self.db.PhotoAlbums), 1) + + def testRenamePhotoAlbum(self): + bad = [] + good = [] + + self.testAddPhotoAlbum() + pas = [x for x in self.db.PhotoAlbums if x.name != "Photo Library"] + for pa in pas: + bad.append(pa.name) + pa.name = "%s (renamed)" % pa.name + good.append(pa.name) + + pas = [x for x in self.db.PhotoAlbums if x.name != "Photo Library"] + for pa in pas: + self.failUnless(pa.name in bad) + self.failUnless(pa.name not in good) + + def testEnumeratePhotoAlbums(self): + [photo for photo in self.db.PhotoAlbums] + + def testAddPhoto(self): + photoname = os.path.join(self.mp, + 'iPod_Control', + 'tiny.png') + self.failUnless(os.path.exists(photoname)) + for n in range(1,5): + t = self.db.new_Photo(filename=photoname) + self.assertEqual(len(self.db), n) + + def testAddPhotoToAlbum(self): + self.testAddPhoto() + pa = self.db.new_PhotoAlbum(title="Add To Album Test") + count = len(pa) + for p in self.db.PhotoAlbums[0]: + pa.add(p) + self.assertEqual(len(pa), len(self.db.PhotoAlbums[0])) + self.failUnless(len(pa) > count) + + def testRemovePhotoFromAlbum(self): + self.testAddPhotoToAlbum() + pa = self.db.PhotoAlbums[1] + for p in pa[:]: + pa.remove(p) + # make sure we didn't delete the photo + self.failUnless(len(self.db.PhotoAlbums[0]) > 0) + # but that we did remove them from album + self.assertEqual(len(pa), 0) + + def testAddRemovePhoto(self): + self.testAddPhoto() + self.failUnless(len(self.db) > 0) + for photo in self.db.PhotoAlbums[0][:]: + self.db.remove(photo) + self.assertEqual(len(self.db), 0) + + def testAddCountPhotos(self): + count = len(self.db) + self.testAddPhoto() + self.failUnless(len(self.db) > count) + + def testEnumeratePhotoAlbums(self): + [photo for photo in self.db.PhotoAlbums] + + def testEnumeratePhotos(self): + for album in self.db.PhotoAlbums: + [photo for photo in album] + + def testEnumeratePhotosThumbs(self): + for album in self.db.PhotoAlbums: + for photo in album: + [thumb for thumb in photo.thumbnails] + if __name__ == '__main__': unittest.main() |