mirror of
https://github.com/gryf/pygtktalog.git
synced 2025-12-17 03:20:17 +01:00
Added basic Python3 support
This commit is contained in:
13
README.rst
13
README.rst
@@ -44,15 +44,14 @@ Requirements
|
||||
|
||||
pyGTKtalog requires python and following libraries:
|
||||
|
||||
* `python 2.7`_
|
||||
* `sqlalchemy 1.0`_
|
||||
* `pygtk 2.24`_ (only for ``gtktalog.py``)
|
||||
* `python 3`_, tested on python 3.6
|
||||
* `sqlalchemy 1.2`_ or higher
|
||||
* `pygtk 2.24`_ (only for ``gtktalog.py``, will not work with python3)
|
||||
* `pillow`_ for image manipulation
|
||||
* `exifread`_ for parse EXIF information
|
||||
|
||||
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
|
||||
GTK3.
|
||||
higher versions of libraries. GTK3 support will follow.
|
||||
|
||||
pyGTKtalog extensively uses external programs in unix spirit, however there is
|
||||
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/
|
||||
.. _pillow: https://python-pillow.org/
|
||||
.. _pygtk 2.24: http://www.pygtk.org
|
||||
.. _python 2.7: http://www.python.org/
|
||||
.. _sqlalchemy 1.0: http://www.sqlalchemy.org
|
||||
.. _python 3: http://www.python.org/
|
||||
.. _sqlalchemy 1.2: http://www.sqlalchemy.org
|
||||
.. _tagging files: http://en.wikipedia.org/wiki/tag_%28metadata%29
|
||||
.. _tox: https://testrun.org/tox
|
||||
|
||||
@@ -6,19 +6,15 @@
|
||||
Created: 2009-05-05
|
||||
"""
|
||||
|
||||
__version__ = "2.0.0"
|
||||
__version__ = "3.0.0"
|
||||
__appname__ = "pyGTKtalog"
|
||||
__copyright__ = u"\u00A9 Roman 'gryf' Dobosz"
|
||||
__copyright__ = "\u00A9 Roman 'gryf' Dobosz"
|
||||
__summary__ = "%s is simple tool for managing file collections." % __appname__
|
||||
__web__ = "http://github.com/gryf/pygtktalog"
|
||||
|
||||
import os
|
||||
import sys
|
||||
import locale
|
||||
import gettext
|
||||
import __builtin__
|
||||
|
||||
from pygtktalog.logger import get_logger
|
||||
|
||||
|
||||
__all__ = ['dbcommon',
|
||||
@@ -49,9 +45,9 @@ except locale.Error:
|
||||
# module.textdomain(GETTEXT_DOMAIN)
|
||||
|
||||
# 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):
|
||||
# get_logger(__name__).error(exc_val)
|
||||
#
|
||||
|
||||
@@ -63,8 +63,7 @@ class File(Base):
|
||||
self.source = src
|
||||
|
||||
def __repr__(self):
|
||||
return "<File('%s', %s)>" % (self.filename.encode('utf-8'),
|
||||
str(self.id))
|
||||
return "<File('%s', %s)>" % (self.filename, str(self.id))
|
||||
|
||||
def get_all_children(self):
|
||||
"""
|
||||
|
||||
@@ -36,7 +36,7 @@ def cprint(txt, color):
|
||||
"magenta": MAGENTA,
|
||||
"cyan": CYAN,
|
||||
"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):
|
||||
|
||||
@@ -50,7 +50,7 @@ def calculate_image_path(dbpath=None, create=False):
|
||||
if not os.path.exists(images_dir):
|
||||
try:
|
||||
os.mkdir(images_dir)
|
||||
except OSError, err:
|
||||
except OSError as err:
|
||||
if err.errno != errno.EEXIST:
|
||||
raise
|
||||
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):
|
||||
"""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 = [new_path[i:i + 2] for i in range(0, len(new_path), 2)]
|
||||
|
||||
@@ -155,7 +155,7 @@ class Scan(object):
|
||||
# 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
|
||||
# unconvertable characters.
|
||||
update_path = update_path.encode("utf-8")
|
||||
update_path = update_path
|
||||
|
||||
# refresh objects
|
||||
LOG.debug("Refreshing objects")
|
||||
@@ -199,8 +199,7 @@ class Scan(object):
|
||||
'.ogm': 'video',
|
||||
'.ogv': 'video'}
|
||||
|
||||
fp = os.path.join(fobj.filepath.encode(sys.getfilesystemencoding()),
|
||||
fobj.filename.encode(sys.getfilesystemencoding()))
|
||||
fp = os.path.join(fobj.filepath, fobj.filename)
|
||||
|
||||
mimeinfo = mimetypes.guess_type(fp)
|
||||
if mimeinfo[0]:
|
||||
@@ -287,13 +286,8 @@ class Scan(object):
|
||||
"""
|
||||
fullpath = os.path.join(path, fname)
|
||||
|
||||
fname = fname.decode(sys.getfilesystemencoding(),
|
||||
errors="replace")
|
||||
path = path.decode(sys.getfilesystemencoding(),
|
||||
errors="replace")
|
||||
|
||||
if ftype == TYPE['link']:
|
||||
fname = fname + " -> " + os.readlink(fullpath).decode('utf-8')
|
||||
fname = fname + " -> " + os.readlink(fullpath)
|
||||
|
||||
fob = {'filename': fname,
|
||||
'path': path,
|
||||
@@ -378,7 +372,7 @@ class Scan(object):
|
||||
LOG.info("Scanning `%s' [%s/%s]", fullpath, self.current_count,
|
||||
self.files_count)
|
||||
|
||||
root, dirs, files = os.walk(fullpath).next()
|
||||
root, dirs, files = next(os.walk(fullpath))
|
||||
for fname in files:
|
||||
fpath = os.path.join(root, fname)
|
||||
extension = os.path.splitext(fname)[1]
|
||||
|
||||
@@ -92,10 +92,10 @@ class Video(object):
|
||||
if scale < 1:
|
||||
return None
|
||||
|
||||
no_pictures = self.tags['length'] / scale
|
||||
no_pictures = self.tags['length'] // scale
|
||||
|
||||
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:
|
||||
# for really short movies
|
||||
no_pictures = 4
|
||||
@@ -113,16 +113,16 @@ class Video(object):
|
||||
"""
|
||||
Return formatted tags as a string
|
||||
"""
|
||||
out_tags = u''
|
||||
out_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:
|
||||
out_tags += u"Resolution: %sx%s\n" % (self.tags['width'],
|
||||
out_tags += "Resolution: %sx%s\n" % (self.tags['width'],
|
||||
self.tags['height'])
|
||||
|
||||
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:
|
||||
out_tags += "Video codec: %s\n" % self.tags['video_codec']
|
||||
@@ -178,7 +178,7 @@ class Video(object):
|
||||
@directory - full output directory name
|
||||
@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
|
||||
for dummy in range(1, no_pictures + 1):
|
||||
current_time += step
|
||||
@@ -191,7 +191,8 @@ class Video(object):
|
||||
try:
|
||||
shutil.move(os.path.join(directory, "00000001.jpg"),
|
||||
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 '
|
||||
'%s. Errors: %s, %s', self.filename, time, errno,
|
||||
strerror)
|
||||
@@ -234,7 +235,7 @@ class Video(object):
|
||||
img_list = [Image.open(os.path.join(directory, fn)).resize(dim) \
|
||||
for fn in ifn_list]
|
||||
|
||||
rows = no_pictures / row_length
|
||||
rows = no_pictures // row_length
|
||||
cols = row_length
|
||||
isize = (cols * dim[0] + cols + 1,
|
||||
rows * dim[1] + rows + 1)
|
||||
|
||||
@@ -40,7 +40,7 @@ def colorize(txt, color):
|
||||
def asserdb(func):
|
||||
def wrapper(args):
|
||||
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)
|
||||
func(args)
|
||||
return wrapper
|
||||
@@ -151,7 +151,7 @@ class Iface(object):
|
||||
node = self.root
|
||||
msg = "Content of path `/':"
|
||||
|
||||
print colorize(msg, 'white')
|
||||
print(colorize(msg, 'white'))
|
||||
|
||||
if recursive:
|
||||
items = self._walk(node)
|
||||
@@ -168,7 +168,7 @@ class Iface(object):
|
||||
else:
|
||||
filenames = sorted(items.keys())
|
||||
|
||||
print '\n'.join(filenames)
|
||||
print('\n'.join(filenames))
|
||||
|
||||
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()
|
||||
node = self._resolve_path(path)
|
||||
if node == self.root:
|
||||
print colorize('Cannot update entire db, since root was provided '
|
||||
'as path.', 'red')
|
||||
print(colorize('Cannot update entire db, since root was provided '
|
||||
'as path.', 'red'))
|
||||
return
|
||||
|
||||
if not dir_to_update:
|
||||
@@ -189,8 +189,8 @@ class Iface(object):
|
||||
if not os.path.exists(dir_to_update):
|
||||
raise OSError("Path to updtate doesn't exists: %s", dir_to_update)
|
||||
|
||||
print colorize("Updating node `%s' against directory "
|
||||
"`%s'" % (path, dir_to_update), 'white')
|
||||
print(colorize("Updating node `%s' against directory "
|
||||
"`%s'" % (path, dir_to_update), 'white'))
|
||||
if not self.dry_run:
|
||||
scanob = scan.Scan(dir_to_update)
|
||||
# scanob.update_files(node.id)
|
||||
@@ -215,8 +215,8 @@ class Iface(object):
|
||||
self.sess.add(config)
|
||||
self.sess.commit()
|
||||
|
||||
print colorize("Creating new db against directory `%s'" % dir_to_add,
|
||||
'white')
|
||||
print(colorize("Creating new db against directory `%s'" % dir_to_add,
|
||||
'white'))
|
||||
if not self.dry_run:
|
||||
if data_dir == ':same_as_db:':
|
||||
misc.calculate_image_path(None, True)
|
||||
@@ -234,7 +234,7 @@ class Iface(object):
|
||||
if not os.path.exists(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:
|
||||
scanob = scan.Scan(dir_to_add)
|
||||
scanob.add_files()
|
||||
@@ -273,19 +273,19 @@ class Iface(object):
|
||||
result = []
|
||||
|
||||
for word in search_words:
|
||||
phrase = u'%%%s%%' % word.decode('utf-8')
|
||||
phrase = u'%%%s%%' % word
|
||||
query = query.filter(dbo.File.filename.like(phrase))
|
||||
|
||||
for item in query.all():
|
||||
result.append(self._get_full_path(item))
|
||||
|
||||
if not result:
|
||||
print "No results for `%s'" % ' '.join(search_words)
|
||||
print("No results for `%s'" % ' '.join(search_words))
|
||||
return
|
||||
|
||||
result.sort()
|
||||
for item in result:
|
||||
print self._annotate(item, search_words)
|
||||
print(self._annotate(item, search_words))
|
||||
|
||||
def fsck(self):
|
||||
"""Fsck orphaned images/thumbs"""
|
||||
@@ -342,9 +342,9 @@ class Iface(object):
|
||||
sys.stdout.flush()
|
||||
|
||||
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):
|
||||
print filename
|
||||
print(filename)
|
||||
self.sess.rollback()
|
||||
else:
|
||||
_remove_files(image_path, files_to_remove)
|
||||
|
||||
5
setup.py
5
setup.py
@@ -7,7 +7,7 @@ from distutils.core import setup
|
||||
|
||||
setup(name='pygtktalog',
|
||||
packages=['pygtktalog'],
|
||||
version='2.0',
|
||||
version='3.0',
|
||||
description='Catalog application with GTK interface',
|
||||
author='Roman Dobosz',
|
||||
author_email='gryf73@gmail.com',
|
||||
@@ -18,7 +18,8 @@ setup(name='pygtktalog',
|
||||
scripts=['scripts/cmdcatalog.py'],
|
||||
classifiers=['Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 2 :: Only',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Development Status :: 4 - Beta',
|
||||
'Environment :: Console',
|
||||
'Intended Audience :: End Users/Desktop',
|
||||
|
||||
Reference in New Issue
Block a user