Package elisa :: Package plugins :: Package base :: Module local_media
[hide private]
[frames] | no frames]

Source Code for Module elisa.plugins.base.local_media

  1  # -*- coding: utf-8 -*- 
  2  # Elisa - Home multimedia server 
  3  # Copyright (C) 2006-2008 Fluendo Embedded S.L. (www.fluendo.com). 
  4  # All rights reserved. 
  5  # 
  6  # This file is available under one of two license agreements. 
  7  # 
  8  # This file is licensed under the GPL version 3. 
  9  # See "LICENSE.GPL" in the root of this distribution including a special 
 10  # exception to use Elisa with Fluendo's plugins. 
 11  # 
 12  # The GPL part of Elisa is also available under a commercial licensing 
 13  # agreement from Fluendo. 
 14  # See "LICENSE.Elisa" in the root directory of this distribution package 
 15  # for details on that license. 
 16   
 17  """ 
 18  LocalMediaProvider component class 
 19  """ 
 20   
 21   
 22  __maintainer__ = 'Florian Boucault <florian@fluendo.com>' 
 23  __maintainer2__ = 'Philippe Normand <philippe@fluendo.com>' 
 24   
 25  from elisa.base_components.media_provider import MediaProvider, UriNotMonitorable 
 26  from elisa.base_components.media_provider import NotifyEvent 
 27  from elisa.core import common 
 28  from elisa.core.media_uri import MediaUri 
 29  from elisa.core.media_file import MediaFile 
 30  from elisa.core.utils import misc, sorting 
 31  from elisa.extern import path 
 32   
 33  from twisted.internet import reactor 
 34   
 35  try: 
 36      from elisa.extern.coherence import inotify 
 37  except ImportError: 
 38      inotify = None 
 39   
 40  import os 
 41  import shutil 
 42  import re 
 43   
 44  from twisted.internet import defer, task, threads 
 45   
 46   
47 -class LocalMedia(MediaProvider):
48 """ 49 This class implements local filesystem support 50 """ 51 52 hidden_fnmatch_pattern = '[!.]*' 53 54 default_config = {'hidden_file_chars': '!.'} 55 56
57 - def __init__(self):
58 MediaProvider.__init__(self) 59 self._paths = {}
60
61 - def initialize(self):
62 ret = super(LocalMedia, self).initialize() 63 self._hidden_file_chars = self._get_option('hidden_file_chars') 64 65 return ret
66
67 - def _get_option(self, name):
68 try: 69 return self.config[name] 70 except KeyError: 71 return self.default_config[name]
72
74 return { 'file': 0 }
75
76 - def get_media_type(self, uri):
77 if os.path.isdir(uri.path): 78 return defer.succeed({'file_type': 'directory', 'mime_type': ''}) 79 80 metadata_manager = common.application.metadata_manager 81 media_db = common.application.media_manager.media_db 82 83 if media_db: 84 infos = media_db.get_media_information(uri, extended=False) 85 if infos: 86 file_type = infos.format 87 if infos.typ: 88 mime_type = infos.typ 89 else: 90 mime_type = None 91 media_type = {'file_type': file_type, 'mime_type': mime_type} 92 self.debug('returning cached media type %s for uri %s', 93 media_type, uri) 94 return defer.succeed({'file_type': file_type, 95 'mime_type': mime_type}) 96 97 def get_metadata_done(metadata): 98 self.debug("got metadata for %s: %s", uri, metadata) 99 100 del metadata['uri'] 101 102 return metadata
103 104 def get_metadata_failure(failure): 105 self.debug("error getting media type for uri %s: %s", uri, failure) 106 107 return failure
108 109 req_metadata = {'uri': uri, 'file_type': None, 'mime_type': None} 110 get_metadata_dfr = metadata_manager.get_metadata(req_metadata) 111 get_metadata_dfr.addCallbacks(get_metadata_done, get_metadata_failure) 112 113 return get_metadata_dfr 114
115 - def is_directory(self, uri):
116 return defer.succeed(os.path.isdir(uri.path))
117
118 - def has_children_with_types(self, uri, media_types):
119 if not os.path.isdir(uri.path): 120 return defer.succeed(False) 121 122 try: 123 contents = os.listdir(uri.path) 124 except OSError, error: 125 self.info("Could not list directory %r: %s", path, error) 126 return defer.succeed(False) 127 128 if len(contents) == 0: 129 return defer.succeed(False) 130 131 media_manager = common.application.media_manager 132 133 # we go over all the children until we find one that matches media_types 134 has_children = False 135 136 def get_media_type_done(metadata): 137 if not media_types or metadata['file_type'] in media_types: 138 global hash_children 139 has_children = True
140 141 def get_media_type_failure(failure): 142 # consume it 143 return None 144 145 def children_iter(): 146 for child in contents: 147 if has_children: 148 break 149 150 if len(child) == 0 or \ 151 child[0] in self._hidden_file_chars: 152 yield None 153 continue 154 155 try: 156 child_uri = uri.join(child) 157 except UnicodeDecodeError, e: 158 self.warning(e) 159 # skip this child 160 yield None 161 continue 162 163 get_media_type_dfr = media_manager.get_media_type(child_uri) 164 get_media_type_dfr.addCallbacks(get_media_type_done, 165 get_media_type_failure) 166 167 yield get_media_type_dfr 168 169 def children_iter_done(iterator): 170 return has_children 171 172 dfr = task.coiterate(children_iter()) 173 dfr.addCallback(children_iter_done) 174 175 return dfr 176
177 - def get_direct_children(self, uri, list_of_children):
178 if not os.path.isdir(uri.path): 179 return list_of_children 180 181 contents = os.listdir(uri.path) 182 183 def natural_sort_done(result): 184 def children_iter(): 185 folders = [] 186 files = [] 187 188 for filename in contents: 189 if len(filename) == 0 or \ 190 filename[0] in self._hidden_file_chars: 191 yield None 192 continue 193 194 file_path = os.path.join(uri.path, filename) 195 196 parts = {'scheme': 'file', 'path': file_path} 197 file_uri = MediaUri(parts) 198 metadata = {} 199 try: 200 metadata['fs_mtime'] = os.stat(file_path).st_mtime 201 except OSError: 202 pass 203 204 item = (file_uri, metadata) 205 206 if os.path.isdir(file_path): 207 folders.append(item) 208 else: 209 files.append(item) 210 211 yield None 212 213 for lst in (folders, files): 214 for item in lst: 215 list_of_children.append(item) 216 yield None
217 218 def children_iter_done(iterator): 219 return list_of_children 220 221 dfr = task.coiterate(children_iter()) 222 dfr.addCallback(children_iter_done) 223 224 return dfr 225 226 # sort directory contents alpha-numerically 227 dfr = threads.deferToThread(sorting.natural_sort, contents) 228 dfr.addCallback(natural_sort_done) 229 230 return dfr 231
232 - def _blocking_open(self, uri, mode='r'):
233 ret = None 234 if not os.path.isdir(uri.path): 235 try: 236 handle = open(uri.path, mode) 237 except Exception, exc: 238 self.info("Could not open %s : %s" % (uri, exc)) 239 else: 240 ret = MediaFile(self, handle) 241 return ret
242
243 - def next_location(self, uri, root=None):
244 return threads.deferToThread(self._blocking_next_location, uri, 245 root=root)
246
247 - def _blocking_next_location(self, uri, root=None):
248 next_uri = None 249 250 local = uri.path 251 252 if os.path.isdir(local): 253 dir_path = local 254 else: 255 dir_path = os.path.dirname(local) 256 257 root_path = None 258 walk_gen = None 259 if root: 260 root_path = root.path 261 walk_gen = self._paths.get(root_path) 262 else: 263 for walker_root, walker in self._paths.iteritems(): 264 if local.startswith(walker_root): 265 walk_gen = walker 266 root_path = walker_root 267 break 268 269 if not root_path: 270 root_path = dir_path 271 272 end = False 273 if not walk_gen: 274 p = path.path(root_path) 275 walk_gen = p.walk(self.hidden_fnmatch_pattern, errors='ignore') 276 self._paths[root_path] = walk_gen 277 if root_path != local: 278 try: 279 n = walk_gen.next() 280 except StopIteration: 281 end = True 282 else: 283 while n != local: 284 try: 285 n = walk_gen.next() 286 except StopIteration: 287 del self._paths[root_path] 288 end = True 289 break 290 291 if not end: 292 try: 293 next_file = walk_gen.next() 294 except StopIteration: 295 del self._paths[root_path] 296 next_file = None 297 298 if next_file: 299 parts = {'scheme': 'file', 'path': next_file} 300 next_uri = MediaUri(parts) 301 302 self.debug("Next URI of %s: %s", uri, next_uri) 303 return next_uri
304
305 - def previous_location(self, uri):
306 return threads.deferToThread(self._blocking_previous_location, uri)
307
308 - def _blocking_previous_location(self, uri):
309 if uri == None: 310 return None 311 312 if os.path.isdir(uri.path): 313 dir_path = uri.path 314 else: 315 dir_path = os.path.dirname(uri.path) 316 317 p = path.path(dir_path) 318 walk_gen = p.walk(self.hidden_fnmatch_pattern, errors='ignore') 319 320 p = dir_path 321 if dir_path != uri.path: 322 while True: 323 try: 324 n = walk_gen.next() 325 if n == uri.path: 326 break 327 else: 328 p = n 329 except StopIteration: 330 return None 331 332 if p == uri.path: 333 return None 334 if p: 335 parts = {'scheme': 'file', 'path': p} 336 return MediaUri(parts) 337 return None
338
339 - def monitor_uri(self, uri, callback, *extra_args):
340 341 def inotify_cb(iwp, filename, mask, parameter): 342 media_uri = MediaUri(unicode("file://%s/%s" % (iwp.path, filename))) 343 event = self._inotify_event_type(mask) 344 return callback(media_uri, {}, event,uri, *extra_args)
345 346 if self.uri_is_monitorable(uri): 347 try: 348 inotify_instance = inotify.INotify() 349 except SystemError, error: 350 self.warning(error) 351 raise UriNotMonitorable(uri) 352 else: 353 path = uri.path.encode('utf-8') 354 355 self.info("Starting to monitor %r", path) 356 try: 357 inotify_instance.watch(path, auto_add = False, \ 358 recursive=False, \ 359 mask = inotify.IN_CREATE \ 360 | inotify.IN_DELETE \ 361 | inotify.IN_ATTRIB \ 362 | inotify.IN_MOVED_TO \ 363 | inotify.IN_MOVED_FROM, \ 364 callbacks=(inotify_cb, None)) 365 except IOError, error: 366 raise UriNotMonitorable(uri) 367 else: 368 raise UriNotMonitorable(uri) 369
370 - def _inotify_event_type(self, mask):
371 if mask & inotify.IN_CREATE or mask & inotify.IN_MOVED_TO: 372 return NotifyEvent.ADDED 373 elif mask & inotify.IN_DELETE or mask & inotify.IN_UNMOUNT or mask & inotify.IN_MOVED_FROM: 374 return NotifyEvent.REMOVED 375 elif mask & inotify.IN_ATTRIB: 376 return NotifyEvent.MODIFIED 377 else: 378 # FIXME: exception should be raised 379 inotify_instance = inotify.INotify() 380 human = inotify_instance.flag_to_human(mask) 381 self.info("Unexpected inotify event: %s" % human) 382 return NotifyEvent.REMOVED
383 384
385 - def unmonitor_uri(self, uri):
386 self.debug("Stopping %r monitoring", uri) 387 inotify.INotify().ignore(uri.path)
388 389
390 - def uri_is_monitorable(self, uri):
391 monitorable = False 392 home = os.path.expanduser('~') 393 if inotify != None: 394 inotify_instance = inotify.INotify() 395 try: 396 inotify_instance.watch(home) 397 except UriNotMonitorable: 398 monitorable = False 399 else: 400 monitorable = True 401 inotify_instance.ignore(home) 402 return monitorable
403
404 - def uri_is_monitored(self, uri):
405 return inotify and inotify.INotify().is_watched(uri.path)
406
407 - def copy(self, orig_uri, dest_uri, recursive=False):
408 try: 409 if recursive: 410 shutil.copytree(orig_uri.path, dest_uri.path) 411 else: 412 shutil.copy(orig_uri.path, dest_uri.path) 413 except Exception, e: 414 raise e
415
416 - def move(self, orig_uri, dest_uri):
417 try: 418 shutil.move(orig_uri.path, dest_uri.path) 419 except shutil.Error, e: 420 raise e
421 422
423 - def delete(self, uri, recursive=False):
424 try: 425 if recursive: 426 shutil.rmtree(uri.path) 427 else: 428 os.remove(uri.path) 429 except shutil.Error, e: 430 raise e
431