mirror of
https://github.com/gryf/pygtktalog.git
synced 2025-12-17 11:30:19 +01:00
Legacy, working version. Several patches included to conform new format of the DB
This commit is contained in:
31
README
31
README
@@ -1,5 +1,5 @@
|
||||
pyGTKtalog 1.0
|
||||
==================
|
||||
==============
|
||||
|
||||
pyGTKtalog is Linux/FreeBSD program for indexing CD/DVD or directories on
|
||||
filesystem. It is similar to gtktalog <http://www.nongnu.org/gtktalog/> or
|
||||
@@ -75,6 +75,7 @@ There are still minor aims for versions 1.x to be done:
|
||||
- implement advanced search
|
||||
|
||||
For version 2.0:
|
||||
- Export/Import
|
||||
- Icon grid in files view
|
||||
- command line support: query, adding media to collection etc
|
||||
- internationalization
|
||||
@@ -82,7 +83,8 @@ For version 2.0:
|
||||
- user definied group of tags (represented by color in cloud tag)
|
||||
- hiding specified files - configurable, like dot prefixed, cfg and manualy
|
||||
selected
|
||||
|
||||
- tests
|
||||
- warning about existing image in media directory
|
||||
Removed:
|
||||
- filetypes handling (movies, images, archives, documents etc). Now it have
|
||||
common, unified external "plugin" system - simple text output from command
|
||||
@@ -103,17 +105,22 @@ Removed:
|
||||
NOTES
|
||||
=====
|
||||
|
||||
Catalog file is tared and gziped sqlite database and directories with images and
|
||||
thumbnails. If there are more images, the size of catalog file will grow. So be
|
||||
carefull with adding big images in your catalog file!
|
||||
Catalog file is plain sqlite database (optionally compressed with bzip2). All
|
||||
images are stored in ~/.pygtktalog/images directory. Names for images are
|
||||
generated sha512 hash from image file itself. There is small possibility for two
|
||||
identical hash for different image files. However, no images are overwritten.
|
||||
Thumbnail filename for each image is simply concatenation of image filename in
|
||||
images directory and '_t' string.
|
||||
|
||||
There is also converter form old database to new. In fact no image are stored in
|
||||
archive with katalog. All thumnails will be lost. All images without big image
|
||||
will be lost. There ar serious changes with application design, and I decided,
|
||||
that is better to keep media unpacked on disk, instead of pack it every time
|
||||
with save and unpack with open methods. New design prevent from deleting eny
|
||||
file from media directory (placed in ~/.pygtktalog/images). Functionality for
|
||||
exporting images and corresponding db file is planned.
|
||||
There is also converter from old database to new for internal use only. In
|
||||
public release there will be no other formats so it will be useless, and
|
||||
deleted. There are some issues with converting. All thumbnails will be lost. All
|
||||
images without big image will be lost. There are serious changes with
|
||||
application design, and I decided, that is better to keep media unpacked on
|
||||
disk, instead of pack it every time with save and unpack with open methods. New
|
||||
design prevent from deleting any file from media directory (placed in
|
||||
~/.pygtktalog/images). Functionality for exporting images and corresponding db
|
||||
file is planned.
|
||||
|
||||
BUGS
|
||||
====
|
||||
|
||||
@@ -64,13 +64,17 @@ def check_requirements():
|
||||
pass
|
||||
|
||||
try:
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
import sqlite3 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"
|
||||
sys.exit(1)
|
||||
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"
|
||||
sys.exit(1)
|
||||
|
||||
if conf.confd['exportxls']:
|
||||
try:
|
||||
|
||||
@@ -62,6 +62,27 @@
|
||||
<property name="visible">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="import">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Import</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_import_activate"/>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="export">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Export</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_export_activate"/>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkSeparatorMenuItem" id="separator13">
|
||||
<property name="visible">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="recent_files1">
|
||||
<property name="visible">True</property>
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
__version__ = "1.0 RC2"
|
||||
LICENCE = open('LICENCE').read()
|
||||
|
||||
__version__ = "1.0.1"
|
||||
#LICENCE = open('LICENCE').read()
|
||||
LICENCE = ''
|
||||
import os.path
|
||||
from os import popen
|
||||
from utils import deviceHelper
|
||||
@@ -871,6 +871,25 @@ class MainController(Controller):
|
||||
self.view['discs'].expand_all()
|
||||
return
|
||||
|
||||
def on_export_activate(self, menu_item):
|
||||
"""export db file and coressponding images to tar.bz2 archive"""
|
||||
dialog = Dialogs.ChooseFilename(None, "Choose export file")
|
||||
filepath = dialog.run()
|
||||
|
||||
if not filepath:
|
||||
return
|
||||
|
||||
list_of_paths = self.view['images'].get_selected_items()
|
||||
model = self.view['images'].get_model()
|
||||
|
||||
count = 0
|
||||
|
||||
if len(list_of_paths) == 0:
|
||||
# no picture was selected. default to save all of them
|
||||
for image in model:
|
||||
if self.model.save_image(image[0], filepath):
|
||||
count += 1
|
||||
|
||||
def on_collapse_all1_activate(self, menu_item):
|
||||
self.view['discs'].collapse_all()
|
||||
return
|
||||
@@ -1579,7 +1598,10 @@ class MainController(Controller):
|
||||
buf.insert_with_tags(buf.get_end_iter(), "Filename: ", tag)
|
||||
buf.insert(buf.get_end_iter(), set['filename'] + "\n")
|
||||
buf.insert_with_tags(buf.get_end_iter(), "Date: ", tag)
|
||||
buf.insert(buf.get_end_iter(), str(set['fileinfo']['date']) + "\n")
|
||||
try:
|
||||
buf.insert(buf.get_end_iter(), str(set['fileinfo']['date']) + "\n")
|
||||
except:
|
||||
import ipdb; ipdb.set_trace()
|
||||
buf.insert_with_tags(buf.get_end_iter(), "Size: ", tag)
|
||||
buf.insert(buf.get_end_iter(), str(set['fileinfo']['size']) + "\n")
|
||||
|
||||
|
||||
@@ -23,11 +23,9 @@
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
from shutil import copy
|
||||
from os import path, mkdir
|
||||
from os import path
|
||||
from hashlib import sha512
|
||||
from tempfile import mkstemp
|
||||
|
||||
from utils import EXIF
|
||||
import Image
|
||||
|
||||
class Img(object):
|
||||
@@ -46,14 +44,15 @@ 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")
|
||||
|
||||
|
||||
# check wheter image already exists
|
||||
if path.exists(image_filename) and path.exists(thumbnail):
|
||||
if __debug__:
|
||||
print "image", self.filename, "with hash", self.sha512, "already exist"
|
||||
print "image", self.filename, "with hash",
|
||||
print self.sha512, "already exist"
|
||||
return self.sha512
|
||||
|
||||
if not path.exists(thumbnail):
|
||||
|
||||
@@ -45,10 +45,11 @@ class Qst(object):
|
||||
|
||||
def run(self):
|
||||
retval = self.dialog.run()
|
||||
self.dialog.destroy()
|
||||
retval = False
|
||||
if retval == gtk.RESPONSE_OK:
|
||||
return True
|
||||
return False
|
||||
retval = True
|
||||
self.dialog.destroy()
|
||||
return retval
|
||||
|
||||
class Inf(object):
|
||||
"""Show simple dialog for notices"""
|
||||
@@ -139,10 +140,11 @@ class InputDiskLabel(object):
|
||||
entry = gladexml.get_widget("volname")
|
||||
entry.set_text(self.label)
|
||||
result = dialog.run()
|
||||
dialog.destroy()
|
||||
res = None
|
||||
if result == gtk.RESPONSE_OK:
|
||||
return entry.get_text()
|
||||
return None
|
||||
res = entry.get_text()
|
||||
dialog.destroy()
|
||||
return res
|
||||
|
||||
class InputNewName(object):
|
||||
"""Sepcific dialog for quering user for a disc label"""
|
||||
@@ -158,10 +160,11 @@ class InputNewName(object):
|
||||
entry = gladexml.get_widget("name")
|
||||
entry.set_text(self.name)
|
||||
result = dialog.run()
|
||||
dialog.destroy()
|
||||
res = None
|
||||
if result == gtk.RESPONSE_OK:
|
||||
return entry.get_text()
|
||||
return None
|
||||
res = entry.get_text()
|
||||
dialog.destroy()
|
||||
return res
|
||||
|
||||
class PointDirectoryToAdd(object):
|
||||
"""Sepcific dialog for quering user for selecting directory to add"""
|
||||
@@ -206,7 +209,7 @@ class PointDirectoryToAdd(object):
|
||||
ch = True
|
||||
result = dialog.run()
|
||||
while ch:
|
||||
if result == gtk.RESPONSE_OK and (self.volname.get_text()=='' or \
|
||||
if result == gtk.RESPONSE_OK and (self.volname.get_text() == '' or \
|
||||
self.directory.get_text() == ''):
|
||||
a = Err("Error - pyGTKtalog",
|
||||
"There are fields needed to be filled.",
|
||||
@@ -215,11 +218,15 @@ class PointDirectoryToAdd(object):
|
||||
result = dialog.run()
|
||||
else:
|
||||
ch = False
|
||||
dialog.destroy()
|
||||
|
||||
volname = self.volname.get_text()
|
||||
directory = self.directory.get_text()
|
||||
|
||||
res = (None,None)
|
||||
if result == gtk.RESPONSE_OK:
|
||||
return self.volname.get_text(),self.directory.get_text()
|
||||
else:
|
||||
return None,None
|
||||
res = (volname, directory)
|
||||
dialog.destroy()
|
||||
return res
|
||||
|
||||
class SelectDirectory(object):
|
||||
"""Sepcific dialog for quering user for selecting directory to add"""
|
||||
@@ -257,15 +264,15 @@ class SelectDirectory(object):
|
||||
dialog.destroy()
|
||||
return retval
|
||||
|
||||
class ChooseDBFilename(object):
|
||||
"""Sepcific dialog for quering user for selecting filename for database"""
|
||||
class ChooseFilename(object):
|
||||
"""Dialog for quering user for selecting filename"""
|
||||
|
||||
URI=None
|
||||
|
||||
def __init__(self, path=None):
|
||||
def __init__(self, path=None, title=''):
|
||||
self.path = path
|
||||
self.dialog = gtk.FileChooserDialog(
|
||||
title="Save catalog as...",
|
||||
title="",
|
||||
action=gtk.FILE_CHOOSER_ACTION_SAVE,
|
||||
buttons=(
|
||||
gtk.STOCK_CANCEL,
|
||||
@@ -276,7 +283,44 @@ class ChooseDBFilename(object):
|
||||
self.dialog.set_action(gtk.FILE_CHOOSER_ACTION_SAVE)
|
||||
self.dialog.set_default_response(gtk.RESPONSE_OK)
|
||||
self.dialog.set_do_overwrite_confirmation(True)
|
||||
self.dialog.set_title('Save catalog to file...')
|
||||
self.dialog.set_title(title)
|
||||
|
||||
f = gtk.FileFilter()
|
||||
f.set_name("Catalog files")
|
||||
f.add_pattern("*.sqlite")
|
||||
f.add_pattern("*.sqlite.bz2")
|
||||
self.dialog.add_filter(f)
|
||||
f = gtk.FileFilter()
|
||||
f.set_name("All files")
|
||||
f.add_pattern("*.*")
|
||||
self.dialog.add_filter(f)
|
||||
|
||||
def run(self):
|
||||
if self.URI:
|
||||
self.dialog.set_current_folder_uri(self.URI)
|
||||
elif self.path and os.path.exists(self.path):
|
||||
self.path = "file://"+os.path.abspath(self.path)
|
||||
self.dialog.set_current_folder_uri(self.path)
|
||||
|
||||
response = self.dialog.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
filename = self.dialog.get_filename()
|
||||
self.__class__.URI = self.dialog.get_current_folder_uri()
|
||||
self.dialog.destroy()
|
||||
return filename
|
||||
else:
|
||||
self.dialog.destroy()
|
||||
return None
|
||||
pass
|
||||
|
||||
class ChooseDBFilename(ChooseFilename):
|
||||
"""Sepcific dialog for quering user for selecting filename for database"""
|
||||
|
||||
URI=None
|
||||
|
||||
def __init__(self, path=None):
|
||||
ChooseFilename.__init__(self)
|
||||
self.dialog.set_title('Save catalog as...')
|
||||
|
||||
f = gtk.FileFilter()
|
||||
f.set_name("Catalog files")
|
||||
@@ -298,11 +342,6 @@ class ChooseDBFilename(object):
|
||||
response = self.dialog.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
filename = self.dialog.get_filename()
|
||||
print filename, ' do ',
|
||||
if filename[-11:].lower() != '.sqlite.bz2' and \
|
||||
filename[-7:].lower() != '.sqlite':
|
||||
filename = filename + '.sqlite.bz2'
|
||||
print filename
|
||||
self.__class__.URI = self.dialog.get_current_folder_uri()
|
||||
self.dialog.destroy()
|
||||
return filename
|
||||
@@ -488,10 +527,11 @@ class StatsDialog(object):
|
||||
entry.set_text(str(self.values['size']))
|
||||
|
||||
result = dialog.run()
|
||||
dialog.destroy()
|
||||
retval = None
|
||||
if result == gtk.RESPONSE_OK:
|
||||
return entry.get_text()
|
||||
return None
|
||||
retval = entry.get_text()
|
||||
dialog.destroy()
|
||||
return retval
|
||||
|
||||
class TagsDialog(object):
|
||||
"""Sepcific dialog for display stats"""
|
||||
@@ -507,10 +547,11 @@ class TagsDialog(object):
|
||||
|
||||
result = dialog.run()
|
||||
|
||||
dialog.destroy()
|
||||
retval = None
|
||||
if result == gtk.RESPONSE_OK:
|
||||
return entry.get_text()
|
||||
return None
|
||||
retval = entry.get_text()
|
||||
dialog.destroy()
|
||||
return retval
|
||||
|
||||
class TagsRemoveDialog(object):
|
||||
"""Sepcific dialog for display stats"""
|
||||
@@ -572,14 +613,15 @@ class TagsRemoveDialog(object):
|
||||
|
||||
result = dialog.run()
|
||||
|
||||
dialog.destroy()
|
||||
retval = (None, None)
|
||||
if result == gtk.RESPONSE_OK:
|
||||
ids = []
|
||||
for i in model:
|
||||
if i[2]:
|
||||
ids.append(i[0])
|
||||
return "ok", ids
|
||||
return None, None
|
||||
retval = ("ok", ids)
|
||||
dialog.destroy()
|
||||
return retval
|
||||
|
||||
class EditDialog(object):
|
||||
"""Sepcific dialog for display stats"""
|
||||
|
||||
Reference in New Issue
Block a user