1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """
18 Module responsible for starting the Application
19 """
20
21 __maintainer__ = 'Philippe Normand <philippe@fluendo.com>'
22
23 import pkg_resources
24 import sys, time
25 import os
26 import datetime
27 import locale
28 import gobject
29
30 from elisa.core import common
31 common.boot()
32
33 from elisa.core import __version__, version_info, config, log
34 from elisa.core.utils import exception_hook
35 from elisa.core.bus import bus, bus_message
36 from elisa.core.utils import i18n
37 from elisa.core.utils import classinit
38 from elisa.core import thumbnailer
39 from elisa.core import media_uri
40 from elisa.core.utils.mime_getter import MimeGetter
41
42 from elisa.core import plugin_registry, config_upgrader
43 from elisa.core import input_manager, media_manager, service_manager
44 from elisa.core import metadata_manager
45 from elisa.core import interface_controller, player_registry
46
47 from twisted.internet import reactor, defer, threads
48 from twisted.python import usage
49 from twisted.web import client
50
51 from elisa.extern.translation import Translator
52
53 UPDATES_URL = "http://elisa.fluendo.com/updates/update.php?install_date=%(date)s&version=%(version)s"
54 CONFIG_DIR = os.path.join(os.path.expanduser('~'), ".elisa")
55 CONFIG_FILE = "elisa.conf"
56 TRANS_FILE = "data/translations.lst"
57
58 DEFAULT_CONFIG = """\
59 [general]
60 version = '%(version)s'
61 install_date = '%(install_date)s'
62 media_providers = ['daap:daap_media', 'youtube:youtube_media', 'shoutcast:shoutcast_media', 'fspot:fspot_media', 'coherence:upnp_media', 'ipod:ipod_media', 'base:local_media', 'media_db:elisa_media', 'gvfs:gnomevfs_media', 'audiocd:audiocd_media', 'flickr:flickr_media', 'stage6:stage_media']
63 metadata_providers = ['gstreamer:gst_metadata_client', 'amazon:amazon_covers']
64 service_providers = ['gnome:gnome_screensaver_service', 'hal:hal_service', 'coherence:coherence_service']
65 player_engines = ['base:playbin_engine', 'audiocd:cdda_engine']
66 backends = ['backend1']
67 frontends = ['frontend1']
68
69 [media_scanner]
70 enabled = '1'
71 db_backend = 'sqlite'
72 database = 'elisa.db'
73 fivemin_location_updates = []
74 hourly_location_updates = []
75 daily_location_updates = []
76 weekly_location_updates = []
77 unmonitored_locations = []
78
79 [backend1]
80 activity = 'raval:elisa_activity'
81 mvc_mappings = 'raval:data/raval_mvc_mappings.conf'
82 input_providers = ['lirc:lirc_input']
83
84 [frontend1]
85 backend = 'backend1'
86 theme = 'raval:tango_theme'
87 input_providers = ['pigment:pigment_input']
88
89 [xmlmenu:locations_builder]
90 locations = []
91 auto_locations = 1
92
93 [lirc:lirc_input]
94 # filename of the LIRC config map to use
95 lirc_rc = 'streamzap.lirc'
96 delay = '4'
97 repeat = '1'
98
99 [coherence:coherence_service]
100 logmode = 'none'
101 controlpoint = 'yes'
102
103 [[plugins]]
104
105 [base:service_activity]
106 # a list of activites, which should beappear in the service_menu
107 service_activities = ['service:about_activity']
108
109 [base:local_media]
110 hidden_file_chars = '!.'
111
112 [dvd:dvd_activity]
113 # uri of the dvd. must be file:///* or dvd://
114 dvd_uri = 'dvd://'
115
116 [player]
117 audiosink = 'autoaudiosink'
118
119 [theme_switcher:theme_switcher_activity]
120 # a list of themes 'plugin:component' like 'raval:tango_theme'
121 themes = ['raval:tango_theme', 'raval:poblenou_theme', 'raval:chris_theme']"""
122
123
125 """ Application is the entry point of Elisa. It groups all the necessary
126 elements needed for Elisa to run. It is in charge of instantiating a
127 Config and a PluginRegistry. Application also provides access to
128 input events and data, and holds the user interfaces. It creates
129 various managers (InputManager, MediaManager...),
130 an InterfaceController and a DBBackend.
131
132 @ivar plugin_registry: loads and manages the plugins
133 @type plugin_registry: L{elisa.core.plugin_registry.PluginRegistry}
134 @ivar config: Application's configuration file, storing options
135 @type config: L{elisa.core.config.Config}
136 @ivar bus: DOCME
137 @type bus: L{elisa.core.bus.bus.Bus}
138 @ivar translator: DOCME
139 @type translator: L{elisa.extern.translator.Translator}
140 @ivar metadata_manager: DOCME
141 @type metadata_manager: L{elisa.core.metadata_manager.MetadataManager}
142 @ivar mime_getter: DOCME
143 @type mime_getter: L{elisa.core.utils.mime_getter.MimeGetter}
144 @ivar service_manager: DOCME
145 @type service_manager: L{elisa.core.service_manager.ServiceManager}
146 @ivar player_registry: DOCME
147 @type player_registry: L{elisa.core.player_registry.PlayerRegistry}
148 @ivar interface_controller: DOCME
149 @type interface_controller: L{elisa.core.interface_controller.InterfaceController}
150 @ivar input_manager: DOCME
151 @type input_manager: L{elisa.core.input_manager.InputManager}
152 @ivar media_manager: DOCME
153 @type media_manager: L{elisa.core.media_manager.MediaManager}
154 @ivar thumbnailer: DOCME
155 @type thumbnailer: L{elisa.core.thumbnailer.Thumbnailer}
156 """
157
158
159 __metaclass__ = classinit.ClassInitMeta
160 __classinit__ = classinit.build_properties
161
162 log_category = "application"
163
164 - def __init__(self, config_filename=None, show_tracebacks=False):
207
211
213
214
215 try:
216 locale.getpreferredencoding()
217 except locale.Error, error:
218 self.warning(error)
219 self.warning("Falling back to system locale")
220
221 os.environ['LANG'] = 'C'
222
223 self._translator = Translator()
224
226 if not config_filename:
227 if not os.path.exists(CONFIG_DIR):
228 try:
229 os.makedirs(CONFIG_DIR)
230 except OSError, e:
231 self.warning("Could not create '%s': %s" % (CONFIG_DIR, e))
232 raise
233
234 config_filename = os.path.join(CONFIG_DIR, CONFIG_FILE)
235
236 self.info("Using config file: %r", config_filename)
237 self._config_filename = config_filename
238 today = datetime.date.today().isoformat()
239 default_config = DEFAULT_CONFIG % {'version': __version__,
240 'install_date': today}
241
242 try:
243 cfg = config.Config(config_filename, default_config=default_config)
244 except config.ConfigError, error:
245 self.warning(error)
246 raise
247
248 if not cfg.first_load:
249
250 upgrader = config_upgrader.ConfigUpgrader(cfg, default_config)
251 cfg = upgrader.update_for(version_info)
252
253 self._install_date = cfg.get_option('install_date', section='general',
254 default=today)
255 self._config = cfg
256
258 """
259 This imports profiling modules (TODO)
260 """
261 enable = int(self.config.get_option('enable_profiling', default='0'))
262 if enable:
263
264 pass
265
267 url = UPDATES_URL % {'date': self._install_date, 'version': __version__}
268 dfr = client.getPage(url)
269
270 def got_result(result):
271
272 pass
273
274 dfr.addCallback(got_result)
275
277 """ Override the default system exception hook with our own
278 """
279
280 logdir = None
281
282 params = {'format': 'text', 'logdir': logdir,
283 'file': sys.stderr,
284 'display': self.show_tracebacks}
285 self._except_hook = exception_hook.ExceptionHook(**params)
286 sys.excepthook = self._except_hook
287
288
289
290
292 """ Call this to force the exception hook to handle the exception on top
293 of the stack. This can be handy to use from and except: block.
294 """
295 self._except_hook.handle()
296
298 return self._plugin_registry
299
302
304 return self._translator
305
308
310 return self._mime_getter
311
313 return self._service_manager
314
315
317 return self._player_registry
318
320 return self._interface_controller
321
324
327
329 return self._thumbnailer
330
333
335 def initialize_managers_done(result, managers):
336
337 failed = False
338 first_failure = None
339 initialized = []
340 for i, item in enumerate(result):
341
342
343 res, obj = item
344 manager = managers[i]
345 if res == False:
346 if first_failure is None:
347
348
349 first_failure = obj
350
351 self.warning("Manager %s failed to initialize: %s",
352 manager, result)
353 failed = True
354 else:
355 initialized.append(manager)
356
357 if failed:
358 deferreds = []
359
360 for manager in initialized:
361 deferreds.append(defer.maybeDeferred(manager.stop))
362
363 dfr = defer.DeferredList(deferreds)
364
365
366 dfr.addCallback(lambda result: defer.fail(first_failure))
367 return dfr
368
369 self._player_registry.initialize()
370 self._interface_controller.initialize()
371 self.bus.send_message(bus_message.ComponentsLoaded())
372
373
374
375
376
377 managers = (self._service_manager, self._metadata_manager,
378 self._media_manager, self._input_manager)
379 deferreds = []
380 for manager in managers:
381 self.debug("Initializing manager %s" % manager)
382 deferreds.append(defer.maybeDeferred(manager.initialize))
383
384 dfr = defer.DeferredList(deferreds)
385 dfr.addCallback(initialize_managers_done, managers)
386 return dfr
387
405
408
409 - def stop(self, stop_reactor=True):
410 """Stop the application.
411
412 @param stop_reactor: stop the reactor after stopping the application
413 @type stop_reactor: bool
414 @rtype: L{twisted.internet.defer.Deferred}
415 """
416
417 def interface_controller_stopped(result):
418 self.info("Stopping managers")
419 self.player_registry.deinitialize()
420 self.thumbnailer.stop()
421
422 manager_deferreds = []
423 for manager in (self.service_manager, self.metadata_manager,
424 self.media_manager, self.input_manager):
425 manager_deferreds.append(defer.maybeDeferred(manager.stop))
426
427 dfr = defer.DeferredList(manager_deferreds)
428 dfr.addCallback(managers_stopped)
429 return dfr
430
431 def managers_stopped(managers):
432 self.info("Stopping reactor")
433 self.bus.stop()
434
435 self.running = False
436
437 if self.config:
438 self.config.write()
439
440 if stop_reactor and reactor.running:
441 reactor.stop()
442
443 if self.running:
444
445
446 self.info("Stopping interface controller")
447 dfr = self.interface_controller.stop()
448 dfr.addCallback(interface_controller_stopped)
449 else:
450 dfr = defer.succeed(None)
451
452 return dfr
453
455 """
456 Application's command-line options definitions
457 """
458
459 optFlags = [['version', '', 'show elisa version'],
460 ['twisted-version', '', 'show twisted version'],
461 ['tracebacks', 't', 'display tracebacks'],
462 ['log', 'l', 'log output in elisa.log files'],
463 ]
464
465 tracebacks = False
466
469
472
474 print 'Elisa version %s' % __version__
475 sys.exit(0)
476
477
478 -def main(args=None):
479 """ Parse command-line options and start a new Application in the
480 Twisted's reactor.
481 """
482 if not args:
483 args = sys.argv
484
485 options = Options()
486 try:
487 options.parseOptions(args[1:])
488 except usage.UsageError, errortext:
489 print '%s: %s' % (args[0], errortext)
490 print '%s: Try --help for usage details.' % (args[0])
491 sys.exit(1)
492
493 try:
494 app = Application(options['config_file'],
495 show_tracebacks=options['tracebacks'])
496 except Exception, exc:
497 print 'Error', exc
498 if options['tracebacks']:
499 raise
500 else:
501 def initialize_done(result):
502 return app.start()
503
504 def initialize_failure(failure):
505 print 'Elisa failed to initialize', failure
506 reactor.stop()
507
508
509
510 dfr = app.initialize()
511 dfr.addCallbacks(initialize_done, initialize_failure)
512 reactor.run()
513 dfr = app.stop(stop_reactor=False)
514