mirror of
https://github.com/gryf/pygtktalog.git
synced 2025-12-17 11:30:19 +01:00
changes in tests, upgrade to new EXIF module, etc
This commit is contained in:
5
README
5
README
@@ -24,9 +24,8 @@ REQUIREMENTS
|
||||
|
||||
pyGTKtalog is written in python with following dependencies:
|
||||
|
||||
- python 2.4 or higher
|
||||
- pygtk 2.10 or higher <http://www.pygtk.org>
|
||||
- pysqlite2 <http://pysqlite.org/> (unnecessary, if python 2.5 is used)
|
||||
- python 2.5 or higher
|
||||
- pygtk 2.12 or higher <http://www.pygtk.org>
|
||||
|
||||
Optional modules:
|
||||
|
||||
|
||||
123
pygtktalog.py
123
pygtktalog.py
@@ -1,110 +1,62 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
#
|
||||
# Author: Roman 'gryf' Dobosz gryf@elysium.pl
|
||||
#
|
||||
# Copyright (C) 2007 by Roman 'gryf' Dobosz
|
||||
#
|
||||
# This file is part of pyGTKtalog.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Application main launch file.
|
||||
Type: core
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2007-05-01
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
try:
|
||||
import gtk
|
||||
except ImportError:
|
||||
print "You need to install pyGTK v2.10.x or newer."
|
||||
raise
|
||||
import locale
|
||||
import gettext
|
||||
|
||||
def setup_path():
|
||||
"""Sets up the python include paths to include needed directories"""
|
||||
import os.path
|
||||
import gtk
|
||||
import pygtk
|
||||
pygtk.require("2.0")
|
||||
|
||||
from src.lib.globs import TOPDIR
|
||||
sys.path = [os.path.join(TOPDIR, "src")] + sys.path
|
||||
return
|
||||
import gtkmvc
|
||||
gtkmvc.require("1.2.2")
|
||||
|
||||
from src.lib.globs import TOPDIR
|
||||
from src.lib.globs import APPL_SHORT_NAME
|
||||
sys.path = [os.path.join(TOPDIR, "src")] + sys.path
|
||||
from models.m_config import ConfigModel
|
||||
from models.m_main import MainModel
|
||||
from ctrls.c_main import MainController
|
||||
from views.v_main import MainView
|
||||
|
||||
def check_requirements():
|
||||
"""Checks versions and other requirements"""
|
||||
import sys
|
||||
import gtkmvc
|
||||
gtkmvc.require("1.2.0")
|
||||
|
||||
try:
|
||||
from models.m_config import ConfigModel
|
||||
except ImportError:
|
||||
print "Some fundamental files are missing.",
|
||||
print "Try runnig pyGTKtalog in his root directory"
|
||||
raise
|
||||
|
||||
conf = ConfigModel()
|
||||
conf.load()
|
||||
|
||||
try:
|
||||
import pygtk
|
||||
#tell pyGTK, if possible, that we want GTKv2
|
||||
pygtk.require("2.0")
|
||||
except ImportError:
|
||||
#Some distributions come with GTK2, but not pyGTK
|
||||
pass
|
||||
|
||||
try:
|
||||
import sqlite3 as sqlite
|
||||
except ImportError:
|
||||
try:
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
except ImportError:
|
||||
print "pyGTKtalog uses SQLite DB.\nYou'll need to get it and the",
|
||||
print "python bindings as well.",
|
||||
print "http://www.sqlite.org"
|
||||
print "http://initd.org/tracker/pysqlite"
|
||||
print "Alternatively install python 2.5 or higher"
|
||||
raise
|
||||
|
||||
if conf.confd['exportxls']:
|
||||
try:
|
||||
import pyExcelerator
|
||||
except ImportError:
|
||||
print "WARNING: You'll need pyExcelerator, if you want to export",
|
||||
print "DB to XLS format."
|
||||
print "http://sourceforge.net/projects/pyexcelerator"
|
||||
|
||||
if conf.confd['thumbs'] and conf.confd['retrive']:
|
||||
try:
|
||||
import Image
|
||||
except ImportError:
|
||||
print "WARNING: You'll need Python Imaging Library (PIL), if you",
|
||||
print "want to make\nthumbnails!"
|
||||
print _("WARNING: You'll need Python Imaging Library (PIL), if "
|
||||
"you want to make thumbnails!")
|
||||
raise
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
def run():
|
||||
"""Create model, controller and view and launch it."""
|
||||
# Directory from where pygtkatalog was invoced. We need it for calculate
|
||||
# path for argument (catalog file)
|
||||
execution_dir = os.path.abspath(os.path.curdir)
|
||||
|
||||
# Directory, where this files lies. We need it to setup private source
|
||||
# paths
|
||||
libraries_dir = os.path.dirname(__file__)
|
||||
os.chdir(libraries_dir)
|
||||
if libraries_dir:
|
||||
os.chdir(libraries_dir)
|
||||
|
||||
setup_path()
|
||||
#check_requirements()
|
||||
# Setup i18n
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
gettext.install(APPL_SHORT_NAME, 'locale', unicode=True)
|
||||
|
||||
from models.m_main import MainModel
|
||||
from ctrls.c_main import MainController
|
||||
from views.v_main import MainView
|
||||
check_requirements()
|
||||
|
||||
model = MainModel()
|
||||
if len(sys.argv) > 1:
|
||||
@@ -115,6 +67,9 @@ if __name__ == "__main__":
|
||||
try:
|
||||
gtk.main()
|
||||
except KeyboardInterrupt:
|
||||
#model.config.save()
|
||||
#model.cleanup()
|
||||
model.config.save()
|
||||
model.cleanup()
|
||||
gtk.main_quit
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
||||
@@ -252,7 +252,7 @@ class MainController(Controller):
|
||||
except TypeError:
|
||||
if __debug__:
|
||||
print "c_main.py: on_edit2_activate(): 0",
|
||||
print "zaznaczonych wierszy"
|
||||
print "selected rows"
|
||||
return
|
||||
|
||||
val = self.model.get_file_info(id)
|
||||
@@ -281,11 +281,9 @@ class MainController(Controller):
|
||||
|
||||
def on_remove_thumb1_activate(self, menu_item):
|
||||
if self.model.config.confd['delwarn']:
|
||||
title = 'Delete thumbnails'
|
||||
question = 'Delete thumbnails?'
|
||||
description = "Thumbnails for selected items will be permanently"
|
||||
description += " removed from catalog."
|
||||
obj = Dialogs.Qst(title, question, description)
|
||||
obj = Dialogs.Qst(_("Delete thumbnails"), _("Delete thumbnails?"),
|
||||
_("Thumbnails for selected items will be "
|
||||
"permanently removed from catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
try:
|
||||
@@ -307,11 +305,9 @@ class MainController(Controller):
|
||||
|
||||
def on_remove_image1_activate(self, menu_item):
|
||||
if self.model.config.confd['delwarn']:
|
||||
title = 'Delete images'
|
||||
question = 'Delete all images?'
|
||||
description = 'All images for selected items will be permanently'
|
||||
description += ' removed from catalog.'
|
||||
obj = Dialogs.Qst(title, question, description)
|
||||
obj = Dialogs.Qst(_("Delete images"), _("Delete all images?"),
|
||||
_("All images for selected items will be "
|
||||
"permanently removed from catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
try:
|
||||
@@ -343,8 +339,8 @@ class MainController(Controller):
|
||||
else:
|
||||
ImageView(img)
|
||||
else:
|
||||
Dialogs.Inf("Image view", "No Image",
|
||||
"Image file does not exist.")
|
||||
Dialogs.Inf(_("Image view"), _("No Image"),
|
||||
_("Image file does not exist."))
|
||||
|
||||
def on_rename1_activate(self, widget):
|
||||
model, iter = self.view['discs'].get_selection().get_selected()
|
||||
@@ -492,11 +488,10 @@ class MainController(Controller):
|
||||
# check if any unsaved project is on go.
|
||||
if self.model.unsaved_project and \
|
||||
self.model.config.confd['confirmquit']:
|
||||
title = 'Quit application - pyGTKtalog'
|
||||
question = 'Do you really want to quit?'
|
||||
description = "Current database is not saved, any changes will "
|
||||
description += "be lost."
|
||||
if not Dialogs.Qst(title, question, description).run():
|
||||
if not Dialogs.Qst(_("Quit application") + " - pyGTKtalog",
|
||||
_("Do you really want to quit?"),
|
||||
_("Current database is not saved, any changes "
|
||||
"will be lost.")).run():
|
||||
return
|
||||
self.__store_settings()
|
||||
self.model.cleanup()
|
||||
@@ -506,10 +501,10 @@ class MainController(Controller):
|
||||
def on_new_activate(self, widget):
|
||||
"""Create new database file"""
|
||||
if self.model.unsaved_project:
|
||||
title = 'Unsaved data - pyGTKtalog'
|
||||
question = "Do you want to abandon changes?"
|
||||
desc = "Current database is not saved, any changes will be lost."
|
||||
if not Dialogs.Qst(title, question, desc).run():
|
||||
if not Dialogs.Qst(_("Unsaved data") + " - pyGTKtalog",
|
||||
_("Do you want to abandon changes?"),
|
||||
_("Current database is not saved, any changes "
|
||||
"will be lost.")).run():
|
||||
return
|
||||
self.model.new()
|
||||
|
||||
@@ -525,8 +520,8 @@ class MainController(Controller):
|
||||
|
||||
def on_add_cd_activate(self, widget, label=None, current_id=None):
|
||||
"""Add directory structure from cd/dvd disc"""
|
||||
mount = device_helper.volmount(self.model.config.confd['cd'])
|
||||
if mount == 'ok':
|
||||
mount, msg = device_helper.volmount(self.model.config.confd['cd'])
|
||||
if mount:
|
||||
guessed_label = device_helper.volname(self.model.config.confd['cd'])
|
||||
if not label:
|
||||
label = Dialogs.InputDiskLabel(guessed_label).run()
|
||||
@@ -543,10 +538,10 @@ class MainController(Controller):
|
||||
device_helper.volumount(self.model.config.confd['cd'])
|
||||
return True
|
||||
else:
|
||||
Dialogs.Wrn("Error mounting device - pyGTKtalog",
|
||||
"Cannot mount device pointed to %s" %
|
||||
Dialogs.Wrn(_("Error mounting device") + " - pyGTKtalog",
|
||||
_("Cannot mount device pointed to %s") %
|
||||
self.model.config.confd['cd'],
|
||||
"Last mount message:\n%s" % mount)
|
||||
_("Last mount message:\n%s") % msg)
|
||||
return False
|
||||
|
||||
def on_add_directory_activate(self, widget, path=None, label=None,
|
||||
@@ -599,6 +594,7 @@ class MainController(Controller):
|
||||
|
||||
def on_save_activate(self, widget):
|
||||
"""Save catalog to file"""
|
||||
# FIXME: Traceback when recent is null
|
||||
if self.model.filename:
|
||||
self.model.save()
|
||||
self.__set_title(filepath=self.model.filename)
|
||||
@@ -618,8 +614,8 @@ class MainController(Controller):
|
||||
self.model.config.add_recent(path)
|
||||
self.__set_title(filepath=path)
|
||||
else:
|
||||
Dialogs.Err("Error writing file - pyGTKtalog",
|
||||
"Cannot write file %s." % path, "%s" % err)
|
||||
Dialogs.Err(_("Error writing file") + " - pyGTKtalog",
|
||||
_("Cannot write file %s.") % path, "%s" % err)
|
||||
|
||||
def on_stat1_activate(self, menu_item, selected_id=None):
|
||||
data = self.model.get_stats(selected_id)
|
||||
@@ -640,9 +636,9 @@ class MainController(Controller):
|
||||
"""Open catalog file"""
|
||||
confirm = self.model.config.confd['confirmabandon']
|
||||
if self.model.unsaved_project and confirm:
|
||||
obj = Dialogs.Qst('Unsaved data - pyGTKtalog',
|
||||
'There is not saved database',
|
||||
'Pressing "Ok" will abandon catalog.')
|
||||
obj = Dialogs.Qst(_("Unsaved data") + " - pyGTKtalog",
|
||||
_("There is not saved database"),
|
||||
_("Pressing <b>Ok</b> will abandon catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
|
||||
@@ -666,8 +662,8 @@ class MainController(Controller):
|
||||
|
||||
if path:
|
||||
if not self.model.open(path):
|
||||
Dialogs.Err("Error opening file - pyGTKtalog",
|
||||
"Cannot open file %s." % path)
|
||||
Dialogs.Err(_("Error opening file") + " - pyGTKtalog",
|
||||
_("Cannot open file %s.") % path)
|
||||
else:
|
||||
self.__generate_recent_menu()
|
||||
self.__activate_ui(path)
|
||||
@@ -733,13 +729,14 @@ class MainController(Controller):
|
||||
"""delete selected images (with thumbnails)"""
|
||||
list_of_paths = self.view['images'].get_selected_items()
|
||||
if not list_of_paths:
|
||||
Dialogs.Inf("Delete images", "No images selected",
|
||||
"You have to select at least one image to delete.")
|
||||
Dialogs.Inf(_("Delete images"), _("No images selected"),
|
||||
_("You have to select at least one image to delete."))
|
||||
return
|
||||
|
||||
if self.model.config.confd['delwarn']:
|
||||
obj = Dialogs.Qst('Delete images', 'Delete selected images?',
|
||||
'Selected images will be permanently removed from catalog.')
|
||||
obj = Dialogs.Qst(_("Delete images"), _("Delete selected images?"),
|
||||
_("Selected images will be permanently removed"
|
||||
" from catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
|
||||
@@ -768,7 +765,7 @@ class MainController(Controller):
|
||||
|
||||
def on_img_save_activate(self, menu_item):
|
||||
"""export images (not thumbnails) into desired direcotry"""
|
||||
dialog = Dialogs.SelectDirectory("Choose directory to save images")
|
||||
dialog = Dialogs.SelectDirectory(_("Choose directory to save images"))
|
||||
filepath = dialog.run()
|
||||
|
||||
if not filepath:
|
||||
@@ -793,14 +790,13 @@ class MainController(Controller):
|
||||
count += 1
|
||||
|
||||
if count > 0:
|
||||
Dialogs.Inf("Save images",
|
||||
"%d images was succsefully saved." % count,
|
||||
"Images are placed in directory:\n%s." % filepath)
|
||||
Dialogs.Inf(_("Save images"),
|
||||
_("%d images was succsefully saved.") % count,
|
||||
_("Images are placed in directory:\n%s.") % filepath)
|
||||
else:
|
||||
description = "Images probably don't have real images - only"
|
||||
description += " thumbnails."
|
||||
Dialogs.Inf("Save images",
|
||||
"No images was saved.",
|
||||
description = _("Images probably don't have real images - only"
|
||||
" thumbnails.")
|
||||
Dialogs.Inf(_("Save images"), _("No images was saved."),
|
||||
description)
|
||||
return
|
||||
|
||||
@@ -809,12 +805,12 @@ class MainController(Controller):
|
||||
list_of_paths = self.view['images'].get_selected_items()
|
||||
|
||||
if not list_of_paths:
|
||||
Dialogs.Inf("Set thumbnail", "No image selected",
|
||||
"You have to select one image to set as thumbnail.")
|
||||
Dialogs.Inf(_("Set thumbnail"), _("No image selected"),
|
||||
_("You have to select one image to set as thumbnail."))
|
||||
return
|
||||
if len(list_of_paths) >1:
|
||||
Dialogs.Inf("Set thumbnail", "To many images selected",
|
||||
"You have to select one image to set as thumbnail.")
|
||||
Dialogs.Inf(_("Set thumbnail"), _("To many images selected"),
|
||||
_("You have to select one image to set as thumbnail."))
|
||||
return
|
||||
|
||||
model = self.view['images'].get_model()
|
||||
@@ -872,7 +868,7 @@ class MainController(Controller):
|
||||
|
||||
def on_export_activate(self, menu_item):
|
||||
"""export db file and coressponding images to tar.bz2 archive"""
|
||||
dialog = Dialogs.ChooseFilename(None, "Choose export file")
|
||||
dialog = Dialogs.ChooseFilename(None, _("Choose export file"))
|
||||
filepath = dialog.run()
|
||||
|
||||
if not filepath:
|
||||
@@ -1018,8 +1014,8 @@ class MainController(Controller):
|
||||
def on_delete_tag_activate(self, menu_item):
|
||||
ids = self.__get_tv_selection_ids(self.view['files'])
|
||||
if not ids:
|
||||
Dialogs.Inf("Remove tags", "No files selected",
|
||||
"You have to select some files first.")
|
||||
Dialogs.Inf(_("Remove tags"), _("No files selected"),
|
||||
_("You have to select some files first."))
|
||||
return
|
||||
|
||||
tags = self.model.get_tags_by_file_id(ids)
|
||||
@@ -1027,8 +1023,9 @@ class MainController(Controller):
|
||||
d = Dialogs.TagsRemoveDialog(tags)
|
||||
retcode, retval = d.run()
|
||||
if retcode=="ok" and not retval:
|
||||
Dialogs.Inf("Remove tags", "No tags selected",
|
||||
"You have to select any tag to remove from files.")
|
||||
Dialogs.Inf(_("Remove tags"), _("No tags selected"),
|
||||
_("You have to select any tag to remove from"
|
||||
" files."))
|
||||
return
|
||||
elif retcode == "ok" and retval:
|
||||
self.model.delete_tags(ids, retval)
|
||||
@@ -1053,7 +1050,7 @@ class MainController(Controller):
|
||||
|
||||
def on_add_image1_activate(self, menu_item):
|
||||
dialog = Dialogs.LoadImageFile(True)
|
||||
msg = "Don't copy images. Generate only thumbnails."
|
||||
msg = _("Don't copy images. Generate only thumbnails.")
|
||||
toggle = gtk.CheckButton(msg)
|
||||
toggle.show()
|
||||
dialog.dialog.set_extra_widget(toggle)
|
||||
@@ -1117,15 +1114,15 @@ class MainController(Controller):
|
||||
return
|
||||
|
||||
if not selected_iter:
|
||||
Dialogs.Inf("Delete disc", "No disc selected",
|
||||
"You have to select disc first before you " +\
|
||||
"can delete it")
|
||||
Dialogs.Inf(_("Delete disc"), _("No disc selected"),
|
||||
_("You have to select disc first before you "
|
||||
"can delete it"))
|
||||
return
|
||||
|
||||
if self.model.config.confd['delwarn']:
|
||||
name = model.get_value(selected_iter, 1)
|
||||
obj = Dialogs.Qst('Delete %s' % name, 'Delete %s?' % name,
|
||||
'Object will be permanently removed.')
|
||||
obj = Dialogs.Qst(_("Delete %s") % name, _("Delete %s?") % name,
|
||||
_("Object will be permanently removed."))
|
||||
if not obj.run():
|
||||
return
|
||||
|
||||
@@ -1168,14 +1165,14 @@ class MainController(Controller):
|
||||
return
|
||||
|
||||
if not list_of_paths:
|
||||
Dialogs.Inf("Delete files", "No files selected",
|
||||
"You have to select at least one file to delete.")
|
||||
Dialogs.Inf(_("Delete files"), _("No files selected"),
|
||||
_("You have to select at least one file to delete."))
|
||||
return
|
||||
|
||||
if self.model.config.confd['delwarn']:
|
||||
description = "Selected files and directories will be "
|
||||
description += "permanently\n removed from catalog."
|
||||
obj = Dialogs.Qst("Delete files", "Delete files?", description)
|
||||
obj = Dialogs.Qst(_("Delete files"), _("Delete files?"),
|
||||
_("Selected files and directories will be "
|
||||
"permanently\n removed from catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
|
||||
@@ -1216,10 +1213,9 @@ class MainController(Controller):
|
||||
|
||||
def on_th_delete_activate(self, menu_item):
|
||||
if self.model.config.confd['delwarn']:
|
||||
title = 'Delete thumbnail'
|
||||
question = 'Delete thumbnail?'
|
||||
dsc = "Current thumbnail will be permanently removed from catalog."
|
||||
obj = Dialogs.Qst(title, question, dsc)
|
||||
obj = Dialogs.Qst(_("Delete thumbnail"), _("Delete thumbnail?"),
|
||||
_("Current thumbnail will be permanently removed"
|
||||
" from catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
path, column = self.view['files'].get_cursor()
|
||||
@@ -1366,17 +1362,19 @@ class MainController(Controller):
|
||||
msg = device_helper.eject_cd(ejectapp,
|
||||
self.model.config.confd['cd'])
|
||||
if msg != 'ok':
|
||||
Dialogs.Wrn("error ejecting device - pyGTKtalog",
|
||||
"Cannot eject device pointed to %s" %
|
||||
title = _("Error ejecting device")
|
||||
Dialogs.Wrn(title + " - pyGTKtalog",
|
||||
_("Cannot eject device pointed to %s") %
|
||||
self.model.config.confd['cd'],
|
||||
"Last eject message:\n%s" % msg)
|
||||
_("Last eject message:\n%s") % msg)
|
||||
else:
|
||||
msg = device_helper.volumount(self.model.config.confd['cd'])
|
||||
if msg != 'ok':
|
||||
Dialogs.Wrn("error unmounting device - pyGTKtalog",
|
||||
"Cannot unmount device pointed to %s" %
|
||||
title = _("Error unmounting device")
|
||||
Dialogs.Wrn(title + " - pyGTKtalog",
|
||||
_("Cannot unmount device pointed to %s") %
|
||||
self.model.config.confd['cd'],
|
||||
"Last umount message:\n%s" % msg)
|
||||
_("Last umount message:\n%s") % msg)
|
||||
return
|
||||
|
||||
def property_progress_value_change(self, model, old, new):
|
||||
|
||||
1363
src/lib/EXIF.py
1363
src/lib/EXIF.py
File diff suppressed because it is too large
Load Diff
@@ -1,32 +1,23 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
#
|
||||
# Author: Roman 'gryf' Dobosz gryf@elysium.pl
|
||||
#
|
||||
# Copyright (C) 2007 by Roman 'gryf' Dobosz
|
||||
#
|
||||
# This file is part of pyGTKtalog.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Simple functions for device management.
|
||||
Type: lib
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
import os
|
||||
import locale
|
||||
import gettext
|
||||
|
||||
from src.lib.globs import APPL_SHORT_NAME
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
gettext.install(APPL_SHORT_NAME, 'locale', unicode=True)
|
||||
|
||||
def volname(mntp):
|
||||
"""read volume name from cd/dvd"""
|
||||
dev = mountpoint_to_dev(mntp)
|
||||
label = None
|
||||
if dev != None:
|
||||
try:
|
||||
disk = open(dev, "rb")
|
||||
@@ -35,17 +26,20 @@ def volname(mntp):
|
||||
disk.close()
|
||||
except IOError:
|
||||
return None
|
||||
return label
|
||||
return None
|
||||
return label
|
||||
|
||||
def volmount(mntp):
|
||||
"""mount device, return 'ok' or error message"""
|
||||
"""
|
||||
Mount device.
|
||||
@param mountpoint
|
||||
@returns tuple with bool status of mount, and string with error message
|
||||
"""
|
||||
_in, _out, _err = os.popen3("mount %s" % mntp)
|
||||
inf = _err.readlines()
|
||||
if len(inf) > 0:
|
||||
return inf[0].strip()
|
||||
return False, inf[0].strip()
|
||||
else:
|
||||
return 'ok'
|
||||
return True, ''
|
||||
|
||||
def volumount(mntp):
|
||||
"""mount device, return 'ok' or error message"""
|
||||
@@ -88,5 +82,5 @@ def eject_cd(eject_app, cdrom):
|
||||
return inf[0].strip()
|
||||
|
||||
return 'ok'
|
||||
return "Eject program not specified"
|
||||
return _("Eject program not specified")
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ TOPDIR = top_dir
|
||||
RESOURCES_DIR = os.path.join(TOPDIR, "resources")
|
||||
GLADE_DIR = os.path.join(RESOURCES_DIR, "glade")
|
||||
STYLES_DIR = os.path.join(RESOURCES_DIR, "styles")
|
||||
APPL_SHORT_NAME = "pygtktalog2"
|
||||
APPL_SHORT_NAME = "pygtktalog"
|
||||
APPL_VERSION = (1, 0, 2)
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import os
|
||||
from datetime import date
|
||||
|
||||
class GthumbCommentParser(object):
|
||||
|
||||
"""Read and return comments created eith gThumb program"""
|
||||
def __init__(self, image_path, image_filename):
|
||||
self.path = image_path
|
||||
self.filename = image_filename
|
||||
@@ -36,16 +36,16 @@ class GthumbCommentParser(object):
|
||||
"""Return dictionary with apropriate fields, or None if no comment
|
||||
available"""
|
||||
try:
|
||||
gf = gzip.open(os.path.join(self.path,
|
||||
'.comments', self.filename + '.xml'))
|
||||
gzf = gzip.open(os.path.join(self.path, '.comments',
|
||||
self.filename + '.xml'))
|
||||
except:
|
||||
return None
|
||||
|
||||
try:
|
||||
xml = gf.read()
|
||||
gf.close()
|
||||
xml = gzf.read()
|
||||
gzf.close()
|
||||
except:
|
||||
gf.close()
|
||||
gzf.close()
|
||||
return None
|
||||
|
||||
if not xml:
|
||||
|
||||
@@ -44,7 +44,6 @@ class Img(object):
|
||||
"""Save image and asociated thumbnail into specific directory structure
|
||||
returns filename for image"""
|
||||
|
||||
|
||||
image_filename = path.join(self.base, self.sha512)
|
||||
thumbnail = path.join(self.base, self.sha512 + "_t")
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Gather video file information. Uses external tools.
|
||||
Type: lib
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
from os import popen
|
||||
from sys import argv, exit
|
||||
import sys
|
||||
|
||||
class Midentify(object):
|
||||
"""Class for retrive midentify script output and put it in dict.
|
||||
@@ -16,48 +21,41 @@ class Midentify(object):
|
||||
|
||||
def get_data(self):
|
||||
"""return dict with clip information"""
|
||||
output = popen("midentify \"%s\"" % self.filename).readlines()
|
||||
output = popen('midentify "%s"' % self.filename).readlines()
|
||||
|
||||
attrs = {'ID_VIDEO_WIDTH': ['width', int],
|
||||
'ID_VIDEO_HEIGHT': ['height', int],
|
||||
'ID_LENGTH': ['length', lambda x: int(x.split(".")[0])],
|
||||
# length is in seconds
|
||||
'ID_DEMUXER': ['container', str],
|
||||
'ID_VIDEO_FORMAT': ['video_format', str],
|
||||
'ID_VIDEO_CODEC': ['video_codec', str],
|
||||
'ID_AUDIO_CODEC': ['audio_codec', str],
|
||||
'ID_AUDIO_FORMAT': ['audio_format', str],
|
||||
'ID_AUDIO_NCH': ['audio_no_channels', int],}
|
||||
|
||||
for line in output:
|
||||
line = line.strip()
|
||||
if "ID_VIDEO_WIDTH" in line:
|
||||
self.tags['width'] = line.replace("ID_VIDEO_WIDTH=", "")
|
||||
elif "ID_VIDEO_HEIGHT" in line:
|
||||
self.tags['height'] = line.replace("ID_VIDEO_HEIGHT=", "")
|
||||
elif "ID_LENGTH" in line:
|
||||
length = line.replace("ID_LENGTH=", "")
|
||||
if "." in length:
|
||||
length = length.split(".")[0]
|
||||
seconds = int(length)
|
||||
if seconds > 0:
|
||||
hours = seconds / 3600
|
||||
seconds -= hours * 3600
|
||||
minutes = seconds / 60
|
||||
seconds -= minutes * 60
|
||||
self.tags['length'] = length
|
||||
length_str = "%02d:%02d:%02d" % (hours, minutes, seconds)
|
||||
self.tags['duration'] = length_str
|
||||
elif "ID_DEMUXER" in line:
|
||||
self.tags['container'] = line.replace("ID_DEMUXER=", "")
|
||||
elif "ID_VIDEO_FORMAT" in line:
|
||||
self.tags['video_format'] = line.replace("ID_VIDEO_FORMAT=", "")
|
||||
elif "ID_VIDEO_CODEC" in line:
|
||||
self.tags['video_codec'] = line.replace("ID_VIDEO_CODEC=", "")
|
||||
elif "ID_AUDIO_CODEC" in line:
|
||||
self.tags['audio_codec'] = line.replace("ID_AUDIO_CODEC=", "")
|
||||
elif "ID_AUDIO_FORMAT" in line:
|
||||
self.tags['audio_format'] = line.replace("ID_AUDIO_FORMAT=", "")
|
||||
elif "ID_AUDIO_NCH" in line:
|
||||
self.tags['audio_no_channels'] = line.replace("ID_AUDIO_NCH=",
|
||||
"")
|
||||
for attr in attrs:
|
||||
if attr in line:
|
||||
self.tags[attrs[attr][0]] = \
|
||||
attrs[attr][1](line.replace("%s=" % attr, ""))
|
||||
|
||||
if 'length' in self.tags:
|
||||
if self.tags['length'] > 0:
|
||||
hours = self.tags['length'] / 3600
|
||||
seconds = self.tags['length'] - hours * 3600
|
||||
minutes = seconds / 60
|
||||
seconds -= minutes * 60
|
||||
length_str = "%02d:%02d:%02d" % (hours, minutes, seconds)
|
||||
self.tags['duration'] = length_str
|
||||
return self.tags
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""run as standalone script"""
|
||||
if len(argv) < 2:
|
||||
print "usage: %s filename" % argv[0]
|
||||
exit()
|
||||
if len(sys.argv) < 2:
|
||||
print "usage: %s filename" % sys.argv[0]
|
||||
sys.exit()
|
||||
|
||||
for arg in argv[1:]:
|
||||
for arg in sys.argv[1:]:
|
||||
mid = Midentify(arg)
|
||||
print mid.get_data()
|
||||
|
||||
@@ -27,23 +27,17 @@ import sys
|
||||
import shutil
|
||||
import bz2
|
||||
import math
|
||||
import sqlite3 as sqlite
|
||||
from tempfile import mkstemp
|
||||
from datetime import datetime
|
||||
import threading as _threading
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
|
||||
from gtkmvc.model_mt import ModelMT
|
||||
|
||||
try:
|
||||
import sqlite3 as sqlite
|
||||
except ImportError:
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import threading as _threading
|
||||
|
||||
from m_config import ConfigModel
|
||||
|
||||
try:
|
||||
from lib.thumbnail import Thumbnail
|
||||
from lib.img import Img
|
||||
@@ -53,6 +47,7 @@ from lib.parse_exif import ParseExif
|
||||
from lib.gthumb import GthumbCommentParser
|
||||
|
||||
from lib.no_thumb import no_thumb as no_thumb_img
|
||||
from lib.video import Video
|
||||
|
||||
class MainModel(ModelMT):
|
||||
"""Create, load, save, manipulate db file which is container for data"""
|
||||
@@ -91,6 +86,7 @@ class MainModel(ModelMT):
|
||||
# images extensions - only for PIL and EXIF
|
||||
IMG = ['jpg', 'jpeg', 'gif', 'png', 'tif', 'tiff', 'tga', 'pcx', 'bmp',
|
||||
'xbm', 'xpm', 'jp2', 'jpx', 'pnm']
|
||||
MOV = ['avi', 'mpg', 'mpeg', 'mkv', 'wmv', 'ogm', 'mov']
|
||||
|
||||
def __init__(self):
|
||||
"""initialize"""
|
||||
@@ -1754,6 +1750,23 @@ class MainModel(ModelMT):
|
||||
|
||||
ext = i.split('.')[-1].lower()
|
||||
|
||||
# Video
|
||||
if ext in self.MOV:
|
||||
#import rpdb2; rpdb2.start_embedded_debugger('pass')
|
||||
v = Video(current_file)
|
||||
cfn = v.capture()
|
||||
img = Img(cfn, self.image_path)
|
||||
th = img.save()
|
||||
if th:
|
||||
sql = """INSERT INTO
|
||||
thumbnails(file_id, filename)
|
||||
VALUES(?, ?)"""
|
||||
db_cursor.execute(sql, (fileid, th+"_t"))
|
||||
sql = """INSERT INTO images(file_id, filename)
|
||||
VALUES(?, ?)"""
|
||||
db_cursor.execute(sql, (fileid, th))
|
||||
os.unlink(cfn)
|
||||
|
||||
# Images - thumbnails and exif data
|
||||
if self.config.confd['thumbs'] and ext in self.IMG:
|
||||
thumb = Thumbnail(current_file, self.image_path)
|
||||
|
||||
@@ -1,53 +1,27 @@
|
||||
#!/usr/bin/env python
|
||||
# This Python file uses the following encoding: utf-8
|
||||
#
|
||||
# Author: Roman 'gryf' Dobosz gryf@elysium.pl
|
||||
#
|
||||
# Copyright (C) 2007 by Roman 'gryf' Dobosz
|
||||
#
|
||||
# This file is part of pyGTKtalog.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Test harvester and runner.
|
||||
Type: exec
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
import sys
|
||||
import unittest
|
||||
from os import path, chdir
|
||||
import glob
|
||||
|
||||
|
||||
def my_import(module, name):
|
||||
"""import replacement"""
|
||||
mod = __import__(module, {}, {}, [name])
|
||||
components = name.split('.')
|
||||
for comp in components[1:]:
|
||||
mod = getattr(mod, comp)
|
||||
return mod
|
||||
|
||||
def setup_path():
|
||||
"""Sets up the python include paths to include needed directories"""
|
||||
this_path = path.abspath(path.dirname(__file__))
|
||||
sys.path = [path.join(this_path, "../../src")] + sys.path
|
||||
sys.path = [path.join(this_path, "../../src/test")] + sys.path
|
||||
sys.path = [path.join(this_path, "../../src/test/unit")] + sys.path
|
||||
return
|
||||
|
||||
def build_suite():
|
||||
"""build suite test from files in unit directory"""
|
||||
"""Build suite test from files in unit directory. Filenames with test
|
||||
suites should always end with "_test.py"."""
|
||||
modules = []
|
||||
classes = []
|
||||
for fname in glob.glob1('unit', '*_test.py'):
|
||||
class_name = fname[:-8]
|
||||
if "_" in class_name:
|
||||
@@ -59,7 +33,6 @@ def build_suite():
|
||||
class_name = "Test" + class_name.capitalize()
|
||||
|
||||
modules.append(fname[:-3])
|
||||
classes.append(class_name)
|
||||
|
||||
modules = map(__import__, modules)
|
||||
load = unittest.defaultTestLoader.loadTestsFromModule
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: This is simple dummy test for... testing purposes :)
|
||||
Type: test
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
import unittest
|
||||
|
||||
class TestDummy(unittest.TestCase):
|
||||
"""Fake test class"""
|
||||
def test_dummyMethod(self):
|
||||
def test_dummy_method(self):
|
||||
"""Test simple assertion"""
|
||||
self.assertTrue(True)
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: This is another dummy test.
|
||||
Type: test
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
import unittest
|
||||
|
||||
class TestFooBar(unittest.TestCase):
|
||||
"""Fake test class"""
|
||||
def test_dummyMethod(self):
|
||||
def test_dummy_method(self):
|
||||
"""Test simple assertion"""
|
||||
self.assertTrue(True)
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Tests for Midentify class.
|
||||
Type: test
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
import unittest
|
||||
from lib.midentify import Midentify
|
||||
|
||||
@@ -8,82 +14,82 @@ class TestMidentify(unittest.TestCase):
|
||||
Midentify script belongs to mplayer package.
|
||||
"""
|
||||
|
||||
def test_testAvi(self):
|
||||
def test_avi(self):
|
||||
"""test mock avi file, should return dict with expected values"""
|
||||
avi = Midentify("mocks/m.avi")
|
||||
result_dict = avi.get_data()
|
||||
self.assertTrue(len(result_dict) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(result_dict['audio_format'], '85')
|
||||
self.assertEqual(result_dict['width'], '128')
|
||||
self.assertEqual(result_dict['audio_no_channels'], '2')
|
||||
self.assertEqual(result_dict['height'], '96')
|
||||
self.assertEqual(result_dict['width'], 128)
|
||||
self.assertEqual(result_dict['audio_no_channels'], 2)
|
||||
self.assertEqual(result_dict['height'], 96)
|
||||
self.assertEqual(result_dict['video_format'], 'XVID')
|
||||
self.assertEqual(result_dict['length'], '4')
|
||||
self.assertEqual(result_dict['length'], 4)
|
||||
self.assertEqual(result_dict['audio_codec'], 'mp3')
|
||||
self.assertEqual(result_dict['video_codec'], 'ffodivx')
|
||||
self.assertEqual(result_dict['duration'], '00:00:04')
|
||||
self.assertEqual(result_dict['container'], 'avi')
|
||||
|
||||
def test_testAvi2(self):
|
||||
def test_avi2(self):
|
||||
"""test another mock avi file, should return dict with expected
|
||||
values"""
|
||||
avi = Midentify("mocks/m1.avi")
|
||||
result_dict = avi.get_data()
|
||||
self.assertTrue(len(result_dict) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(result_dict['audio_format'], '85')
|
||||
self.assertEqual(result_dict['width'], '128')
|
||||
self.assertEqual(result_dict['audio_no_channels'], '2')
|
||||
self.assertEqual(result_dict['height'], '96')
|
||||
self.assertEqual(result_dict['width'], 128)
|
||||
self.assertEqual(result_dict['audio_no_channels'], 2)
|
||||
self.assertEqual(result_dict['height'], 96)
|
||||
self.assertEqual(result_dict['video_format'], 'H264')
|
||||
self.assertEqual(result_dict['length'], '4')
|
||||
self.assertEqual(result_dict['length'], 4)
|
||||
self.assertEqual(result_dict['audio_codec'], 'mp3')
|
||||
self.assertEqual(result_dict['video_codec'], 'ffh264')
|
||||
self.assertEqual(result_dict['duration'], '00:00:04')
|
||||
self.assertEqual(result_dict['container'], 'avi')
|
||||
|
||||
def test_testMkv(self):
|
||||
def test_mkv(self):
|
||||
"""test mock mkv file, should return dict with expected values"""
|
||||
avi = Midentify("mocks/m.mkv")
|
||||
result_dict = avi.get_data()
|
||||
self.assertTrue(len(result_dict) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(result_dict['audio_format'], '8192')
|
||||
self.assertEqual(result_dict['width'], '128')
|
||||
self.assertEqual(result_dict['audio_no_channels'], '2')
|
||||
self.assertEqual(result_dict['height'], '96')
|
||||
self.assertEqual(result_dict['width'], 128)
|
||||
self.assertEqual(result_dict['audio_no_channels'], 2)
|
||||
self.assertEqual(result_dict['height'], 96)
|
||||
self.assertEqual(result_dict['video_format'], 'mp4v')
|
||||
self.assertEqual(result_dict['length'], '4')
|
||||
self.assertEqual(result_dict['length'], 4)
|
||||
self.assertEqual(result_dict['audio_codec'], 'a52')
|
||||
self.assertEqual(result_dict['video_codec'], 'ffodivx')
|
||||
self.assertEqual(result_dict['duration'], '00:00:04')
|
||||
self.assertEqual(result_dict['container'], 'mkv')
|
||||
|
||||
def test_testMpg(self):
|
||||
def test_mpg(self):
|
||||
"""test mock mpg file, should return dict with expected values"""
|
||||
avi = Midentify("mocks/m.mpg")
|
||||
result_dict = avi.get_data()
|
||||
self.assertTrue(len(result_dict) != 0, "result should have lenght > 0")
|
||||
self.assertFalse(result_dict.has_key('audio_format'))
|
||||
self.assertEqual(result_dict['width'], '128')
|
||||
self.assertEqual(result_dict['width'], 128)
|
||||
self.assertFalse(result_dict.has_key('audio_no_channels'))
|
||||
self.assertEqual(result_dict['height'], '96')
|
||||
self.assertEqual(result_dict['height'], 96)
|
||||
self.assertEqual(result_dict['video_format'], '0x10000001')
|
||||
self.assertFalse(result_dict.has_key('lenght'))
|
||||
self.assertFalse(result_dict.has_key('audio_codec'))
|
||||
self.assertEqual(result_dict['video_codec'], 'mpegpes')
|
||||
self.assertEqual(result_dict['video_codec'], 'ffmpeg1')
|
||||
self.assertFalse(result_dict.has_key('duration'))
|
||||
self.assertEqual(result_dict['container'], 'mpeges')
|
||||
|
||||
def test_testOgm(self):
|
||||
def test_ogm(self):
|
||||
"""test mock ogm file, should return dict with expected values"""
|
||||
avi = Midentify("mocks/m.ogm")
|
||||
result_dict = avi.get_data()
|
||||
self.assertTrue(len(result_dict) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(result_dict['audio_format'], '8192')
|
||||
self.assertEqual(result_dict['width'], '160')
|
||||
self.assertEqual(result_dict['audio_no_channels'], '2')
|
||||
self.assertEqual(result_dict['height'], '120')
|
||||
self.assertEqual(result_dict['width'], 160)
|
||||
self.assertEqual(result_dict['audio_no_channels'], 2)
|
||||
self.assertEqual(result_dict['height'], 120)
|
||||
self.assertEqual(result_dict['video_format'], 'H264')
|
||||
self.assertEqual(result_dict['length'], '4')
|
||||
self.assertEqual(result_dict['length'], 4)
|
||||
self.assertEqual(result_dict['audio_codec'], 'a52')
|
||||
self.assertEqual(result_dict['video_codec'], 'ffh264')
|
||||
self.assertEqual(result_dict['duration'], '00:00:04')
|
||||
|
||||
Reference in New Issue
Block a user