Recording a todo
[watersofshiloah] / src / mormonchannel_gtk.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 @todo Add python-support support, http://svn.debian.org/viewsvn/collab-maint/deb-maint/python-support/trunk/README
6 @todo Use XDG
7 @todo Need to confirm id's are persistent (not just for todos but broken behavior on transition)
8         @todo Track recent
9         @todo Persisted Pause
10         @todo Favorites
11 @todo Sleep timer
12 @todo Podcast integration
13         @todo Default with BYU Devotionals, http://speeches.byu.edu/?act=help&page=podcast
14         @todo Mormon Messages
15 @todo Reverse order option.  Toggle between playing ascending/descending chronological order
16 @todo Jump to website
17 @todo Offline mode
18 @todo Re-use windows for better performance
19 @todo Make radio program updates only happen when the app has focus to reduce CPU wakes
20 """
21
22 from __future__ import with_statement
23
24 import os
25 import gc
26 import logging
27 import ConfigParser
28
29 import gobject
30 import dbus
31 import dbus.mainloop.glib
32 import gtk
33
34 try:
35         import osso
36 except ImportError:
37         osso = None
38
39 import constants
40 import hildonize
41 import util.misc as misc_utils
42
43 import imagestore
44 import player
45 import stream_index
46 import windows
47
48
49 _moduleLogger = logging.getLogger(__name__)
50 PROFILE_STARTUP = False
51
52
53 class MormonChannelProgram(hildonize.get_app_class()):
54
55         def __init__(self):
56                 super(MormonChannelProgram, self).__init__()
57                 currentPath = os.path.abspath(__file__)
58                 storePath = os.path.join(os.path.split(os.path.dirname(currentPath))[0], "data")
59                 self._store = imagestore.ImageStore(storePath, constants._cache_path_)
60                 self._index = stream_index.AudioIndex()
61                 self._player = player.Player(self._index)
62
63                 self._store.start()
64                 self._index.start()
65                 try:
66                         if not hildonize.IS_HILDON_SUPPORTED:
67                                 _moduleLogger.info("No hildonization support")
68
69                         if osso is not None:
70                                 self._osso_c = osso.Context(constants.__app_name__, constants.__version__, False)
71                                 self._deviceState = osso.DeviceState(self._osso_c)
72                                 self._deviceState.set_device_state_callback(self._on_device_state_change, 0)
73                         else:
74                                 _moduleLogger.info("No osso support")
75                                 self._osso_c = None
76                                 self._deviceState = None
77
78                         self._sourceSelector = windows.source.SourceSelector(self, self._player, self._store, self._index)
79                         self._sourceSelector.window.connect("destroy", self._on_destroy)
80                         self._sourceSelector.window.set_default_size(400, 800)
81                         self._sourceSelector.show()
82                         self._load_settings()
83                 except:
84                         self._index.stop()
85                         self._store.stop()
86                         raise
87
88         def _save_settings(self):
89                 config = ConfigParser.SafeConfigParser()
90
91                 self._sourceSelector.save_settings(config, "Windows")
92
93                 with open(constants._user_settings_, "wb") as configFile:
94                         config.write(configFile)
95
96         def _load_settings(self):
97                 config = ConfigParser.SafeConfigParser()
98                 config.read(constants._user_settings_)
99
100                 self._sourceSelector.load_settings(config, "Windows")
101
102         @misc_utils.log_exception(_moduleLogger)
103         def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
104                 """
105                 For system_inactivity, we have no background tasks to pause
106
107                 @note Hildon specific
108                 """
109                 if memory_low:
110                         gc.collect()
111
112                 if save_unsaved_data or shutdown:
113                         self._save_settings()
114
115         @misc_utils.log_exception(_moduleLogger)
116         def _on_destroy(self, widget = None, data = None):
117                 try:
118                         self.quit()
119                 finally:
120                         gtk.main_quit()
121
122         def quit(self):
123                 try:
124                         self._save_settings()
125                 except Exception:
126                         _moduleLogger.exception("Error saving settigns")
127
128                 try:
129                         self._player.stop()
130                 except Exception:
131                         _moduleLogger.exception("Error stopping player")
132                 try:
133                         self._index.stop()
134                 except Exception:
135                         _moduleLogger.exception("Error stopping index")
136                 try:
137                         self._store.stop()
138                 except Exception:
139                         _moduleLogger.exception("Error stopping store")
140
141                 try:
142                         self._deviceState.close()
143                 except AttributeError:
144                         pass # Either None or close was removed (in Fremantle)
145                 except Exception:
146                         _moduleLogger.exception("Error closing device state")
147                 try:
148                         self._osso_c.close()
149                 except AttributeError:
150                         pass # Either None or close was removed (in Fremantle)
151                 except Exception:
152                         _moduleLogger.exception("Error closing osso state")
153
154
155 def run():
156         gobject.threads_init()
157         gtk.gdk.threads_init()
158         l = dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
159
160         # HACK Playback while silent on Maemo 5
161         hildonize.set_application_name("FMRadio")
162
163         app = MormonChannelProgram()
164         if not PROFILE_STARTUP:
165                 try:
166                         gtk.main()
167                 except KeyboardInterrupt:
168                         app.quit()
169                         raise
170         else:
171                 app.quit()
172
173
174 if __name__ == "__main__":
175         logging.basicConfig(level=logging.DEBUG)
176         run()