mirror of
https://github.com/gryf/pygtktalog.git
synced 2026-03-26 22:03:30 +01:00
Compare commits
4 Commits
pyGTKtalog
...
3141add678
| Author | SHA1 | Date | |
|---|---|---|---|
| 3141add678 | |||
| dadeebe8a1 | |||
| 6c6f01781a | |||
| 07690f9c94 |
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
|
||||
|
||||
228
pavement.py
228
pavement.py
@@ -1,228 +0,0 @@
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Makefile and setup.py replacement. Used python packages -
|
||||
paver, nosetests. External commands - xgettext, intltool-extract, hg,
|
||||
grep.
|
||||
Type: management
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2009-05-07
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
|
||||
from paver.easy import sh, dry, call_task, options, Bunch
|
||||
from paver.tasks import task, needs, help, cmdopts
|
||||
from paver.setuputils import setup
|
||||
from paver.misctasks import generate_setup, minilib
|
||||
import paver.doctools
|
||||
|
||||
try:
|
||||
from pylint import lint
|
||||
HAVE_LINT = True
|
||||
except ImportError:
|
||||
HAVE_LINT = False
|
||||
|
||||
PO_HEADER = """#
|
||||
# pygtktalog Language File
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: pygtktalog\\n"
|
||||
"POT-Creation-Date: %(time)s\\n"
|
||||
"Last-Translator: Roman Dobosz<gryf73@gmail.com>\\n"
|
||||
"MIME-Version: 1.0\\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\\n"
|
||||
"Content-Transfer-Encoding: utf-8\\n"
|
||||
"""
|
||||
|
||||
REV = os.popen("hg sum 2>/dev/null|grep ^Revis|cut -d ' ' -f 2").readlines()
|
||||
if REV:
|
||||
REV = "r" + REV[0].strip()
|
||||
else:
|
||||
REV = '0'
|
||||
|
||||
LOCALES = {'pl': 'pl_PL.utf8', 'en': 'en_EN'}
|
||||
POTFILE = 'locale/pygtktalog.pot'
|
||||
|
||||
|
||||
# distutil/setuptool setup method.
|
||||
setup(
|
||||
name='pyGTKtalog',
|
||||
version='1.99.%s' % REV,
|
||||
|
||||
long_description='pyGTKtalog is application similar to WhereIsIT, '
|
||||
'for collecting information on files from CD/DVD '
|
||||
'or directories.',
|
||||
description='pyGTKtalog is a file indexing tool written in pyGTK',
|
||||
author='Roman Dobosz',
|
||||
author_email='gryf73@gmail.com',
|
||||
url='http://google.com',
|
||||
platforms=['Linux', 'BSD'],
|
||||
license='GNU General Public License (GPL)',
|
||||
classifiers=['Development Status :: 2 - Pre-Alpha',
|
||||
'Environment :: X11 Applications :: GTK',
|
||||
'Intended Audience :: End Users/Desktop',
|
||||
'License :: OSI Approved :: GNU General Public License '
|
||||
'(GPL)',
|
||||
'Natural Language :: English',
|
||||
'Natural Language :: Polish',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Desktop Environment',
|
||||
'Topic :: Utilities'],
|
||||
|
||||
include_package_data=True,
|
||||
exclude_package_data={'': ['*.patch']},
|
||||
packages=["pygtktalog"],
|
||||
scripts=['bin/gtktalog.py'],
|
||||
test_suite='nose.collector'
|
||||
)
|
||||
|
||||
options(sphinx=Bunch(builddir="build", sourcedir="source"))
|
||||
|
||||
|
||||
@task
|
||||
@needs(['locale_gen', 'minilib', 'generate_setup'])
|
||||
def sdist():
|
||||
"""sdist with message catalogs"""
|
||||
call_task("setuptools.command.sdist")
|
||||
|
||||
|
||||
@task
|
||||
@needs(['locale_gen'])
|
||||
def build():
|
||||
"""build with message catalogs"""
|
||||
call_task("setuptools.command.build")
|
||||
|
||||
|
||||
@task
|
||||
def clean():
|
||||
"""remove 'pyo', 'pyc', 'h' and '~' files"""
|
||||
# clean *.pyc, *.pyo and jEdit backup files *~
|
||||
for root, dummy, files in os.walk("."):
|
||||
for fname in files:
|
||||
if fname.endswith(".pyc") or fname.endswith(".pyo") or \
|
||||
fname.endswith("~") or fname.endswith(".h") or \
|
||||
fname == '.coverage':
|
||||
fdel = os.path.join(root, fname)
|
||||
os.unlink(fdel)
|
||||
print "deleted", fdel
|
||||
|
||||
|
||||
@task
|
||||
@needs(["clean"])
|
||||
def distclean():
|
||||
"""make clean, and remove any dist/build/egg stuff from project"""
|
||||
for dirname in [os.path.join('pygtktalog', 'locale'), 'build', 'dist',
|
||||
'pyGTKtalog.egg-info']:
|
||||
if os.path.exists(dirname):
|
||||
shutil.rmtree(dirname, ignore_errors=True)
|
||||
print "removed directory", dirname
|
||||
|
||||
for filename in ['paver-minilib.zip', 'setup.py', 'tests/.coverage']:
|
||||
if os.path.exists(filename):
|
||||
os.unlink(filename)
|
||||
print "deleted", filename
|
||||
|
||||
|
||||
@task
|
||||
def run():
|
||||
"""run application"""
|
||||
sh("PYTHONPATH=%s:$PYTHONPATH bin/gtktalog.py" % _setup_env())
|
||||
#import gtktalog
|
||||
#gtktalog.run()
|
||||
|
||||
|
||||
@task
|
||||
def pot():
|
||||
"""generate 'pot' file out of python/glade files"""
|
||||
if not os.path.exists('locale'):
|
||||
os.mkdir('locale')
|
||||
|
||||
if not os.path.exists(POTFILE):
|
||||
fname = open(POTFILE, "w")
|
||||
fname.write(PO_HEADER % {'time': datetime.now()})
|
||||
|
||||
cmd = "xgettext --omit-header -k_ -kN_ -j -o %s %s"
|
||||
cmd_glade = "intltool-extract --type=gettext/glade %s"
|
||||
|
||||
for walk_tuple in os.walk("pygtktalog"):
|
||||
root = walk_tuple[0]
|
||||
for fname in walk_tuple[2]:
|
||||
if fname.endswith(".py"):
|
||||
sh(cmd % (POTFILE, os.path.join(root, fname)))
|
||||
elif fname.endswith(".glade"):
|
||||
sh(cmd_glade % os.path.join(root, fname))
|
||||
sh(cmd % (POTFILE, os.path.join(root, fname + ".h")))
|
||||
|
||||
|
||||
@task
|
||||
@needs(['pot'])
|
||||
def locale_merge():
|
||||
"""create or merge if exists 'po' translation files"""
|
||||
potfile = os.path.join('locale', 'pygtktalog.pot')
|
||||
|
||||
for lang in LOCALES:
|
||||
msg_catalog = os.path.join('locale', "%s.po" % lang)
|
||||
if os.path.exists(msg_catalog):
|
||||
sh('msgmerge -U %s %s' % (msg_catalog, potfile))
|
||||
else:
|
||||
shutil.copy(potfile, msg_catalog)
|
||||
|
||||
|
||||
@task
|
||||
@needs(['locale_merge'])
|
||||
def locale_gen():
|
||||
"""generate message catalog file for available locale files"""
|
||||
full_path = os.path.join('pygtktalog', 'locale')
|
||||
if not os.path.exists(full_path):
|
||||
os.mkdir(full_path)
|
||||
|
||||
for lang in LOCALES:
|
||||
lang_path = full_path
|
||||
for dirname in [lang, 'LC_MESSAGES']:
|
||||
lang_path = os.path.join(lang_path, dirname)
|
||||
if not os.path.exists(lang_path):
|
||||
os.mkdir(lang_path)
|
||||
catalog_file = os.path.join(lang_path, 'pygtktalog.mo')
|
||||
msg_catalog = os.path.join('locale', "%s.po" % lang)
|
||||
sh('msgfmt %s -o %s' % (msg_catalog, catalog_file))
|
||||
|
||||
|
||||
if HAVE_LINT:
|
||||
@task
|
||||
def pylint():
|
||||
'''Check the module you're building with pylint.'''
|
||||
pylintopts = ['pygtktalog']
|
||||
dry('pylint %s' % (" ".join(pylintopts)), lint.Run, pylintopts)
|
||||
|
||||
|
||||
@task
|
||||
@cmdopts([('coverage', 'c', 'display coverage information')])
|
||||
def test(options):
|
||||
"""run unit tests"""
|
||||
cmd = "PYTHONPATH=%s:$PYTHONPATH nosetests -w tests" % _setup_env()
|
||||
if hasattr(options.test, 'coverage'):
|
||||
cmd += " --with-coverage --cover-package pygtktalog"
|
||||
os.system(cmd)
|
||||
|
||||
|
||||
@task
|
||||
@needs(['locale_gen'])
|
||||
def runpl():
|
||||
"""run application with pl_PL localization. This is just for my
|
||||
convenience"""
|
||||
os.environ['LC_ALL'] = 'pl_PL.utf8'
|
||||
run()
|
||||
|
||||
|
||||
def _setup_env():
|
||||
"""Helper function to set up paths"""
|
||||
# current directory
|
||||
this_path = os.path.dirname(os.path.abspath(__file__))
|
||||
if this_path not in sys.path:
|
||||
sys.path.insert(0, this_path)
|
||||
|
||||
return this_path
|
||||
@@ -5,54 +5,14 @@
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
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',
|
||||
'dbobjects',
|
||||
'dialogs',
|
||||
'logger',
|
||||
'misc']
|
||||
|
||||
GETTEXT_DOMAIN = 'pygtktalog'
|
||||
# There should be message catalogs in "locale" directory placed by setup.py
|
||||
# script. If there is no such directory, let's assume that message catalogs are
|
||||
# placed in system wide location such as /usr/share/locale by Linux
|
||||
# distribution package maintainer.
|
||||
LOCALE_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)),
|
||||
'locale')
|
||||
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
except locale.Error:
|
||||
# unknown locale string, fallback to C
|
||||
locale.setlocale(locale.LC_ALL, 'C')
|
||||
|
||||
# for module in gtk.glade, gettext:
|
||||
# if os.path.exists(LOCALE_PATH):
|
||||
# module.bindtextdomain(GETTEXT_DOMAIN, LOCALE_PATH)
|
||||
# else:
|
||||
# module.bindtextdomain(GETTEXT_DOMAIN)
|
||||
# module.textdomain(GETTEXT_DOMAIN)
|
||||
|
||||
# register the gettext function for the whole interpreter as "_"
|
||||
__builtin__._ = gettext.gettext
|
||||
|
||||
# wrap errors into usefull message
|
||||
#def log_exception(exc_type, exc_val, traceback):
|
||||
# get_logger(__name__).error(exc_val)
|
||||
#
|
||||
#sys.excepthook = log_exception
|
||||
|
||||
@@ -12,11 +12,6 @@ from sqlalchemy.ext.declarative import declarative_base
|
||||
from pygtktalog.logger import get_logger
|
||||
|
||||
|
||||
# setup SQLAlchemy logging facility
|
||||
# TODO: Logger("sqlalchemy")
|
||||
# or maybe it will be better to separate sqlalchemy stuff from application
|
||||
#get_logger("sqlalchemy", 'INFO')
|
||||
|
||||
# Prepare SQLAlchemy objects
|
||||
Meta = MetaData()
|
||||
Base = declarative_base(metadata=Meta)
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
@@ -149,8 +148,8 @@ class Thumbnail(Base):
|
||||
if not os.path.exists(os.path.join(img_path, *new_name)):
|
||||
shutil.move(thumb, os.path.join(img_path, *new_name))
|
||||
else:
|
||||
LOG.info("Thumbnail already exists (%s: %s)" % \
|
||||
(fname, "/".join(new_name)))
|
||||
LOG.info("Thumbnail already exists (%s: %s)",
|
||||
fname, "/".join(new_name))
|
||||
os.unlink(thumb)
|
||||
|
||||
def __repr__(self):
|
||||
@@ -203,7 +202,6 @@ class Image(Base):
|
||||
else:
|
||||
LOG.info("Thumbnail already generated %s" % "/".join(new_name))
|
||||
|
||||
|
||||
def get_copy(self):
|
||||
"""
|
||||
Create the very same object as self with exception of id field
|
||||
|
||||
@@ -138,21 +138,30 @@ class MainWindow(object):
|
||||
def _setup_menu_toolbar(self, vbox):
|
||||
"""Create menu/toolbar using uimanager."""
|
||||
actions = [('File', None, '_File'),
|
||||
('New', gtk.STOCK_NEW, '_New', None, 'Create new catalog', self.on_new),
|
||||
('Open', gtk.STOCK_OPEN, '_Open', None, 'Open catalog file', self.on_open),
|
||||
('Save', gtk.STOCK_SAVE, '_Save', None, 'Save catalog file', self.on_save),
|
||||
('Save As', gtk.STOCK_SAVE_AS, '_Save As', None, None, self.on_save),
|
||||
('New', gtk.STOCK_NEW, '_New', None,
|
||||
'Create new catalog', self.on_new),
|
||||
('Open', gtk.STOCK_OPEN, '_Open', None,
|
||||
'Open catalog file', self.on_open),
|
||||
('Save', gtk.STOCK_SAVE, '_Save', None,
|
||||
'Save catalog file', self.on_save),
|
||||
('Save As', gtk.STOCK_SAVE_AS,
|
||||
'_Save As', None, None, self.on_save),
|
||||
('Import', None, '_Import', None, None, self.on_import),
|
||||
('Export', None, '_Export', None, None, self.on_export),
|
||||
('Recent', None, '_Recent files'),
|
||||
('Quit', gtk.STOCK_QUIT, '_Quit', None, 'Quit the Program', self.on_quit),
|
||||
('Quit', gtk.STOCK_QUIT, '_Quit', None,
|
||||
'Quit the Program', self.on_quit),
|
||||
('Edit', None, '_Edit'),
|
||||
('Delete', gtk.STOCK_DELETE, '_Delete', None, None, self.on_delete),
|
||||
('Find', gtk.STOCK_FIND, '_Find', None, 'Find file', self.on_find),
|
||||
('Delete', gtk.STOCK_DELETE, '_Delete', None, None,
|
||||
self.on_delete),
|
||||
('Find', gtk.STOCK_FIND, '_Find', None, 'Find file',
|
||||
self.on_find),
|
||||
('Preferences', gtk.STOCK_PREFERENCES, '_Preferences'),
|
||||
('Catalog', None, '_Catalog'),
|
||||
('Add_CD', gtk.STOCK_CDROM, '_Add CD', None, 'Add CD/DVD/BR to catalog'),
|
||||
('Add_Dir', gtk.STOCK_DIRECTORY, '_Add Dir', None, 'Add directory to catalog'),
|
||||
('Add_CD', gtk.STOCK_CDROM, '_Add CD', None,
|
||||
'Add CD/DVD/BR to catalog'),
|
||||
('Add_Dir', gtk.STOCK_DIRECTORY, '_Add Dir', None,
|
||||
'Add directory to catalog'),
|
||||
('Delete_all_images', None, '_Delete all images'),
|
||||
('Delete_all_thumbnails', None, '_Delete all thumbnails'),
|
||||
('Save_all_images', None, '_Save all images…'),
|
||||
@@ -244,5 +253,5 @@ class MainWindow(object):
|
||||
|
||||
|
||||
def run():
|
||||
gui = MainWindow()
|
||||
MainWindow()
|
||||
gtk.mainloop()
|
||||
|
||||
@@ -27,6 +27,7 @@ COLORS = {'WARNING': YELLOW,
|
||||
'CRITICAL': WHITE,
|
||||
'ERROR': RED}
|
||||
|
||||
|
||||
def cprint(txt, color):
|
||||
color_map = {"black": BLACK,
|
||||
"red": RED,
|
||||
@@ -36,7 +37,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):
|
||||
@@ -58,13 +59,11 @@ class ColoredFormatter(logging.Formatter):
|
||||
record.levelname = levelname_color
|
||||
return logging.Formatter.format(self, record)
|
||||
|
||||
|
||||
log_obj = None
|
||||
|
||||
#def get_logger(module_name, level='INFO', to_file=False):
|
||||
#def get_logger(module_name, level='DEBUG', to_file=True):
|
||||
|
||||
def get_logger(module_name, level='INFO', to_file=True, to_console=True):
|
||||
# def get_logger(module_name, level='DEBUG', to_file=True, to_console=True):
|
||||
#def get_logger(module_name, level='DEBUG', to_file=False):
|
||||
"""
|
||||
Prepare and return log object. Standard formatting is used for all logs.
|
||||
Arguments:
|
||||
@@ -83,11 +82,9 @@ def get_logger(module_name, level='INFO', to_file=True, to_console=True):
|
||||
log.setLevel(LEVEL[level])
|
||||
|
||||
if to_console:
|
||||
#path = "/dev/null"
|
||||
|
||||
console_handler = logging.StreamHandler(sys.stderr)
|
||||
console_formatter = ColoredFormatter("%(filename)s:%(lineno)s - "
|
||||
"%(levelname)s - %(message)s")
|
||||
"%(levelname)s - %(message)s")
|
||||
console_handler.setFormatter(console_formatter)
|
||||
|
||||
log.addHandler(console_handler)
|
||||
|
||||
@@ -29,6 +29,7 @@ def float_to_string(float_length):
|
||||
sec = int(float_length)
|
||||
return "%02d:%02d:%02d" % (hour, minutes, sec)
|
||||
|
||||
|
||||
def calculate_image_path(dbpath=None, create=False):
|
||||
"""Calculate image path out of provided path or using current connection"""
|
||||
if not dbpath:
|
||||
@@ -50,7 +51,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):
|
||||
@@ -58,9 +59,10 @@ def calculate_image_path(dbpath=None, create=False):
|
||||
|
||||
return os.path.abspath(images_dir)
|
||||
|
||||
|
||||
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)]
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: pyGTK common utility functions
|
||||
Type: tility
|
||||
Type: utility
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2010-11-07 13:30:37
|
||||
"""
|
||||
|
||||
|
||||
def get_tv_item_under_cursor(treeview):
|
||||
"""
|
||||
Get item (most probably id of the row) form tree view under cursor.
|
||||
@@ -22,4 +23,3 @@ def get_tv_item_under_cursor(treeview):
|
||||
item_id = model.get_value(tm_iter, 0)
|
||||
return item_id
|
||||
return None
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
Created: 2011-03-27
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
from datetime import datetime
|
||||
import mimetypes
|
||||
@@ -26,7 +25,6 @@ RE_FN_START = re.compile(r'(?P<fname_start>'
|
||||
r'(\[[A-Fa-f0-9]{8}\])\..*')
|
||||
|
||||
|
||||
|
||||
class NoAccessError(Exception):
|
||||
"""No access exception"""
|
||||
pass
|
||||
@@ -114,7 +112,7 @@ class Scan(object):
|
||||
# number of objects to retrieve at once. Limit is 999. Let's do a
|
||||
# little bit below.
|
||||
num = 900
|
||||
steps = len(all_ids) / num + 1
|
||||
steps = len(all_ids) // num + 1
|
||||
for step in range(steps):
|
||||
all_obj.extend(self._session
|
||||
.query(File)
|
||||
@@ -155,7 +153,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")
|
||||
@@ -181,8 +179,8 @@ class Scan(object):
|
||||
# self._session.merge(self._files[0])
|
||||
LOG.debug("Deleting objects whitout parent: %s",
|
||||
str(self._session.query(File)
|
||||
.filter(File.parent==None).all()))
|
||||
self._session.query(File).filter(File.parent==None).delete()
|
||||
.filter(File.parent.is_(None)).all()))
|
||||
self._session.query(File).filter(File.parent.is_(None)).delete()
|
||||
|
||||
self._session.commit()
|
||||
return self._files
|
||||
@@ -199,8 +197,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]:
|
||||
@@ -208,7 +205,7 @@ class Scan(object):
|
||||
|
||||
ext = os.path.splitext(fp)[1]
|
||||
|
||||
if mimeinfo and mimeinfo in mimedict.keys():
|
||||
if mimeinfo and mimeinfo in mimedict:
|
||||
mimedict[mimeinfo](fobj, fp)
|
||||
elif ext and ext in extdict:
|
||||
mimedict[extdict[ext]](fobj, fp)
|
||||
@@ -287,13 +284,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 +370,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]
|
||||
@@ -423,7 +415,7 @@ class Scan(object):
|
||||
for dirname in dirs:
|
||||
dirpath = os.path.join(root, dirname)
|
||||
|
||||
if not os.access(dirpath, os.R_OK|os.X_OK):
|
||||
if not os.access(dirpath, os.R_OK | os.X_OK):
|
||||
LOG.info("Cannot access directory %s", dirpath)
|
||||
continue
|
||||
|
||||
@@ -481,8 +473,8 @@ class Scan(object):
|
||||
|
||||
def _set_image_path(self):
|
||||
"""Get or calculate the images path"""
|
||||
image_path = self._session.query(Config) \
|
||||
.filter(Config.key=="image_path").one()
|
||||
image_path = (self._session.query(Config)
|
||||
.filter(Config.key == "image_path")).one()
|
||||
if image_path.value == ":same_as_db:":
|
||||
image_path = pygtktalog.misc.calculate_image_path()
|
||||
else:
|
||||
@@ -507,4 +499,3 @@ def _get_dirsize(path):
|
||||
os.path.join(root, fname))
|
||||
LOG.debug("_get_dirsize, %s: %d", path, size)
|
||||
return size
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ class ThumbCreator(object):
|
||||
"""
|
||||
try:
|
||||
image_thumb = Image.open(self.filename).convert('RGB')
|
||||
except:
|
||||
except Exception:
|
||||
return None
|
||||
it_x, it_y = image_thumb.size
|
||||
if it_x > self.thumb_x or it_y > self.thumb_y:
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2009-04-04
|
||||
"""
|
||||
import math
|
||||
import os
|
||||
import shutil
|
||||
from tempfile import mkdtemp, mkstemp
|
||||
import math
|
||||
import tempfile
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from pygtktalog.misc import float_to_string
|
||||
from pygtktalog.logger import get_logger
|
||||
|
||||
@@ -49,7 +50,7 @@ class Video(object):
|
||||
'ID_AUDIO_CODEC': ['audio_codec', self._return_lower],
|
||||
'ID_AUDIO_FORMAT': ['audio_format', self._return_lower],
|
||||
'ID_AUDIO_NCH': ['audio_no_channels', int]}
|
||||
# TODO: what about audio/subtitle language/existence?
|
||||
# TODO: what about audio/subtitle language/existence?
|
||||
|
||||
for key in output:
|
||||
if key in attrs:
|
||||
@@ -58,9 +59,9 @@ class Video(object):
|
||||
if 'length' in self.tags and self.tags['length'] > 0:
|
||||
start = self.tags.get('start', 0)
|
||||
length = self.tags['length'] - start
|
||||
hours = length / 3600
|
||||
hours = length // 3600
|
||||
seconds = length - hours * 3600
|
||||
minutes = seconds / 60
|
||||
minutes = seconds // 60
|
||||
seconds -= minutes * 60
|
||||
length_str = "%02d:%02d:%02d" % (hours, minutes, seconds)
|
||||
self.tags['duration'] = length_str
|
||||
@@ -92,16 +93,16 @@ 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
|
||||
|
||||
tempdir = mkdtemp()
|
||||
file_desc, image_fn = mkstemp(suffix=".jpg")
|
||||
tempdir = tempfile.mkdtemp()
|
||||
file_desc, image_fn = tempfile.mkstemp(suffix=".jpg")
|
||||
os.close(file_desc)
|
||||
self._make_captures(tempdir, no_pictures)
|
||||
self._make_montage(tempdir, image_fn, no_pictures)
|
||||
@@ -113,16 +114,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'],
|
||||
self.tags['height'])
|
||||
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,20 +179,21 @@ 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
|
||||
time = float_to_string(current_time)
|
||||
cmd = "mplayer \"%s\" -ao null -brightness 0 -hue 0 " \
|
||||
"-saturation 0 -contrast 0 -mc 0 -vf-clr -vo jpeg:outdir=\"%s\" -ss %s" \
|
||||
" -frames 1 2>/dev/null"
|
||||
cmd = ('mplayer "%s" -ao null -brightness 0 -hue 0 '
|
||||
'-saturation 0 -contrast 0 -mc 0 -vf-clr '
|
||||
'-vo jpeg:outdir="%s" -ss %s -frames 1 2>/dev/null')
|
||||
os.popen(cmd % (self.filename, directory, time)).readlines()
|
||||
|
||||
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)
|
||||
@@ -216,13 +218,13 @@ class Video(object):
|
||||
|
||||
if not (self.tags['width'] * row_length) > self.out_width:
|
||||
for i in [8, 6, 5]:
|
||||
if (no_pictures % i) == 0 and \
|
||||
(i * self.tags['width']) <= self.out_width:
|
||||
if ((no_pictures % i) == 0 and
|
||||
(i * self.tags['width']) <= self.out_width):
|
||||
row_length = i
|
||||
break
|
||||
|
||||
coef = float(self.out_width - row_length - 1) / \
|
||||
(self.tags['width'] * row_length)
|
||||
coef = (float(self.out_width - row_length - 1) /
|
||||
(self.tags['width'] * row_length))
|
||||
if coef < 1:
|
||||
dim = (int(self.tags['width'] * coef),
|
||||
int(self.tags['height'] * coef))
|
||||
@@ -231,10 +233,10 @@ class Video(object):
|
||||
|
||||
ifn_list = os.listdir(directory)
|
||||
ifn_list.sort()
|
||||
img_list = [Image.open(os.path.join(directory, fn)).resize(dim) \
|
||||
for fn in ifn_list]
|
||||
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)
|
||||
@@ -250,7 +252,7 @@ class Video(object):
|
||||
bbox = (left, upper, right, lower)
|
||||
try:
|
||||
img = img_list.pop(0)
|
||||
except:
|
||||
except Exception:
|
||||
break
|
||||
inew.paste(img, bbox)
|
||||
inew.save(image_fn, 'JPEG')
|
||||
@@ -271,7 +273,7 @@ class Video(object):
|
||||
"""
|
||||
try:
|
||||
return int(chain.split(".")[0])
|
||||
except:
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
Fast and ugly CLI interface for pyGTKtalog
|
||||
"""
|
||||
import argparse
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
@@ -24,6 +23,7 @@ BOLD_SEQ = '\033[1m'
|
||||
|
||||
LOG = logger.get_logger(__name__)
|
||||
|
||||
|
||||
def colorize(txt, color):
|
||||
"""Pretty print with colors to console."""
|
||||
color_map = {'black': BLACK,
|
||||
@@ -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,30 +273,29 @@ 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"""
|
||||
image_path = self.sess.query(dbo.Config).\
|
||||
filter(dbo.Config.key=='image_path').one().value
|
||||
image_path = (self.sess.query(dbo.Config)
|
||||
.filter(dbo.Config.key == 'image_path')).one().value
|
||||
|
||||
if image_path == ':same_as_db:':
|
||||
image_path = misc.calculate_image_path(None, False)
|
||||
|
||||
files_to_remove = []
|
||||
obj_to_remove = []
|
||||
|
||||
# remove images/thumbnails which doesn't have file relation
|
||||
for name, obj in (("images", dbo.Image),
|
||||
@@ -318,20 +317,20 @@ class Iface(object):
|
||||
fname).lstrip('/')
|
||||
|
||||
if '_t' in fname:
|
||||
obj = self.sess.query(dbo.Thumbnail)\
|
||||
.filter(dbo.Thumbnail.filename==fname_).all()
|
||||
obj = (self.sess.query(dbo.Thumbnail)
|
||||
.filter(dbo.Thumbnail.filename == fname_)).all()
|
||||
if obj:
|
||||
continue
|
||||
|
||||
obj = self.sess.query(dbo.Image)\
|
||||
.filter(dbo.Image.filename==\
|
||||
fname_.replace('_t.', '.')).all()
|
||||
obj = (self.sess.query(dbo.Image)
|
||||
.filter(dbo.Image.filename ==
|
||||
fname_.replace('_t.', '.'))).all()
|
||||
if obj:
|
||||
continue
|
||||
|
||||
else:
|
||||
obj = self.sess.query(dbo.Image)\
|
||||
.filter(dbo.Image.filename==fname_).all()
|
||||
obj = (self.sess.query(dbo.Image)
|
||||
.filter(dbo.Image.filename == fname_)).all()
|
||||
if obj:
|
||||
continue
|
||||
|
||||
@@ -342,9 +341,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)
|
||||
@@ -417,13 +416,13 @@ def add_dir(args):
|
||||
obj.close()
|
||||
|
||||
|
||||
@asserdb
|
||||
def create_db(args):
|
||||
"""List"""
|
||||
obj = Iface(args.db, args.pretend, args.debug)
|
||||
obj.create(args.dir_to_add, args.imagedir)
|
||||
obj.close()
|
||||
|
||||
|
||||
@asserdb
|
||||
def search(args):
|
||||
"""Find"""
|
||||
@@ -431,6 +430,7 @@ def search(args):
|
||||
obj.find(args.search_words)
|
||||
obj.close()
|
||||
|
||||
|
||||
@asserdb
|
||||
def cleanup(args):
|
||||
"""Cleanup"""
|
||||
@@ -439,7 +439,6 @@ def cleanup(args):
|
||||
obj.close()
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
"""Main"""
|
||||
parser = argparse.ArgumentParser()
|
||||
@@ -472,7 +471,7 @@ def main():
|
||||
create.add_argument('dir_to_add')
|
||||
create.add_argument('-i', '--imagedir', help="Directory where to put "
|
||||
"images for the database. Popular, but deprecated "
|
||||
"choice is `~/.pygtktalog/images'. Currnet default "
|
||||
"choice is `~/.pygtktalog/images'. Current default "
|
||||
"is special string `:same_as_db:' which will try to "
|
||||
"create directory with the same name as the db with "
|
||||
"data suffix", default=':same_as_db:')
|
||||
@@ -510,7 +509,12 @@ def main():
|
||||
fsck.set_defaults(func=cleanup)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
||||
if 'func' in args:
|
||||
args.func(args)
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -64,5 +64,6 @@ def main():
|
||||
app = App(db)
|
||||
app.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
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',
|
||||
|
||||
@@ -23,7 +23,7 @@ def populate_with_mock_files(dir_):
|
||||
files_no = 0
|
||||
for file_ in files1:
|
||||
with open(os.path.join(dir_, file_), "wb") as fobj:
|
||||
fobj.write("\xde\xad\xbe\xef" * len(file_))
|
||||
fobj.write(b"\xde\xad\xbe\xef" * len(file_))
|
||||
files_no += 1
|
||||
|
||||
os.symlink(os.path.join(dir_, files1[-1]), os.path.join(dir_, 'link.jpg'))
|
||||
@@ -32,7 +32,7 @@ def populate_with_mock_files(dir_):
|
||||
os.mkdir(os.path.join(dir_, 'directory'))
|
||||
for file_ in files2:
|
||||
with open(os.path.join(dir_, 'directory', file_), "wb") as fobj:
|
||||
fobj.write("\xfe\xad\xfa\xce" * len(file_))
|
||||
fobj.write(b"\xfe\xad\xfa\xce" * len(file_))
|
||||
files_no += 1
|
||||
|
||||
return files_no
|
||||
@@ -178,8 +178,8 @@ class TestScan(unittest.TestCase):
|
||||
self.assertTrue(file_ob is not file2_ob)
|
||||
|
||||
# While Image objects points to the same file
|
||||
self.assertTrue(file_ob.images[0].filename == \
|
||||
file2_ob.images[0].filename)
|
||||
self.assertTrue(file_ob.images[0].filename ==
|
||||
file2_ob.images[0].filename)
|
||||
|
||||
# they are different objects
|
||||
self.assertTrue(file_ob.images[0] is not file2_ob.images[0])
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
"""
|
||||
import os
|
||||
import unittest
|
||||
from unittest import mock
|
||||
import io
|
||||
|
||||
import PIL
|
||||
|
||||
@@ -130,7 +132,7 @@ ID_AUDIO_RATE=22050
|
||||
ID_AUDIO_NCH=1
|
||||
ID_AUDIO_CODEC=ffac3
|
||||
ID_EXIT=EOF""",
|
||||
"m.wmv":"""ID_AUDIO_ID=1
|
||||
"m.wmv": """ID_AUDIO_ID=1
|
||||
ID_VIDEO_ID=2
|
||||
ID_FILENAME=m.wmv
|
||||
ID_DEMUXER=asf
|
||||
@@ -198,6 +200,7 @@ class Readlines(object):
|
||||
def readlines(self):
|
||||
return self.data.split('\n')
|
||||
|
||||
|
||||
def mock_popen(command):
|
||||
key = None
|
||||
if 'midentify' in command:
|
||||
@@ -205,22 +208,25 @@ def mock_popen(command):
|
||||
elif 'jpeg:outdir' in command:
|
||||
# simulate capture for mplayer
|
||||
img_dir = command.split('"')[-2]
|
||||
img = PIL.Image.new('RGBA', (320, 200))
|
||||
img = PIL.Image.new('RGB', (320, 200))
|
||||
with open(os.path.join(img_dir, "00000001.jpg"), "wb") as fobj:
|
||||
img.save(fobj)
|
||||
|
||||
return Readlines(key)
|
||||
|
||||
|
||||
os.popen = mock_popen
|
||||
# os.popen = mock_popen
|
||||
|
||||
|
||||
class TestVideo(unittest.TestCase):
|
||||
"""test class for retrive midentify script output"""
|
||||
|
||||
def test_avi(self):
|
||||
@mock.patch('os.popen')
|
||||
def test_avi(self, popen):
|
||||
"""test mock avi file, should return dict with expected values"""
|
||||
avi = Video("m.avi")
|
||||
fname = "m.avi"
|
||||
popen.return_value = io.StringIO(DATA[fname])
|
||||
avi = Video(fname)
|
||||
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(avi.tags['audio_format'], '85')
|
||||
self.assertEqual(avi.tags['width'], 128)
|
||||
@@ -233,10 +239,13 @@ class TestVideo(unittest.TestCase):
|
||||
self.assertEqual(avi.tags['duration'], '00:00:04')
|
||||
self.assertEqual(avi.tags['container'], 'avi')
|
||||
|
||||
def test_avi2(self):
|
||||
@mock.patch('os.popen')
|
||||
def test_avi2(self, popen):
|
||||
"""test another mock avi file, should return dict with expected
|
||||
values"""
|
||||
avi = Video("m1.avi")
|
||||
fname = "m1.avi"
|
||||
popen.return_value = io.StringIO(DATA[fname])
|
||||
avi = Video(fname)
|
||||
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(avi.tags['audio_format'], '85')
|
||||
self.assertEqual(avi.tags['width'], 128)
|
||||
@@ -249,9 +258,12 @@ class TestVideo(unittest.TestCase):
|
||||
self.assertEqual(avi.tags['duration'], '00:00:04')
|
||||
self.assertEqual(avi.tags['container'], 'avi')
|
||||
|
||||
def test_mkv(self):
|
||||
@mock.patch('os.popen')
|
||||
def test_mkv(self, popen):
|
||||
"""test mock mkv file, should return dict with expected values"""
|
||||
mkv = Video("m.mkv")
|
||||
fname = "m.mkv"
|
||||
popen.return_value = io.StringIO(DATA[fname])
|
||||
mkv = Video(fname)
|
||||
self.assertTrue(len(mkv.tags) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(mkv.tags['audio_format'], '8192')
|
||||
self.assertEqual(mkv.tags['width'], 128)
|
||||
@@ -264,24 +276,30 @@ class TestVideo(unittest.TestCase):
|
||||
self.assertEqual(mkv.tags['duration'], '00:00:04')
|
||||
self.assertTrue(mkv.tags['container'] in ('mkv', 'lavfpref'))
|
||||
|
||||
def test_mpg(self):
|
||||
@mock.patch('os.popen')
|
||||
def test_mpg(self, popen):
|
||||
"""test mock mpg file, should return dict with expected values"""
|
||||
mpg = Video("m.mpg")
|
||||
fname = "m.mpg"
|
||||
popen.return_value = io.StringIO(DATA[fname])
|
||||
mpg = Video(fname)
|
||||
self.assertTrue(len(mpg.tags) != 0, "result should have lenght > 0")
|
||||
self.assertFalse(mpg.tags.has_key('audio_format'))
|
||||
self.assertFalse('audio_format' in mpg.tags)
|
||||
self.assertEqual(mpg.tags['width'], 128)
|
||||
self.assertFalse(mpg.tags.has_key('audio_no_channels'))
|
||||
self.assertFalse('audio_no_channels' in mpg.tags)
|
||||
self.assertEqual(mpg.tags['height'], 96)
|
||||
self.assertEqual(mpg.tags['video_format'], '0x10000001')
|
||||
self.assertFalse(mpg.tags.has_key('lenght'))
|
||||
self.assertFalse(mpg.tags.has_key('audio_codec'))
|
||||
self.assertFalse('lenght' in mpg.tags)
|
||||
self.assertFalse('audio_codec' in mpg.tags)
|
||||
self.assertEqual(mpg.tags['video_codec'], 'ffmpeg1')
|
||||
self.assertFalse(mpg.tags.has_key('duration'))
|
||||
self.assertFalse('duration' in mpg.tags)
|
||||
self.assertEqual(mpg.tags['container'], 'mpeges')
|
||||
|
||||
def test_ogm(self):
|
||||
@mock.patch('os.popen')
|
||||
def test_ogm(self, popen):
|
||||
"""test mock ogm file, should return dict with expected values"""
|
||||
ogm = Video("m.ogm")
|
||||
fname = "m.ogm"
|
||||
popen.return_value = io.StringIO(DATA[fname])
|
||||
ogm = Video(fname)
|
||||
self.assertTrue(len(ogm.tags) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(ogm.tags['audio_format'], '8192')
|
||||
self.assertEqual(ogm.tags['width'], 160)
|
||||
@@ -294,9 +312,12 @@ class TestVideo(unittest.TestCase):
|
||||
self.assertEqual(ogm.tags['duration'], '00:00:04')
|
||||
self.assertTrue(ogm.tags['container'] in ('ogg', 'lavfpref'))
|
||||
|
||||
def test_wmv(self):
|
||||
@mock.patch('os.popen')
|
||||
def test_wmv(self, popen):
|
||||
"""test mock wmv file, should return dict with expected values"""
|
||||
wmv = Video("m.wmv")
|
||||
fname = "m.wmv"
|
||||
popen.return_value = io.StringIO(DATA[fname])
|
||||
wmv = Video(fname)
|
||||
self.assertTrue(len(wmv.tags) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(wmv.tags['audio_format'], '353')
|
||||
self.assertEqual(wmv.tags['width'], 852)
|
||||
@@ -309,9 +330,12 @@ class TestVideo(unittest.TestCase):
|
||||
self.assertEqual(wmv.tags['duration'], '01:17:32')
|
||||
self.assertEqual(wmv.tags['container'], 'asf')
|
||||
|
||||
def test_mp4(self):
|
||||
@mock.patch('os.popen')
|
||||
def test_mp4(self, popen):
|
||||
"""test mock mp4 file, should return dict with expected values"""
|
||||
mp4 = Video("m.mp4")
|
||||
fname = "m.mp4"
|
||||
popen.return_value = io.StringIO(DATA[fname])
|
||||
mp4 = Video(fname)
|
||||
self.assertTrue(len(mp4.tags) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(mp4.tags['audio_format'], 'mp4a')
|
||||
self.assertEqual(mp4.tags['width'], 720)
|
||||
@@ -324,21 +348,31 @@ class TestVideo(unittest.TestCase):
|
||||
self.assertEqual(mp4.tags['duration'], '00:01:09')
|
||||
self.assertEqual(mp4.tags['container'], 'lavfpref')
|
||||
|
||||
def test_capture(self):
|
||||
@mock.patch('shutil.move')
|
||||
@mock.patch('pygtktalog.video.Image')
|
||||
@mock.patch('os.listdir')
|
||||
@mock.patch('shutil.rmtree')
|
||||
@mock.patch('os.close')
|
||||
@mock.patch('tempfile.mkstemp')
|
||||
@mock.patch('tempfile.mkdtemp')
|
||||
@mock.patch('os.popen')
|
||||
def test_capture(self, popen, mkdtemp, mkstemp, fclose, rmtree, listdir,
|
||||
img, move):
|
||||
"""test capture with some small movie and play a little with tags"""
|
||||
avi = Video("m.avi")
|
||||
fname = 'm.avi'
|
||||
popen.return_value = io.StringIO(DATA[fname])
|
||||
mkdtemp.return_value = '/tmp'
|
||||
mkstemp.return_value = (10, 'foo.jpg')
|
||||
listdir.return_value = ['a.jpg', 'b.jpg', 'c.jpg', 'd.jpg']
|
||||
|
||||
avi = Video(fname)
|
||||
filename = avi.capture()
|
||||
self.assertTrue(filename != None)
|
||||
self.assertTrue(os.path.exists(filename))
|
||||
file_size = os.stat(filename)[6]
|
||||
self.assertAlmostEqual(file_size/10000.0, 0.151, 0)
|
||||
os.unlink(filename)
|
||||
self.assertIsNotNone(filename)
|
||||
|
||||
for length in (480, 380, 4):
|
||||
avi.tags['length'] = length
|
||||
filename = avi.capture()
|
||||
self.assertTrue(filename is not None)
|
||||
os.unlink(filename)
|
||||
|
||||
avi.tags['length'] = 3
|
||||
self.assertTrue(avi.capture() is None)
|
||||
@@ -351,7 +385,6 @@ class TestVideo(unittest.TestCase):
|
||||
avi.tags['width'] = 1025
|
||||
filename = avi.capture()
|
||||
self.assertTrue(filename is not None)
|
||||
os.unlink(filename)
|
||||
|
||||
del(avi.tags['length'])
|
||||
self.assertTrue(avi.capture() is None)
|
||||
|
||||
Reference in New Issue
Block a user