~ubuntu-branches/ubuntu/oneiric/calibre/oneiric

« back to all changes in this revision

Viewing changes to src/calibre/library/add_to_library.py

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2010-06-21 10:18:08 UTC
  • mfrom: (1.3.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20100621101808-aue828f532tmo4zt
Tags: 0.7.2+dfsg-1
* New major upstream version. See http://calibre-ebook.com/new-in/seven for
  details.
* Refresh patches to apply cleanly.
* debian/control: Bump python-cssutils to >= 0.9.7~ to ensure the existence
  of the CSSRuleList.rulesOfType attribute. This makes epub conversion work
  again. (Closes: #584756)
* Add debian/local/calibre-mount-helper: Simple and safe replacement for
  upstream's calibre-mount-helper, using udisks --mount and eject.
  (Closes: #584915, LP: #561958)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
 
3
 
 
4
__license__   = 'GPL v3'
 
5
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
 
6
__docformat__ = 'restructuredtext en'
 
7
 
 
8
import os
 
9
from hashlib import sha1
 
10
 
 
11
from calibre.constants import filesystem_encoding
 
12
from calibre.ebooks import BOOK_EXTENSIONS
 
13
 
 
14
def find_folders_under(root, db, add_root=True, # {{{
 
15
        follow_links=False, cancel_callback=lambda : False):
 
16
    '''
 
17
    Find all folders under the specified root path, ignoring any folders under
 
18
    the library path of db
 
19
 
 
20
    root must be a bytestring in filesystem_encoding
 
21
 
 
22
    If follow_links is True, follow symbolic links. WARNING; this can lead to
 
23
    infinite recursion.
 
24
 
 
25
    cancel_callback must be a no argument callable that returns True to cancel
 
26
    the search
 
27
    '''
 
28
    assert not isinstance(root, unicode) # root must be in filesystem encoding
 
29
    lp = db.library_path
 
30
    if isinstance(lp, unicode):
 
31
        try:
 
32
            lp = lp.encode(filesystem_encoding)
 
33
        except:
 
34
            lp = None
 
35
    if lp:
 
36
        lp = os.path.abspath(lp)
 
37
 
 
38
    root = os.path.abspath(root)
 
39
 
 
40
    ans = set([])
 
41
    for dirpath, dirnames, __ in os.walk(root, topdown=True, followlinks=follow_links):
 
42
        if cancel_callback():
 
43
            break
 
44
        for x in list(dirnames):
 
45
            path = os.path.join(dirpath, x)
 
46
            if lp and path.startswith(lp):
 
47
                dirnames.remove(x)
 
48
        if lp and dirpath.startswith(lp):
 
49
            continue
 
50
        ans.add(dirpath)
 
51
 
 
52
    if not add_root:
 
53
        ans.remove(root)
 
54
 
 
55
    return ans
 
56
 
 
57
# }}}
 
58
 
 
59
class FormatCollection(object): # {{{
 
60
 
 
61
    def __init__(self, parent_folder, formats):
 
62
        self.path_map = {}
 
63
        for x in set(formats):
 
64
            fmt = os.path.splitext(x)[1].lower()
 
65
            if fmt:
 
66
                fmt = fmt[1:]
 
67
                self.path_map[fmt] = x
 
68
        self.parent_folder = None
 
69
        self.hash_map = {}
 
70
        for fmt, path in self.format_map.items():
 
71
            self.hash_map[fmt] = self.hash_of_file(path)
 
72
 
 
73
    def hash_of_file(self, path):
 
74
        with open(path, 'rb') as f:
 
75
            return sha1(f.read()).digest()
 
76
 
 
77
    @property
 
78
    def hashes(self):
 
79
        return frozenset(self.formats.values())
 
80
 
 
81
    @property
 
82
    def is_empty(self):
 
83
        return len(self) == 0
 
84
 
 
85
    def __iter__(self):
 
86
        for x in self.path_map:
 
87
            yield x
 
88
 
 
89
    def __len__(self):
 
90
        return len(self.path_map)
 
91
 
 
92
    def remove(self, fmt):
 
93
        self.hash_map.pop(fmt, None)
 
94
        self.path_map.pop(fmt, None)
 
95
 
 
96
    def matches(self, other):
 
97
        if not self.hashes.intersection(other.hashes):
 
98
            return False
 
99
        for fmt in self:
 
100
            if self.hash_map[fmt] != other.hash_map.get(fmt, False):
 
101
                return False
 
102
        return True
 
103
 
 
104
    def merge(self, other):
 
105
        for fmt in list(other):
 
106
            self.path_map[fmt] = other.path_map[fmt]
 
107
            self.hash_map[fmt] = other.hash_map[fmt]
 
108
            other.remove(fmt)
 
109
 
 
110
# }}}
 
111
 
 
112
def books_in_folder(folder, one_per_folder, # {{{
 
113
        cancel_callback=lambda : False):
 
114
    assert not isinstance(folder, unicode)
 
115
 
 
116
    dirpath = os.path.abspath(folder)
 
117
    if one_per_folder:
 
118
        formats = set([])
 
119
        for path in os.listdir(dirpath):
 
120
            if cancel_callback():
 
121
                return []
 
122
            path = os.path.abspath(os.path.join(dirpath, path))
 
123
            if os.path.isdir(path) or not os.access(path, os.R_OK):
 
124
                continue
 
125
            ext = os.path.splitext(path)[1]
 
126
            if not ext:
 
127
                continue
 
128
            ext = ext[1:].lower()
 
129
            if ext not in BOOK_EXTENSIONS and ext != 'opf':
 
130
                continue
 
131
            formats.add(path)
 
132
        return [FormatCollection(folder, formats)]
 
133
    else:
 
134
        books = {}
 
135
        for path in os.listdir(dirpath):
 
136
            if cancel_callback():
 
137
                return
 
138
            path = os.path.abspath(os.path.join(dirpath, path))
 
139
            if os.path.isdir(path) or not os.access(path, os.R_OK):
 
140
                continue
 
141
            ext = os.path.splitext(path)[1]
 
142
            if not ext:
 
143
                continue
 
144
            ext = ext[1:].lower()
 
145
            if ext not in BOOK_EXTENSIONS:
 
146
                continue
 
147
 
 
148
            key = os.path.splitext(path)[0]
 
149
            if not books.has_key(key):
 
150
                books[key] = set([])
 
151
            books[key].add(path)
 
152
 
 
153
        return [FormatCollection(folder, x) for x in books.values() if x]
 
154
 
 
155
# }}}
 
156
 
 
157
def hash_merge_format_collections(collections, cancel_callback=lambda:False):
 
158
    ans = []
 
159
 
 
160
    collections = list(collections)
 
161
    l = len(collections)
 
162
    for i in range(l):
 
163
        if cancel_callback():
 
164
            return collections
 
165
        one = collections[i]
 
166
        if one.is_empty:
 
167
            continue
 
168
        for j in range(i+1, l):
 
169
            if cancel_callback():
 
170
                return collections
 
171
            two = collections[j]
 
172
            if two.is_empty:
 
173
                continue
 
174
            if one.matches(two):
 
175
                one.merge(two)
 
176
        ans.append(one)
 
177
 
 
178
    return ans