1
0
mirror of https://github.com/gryf/pygtktalog.git synced 2025-12-17 11:30:19 +01:00

Added basic Python3 support

This commit is contained in:
2019-02-24 13:16:23 +01:00
parent 28a99b0470
commit 07690f9c94
9 changed files with 50 additions and 60 deletions

View File

@@ -44,15 +44,14 @@ Requirements
pyGTKtalog requires python and following libraries: pyGTKtalog requires python and following libraries:
* `python 2.7`_ * `python 3`_, tested on python 3.6
* `sqlalchemy 1.0`_ * `sqlalchemy 1.2`_ or higher
* `pygtk 2.24`_ (only for ``gtktalog.py``) * `pygtk 2.24`_ (only for ``gtktalog.py``, will not work with python3)
* `pillow`_ for image manipulation * `pillow`_ for image manipulation
* `exifread`_ for parse EXIF information * `exifread`_ for parse EXIF information
It may work on other (lower) version of libraries, and it should work with It may work on other (lower) version of libraries, and it should work with
higher versions of libraries, although it will not work on Python 3 yet, nor higher versions of libraries. GTK3 support will follow.
GTK3.
pyGTKtalog extensively uses external programs in unix spirit, however there is pyGTKtalog extensively uses external programs in unix spirit, however there is
small possibility of using it Windows (probably with limitations) and quite big small possibility of using it Windows (probably with limitations) and quite big
@@ -148,7 +147,7 @@ file in top-level directory.
.. _paver: https://pythonhosted.org/paver/ .. _paver: https://pythonhosted.org/paver/
.. _pillow: https://python-pillow.org/ .. _pillow: https://python-pillow.org/
.. _pygtk 2.24: http://www.pygtk.org .. _pygtk 2.24: http://www.pygtk.org
.. _python 2.7: http://www.python.org/ .. _python 3: http://www.python.org/
.. _sqlalchemy 1.0: http://www.sqlalchemy.org .. _sqlalchemy 1.2: http://www.sqlalchemy.org
.. _tagging files: http://en.wikipedia.org/wiki/tag_%28metadata%29 .. _tagging files: http://en.wikipedia.org/wiki/tag_%28metadata%29
.. _tox: https://testrun.org/tox .. _tox: https://testrun.org/tox

View File

@@ -6,19 +6,15 @@
Created: 2009-05-05 Created: 2009-05-05
""" """
__version__ = "2.0.0" __version__ = "3.0.0"
__appname__ = "pyGTKtalog" __appname__ = "pyGTKtalog"
__copyright__ = u"\u00A9 Roman 'gryf' Dobosz" __copyright__ = "\u00A9 Roman 'gryf' Dobosz"
__summary__ = "%s is simple tool for managing file collections." % __appname__ __summary__ = "%s is simple tool for managing file collections." % __appname__
__web__ = "http://github.com/gryf/pygtktalog" __web__ = "http://github.com/gryf/pygtktalog"
import os import os
import sys
import locale import locale
import gettext import gettext
import __builtin__
from pygtktalog.logger import get_logger
__all__ = ['dbcommon', __all__ = ['dbcommon',
@@ -49,10 +45,10 @@ except locale.Error:
# module.textdomain(GETTEXT_DOMAIN) # module.textdomain(GETTEXT_DOMAIN)
# register the gettext function for the whole interpreter as "_" # register the gettext function for the whole interpreter as "_"
__builtin__._ = gettext.gettext _ = gettext.gettext
# wrap errors into usefull message # # wrap errors into usefull message
#def log_exception(exc_type, exc_val, traceback): # def log_exception(exc_type, exc_val, traceback):
# get_logger(__name__).error(exc_val) # get_logger(__name__).error(exc_val)
# #
#sys.excepthook = log_exception # sys.excepthook = log_exception

View File

@@ -63,8 +63,7 @@ class File(Base):
self.source = src self.source = src
def __repr__(self): def __repr__(self):
return "<File('%s', %s)>" % (self.filename.encode('utf-8'), return "<File('%s', %s)>" % (self.filename, str(self.id))
str(self.id))
def get_all_children(self): def get_all_children(self):
""" """

View File

@@ -36,7 +36,7 @@ def cprint(txt, color):
"magenta": MAGENTA, "magenta": MAGENTA,
"cyan": CYAN, "cyan": CYAN,
"white": WHITE} "white": WHITE}
print COLOR_SEQ % (30 + color_map[color]) + txt + RESET_SEQ print(COLOR_SEQ % (30 + color_map[color]) + txt + RESET_SEQ)
class DummyFormater(logging.Formatter): class DummyFormater(logging.Formatter):

View File

@@ -50,7 +50,7 @@ def calculate_image_path(dbpath=None, create=False):
if not os.path.exists(images_dir): if not os.path.exists(images_dir):
try: try:
os.mkdir(images_dir) os.mkdir(images_dir)
except OSError, err: except OSError as err:
if err.errno != errno.EEXIST: if err.errno != errno.EEXIST:
raise raise
elif not os.path.exists(images_dir): elif not os.path.exists(images_dir):
@@ -60,7 +60,7 @@ def calculate_image_path(dbpath=None, create=False):
def mk_paths(fname, img_path): def mk_paths(fname, img_path):
"""Make path for provided pathname by calculating crc32 out of file""" """Make path for provided pathname by calculating crc32 out of file"""
with open(fname) as fobj: with open(fname, 'r+b') as fobj:
new_path = "%x" % (crc32(fobj.read(10*1024*1024)) & 0xffffffff) new_path = "%x" % (crc32(fobj.read(10*1024*1024)) & 0xffffffff)
new_path = [new_path[i:i + 2] for i in range(0, len(new_path), 2)] new_path = [new_path[i:i + 2] for i in range(0, len(new_path), 2)]

View File

@@ -155,7 +155,7 @@ class Scan(object):
# in case of such, better get me a byte string. It is not perfect # in case of such, better get me a byte string. It is not perfect
# though, since it WILL crash if the update_path would contain some # though, since it WILL crash if the update_path would contain some
# unconvertable characters. # unconvertable characters.
update_path = update_path.encode("utf-8") update_path = update_path
# refresh objects # refresh objects
LOG.debug("Refreshing objects") LOG.debug("Refreshing objects")
@@ -199,8 +199,7 @@ class Scan(object):
'.ogm': 'video', '.ogm': 'video',
'.ogv': 'video'} '.ogv': 'video'}
fp = os.path.join(fobj.filepath.encode(sys.getfilesystemencoding()), fp = os.path.join(fobj.filepath, fobj.filename)
fobj.filename.encode(sys.getfilesystemencoding()))
mimeinfo = mimetypes.guess_type(fp) mimeinfo = mimetypes.guess_type(fp)
if mimeinfo[0]: if mimeinfo[0]:
@@ -287,13 +286,8 @@ class Scan(object):
""" """
fullpath = os.path.join(path, fname) fullpath = os.path.join(path, fname)
fname = fname.decode(sys.getfilesystemencoding(),
errors="replace")
path = path.decode(sys.getfilesystemencoding(),
errors="replace")
if ftype == TYPE['link']: if ftype == TYPE['link']:
fname = fname + " -> " + os.readlink(fullpath).decode('utf-8') fname = fname + " -> " + os.readlink(fullpath)
fob = {'filename': fname, fob = {'filename': fname,
'path': path, 'path': path,
@@ -378,7 +372,7 @@ class Scan(object):
LOG.info("Scanning `%s' [%s/%s]", fullpath, self.current_count, LOG.info("Scanning `%s' [%s/%s]", fullpath, self.current_count,
self.files_count) self.files_count)
root, dirs, files = os.walk(fullpath).next() root, dirs, files = next(os.walk(fullpath))
for fname in files: for fname in files:
fpath = os.path.join(root, fname) fpath = os.path.join(root, fname)
extension = os.path.splitext(fname)[1] extension = os.path.splitext(fname)[1]

View File

@@ -92,10 +92,10 @@ class Video(object):
if scale < 1: if scale < 1:
return None return None
no_pictures = self.tags['length'] / scale no_pictures = self.tags['length'] // scale
if no_pictures > 8: if no_pictures > 8:
no_pictures = (no_pictures / 8) * 8 # only multiple of 8, please. no_pictures = (no_pictures // 8) * 8 # only multiple of 8, please.
else: else:
# for really short movies # for really short movies
no_pictures = 4 no_pictures = 4
@@ -113,16 +113,16 @@ class Video(object):
""" """
Return formatted tags as a string Return formatted tags as a string
""" """
out_tags = u'' out_tags = ''
if 'container' in self.tags: if 'container' in self.tags:
out_tags += u"Container: %s\n" % self.tags['container'] out_tags += "Container: %s\n" % self.tags['container']
if 'width' in self.tags and 'height' in self.tags: if 'width' in self.tags and 'height' in self.tags:
out_tags += u"Resolution: %sx%s\n" % (self.tags['width'], out_tags += "Resolution: %sx%s\n" % (self.tags['width'],
self.tags['height']) self.tags['height'])
if 'duration' in self.tags: if 'duration' in self.tags:
out_tags += u"Duration: %s\n" % self.tags['duration'] out_tags += "Duration: %s\n" % self.tags['duration']
if 'video_codec' in self.tags: if 'video_codec' in self.tags:
out_tags += "Video codec: %s\n" % self.tags['video_codec'] out_tags += "Video codec: %s\n" % self.tags['video_codec']
@@ -178,7 +178,7 @@ class Video(object):
@directory - full output directory name @directory - full output directory name
@no_pictures - number of pictures to take @no_pictures - number of pictures to take
""" """
step = float(self.tags['length'] / (no_pictures + 1)) step = self.tags['length'] / (no_pictures + 1)
current_time = 0 current_time = 0
for dummy in range(1, no_pictures + 1): for dummy in range(1, no_pictures + 1):
current_time += step current_time += step
@@ -191,7 +191,8 @@ class Video(object):
try: try:
shutil.move(os.path.join(directory, "00000001.jpg"), shutil.move(os.path.join(directory, "00000001.jpg"),
os.path.join(directory, "picture_%s.jpg" % time)) os.path.join(directory, "picture_%s.jpg" % time))
except IOError, (errno, strerror): except IOError as exc:
errno, strerror = exc.args
LOG.error('error capturing file from movie "%s" at position ' LOG.error('error capturing file from movie "%s" at position '
'%s. Errors: %s, %s', self.filename, time, errno, '%s. Errors: %s, %s', self.filename, time, errno,
strerror) strerror)
@@ -234,7 +235,7 @@ class Video(object):
img_list = [Image.open(os.path.join(directory, fn)).resize(dim) \ img_list = [Image.open(os.path.join(directory, fn)).resize(dim) \
for fn in ifn_list] for fn in ifn_list]
rows = no_pictures / row_length rows = no_pictures // row_length
cols = row_length cols = row_length
isize = (cols * dim[0] + cols + 1, isize = (cols * dim[0] + cols + 1,
rows * dim[1] + rows + 1) rows * dim[1] + rows + 1)

View File

@@ -40,7 +40,7 @@ def colorize(txt, color):
def asserdb(func): def asserdb(func):
def wrapper(args): def wrapper(args):
if not os.path.exists(args.db): if not os.path.exists(args.db):
print colorize("File `%s' does not exists!" % args.db, 'red') print(colorize("File `%s' does not exists!" % args.db, 'red'))
sys.exit(1) sys.exit(1)
func(args) func(args)
return wrapper return wrapper
@@ -151,7 +151,7 @@ class Iface(object):
node = self.root node = self.root
msg = "Content of path `/':" msg = "Content of path `/':"
print colorize(msg, 'white') print(colorize(msg, 'white'))
if recursive: if recursive:
items = self._walk(node) items = self._walk(node)
@@ -168,7 +168,7 @@ class Iface(object):
else: else:
filenames = sorted(items.keys()) filenames = sorted(items.keys())
print '\n'.join(filenames) print('\n'.join(filenames))
def update(self, path, dir_to_update=None): def update(self, path, dir_to_update=None):
""" """
@@ -179,8 +179,8 @@ class Iface(object):
self.root = self.root.filter(dbo.File.type == dbo.TYPE['root']).first() self.root = self.root.filter(dbo.File.type == dbo.TYPE['root']).first()
node = self._resolve_path(path) node = self._resolve_path(path)
if node == self.root: if node == self.root:
print colorize('Cannot update entire db, since root was provided ' print(colorize('Cannot update entire db, since root was provided '
'as path.', 'red') 'as path.', 'red'))
return return
if not dir_to_update: if not dir_to_update:
@@ -189,8 +189,8 @@ class Iface(object):
if not os.path.exists(dir_to_update): if not os.path.exists(dir_to_update):
raise OSError("Path to updtate doesn't exists: %s", dir_to_update) raise OSError("Path to updtate doesn't exists: %s", dir_to_update)
print colorize("Updating node `%s' against directory " print(colorize("Updating node `%s' against directory "
"`%s'" % (path, dir_to_update), 'white') "`%s'" % (path, dir_to_update), 'white'))
if not self.dry_run: if not self.dry_run:
scanob = scan.Scan(dir_to_update) scanob = scan.Scan(dir_to_update)
# scanob.update_files(node.id) # scanob.update_files(node.id)
@@ -215,8 +215,8 @@ class Iface(object):
self.sess.add(config) self.sess.add(config)
self.sess.commit() self.sess.commit()
print colorize("Creating new db against directory `%s'" % dir_to_add, print(colorize("Creating new db against directory `%s'" % dir_to_add,
'white') 'white'))
if not self.dry_run: if not self.dry_run:
if data_dir == ':same_as_db:': if data_dir == ':same_as_db:':
misc.calculate_image_path(None, True) misc.calculate_image_path(None, True)
@@ -234,7 +234,7 @@ class Iface(object):
if not os.path.exists(dir_to_add): if not os.path.exists(dir_to_add):
raise OSError("Path to add doesn't exists: %s", dir_to_add) raise OSError("Path to add doesn't exists: %s", dir_to_add)
print colorize("Adding directory `%s'" % dir_to_add, 'white') print(colorize("Adding directory `%s'" % dir_to_add, 'white'))
if not self.dry_run: if not self.dry_run:
scanob = scan.Scan(dir_to_add) scanob = scan.Scan(dir_to_add)
scanob.add_files() scanob.add_files()
@@ -273,19 +273,19 @@ class Iface(object):
result = [] result = []
for word in search_words: for word in search_words:
phrase = u'%%%s%%' % word.decode('utf-8') phrase = u'%%%s%%' % word
query = query.filter(dbo.File.filename.like(phrase)) query = query.filter(dbo.File.filename.like(phrase))
for item in query.all(): for item in query.all():
result.append(self._get_full_path(item)) result.append(self._get_full_path(item))
if not result: if not result:
print "No results for `%s'" % ' '.join(search_words) print("No results for `%s'" % ' '.join(search_words))
return return
result.sort() result.sort()
for item in result: for item in result:
print self._annotate(item, search_words) print(self._annotate(item, search_words))
def fsck(self): def fsck(self):
"""Fsck orphaned images/thumbs""" """Fsck orphaned images/thumbs"""
@@ -342,9 +342,9 @@ class Iface(object):
sys.stdout.flush() sys.stdout.flush()
if self.dry_run: if self.dry_run:
print "Following files are not associated to any items in the DB:" print("Following files are not associated to any items in the DB:")
for filename in sorted(files_to_remove): for filename in sorted(files_to_remove):
print filename print(filename)
self.sess.rollback() self.sess.rollback()
else: else:
_remove_files(image_path, files_to_remove) _remove_files(image_path, files_to_remove)

View File

@@ -7,7 +7,7 @@ from distutils.core import setup
setup(name='pygtktalog', setup(name='pygtktalog',
packages=['pygtktalog'], packages=['pygtktalog'],
version='2.0', version='3.0',
description='Catalog application with GTK interface', description='Catalog application with GTK interface',
author='Roman Dobosz', author='Roman Dobosz',
author_email='gryf73@gmail.com', author_email='gryf73@gmail.com',
@@ -18,7 +18,8 @@ setup(name='pygtktalog',
scripts=['scripts/cmdcatalog.py'], scripts=['scripts/cmdcatalog.py'],
classifiers=['Programming Language :: Python :: 2', classifiers=['Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 2 :: Only', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Development Status :: 4 - Beta', 'Development Status :: 4 - Beta',
'Environment :: Console', 'Environment :: Console',
'Intended Audience :: End Users/Desktop', 'Intended Audience :: End Users/Desktop',