Passed
Pull Request — master (#443)
by Jaisen
05:38
created

elodie.media.base.Base.set_checksum()   A

Complexity

Conditions 1

Size

Total Lines 9
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 2
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
"""
2
The base module provides a base :class:`Base` class for all objects that
3
are tracked by Elodie. The Base class provides some base functionality used
4
by all the media types, but isn't itself used to represent anything. Its
5
sub-classes (:class:`~elodie.media.audio.Audio`,
6
:class:`~elodie.media.photo.Photo`, :class:`~elodie.media.video.Video`, and
7
:class:`~elodie.media.text.Text`)
8
are used to represent the actual files.
9
10
.. moduleauthor:: Jaisen Mathai <[email protected]>
11
"""
12
13
import mimetypes
14
import os
15
16
try:        # Py3k compatibility
17
    basestring
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable basestring does not seem to be defined.
Loading history...
18
except NameError:
19
    basestring = (bytes, str)
20
21
22
class Base(object):
23
24
    """The base class for all media objects.
25
26
    :param str source: The fully qualified path to the video file.
27
    """
28
29
    __name__ = 'Base'
30
31
    extensions = ()
32
33
    def __init__(self, source=None):
34
        self.source = source
35
        self.reset_cache()
36
37
    def format_metadata(self, **kwargs):
38
        """Method to consistently return a populated metadata dictionary.
39
40
        :returns: dict
41
        """
42
43
    def get_album(self):
44
        """Base method for getting an album
45
46
        :returns: None
47
        """
48
        return None
49
50
    def get_file_path(self):
51
        """Get the full path to the video.
52
53
        :returns: string
54
        """
55
        return self.source
56
57
    def get_coordinate(self, type):
58
        return None
59
60
    def get_extension(self):
61
        """Get the file extension as a lowercased string.
62
63
        :returns: string or None for a non-video
64
        """
65
        if(not self.is_valid()):
66
            return None
67
68
        source = self.source
69
        return os.path.splitext(source)[1][1:].lower()
70
71
    def get_camera_make(self):
72
        return None
73
74
    def get_camera_model(self):
75
        return None
76
77
    def get_checksum(self):
78
        self.get_metadata()
79
        return self.metadata['checksum']
80
81
    def get_metadata(self, update_cache=False):
82
        """Get a dictionary of metadata for any file.
83
84
        All keys will be present and have a value of None if not obtained.
85
86
        :returns: dict or None for non-text files
87
        """
88
        if(not self.is_valid()):
89
            return None
90
91
        if(isinstance(self.metadata, dict) and update_cache is False):
92
            return self.metadata
93
94
        source = self.source
95
96
        self.metadata = {
97
            'checksum': None,
98
            'date_taken': self.get_date_taken(),
99
            'camera_make': self.get_camera_make(),
100
            'camera_model': self.get_camera_model(),
101
            'latitude': self.get_coordinate('latitude'),
102
            'longitude': self.get_coordinate('longitude'),
103
            'album': self.get_album(),
104
            'title': self.get_title(),
105
            'mime_type': self.get_mimetype(),
106
            'original_name': self.get_original_name(),
107
            'base_name': os.path.splitext(os.path.basename(source))[0],
108
            'extension': self.get_extension(),
109
            'directory_path': os.path.dirname(source)
110
        }
111
112
        return self.metadata
113
114
    def get_mimetype(self):
115
        """Get the mimetype of the file.
116
117
        :returns: str or None for unsupported files.
118
        """
119
        if(not self.is_valid()):
120
            return None
121
122
        source = self.source
123
        mimetype = mimetypes.guess_type(source)
124
        if(mimetype is None):
125
            return None
126
127
        return mimetype[0]
128
129
    def get_original_name(self):
130
        """Get the original name of the file from before it was imported.
131
        Does not include the extension.
132
        Overridden by Media class for files with EXIF.
133
134
        :returns: str or None for unsupported files.
135
        """
136
        return None
137
138
    def get_title(self):
139
        """Base method for getting the title of a file
140
141
        :returns: None
142
        """
143
        return None
144
145
    def is_valid(self):
146
        """Check the file extension against valid file extensions.
147
148
        The list of valid file extensions come from self.extensions.
149
150
        :returns: bool
151
        """
152
        source = self.source
153
        return os.path.splitext(source)[1][1:].lower() in self.extensions
154
155
    def reset_cache(self):
156
        """Resets any internal cache
157
        """
158
        self.metadata = None
159
160
    def set_album(self, name):
161
        """Base method for setting the album of a file
162
163
        :returns: None
164
        """
165
        return None
166
167
    def set_album_from_folder(self):
168
        """Set the album attribute based on the leaf folder name
169
170
        :returns: bool
171
        """
172
        metadata = self.get_metadata()
173
174
        # If this file has an album already set we do not overwrite EXIF
175
        if(not isinstance(metadata, dict) or metadata['album'] is not None):
176
            return False
177
178
        folder = os.path.basename(metadata['directory_path'])
179
        # If folder is empty we skip
180
        if(len(folder) == 0):
181
            return False
182
183
        self.set_album(folder)
184
        return True
185
186
    def set_checksum(self, new_checksum):
187
        """Add the checksum to the metadata.
188
189
        The checksum in `metadata` is passed in and not derived from the file itself.
190
        It was added to pass in to plugins.
191
        See gh-68.
192
        """
193
        self.get_metadata()
194
        self.metadata['checksum'] = new_checksum
195
196
    def set_metadata_basename(self, new_basename):
197
        """Update the basename attribute in the metadata dict for this instance.
198
199
        This is used for when we update the EXIF title of a media file. Since
200
        that determines the name of a file if we update the title of a file
201
        more than once it appends to the file name.
202
203
        i.e. 2015-12-31_00-00-00-my-first-title-my-second-title.jpg
204
205
        :param str new_basename: New basename of file (with the old title
206
            removed).
207
        """
208
        self.get_metadata()
209
        self.metadata['base_name'] = new_basename
210
211
    def set_metadata(self, **kwargs):
212
        """Method to manually update attributes in metadata.
213
214
        :params dict kwargs: Named parameters to update.
215
        """
216
        metadata = self.get_metadata()
217
        for key in kwargs:
218
            if(key in metadata):
219
                self.metadata[key] = kwargs[key]
220
221
    def set_original_name(self):
222
        """Stores the original file name into EXIF/metadata.
223
        :returns: bool
224
        """
225
        return False
226
227
    @classmethod
228
    def get_class_by_file(cls, _file, classes):
229
        """Static method to get a media object by file.
230
        """
231
        if not isinstance(_file, basestring) or not os.path.isfile(_file):
232
            return None
233
234
        extension = os.path.splitext(_file)[1][1:].lower()
235
236
        if len(extension) > 0:
237
            for i in classes:
238
                if(extension in i.extensions):
239
                    return i(_file)
240
241
        return None
242
243
    @classmethod
244
    def get_valid_extensions(cls):
245
        """Static method to access static extensions variable.
246
247
        :returns: tuple(str)
248
        """
249
        return cls.extensions
250
251
252
def get_all_subclasses(cls=None):
253
    """Module method to get all subclasses of Base.
254
    """
255
    subclasses = set()
256
257
    this_class = Base
258
    if cls is not None:
259
        this_class = cls
260
261
    subclasses.add(this_class)
262
263
    this_class_subclasses = this_class.__subclasses__()
264
    for child_class in this_class_subclasses:
265
        subclasses.update(get_all_subclasses(child_class))
266
267
    return subclasses
268