[kupfer: 21/27] plugin: +google_picasa - access and upload files to Picasa Web Album



commit c5dde6a96b24ab5842f10a571bbf111c6955cb72
Author: Karol BÄ?dkowski <karol bedkowski gmail com>
Date:   Tue Feb 16 21:25:03 2010 +0100

    plugin: +google_picasa - access and upload files to Picasa Web Album
    
    With this plugin you can:
    - quick access to your albums
    - quick access to albums configured users (picasa api don't support loading
      favorites from picasa)
    - upload files to existing or new albums
    - upload dirs as new albums
    
    Users and albums are updated in background every 30 minutes, and after each
    upload.
    
    + turn on ComposedIconSmall

 kupfer/plugin/google_picasa.py |  437 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 437 insertions(+), 0 deletions(-)
---
diff --git a/kupfer/plugin/google_picasa.py b/kupfer/plugin/google_picasa.py
new file mode 100644
index 0000000..caad27f
--- /dev/null
+++ b/kupfer/plugin/google_picasa.py
@@ -0,0 +1,437 @@
+# -*- coding: UTF-8 -*-
+__kupfer_name__ = _("Google Picasa")
+__kupfer_sources__ = ("PicasaUsersSource", )
+__kupfer_actions__ = ('UploadFileToPicasa', 'UploadDirToPicasa')
+__description__ = _("Show albums and upload files to Picasa.")
+__version__ = "2010-02-04"
+__author__ = "Karol BÄ?dkowski <karol bedkowski gmail com>"
+
+import os.path
+import time
+
+import gdata.service
+import gdata.photos.service
+
+from kupfer.objects import Action, FileLeaf, TextLeaf
+from kupfer.objects import UrlLeaf, Source
+from kupfer.obj.special import PleaseConfigureLeaf, InvalidCredentialsLeaf
+from kupfer import plugin_support, pretty, icons
+from kupfer.ui.progress_dialog import ProgressDialogController
+from kupfer.obj.helplib import background_loader
+from kupfer import kupferstring
+from kupfer import task
+
+__kupfer_settings__ = plugin_support.PluginSettings(
+	{
+		'key': 'userpass',
+		'label': '',
+		'type': plugin_support.UserNamePassword,
+		'value': "",
+	},
+	{
+		'key': 'showusers',
+		'label': _('Users to show: (,-separated)'),
+		'type': str,
+		'value': '',
+	},
+	{
+		'key': 'loadicons',
+		'label': _('Load user and album icons'),
+		'type': bool,
+		'value': True,
+	},
+)
+
+
+UPDATE_DELAY_S = 15
+UPDATE_INTERVAL_S = 30 * 60 # 30 min
+ALBUM_URL = '/data/feed/api/user/%s/albumid/%s'
+USER_URL = 'http://picasaweb.google.com/%(user)s'
+
+
+def is_plugin_configured():
+	upass = __kupfer_settings__['userpass']
+	return bool(upass and upass.username and upass.password)
+
+
+def valid_file(filepath):
+	''' check is file supported by picasa '''
+	extension = os.path.splitext(filepath)[1].lower()
+	return extension in ('.jpg', '.jpeg', '.png', '.gif')
+
+
+class UploadTask(task.ThreadTask):
+	""" Uploading files to picasa """
+
+	def __init__(self):
+		task.ThreadTask.__init__(self)
+		self._files_to_upload = []
+		self._files_albums_count = 0
+
+	def add_files_to_existing_album(self, files, album_id):
+		''' upload files to existing album.
+		@files: list of local files (full path)
+		@album_id: picasa album id
+		'''
+		self._files_to_upload.append((files, album_id, None))
+		self._files_albums_count += len(files)
+
+	def add_files_to_new_album(self, files, album_name):
+		''' create new album and upload files.
+		@files: list of local files (full path)
+		@album_name: new album name 
+		'''
+		self._files_to_upload.append((files, None, album_name))
+		self._files_albums_count += len(files) + 1
+
+	def thread_do(self):
+		gd_client = picasa_login()
+		if not gd_client:
+			return
+		progres_dialog = ProgressDialogController(
+				_('Kupfer - Uploading files'),
+				_("Uploading files and dirs to Picasa Web Album"),
+				max_value=self._files_albums_count)
+		progres_dialog.show()
+		try:
+			upass = __kupfer_settings__['userpass']
+			progress = 0
+			for files, album_id, album_name in self._files_to_upload:
+				# create album
+				if album_id is None:
+					progres_dialog.update(progress, _('<b>Creating album:</b> %s') \
+							% album_name)
+					album = gd_client.InsertAlbum(title=album_name,
+							summary=_('Album created by Kupfer'))
+					album_id = album.gphoto_id.text
+					progress += 1
+				# send files
+				album_url = ALBUM_URL % (upass.username, album_id)
+				for filename in files:
+					pretty.print_debug(__name__, 'upload: sending', filename)
+					progres_dialog.update(progress, _('<b>File:</b> %s') % filename)
+					if progres_dialog.aborted:
+						pretty.print_debug(__name__, 'upload: abort')
+						break
+					# send file
+					gd_client.InsertPhotoSimple(album_url,
+							os.path.basename(filename), '', filename)
+					pretty.print_debug(__name__, 'upload: file sended', filename)
+					progress += 1
+
+		except (gdata.service.Error, gdata.photos.service.GooglePhotosException), \
+				err:
+			pretty.print_error(__name__, 'upload error', err)
+
+		finally:
+			progres_dialog.hide()
+
+	def thread_finish(self):
+		get_albums.activate()
+
+
+def picasa_login():
+	if not is_plugin_configured():
+		return None
+	gd_client = None
+	try:
+		upass = __kupfer_settings__['userpass']
+		gd_client = gdata.photos.service.PhotosService()
+		gd_client.email = upass.username
+		gd_client.password = upass.password
+		gd_client.source = 'kupfer-google_picasa'
+		gd_client.ProgrammaticLogin()
+	except (gdata.service.BadAuthentication, gdata.service.CaptchaRequired), err:
+		pretty.print_error(__name__, 'picasa_login', 'authentication error',
+				err)
+		gd_client = None
+	return gd_client
+
+
+def get_thumb(gd_client, thumb_url):
+	''' Load thumb from web '''
+	thumb = None
+	if thumb_url:
+		thumb_media = gd_client.GetMedia(thumb_url)
+		if thumb_media:
+			thumb = thumb_media.file_handle.read()
+	return thumb
+
+
+def get_user_leaf(gd_client, user_name):
+	''' Create PicasaUser obj for given @user_name. '''
+	leaf = None
+	try:
+		user_info = gd_client.GetContacts(user_name)
+	except gdata.photos.service.GooglePhotosException, err:
+		pretty.print_info(__name__, 'get_uers_leaf', err)
+	else:
+		thumb = None
+		if __kupfer_settings__['loadicons']:
+			thumb = get_thumb(gd_client, user_info.thumbnail.text)
+		user_url = USER_URL % dict(user=user_info.user.text)
+		leaf = PicasaUser(user_url, kupferstring.tounicode(user_info.nickname.text),
+				thumb)
+	return leaf
+
+
+ background_loader(interval=UPDATE_INTERVAL_S, delay=UPDATE_DELAY_S,
+		name='picasa-albums')
+def get_albums():
+	''' Load user albums, and albums users defined in 'showusers' setting. '''
+	pretty.print_debug(__name__, 'get_albums')
+	start_time = time.time()
+	gd_client = picasa_login()
+	if not gd_client:
+		return [InvalidCredentialsLeaf(__name__, __kupfer_name__)]
+
+	pusers = []
+	try:
+		user = __kupfer_settings__['userpass'].username
+		user_names = (__kupfer_settings__['showusers'] or '').split(',')
+		if user not in user_names:
+			user_names.append(user)
+
+		for user_name in user_names:
+			pretty.print_debug(__name__, 'get_albums: get album', user_name)
+			# get user info
+			picasa_user_leaf = get_user_leaf(gd_client, user_name)
+			if picasa_user_leaf is None:
+				continue
+			picasa_user_leaf.my_albums = (user_name == user) # mark my albums
+			# get albums
+			user_albums = []
+			for album in gd_client.GetUserFeed(user=user_name).entry:
+				# get album thumbnail:
+				thumb = None
+				if album.media.thumbnail and __kupfer_settings__['loadicons']:
+					thumb = get_thumb(gd_client, album.media.thumbnail[0].url)
+				name = kupferstring.tounicode(album.title.text)
+				album = PicasaAlbum(album.GetAlternateLink().href,
+						name, album.numphotos.text,
+						album.gphoto_id.text, thumb,
+						kupferstring.tounicode(user_name))
+				user_albums.append(album)
+			picasa_user_leaf.update_albums(user_albums)
+			pusers.append(picasa_user_leaf)
+	except gdata.service.Error, err:
+		pretty.print_error(__name__, 'get_albums', err)
+	pretty.print_debug(__name__, 'get_albums finished', 'loaded: ', len(pusers),
+			str(time.time()-start_time))
+	return pusers
+
+
+def _get_valid_files_in_dir(dir_path):
+	''' get all files acceptable by picasa in given directory '''
+	files = [os.path.join(dir_path, filename)
+			for filename  in os.listdir(dir_path)
+			if valid_file(filename)]
+	return files
+
+
+class PicasaUser(UrlLeaf):
+	''' Leaf represent user from Picasa '''
+	def __init__(self, url, name, thumb=None, albums=None):
+		UrlLeaf.__init__(self, url, name)
+		# list of user albums [PicasaAlbum]
+		self.update_albums(albums)
+		self.thumb = thumb
+		self.my_albums = False
+
+	def update_albums(self, albums):
+		self.albums = albums or []
+		albums_count = len(self.albums)
+		self.description = ngettext("One album", "%(num)d albums",
+			albums_count) % {"num": albums_count}
+
+	def has_content(self):
+		return bool(self.albums)
+
+	def content_source(self, alternate=False):
+		return PicasaAlbumSource(self)
+
+	def get_thumbnail(self, width, height):
+		if self.thumb:
+			return icons.get_pixbuf_from_data(self.thumb, width, height)
+		return UrlLeaf.get_thumbnail(self, width, height)
+
+	def get_gicon(self):
+		return icons.ComposedIconSmall("stock_person", "picasa")
+
+	def get_description(self):
+		return self.description
+
+
+class PicasaAlbum(UrlLeaf):
+	''' Leaf represent single album in Picasa '''
+	def __init__(self, url, name, pict_count, album_id, thumb, user):
+		UrlLeaf.__init__(self, url, name)
+		self.album_id = album_id
+		self.thumb = thumb
+		photos_info = ngettext("one photo", "%(num)s photos",
+				int(pict_count)) % {"num": pict_count}
+		self.description = ': '.join((user, photos_info))
+
+	def get_description(self):
+		return self.description
+
+	def get_thumbnail(self, width, height):
+		if self.thumb:
+			return icons.get_pixbuf_from_data(self.thumb, width, height)
+		return UrlLeaf.get_thumbnail(self, width, height)
+
+	def get_gicon(self):
+		return icons.ComposedIconSmall(self.get_icon_name(), "picasa")
+
+
+class UploadFileToPicasa(Action):
+	''' upload selected files or files from selected dirs into existing
+		album or new album (by enter new name) '''
+	def __init__(self):
+		Action.__init__(self, _('Upload to Picasa Album...'))
+
+	def activate(self, obj, iobj):
+		return self.activate_multiple((obj, ), (iobj, ))
+
+	def activate_multiple(self, objects, iobjects):
+		utask = UploadTask()
+		files = []
+		for obj in objects:
+			if obj.is_dir():
+				files.extend(_get_valid_files_in_dir(obj.object))
+			else:
+				files.append(obj.object)
+		for iobj in iobjects:
+			if isinstance(iobj, PicasaAlbum):
+				utask.add_files_to_existing_album(files, iobj.album_id)
+			else:
+				utask.add_files_to_new_album(files, iobj.object)
+		return utask
+
+	def is_async(self):
+		return True
+
+	def get_icon_name(self):
+		return "document-save"
+
+	def item_types(self):
+		yield FileLeaf
+
+	def valid_for_item(self, item):
+		return (valid_file(item.object) or item.is_dir()) \
+				and is_plugin_configured()
+
+	def requires_object(self):
+		return True
+
+	def object_types(self):
+		yield PicasaAlbum
+		yield TextLeaf
+
+	def object_source(self, for_item=None):
+		return PicasaPrivAlbumsSource()
+
+	def get_description(self):
+		return _("Upload files to Picasa album")
+
+
+class UploadDirToPicasa(Action):
+	''' Upload whole directories as new albums '''
+	def __init__(self):
+		Action.__init__(self, _('Upload to Picasa as New Album'))
+
+	def activate(self, obj):
+		return self.activate_multiple((obj, ))
+
+	def activate_multiple(self, objects):
+		utask = UploadTask()
+		for obj in objects:
+			dir_path = obj.object
+			files_to_upload = _get_valid_files_in_dir(dir_path)
+			if files_to_upload:
+				album_name = os.path.basename(dir_path)
+				utask.add_files_to_new_album(files_to_upload, album_name)
+		return utask
+
+	def is_async(self):
+		return True
+
+	def get_icon_name(self):
+		return "document-save"
+
+	def item_types(self):
+		yield FileLeaf
+
+	def valid_for_item(self, item):
+		return item.is_dir() and is_plugin_configured()
+
+	def get_description(self):
+		return _("Create album from selected local directory")
+
+
+class PicasaPrivAlbumsSource(Source):
+	def __init__(self, name=_("Picasa Albums")):
+		Source.__init__(self, name)
+
+	def get_items(self):
+		if is_plugin_configured():
+			for user in get_albums() or []:
+				if user.my_albums:
+					return user.albums
+		return []
+
+	def should_sort_lexically(self):
+		return True
+
+	def provides(self):
+		yield PicasaAlbum
+
+
+class PicasaUsersSource(Source):
+	def __init__(self, name=_("Picasa Albums")):
+		Source.__init__(self, name)
+		self._version = 2
+
+	def initialize(self):
+		get_albums.fill_cache(self.cached_items)
+		get_albums.bind_and_start(self.mark_for_update)
+
+	def get_items(self):
+		if is_plugin_configured():
+			return get_albums() or []
+		return [PleaseConfigureLeaf(__name__, __kupfer_name__)]
+
+	def should_sort_lexically(self):
+		return True
+
+	def provides(self):
+		yield PicasaUser
+		yield PleaseConfigureLeaf
+
+	def get_description(self):
+		return _("User albums in Picasa")
+
+	def get_icon_name(self):
+		return "picasa"
+
+
+class PicasaAlbumSource(Source):
+	""" Source return albums for given user"""
+	def __init__(self, picasa_user, name=_("Albums")):
+		Source.__init__(self, name)
+		self.picasa_user = picasa_user
+
+	def get_items(self):
+		return self.picasa_user.albums
+
+	def should_sort_lexically(self):
+		return True
+
+	def provides(self):
+		yield PicasaAlbum
+
+	def has_parent(self):
+		return True
+
+	def get_parent(self):
+		return self.picasa_user



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]