Merge lp:entertainer/future into lp:entertainer
- entertainer-future
- Merge into trunk
Proposed by
Matt Layman
Status: | Merged |
---|---|
Approved by: | Matt Layman |
Approved revision: | not available |
Merge reported by: | Matt Layman |
Merged at revision: | not available |
Proposed branch: | lp:entertainer/future |
Merge into: | lp:entertainer |
Diff against target: |
17252 lines (+6079/-7223) 180 files modified
MANIFEST.in (+7/-0) README (+6/-0) cfg/content.conf (+22/-14) cfg/preferences.conf (+0/-16) docs/COPYING (+1/-0) docs/DEPENDENCIES (+1/-1) entertainer (+2/-1) entertainer-backend (+1/-5) entertainer-manager (+6/-7) entertainer-preferences (+0/-14) entertainer-server (+7/-0) entertainerlib/backend/backend_server.py (+5/-16) entertainerlib/backend/components/feeds/feed_fetcher.py (+2/-2) entertainerlib/backend/components/feeds/feed_manager.py (+3/-4) entertainerlib/backend/components/feeds/feed_utils.py (+1/-2) entertainerlib/backend/components/mediacache/image_cache.py (+4/-6) entertainerlib/backend/components/mediacache/media_cache_manager.py (+9/-9) entertainerlib/backend/components/mediacache/music_cache.py (+5/-5) entertainerlib/backend/components/mediacache/video_cache.py (+3/-3) entertainerlib/backend/components/mediacache/video_metadata_search.py (+2/-2) entertainerlib/backend/core/client_connection.py (+1/-1) entertainerlib/backend/core/connection_server.py (+1/-1) entertainerlib/backend/core/message_bus.py (+2/-13) entertainerlib/backend/core/message_bus_proxy.py (+2/-2) entertainerlib/backend/core/message_type_priority.py (+9/-38) entertainerlib/client/__init__.py (+9/-9) entertainerlib/client/backend_connection.py (+1/-29) entertainerlib/client/client.py (+41/-47) entertainerlib/client/media_player.py (+15/-9) entertainerlib/client/medialibrary/feeds.py (+1/-1) entertainerlib/client/medialibrary/images.py (+4/-13) entertainerlib/client/medialibrary/music.py (+6/-15) entertainerlib/client/medialibrary/videos.py (+9/-18) entertainerlib/client/translation_setup.py (+36/-26) entertainerlib/configuration.py (+257/-489) entertainerlib/db/connection.py (+1/-1) entertainerlib/db/models.py (+3/-2) entertainerlib/dialog.py (+1113/-0) entertainerlib/download.py (+455/-0) entertainerlib/gui/screen_history.py (+2/-2) entertainerlib/gui/screens/album.py (+5/-6) entertainerlib/gui/screens/artist.py (+5/-5) entertainerlib/gui/screens/audio_play.py (+4/-4) entertainerlib/gui/screens/disc.py (+9/-11) entertainerlib/gui/screens/factory.py (+20/-19) entertainerlib/gui/screens/feed.py (+6/-6) entertainerlib/gui/screens/feed_entry.py (+6/-6) entertainerlib/gui/screens/main.py (+11/-17) entertainerlib/gui/screens/movie.py (+13/-10) entertainerlib/gui/screens/music.py (+7/-6) entertainerlib/gui/screens/photo.py (+20/-21) entertainerlib/gui/screens/photo_albums.py (+16/-17) entertainerlib/gui/screens/photographs.py (+6/-7) entertainerlib/gui/screens/question.py (+3/-3) entertainerlib/gui/screens/rss.py (+6/-6) entertainerlib/gui/screens/screen.py (+4/-6) entertainerlib/gui/screens/tv_episodes.py (+7/-7) entertainerlib/gui/screens/tv_series.py (+5/-6) entertainerlib/gui/screens/video.py (+6/-5) entertainerlib/gui/screens/video_osd.py (+38/-22) entertainerlib/gui/screens/weather.py (+6/-5) entertainerlib/gui/system_tray_icon.py (+24/-31) entertainerlib/gui/tabs/albums_tab.py (+7/-6) entertainerlib/gui/tabs/artists_tab.py (+7/-7) entertainerlib/gui/tabs/lyrics_tab.py (+4/-4) entertainerlib/gui/tabs/movies_tab.py (+7/-6) entertainerlib/gui/tabs/playing_tab.py (+3/-3) entertainerlib/gui/tabs/series_tab.py (+6/-6) entertainerlib/gui/tabs/tab.py (+6/-6) entertainerlib/gui/tabs/tracks_tab.py (+7/-6) entertainerlib/gui/tabs/video_clips_tab.py (+7/-6) entertainerlib/gui/transitions/factory.py (+8/-7) entertainerlib/gui/transitions/fade.py (+1/-1) entertainerlib/gui/transitions/no_effect.py (+2/-1) entertainerlib/gui/transitions/slide.py (+15/-11) entertainerlib/gui/transitions/zoom_and_fade.py (+1/-1) entertainerlib/gui/user_event.py (+1/-1) entertainerlib/gui/user_interface.py (+38/-46) entertainerlib/gui/widgets/arrow_texture.py (+24/-15) entertainerlib/gui/widgets/base.py (+6/-5) entertainerlib/gui/widgets/clock_label.py (+1/-1) entertainerlib/gui/widgets/eyecandy_texture.py (+3/-3) entertainerlib/gui/widgets/grid_menu.py (+24/-20) entertainerlib/gui/widgets/image_menu.py (+10/-6) entertainerlib/gui/widgets/label.py (+11/-11) entertainerlib/gui/widgets/list_indicator.py (+5/-4) entertainerlib/gui/widgets/loading_animation.py (+12/-16) entertainerlib/gui/widgets/menu_item.py (+1/-1) entertainerlib/gui/widgets/menu_overlay.py (+3/-3) entertainerlib/gui/widgets/progress_bar.py (+6/-7) entertainerlib/gui/widgets/reflection_texture.py (+6/-6) entertainerlib/gui/widgets/rounded_texture.py (+6/-5) entertainerlib/gui/widgets/scroll_area.py (+6/-7) entertainerlib/gui/widgets/scroll_menu.py (+5/-5) entertainerlib/gui/widgets/selector.py (+20/-23) entertainerlib/gui/widgets/special_behaviours.py (+0/-2) entertainerlib/gui/widgets/tab_group.py (+8/-7) entertainerlib/gui/widgets/text_menu.py (+13/-12) entertainerlib/gui/widgets/texture.py (+3/-3) entertainerlib/gui/widgets/volume_indicator.py (+9/-5) entertainerlib/indexing/__init__.py (+1/-0) entertainerlib/indexing/handlers.py (+144/-0) entertainerlib/indexing/indexer.py (+57/-0) entertainerlib/indexing/utilities.py (+49/-0) entertainerlib/logger.py (+2/-2) entertainerlib/network/__init__.py (+27/-0) entertainerlib/network/local/__init__.py (+1/-0) entertainerlib/network/local/client.py (+35/-0) entertainerlib/network/local/commands.py (+14/-0) entertainerlib/network/local/server.py (+40/-0) entertainerlib/network/storage.py (+1/-1) entertainerlib/storage/__init__.py (+0/-5) entertainerlib/tests/__init__.py (+4/-8) entertainerlib/tests/mock.py (+24/-42) entertainerlib/tests/test_arrowtexture.py (+4/-3) entertainerlib/tests/test_base.py (+3/-2) entertainerlib/tests/test_configuration.py (+40/-129) entertainerlib/tests/test_database.py (+1/-1) entertainerlib/tests/test_eyecandytexture.py (+3/-2) entertainerlib/tests/test_feedconfigtools.py (+8/-6) entertainerlib/tests/test_feedentryparser.py (+1/-1) entertainerlib/tests/test_filehandlers.py (+165/-0) entertainerlib/tests/test_frontendfeed.py (+3/-3) entertainerlib/tests/test_frontendfeedentry.py (+3/-3) entertainerlib/tests/test_frontendfeedlibrary.py (+8/-6) entertainerlib/tests/test_gridmenu.py (+3/-3) entertainerlib/tests/test_imagemenu.py (+3/-3) entertainerlib/tests/test_imagemenuitem.py (+3/-3) entertainerlib/tests/test_indexer.py (+56/-0) entertainerlib/tests/test_indexerutilities.py (+32/-0) entertainerlib/tests/test_label.py (+3/-3) entertainerlib/tests/test_listindicator.py (+3/-2) entertainerlib/tests/test_loadinganimation.py (+3/-3) entertainerlib/tests/test_logger.py (+1/-1) entertainerlib/tests/test_lyricsdownloader.py (+2/-1) entertainerlib/tests/test_mediaplayer.py (+3/-3) entertainerlib/tests/test_menuitem.py (+2/-2) entertainerlib/tests/test_models.py (+1/-204) entertainerlib/tests/test_motionbuffer.py (+2/-2) entertainerlib/tests/test_music.py (+3/-9) entertainerlib/tests/test_opmlparser.py (+1/-1) entertainerlib/tests/test_progressbar.py (+2/-2) entertainerlib/tests/test_reflectiontexture.py (+3/-3) entertainerlib/tests/test_resources.py (+39/-0) entertainerlib/tests/test_roundedtexture.py (+3/-2) entertainerlib/tests/test_screen.py (+7/-7) entertainerlib/tests/test_screenfactory.py (+21/-21) entertainerlib/tests/test_screenhistory.py (+3/-3) entertainerlib/tests/test_scrollarea.py (+3/-6) entertainerlib/tests/test_scrollmenu.py (+2/-2) entertainerlib/tests/test_storage.py (+1/-2) entertainerlib/tests/test_tabgroup.py (+4/-4) entertainerlib/tests/test_textmenu.py (+2/-2) entertainerlib/tests/test_textmenuitem.py (+2/-2) entertainerlib/tests/test_texture.py (+3/-3) entertainerlib/tests/test_theme.py (+3/-3) entertainerlib/tests/test_transitionfactory.py (+9/-8) entertainerlib/tests/test_userinterface.py (+5/-15) entertainerlib/tests/test_volumeindicator.py (+3/-2) entertainerlib/tests/test_weather.py (+1/-1) entertainerlib/thumbnailer.py (+1/-1) entertainerlib/uis/log_dialog.ui (+55/-57) entertainerlib/uis/manager.ui (+1885/-1317) entertainerlib/uis/open_feed_source_dialog.ui (+54/-51) entertainerlib/uis/system_tray_icon_menu.ui (+51/-77) entertainerlib/utils/__init__.py (+0/-2) entertainerlib/utils/albumart_downloader.py (+0/-346) entertainerlib/utils/cd_utils.py (+0/-8) entertainerlib/utils/content_management_dialog.py (+0/-782) entertainerlib/utils/glade/entertainer-preferences.glade (+0/-1223) entertainerlib/utils/log_viewer.py (+0/-177) entertainerlib/utils/lyrics_downloader.py (+0/-112) entertainerlib/utils/open_feed_source_dialog.py (+0/-142) entertainerlib/utils/preferences_dialog.py (+0/-315) entertainerlib/weather.py (+1/-1) pylintrc (+1/-1) setup.py (+21/-19) tools/messagebus-notifier (+0/-51) tools/po/entertainer.pot (+544/-658) tools/translations_generator.py (+7/-7) |
To merge this branch: | bzr merge lp:entertainer/future |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Matt Layman | Approve | ||
Review via email: mp+16371@code.launchpad.net |
Commit message
Merge in all the good work that had been happening in future. This should get trunk working again. It was badly neglected.
Description of the change
To post a comment you must log in.
Revision history for this message
Matt Layman (mblayman) wrote : | # |
lp:entertainer/future
updated
- 382. By Matt Layman
-
Removes some crufty backend connection messages that did nothing. (Matt Layman)
- 383. By Matt Layman
-
Converted glade to gtk builder. (Francesco Marella)
- 384. By Matt Layman
-
Fix Open feed dialog. (Francesco Marella)
- 385. By Matt Layman
-
Final cleanup of clutter 1.0 transition. Fixed the fullscreen size problem.
(Matt Layman)
Revision history for this message
Matt Layman (mblayman) wrote : | # |
Discussed on IRC with Paul. Trunk is so broken that this needs to be done.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'MANIFEST.in' |
2 | --- MANIFEST.in 2008-10-05 17:11:31 +0000 |
3 | +++ MANIFEST.in 2010-01-23 03:34:10 +0000 |
4 | @@ -1,3 +1,10 @@ |
5 | include entertainer* |
6 | +include docs/COPYING docs/DEPENDENCIES |
7 | +include docs/entertainer.1 docs/entertainer.desktop |
8 | recursive-include cfg * |
9 | +recursive-include entertainerlib * |
10 | +recursive-include icons * |
11 | +recursive-include themes * |
12 | +recursive-include tools * |
13 | +global-exclude *pyc |
14 | |
15 | |
16 | === added file 'README' |
17 | --- README 1970-01-01 00:00:00 +0000 |
18 | +++ README 2010-01-23 03:34:10 +0000 |
19 | @@ -0,0 +1,6 @@ |
20 | +To install Entertainer, run: |
21 | + |
22 | +python setup.py install |
23 | + |
24 | +For information on how to contribute, read docs/HACKING. |
25 | + |
26 | |
27 | === modified file 'cfg/content.conf' |
28 | --- cfg/content.conf 2009-05-31 17:11:16 +0000 |
29 | +++ cfg/content.conf 2010-01-23 03:34:11 +0000 |
30 | @@ -1,26 +1,34 @@ |
31 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
32 | -[Images] |
33 | -folders = |
34 | -display_hidden_files_folders = False |
35 | +[Media] |
36 | +download_lyrics = False |
37 | +download_album_art = True |
38 | +download_metadata = True |
39 | +display_eject_in_menu = False |
40 | +folders = |
41 | |
42 | [Weather] |
43 | location = Bath,England |
44 | display_in_menu = True |
45 | -metric_units = True |
46 | - |
47 | -[Music] |
48 | -folders = |
49 | -download_lyrics = False |
50 | -download_album_art = True |
51 | - |
52 | -[Videos] |
53 | -folders = |
54 | -download_metadata = True |
55 | |
56 | [RSS] |
57 | -feeds = http://theironlion.net/feeds/blog;http://www.joshuascotton.com/main/archives/tag/entertainer/feed;http://laymanstermsdev.wordpress.com/feed |
58 | +feeds = http://theironlion.net/blog/feed;http://www.joshuascotton.com/main/archives/tag/entertainer/feed;http://laymanstermsdev.wordpress.com/feed |
59 | fetch_interval = 15 |
60 | |
61 | [CD] |
62 | display_eject_in_menu = False |
63 | |
64 | +[Photographs] |
65 | +slideshow_step = 5 |
66 | + |
67 | +[General] |
68 | +stage_width = 1366 |
69 | +stage_height = 768 |
70 | +show_effects = True |
71 | +start_in_fullscreen = True |
72 | +theme = Default |
73 | +backend_port = 45054 |
74 | +history_size = 8 |
75 | +transition_effect = Slide |
76 | +start_server_auto = True |
77 | +display_icon = False |
78 | + |
79 | |
80 | === removed file 'cfg/preferences.conf' |
81 | --- cfg/preferences.conf 2009-05-06 03:40:22 +0000 |
82 | +++ cfg/preferences.conf 1970-01-01 00:00:00 +0000 |
83 | @@ -1,16 +0,0 @@ |
84 | -# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
85 | -[Photographs] |
86 | -slideshow_step = 5 |
87 | - |
88 | -[General] |
89 | -stage_width = 1366 |
90 | -stage_height = 768 |
91 | -show_effects = True |
92 | -start_in_fullscreen = True |
93 | -theme = Default |
94 | -backend_port = 45054 |
95 | -history_size = 8 |
96 | -transition_effect = Slide |
97 | -start_server_auto = True |
98 | -display_icon = False |
99 | - |
100 | |
101 | === modified file 'docs/COPYING' |
102 | --- docs/COPYING 2009-05-09 15:45:18 +0000 |
103 | +++ docs/COPYING 2010-01-23 03:34:11 +0000 |
104 | @@ -359,6 +359,7 @@ |
105 | GNU General Public License for more details. |
106 | |
107 | Entertainer Developers, as referenced in each file's copyright, refers to: |
108 | + * Francesco Marella <francesco.marella@gmail.com> |
109 | * Jamie Bennett <jamie@linuxuk.org> |
110 | * Joshua Scotton <josh@joshuascotton.com> |
111 | * Lauri Taimila <lauri@taimila.com> |
112 | |
113 | === modified file 'docs/DEPENDENCIES' |
114 | --- docs/DEPENDENCIES 2009-05-09 19:08:39 +0000 |
115 | +++ docs/DEPENDENCIES 2010-01-23 03:34:11 +0000 |
116 | @@ -4,7 +4,6 @@ |
117 | python-ctypes |
118 | python-eyed3 |
119 | python-feedparser |
120 | -python-glade2 |
121 | python-gobject |
122 | python-gst0.10 |
123 | python-gtk2 |
124 | @@ -14,3 +13,4 @@ |
125 | python-pyvorbis |
126 | python-storm |
127 | python-twisted |
128 | +python-xdg |
129 | |
130 | === modified file 'entertainer' |
131 | --- entertainer 2009-05-06 02:58:08 +0000 |
132 | +++ entertainer 2010-01-23 03:34:10 +0000 |
133 | @@ -1,8 +1,9 @@ |
134 | #!/usr/bin/env python |
135 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
136 | +'''Main client executable''' |
137 | '''Main frontend executable''' |
138 | |
139 | -from entertainerlib.frontend import main |
140 | +from entertainerlib.client import main |
141 | |
142 | if __name__ == '__main__': |
143 | main() |
144 | |
145 | === modified file 'entertainer-backend' |
146 | --- entertainer-backend 2009-05-06 02:58:08 +0000 |
147 | +++ entertainer-backend 2010-01-23 03:34:10 +0000 |
148 | @@ -51,11 +51,7 @@ |
149 | sys.exit(0) |
150 | |
151 | if len(sys.argv) > 1 and sys.argv[1] == "--foreground": |
152 | - try: |
153 | - backend = BackendServer() |
154 | - except KeyboardInterrupt: |
155 | - backend.quitBackend() |
156 | - sys.exit() |
157 | + backend = BackendServer() |
158 | else: |
159 | print "Entertainer backend starting..." |
160 | libc = ctypes.CDLL('libc.so.6') |
161 | |
162 | === renamed file 'entertainer-content-manager' => 'entertainer-manager' |
163 | --- entertainer-content-manager 2009-04-28 23:54:49 +0000 |
164 | +++ entertainer-manager 2010-01-23 03:34:10 +0000 |
165 | @@ -1,15 +1,14 @@ |
166 | #!/usr/bin/env python |
167 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
168 | -'''Content Management executable''' |
169 | +'''Manager executable''' |
170 | |
171 | import gtk |
172 | |
173 | -from entertainerlib.frontend.translation_setup import TranslationSetup |
174 | +from entertainerlib.client.translation_setup import TranslationSetup |
175 | TranslationSetup() |
176 | |
177 | -from entertainerlib.utils.content_management_dialog import ( |
178 | - ContentManagementDialog) |
179 | - |
180 | - |
181 | -ContentManagementDialog(True) |
182 | +from entertainerlib.dialog import ManagerDialog |
183 | + |
184 | + |
185 | +ManagerDialog(True) |
186 | gtk.main() |
187 | |
188 | === removed file 'entertainer-preferences' |
189 | --- entertainer-preferences 2009-04-28 23:54:49 +0000 |
190 | +++ entertainer-preferences 1970-01-01 00:00:00 +0000 |
191 | @@ -1,14 +0,0 @@ |
192 | -#!/usr/bin/env python |
193 | -# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
194 | -'''A preferences editing tool''' |
195 | - |
196 | -import gtk |
197 | - |
198 | -from entertainerlib.frontend.translation_setup import TranslationSetup |
199 | -TranslationSetup() |
200 | - |
201 | -from entertainerlib.utils.preferences_dialog import PreferencesDialog |
202 | - |
203 | - |
204 | -PreferencesDialog(True) |
205 | -gtk.main() |
206 | |
207 | === added file 'entertainer-server' |
208 | --- entertainer-server 1970-01-01 00:00:00 +0000 |
209 | +++ entertainer-server 2010-01-23 03:34:10 +0000 |
210 | @@ -0,0 +1,7 @@ |
211 | +#!/usr/bin/env python |
212 | +'''Server executable''' |
213 | + |
214 | +from entertainerlib.network import server_main |
215 | + |
216 | +server_main() |
217 | + |
218 | |
219 | === modified file 'entertainerlib/backend/backend_server.py' |
220 | --- entertainerlib/backend/backend_server.py 2009-05-06 02:58:08 +0000 |
221 | +++ entertainerlib/backend/backend_server.py 2010-01-23 03:34:11 +0000 |
222 | @@ -3,8 +3,8 @@ |
223 | |
224 | import gobject |
225 | |
226 | -from entertainerlib.utils.configuration import Configuration |
227 | -from entertainerlib.utils.logger import Logger |
228 | +from entertainerlib.configuration import Configuration |
229 | +from entertainerlib.logger import Logger |
230 | |
231 | # Entertainer backend core |
232 | from entertainerlib.backend.core.message import Message |
233 | @@ -39,18 +39,13 @@ |
234 | self.config = Configuration() |
235 | self.logger = Logger().getLogger('backend.BackendServer') |
236 | self.message_bus = MessageBus() |
237 | - self._port = self.config.get_port() |
238 | + self._port = self.config.port |
239 | |
240 | # Connection server - Thread that listens incoming socket connections |
241 | self.connection_server = None |
242 | |
243 | - # Config files |
244 | - self.content_config = None |
245 | - self.preferences = None |
246 | - |
247 | self.scheduler = None |
248 | self.feed_manager = None |
249 | - self.guide_updater = None |
250 | self.media_manager = None |
251 | |
252 | # The order of the initialize method calls is significant! Don't change |
253 | @@ -65,15 +60,13 @@ |
254 | """Initialize configuration""" |
255 | cfg_dict = { |
256 | MessageType.CONTENT_CONF_UPDATED : MessagePriority.VERY_HIGH, |
257 | - MessageType.PREFERENCES_CONF_UPDATED : MessagePriority.VERY_HIGH |
258 | } |
259 | self.message_bus.registerMessageHandler(self.config, cfg_dict) |
260 | self.logger.debug("Configuration intialized successfully") |
261 | |
262 | def initialize_connection_server(self): |
263 | """Initialize connection server.""" |
264 | - self.connection_server = ConnectionServer(self._port, |
265 | - self.message_bus) |
266 | + self.connection_server = ConnectionServer(self._port, self.message_bus) |
267 | # Start listening incoming connections |
268 | self.connection_server.start() |
269 | |
270 | @@ -81,7 +74,7 @@ |
271 | """Initialize message scheduler.""" |
272 | self.scheduler = MessageScheduler(self.message_bus) |
273 | self.scheduler.addMessage(Message(MessageType.UPDATE_FEEDS), |
274 | - 60 * self.config.get_feed_fetch_interval()) |
275 | + 60 * self.config.feed_fetch_interval) |
276 | self.logger.debug("Message scheduler intialized successfully") |
277 | |
278 | def initialize_feed_manager(self): |
279 | @@ -105,7 +98,3 @@ |
280 | self.message_bus.registerMessageHandler(self.media_manager, media_dict) |
281 | self.logger.debug("Media Manager intialized successfully") |
282 | |
283 | - def quitBackend(self): |
284 | - '''Close the backend server''' |
285 | - self.message_bus.unregisterAllMessageHandlers() |
286 | - |
287 | |
288 | === modified file 'entertainerlib/backend/components/feeds/feed_fetcher.py' |
289 | --- entertainerlib/backend/components/feeds/feed_fetcher.py 2009-05-06 02:58:08 +0000 |
290 | +++ entertainerlib/backend/components/feeds/feed_fetcher.py 2010-01-23 03:34:11 +0000 |
291 | @@ -7,8 +7,8 @@ |
292 | from datetime import datetime |
293 | from pysqlite2 import dbapi2 as sqlite |
294 | |
295 | -from entertainerlib.utils.configuration import Configuration |
296 | -from entertainerlib.utils.logger import Logger |
297 | +from entertainerlib.configuration import Configuration |
298 | +from entertainerlib.logger import Logger |
299 | |
300 | # Messaging system |
301 | from entertainerlib.backend.core.message import Message |
302 | |
303 | === modified file 'entertainerlib/backend/components/feeds/feed_manager.py' |
304 | --- entertainerlib/backend/components/feeds/feed_manager.py 2009-05-06 02:58:08 +0000 |
305 | +++ entertainerlib/backend/components/feeds/feed_manager.py 2010-01-23 03:34:11 +0000 |
306 | @@ -5,8 +5,8 @@ |
307 | from pysqlite2 import dbapi2 as sqlite |
308 | from entertainerlib.backend.components.feeds.feed_fetcher import FeedFetcher |
309 | |
310 | -from entertainerlib.utils.configuration import Configuration |
311 | -from entertainerlib.utils.logger import Logger |
312 | +from entertainerlib.configuration import Configuration |
313 | +from entertainerlib.logger import Logger |
314 | |
315 | # Messaging system |
316 | from entertainerlib.backend.core.message_type_priority import MessageType |
317 | @@ -35,8 +35,7 @@ |
318 | Update all feeds to cache in a new thread and after |
319 | that emit FEED_DB_UPDATED message to the messagebus. |
320 | """ |
321 | - fetch_thread = FeedFetcher(self.message_bus, |
322 | - self.config.get_feeds()) |
323 | + fetch_thread = FeedFetcher(self.message_bus, self.config.feeds) |
324 | fetch_thread.start() |
325 | |
326 | def createFeedCacheDatabase(self): |
327 | |
328 | === renamed file 'entertainerlib/utils/feed_utils.py' => 'entertainerlib/backend/components/feeds/feed_utils.py' |
329 | --- entertainerlib/utils/feed_utils.py 2009-05-06 02:02:20 +0000 |
330 | +++ entertainerlib/backend/components/feeds/feed_utils.py 2010-01-23 03:34:11 +0000 |
331 | @@ -6,9 +6,8 @@ |
332 | from xml.dom import minidom |
333 | |
334 | import gtk |
335 | -import gtk.glade |
336 | |
337 | -from entertainerlib.utils.configuration import Configuration |
338 | +from entertainerlib.configuration import Configuration |
339 | |
340 | |
341 | class FeedEntryParser: |
342 | |
343 | === modified file 'entertainerlib/backend/components/mediacache/image_cache.py' |
344 | --- entertainerlib/backend/components/mediacache/image_cache.py 2009-06-01 09:55:40 +0000 |
345 | +++ entertainerlib/backend/components/mediacache/image_cache.py 2010-01-23 03:34:11 +0000 |
346 | @@ -8,8 +8,8 @@ |
347 | from pysqlite2 import dbapi2 as sqlite |
348 | |
349 | from entertainerlib.thumbnailer import ImageThumbnailer |
350 | -from entertainerlib.utils.configuration import Configuration |
351 | -from entertainerlib.utils.logger import Logger |
352 | +from entertainerlib.configuration import Configuration |
353 | +from entertainerlib.logger import Logger |
354 | |
355 | from entertainerlib.backend.components.mediacache.cache import Cache |
356 | |
357 | @@ -117,15 +117,13 @@ |
358 | "Path doesn't exist: " + path) |
359 | else: |
360 | for root, dirs, files in os.walk(path): |
361 | - if os.path.split(root)[-1][0] == "." and not \ |
362 | - self.config.display_hidden_files_folders(): |
363 | + if os.path.split(root)[-1][0] == ".": |
364 | continue |
365 | if not self.isDirectoryInCache(root): |
366 | self._addAlbum(root) |
367 | |
368 | for name in files: |
369 | - if os.path.split(name)[-1][0] == "." and not \ |
370 | - self.config.display_hidden_files_folders(): |
371 | + if os.path.split(name)[-1][0] == ".": |
372 | continue |
373 | if self.isSupportedFormat(name): |
374 | self.addFile(os.path.join(root, name)) |
375 | |
376 | === modified file 'entertainerlib/backend/components/mediacache/media_cache_manager.py' |
377 | --- entertainerlib/backend/components/mediacache/media_cache_manager.py 2009-05-06 02:58:08 +0000 |
378 | +++ entertainerlib/backend/components/mediacache/media_cache_manager.py 2010-01-23 03:34:11 +0000 |
379 | @@ -1,8 +1,8 @@ |
380 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
381 | '''MediaCacheManager - Downloads metadata and keeps media cache up-to-date''' |
382 | |
383 | -from entertainerlib.utils.configuration import Configuration |
384 | -from entertainerlib.utils.logger import Logger |
385 | +from entertainerlib.configuration import Configuration |
386 | +from entertainerlib.logger import Logger |
387 | |
388 | from entertainerlib.backend.core.message_type_priority import MessageType |
389 | from entertainerlib.backend.core.message_handler import MessageHandler |
390 | @@ -13,7 +13,7 @@ |
391 | from entertainerlib.backend.components.mediacache.video_cache import VideoCache |
392 | |
393 | class MediaCacheManager(MessageHandler): |
394 | - """Makes sure that frontend has all the data available.""" |
395 | + """Makes sure that client has all the data available.""" |
396 | |
397 | def __init__(self): |
398 | """ |
399 | @@ -23,11 +23,11 @@ |
400 | self.logger = Logger().getLogger( |
401 | 'backend.components.mediacache.MediaCacheManager') |
402 | self.config = Configuration() |
403 | - self.video_folders = self.config.get_video_folders() |
404 | + self.video_folders = self.config.media_folders |
405 | self._index_videos(self.video_folders) |
406 | - self.music_folders = self.config.get_music_folders() |
407 | + self.music_folders = self.config.media_folders |
408 | self._index_music(self.music_folders) |
409 | - self.image_folders = self.config.get_image_folders() |
410 | + self.image_folders = self.config.media_folders |
411 | self._index_images(self.image_folders) |
412 | |
413 | # Should we rebuild to detect files that were removed while backend was |
414 | @@ -104,9 +104,9 @@ |
415 | we need to index them. If folders are removed, we need to remove |
416 | them from the cache and also from FileSystemObeserver. |
417 | """ |
418 | - updated_video_folders = self.config.get_video_folders() |
419 | - updated_music_folders = self.config.get_music_folders() |
420 | - updated_image_folders = self.config.get_image_folders() |
421 | + updated_video_folders = self.config.media_folders |
422 | + updated_music_folders = self.config.media_folders |
423 | + updated_image_folders = self.config.media_folders |
424 | |
425 | # Handle image folder changes |
426 | current_images = set(self.image_folders) |
427 | |
428 | === modified file 'entertainerlib/backend/components/mediacache/music_cache.py' |
429 | --- entertainerlib/backend/components/mediacache/music_cache.py 2009-05-09 17:03:51 +0000 |
430 | +++ entertainerlib/backend/components/mediacache/music_cache.py 2010-01-23 03:34:11 +0000 |
431 | @@ -9,11 +9,11 @@ |
432 | import ogg.vorbis |
433 | from pysqlite2 import dbapi2 as sqlite |
434 | |
435 | -from entertainerlib.utils.albumart_downloader import AlbumArtDownloader |
436 | -from entertainerlib.utils.configuration import Configuration |
437 | -from entertainerlib.utils.logger import Logger |
438 | - |
439 | from entertainerlib.backend.components.mediacache.cache import Cache |
440 | +from entertainerlib.configuration import Configuration |
441 | +from entertainerlib.download import AlbumArtDownloader |
442 | +from entertainerlib.logger import Logger |
443 | + |
444 | |
445 | class MusicCache(Cache): |
446 | """ |
447 | @@ -433,7 +433,7 @@ |
448 | album_art_file) |
449 | # Local not found -> try internet |
450 | else: |
451 | - if self.config.download_album_art(): |
452 | + if self.config.download_album_art: |
453 | if album != "Unknown album" and artist != "Unknown Artist": |
454 | loader_thread = AlbumArtDownloader(album, artist, |
455 | self.config.ALBUM_ART_DIR) |
456 | |
457 | === modified file 'entertainerlib/backend/components/mediacache/video_cache.py' |
458 | --- entertainerlib/backend/components/mediacache/video_cache.py 2009-05-06 02:58:08 +0000 |
459 | +++ entertainerlib/backend/components/mediacache/video_cache.py 2010-01-23 03:34:11 +0000 |
460 | @@ -6,8 +6,8 @@ |
461 | from pysqlite2 import dbapi2 as sqlite |
462 | |
463 | from entertainerlib.thumbnailer import VideoThumbnailer |
464 | -from entertainerlib.utils.configuration import Configuration |
465 | -from entertainerlib.utils.logger import Logger |
466 | +from entertainerlib.configuration import Configuration |
467 | +from entertainerlib.logger import Logger |
468 | |
469 | from entertainerlib.backend.components.mediacache.cache import Cache |
470 | from entertainerlib.backend.components.mediacache.video_metadata_search import ( |
471 | @@ -243,7 +243,7 @@ |
472 | VALUES (:fn)""", |
473 | { "fn" : filename } ) |
474 | self.__db_conn.commit() |
475 | - if self.config.download_video_metadata(): |
476 | + if self.config.download_metadata: |
477 | self.__searchMetadata(filename) |
478 | |
479 | def __searchMetadata(self, filename): |
480 | |
481 | === modified file 'entertainerlib/backend/components/mediacache/video_metadata_search.py' |
482 | --- entertainerlib/backend/components/mediacache/video_metadata_search.py 2009-05-06 02:58:08 +0000 |
483 | +++ entertainerlib/backend/components/mediacache/video_metadata_search.py 2010-01-23 03:34:11 +0000 |
484 | @@ -9,8 +9,8 @@ |
485 | import threading |
486 | from pysqlite2 import dbapi2 as sqlite |
487 | |
488 | -from entertainerlib.utils.logger import Logger |
489 | -from entertainerlib.utils.configuration import Configuration |
490 | +from entertainerlib.logger import Logger |
491 | +from entertainerlib.configuration import Configuration |
492 | |
493 | class VideoMetadataSearch(threading.Thread): |
494 | """ |
495 | |
496 | === modified file 'entertainerlib/backend/core/client_connection.py' |
497 | --- entertainerlib/backend/core/client_connection.py 2009-05-06 02:58:08 +0000 |
498 | +++ entertainerlib/backend/core/client_connection.py 2010-01-23 03:34:11 +0000 |
499 | @@ -8,7 +8,7 @@ |
500 | # Messaging system |
501 | from entertainerlib.backend.core.message_handler import MessageHandler |
502 | |
503 | -from entertainerlib.utils.logger import Logger |
504 | +from entertainerlib.logger import Logger |
505 | |
506 | class ClientConnection(threading.Thread, MessageHandler): |
507 | """ |
508 | |
509 | === modified file 'entertainerlib/backend/core/connection_server.py' |
510 | --- entertainerlib/backend/core/connection_server.py 2009-05-06 02:58:08 +0000 |
511 | +++ entertainerlib/backend/core/connection_server.py 2010-01-23 03:34:11 +0000 |
512 | @@ -7,7 +7,7 @@ |
513 | |
514 | from entertainerlib.backend.core.client_connection import ClientConnection |
515 | |
516 | -from entertainerlib.utils.logger import Logger |
517 | +from entertainerlib.logger import Logger |
518 | |
519 | class ConnectionServer(threading.Thread): |
520 | """ |
521 | |
522 | === modified file 'entertainerlib/backend/core/message_bus.py' |
523 | --- entertainerlib/backend/core/message_bus.py 2009-05-06 02:58:08 +0000 |
524 | +++ entertainerlib/backend/core/message_bus.py 2010-01-23 03:34:11 +0000 |
525 | @@ -6,7 +6,7 @@ |
526 | from entertainerlib.backend.core.message import Message |
527 | from entertainerlib.backend.core.message_handler import MessageHandler |
528 | from entertainerlib.backend.core.message_type_priority import MessageType |
529 | -from entertainerlib.utils.logger import Logger |
530 | +from entertainerlib.logger import Logger |
531 | |
532 | class MessageBus: |
533 | """ |
534 | @@ -23,12 +23,7 @@ |
535 | When MessageHandler is registered to the MessageBus there is also another |
536 | parameter besides handler itself. Second parameter is a dictionary that |
537 | defines MessageTypes that registered handler wants to be notified of and |
538 | - also priorities for those message types. |
539 | - |
540 | - Example of second parameter: |
541 | - dict = {MessageType.FRONTEND_OPENED : MessagePriority.LOW , |
542 | - MessageType.FRONTEND_CLOSED : MessagePriority.NORMAL } |
543 | - """ |
544 | + also priorities for those message types.""" |
545 | |
546 | # This determines number of message types avaialble. In other words, this |
547 | # variable tells how many variables is defined in MessageType class. |
548 | @@ -89,12 +84,6 @@ |
549 | self.logger.debug("MessageHandler '" + str(message_handler) + |
550 | "' unregistered from the message bus.") |
551 | |
552 | - def unregisterAllMessageHandlers(self): |
553 | - """ |
554 | - Unregisters all MessageHandler from this MessageBus. |
555 | - """ |
556 | - self.message_handlers[:] = [] |
557 | - |
558 | def notifyMessage(self, message): |
559 | """ |
560 | Emit a new Message to this MessageBus. |
561 | |
562 | === modified file 'entertainerlib/backend/core/message_bus_proxy.py' |
563 | --- entertainerlib/backend/core/message_bus_proxy.py 2009-05-06 02:58:08 +0000 |
564 | +++ entertainerlib/backend/core/message_bus_proxy.py 2010-01-23 03:34:11 +0000 |
565 | @@ -6,7 +6,7 @@ |
566 | import threading |
567 | from cStringIO import StringIO |
568 | |
569 | -from entertainerlib.utils.configuration import Configuration |
570 | +from entertainerlib.configuration import Configuration |
571 | |
572 | class MessageBusProxy(threading.Thread): |
573 | """ |
574 | @@ -57,7 +57,7 @@ |
575 | MessageHandler. |
576 | """ |
577 | # Open socket |
578 | - self.socket_to_server.connect(('localhost', self.config.get_port())) |
579 | + self.socket_to_server.connect(('localhost', self.config.port)) |
580 | |
581 | # Send client name |
582 | self.socket_to_server.sendall(self.client_name + "\n") |
583 | |
584 | === modified file 'entertainerlib/backend/core/message_type_priority.py' |
585 | --- entertainerlib/backend/core/message_type_priority.py 2009-05-06 02:58:08 +0000 |
586 | +++ entertainerlib/backend/core/message_type_priority.py 2010-01-23 03:34:11 +0000 |
587 | @@ -15,56 +15,27 @@ |
588 | VERY_LOW = 40 |
589 | |
590 | class MessageType: |
591 | - """ |
592 | - Determines all allowed Message types. MessageHandler should use these |
593 | - to determine type. This simply makes code more readable. |
594 | - |
595 | - Example: |
596 | - if message.get_type() == MessageType.FRONTEND_OPENED: |
597 | - do_something_useful() |
598 | - """ |
599 | - |
600 | - # Indicates that Preferences UI has been used to update preferences. |
601 | - PREFERENCES_CONF_UPDATED = 0 |
602 | + """Determines all allowed Message types. MessageHandler should use these to |
603 | + determine type. This simply makes code more readable.""" |
604 | |
605 | # Indicates that Content Management UI has been used to update contents. |
606 | - CONTENT_CONF_UPDATED = 1 |
607 | - |
608 | - # Indicates that frontend has been opened. |
609 | - FRONTEND_OPENED = 2 |
610 | - |
611 | - # Indicates that frontend has been closed. |
612 | - FRONTEND_CLOSED = 3 |
613 | + CONTENT_CONF_UPDATED = 0 |
614 | |
615 | # Indicates that Feed cache has been updated. |
616 | - FEED_DB_UPDATED = 4 |
617 | - |
618 | - NOT_USED_5 = 5 |
619 | - |
620 | - NOT_USED_1 = 6 |
621 | + FEED_DB_UPDATED = 1 |
622 | |
623 | # Indicates that Feed cache should be updated. |
624 | - UPDATE_FEEDS = 7 |
625 | - |
626 | - NOT_USED_6 = 8 |
627 | - |
628 | - #This should be left in otherwise message_bus.py breaks |
629 | - #If another message type is needed please use this |
630 | - DO_NOT_DELETE_USE_NEXT = 9 |
631 | - |
632 | - NOT_USED_2 = 10 |
633 | - NOT_USED_3 = 11 |
634 | - NOT_USED_4 = 12 |
635 | + UPDATE_FEEDS = 2 |
636 | |
637 | # Require to rebuild image cache |
638 | - REBUILD_IMAGE_CACHE = 13 |
639 | + REBUILD_IMAGE_CACHE = 3 |
640 | |
641 | # Require to rebuild music cache |
642 | - REBUILD_MUSIC_CACHE = 14 |
643 | + REBUILD_MUSIC_CACHE = 4 |
644 | |
645 | # Require to rebuild video cache |
646 | - REBUILD_VIDEO_CACHE = 15 |
647 | + REBUILD_VIDEO_CACHE = 5 |
648 | |
649 | # Require to rebuild feed cache |
650 | - REBUILD_FEED_CACHE = 16 |
651 | + REBUILD_FEED_CACHE = 6 |
652 | |
653 | |
654 | === renamed directory 'entertainerlib/frontend' => 'entertainerlib/client' |
655 | === modified file 'entertainerlib/client/__init__.py' |
656 | --- entertainerlib/frontend/__init__.py 2009-05-06 03:40:22 +0000 |
657 | +++ entertainerlib/client/__init__.py 2010-01-23 03:34:11 +0000 |
658 | @@ -1,12 +1,12 @@ |
659 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
660 | -'''Frontend gui to entertainer''' |
661 | +'''Client code for Entertainer.''' |
662 | # pylint: disable-msg=W0612 |
663 | |
664 | def main(*args, **kwargs): |
665 | - '''Frontend runner''' |
666 | + '''Client code main loop.''' |
667 | |
668 | # Import statements are inside the function so that they aren't imported |
669 | - # every time something from the frontend is imported |
670 | + # every time something from the client is imported |
671 | |
672 | # cluttergtk must be imported before the first import of clutter so it |
673 | # must be imported even though pylint complains about it not being used. |
674 | @@ -15,23 +15,23 @@ |
675 | import gobject |
676 | import gtk |
677 | |
678 | - from entertainerlib.frontend.translation_setup import TranslationSetup |
679 | + from entertainerlib.client.translation_setup import TranslationSetup |
680 | TranslationSetup() |
681 | |
682 | from entertainerlib.backend.backend_server import BackendServer |
683 | - from entertainerlib.utils.configuration import Configuration |
684 | - from entertainerlib.frontend.frontend_client import FrontendClient |
685 | + from entertainerlib.configuration import Configuration |
686 | + from entertainerlib.client.client import Client |
687 | |
688 | gobject.threads_init() |
689 | gtk.gdk.threads_init() |
690 | clutter.threads_init() |
691 | |
692 | config = Configuration() |
693 | - if config.start_auto_server(): |
694 | + if config.start_auto_server: |
695 | print "Entertainer backend starting..." |
696 | BackendServer() |
697 | |
698 | - frontend_client = FrontendClient() |
699 | - frontend_client.start() |
700 | + client_client = Client() |
701 | + client_client.start() |
702 | |
703 | |
704 | |
705 | === modified file 'entertainerlib/client/backend_connection.py' |
706 | --- entertainerlib/frontend/backend_connection.py 2009-05-06 02:58:08 +0000 |
707 | +++ entertainerlib/client/backend_connection.py 2010-01-23 03:34:11 +0000 |
708 | @@ -7,49 +7,21 @@ |
709 | from entertainerlib.backend.core.message_handler import MessageHandler |
710 | |
711 | class BackendConnection(MessageHandler): |
712 | - """ |
713 | - BackendConnection - Connection to the Entertainer backend. Instance from |
714 | - this class is a gate to backend messagebus. |
715 | - """ |
716 | + """Connection to the Entertainer backend messagebus.""" |
717 | |
718 | def __init__(self): |
719 | - """Initialize connection. Connects to backend.""" |
720 | - #messages = { |
721 | - # MessageType.CONTENT_CONF_UPDATED : MessagePriority.NORMAL, |
722 | - # MessageType.PREFERENCES_CONF_UPDATED : MessagePriority.NORMAL, |
723 | - # MessageType.FEED_DB_UPDATED : MessagePriority.HIGH } |
724 | - # XXX: rockstar - The messages above are the "real" messages, and I'm |
725 | - # not sure why they are commented out. Anyone? |
726 | MessageHandler.__init__(self) |
727 | messages = {} |
728 | name = "Entertainer Frontend" |
729 | self.message_bus_proxy = MessageBusProxy(messages, self, name) |
730 | self.message_bus_proxy.connectToMessageBus() |
731 | self.message_bus_proxy.start() |
732 | - self.message_bus_proxy.sendMessage( |
733 | - Message(MessageType.FRONTEND_OPENED)) |
734 | |
735 | def close_connection(self): |
736 | """Close connection to backend""" |
737 | - self.message_bus_proxy.sendMessage( |
738 | - Message(MessageType.FRONTEND_CLOSED)) |
739 | self.message_bus_proxy.disconnectFromMessageBus() |
740 | |
741 | def request_feed_update(self): |
742 | """Request backend to fetch all feeds from the Internet.""" |
743 | self.message_bus_proxy.sendMessage(Message(MessageType.UPDATE_FEEDS)) |
744 | |
745 | - def request_weather_update(self): |
746 | - """Request backend to update weather information from the Internet.""" |
747 | - self.message_bus_proxy.sendMessage(Message(MessageType.UPDATE_WEATHER)) |
748 | - |
749 | - # Implements MessageHandler interface |
750 | - def handleMessage(self, message): |
751 | - """Handle received messages. (Implements MessageHandler interface)""" |
752 | - if message.get_type() == MessageType.CONTENT_CONF_UPDATED: |
753 | - pass |
754 | - elif message.get_type() == MessageType.PREFERENCES_CONF_UPDATED: |
755 | - pass |
756 | - elif message.get_type() == MessageType.FEED_DB_UPDATED: |
757 | - pass |
758 | - |
759 | |
760 | === renamed file 'entertainerlib/frontend/frontend_client.py' => 'entertainerlib/client/client.py' |
761 | --- entertainerlib/frontend/frontend_client.py 2009-05-06 02:58:08 +0000 |
762 | +++ entertainerlib/client/client.py 2010-01-23 03:34:11 +0000 |
763 | @@ -1,71 +1,65 @@ |
764 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
765 | -'''Entertainer Frontend - This is a client side of Entertainer.''' |
766 | +'''Entertainer client.''' |
767 | |
768 | import sys |
769 | |
770 | import gtk |
771 | - |
772 | -from entertainerlib.frontend.backend_connection import BackendConnection |
773 | -from entertainerlib.frontend.gui.user_interface import UserInterface |
774 | -from entertainerlib.frontend.medialibrary.feeds import FeedLibrary |
775 | -from entertainerlib.frontend.medialibrary.music import MusicLibrary |
776 | -from entertainerlib.frontend.medialibrary.images import ImageLibrary |
777 | -from entertainerlib.frontend.medialibrary.videos import VideoLibrary |
778 | -from entertainerlib.utils.configuration import Configuration |
779 | -from entertainerlib.utils.logger import Logger |
780 | -from entertainerlib.utils.system_tray_icon import SystemTrayIcon |
781 | - |
782 | -class FrontendClient: |
783 | - ''' |
784 | - Entertainer frontend |
785 | - |
786 | - This is a frontend application of the Entertainer. Frontend is a GUI part |
787 | - that user sees on the screen. This class is a core of the frontend. Frontend |
788 | - connects to the backend's messagebus at startup. |
789 | - ''' |
790 | +from twisted.internet import gtk2reactor |
791 | +gtk2reactor.install() # Install the gtk2 reactor before import the real reactor |
792 | +from twisted.internet import reactor |
793 | +from twisted.internet.protocol import ClientCreator |
794 | +from twisted.python.log import startLogging |
795 | + |
796 | +from entertainerlib.client.backend_connection import BackendConnection |
797 | +from entertainerlib.client.medialibrary.feeds import FeedLibrary |
798 | +from entertainerlib.client.medialibrary.music import MusicLibrary |
799 | +from entertainerlib.client.medialibrary.images import ImageLibrary |
800 | +from entertainerlib.client.medialibrary.videos import VideoLibrary |
801 | +from entertainerlib.configuration import Configuration |
802 | +from entertainerlib.gui.user_interface import UserInterface |
803 | +from entertainerlib.gui.system_tray_icon import SystemTrayIcon |
804 | +from entertainerlib.network.local.client import EntertainerLocalClientProtocol |
805 | + |
806 | + |
807 | +class Client: |
808 | + '''This is a client application of the Entertainer. Entertainer's client |
809 | + hooks into the server, and then provides a user interface for the data the |
810 | + server creates.''' |
811 | |
812 | def __init__(self): |
813 | - ''' |
814 | - Create a new frontend. |
815 | - |
816 | - This initializes all frontend stuff like media librarys, remote |
817 | - control receiver and GUI. After this we just wait user actions. |
818 | - ''' |
819 | config = Configuration() |
820 | - self.logger = Logger().getLogger('frontend.FrontendClient') |
821 | - self.backend_connection = self.initialize_backend_connection() |
822 | + self.backend_connection = BackendConnection() |
823 | feed_library = FeedLibrary(self.backend_connection) |
824 | - music_library = MusicLibrary(self.backend_connection) |
825 | - image_library = ImageLibrary(self.backend_connection) |
826 | - video_library = VideoLibrary(self.backend_connection) |
827 | + music_library = MusicLibrary() |
828 | + image_library = ImageLibrary() |
829 | + video_library = VideoLibrary() |
830 | self.ui = UserInterface( |
831 | feed_library, image_library, music_library, video_library, |
832 | - self.quit_frontend) |
833 | - |
834 | - if config.tray_icon_enabled(): |
835 | - SystemTrayIcon( |
836 | - self.quit_frontend, self.toggle_interface_visibility) |
837 | + self.quit_client) |
838 | + |
839 | + if config.tray_icon_enabled: |
840 | + SystemTrayIcon(self.quit_client, self.toggle_interface_visibility) |
841 | + |
842 | + startLogging(sys.stdout) |
843 | + client = EntertainerLocalClientProtocol |
844 | + |
845 | + ClientCreator(reactor, client).connectTCP( |
846 | + config.network_options['host'], |
847 | + config.network_options['port']) |
848 | |
849 | def start(self): |
850 | '''Start the necessary main loop.''' |
851 | self.ui.start_up() |
852 | self.interface_visible = True |
853 | gtk.gdk.threads_enter() |
854 | - gtk.main() |
855 | + reactor.run() |
856 | gtk.gdk.threads_leave() |
857 | |
858 | - def initialize_backend_connection(self): |
859 | - '''Connect to the backend server.''' |
860 | - backend_connection = BackendConnection() |
861 | - self.logger.debug('Connected to the Entertainer backend server.') |
862 | - |
863 | - return backend_connection |
864 | - |
865 | - def quit_frontend(self): |
866 | - '''Clean up the connection to the backend then close the frontend.''' |
867 | + def quit_client(self): |
868 | + '''Clean up the connection to the backend then close the client.''' |
869 | self.backend_connection.close_connection() |
870 | |
871 | - gtk.main_quit() |
872 | + reactor.stop() |
873 | sys.exit(0) |
874 | |
875 | def toggle_interface_visibility(self): |
876 | |
877 | === modified file 'entertainerlib/client/media_player.py' |
878 | --- entertainerlib/frontend/media_player.py 2009-06-29 19:41:35 +0000 |
879 | +++ entertainerlib/client/media_player.py 2010-01-23 03:34:11 +0000 |
880 | @@ -9,10 +9,10 @@ |
881 | import gobject |
882 | import gst |
883 | |
884 | -from entertainerlib.frontend.gui.widgets.motion_buffer import MotionBuffer |
885 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
886 | -from entertainerlib.frontend.medialibrary.playable import Playable |
887 | -from entertainerlib.utils.logger import Logger |
888 | +from entertainerlib.gui.widgets.motion_buffer import MotionBuffer |
889 | +from entertainerlib.gui.widgets.texture import Texture |
890 | +from entertainerlib.client.medialibrary.playable import Playable |
891 | +from entertainerlib.logger import Logger |
892 | |
893 | class MediaPlayer(gobject.GObject, object): |
894 | ''' |
895 | @@ -72,7 +72,7 @@ |
896 | self.is_playing = False # Is media player currently playing |
897 | self.is_reactive_allowed = False # Is the video_texture reactive |
898 | |
899 | - self.logger = Logger().getLogger('frontend.MediaPlayer') |
900 | + self.logger = Logger().getLogger('client.MediaPlayer') |
901 | |
902 | self._internal_callback_timeout_key = None |
903 | |
904 | @@ -105,7 +105,9 @@ |
905 | self.next() |
906 | elif message.type == gst.MESSAGE_ERROR: |
907 | self.video_texture.set_playing(False) |
908 | - self.video_texture.set_property("position", 0) |
909 | + # XXX: laymansterms - I don't know the implications of removing the |
910 | + # position property. |
911 | + #self.video_texture.set_property("position", 0) |
912 | err, debug = message.parse_error() |
913 | self.logger.error("Error: %(err)s, %(debug)s" % \ |
914 | {'err': err, 'debug': debug}) |
915 | @@ -167,7 +169,9 @@ |
916 | or self.media.get_type() == Playable.VIDEO_STREAM: |
917 | self.video_texture.set_playing(False) |
918 | self.video_texture.set_uri(playable.get_uri()) |
919 | - self.video_texture.set_property("position", 0) |
920 | + # XXX: laymansterms - I don't know the implications of removing the |
921 | + # position property. |
922 | + #self.video_texture.set_property("position", 0) |
923 | |
924 | def get_media(self): |
925 | '''Get URI of the current media stream.''' |
926 | @@ -248,7 +252,9 @@ |
927 | self.stage.set_color(self.bgcolor) |
928 | self.stage.remove(self.video_texture) |
929 | self.video_texture.set_playing(False) |
930 | - self.video_texture.set_property("position", 0) |
931 | + # XXX: laymansterms - I don't know the implications of removing the |
932 | + # position property. |
933 | + #self.video_texture.set_property("position", 0) |
934 | self.emit('stop') |
935 | |
936 | if self._internal_callback_timeout_key is not None: |
937 | @@ -510,7 +516,7 @@ |
938 | def get_texture(self): |
939 | '''Get media's texture. This is a video texture or album art texture.''' |
940 | if self.media.get_type() == Playable.VIDEO_STREAM: |
941 | - return clutter.CloneTexture(self.video_texture) |
942 | + return clutter.Clone(self.video_texture) |
943 | |
944 | elif self.media.get_type() == Playable.AUDIO_STREAM: |
945 | url = self.media.get_album_art_url() |
946 | |
947 | === modified file 'entertainerlib/client/medialibrary/feeds.py' |
948 | --- entertainerlib/frontend/medialibrary/feeds.py 2009-06-25 19:49:48 +0000 |
949 | +++ entertainerlib/client/medialibrary/feeds.py 2010-01-23 03:34:11 +0000 |
950 | @@ -3,7 +3,7 @@ |
951 | |
952 | from pysqlite2 import dbapi2 as sqlite |
953 | |
954 | -from entertainerlib.utils.configuration import Configuration |
955 | +from entertainerlib.configuration import Configuration |
956 | |
957 | class FeedLibrary(object): |
958 | '''This library can be used to handle RSS feeds in Entertainer.''' |
959 | |
960 | === modified file 'entertainerlib/client/medialibrary/images.py' |
961 | --- entertainerlib/frontend/medialibrary/images.py 2009-05-06 03:40:22 +0000 |
962 | +++ entertainerlib/client/medialibrary/images.py 2010-01-23 03:34:11 +0000 |
963 | @@ -4,25 +4,16 @@ |
964 | import os |
965 | from pysqlite2 import dbapi2 as sqlite |
966 | |
967 | -from entertainerlib.utils.configuration import Configuration |
968 | +from entertainerlib.configuration import Configuration |
969 | |
970 | class ImageLibrary: |
971 | - """ |
972 | - Image library. |
973 | - |
974 | - Entertainer's image cache. |
975 | - """ |
976 | - |
977 | - def __init__(self, backend_connection): |
978 | - """ |
979 | - Initialize image library |
980 | - @param backend_connection: BackendConnection object |
981 | - """ |
982 | + """Entertainer's image cache.""" |
983 | + |
984 | + def __init__(self): |
985 | self.config = Configuration() |
986 | |
987 | if not os.path.exists(self.config.IMAGE_DB): |
988 | raise Exception("Image database doesn't exist!") |
989 | - self.backend_connection = backend_connection |
990 | |
991 | def get_all_images(self): |
992 | """ |
993 | |
994 | === modified file 'entertainerlib/client/medialibrary/music.py' |
995 | --- entertainerlib/frontend/medialibrary/music.py 2009-07-19 19:27:31 +0000 |
996 | +++ entertainerlib/client/medialibrary/music.py 2010-01-23 03:34:11 +0000 |
997 | @@ -5,10 +5,10 @@ |
998 | import CDDB, DiscID |
999 | from pysqlite2 import dbapi2 as sqlite |
1000 | |
1001 | -from entertainerlib.utils.configuration import Configuration |
1002 | +from entertainerlib.configuration import Configuration |
1003 | |
1004 | -from entertainerlib.frontend.medialibrary.playable import Playable |
1005 | -from entertainerlib.utils.lyrics_downloader import LyricsDownloader |
1006 | +from entertainerlib.client.medialibrary.playable import Playable |
1007 | +from entertainerlib.download import LyricsDownloader |
1008 | |
1009 | |
1010 | class MusicLibraryException(Exception): |
1011 | @@ -28,22 +28,13 @@ |
1012 | pass |
1013 | |
1014 | class MusicLibrary: |
1015 | - """ |
1016 | - Music library. |
1017 | - |
1018 | - Interface for Entertainer's music cache. |
1019 | - """ |
1020 | - |
1021 | - def __init__(self, backend_connection): |
1022 | - """ |
1023 | - Initialize library. |
1024 | - @param backend_connection: BackendConnection object |
1025 | - """ |
1026 | + """Interface for Entertainer's music cache.""" |
1027 | + |
1028 | + def __init__(self): |
1029 | self.config = Configuration() |
1030 | |
1031 | if not os.path.exists(self.config.MUSIC_DB): |
1032 | raise Exception("Music database doesn't exist!") |
1033 | - self.backend_connection = backend_connection |
1034 | self.db_connection = sqlite.connect(self.config.MUSIC_DB) |
1035 | self.cursor = self.db_connection.cursor() |
1036 | |
1037 | |
1038 | === modified file 'entertainerlib/client/medialibrary/videos.py' |
1039 | --- entertainerlib/frontend/medialibrary/videos.py 2009-05-06 02:58:08 +0000 |
1040 | +++ entertainerlib/client/medialibrary/videos.py 2010-01-23 03:34:11 +0000 |
1041 | @@ -4,28 +4,19 @@ |
1042 | import os |
1043 | from pysqlite2 import dbapi2 as sqlite |
1044 | |
1045 | -from entertainerlib.utils.configuration import Configuration |
1046 | -from entertainerlib.utils.logger import Logger |
1047 | +from entertainerlib.configuration import Configuration |
1048 | +from entertainerlib.logger import Logger |
1049 | |
1050 | -from entertainerlib.frontend.medialibrary.playable import Playable |
1051 | +from entertainerlib.client.medialibrary.playable import Playable |
1052 | |
1053 | class VideoLibrary: |
1054 | - """ |
1055 | - Video library. |
1056 | - |
1057 | - Interface for Entertainer's video cache. |
1058 | - """ |
1059 | - |
1060 | - def __init__(self, backend_connection): |
1061 | - """ |
1062 | - Initialize video library |
1063 | - @param backend_connection: BackendConnection object |
1064 | - """ |
1065 | + """Interface for Entertainer's video cache.""" |
1066 | + |
1067 | + def __init__(self): |
1068 | self.config = Configuration() |
1069 | |
1070 | if not os.path.exists(self.config.VIDEO_DB): |
1071 | raise Exception("Video database doesn't exist!") |
1072 | - self.backend_connection = backend_connection |
1073 | |
1074 | def get_movies(self): |
1075 | """ |
1076 | @@ -133,7 +124,7 @@ |
1077 | |
1078 | #Setting default values |
1079 | self.logger = Logger().getLogger( |
1080 | - 'frontend.medialibrary.videos.VideoItem') |
1081 | + 'client.medialibrary.videos.VideoItem') |
1082 | self.__title = "" |
1083 | self.__filename = "" |
1084 | self.__length = 0 |
1085 | @@ -220,12 +211,12 @@ |
1086 | else: |
1087 | self.logger.error("Thumbnail does not exist for " + \ |
1088 | self.get_filename() + ", using default art instead") |
1089 | - return os.path.join(self.config.get_theme_path(), |
1090 | + return os.path.join(self.config.theme_path, |
1091 | "images/default_movie_art.png") |
1092 | else: |
1093 | self.logger.error("Thumbnail does not exist for " + \ |
1094 | self.get_filename() + ", using default art instead") |
1095 | - return os.path.join(self.config.get_theme_path(), |
1096 | + return os.path.join(self.config.theme_path, |
1097 | "images/default_movie_art.png") |
1098 | |
1099 | def has_thumbnail(self): |
1100 | |
1101 | === modified file 'entertainerlib/client/translation_setup.py' |
1102 | --- entertainerlib/frontend/translation_setup.py 2009-07-26 07:59:56 +0000 |
1103 | +++ entertainerlib/client/translation_setup.py 2010-01-23 03:34:11 +0000 |
1104 | @@ -1,38 +1,48 @@ |
1105 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
1106 | '''Translation Setup Code''' |
1107 | |
1108 | +import locale |
1109 | import os |
1110 | |
1111 | import gettext |
1112 | import gtk |
1113 | -import gtk.glade |
1114 | - |
1115 | -TRANSLATION_SOURCE = { |
1116 | - 'branch' : os.path.abspath(os.path.dirname(__file__) + '/../../locale'), |
1117 | - # Hardcoded path for a package install |
1118 | - 'package' : "/usr/share/locale" |
1119 | - } |
1120 | +from xdg import BaseDirectory |
1121 | |
1122 | class TranslationSetup: |
1123 | def __init__(self): |
1124 | + '''Because of how early translation setup has to occur. This should be |
1125 | + the only file outside of Configuration that imports xdg.''' |
1126 | + |
1127 | + def install_locale(locale_dir): |
1128 | + '''Install locale data from the provided directory.''' |
1129 | + # This sets up the _ function |
1130 | + gettext.install('entertainer', localedir=locale_dir, |
1131 | + unicode=True) |
1132 | + # Call the C library gettext functions and set the codeset |
1133 | + # to avoid locale-dependent translation of the message catalog |
1134 | + locale.bindtextdomain('entertainer', locale_dir) |
1135 | + locale.bind_textdomain_codeset('entertainer', "UTF-8") |
1136 | + # XXX: fmarl - setlocale load in current locale properly |
1137 | + # We can remove it and get feedback from users to see if |
1138 | + # this hack it's really needed. |
1139 | + try: |
1140 | + locale.setlocale(locale.LC_ALL, "") |
1141 | + except locale.Error, e: |
1142 | + pass |
1143 | + |
1144 | # Find locale data from a dev branch if we can |
1145 | - if os.path.exists(TRANSLATION_SOURCE['branch']): |
1146 | - #This setups the _ function |
1147 | - gettext.install('entertainer', TRANSLATION_SOURCE['branch']) |
1148 | - |
1149 | - #This setups the glade translations |
1150 | - gtk.glade.bindtextdomain('entertainer', |
1151 | - TRANSLATION_SOURCE['branch']) |
1152 | - # Install locale data from the hardcoded package path |
1153 | - elif os.path.exists(TRANSLATION_SOURCE['package']): |
1154 | - #This setups the _ function |
1155 | - gettext.install('entertainer', TRANSLATION_SOURCE['package']) |
1156 | - |
1157 | - #This setups the glade translations |
1158 | - gtk.glade.bindtextdomain('entertainer', |
1159 | - TRANSLATION_SOURCE['package']) |
1160 | + dev_locale = os.path.abspath(os.path.dirname(__file__) + |
1161 | + '/../../locale') |
1162 | + if os.path.exists(dev_locale): |
1163 | + install_locale(dev_locale) |
1164 | + |
1165 | + # Install locale data from the system location |
1166 | else: |
1167 | - pass |
1168 | - |
1169 | - gtk.glade.textdomain ('entertainer') |
1170 | - |
1171 | + system_data_dirs = [data_dir for data_dir in |
1172 | + BaseDirectory.xdg_data_dirs if not |
1173 | + data_dir.startswith(BaseDirectory.xdg_data_home)] |
1174 | + # Since we don't know for certain where the mo files were installed, |
1175 | + # we try to install from both /usr/share and /usr/local/share. |
1176 | + for data_dir in system_data_dirs: |
1177 | + system_locale = os.path.join(data_dir, 'locale') |
1178 | + install_locale(system_locale) |
1179 | |
1180 | === renamed file 'entertainerlib/utils/configuration.py' => 'entertainerlib/configuration.py' |
1181 | --- entertainerlib/utils/configuration.py 2009-06-01 09:55:40 +0000 |
1182 | +++ entertainerlib/configuration.py 2010-01-23 03:34:11 +0000 |
1183 | @@ -2,475 +2,217 @@ |
1184 | '''Configuration - Class represents Entertainer's configuration''' |
1185 | |
1186 | import os |
1187 | -import sys |
1188 | import shutil |
1189 | import ConfigParser |
1190 | -from ConfigParser import ParsingError, NoSectionError, NoOptionError |
1191 | - |
1192 | +from ConfigParser import NoSectionError, NoOptionError |
1193 | + |
1194 | +from xdg import BaseDirectory |
1195 | + |
1196 | +from entertainerlib.backend.core.message_handler import MessageHandler |
1197 | from entertainerlib.backend.core.message_type_priority import MessageType |
1198 | -from entertainerlib.backend.core.message_handler import MessageHandler |
1199 | - |
1200 | -from entertainerlib.utils.theme import Theme |
1201 | - |
1202 | -SOURCE_CONFIG = { |
1203 | - 'branch' : os.path.abspath(os.path.dirname(__file__) + '/../../cfg'), |
1204 | - # Hardcoded path for a package install |
1205 | - 'package' : "/usr/share/entertainer/cfg" |
1206 | - } |
1207 | +from entertainerlib.db.connection import Database |
1208 | +from entertainerlib.gui.theme import Theme |
1209 | |
1210 | class Configuration(MessageHandler): |
1211 | - """ |
1212 | - Configuration of Entertainer |
1213 | - |
1214 | - This class is an interface to all configuration parameters. All components |
1215 | - of Entertainer should get configuration values through this class. Object |
1216 | - from this class reads current config files and returns values based on |
1217 | - those files. If there are missing lines or other errors in config file, |
1218 | - then object return default values defined in this class. |
1219 | - """ |
1220 | - |
1221 | - CFG_DIR = os.path.expanduser("~/.config/entertainer") |
1222 | - TEST_DIR = None |
1223 | - |
1224 | - # This dictionary keeps track of data values that are written to config |
1225 | - # files but need to be returned to a known state while testing. |
1226 | - _tainted = {} |
1227 | - |
1228 | - # This dictionary keeps track of data values that are tainted in memory |
1229 | - # instead of a config file |
1230 | - _tainted_in_memory = {} |
1231 | + '''Interface to all configuration parameters. All components of Entertainer |
1232 | + should get configuration values through this class.''' |
1233 | |
1234 | _shared_state = {} |
1235 | |
1236 | def __init__(self, test_dir=None): |
1237 | - """ |
1238 | - Read configuration files and setup this object. |
1239 | - """ |
1240 | self.__dict__ = self._shared_state |
1241 | MessageHandler.__init__(self) |
1242 | |
1243 | - if not self._shared_state: |
1244 | - self.TEST_DIR = test_dir |
1245 | - |
1246 | + if not self._shared_state or test_dir is not None: |
1247 | # Set in a production mode or a test mode |
1248 | - if self.TEST_DIR is None: |
1249 | - self.cfg_dir = self.CFG_DIR |
1250 | + if test_dir is None: |
1251 | + self.resources = Resources() |
1252 | else: |
1253 | - self.cfg_dir = self.TEST_DIR |
1254 | - |
1255 | - if not os.path.exists(os.path.expanduser(self.cfg_dir)): |
1256 | - self.create_cfg_dir() |
1257 | - |
1258 | - self.ENTERTAINER_LOG = os.path.join(self.cfg_dir, |
1259 | - u'entertainer.log') |
1260 | - |
1261 | - self.FEED_DB = os.path.join(self.cfg_dir, 'cache/feed.db') |
1262 | - self.IMAGE_DB = os.path.join(self.cfg_dir, 'cache/image.db') |
1263 | - self.MUSIC_DB = os.path.join(self.cfg_dir, 'cache/music.db') |
1264 | - self.VIDEO_DB = os.path.join(self.cfg_dir, 'cache/video.db') |
1265 | - |
1266 | - self.THUMB_DIR = os.path.join(self.cfg_dir, 'cache', 'thumbnails') |
1267 | + self.resources = Resources(config_testing_dir=test_dir) |
1268 | + |
1269 | + self.cache_dir = self.resources.cache_dir |
1270 | + self.config_dir = self.resources.config_dir |
1271 | + self.data_dir = self.resources.data_dir |
1272 | + |
1273 | + self.LOG = os.path.join(self.cache_dir, u'entertainer.log') |
1274 | + |
1275 | + self.MEDIA_DB = Database(os.path.join(self.cache_dir, 'media')) |
1276 | + |
1277 | + self.FEED_DB = os.path.join(self.cache_dir, 'feed.db') |
1278 | + self.IMAGE_DB = os.path.join(self.cache_dir, 'image.db') |
1279 | + self.MUSIC_DB = os.path.join(self.cache_dir, 'music.db') |
1280 | + self.VIDEO_DB = os.path.join(self.cache_dir, 'video.db') |
1281 | + |
1282 | + self.THUMB_DIR = os.path.join(self.cache_dir, 'thumbnails') |
1283 | self.IMAGE_THUMB_DIR = os.path.join(self.THUMB_DIR, 'image') |
1284 | self.VIDEO_THUMB_DIR = os.path.join(self.THUMB_DIR, 'video') |
1285 | - self.ALBUM_ART_DIR = os.path.join(self.cfg_dir, 'cache/album_art') |
1286 | - self.MOVIE_ART_DIR = os.path.join(self.cfg_dir, 'cache/movie_art') |
1287 | - |
1288 | - # Preferences file |
1289 | - self.preferences_conf = os.path.join(self.cfg_dir, |
1290 | - 'preferences.conf') |
1291 | - |
1292 | - # Content file |
1293 | - self.content_conf = os.path.join(self.cfg_dir, 'content.conf') |
1294 | - |
1295 | - self.content_config = ConfigParser.ConfigParser() |
1296 | - self.preferences = ConfigParser.ConfigParser() |
1297 | - try: |
1298 | - self.content_config.readfp(open(self.content_conf)) |
1299 | - self.preferences.readfp(open(self.preferences_conf)) |
1300 | - except ParsingError: |
1301 | - print("ParsingError with configuration file.") |
1302 | - sys.exit(1) |
1303 | - except IOError: |
1304 | - print("IOError: Couldn't read configuration file.") |
1305 | - sys.exit(1) |
1306 | - |
1307 | - self.theme = Theme(self.get_theme_path()) |
1308 | - |
1309 | - self.stage_width = None |
1310 | - self.stage_height = None |
1311 | - |
1312 | - def create_cfg_dir(self): |
1313 | - '''Create a configuration directory and default config files.''' |
1314 | - |
1315 | - def create_cache_hierarchy(config_dir): |
1316 | - '''Create the directories needed for the cache''' |
1317 | - directories = [ |
1318 | - 'cache', |
1319 | - 'cache/album_art', |
1320 | - 'cache/movie_art', |
1321 | - 'cache/thumbnails', |
1322 | - 'cache/thumbnails/image', |
1323 | - 'cache/thumbnails/video' |
1324 | - ] |
1325 | - |
1326 | - for directory in directories: |
1327 | - new_dir = os.path.join(config_dir, directory) |
1328 | - os.mkdir(new_dir) |
1329 | - |
1330 | - config_dir = self.get_cfg_dir() |
1331 | - try: |
1332 | - # Copy configuration data from a dev branch if we can |
1333 | - if os.path.exists(SOURCE_CONFIG['branch']): |
1334 | - shutil.copytree(SOURCE_CONFIG['branch'], config_dir) |
1335 | - create_cache_hierarchy(config_dir) |
1336 | - # Install configuration data from the hardcoded package path |
1337 | - elif os.path.exists(SOURCE_CONFIG['package']): |
1338 | - shutil.copytree(SOURCE_CONFIG['package'], config_dir) |
1339 | - create_cache_hierarchy(config_dir) |
1340 | - else: |
1341 | - print "Couldn't find configuration data. Execution aborted." |
1342 | - sys.exit(1) |
1343 | - except OSError: |
1344 | - print "Couldn't copy configuration data to %s. Execution aborted." \ |
1345 | - % config_dir |
1346 | - sys.exit(1) |
1347 | + self.ALBUM_ART_DIR = os.path.join(self.cache_dir, 'album_art') |
1348 | + self.MOVIE_ART_DIR = os.path.join(self.cache_dir, 'movie_art') |
1349 | + |
1350 | + self.read_config_file() |
1351 | + |
1352 | + self.theme = Theme(self.theme_path) |
1353 | + |
1354 | + self._stage_width = None |
1355 | + self._stage_height = None |
1356 | + |
1357 | + # Network options specify the server type and extra options |
1358 | + self.network_options = { |
1359 | + 'type': 'local', |
1360 | + 'host': 'localhost', |
1361 | + 'port': 55545} |
1362 | + |
1363 | + def read_config_file(self): |
1364 | + '''Read in the config file.''' |
1365 | + self.content_conf = os.path.join(self.config_dir, 'content.conf') |
1366 | + self.content = ConfigParser.ConfigParser() |
1367 | + self.content.readfp(open(self.content_conf)) |
1368 | |
1369 | def update_configuration(self): |
1370 | - """ |
1371 | - Read configuration files again and update this object. |
1372 | - """ |
1373 | - try: |
1374 | - self.content_config.readfp(open(self.content_conf)) |
1375 | - self.preferences.readfp(open(self.preferences_conf)) |
1376 | - except ParsingError: |
1377 | - raise Exception("Syntax error in configuration file.") |
1378 | - except IOError: |
1379 | - raise IOError("Couldn't read config file.") |
1380 | + '''Read configuration file again and update this object.''' |
1381 | + self.content.readfp(open(self.content_conf)) |
1382 | |
1383 | - def write_content_value(self, section, option, value, sanitize=False): |
1384 | + def write_content_value(self, section, option, value): |
1385 | """Write a new value to the content configuration file.""" |
1386 | - if self.TEST_DIR and not sanitize: |
1387 | - self.taint('content', section, option) |
1388 | |
1389 | - try: |
1390 | - self.content_config.set(section, option, value) |
1391 | + def write_value(section, option, value): |
1392 | + '''Actually write the value to the section and option.''' |
1393 | + self.content.set(section, option, value) |
1394 | cfg_file = file(self.content_conf, 'w') |
1395 | - self.content_config.write(cfg_file) |
1396 | + self.content.write(cfg_file) |
1397 | + |
1398 | + try: |
1399 | + write_value(section, option, value) |
1400 | except NoSectionError: |
1401 | - raise Exception("No Section to set in content.conf file") |
1402 | + # Provide an upgrade path to additions of new sections. |
1403 | + shutil.rmtree(self.config_dir) |
1404 | + self.resources.create_configuration() |
1405 | + self.read_config_file() |
1406 | + write_value(section, option, value) |
1407 | except NoOptionError: |
1408 | raise Exception("No Option to set in content.conf file") |
1409 | |
1410 | - def write_preference_value(self, section, option, value, sanitize=False): |
1411 | - """Write a new value to the preferences configuration file.""" |
1412 | - if self.TEST_DIR and not sanitize: |
1413 | - self.taint('preferences', section, option) |
1414 | - |
1415 | - try: |
1416 | - self.preferences.set(section, option, value) |
1417 | - cfg_file = file(self.preferences_conf, 'w') |
1418 | - self.preferences.write(cfg_file) |
1419 | - except NoSectionError: |
1420 | - raise Exception("No Section to set in preferences.conf file") |
1421 | - except NoOptionError: |
1422 | - raise Exception("No Option to set in preferences.conf file") |
1423 | - |
1424 | - def get_stage_width(self): |
1425 | - '''Get the stage width from the preferences''' |
1426 | - if self.stage_width: |
1427 | - return self.stage_width |
1428 | - |
1429 | - try: |
1430 | - self.stage_width = self.preferences.getint("General", "stage_width") |
1431 | - except (NoSectionError, NoOptionError): |
1432 | - self.stage_width = 1366 # Default |
1433 | - |
1434 | - return self.stage_width |
1435 | - |
1436 | - def set_stage_width(self, new_width, sanitize=False): |
1437 | - '''Set the stage width for the in memory instance''' |
1438 | - # Taint data if in test mode |
1439 | - if self.TEST_DIR and not sanitize: |
1440 | - kwargs = {'new_width' : self.get_stage_width(), |
1441 | - 'sanitize' : True} |
1442 | - self.taint_in_memory(self.set_stage_width, kwargs) |
1443 | - |
1444 | - self.stage_width = new_width |
1445 | - |
1446 | - def get_stage_height(self): |
1447 | - '''Get the stage height from the preferences''' |
1448 | - if self.stage_height: |
1449 | - return self.stage_height |
1450 | - |
1451 | - try: |
1452 | - self.stage_height = self.preferences.getint("General", |
1453 | - "stage_height") |
1454 | - except (NoSectionError, NoOptionError): |
1455 | - self.stage_height = 768 # Default |
1456 | - |
1457 | - return self.stage_height |
1458 | - |
1459 | - def set_stage_height(self, new_height, sanitize=False): |
1460 | - '''Set the stage height for the in memory instance''' |
1461 | - # Taint data if in test mode |
1462 | - if self.TEST_DIR and not sanitize: |
1463 | - kwargs = {'new_height' : self.get_stage_height(), |
1464 | - 'sanitize' : True} |
1465 | - self.taint_in_memory(self.set_stage_height, kwargs) |
1466 | - |
1467 | - self.stage_height = new_height |
1468 | - |
1469 | - def get_theme_path(self): |
1470 | - """ |
1471 | - Get absolute path of the used theme. |
1472 | - @return: string (path) |
1473 | - """ |
1474 | - theme_path = os.path.join(self.get_cfg_dir(), 'themes') |
1475 | - try: |
1476 | - theme = self.preferences.get("General", "theme") |
1477 | - except (NoSectionError, NoOptionError): |
1478 | - return os.path.join(theme_path, "Default") |
1479 | - |
1480 | - if os.path.exists(os.path.join(theme_path, theme)): |
1481 | - return os.path.join(theme_path, theme) |
1482 | - else: |
1483 | - return os.path.join(theme_path, "Default") |
1484 | - |
1485 | + def _get_stage_width(self): |
1486 | + '''stage_width property getter.''' |
1487 | + if self._stage_width: |
1488 | + return self._stage_width |
1489 | + |
1490 | + self._stage_width = self.content.getint("General", "stage_width") |
1491 | + return self._stage_width |
1492 | + |
1493 | + def _set_stage_width(self, new_width): |
1494 | + '''stage_width property setter.''' |
1495 | + self._stage_width = new_width |
1496 | + |
1497 | + stage_width = property(_get_stage_width, _set_stage_width) |
1498 | + |
1499 | + def _get_stage_height(self): |
1500 | + '''stage_height property getter.''' |
1501 | + if self._stage_height: |
1502 | + return self._stage_height |
1503 | + |
1504 | + self._stage_height = self.content.getint("General", "stage_height") |
1505 | + return self._stage_height |
1506 | + |
1507 | + def _set_stage_height(self, new_height): |
1508 | + '''stage_height property setter.''' |
1509 | + self._stage_height = new_height |
1510 | + |
1511 | + stage_height = property(_get_stage_height, _set_stage_height) |
1512 | + |
1513 | + @property |
1514 | + def theme_path(self): |
1515 | + '''Path to the currently selected theme.''' |
1516 | + theme_path = os.path.join(self.data_dir, 'themes') |
1517 | + theme = self.content.get("General", "theme") |
1518 | + return os.path.join(theme_path, theme) |
1519 | + |
1520 | + @property |
1521 | def tray_icon_enabled(self): |
1522 | - """ |
1523 | - Display tray icon. |
1524 | - @return: boolean |
1525 | - """ |
1526 | - try: |
1527 | - result = self.preferences.getboolean("General", "display_icon") |
1528 | - except (NoSectionError, NoOptionError): |
1529 | - return False |
1530 | - return result |
1531 | - |
1532 | - def get_feed_fetch_interval(self): |
1533 | - """ |
1534 | - Get time interval (in minutes) for feed fetching. |
1535 | - @return: Integer |
1536 | - """ |
1537 | - try: |
1538 | - result = self.content_config.getint("RSS", "fetch_interval") |
1539 | - except (NoSectionError, NoOptionError): |
1540 | - return 60 # Default is one hour |
1541 | - return result |
1542 | - |
1543 | - def get_port(self): |
1544 | - """ |
1545 | - Get backend server's port number. |
1546 | - @return: Integer |
1547 | - """ |
1548 | - try: |
1549 | - result = self.preferences.getint("General", "backend_port") |
1550 | - except (NoSectionError, NoOptionError): |
1551 | - return 45054 # Default port |
1552 | - return result |
1553 | - |
1554 | - def get_video_folders(self): |
1555 | - """ |
1556 | - Get list of video folders |
1557 | - @return:String Array |
1558 | - """ |
1559 | - try: |
1560 | - video_list = self.content_config.get("Videos", "folders") |
1561 | - result = self._is_valid_media_folder(video_list.split(';')) |
1562 | - except (NoSectionError, NoOptionError): |
1563 | - return [] |
1564 | - return result |
1565 | - |
1566 | - def get_music_folders(self): |
1567 | - """ |
1568 | - Get list of music folders |
1569 | - @return: String Array |
1570 | - """ |
1571 | - try: |
1572 | - music_list = self.content_config.get("Music", "folders") |
1573 | - result = music_list.split(';') |
1574 | - except (NoSectionError, NoOptionError): |
1575 | - return [] |
1576 | - return result |
1577 | - |
1578 | - def get_image_folders(self): |
1579 | - """ |
1580 | - Get list of image folders |
1581 | - @return: String Array |
1582 | - """ |
1583 | - try: |
1584 | - image_list = self.content_config.get("Images", "folders") |
1585 | - result = self._is_valid_media_folder(image_list.split(';')) |
1586 | - except (NoSectionError, NoOptionError): |
1587 | - return [] |
1588 | - return result |
1589 | - |
1590 | - def get_feeds(self): |
1591 | - """ |
1592 | - Get list of feeds. |
1593 | - @return: String Array |
1594 | - """ |
1595 | - try: |
1596 | - rss_feeds = self.content_config.get("RSS", "feeds") |
1597 | - result = rss_feeds.split(';') |
1598 | - except (NoSectionError, NoOptionError): |
1599 | - return [] |
1600 | - return result |
1601 | - |
1602 | - def get_weather_location(self): |
1603 | - """ |
1604 | - Get list of weather location codes. |
1605 | - @return: List of strings |
1606 | - """ |
1607 | - try: |
1608 | - location = self.content_config.get("Weather", "location") |
1609 | - except (NoSectionError, NoOptionError): |
1610 | - location = '' |
1611 | - return location |
1612 | - |
1613 | - def display_weather_in_frontend(self): |
1614 | - """ |
1615 | - Should we display weather in frontend |
1616 | - @return: Boolean |
1617 | - """ |
1618 | - try: |
1619 | - result = self.content_config.getboolean("Weather", |
1620 | - "display_in_menu") |
1621 | - except (NoSectionError, NoOptionError): |
1622 | - return False |
1623 | - return result |
1624 | - |
1625 | - def display_cd_eject_in_frontend(self): |
1626 | - """ |
1627 | - Should we display the cd eject button in frontend |
1628 | - @return: Boolean |
1629 | - """ |
1630 | - try: |
1631 | - result = self.content_config.getboolean("CD", |
1632 | - "display_eject_in_menu") |
1633 | - except (NoSectionError, NoOptionError): |
1634 | - return False |
1635 | - return result |
1636 | - |
1637 | - def download_video_metadata(self): |
1638 | - """ |
1639 | - Get True if video metadata should be downloaded, otherwise False |
1640 | - """ |
1641 | - try: |
1642 | - result = self.content_config.getboolean("Videos", |
1643 | - "download_metadata") |
1644 | - except (NoSectionError, NoOptionError): |
1645 | - return False |
1646 | - return result |
1647 | - |
1648 | + '''Boolean to indicate whether or not to show the tray icon.''' |
1649 | + return self.content.getboolean("General", "display_icon") |
1650 | + |
1651 | + @property |
1652 | + def feed_fetch_interval(self): |
1653 | + '''Time interval (in minutes) for feed fetching.''' |
1654 | + return self.content.getint("RSS", "fetch_interval") |
1655 | + |
1656 | + @property |
1657 | + def port(self): |
1658 | + '''Server's port number.''' |
1659 | + return self.content.getint("General", "backend_port") |
1660 | + |
1661 | + @property |
1662 | + def media_folders(self): |
1663 | + '''Return a list of folders for media.''' |
1664 | + media = self.content.get("Media", "folders") |
1665 | + return self._is_valid_media_folder(media.split(';')) |
1666 | + |
1667 | + @property |
1668 | + def feeds(self): |
1669 | + '''List of feeds.''' |
1670 | + rss_feeds = self.content.get("RSS", "feeds") |
1671 | + return rss_feeds.split(';') |
1672 | + |
1673 | + @property |
1674 | + def weather_location(self): |
1675 | + '''User's weather location.''' |
1676 | + return self.content.get("Weather", "location") |
1677 | + |
1678 | + @property |
1679 | + def display_weather_in_client(self): |
1680 | + '''Boolean to tell if weather should be displayed.''' |
1681 | + return self.content.getboolean("Weather", "display_in_menu") |
1682 | + |
1683 | + @property |
1684 | + def download_metadata(self): |
1685 | + '''Boolean to tell if metadata should be downloaded.''' |
1686 | + return self.content.getboolean("Media", "download_metadata") |
1687 | + |
1688 | + @property |
1689 | def download_album_art(self): |
1690 | - """ |
1691 | - Download album art from the Internet. |
1692 | - @return: boolean |
1693 | - """ |
1694 | - try: |
1695 | - result = self.content_config.getboolean("Music", |
1696 | - "download_album_art") |
1697 | - except (NoSectionError, NoOptionError): |
1698 | - return False |
1699 | - return result |
1700 | + '''Boolean to tell if album art should be downloaded.''' |
1701 | + return self.content.getboolean("Media", "download_album_art") |
1702 | |
1703 | + @property |
1704 | def download_lyrics(self): |
1705 | - """ |
1706 | - Download song lyrics from the Internet. |
1707 | - @return: boolean |
1708 | - """ |
1709 | - try: |
1710 | - result = self.content_config.getboolean("Music", "download_lyrics") |
1711 | - except (NoSectionError, NoOptionError): |
1712 | - return False |
1713 | - return result |
1714 | - |
1715 | - def display_hidden_files_folders(self): |
1716 | - """If True hidden files and folders will be added to image library.""" |
1717 | - try: |
1718 | - result = self.content_config.getboolean("Images", |
1719 | - "display_hidden_files_folders") |
1720 | - except (NoSectionError, NoOptionError): |
1721 | - return False |
1722 | - return result |
1723 | - |
1724 | + '''Boolean to tell if lyrics should be downloaded.''' |
1725 | + return self.content.getboolean("Media", "download_lyrics") |
1726 | + |
1727 | + @property |
1728 | def show_effects(self): |
1729 | - """ |
1730 | - Use animations on user interface. |
1731 | - @return: boolean |
1732 | - """ |
1733 | - try: |
1734 | - result = self.preferences.getboolean("General", "show_effects") |
1735 | - except (NoSectionError, NoOptionError): |
1736 | - return False |
1737 | - return result |
1738 | + '''Boolean to tell if effects should be shown.''' |
1739 | + return self.content.getboolean("General", "show_effects") |
1740 | |
1741 | + @property |
1742 | def transition_effect(self): |
1743 | - """ |
1744 | - Which transition effect should be used when switching screens. |
1745 | - @return: string (name of the effect) |
1746 | - """ |
1747 | - try: |
1748 | - result = self.preferences.get("General", "transition_effect") |
1749 | - except (NoSectionError, NoOptionError): |
1750 | - return False |
1751 | - return result |
1752 | - |
1753 | - def get_theme_name(self): |
1754 | - """ |
1755 | - Get the name of the current theme. |
1756 | - @return: string (theme name) |
1757 | - """ |
1758 | - try: |
1759 | - result = self.preferences.get("General", "theme") |
1760 | - except (NoSectionError, NoOptionError): |
1761 | - result = "Default" |
1762 | - return result |
1763 | - |
1764 | + '''Internal name of the user's selected screen transition style.''' |
1765 | + return self.content.get("General", "transition_effect") |
1766 | + |
1767 | + @property |
1768 | + def theme_name(self): |
1769 | + '''Name of the current theme.''' |
1770 | + return self.content.get("General", "theme") |
1771 | + |
1772 | + @property |
1773 | def start_in_fullscreen(self): |
1774 | '''Boolean to determine whether to start in fullscreen mode or not.''' |
1775 | - try: |
1776 | - result = self.preferences.getboolean("General", |
1777 | - "start_in_fullscreen") |
1778 | - except (NoSectionError, NoOptionError): |
1779 | - return True |
1780 | - return result |
1781 | + return self.content.getboolean("General", "start_in_fullscreen") |
1782 | |
1783 | + @property |
1784 | def start_auto_server(self): |
1785 | - """ |
1786 | - Return True if backend server should auto start |
1787 | - @return: boolean |
1788 | - """ |
1789 | - try: |
1790 | - result = self.preferences.getboolean("General", |
1791 | - "start_server_auto") |
1792 | - except (NoSectionError, NoOptionError): |
1793 | - return False |
1794 | - return result |
1795 | + '''Boolean to tell if the server should be auto started.''' |
1796 | + return self.content.getboolean("General", "start_server_auto") |
1797 | |
1798 | + @property |
1799 | def history_size(self): |
1800 | - """ |
1801 | - Get screen history size. This determines how many screen are kept in |
1802 | - memory when scrolling. |
1803 | - @return: Integer |
1804 | - """ |
1805 | - try: |
1806 | - result = self.preferences.getint("General", "history_size") |
1807 | - except (NoSectionError, NoOptionError): |
1808 | - return 10 |
1809 | - return result |
1810 | + '''Number of screens to hold in history.''' |
1811 | + return self.content.getint("General", "history_size") |
1812 | |
1813 | - def get_slideshow_step(self): |
1814 | - """ |
1815 | - Get the slideshow step in seconds used by the photo slideshow |
1816 | - @return: Integer |
1817 | - """ |
1818 | - try: |
1819 | - result = self.preferences.getint("Photographs", "slideshow_step") |
1820 | - except (NoSectionError, NoOptionError): |
1821 | - return 5 |
1822 | - return result |
1823 | + @property |
1824 | + def slideshow_step(self): |
1825 | + '''Amount of seconds before the slideshow steps to the next image.''' |
1826 | + return self.content.getint("Photographs", "slideshow_step") |
1827 | |
1828 | # Implements MessageHandler interface |
1829 | def handleMessage(self, message): |
1830 | @@ -480,73 +222,99 @@ |
1831 | """ |
1832 | if message.get_type() == MessageType.CONTENT_CONF_UPDATED: |
1833 | self.update_configuration() |
1834 | - elif message.get_type() == MessageType.PREFERENCES_CONF_UPDATED: |
1835 | - self.update_configuration() |
1836 | - |
1837 | - def get_cfg_dir(self): |
1838 | - """ |
1839 | - Get the entertainer configuration dir. |
1840 | - @return: strings |
1841 | - """ |
1842 | - return self.cfg_dir |
1843 | - |
1844 | - def taint(self, kind, section, option): |
1845 | - '''Taint any data that is written to config files so that it can be |
1846 | - returned to a known state later''' |
1847 | - if kind == 'content': |
1848 | - value = self.content_config.get(section, option) |
1849 | - elif kind == 'preferences': |
1850 | - value = self.preferences.get(section, option) |
1851 | - else: |
1852 | - raise Exception( |
1853 | - "Taint type must be either 'content' or 'preferences'") |
1854 | - |
1855 | - key = "%s %s %s" % (kind, section, option) |
1856 | - self._tainted[key] = value |
1857 | - |
1858 | - def taint_in_memory(self, callback, kwargs): |
1859 | - '''Taint any data that is stored in memory. Callback is used as the |
1860 | - key to a tainted_in_memory dictionary whose stored value is a |
1861 | - dictionary of arguments (kwargs) needed to restore the original |
1862 | - value''' |
1863 | - self._tainted_in_memory[callback] = kwargs |
1864 | - |
1865 | - def sanitize(self): |
1866 | - '''Restore all tainted values to their original state and clear the |
1867 | - tainted dictionary''' |
1868 | - # Sanitize values in config files |
1869 | - for key in self._tainted.keys(): |
1870 | - # tokens will contain type, section, and option |
1871 | - tokens = key.split() |
1872 | - if tokens[0] == 'content': |
1873 | - # Write in the sanitize mode so taint checking will be skipped |
1874 | - self.write_content_value(tokens[1], tokens[2], |
1875 | - self._tainted[key], sanitize=True) |
1876 | - elif tokens[0] == 'preferences': |
1877 | - # Write in the sanitize mode so taint checking will be skipped |
1878 | - self.write_preference_value(tokens[1], tokens[2], |
1879 | - self._tainted[key], sanitize=True) |
1880 | - |
1881 | - self.update_configuration() |
1882 | - self._tainted = {} |
1883 | - |
1884 | - # Sanitize values in memory |
1885 | - for callback in self._tainted_in_memory.keys(): |
1886 | - kwargs = self._tainted_in_memory[callback] |
1887 | - callback(**kwargs) |
1888 | - |
1889 | - self._tainted_in_memory = {} |
1890 | |
1891 | def _is_valid_media_folder(self, folder_list): |
1892 | """Return a folder list where eventual cache folders are removed""" |
1893 | - cache_dir = os.path.join(self.get_cfg_dir(), 'cache') |
1894 | valid_folder_list = [] |
1895 | |
1896 | for folder in folder_list: |
1897 | - common_prefix = os.path.commonprefix([cache_dir, folder]) |
1898 | - if common_prefix != cache_dir: |
1899 | - # folder is not a subfolder of cache_dir we accept it |
1900 | + common_prefix = os.path.commonprefix([self.cache_dir, folder]) |
1901 | + if common_prefix != self.cache_dir: |
1902 | + # if folder is not a subfolder of cache_dir, we accept it |
1903 | valid_folder_list.append(folder) |
1904 | |
1905 | return valid_folder_list |
1906 | |
1907 | + |
1908 | +class Resources(object): |
1909 | + '''A Wrapper for the XDG directories. Also handles creation of a new setup |
1910 | + if the Entertainer directories within the XDG directories are missing. This |
1911 | + class is meant solely to support Configuration and should NOT be publicly |
1912 | + used because of testing conflicts that would occur.''' |
1913 | + |
1914 | + def __init__(self, resource='entertainer', config_testing_dir=None): |
1915 | + if config_testing_dir is None: |
1916 | + self.cache_dir = os.path.join(BaseDirectory.xdg_cache_home, |
1917 | + resource) |
1918 | + self.config_dir = os.path.join(BaseDirectory.xdg_config_home, |
1919 | + resource) |
1920 | + self.data_dir = os.path.join(BaseDirectory.xdg_data_home, resource) |
1921 | + else: |
1922 | + # Running in the test suite so don't create the XDG locations. |
1923 | + self.cache_dir = os.path.join(config_testing_dir, 'cache') |
1924 | + self.config_dir = os.path.join(config_testing_dir, 'config') |
1925 | + self.data_dir = os.path.join(config_testing_dir, 'data') |
1926 | + |
1927 | + # Ensure that the directories exist. |
1928 | + if not os.path.exists(self.cache_dir): |
1929 | + self.create_cache_hierarchy() |
1930 | + if not os.path.exists(self.config_dir): |
1931 | + self.create_configuration() |
1932 | + if not os.path.exists(self.data_dir): |
1933 | + self.create_initial_data() |
1934 | + |
1935 | + def create_cache_hierarchy(self): |
1936 | + '''Create the cache hierarchy that is assumed to exist.''' |
1937 | + os.makedirs(os.path.join(self.cache_dir, 'album_art')) |
1938 | + os.makedirs(os.path.join(self.cache_dir, 'movie_art')) |
1939 | + os.makedirs(os.path.join(self.cache_dir, 'thumbnails', 'image')) |
1940 | + os.makedirs(os.path.join(self.cache_dir, 'thumbnails', 'video')) |
1941 | + |
1942 | + def create_configuration(self): |
1943 | + '''Create the user's configuration area and populate with the default |
1944 | + content configuration.''' |
1945 | + |
1946 | + # Copy configuration data from a dev branch if we can |
1947 | + dev_config = os.path.abspath(os.path.dirname(__file__) + '/../cfg') |
1948 | + if os.path.exists(dev_config): |
1949 | + shutil.copytree(dev_config, self.config_dir) |
1950 | + return |
1951 | + |
1952 | + # Must be a proper installation so install from the system location. |
1953 | + installed_config = os.path.join(self.installed_data_dir, 'cfg') |
1954 | + shutil.copytree(installed_config, self.config_dir) |
1955 | + |
1956 | + def create_initial_data(self): |
1957 | + '''Create the initial data directory and populate with the default data |
1958 | + used by Entertainer.''' |
1959 | + os.mkdir(self.data_dir) |
1960 | + |
1961 | + # Copy configuration data from a dev branch if we can |
1962 | + dev_config = os.path.abspath(os.path.dirname(__file__) + '/../themes') |
1963 | + if os.path.exists(dev_config): |
1964 | + shutil.copytree(dev_config, os.path.join(self.data_dir, 'themes')) |
1965 | + return |
1966 | + |
1967 | + # Must be a proper installation so install from the system location. |
1968 | + themes_dir = os.path.join(self.installed_data_dir, 'themes') |
1969 | + shutil.copytree(themes_dir, os.path.join(self.data_dir, 'themes')) |
1970 | + |
1971 | + @property |
1972 | + def installed_data_dir(self): |
1973 | + '''Since different distros decide on the install path differently, scan |
1974 | + through the system data directories to find where Entertainer was |
1975 | + installed.''' |
1976 | + |
1977 | + # Get rid of the user's home data directory because we don't want it. |
1978 | + system_data_dirs = [data_dir for data_dir in BaseDirectory.xdg_data_dirs |
1979 | + if not data_dir.startswith(BaseDirectory.xdg_data_home)] |
1980 | + |
1981 | + installed_data_dir = None |
1982 | + for directory in system_data_dirs: |
1983 | + possible_data_dir = os.path.join(directory, 'entertainer') |
1984 | + if os.path.exists(possible_data_dir): |
1985 | + installed_data_dir = possible_data_dir |
1986 | + break |
1987 | + |
1988 | + return installed_data_dir |
1989 | + |
1990 | |
1991 | === renamed directory 'entertainerlib/backend/core/db' => 'entertainerlib/db' |
1992 | === modified file 'entertainerlib/db/connection.py' |
1993 | --- entertainerlib/backend/core/db/connection.py 2009-05-11 23:37:27 +0000 |
1994 | +++ entertainerlib/db/connection.py 2010-01-23 03:34:11 +0000 |
1995 | @@ -27,7 +27,7 @@ |
1996 | 'PhotoImage': '''CREATE TABLE `photoimage` ( |
1997 | id INTEGER PRIMARY KEY AUTOINCREMENT, |
1998 | filename TEXT, |
1999 | - thumb VARCHAR(32), |
2000 | + thumbnail VARCHAR(32), |
2001 | title TEXT, |
2002 | description TEXT, |
2003 | creation_date DATETIME, |
2004 | |
2005 | === modified file 'entertainerlib/db/models.py' |
2006 | --- entertainerlib/backend/core/db/models.py 2009-04-28 23:54:49 +0000 |
2007 | +++ entertainerlib/db/models.py 2010-01-23 03:34:11 +0000 |
2008 | @@ -6,6 +6,7 @@ |
2009 | from storm.properties import Bool, DateTime, Int, Unicode |
2010 | from storm.references import Reference, ReferenceSet |
2011 | |
2012 | + |
2013 | class BaseModel(Storm): |
2014 | '''Abstract class from which all Entertainer models inherit.''' |
2015 | |
2016 | @@ -96,7 +97,7 @@ |
2017 | __storm_table__ = 'photoimage' |
2018 | id = Int(primary=True) |
2019 | filename = Unicode() |
2020 | - thumb = Unicode() |
2021 | + thumbnail = Unicode() |
2022 | title = Unicode() |
2023 | description = Unicode() |
2024 | creation_date = DateTime() |
2025 | @@ -108,7 +109,7 @@ |
2026 | ret = { |
2027 | 'id': self.id, |
2028 | 'filename': self.filename, |
2029 | - 'thumb': self.thumb, |
2030 | + 'thumbnail': self.thumbnail, |
2031 | 'title': self.title, |
2032 | 'description': self.description, |
2033 | 'creation_date': self.creation_date, |
2034 | |
2035 | === added file 'entertainerlib/dialog.py' |
2036 | --- entertainerlib/dialog.py 1970-01-01 00:00:00 +0000 |
2037 | +++ entertainerlib/dialog.py 2010-01-23 03:34:11 +0000 |
2038 | @@ -0,0 +1,1113 @@ |
2039 | +# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
2040 | +'''Dialogs for Entertainer.''' |
2041 | +# pylint: disable-msg=C0302 |
2042 | + |
2043 | +import os |
2044 | +import shutil |
2045 | +import socket |
2046 | +import sys |
2047 | +import tarfile |
2048 | + |
2049 | +import gtk |
2050 | + |
2051 | +from entertainerlib.backend.core.message import Message |
2052 | +from entertainerlib.backend.core.message_bus_proxy import MessageBusProxy |
2053 | +from entertainerlib.backend.core.message_type_priority import MessageType |
2054 | +from entertainerlib.configuration import Configuration |
2055 | +from entertainerlib.logger import Logger |
2056 | +from entertainerlib.gui.theme import Theme |
2057 | +from entertainerlib.backend.components.feeds.feed_utils import (OPMLParser, |
2058 | + FeedConfigTools) |
2059 | +from entertainerlib.weather import Weather |
2060 | + |
2061 | + |
2062 | +class ManagerDialog: |
2063 | + """ |
2064 | + This is a content management tool for Entertainer media center application. |
2065 | + """ |
2066 | + |
2067 | + # Temporary storage for entered URL |
2068 | + url = "" |
2069 | + UI_DIR = os.path.join(os.path.dirname(__file__), "uis") |
2070 | + |
2071 | + def __init__(self, stand_alone): |
2072 | + """ |
2073 | + Initialize content management dialog |
2074 | + @param stand_alone: Boolean, Is this dialog running as a stand alone |
2075 | + process |
2076 | + """ |
2077 | + self.stand_alone = stand_alone |
2078 | + self.config = Configuration() |
2079 | + self.themes = [] |
2080 | + self.weather = Weather() |
2081 | + |
2082 | + # Load UI with gtk.Builder |
2083 | + uifile = os.path.join(self.UI_DIR, 'manager.ui') |
2084 | + self.builder = gtk.Builder() |
2085 | + self.builder.set_translation_domain('entertainer') |
2086 | + self.builder.add_from_file(uifile) |
2087 | + |
2088 | + # Get content management dialog and bind signal callbacks |
2089 | + self.dialog = self.builder.get_object("ManagerDialog") |
2090 | + if (self.dialog): |
2091 | + callback_dic = { |
2092 | + # Dialog-wide callbacks |
2093 | + "on_button_open_list_clicked" : |
2094 | + self.on_button_open_list_clicked, |
2095 | + "on_close_button_clicked" : self.on_close_button_clicked, |
2096 | + "on_ManagerDialog_destroy" : self.on_dialog_closed, |
2097 | + |
2098 | + # Media tab |
2099 | + "on_button_remove_media_clicked" : |
2100 | + self.on_button_remove_media_clicked, |
2101 | + "on_button_add_media_clicked" : |
2102 | + self.on_button_add_media_clicked, |
2103 | + "on_button_edit_media_clicked" : |
2104 | + self.on_button_edit_media_clicked, |
2105 | + "on_checkbutton_video_metadata_toggled" : |
2106 | + self.on_checkbutton_video_metadata_toggled, |
2107 | + "on_lyrics_checkbox_toggled" : self.on_lyrics_checkbox_toggled, |
2108 | + "on_art_checkbox_toggled" : self.on_art_checkbox_toggled, |
2109 | + "on_button_media_rebuild_clicked" : |
2110 | + self.on_button_media_rebuild_clicked, |
2111 | + |
2112 | + # Feed tab |
2113 | + "on_button_add_feed_clicked" : |
2114 | + self.on_button_add_feed_clicked, |
2115 | + "on_button_remove_feed_clicked" : |
2116 | + self.on_button_remove_feed_clicked, |
2117 | + "on_button_edit_feed_clicked" : |
2118 | + self.on_button_edit_feed_clicked, |
2119 | + "on_fetch_interval_spinbutton_value_changed" : |
2120 | + self.on_fetch_interval_spinbutton_value_changed, |
2121 | + "on_url_dialog_delete_event" : self.on_url_dialog_delete_event, |
2122 | + "on_url_dialog_ok_button_clicked" : |
2123 | + self.on_url_dialog_ok_button_clicked, |
2124 | + "on_url_dialog_cancel_button_clicked" : |
2125 | + self.on_url_dialog_cancel_button_clicked, |
2126 | + "on_button_feed_rebuild_clicked" : |
2127 | + self.on_button_feed_rebuild_clicked, |
2128 | + |
2129 | + # Weather tab |
2130 | + "on_button_add_weather_clicked" : |
2131 | + self.on_button_add_weather_clicked, |
2132 | + "on_button_remove_weather_clicked" : |
2133 | + self.on_button_remove_weather_clicked, |
2134 | + "on_weather_display_checkbox_toggled" : |
2135 | + self.on_weather_display_checkbox_toggled, |
2136 | + "on_location_find_button_clicked" : |
2137 | + self.on_location_find_button_clicked, |
2138 | + "on_location_cancel_button_clicked" : |
2139 | + self.on_location_cancel_button_clicked, |
2140 | + "on_location_add_button_clicked" : |
2141 | + self.on_location_add_button_clicked, |
2142 | + "on_location_entry_activate" : self.on_location_entry_activate, |
2143 | + |
2144 | + # User Interface tab |
2145 | + "on_theme_list_cursor_changed" : |
2146 | + self.on_theme_list_cursor_changed, |
2147 | + "on_theme_add_button_clicked" : |
2148 | + self.on_theme_add_button_clicked, |
2149 | + "on_theme_remove_button_clicked" : |
2150 | + self.on_theme_remove_button_clicked, |
2151 | + "on_checkbutton_effects_toggled" : |
2152 | + self.on_checkbutton_effects_toggled, |
2153 | + "on_combobox_effects_changed" : |
2154 | + self.on_combobox_effects_changed, |
2155 | + |
2156 | + # General tab |
2157 | + "on_checkbutton_fullscreen_toggled" : |
2158 | + self.on_checkbutton_fullscreen_toggled, |
2159 | + "on_checkbutton_autostart_toggled" : |
2160 | + self.on_checkbutton_autostart_toggled, |
2161 | + "on_checkbutton_systray_icon_toggled" : |
2162 | + self.on_checkbutton_systray_icon_toggled, |
2163 | + "on_spinbutton_slideshow_step_value_changed": |
2164 | + self.on_spinbutton_slideshow_step_value_changed |
2165 | + } |
2166 | + |
2167 | + self.builder.connect_signals(callback_dic) |
2168 | + |
2169 | + # Initialize dialog widgets with correct values and show dialog |
2170 | + self.init_dialog_values_from_configure_file() |
2171 | + self.dialog.resize(500, 300) |
2172 | + self.dialog.show() |
2173 | + |
2174 | + # Initialize location list in search dialog |
2175 | + result_list = self.builder.get_object("location_results_treeview") |
2176 | + store = gtk.ListStore(str) |
2177 | + result_list.set_model(store) |
2178 | + cell_renderer = gtk.CellRendererText() |
2179 | + column = gtk.TreeViewColumn(_("Location"), cell_renderer, text=0) |
2180 | + result_list.append_column(column) |
2181 | + |
2182 | + def on_dialog_closed(self, widget): |
2183 | + """Callback function for dialog's close button""" |
2184 | + try: |
2185 | + proxy = MessageBusProxy(client_name = "Manager GUI") |
2186 | + proxy.connectToMessageBus() |
2187 | + proxy.sendMessage(Message(MessageType.CONTENT_CONF_UPDATED)) |
2188 | + proxy.disconnectFromMessageBus() |
2189 | + except socket.error: |
2190 | + error = gtk.MessageDialog( |
2191 | + None, gtk.DIALOG_MODAL, |
2192 | + gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _( |
2193 | + "Entertainer backend is not running. " |
2194 | + "Cache cannot be rebuilt." |
2195 | + )) |
2196 | + error.run() |
2197 | + error.destroy() |
2198 | + |
2199 | + if(self.stand_alone): |
2200 | + self.dialog.hide() |
2201 | + self.dialog.destroy() |
2202 | + gtk.main_quit() |
2203 | + else: |
2204 | + self.dialog.hide() |
2205 | + self.dialog.destroy() |
2206 | + |
2207 | + def on_close_button_clicked(self, widget): |
2208 | + """Callback function for dialog's close button""" |
2209 | + if(self.stand_alone): |
2210 | + self.dialog.hide() |
2211 | + self.dialog.destroy() |
2212 | + gtk.main_quit() |
2213 | + else: |
2214 | + self.dialog.hide() |
2215 | + self.dialog.destroy() |
2216 | + |
2217 | + def on_button_add_media_clicked(self, widget): |
2218 | + """Opens add URL dialog. """ |
2219 | + widget = self.builder.get_object("treeview_media") |
2220 | + model = widget.get_model() |
2221 | + # Open "Select folder" dialog |
2222 | + dialog = gtk.FileChooserDialog(_("Select folder"), None, |
2223 | + gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, |
2224 | + (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL, |
2225 | + gtk.STOCK_OPEN,gtk.RESPONSE_OK), |
2226 | + None) |
2227 | + status = dialog.run() |
2228 | + # If folder was selected we add it to model and update config file |
2229 | + if(status == gtk.RESPONSE_OK): |
2230 | + self.add_to_model_and_config(dialog.get_current_folder(), model, |
2231 | + self.media_folders, "Media") |
2232 | + dialog.destroy() |
2233 | + |
2234 | + def on_button_remove_media_clicked(self, widget): |
2235 | + """Remove currently selected folder from media folders""" |
2236 | + widget = self.builder.get_object("treeview_media") |
2237 | + model = widget.get_model() |
2238 | + selection = widget.get_selection().get_selected() |
2239 | + if selection[1] == None: |
2240 | + return |
2241 | + rm_folder = model.get_value(selection[1], 0) |
2242 | + self.media_folders.remove(rm_folder) |
2243 | + str_folders = ";".join(self.media_folders) |
2244 | + self.config.write_content_value("Media", "folders", str_folders) |
2245 | + model.remove(selection[1]) |
2246 | + |
2247 | + def on_button_edit_media_clicked(self, widget): |
2248 | + """Edit currently selected folder""" |
2249 | + widget = self.builder.get_object("treeview_media") |
2250 | + url_dialog = self.builder.get_object("url_dialog") |
2251 | + url_entry = self.builder.get_object("url_entry") |
2252 | + model = widget.get_model() |
2253 | + selection = widget.get_selection().get_selected() |
2254 | + if selection[1] == None: |
2255 | + return |
2256 | + folder = model.get_value(selection[1], 0) |
2257 | + url_entry.set_text(folder) |
2258 | + url_dialog.set_title(_("Edit URL")) |
2259 | + status = url_dialog.run() |
2260 | + if status == gtk.RESPONSE_OK and os.path.exists(self.url): |
2261 | + # Update list model |
2262 | + model.set_value(selection[1], 0, self.url) |
2263 | + # Update configure file |
2264 | + pos = self.media_folders.index(folder) |
2265 | + self.media_folders.remove(folder) |
2266 | + self.media_folders.insert(pos, self.url) |
2267 | + str_folders = ";".join(self.media_folders) |
2268 | + self.config.write_content_value("Media", "folders", |
2269 | + str_folders) |
2270 | + |
2271 | + def on_checkbutton_autostart_toggled(self, widget): |
2272 | + '''Server autostart checkbox toggled.''' |
2273 | + self.config.write_content_value("General", "start_server_auto", |
2274 | + widget.get_active()) |
2275 | + |
2276 | + def on_checkbutton_fullscreen_toggled(self, widget): |
2277 | + '''Start in fullscreen checkbox toggled.''' |
2278 | + self.config.write_content_value("General", "start_in_fullscreen", |
2279 | + widget.get_active()) |
2280 | + |
2281 | + def on_checkbutton_systray_icon_toggled(self, widget): |
2282 | + """System Tray Icon checkbox toggled""" |
2283 | + self.config.write_content_value("General", "display_icon", |
2284 | + widget.get_active()) |
2285 | + |
2286 | + def on_checkbutton_video_metadata_toggled(self, widget): |
2287 | + """ |
2288 | + Download video file metadata from internet |
2289 | + @param widget: GTK-Widget |
2290 | + """ |
2291 | + self.config.write_content_value("Media", "download_metadata", |
2292 | + widget.get_active()) |
2293 | + |
2294 | + def on_button_add_feed_clicked(self, widget): |
2295 | + """Opens add feed dialog. """ |
2296 | + widget = self.builder.get_object("treeview_feeds") |
2297 | + url_dialog = self.builder.get_object("url_dialog") |
2298 | + model = widget.get_model() |
2299 | + # Open dialog |
2300 | + url_dialog.set_title(_("Add RSS-feed")) |
2301 | + status = url_dialog.run() |
2302 | + # If folder was selected we add it to model and update config file |
2303 | + if(status == gtk.RESPONSE_OK): |
2304 | + model.append([self.url]) |
2305 | + self.feeds.append(self.url) |
2306 | + str_folders = ";".join(self.feeds) |
2307 | + self.config.write_content_value("RSS", "feeds", str_folders) |
2308 | + |
2309 | + def on_button_remove_feed_clicked(self, widget): |
2310 | + """Remove currently selected reed from RSS-feeds""" |
2311 | + widget = self.builder.get_object("treeview_feeds") |
2312 | + model = widget.get_model() |
2313 | + selection = widget.get_selection().get_selected() |
2314 | + if selection[1] == None: |
2315 | + return |
2316 | + rm_folder = model.get_value(selection[1], 0) |
2317 | + self.feeds.remove(rm_folder) |
2318 | + str_folders = ";".join(self.feeds) |
2319 | + self.config.write_content_value("RSS", "feeds", str_folders) |
2320 | + model.remove(selection[1]) |
2321 | + |
2322 | + def on_button_edit_feed_clicked(self, widget): |
2323 | + """Edit currently selected feed""" |
2324 | + widget = self.builder.get_object("treeview_feeds") |
2325 | + url_dialog = self.builder.get_object("url_dialog") |
2326 | + url_entry = self.builder.get_object("url_entry") |
2327 | + model = widget.get_model() |
2328 | + selection = widget.get_selection().get_selected() |
2329 | + if selection[1] == None: |
2330 | + return |
2331 | + feed = model.get_value(selection[1], 0) |
2332 | + url_entry.set_text(feed) |
2333 | + url_dialog.set_title(_("Edit feed")) |
2334 | + status = url_dialog.run() |
2335 | + if status == gtk.RESPONSE_OK: |
2336 | + # Update list model |
2337 | + model.set_value(selection[1], 0, self.url) |
2338 | + # Update configure file |
2339 | + pos = self.feeds.index(feed) |
2340 | + self.feeds.remove(feed) |
2341 | + self.feeds.insert(pos, self.url) |
2342 | + str_feeds = ";".join(self.feeds) |
2343 | + self.config.write_content_value("RSS", "feeds", str_feeds) |
2344 | + |
2345 | + def on_button_open_list_clicked(self, widget): |
2346 | + """Opens the open feed source dialog""" |
2347 | + open_dialog = OpenFeedSourceDialog( |
2348 | + self.builder.get_object("treeview_feeds"), self.feeds) |
2349 | + open_dialog.dialog.connect("destroy", |
2350 | + open_dialog.on_closeButton_clicked) |
2351 | + |
2352 | + def on_fetch_interval_spinbutton_value_changed(self, widget): |
2353 | + self.config.write_content_value("RSS", "fetch_interval", |
2354 | + widget.get_value_as_int()) |
2355 | + |
2356 | + def on_spinbutton_slideshow_step_value_changed(self, widget): |
2357 | + """Activation of slideshow effects""" |
2358 | + self.config.write_content_value("Photographs", "slideshow_step", |
2359 | + int(widget.get_value())) |
2360 | + |
2361 | + def on_lyrics_checkbox_toggled(self, widget): |
2362 | + self.config.write_content_value("Media", "download_lyrics", |
2363 | + widget.get_active()) |
2364 | + |
2365 | + def on_art_checkbox_toggled(self, widget): |
2366 | + self.config.write_content_value("Media", "download_album_art", |
2367 | + widget.get_active()) |
2368 | + |
2369 | + def on_url_dialog_ok_button_clicked(self, widget): |
2370 | + """URL dialog OK button pressed. Sets self.url""" |
2371 | + url_dialog = self.builder.get_object("url_dialog") |
2372 | + url_entry = self.builder.get_object("url_entry") |
2373 | + url_dialog.hide() |
2374 | + self.url = url_entry.get_text() |
2375 | + url_entry.set_text("") |
2376 | + url_dialog.response(gtk.RESPONSE_OK) |
2377 | + |
2378 | + def on_url_dialog_cancel_button_clicked(self, widget): |
2379 | + """URL dialog cancelled. Hides dialog""" |
2380 | + url_dialog = self.builder.get_object("url_dialog") |
2381 | + url_entry = self.builder.get_object("url_entry") |
2382 | + url_dialog.hide() |
2383 | + url_entry.set_text("") |
2384 | + url_dialog.response(gtk.RESPONSE_CANCEL) |
2385 | + |
2386 | + def on_url_dialog_delete_event(self, widget, data): |
2387 | + """Dialog's X clicked. Hides dialog""" |
2388 | + url_dialog = self.builder.get_object("url_dialog") |
2389 | + url_entry = self.builder.get_object("url_entry") |
2390 | + url_dialog.hide() |
2391 | + url_entry.set_text("") |
2392 | + url_dialog.response(gtk.RESPONSE_CANCEL) |
2393 | + return True |
2394 | + |
2395 | + def on_button_add_weather_clicked(self, widget): |
2396 | + """ |
2397 | + Open location search dialog |
2398 | + @param widget: GTK-Widget |
2399 | + """ |
2400 | + location_dialog = self.builder.get_object("weather_search_dialog") |
2401 | + location_dialog.set_title(_("Add location")) |
2402 | + |
2403 | + # Clear results |
2404 | + result_list = self.builder.get_object("location_results_treeview") |
2405 | + model = result_list.get_model() |
2406 | + model.clear() |
2407 | + |
2408 | + status = location_dialog.run() |
2409 | + if(status == gtk.RESPONSE_OK): |
2410 | + print "Added" |
2411 | + |
2412 | + def on_button_remove_weather_clicked(self, widget): |
2413 | + """ |
2414 | + Remove currently selected weather location from the location list |
2415 | + @param widget: GTK-Widget |
2416 | + """ |
2417 | + widget = self.builder.get_object("treeview_locations") |
2418 | + model = widget.get_model() |
2419 | + self.weather_locations = [] |
2420 | + str_folders = "" |
2421 | + self.config.write_content_value("Weather", "location", str_folders) |
2422 | + model.clear() |
2423 | + |
2424 | + def on_weather_display_checkbox_toggled(self, widget): |
2425 | + """ |
2426 | + Checkbox that defines should we use weather conditions |
2427 | + @param widget: GTK-Widget |
2428 | + """ |
2429 | + self.config.write_content_value("Weather", "display_in_menu", |
2430 | + widget.get_active()) |
2431 | + if widget.get_active(): |
2432 | + self.builder.get_object("button_add_weather").set_sensitive(True) |
2433 | + self.builder.get_object( |
2434 | + "button_remove_weather").set_sensitive(True) |
2435 | + self.builder.get_object("treeview_locations").set_sensitive(True) |
2436 | + else: |
2437 | + self.builder.get_object("button_add_weather").set_sensitive(False) |
2438 | + self.builder.get_object( |
2439 | + "button_remove_weather").set_sensitive(False) |
2440 | + self.builder.get_object("treeview_locations").set_sensitive(False) |
2441 | + |
2442 | + def on_location_find_button_clicked(self, widget): |
2443 | + """ |
2444 | + Find location by search string |
2445 | + @param widget: GTK-Widget |
2446 | + """ |
2447 | + add_button = self.builder.get_object("location_add_button") |
2448 | + search_term = self.builder.get_object("location_entry").get_text() |
2449 | + result_list = self.builder.get_object("location_results_treeview") |
2450 | + model = result_list.get_model() |
2451 | + model.clear() |
2452 | + if search_term != "": |
2453 | + self.weather.location = search_term |
2454 | + self.weather.refresh() |
2455 | + results = self.weather.forecasts |
2456 | + if len(results) > 0: |
2457 | + add_button.set_sensitive(True) |
2458 | + model.append([search_term]) |
2459 | + result_list.set_cursor(0) |
2460 | + else: |
2461 | + model.clear() |
2462 | + model.append([_("Location Not Found!")]) |
2463 | + add_button.set_sensitive(False) |
2464 | + |
2465 | + def on_location_cancel_button_clicked(self, widget): |
2466 | + """ |
2467 | + Close location search dialog without taking any actions.0 |
2468 | + @param widget: GTK-Widget |
2469 | + """ |
2470 | + location_dialog = self.builder.get_object("weather_search_dialog") |
2471 | + location_entry = self.builder.get_object("location_entry") |
2472 | + location_dialog.hide() |
2473 | + location_entry.set_text("") |
2474 | + location_dialog.response(gtk.RESPONSE_CANCEL) |
2475 | + |
2476 | + def on_location_add_button_clicked(self, widget): |
2477 | + """ |
2478 | + Add selected location to location list and close search dialog |
2479 | + @param widget: GTK-Widget |
2480 | + """ |
2481 | + self.weather_locations = [] |
2482 | + result_list = self.builder.get_object("location_results_treeview") |
2483 | + model = result_list.get_model() |
2484 | + selection = result_list.get_selection().get_selected() |
2485 | + if selection[1] == None: |
2486 | + return |
2487 | + location_string = model.get_value(selection[1], 0) |
2488 | + |
2489 | + location_list = self.builder.get_object("treeview_locations") |
2490 | + loc_model = location_list.get_model() |
2491 | + loc_model.clear() |
2492 | + loc_model.append([location_string]) |
2493 | + |
2494 | + self.weather_locations.append(location_string) |
2495 | + str_locations = ";".join(self.weather_locations) |
2496 | + self.config.write_content_value("Weather", "location", str_locations) |
2497 | + |
2498 | + location_dialog = self.builder.get_object("weather_search_dialog") |
2499 | + location_entry = self.builder.get_object("location_entry") |
2500 | + location_dialog.hide() |
2501 | + location_entry.set_text("") |
2502 | + location_dialog.response(gtk.RESPONSE_CANCEL) |
2503 | + |
2504 | + def on_location_entry_activate(self, widget): |
2505 | + """ |
2506 | + User hit enter on location entry to start search |
2507 | + @param widget: GTK-Widget |
2508 | + """ |
2509 | + self.on_location_find_button_clicked(widget) |
2510 | + |
2511 | + def on_button_media_rebuild_clicked(self, widget): |
2512 | + '''Rebuild media cache requested.''' |
2513 | + try: |
2514 | + proxy = MessageBusProxy(client_name = "Manager GUI") |
2515 | + proxy.connectToMessageBus() |
2516 | + proxy.sendMessage(Message(MessageType.REBUILD_IMAGE_CACHE)) |
2517 | + proxy.sendMessage(Message(MessageType.REBUILD_MUSIC_CACHE)) |
2518 | + proxy.sendMessage(Message(MessageType.REBUILD_VIDEO_CACHE)) |
2519 | + proxy.disconnectFromMessageBus() |
2520 | + except socket.error: |
2521 | + error = gtk.MessageDialog( |
2522 | + None, gtk.DIALOG_MODAL, |
2523 | + gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _( |
2524 | + "Entertainer backend is not running. " |
2525 | + "Cache cannot be rebuilt." |
2526 | + )) |
2527 | + error.run() |
2528 | + error.destroy() |
2529 | + |
2530 | + def on_button_feed_rebuild_clicked(self, widget): |
2531 | + """ |
2532 | + Rebuild feed cache requested |
2533 | + @param widget: GTK-Widget |
2534 | + """ |
2535 | + #We need the user to confirm the rebuild feed cache request |
2536 | + dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, |
2537 | + gtk.BUTTONS_OK_CANCEL, |
2538 | + _("This will completely remove any feed entries in the cache!")) |
2539 | + status = dialog.run() |
2540 | + #If user has ok'd the request send the message to the message bus |
2541 | + if(status == gtk.RESPONSE_OK): |
2542 | + try: |
2543 | + proxy = MessageBusProxy(client_name = "Manager GUI") |
2544 | + proxy.connectToMessageBus() |
2545 | + proxy.sendMessage(Message(MessageType.REBUILD_FEED_CACHE)) |
2546 | + proxy.disconnectFromMessageBus() |
2547 | + except socket.error: |
2548 | + error = gtk.MessageDialog( |
2549 | + None, gtk.DIALOG_MODAL, |
2550 | + gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _( |
2551 | + "Entertainer backend is not running. " |
2552 | + "Cache cannot be rebuilt." |
2553 | + )) |
2554 | + error.run() |
2555 | + error.destroy() |
2556 | + |
2557 | + dialog.destroy() |
2558 | + |
2559 | + def on_theme_add_button_clicked(self, widget): |
2560 | + """Add theme button clicked""" |
2561 | + themelist = self.builder.get_object("theme_list") |
2562 | + model = themelist.get_model() |
2563 | + # Open "Select folder" dialog |
2564 | + dialog = gtk.FileChooserDialog(_("Select theme package file"), |
2565 | + None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, |
2566 | + gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK), None) |
2567 | + file_filter = gtk.FileFilter() |
2568 | + file_filter.set_name(_("Theme package (tar.gz)")) |
2569 | + file_filter.add_pattern("*.tar.gz") |
2570 | + dialog.add_filter(file_filter) |
2571 | + status = dialog.run() |
2572 | + |
2573 | + # If theme was selected with file chooser |
2574 | + if(status == gtk.RESPONSE_OK): |
2575 | + package = dialog.get_filename() |
2576 | + tar = tarfile.open(package, 'r:gz') # Open tar.gz package |
2577 | + |
2578 | + # Make sure that package contains configuration file (is theme) |
2579 | + content = tar.getnames() |
2580 | + theme_name = None |
2581 | + is_theme = False |
2582 | + for element in content: |
2583 | + if element[-10:] == "theme.conf": |
2584 | + theme_name = element[:-11] |
2585 | + is_theme = True |
2586 | + |
2587 | + # Install them |
2588 | + if is_theme: |
2589 | + tar.extractall(os.path.join(self.config.data_dir, 'themes')) |
2590 | + model.insert(len(model), [theme_name]) |
2591 | + else: |
2592 | + error = gtk.MessageDialog(None, gtk.DIALOG_MODAL, |
2593 | + gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("Invalid theme file!")) |
2594 | + error.run() |
2595 | + error.destroy() |
2596 | + |
2597 | + dialog.destroy() |
2598 | + |
2599 | + def on_theme_list_cursor_changed(self, widget): |
2600 | + """Executed when theme is changed in theme list. Update preview.""" |
2601 | + # Get currently selected theme |
2602 | + themelist = self.builder.get_object("theme_list") |
2603 | + model = themelist.get_model() |
2604 | + selection = themelist.get_selection().get_selected() |
2605 | + name = model.get_value(selection[1], 0) |
2606 | + themedir = os.path.join(self.config.data_dir, 'themes', name) |
2607 | + theme = Theme(theme_path=themedir) |
2608 | + |
2609 | + # Update preview |
2610 | + image = self.builder.get_object("theme_image") |
2611 | + image.set_from_file(os.path.join(themedir, "thumbnail.png")) |
2612 | + name = self.builder.get_object("name_label") |
2613 | + name.set_text(theme.getName()) |
2614 | + author = self.builder.get_object("author_label") |
2615 | + author.set_text(theme.getAuthor()) |
2616 | + license_label = self.builder.get_object("license_label") |
2617 | + license_label.set_text(theme.getLicence()) |
2618 | + copyright_label = self.builder.get_object("copyright_label") |
2619 | + copyright_label.set_text(theme.getCopyright()) |
2620 | + comment = self.builder.get_object("comment_label") |
2621 | + comment.set_text(theme.getComment()) |
2622 | + |
2623 | + self.config.write_content_value("General", "theme", name.get_text()) |
2624 | + |
2625 | + def on_theme_remove_button_clicked(self, widget): |
2626 | + """Remove theme button clicked""" |
2627 | + # Get currently selected theme |
2628 | + themelist = self.builder.get_object("theme_list") |
2629 | + model = themelist.get_model() |
2630 | + selection = themelist.get_selection().get_selected() |
2631 | + name = model.get_value(selection[1], 0) |
2632 | + |
2633 | + confirm = gtk.MessageDialog(None, |
2634 | + gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, |
2635 | + _("Are you sure you want to delete\ntheme: %(name)s") % \ |
2636 | + {'name': name}) |
2637 | + status = confirm.run() |
2638 | + confirm.destroy() |
2639 | + if(status == gtk.RESPONSE_YES): |
2640 | + themedir = os.path.join(self.config.data_dir, 'themes', name) |
2641 | + shutil.rmtree(themedir) |
2642 | + model.remove(selection[1]) |
2643 | + themelist.set_cursor(0) |
2644 | + self.themes.remove(name) |
2645 | + |
2646 | + def on_checkbutton_effects_toggled(self, widget): |
2647 | + """Effect checkbox toggled""" |
2648 | + combobox = self.builder.get_object("combobox_effects") |
2649 | + combobox.set_sensitive(widget.get_active()) |
2650 | + self.config.write_content_value("General", "show_effects", |
2651 | + widget.get_active()) |
2652 | + |
2653 | + def on_combobox_effects_changed(self, widget): |
2654 | + """User changed effect for screen transitions""" |
2655 | + text = widget.get_active_text() |
2656 | + if text == _("No effect"): |
2657 | + english_text = "No effect" |
2658 | + if text == _("Crossfade"): |
2659 | + english_text = "Crossfade" |
2660 | + if text == _("Zoom and fade"): |
2661 | + english_text = "Zoom and fade" |
2662 | + if text == _("Slide"): |
2663 | + english_text = "Slide" |
2664 | + self.config.write_content_value("General", "transition_effect", |
2665 | + english_text) |
2666 | + |
2667 | + def init_dialog_values_from_configure_file(self): |
2668 | + """Read configuration and set dialog widget values with read values. |
2669 | + """ |
2670 | + # == Videos == |
2671 | + medialist_widget = self.builder.get_object("treeview_media") |
2672 | + mediastore = gtk.ListStore(str) |
2673 | + |
2674 | + cell_renderer = gtk.CellRendererText() |
2675 | + column = gtk.TreeViewColumn(_("Folders"), cell_renderer, text=0) |
2676 | + medialist_widget.append_column(column) |
2677 | + |
2678 | + self.media_folders = self.config.media_folders |
2679 | + |
2680 | + # Fill model with folders read from config file |
2681 | + self.init_model(mediastore, self.media_folders) |
2682 | + |
2683 | + medialist_widget.set_model(mediastore) |
2684 | + |
2685 | + # Checkboxes |
2686 | + metadata_checkbox = self.builder.get_object("video_metadata_checkbox") |
2687 | + metadata_checkbox.set_active(self.config.download_metadata) |
2688 | + |
2689 | + art_checkbox = self.builder.get_object("art_checkbox") |
2690 | + art_checkbox.set_active(self.config.download_album_art) |
2691 | + |
2692 | + lyrics_checkbox = self.builder.get_object("lyrics_checkbox") |
2693 | + lyrics_checkbox.set_active(self.config.download_lyrics) |
2694 | + |
2695 | + # == RSS-feeds == |
2696 | + feedlist_widget = self.builder.get_object("treeview_feeds") |
2697 | + feed_model = gtk.ListStore(str) |
2698 | + |
2699 | + rss_cell = gtk.CellRendererText() |
2700 | + rss_column = gtk.TreeViewColumn(_("Feeds"), rss_cell, text=0) |
2701 | + feedlist_widget.append_column(rss_column) |
2702 | + |
2703 | + self.feeds = self.config.feeds |
2704 | + |
2705 | + # Fill model with folders read from config file |
2706 | + for i in range(len(self.feeds)): |
2707 | + feed_model.insert(i, [self.feeds[i]]) |
2708 | + |
2709 | + feedlist_widget.set_model(feed_model) |
2710 | + |
2711 | + # Interval spinner |
2712 | + interval_spinner = self.builder.get_object("fetch_interval_spinbutton") |
2713 | + interval_val = self.config.feed_fetch_interval |
2714 | + if interval_val < 15: |
2715 | + interval_val = 15 |
2716 | + elif interval_val > 900: |
2717 | + interval_val = 900 |
2718 | + interval_spinner.set_value(interval_val) |
2719 | + |
2720 | + # == Weather == |
2721 | + locationlist_widget = self.builder.get_object("treeview_locations") |
2722 | + location_model = gtk.ListStore(str) |
2723 | + |
2724 | + loc_cell = gtk.CellRendererText() |
2725 | + location_column = gtk.TreeViewColumn(_("Location"), loc_cell, text=0) |
2726 | + locationlist_widget.append_column(location_column) |
2727 | + |
2728 | + self.weather_location = self.config.weather_location |
2729 | + |
2730 | + # Fill model with location read from config file |
2731 | + location_model.insert(0, [self.weather_location]) |
2732 | + locationlist_widget.set_model(location_model) |
2733 | + |
2734 | + weather_display_checkbox = self.builder.get_object( |
2735 | + "weather_display_checkbox") |
2736 | + display_val = self.config.display_weather_in_client |
2737 | + weather_display_checkbox.set_active(display_val) |
2738 | + if not display_val: |
2739 | + self.builder.get_object("button_add_weather").set_sensitive(False) |
2740 | + self.builder.get_object("button_remove_weather").set_sensitive( |
2741 | + False) |
2742 | + self.builder.get_object("treeview_locations").set_sensitive(False) |
2743 | + |
2744 | + # == User Interface == |
2745 | + self.load_themes() |
2746 | + current_theme = self.config.theme_name |
2747 | + |
2748 | + themelist_widget = self.builder.get_object("theme_list") |
2749 | + model = gtk.ListStore(str) |
2750 | + |
2751 | + cell_renderer = gtk.CellRendererText() |
2752 | + column = gtk.TreeViewColumn("Themes", cell_renderer, text=0) |
2753 | + themelist_widget.append_column(column) |
2754 | + |
2755 | + # Fill model with installed themes |
2756 | + for i in range(len(self.themes)): |
2757 | + model.insert(i, [self.themes[i]]) |
2758 | + |
2759 | + themelist_widget.set_model(model) |
2760 | + |
2761 | + # Set current theme selected in theme list |
2762 | + index = model.get_iter_first() |
2763 | + unselected = True |
2764 | + index_counter = 0 |
2765 | + while(unselected): |
2766 | + name = model.get_value(index, 0) |
2767 | + if name == current_theme: |
2768 | + unselected = False |
2769 | + themelist_widget.set_cursor(index_counter) |
2770 | + index = model.iter_next(index) |
2771 | + index_counter += 1 |
2772 | + |
2773 | + effect_checkbox = self.builder.get_object("checkbutton_effects") |
2774 | + effect_combobox = self.builder.get_object("combobox_effects") |
2775 | + if self.config.show_effects: |
2776 | + effect_checkbox.set_active(True) |
2777 | + effect_combobox.set_sensitive(True) |
2778 | + else: |
2779 | + effect_checkbox.set_active(False) |
2780 | + effect_combobox.set_sensitive(False) |
2781 | + |
2782 | + # Set Effect Combobox value (Text values are set in ui file) |
2783 | + effect = self.config.transition_effect |
2784 | + if effect == "No effect": |
2785 | + effect_combobox.set_active(0) |
2786 | + if effect == "Crossfade": |
2787 | + effect_combobox.set_active(1) |
2788 | + if effect == "Zoom and fade": |
2789 | + effect_combobox.set_active(2) |
2790 | + if effect == "Slide": |
2791 | + effect_combobox.set_active(3) |
2792 | + |
2793 | + # == General == |
2794 | + checkbutton_fullscreen = self.builder.get_object( |
2795 | + "checkbutton_fullscreen") |
2796 | + if self.config.start_in_fullscreen: |
2797 | + checkbutton_fullscreen.set_active(True) |
2798 | + else: |
2799 | + checkbutton_fullscreen.set_active(False) |
2800 | + |
2801 | + checkbutton_autostart = self.builder.get_object("checkbutton_autostart") |
2802 | + if self.config.start_auto_server: |
2803 | + checkbutton_autostart.set_active(True) |
2804 | + else: |
2805 | + checkbutton_autostart.set_active(False) |
2806 | + |
2807 | + checkbutton_systray_icon = self.builder.get_object( |
2808 | + "checkbutton_systray_icon") |
2809 | + if self.config.tray_icon_enabled: |
2810 | + checkbutton_systray_icon.set_active(True) |
2811 | + else: |
2812 | + checkbutton_systray_icon.set_active(False) |
2813 | + |
2814 | + spinbutton_slideshow_step = self.builder.get_object( |
2815 | + "spinbutton_slideshow_step") |
2816 | + spinbutton_slideshow_step.set_value(self.config.slideshow_step) |
2817 | + |
2818 | + def add_to_model_and_config(self, selected_folder, model, folders, kind): |
2819 | + """ |
2820 | + Add selected_folder to the model and the folders list while updating |
2821 | + the configuration item section specified by type |
2822 | + """ |
2823 | + if not selected_folder in folders: |
2824 | + model.append([selected_folder]) |
2825 | + |
2826 | + if(folders == None): |
2827 | + folders = [selected_folder] |
2828 | + else: |
2829 | + folders.append(selected_folder) |
2830 | + |
2831 | + if "" in folders: |
2832 | + folders.remove("") |
2833 | + str_folders = ";".join(folders) |
2834 | + self.config.write_content_value(kind, "folders", str_folders) |
2835 | + |
2836 | + def init_model(self, model, items): |
2837 | + """Fill model with items from supplied list""" |
2838 | + for i in range(len(items)): |
2839 | + if not str(items[i]).strip() == "": |
2840 | + model.insert(i, [items[i]]) |
2841 | + |
2842 | + def load_themes(self): |
2843 | + """Load themes""" |
2844 | + themes = os.listdir(os.path.join(self.config.data_dir, 'themes')) |
2845 | + for element in themes: |
2846 | + theme = os.path.join(self.config.data_dir, 'themes', element) |
2847 | + if os.path.isdir(theme): |
2848 | + self.themes.append(element) |
2849 | + |
2850 | + |
2851 | +class LogViewer: |
2852 | + """ |
2853 | + Implements dialog that allows user to see logged events. |
2854 | + |
2855 | + This dialog is used to check Entertainer logfiles. It reads all data from |
2856 | + selected file and saves rows to self.log_rows. Then it filters unwanted |
2857 | + rows away by calling self.filterMessages(). This method adds rows to |
2858 | + ListStore, which is the model of TreeView object. |
2859 | + |
2860 | + Combobox and refresh -button actions read files again |
2861 | + Checkbox actions just filter current rows again |
2862 | + """ |
2863 | + |
2864 | + UI_DIR = os.path.join(os.path.dirname(__file__), "uis") |
2865 | + |
2866 | + # Is this dialog running as a stand alone process |
2867 | + __STAND_ALONE = None |
2868 | + |
2869 | + widgets = None |
2870 | + dialog = None |
2871 | + log_store = None |
2872 | + log_rows = [] |
2873 | + |
2874 | + def __init__(self, stand_alone): |
2875 | + self.logfile_entertainer = Configuration().LOG |
2876 | + self.logger = Logger().getLogger('utils.log_viewer') |
2877 | + |
2878 | + self.__STAND_ALONE = stand_alone |
2879 | + try: |
2880 | + uifile = os.path.join(self.UI_DIR, "log_dialog.ui") |
2881 | + self.builder = gtk.Builder() |
2882 | + self.builder.set_translation_domain('entertainer') |
2883 | + self.builder.add_from_file(uifile) |
2884 | + except RuntimeError: |
2885 | + self.logger.critical("Couldn't open ui file: " + uifile) |
2886 | + sys.exit(1) |
2887 | + callback_dic = { |
2888 | + "on_close_log_button_clicked" : self.on_close_log_button_clicked, |
2889 | + "on_log_refresh_button_clicked" : self.update_log_rows, |
2890 | + "on_checkbutton_debug_toggled" : self.filter_messages, |
2891 | + "on_checkbutton_critical_toggled" : self.filter_messages, |
2892 | + "on_checkbutton_error_toggled" : self.filter_messages, |
2893 | + "on_checkbutton_warning_toggled" : self.filter_messages, |
2894 | + "on_checkbutton_info_toggled" : self.filter_messages } |
2895 | + |
2896 | + self.builder.connect_signals(callback_dic) |
2897 | + |
2898 | + # Create log treeview |
2899 | + treeview = self.builder.get_object("treeview_log") |
2900 | + cell_renderer1 = gtk.CellRendererText() |
2901 | + cell_renderer2 = gtk.CellRendererText() |
2902 | + cell_renderer3 = gtk.CellRendererText() |
2903 | + cell_renderer4 = gtk.CellRendererText() |
2904 | + |
2905 | + column1 = gtk.TreeViewColumn("Date") |
2906 | + column1.pack_start(cell_renderer1, True) |
2907 | + column1.set_attributes(cell_renderer1, text = 0) |
2908 | + |
2909 | + column2 = gtk.TreeViewColumn("Time") |
2910 | + column2.pack_start(cell_renderer2, True) |
2911 | + column2.set_attributes(cell_renderer2, text = 1) |
2912 | + |
2913 | + column3 = gtk.TreeViewColumn("Type") |
2914 | + column3.pack_start(cell_renderer3, True) |
2915 | + column3.set_attributes(cell_renderer3, text = 2) |
2916 | + |
2917 | + column4 = gtk.TreeViewColumn("Message") |
2918 | + column4.pack_end(cell_renderer4, True) |
2919 | + column4.set_attributes(cell_renderer4, text = 3) |
2920 | + |
2921 | + treeview.append_column(column1) |
2922 | + treeview.append_column(column2) |
2923 | + treeview.append_column(column3) |
2924 | + treeview.append_column(column4) |
2925 | + treeview.set_headers_visible(True) |
2926 | + |
2927 | + # Set model to view and read data from logfile |
2928 | + self.log_store = gtk.ListStore(str, str, str, str) |
2929 | + treeview.set_model(self.log_store) |
2930 | + self.update_log_rows() |
2931 | + |
2932 | + # Show Log viewer dialog |
2933 | + self.dialog = self.builder.get_object("LogDialog") |
2934 | + self.dialog.resize(750, 500) |
2935 | + self.dialog.connect("destroy", self.on_close_log_button_clicked) |
2936 | + self.dialog.show() |
2937 | + |
2938 | + def update_log_rows(self, widget=None): |
2939 | + """Read logfile and udpate treeview""" |
2940 | + self.log_rows[:] = [] |
2941 | + |
2942 | + try: |
2943 | + for line in open(self.logfile_entertainer, 'r'): |
2944 | + try: |
2945 | + line_table = line.split() |
2946 | + message = ' '.join(line_table[3:]) |
2947 | + row = line_table[:3] + [message] |
2948 | + parsed_row = parse_row(row) |
2949 | + self.log_rows.append(parsed_row) |
2950 | + except IndexError: |
2951 | + print "Cannot parse log line: ", line |
2952 | + except IOError: |
2953 | + print "Cannot find logfile: ", self.logfile_entertainer |
2954 | + |
2955 | + # Reverse so that the latest message is at top |
2956 | + self.log_rows.reverse() |
2957 | + # Filter unwated message types |
2958 | + self.filter_messages() |
2959 | + |
2960 | + def filter_messages(self, widget = None): |
2961 | + """Checks which message types should be displayed on treeview""" |
2962 | + if self.log_store: |
2963 | + self.log_store.clear() |
2964 | + |
2965 | + debug = self.builder.get_object("checkbutton_debug").get_active() |
2966 | + critical = self.builder.get_object("checkbutton_critical").get_active() |
2967 | + error = self.builder.get_object("checkbutton_error").get_active() |
2968 | + warning = self.builder.get_object("checkbutton_warning").get_active() |
2969 | + info = self.builder.get_object("checkbutton_info").get_active() |
2970 | + |
2971 | + for element in self.log_rows: |
2972 | + if element[2] == "DEBUG" and debug: |
2973 | + self.log_store.append(element) |
2974 | + elif element[2] == "CRITICAL" and critical: |
2975 | + self.log_store.append(element) |
2976 | + elif element[2] == "ERROR" and error: |
2977 | + self.log_store.append(element) |
2978 | + elif element[2] == "WARNING" and warning: |
2979 | + self.log_store.append(element) |
2980 | + elif element[2] == "INFO" and info: |
2981 | + self.log_store.append(element) |
2982 | + |
2983 | + # Signal handlers |
2984 | + def on_close_log_button_clicked(self, widget): |
2985 | + """ |
2986 | + If running as a stand alone process, quit. |
2987 | + Otherwise only destroy dialog. |
2988 | + """ |
2989 | + self.dialog.hide() |
2990 | + self.dialog.destroy() |
2991 | + if(self.__STAND_ALONE): |
2992 | + gtk.main_quit() |
2993 | + |
2994 | + |
2995 | +def parse_row(row): |
2996 | + """ |
2997 | + This parses the input list into a list suitable for the logviewer |
2998 | + @author Joshua Scotton |
2999 | + @param row The input list [Date, Time, Class, Type + Description] |
3000 | + """ |
3001 | + if row[3][:5] == "DEBUG": |
3002 | + return [row[0], row[1], "DEBUG", |
3003 | + row[2] + ": " + row[3][5:]] |
3004 | + elif row[3][:8] == "CRITICAL": |
3005 | + return [row[0], row[1], "CRITICAL", |
3006 | + row[2] + ": " + row[3][8:]] |
3007 | + elif row[3][:5] == "ERROR": |
3008 | + return [row[0], row[1], "ERROR", |
3009 | + row[2] + ": " + row[3][5:]] |
3010 | + elif row[3][:7] == "WARNING": |
3011 | + return [row[0], row[1], "WARNING", |
3012 | + row[2] + ": " + row[3][7:]] |
3013 | + elif row[3][:4] == "INFO": |
3014 | + return [row[0], row[1], "INFO", |
3015 | + row[2] + ": " + row[3][4:]] |
3016 | + |
3017 | + |
3018 | +class OpenFeedSourceDialog: |
3019 | + '''Feed source reader dialog''' |
3020 | + |
3021 | + UI_DIR = os.path.join(os.path.dirname(__file__), "uis") |
3022 | + |
3023 | + widgets = None |
3024 | + dialog = None |
3025 | + tree_widget = None |
3026 | + feeds = None |
3027 | + url = None |
3028 | + |
3029 | + def __init__(self, the_widget, the_feeds): |
3030 | + """initialises the gtk window and displays it""" |
3031 | + |
3032 | + #feeds is a pointer to a list of feeds from the config file |
3033 | + self.feeds = the_feeds |
3034 | + |
3035 | + #needed so we can add feeds to the feed list widget |
3036 | + self.tree_widget = the_widget |
3037 | + |
3038 | + # Load UI with gtk.Builder |
3039 | + uifile = os.path.join(self.UI_DIR, "open_feed_source_dialog.ui") |
3040 | + self.builder = gtk.Builder() |
3041 | + self.builder.set_translation_domain('entertainer') |
3042 | + self.builder.add_from_file(uifile) |
3043 | + |
3044 | + # Get content management dialog and bind signal callbacks |
3045 | + self.dialog = self.builder.get_object("open_source_dialog") |
3046 | + if (self.dialog): |
3047 | + callback_dic = { |
3048 | + "on_fileOpen_clicked" : self.on_fileOpen_clicked, |
3049 | + "on_lifereaButton_clicked" : self.on_lifereaButton_clicked, |
3050 | + "on_enterURL_clicked" : self.on_enterURL_clicked, |
3051 | + "on_closeButton_clicked" : self.on_closeButton_clicked, |
3052 | + "on_url_dialog_ok_button_clicked" : |
3053 | + self.on_url_dialog_ok_button_clicked, |
3054 | + "on_url_dialog_cancel_button_clicked" : |
3055 | + self.on_url_dialog_cancel_button_clicked, |
3056 | + "on_url_dialog_delete_event" : self.on_url_dialog_delete_event, |
3057 | + "on_destroy" : self.on_closeButton_clicked |
3058 | + } |
3059 | + |
3060 | + self.builder.connect_signals(callback_dic) |
3061 | + |
3062 | + # Initilize dialog widgets with correct values and show dialog |
3063 | + self.dialog.resize(300, 200) |
3064 | + self.dialog.run() |
3065 | + |
3066 | + def on_fileOpen_clicked(self, widget): |
3067 | + """Opens add file dialog and then adds all feeds in the opml file |
3068 | + selected """ |
3069 | + |
3070 | + #get the model for the feed list widget so we can add the new feeds |
3071 | + model = self.tree_widget.get_model() |
3072 | + |
3073 | + #create select file dialog |
3074 | + dialog = gtk.FileChooserDialog(_("Select OPML file"), None, |
3075 | + gtk.FILE_CHOOSER_ACTION_OPEN, |
3076 | + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, |
3077 | + gtk.RESPONSE_OK)) |
3078 | + |
3079 | + #set dialog up to filter for only opml files |
3080 | + file_filter = gtk.FileFilter() |
3081 | + file_filter.set_name(_("OPML files")) |
3082 | + file_filter.add_pattern("*.opml") |
3083 | + dialog.add_filter(file_filter) |
3084 | + |
3085 | + #set dialog up to allow multiple selections |
3086 | + dialog.set_select_multiple(True) |
3087 | + |
3088 | + #run the dialog |
3089 | + status = dialog.run() |
3090 | + |
3091 | + # if file was selected, get list of feeds from it and add to |
3092 | + #model and update config file |
3093 | + if(status == gtk.RESPONSE_OK): |
3094 | + FeedConfigTools().add_file_feeds_to_widget( |
3095 | + dialog.get_filenames(), model, self.feeds) |
3096 | + dialog.destroy() |
3097 | + |
3098 | + def on_lifereaButton_clicked(self, widget): |
3099 | + """Adds any liferea feeds it finds to the feed widget and config file |
3100 | + """ |
3101 | + #get the model |
3102 | + model = self.tree_widget.get_model() |
3103 | + #get the liferea feeds and then send everything to the |
3104 | + #add_file_feeds_to_widget method |
3105 | + FeedConfigTools().add_file_feeds_to_widget( |
3106 | + [OPMLParser().get_liferea_opml()], model, self.feeds) |
3107 | + |
3108 | + def on_enterURL_clicked(self, widget): |
3109 | + """gets a opml file link from a user and adds any feeds it finds to the |
3110 | + feed widget and config file""" |
3111 | + url_dialog = self.builder.get_object("url_dialog") |
3112 | + model = self.tree_widget.get_model() |
3113 | + # Open dialog |
3114 | + url_dialog.set_title(_("Add OPML File")) |
3115 | + status = url_dialog.run() |
3116 | + # If folder was selected we add it to model and update config file |
3117 | + if(status == gtk.RESPONSE_OK): |
3118 | + FeedConfigTools().add_file_feeds_to_widget([self.url], |
3119 | + model, self.feeds) |
3120 | + |
3121 | + def on_closeButton_clicked(self, widget): |
3122 | + self.dialog.hide() |
3123 | + self.dialog.destroy() |
3124 | + |
3125 | + def on_url_dialog_ok_button_clicked(self, widget): |
3126 | + """URL dialog OK button pressed. Sets self.url""" |
3127 | + url_dialog = self.builder.get_object("url_dialog") |
3128 | + url_entry = self.builder.get_object("url_entry") |
3129 | + url_dialog.hide() |
3130 | + self.url = url_entry.get_text() |
3131 | + url_entry.set_text("") |
3132 | + url_dialog.response(gtk.RESPONSE_OK) |
3133 | + |
3134 | + def on_url_dialog_cancel_button_clicked(self, widget): |
3135 | + """URL dialog cancelled. Hides dialog""" |
3136 | + url_dialog = self.builder.get_object("url_dialog") |
3137 | + url_entry = self.builder.get_object("url_entry") |
3138 | + url_dialog.hide() |
3139 | + url_entry.set_text("") |
3140 | + url_dialog.response(gtk.RESPONSE_CANCEL) |
3141 | + |
3142 | + def on_url_dialog_delete_event(self, widget, data): |
3143 | + """Dialog's X clicked. Hides dialog""" |
3144 | + url_dialog = self.builder.get_object("url_dialog") |
3145 | + url_entry = self.builder.get_object("url_entry") |
3146 | + url_dialog.hide() |
3147 | + url_entry.set_text("") |
3148 | + url_dialog.response(gtk.RESPONSE_CANCEL) |
3149 | + return True |
3150 | + |
3151 | + |
3152 | |
3153 | === added file 'entertainerlib/download.py' |
3154 | --- entertainerlib/download.py 1970-01-01 00:00:00 +0000 |
3155 | +++ entertainerlib/download.py 2010-01-23 03:34:11 +0000 |
3156 | @@ -0,0 +1,455 @@ |
3157 | +# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
3158 | +'''Downloader classes.''' |
3159 | + |
3160 | +import locale |
3161 | +import os |
3162 | +import re |
3163 | +import socket |
3164 | +import threading |
3165 | +import urllib |
3166 | +from xml.dom import minidom |
3167 | + |
3168 | +import gobject |
3169 | + |
3170 | +# Amazon licence for Entertainer |
3171 | +LICENSE_KEY = "1YCWYZ0SPPAJ27YZZ482" |
3172 | +DEFAULT_LOCALE = "en_US" |
3173 | +ASSOCIATE = "webservices-20" |
3174 | + |
3175 | +# We are not allowed to batch more than 2 requests at once |
3176 | +# http://docs.amazonwebservices.com/AWSEcommerceService/4-0/ |
3177 | +# PgCombiningOperations.html |
3178 | +MAX_BATCH_JOBS = 2 |
3179 | + |
3180 | + |
3181 | +class AlbumArtDownloader(threading.Thread): |
3182 | + """ |
3183 | + Search and download album art from the internet. |
3184 | + |
3185 | + This class is heavily based on Rhythmbox - AlbumArt plugin's class |
3186 | + 'AmazonCoverArtSearch'. That plugin is released under GPLv2 (or higher) |
3187 | + and it's copyrights belong to Gareth Murphy and Martin Szulecki. |
3188 | + |
3189 | + See more: http://www.gnome.org/projects/rhythmbox/ |
3190 | + |
3191 | + If you want better cover search, please contribute to Rhyhtmbox project. |
3192 | + """ |
3193 | + |
3194 | + def __init__(self, album, artist, art_file_path, callback = None): |
3195 | + """ |
3196 | + Initialize album art downloader |
3197 | + @param album: Album title |
3198 | + @param artist: Artist name |
3199 | + @param art_file_path: Path where albumart is saved |
3200 | + @param callback: Callback function that is called after search if set |
3201 | + """ |
3202 | + threading.Thread.__init__(self) |
3203 | + self.setName("AlbumArt Downloader") |
3204 | + self.callback_function = callback # Callback function |
3205 | + self.album = album # Album title |
3206 | + self.artist = artist # Artist name |
3207 | + # Album art files are in this directory |
3208 | + self.path = art_file_path |
3209 | + (self.tld, self.encoding) = self.__get_locale () |
3210 | + |
3211 | + def run(self): |
3212 | + """Start searching and downloading albumart.""" |
3213 | + self.search() |
3214 | + |
3215 | + def __get_locale (self): |
3216 | + '''Get locale information from user\'s machine''' |
3217 | + # "JP is the only locale that correctly takes UTF8 input. |
3218 | + # All other locales use LATIN1." |
3219 | + # http://developer.amazonwebservices.com/connect/ |
3220 | + # entry.jspa?externalID=1295&categoryID=117 |
3221 | + supported_locales = { |
3222 | + "en_US" : ("com", "latin1"), |
3223 | + "en_GB" : ("co.uk", "latin1"), |
3224 | + "de" : ("de", "latin1"), |
3225 | + "ja" : ("jp", "utf8") |
3226 | + } |
3227 | + |
3228 | + lc_id = DEFAULT_LOCALE |
3229 | + default = locale.getdefaultlocale ()[0] |
3230 | + if default: |
3231 | + if supported_locales.has_key (default): |
3232 | + lc_id = default |
3233 | + else: |
3234 | + lang = default.split("_")[0] |
3235 | + if supported_locales.has_key (lang): |
3236 | + lc_id = lang |
3237 | + |
3238 | + return supported_locales[lc_id] |
3239 | + |
3240 | + def __valid_match (self, item): |
3241 | + '''Determine if item matches tag criteria''' |
3242 | + return (hasattr (item, "LargeImage") or hasattr (item, "MediumImage")) \ |
3243 | + and hasattr (item, "ItemAttributes") |
3244 | + |
3245 | + def __tidy_up_string (self, str_input): |
3246 | + """ |
3247 | + Tidy up string. Remove spaces, convert to lowercase and replace chars. |
3248 | + """ |
3249 | + # Lowercase |
3250 | + str_input = str_input.lower () |
3251 | + # Strip |
3252 | + str_input = str_input.strip () |
3253 | + |
3254 | + # TODO: Convert accented to unaccented |
3255 | + str_input = str_input.replace (" - ", " ") |
3256 | + str_input = str_input.replace (": ", " ") |
3257 | + str_input = str_input.replace (" & ", " and ") |
3258 | + |
3259 | + return str_input |
3260 | + |
3261 | + def search(self): |
3262 | + """Search album art from Amazon""" |
3263 | + self.searching = True |
3264 | + self.cancel = False |
3265 | + self.keywords = [] |
3266 | + |
3267 | + st_artist = self.artist or u'Unknown' |
3268 | + st_album = self.album or u'Unknown' |
3269 | + |
3270 | + if st_artist == st_album == u'Unknown': |
3271 | + self.on_search_completed (None) |
3272 | + return |
3273 | + |
3274 | + # Tidy up |
3275 | + |
3276 | + # Replace quote characters |
3277 | + # don't replace single quote: could be important punctuation |
3278 | + for char in ["\""]: |
3279 | + st_artist = st_artist.replace (char, '') |
3280 | + st_album = st_album.replace (char, '') |
3281 | + |
3282 | + |
3283 | + self.st_album = st_album |
3284 | + self.st_artist = st_artist |
3285 | + |
3286 | + # Remove variants of Disc/CD [1-9] from album title before search |
3287 | + for exp in ["\([Dd]isc *[1-9]+\)", "\([Cc][Dd] *[1-9]+\)"]: |
3288 | + p = re.compile (exp) |
3289 | + st_album = p.sub ('', st_album) |
3290 | + |
3291 | + st_album_no_vol = st_album |
3292 | + for exp in ["\(*[Vv]ol.*[1-9]+\)*"]: |
3293 | + p = re.compile (exp) |
3294 | + st_album_no_vol = p.sub ('', st_album_no_vol) |
3295 | + |
3296 | + self.st_album_no_vol = st_album_no_vol |
3297 | + |
3298 | + # Save current search's entry properties |
3299 | + self.search_album = st_album |
3300 | + self.search_artist = st_artist |
3301 | + self.search_album_no_vol = st_album_no_vol |
3302 | + |
3303 | + # TODO: Improve to decrease wrong cover downloads, maybe add severity? |
3304 | + # Assemble list of search keywords (and thus search queries) |
3305 | + if st_album == u'Unknown': |
3306 | + self.keywords.append ("%s Best of" % (st_artist)) |
3307 | + self.keywords.append ("%s Greatest Hits" % (st_artist)) |
3308 | + self.keywords.append ("%s Essential" % (st_artist)) |
3309 | + self.keywords.append ("%s Collection" % (st_artist)) |
3310 | + self.keywords.append ("%s" % (st_artist)) |
3311 | + elif st_artist == u'Unknown': |
3312 | + self.keywords.append ("%s" % (st_album)) |
3313 | + if st_album_no_vol != st_artist: |
3314 | + self.keywords.append ("%s" % (st_album_no_vol)) |
3315 | + self.keywords.append ("Various %s" % (st_album)) |
3316 | + else: |
3317 | + if st_album != st_artist: |
3318 | + self.keywords.append ("%s %s" % (st_artist, st_album)) |
3319 | + if st_album_no_vol != st_album: |
3320 | + self.keywords.append ("%s %s" % |
3321 | + (st_artist, st_album_no_vol)) |
3322 | + self.keywords.append ("Various %s" % (st_album)) |
3323 | + self.keywords.append ("%s" % (st_artist)) |
3324 | + |
3325 | + # Initiate asynchronous search |
3326 | + self.search_next () |
3327 | + |
3328 | + def search_next(self): |
3329 | + """Search again, because the last one didn't find any covers.""" |
3330 | + if len (self.keywords) == 0: |
3331 | + # No keywords left to search -> no results |
3332 | + self.on_search_completed (None) |
3333 | + return False |
3334 | + |
3335 | + self.searching = True |
3336 | + |
3337 | + url = "http://ecs.amazonaws." + self.tld + "/onca/xml" \ |
3338 | + "?Service=AWSECommerceService" \ |
3339 | + "&AWSAccessKeyId=" + LICENSE_KEY + \ |
3340 | + "&AssociateTag=" + ASSOCIATE + \ |
3341 | + "&ResponseGroup=Images,ItemAttributes" \ |
3342 | + "&Operation=ItemSearch" \ |
3343 | + "&ItemSearch.Shared.SearchIndex=Music" |
3344 | + |
3345 | + job = 1 |
3346 | + while job <= MAX_BATCH_JOBS and len (self.keywords) > 0: |
3347 | + keyword = self.keywords.pop (0) |
3348 | + keyword = keyword.encode (self.encoding, "ignore") |
3349 | + keyword = keyword.strip () |
3350 | + keyword = urllib.quote (keyword) |
3351 | + url += "&ItemSearch.%d.Keywords=%s" % (job, keyword) |
3352 | + job += 1 |
3353 | + |
3354 | + # Retrieve search for keyword |
3355 | + temp = urllib.urlopen(url) |
3356 | + search_results = temp.read() |
3357 | + self.on_search_response(search_results) |
3358 | + return True |
3359 | + |
3360 | + def __unmarshal(self, element): |
3361 | + rc = object() |
3362 | + child_elements = [e for e in element.childNodes if isinstance (e, |
3363 | + minidom.Element)] |
3364 | + if child_elements: |
3365 | + for child in child_elements: |
3366 | + key = child.tagName |
3367 | + if hasattr (rc, key): |
3368 | + if not isinstance (getattr (rc, key), list): |
3369 | + setattr (rc, key, [getattr (rc, key)]) |
3370 | + getattr (rc, key).append (self.__unmarshal (child)) |
3371 | + # get_best_match_urls() wants a list, even if there is only |
3372 | + # one item/artist |
3373 | + elif child.tagName in ("Items", "Item", "Artist"): |
3374 | + setattr (rc, key, [self.__unmarshal(child)]) |
3375 | + else: |
3376 | + setattr (rc, key, self.__unmarshal(child)) |
3377 | + else: |
3378 | + rc = "".join ([e.data for e in element.childNodes if isinstance (e, |
3379 | + minidom.Text)]) |
3380 | + return rc |
3381 | + |
3382 | + def on_search_response (self, result_data): |
3383 | + '''Check search results |
3384 | + |
3385 | + If results are not good, we search again with the next keyword. |
3386 | + ''' |
3387 | + if result_data is None: |
3388 | + self.search_next() |
3389 | + return |
3390 | + |
3391 | + try: |
3392 | + xmldoc = minidom.parseString(result_data) |
3393 | + except (TypeError, AttributeError): |
3394 | + self.search_next() |
3395 | + return |
3396 | + |
3397 | + data = self.__unmarshal (xmldoc) |
3398 | + if not hasattr (data, "ItemSearchResponse") or \ |
3399 | + not hasattr (data.ItemSearchResponse, "Items"): |
3400 | + # Something went wrong ... |
3401 | + self.search_next () |
3402 | + else: |
3403 | + # We got some search results |
3404 | + self.on_search_results (data.ItemSearchResponse.Items) |
3405 | + |
3406 | + def on_search_results (self, results): |
3407 | + '''Results were found, now we need to take action.''' |
3408 | + self.on_search_completed (results) |
3409 | + |
3410 | + def on_search_completed (self, result): |
3411 | + """ |
3412 | + Search completed and results found. |
3413 | + |
3414 | + Download large album art image from the first result and save it to |
3415 | + the disk. This function diverges greatly from the rhythmbox |
3416 | + implementation in order to avoid their loader and CoverArtDatabase |
3417 | + """ |
3418 | + self.searching = False |
3419 | + image_urls = self.get_best_match_urls(result) |
3420 | + if len(image_urls) == 0: |
3421 | + return |
3422 | + image_url = image_urls[0] |
3423 | + image_file = urllib.urlopen(image_url) |
3424 | + # base64 encode artist and album so there can be a '/' in the artist |
3425 | + # or album |
3426 | + artist_album = self.artist + " - " + self.album |
3427 | + artist_album = artist_album.encode("base64") |
3428 | + |
3429 | + dest = open(os.path.join(self.path, artist_album + ".jpg"),'w') |
3430 | + dest.write(image_file.read()) |
3431 | + dest.close() |
3432 | + |
3433 | + if self.callback_function is not None: |
3434 | + self.callback_function(self.artist, self.album) |
3435 | + |
3436 | + def get_best_match_urls (self, search_results): |
3437 | + """Return tuple of URL's to large and medium cover of the best match""" |
3438 | + # Default to "no match", our results must match our criteria |
3439 | + |
3440 | + # This code comes from Rhythmbox so we can't control the use of 'filter' |
3441 | + # pylint: disable-msg=W0141 |
3442 | + |
3443 | + if not search_results: |
3444 | + return [] |
3445 | + |
3446 | + best_match = None |
3447 | + |
3448 | + for result in search_results: |
3449 | + if not hasattr (result, "Item"): |
3450 | + # Search was unsuccessful, try next batch job |
3451 | + continue |
3452 | + |
3453 | + items = filter(self.__valid_match, result.Item) |
3454 | + if self.search_album != u'Unknown': |
3455 | + album_check = self.__tidy_up_string (self.search_album) |
3456 | + for item in items: |
3457 | + if not hasattr (item.ItemAttributes, "Title"): |
3458 | + continue |
3459 | + |
3460 | + album = self.__tidy_up_string (item.ItemAttributes.Title) |
3461 | + if album == album_check: |
3462 | + # Found exact album, can not get better than that |
3463 | + best_match = item |
3464 | + break |
3465 | + # If we already found a best_match, just keep checking for |
3466 | + # exact one. Check the results for both an album name that |
3467 | + # contains the name we're searching for, and an album name |
3468 | + # that's a substring of the name we're searching for |
3469 | + elif (best_match is None) and \ |
3470 | + (album.find (album_check) != -1 or |
3471 | + album_check.find (album) != -1): |
3472 | + best_match = item |
3473 | + |
3474 | + # If we still have no definite hit, use first result where artist |
3475 | + # matches |
3476 | + if (self.search_album == u'Unknown' and \ |
3477 | + self.search_artist != u'Unknown'): |
3478 | + artist_check = self.__tidy_up_string (self.search_artist) |
3479 | + if best_match is None: |
3480 | + # Check if artist appears in the Artists list |
3481 | + hit = False |
3482 | + for item in items: |
3483 | + if not hasattr (item.ItemAttributes, "Artist"): |
3484 | + continue |
3485 | + |
3486 | + for artist in item.ItemAttributes.Artist: |
3487 | + artist = self.__tidy_up_string (artist) |
3488 | + if artist.find (artist_check) != -1: |
3489 | + best_match = item |
3490 | + hit = True |
3491 | + break |
3492 | + if hit: |
3493 | + break |
3494 | + |
3495 | + urls = [getattr (best_match, size).URL for size in ("LargeImage", |
3496 | + "MediumImage") |
3497 | + if hasattr (best_match, size)] |
3498 | + if urls: |
3499 | + return urls |
3500 | + |
3501 | + # No search was successful |
3502 | + return [] |
3503 | + |
3504 | + |
3505 | +class LyricsDownloader(object): |
3506 | + """ |
3507 | + Search and download song lyrics from the internet. |
3508 | + Update music cache if lyrics found. |
3509 | + """ |
3510 | + |
3511 | + # The permanent user ID from Lyricsfly |
3512 | + # NOTICE: This is the personal user ID for Entertainer, if you want to |
3513 | + # experiment with the API from lyricsfly you can get an ID here => |
3514 | + # http://lyricsfly.com/api/, don't use this one as abuse of our key may |
3515 | + # invalidate it. |
3516 | + _LYRICSFLY_KEY = 'YzIxOTM4M2NkNGQ4MmRmODEtZW50ZXJ0YWluZXItcHJvamVjdC5jb20=' |
3517 | + |
3518 | + def __init__(self, title, artist, callback): |
3519 | + """ |
3520 | + Initialize lyrics downloader. |
3521 | + @param title: Title of the track |
3522 | + @param artist: Artist of the track |
3523 | + @param callback: Callback function which is called when search is over. |
3524 | + lyrics are given as a parameter to this callback function. |
3525 | + """ |
3526 | + self.title = title.lower() |
3527 | + self.artist = artist.lower() |
3528 | + self.callback = callback |
3529 | + |
3530 | + def search(self): |
3531 | + """ |
3532 | + Search lyrics and download if found. Search is done asynchronously. |
3533 | + This method returns immediately and set callback is called when search |
3534 | + is over. |
3535 | + """ |
3536 | + gobject.timeout_add(2000, self._async_search) |
3537 | + |
3538 | + def _async_search(self): |
3539 | + """ |
3540 | + Search lyrics and download if found |
3541 | + """ |
3542 | + lyrics = "" |
3543 | + self._clean_up_artist_title() |
3544 | + lyrics_xml = self._get_lyrics_xml() |
3545 | + if lyrics_xml is not None: |
3546 | + lyrics = self._parse_lyrics_xml(lyrics_xml) |
3547 | + self.callback(lyrics) |
3548 | + |
3549 | + def _clean_up_artist_title(self): |
3550 | + """ |
3551 | + Clean up the artist and title. |
3552 | + """ |
3553 | + # Clean spaces |
3554 | + self.title = self.title.strip() |
3555 | + self.artist = self.artist.strip() |
3556 | + |
3557 | + # Convert title and artist to use in url, special symbols have to be |
3558 | + # replaced by a '%' not '%xx' |
3559 | + # TODO: Find out what the special symbols are (', &, ...) |
3560 | + # not letters, digits, spaces and ()$^*=:;|#@}{][!,.-_\ |
3561 | + self.artist = urllib.quote(self.artist.encode('utf-8'), |
3562 | + "'&()$^*=:;|#@}{][!,\\") |
3563 | + self.title = urllib.quote(self.title.encode('utf-8'), |
3564 | + "'&()$^*=:;|#@}{][!,\\") |
3565 | + |
3566 | + self.artist = self.artist.replace("'", "%").replace("&", "%") |
3567 | + self.title = self.title.replace("'", "%").replace("&", "%") |
3568 | + |
3569 | + def _get_lyrics_xml(self): |
3570 | + """ |
3571 | + Download the lyrics XML-file. |
3572 | + """ |
3573 | + lyrics_xml = None |
3574 | + |
3575 | + # timeout in seconds |
3576 | + timeout = 5 |
3577 | + socket.setdefaulttimeout(timeout) |
3578 | + |
3579 | + url = "http://lyricsfly.com/api/api.php?i=%s&a=%s&t=%s" \ |
3580 | + % (self._LYRICSFLY_KEY.decode('base64'), self.artist, self.title) |
3581 | + try: |
3582 | + temp = urllib.urlopen(url) |
3583 | + lyrics_xml = temp.read() |
3584 | + except IOError: |
3585 | + return None |
3586 | + |
3587 | + return lyrics_xml |
3588 | + |
3589 | + def _parse_lyrics_xml(self, lyrics_xml): |
3590 | + """Parse lyrics XML and return lyrics string""" |
3591 | + xmldoc = minidom.parseString(lyrics_xml).documentElement |
3592 | + |
3593 | + # Get the lyric from the XML file |
3594 | + try: |
3595 | + lyrics = xmldoc.getElementsByTagName('tx')[0].firstChild.nodeValue |
3596 | + except IndexError: |
3597 | + return '' |
3598 | + |
3599 | + # Clean spaces and enters |
3600 | + lyrics = lyrics.strip().replace('\n', '') |
3601 | + lyrics = lyrics.replace('[br]', '\n') |
3602 | + |
3603 | + # Add the artist and title to the top of the lyric |
3604 | + lyrics = xmldoc.getElementsByTagName('ar')[0].firstChild.nodeValue + \ |
3605 | + ' - ' + xmldoc.getElementsByTagName('tt')[0].firstChild.nodeValue + \ |
3606 | + '\n\n' + lyrics |
3607 | + |
3608 | + xmldoc.unlink() |
3609 | + |
3610 | + return lyrics |
3611 | + |
3612 | |
3613 | === renamed directory 'entertainerlib/frontend/gui' => 'entertainerlib/gui' |
3614 | === modified file 'entertainerlib/gui/screen_history.py' |
3615 | --- entertainerlib/frontend/gui/screen_history.py 2009-06-03 01:54:03 +0000 |
3616 | +++ entertainerlib/gui/screen_history.py 2010-01-23 03:34:11 +0000 |
3617 | @@ -1,7 +1,7 @@ |
3618 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
3619 | '''Stack container for screen objects''' |
3620 | |
3621 | -from entertainerlib.utils.configuration import Configuration |
3622 | +from entertainerlib.configuration import Configuration |
3623 | |
3624 | class ScreenHistory(object): |
3625 | '''ScreenHistory contains the latest screens in is a stack.''' |
3626 | @@ -13,7 +13,7 @@ |
3627 | |
3628 | def add_screen(self, screen): |
3629 | '''Push the provided screen onto the history stack.''' |
3630 | - if len(self.screens) < self.config.history_size(): |
3631 | + if len(self.screens) < self.config.history_size: |
3632 | self.screens.append(screen) |
3633 | else: |
3634 | self.remove_from_stage_callback(self.screens[0]) |
3635 | |
3636 | === modified file 'entertainerlib/gui/screens/album.py' |
3637 | --- entertainerlib/frontend/gui/screens/album.py 2009-07-14 10:46:47 +0000 |
3638 | +++ entertainerlib/gui/screens/album.py 2010-01-23 03:34:11 +0000 |
3639 | @@ -5,12 +5,11 @@ |
3640 | import clutter |
3641 | import pango |
3642 | |
3643 | -from entertainerlib.frontend.gui.screens.screen import Screen |
3644 | -from entertainerlib.frontend.gui.widgets.eyecandy_texture import ( |
3645 | - EyeCandyTexture) |
3646 | -from entertainerlib.frontend.gui.widgets.label import Label |
3647 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
3648 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
3649 | +from entertainerlib.gui.screens.screen import Screen |
3650 | +from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture |
3651 | +from entertainerlib.gui.widgets.label import Label |
3652 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
3653 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
3654 | |
3655 | class Album(Screen): |
3656 | '''Screen that allows user to browse and play tracks of the music album.''' |
3657 | |
3658 | === modified file 'entertainerlib/gui/screens/artist.py' |
3659 | --- entertainerlib/frontend/gui/screens/artist.py 2009-05-12 17:18:10 +0000 |
3660 | +++ entertainerlib/gui/screens/artist.py 2010-01-23 03:34:11 +0000 |
3661 | @@ -3,11 +3,11 @@ |
3662 | |
3663 | import pango |
3664 | |
3665 | -from entertainerlib.frontend.gui.screens.screen import Screen |
3666 | -from entertainerlib.frontend.gui.tabs.albums_tab import AlbumsTab |
3667 | -from entertainerlib.frontend.gui.tabs.tracks_tab import TracksTab |
3668 | -from entertainerlib.frontend.gui.widgets.label import Label |
3669 | -from entertainerlib.frontend.medialibrary.playlist import Playlist |
3670 | +from entertainerlib.gui.screens.screen import Screen |
3671 | +from entertainerlib.gui.tabs.albums_tab import AlbumsTab |
3672 | +from entertainerlib.gui.tabs.tracks_tab import TracksTab |
3673 | +from entertainerlib.gui.widgets.label import Label |
3674 | +from entertainerlib.client.medialibrary.playlist import Playlist |
3675 | |
3676 | class Artist(Screen): |
3677 | '''Screen that allows user to browse music by artist.''' |
3678 | |
3679 | === modified file 'entertainerlib/gui/screens/audio_play.py' |
3680 | --- entertainerlib/frontend/gui/screens/audio_play.py 2009-05-28 20:23:17 +0000 |
3681 | +++ entertainerlib/gui/screens/audio_play.py 2010-01-23 03:34:11 +0000 |
3682 | @@ -5,10 +5,10 @@ |
3683 | import gtk |
3684 | import clutter |
3685 | |
3686 | -from entertainerlib.frontend.gui.screens.screen import Screen |
3687 | -from entertainerlib.frontend.gui.tabs.lyrics_tab import LyricsTab |
3688 | -from entertainerlib.frontend.gui.tabs.playing_tab import PlayingTab |
3689 | -from entertainerlib.frontend.gui.widgets.eyecandy_texture import ( |
3690 | +from entertainerlib.gui.screens.screen import Screen |
3691 | +from entertainerlib.gui.tabs.lyrics_tab import LyricsTab |
3692 | +from entertainerlib.gui.tabs.playing_tab import PlayingTab |
3693 | +from entertainerlib.gui.widgets.eyecandy_texture import ( |
3694 | EyeCandyTexture) |
3695 | |
3696 | class AudioPlay(Screen): |
3697 | |
3698 | === modified file 'entertainerlib/gui/screens/disc.py' |
3699 | --- entertainerlib/frontend/gui/screens/disc.py 2009-07-19 20:23:56 +0000 |
3700 | +++ entertainerlib/gui/screens/disc.py 2010-01-23 03:34:11 +0000 |
3701 | @@ -9,17 +9,15 @@ |
3702 | import pango |
3703 | import clutter |
3704 | |
3705 | -from entertainerlib.frontend.gui.screens.screen import Screen |
3706 | -from entertainerlib.frontend.gui.widgets.eyecandy_texture import ( |
3707 | - EyeCandyTexture) |
3708 | -from entertainerlib.frontend.gui.widgets.label import Label |
3709 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
3710 | -from entertainerlib.frontend.gui.widgets.loading_animation import ( |
3711 | - LoadingAnimation) |
3712 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
3713 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
3714 | -from entertainerlib.frontend.medialibrary.playlist import Playlist |
3715 | -from entertainerlib.utils.albumart_downloader import AlbumArtDownloader |
3716 | +from entertainerlib.gui.screens.screen import Screen |
3717 | +from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture |
3718 | +from entertainerlib.gui.widgets.label import Label |
3719 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
3720 | +from entertainerlib.gui.widgets.loading_animation import LoadingAnimation |
3721 | +from entertainerlib.gui.widgets.texture import Texture |
3722 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
3723 | +from entertainerlib.client.medialibrary.playlist import Playlist |
3724 | +from entertainerlib.download import AlbumArtDownloader |
3725 | |
3726 | class Disc(Screen): |
3727 | '''Screen allows user to play tracks from the current Audio CD.''' |
3728 | |
3729 | === modified file 'entertainerlib/gui/screens/factory.py' |
3730 | --- entertainerlib/frontend/gui/screens/factory.py 2009-06-02 01:16:03 +0000 |
3731 | +++ entertainerlib/gui/screens/factory.py 2010-01-23 03:34:11 +0000 |
3732 | @@ -1,25 +1,26 @@ |
3733 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
3734 | '''ScreenFactory - Create correct screen based on the type provided.''' |
3735 | |
3736 | -from entertainerlib.frontend.gui.screens.artist import Artist |
3737 | -from entertainerlib.frontend.gui.screens.album import Album |
3738 | -from entertainerlib.frontend.gui.screens.audio_play import AudioPlay |
3739 | -from entertainerlib.frontend.gui.screens.disc import Disc |
3740 | -from entertainerlib.frontend.gui.screens.feed import Feed |
3741 | -from entertainerlib.frontend.gui.screens.feed_entry import FeedEntry |
3742 | -from entertainerlib.frontend.gui.screens.main import Main |
3743 | -from entertainerlib.frontend.gui.screens.movie import Movie |
3744 | -from entertainerlib.frontend.gui.screens.music import Music |
3745 | -from entertainerlib.frontend.gui.screens.photo import Photo |
3746 | -from entertainerlib.frontend.gui.screens.photo_albums import PhotoAlbums |
3747 | -from entertainerlib.frontend.gui.screens.photographs import Photographs |
3748 | -from entertainerlib.frontend.gui.screens.question import Question |
3749 | -from entertainerlib.frontend.gui.screens.rss import Rss |
3750 | -from entertainerlib.frontend.gui.screens.tv_episodes import TvEpisodes |
3751 | -from entertainerlib.frontend.gui.screens.tv_series import TvSeries |
3752 | -from entertainerlib.frontend.gui.screens.video import Video |
3753 | -from entertainerlib.frontend.gui.screens.video_osd import VideoOSD |
3754 | -from entertainerlib.frontend.gui.screens.weather import WeatherScreen |
3755 | +from entertainerlib.gui.screens.artist import Artist |
3756 | +from entertainerlib.gui.screens.album import Album |
3757 | +from entertainerlib.gui.screens.audio_play import AudioPlay |
3758 | +from entertainerlib.gui.screens.disc import Disc |
3759 | +from entertainerlib.gui.screens.feed import Feed |
3760 | +from entertainerlib.gui.screens.feed_entry import FeedEntry |
3761 | +from entertainerlib.gui.screens.main import Main |
3762 | +from entertainerlib.gui.screens.movie import Movie |
3763 | +from entertainerlib.gui.screens.music import Music |
3764 | +from entertainerlib.gui.screens.photo import Photo |
3765 | +from entertainerlib.gui.screens.photo_albums import PhotoAlbums |
3766 | +from entertainerlib.gui.screens.photographs import Photographs |
3767 | +from entertainerlib.gui.screens.question import Question |
3768 | +from entertainerlib.gui.screens.rss import Rss |
3769 | +from entertainerlib.gui.screens.tv_episodes import TvEpisodes |
3770 | +from entertainerlib.gui.screens.tv_series import TvSeries |
3771 | +from entertainerlib.gui.screens.video import Video |
3772 | +from entertainerlib.gui.screens.video_osd import VideoOSD |
3773 | +from entertainerlib.gui.screens.weather import WeatherScreen |
3774 | + |
3775 | |
3776 | class ScreenFactory(object): |
3777 | '''Generate a screen based on the type provided.''' |
3778 | |
3779 | === modified file 'entertainerlib/gui/screens/feed.py' |
3780 | --- entertainerlib/frontend/gui/screens/feed.py 2009-07-12 07:57:05 +0000 |
3781 | +++ entertainerlib/gui/screens/feed.py 2010-01-23 03:34:11 +0000 |
3782 | @@ -3,12 +3,12 @@ |
3783 | |
3784 | import pango |
3785 | |
3786 | -from entertainerlib.frontend.gui.screens.screen import Screen |
3787 | -from entertainerlib.frontend.gui.widgets.label import Label |
3788 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
3789 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
3790 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
3791 | -from entertainerlib.utils.feed_utils import FeedEntryParser |
3792 | +from entertainerlib.gui.screens.screen import Screen |
3793 | +from entertainerlib.gui.widgets.label import Label |
3794 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
3795 | +from entertainerlib.gui.widgets.texture import Texture |
3796 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
3797 | +from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser |
3798 | |
3799 | class Feed(Screen): |
3800 | '''Screen is displayed when headlines are accessed for a specific feed.''' |
3801 | |
3802 | === modified file 'entertainerlib/gui/screens/feed_entry.py' |
3803 | --- entertainerlib/frontend/gui/screens/feed_entry.py 2009-06-25 18:56:15 +0000 |
3804 | +++ entertainerlib/gui/screens/feed_entry.py 2010-01-23 03:34:11 +0000 |
3805 | @@ -3,12 +3,12 @@ |
3806 | |
3807 | import pango |
3808 | |
3809 | -from entertainerlib.frontend.gui.screens.screen import Screen |
3810 | -from entertainerlib.frontend.gui.user_event import UserEvent |
3811 | -from entertainerlib.frontend.gui.widgets.label import Label |
3812 | -from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea |
3813 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
3814 | -from entertainerlib.utils.feed_utils import FeedEntryParser |
3815 | +from entertainerlib.gui.screens.screen import Screen |
3816 | +from entertainerlib.gui.user_event import UserEvent |
3817 | +from entertainerlib.gui.widgets.label import Label |
3818 | +from entertainerlib.gui.widgets.scroll_area import ScrollArea |
3819 | +from entertainerlib.gui.widgets.texture import Texture |
3820 | +from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser |
3821 | |
3822 | class FeedEntry(Screen): |
3823 | '''Screen displays one feed entry.''' |
3824 | |
3825 | === modified file 'entertainerlib/gui/screens/main.py' |
3826 | --- entertainerlib/frontend/gui/screens/main.py 2009-07-19 19:27:31 +0000 |
3827 | +++ entertainerlib/gui/screens/main.py 2010-01-23 03:34:11 +0000 |
3828 | @@ -3,14 +3,13 @@ |
3829 | |
3830 | import clutter |
3831 | |
3832 | -from entertainerlib.frontend.gui.screens.screen import Screen |
3833 | -from entertainerlib.frontend.gui.widgets.clock_label import ClockLabel |
3834 | -from entertainerlib.frontend.gui.widgets.label import Label |
3835 | -from entertainerlib.frontend.gui.widgets.scroll_menu import ScrollMenu |
3836 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
3837 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
3838 | -from entertainerlib.utils.cd_utils import eject_cd |
3839 | -from entertainerlib.utils.feed_utils import FeedEntryParser |
3840 | +from entertainerlib.gui.screens.screen import Screen |
3841 | +from entertainerlib.gui.widgets.clock_label import ClockLabel |
3842 | +from entertainerlib.gui.widgets.label import Label |
3843 | +from entertainerlib.gui.widgets.scroll_menu import ScrollMenu |
3844 | +from entertainerlib.gui.widgets.texture import Texture |
3845 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
3846 | +from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser |
3847 | |
3848 | class Main(Screen): |
3849 | '''Screen displayed when frontend is opened and provides main navigation.''' |
3850 | @@ -76,12 +75,9 @@ |
3851 | menu.add_item(_("Photographs"), "photo") |
3852 | menu.add_item(_("Headlines"), "rss") |
3853 | |
3854 | - if self.config.display_weather_in_frontend(): |
3855 | + if self.config.display_weather_in_client: |
3856 | menu.add_item(_("Weather"), "weather") |
3857 | |
3858 | - if self.config.display_cd_eject_in_frontend(): |
3859 | - menu.add_item(_("Eject CD"), "eject_cd") |
3860 | - |
3861 | if self.media_player.has_media(): |
3862 | menu.add_item(_("Playing now..."), "playing") |
3863 | |
3864 | @@ -90,7 +86,7 @@ |
3865 | |
3866 | # Menu position |
3867 | menu_clip = menu.visible_items * 70 |
3868 | - menu_y = int((self.config.get_stage_height() - menu_clip + 10) / 2) |
3869 | + menu_y = int((self.config.stage_height - menu_clip + 10) / 2) |
3870 | menu.set_position(self.get_abs_x(0.75), menu_y) |
3871 | |
3872 | return menu |
3873 | @@ -222,8 +218,8 @@ |
3874 | |
3875 | # If the preview was updated fade it in. |
3876 | if update: |
3877 | - fade_in = clutter.Timeline(20, 60) |
3878 | - alpha_in = clutter.Alpha(fade_in, clutter.smoothstep_inc_func) |
3879 | + fade_in = clutter.Timeline(500) |
3880 | + alpha_in = clutter.Alpha(fade_in, clutter.EASE_IN_OUT_SINE) |
3881 | self.behaviour = clutter.BehaviourOpacity(0, 255, alpha_in) |
3882 | self.behaviour.apply(self.preview) |
3883 | fade_in.start() |
3884 | @@ -288,8 +284,6 @@ |
3885 | self.callback("video") |
3886 | elif item.get_name() == "weather": |
3887 | self.callback("weather") |
3888 | - elif item.get_name() == "eject_cd": |
3889 | - eject_cd() |
3890 | elif item.get_name() == "photo": |
3891 | self.callback("photo_albums") |
3892 | elif item.get_name() == "rss": |
3893 | |
3894 | === modified file 'entertainerlib/gui/screens/movie.py' |
3895 | --- entertainerlib/frontend/gui/screens/movie.py 2009-07-21 22:22:53 +0000 |
3896 | +++ entertainerlib/gui/screens/movie.py 2010-01-23 03:34:11 +0000 |
3897 | @@ -5,14 +5,13 @@ |
3898 | import gtk |
3899 | import pango |
3900 | |
3901 | -from entertainerlib.frontend.gui.screens.screen import Screen |
3902 | -from entertainerlib.frontend.gui.user_event import UserEvent |
3903 | -from entertainerlib.frontend.gui.widgets.eyecandy_texture import ( |
3904 | - EyeCandyTexture) |
3905 | -from entertainerlib.frontend.gui.widgets.label import Label |
3906 | -from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea |
3907 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
3908 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
3909 | +from entertainerlib.gui.screens.screen import Screen |
3910 | +from entertainerlib.gui.user_event import UserEvent |
3911 | +from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture |
3912 | +from entertainerlib.gui.widgets.label import Label |
3913 | +from entertainerlib.gui.widgets.scroll_area import ScrollArea |
3914 | +from entertainerlib.gui.widgets.texture import Texture |
3915 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
3916 | |
3917 | class Movie(Screen): |
3918 | '''Screen contains information of one movie.''' |
3919 | @@ -85,10 +84,14 @@ |
3920 | |
3921 | # Stars (rating) |
3922 | star = Texture(self.theme.getImage("star")) |
3923 | + star.hide() |
3924 | + self.add(star) |
3925 | star2 = Texture(self.theme.getImage("star2")) |
3926 | + star2.hide() |
3927 | + self.add(star2) |
3928 | |
3929 | for i in range(self.movie.get_rating()): |
3930 | - tex = clutter.CloneTexture(star) |
3931 | + tex = clutter.Clone(star) |
3932 | tex.set_position( |
3933 | self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * i), |
3934 | self.get_abs_y(0.17)) |
3935 | @@ -97,7 +100,7 @@ |
3936 | |
3937 | dark_star = 5 - self.movie.get_rating() |
3938 | for i in range(dark_star): |
3939 | - tex = clutter.CloneTexture(star2) |
3940 | + tex = clutter.Clone(star2) |
3941 | tex.set_position(self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * \ |
3942 | (i+ self.movie.get_rating())), self.get_abs_y(0.17)) |
3943 | tex.set_size(self.get_abs_x(0.024), self.get_abs_y(0.04)) |
3944 | |
3945 | === modified file 'entertainerlib/gui/screens/music.py' |
3946 | --- entertainerlib/frontend/gui/screens/music.py 2009-05-11 11:43:56 +0000 |
3947 | +++ entertainerlib/gui/screens/music.py 2010-01-23 03:34:11 +0000 |
3948 | @@ -1,12 +1,13 @@ |
3949 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
3950 | '''Music - Screen allows user to select music he/she wants to listen''' |
3951 | |
3952 | -from entertainerlib.frontend.gui.screens.screen import Screen |
3953 | -from entertainerlib.frontend.gui.tabs.albums_tab import AlbumsTab |
3954 | -from entertainerlib.frontend.gui.tabs.artists_tab import ArtistsTab |
3955 | -from entertainerlib.frontend.gui.widgets.label import Label |
3956 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
3957 | -from entertainerlib.frontend.medialibrary.playlist import Playlist |
3958 | +from entertainerlib.gui.screens.screen import Screen |
3959 | +from entertainerlib.gui.tabs.albums_tab import AlbumsTab |
3960 | +from entertainerlib.gui.tabs.artists_tab import ArtistsTab |
3961 | +from entertainerlib.gui.widgets.label import Label |
3962 | +from entertainerlib.gui.widgets.texture import Texture |
3963 | +from entertainerlib.client.medialibrary.playlist import Playlist |
3964 | + |
3965 | |
3966 | class Music(Screen): |
3967 | '''Screen that allows user to browse music library content.''' |
3968 | |
3969 | === modified file 'entertainerlib/gui/screens/photo.py' |
3970 | --- entertainerlib/frontend/gui/screens/photo.py 2009-05-21 16:12:09 +0000 |
3971 | +++ entertainerlib/gui/screens/photo.py 2010-01-23 03:34:11 +0000 |
3972 | @@ -4,9 +4,9 @@ |
3973 | import clutter |
3974 | import gobject |
3975 | |
3976 | -from entertainerlib.frontend.gui.screens.screen import Screen |
3977 | -from entertainerlib.frontend.gui.widgets.label import Label |
3978 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
3979 | +from entertainerlib.gui.screens.screen import Screen |
3980 | +from entertainerlib.gui.widgets.label import Label |
3981 | +from entertainerlib.gui.widgets.texture import Texture |
3982 | |
3983 | class Photo(Screen): |
3984 | '''Screen displays photograph in fullscreen and allows user to zoom in.''' |
3985 | @@ -18,8 +18,8 @@ |
3986 | def __init__(self, current_photo_index, images): |
3987 | Screen.__init__(self, 'Photo') |
3988 | |
3989 | - self.animate = self.config.show_effects() |
3990 | - self.slideshow_step = self.config.get_slideshow_step() |
3991 | + self.animate = self.config.show_effects |
3992 | + self.slideshow_step = self.config.slideshow_step |
3993 | |
3994 | self.zoom_level = 1 |
3995 | self.index = current_photo_index |
3996 | @@ -30,8 +30,8 @@ |
3997 | |
3998 | # Create black background |
3999 | self.background = clutter.Rectangle() |
4000 | - self.background.set_size(self.config.get_stage_width(), |
4001 | - self.config.get_stage_height()) |
4002 | + self.background.set_size( |
4003 | + self.config.stage_width, self.config.stage_height) |
4004 | self.background.set_color((0, 0, 0, 255)) |
4005 | self.add(self.background) |
4006 | |
4007 | @@ -60,9 +60,9 @@ |
4008 | self.texture = Texture(image.get_filename()) |
4009 | self._scale_image(self.texture) |
4010 | |
4011 | - timeline = clutter.Timeline(fps=120, duration=1000) |
4012 | - alpha = clutter.Alpha(timeline, clutter.smoothstep_inc_func) |
4013 | - self.opacity_behaviour = clutter.BehaviourOpacity(alpha=alpha, |
4014 | + timeline = clutter.Timeline(1000) |
4015 | + alpha = clutter.Alpha(timeline, clutter.EASE_IN_OUT_SINE) |
4016 | + self.opacity_behaviour = clutter.BehaviourOpacity(alpha=alpha, |
4017 | opacity_start=0, opacity_end=255) |
4018 | self.opacity_behaviour.apply(self.texture) |
4019 | self.texture.set_opacity(0) |
4020 | @@ -79,34 +79,33 @@ |
4021 | # Center position when zoomed |
4022 | width = texture.get_width() |
4023 | height = texture.get_height() |
4024 | - x_ratio = self.config.get_stage_width() / float(width) |
4025 | - y_ratio = self.config.get_stage_height() / float(height) |
4026 | + x_ratio = self.config.stage_width / float(width) |
4027 | + y_ratio = self.config.stage_height / float(height) |
4028 | |
4029 | if x_ratio > y_ratio: |
4030 | texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER) |
4031 | texture.set_scale( |
4032 | - self.config.get_stage_height() / float(height) * zoom_level, |
4033 | - self.config.get_stage_height() / float(height) * zoom_level) |
4034 | + self.config.stage_height / float(height) * zoom_level, |
4035 | + self.config.stage_height / float(height) * zoom_level) |
4036 | texture.set_anchor_point(0, 0) |
4037 | |
4038 | if zoom_level == 1: # Center image if in normal size |
4039 | new_width = int(width * |
4040 | - (self.config.get_stage_height() / float(height))) |
4041 | + (self.config.stage_height / float(height))) |
4042 | new_x = int( |
4043 | - (self.config.get_stage_width() - new_width) / float(2)) |
4044 | + (self.config.stage_width - new_width) / float(2)) |
4045 | texture.set_position(new_x, 0) |
4046 | else: |
4047 | texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER) |
4048 | texture.set_scale( |
4049 | - self.config.get_stage_width() / float(width) * zoom_level, |
4050 | - self.config.get_stage_width() / float(width) * zoom_level) |
4051 | + self.config.stage_width / float(width) * zoom_level, |
4052 | + self.config.stage_width / float(width) * zoom_level) |
4053 | texture.set_anchor_point(0, 0) |
4054 | |
4055 | if zoom_level == 1: # Center image if in normal size |
4056 | new_height = int( |
4057 | - height * (self.config.get_stage_width() / float(width))) |
4058 | - new_y = int( |
4059 | - (self.config.get_stage_height() - new_height) / float(2)) |
4060 | + height * (self.config.stage_width / float(width))) |
4061 | + new_y = int((self.config.stage_height - new_height) / float(2)) |
4062 | texture.set_position(0, new_y) |
4063 | |
4064 | def is_interested_in_play_action(self): |
4065 | |
4066 | === modified file 'entertainerlib/gui/screens/photo_albums.py' |
4067 | --- entertainerlib/frontend/gui/screens/photo_albums.py 2009-07-19 19:27:31 +0000 |
4068 | +++ entertainerlib/gui/screens/photo_albums.py 2010-01-23 03:34:11 +0000 |
4069 | @@ -6,13 +6,12 @@ |
4070 | import gobject |
4071 | import clutter |
4072 | |
4073 | -from entertainerlib.frontend.gui.screens.screen import Screen |
4074 | -from entertainerlib.frontend.gui.widgets.eyecandy_texture import ( |
4075 | - EyeCandyTexture) |
4076 | -from entertainerlib.frontend.gui.widgets.label import Label |
4077 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4078 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
4079 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
4080 | +from entertainerlib.gui.screens.screen import Screen |
4081 | +from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture |
4082 | +from entertainerlib.gui.widgets.label import Label |
4083 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
4084 | +from entertainerlib.gui.widgets.texture import Texture |
4085 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
4086 | |
4087 | class PhotoAlbums(Screen): |
4088 | '''Screen contains a list of photo albums and album previews.''' |
4089 | @@ -159,15 +158,15 @@ |
4090 | |
4091 | new = self._create_album_preview(album) |
4092 | |
4093 | - if self.config.show_effects(): |
4094 | + if self.config.show_effects: |
4095 | old = self.preview |
4096 | new.set_opacity(0) |
4097 | self.preview = new |
4098 | self.add(self.preview) |
4099 | |
4100 | #Fade out timeline |
4101 | - timeline1 = clutter.Timeline(10, 26) |
4102 | - alpha1 = clutter.Alpha(timeline1, clutter.smoothstep_inc_func) |
4103 | + timeline1 = clutter.Timeline(500) |
4104 | + alpha1 = clutter.Alpha(timeline1, clutter.EASE_IN_OUT_SINE) |
4105 | self.out_opacity = clutter.BehaviourOpacity(255, 0, alpha1) |
4106 | self.out_opacity.apply(old) |
4107 | |
4108 | @@ -175,8 +174,8 @@ |
4109 | old) |
4110 | |
4111 | # Fade in timeline |
4112 | - timeline2 = clutter.Timeline(10, 26) |
4113 | - alpha2 = clutter.Alpha(timeline2, clutter.smoothstep_inc_func) |
4114 | + timeline2 = clutter.Timeline(500) |
4115 | + alpha2 = clutter.Alpha(timeline2, clutter.EASE_IN_OUT_SINE) |
4116 | self.in_opacity = clutter.BehaviourOpacity(0, 255, alpha2) |
4117 | self.in_opacity.apply(new) |
4118 | |
4119 | @@ -211,16 +210,16 @@ |
4120 | """ |
4121 | if len(self.preview_textures)<=1: |
4122 | self.preview_textures[0].set_opacity(255) |
4123 | - elif self.config.show_effects(): |
4124 | + elif self.config.show_effects: |
4125 | #Fade out timeline |
4126 | - fade_out = clutter.Timeline(40, 26) |
4127 | - alpha_out = clutter.Alpha(fade_out, clutter.smoothstep_inc_func) |
4128 | + fade_out = clutter.Timeline(500) |
4129 | + alpha_out = clutter.Alpha(fade_out, clutter.EASE_IN_OUT_SINE) |
4130 | self.out_behaviour = clutter.BehaviourOpacity(255, 0, alpha_out) |
4131 | self.out_behaviour.apply(self.preview_textures[0]) |
4132 | |
4133 | # Fade in timeline |
4134 | - fade_in = clutter.Timeline(40, 26) |
4135 | - alpha_in = clutter.Alpha(fade_in, clutter.smoothstep_inc_func) |
4136 | + fade_in = clutter.Timeline(500) |
4137 | + alpha_in = clutter.Alpha(fade_in, clutter.EASE_IN_OUT_SINE) |
4138 | self.in_behaviour = clutter.BehaviourOpacity(0, 255, alpha_in) |
4139 | self.in_behaviour.apply(self.preview_textures[1]) |
4140 | |
4141 | |
4142 | === modified file 'entertainerlib/gui/screens/photographs.py' |
4143 | --- entertainerlib/frontend/gui/screens/photographs.py 2009-07-14 11:06:21 +0000 |
4144 | +++ entertainerlib/gui/screens/photographs.py 2010-01-23 03:34:11 +0000 |
4145 | @@ -3,13 +3,12 @@ |
4146 | |
4147 | import pango |
4148 | |
4149 | -from entertainerlib.frontend.gui.screens.screen import Screen |
4150 | -from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu |
4151 | -from entertainerlib.frontend.gui.widgets.label import Label |
4152 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4153 | -from entertainerlib.frontend.gui.widgets.loading_animation import ( |
4154 | - LoadingAnimation) |
4155 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
4156 | +from entertainerlib.gui.screens.screen import Screen |
4157 | +from entertainerlib.gui.widgets.image_menu import ImageMenu |
4158 | +from entertainerlib.gui.widgets.label import Label |
4159 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
4160 | +from entertainerlib.gui.widgets.loading_animation import LoadingAnimation |
4161 | +from entertainerlib.gui.widgets.texture import Texture |
4162 | |
4163 | class Photographs(Screen): |
4164 | '''Screen displays a grid of selectable photograph thumbnails.''' |
4165 | |
4166 | === modified file 'entertainerlib/gui/screens/question.py' |
4167 | --- entertainerlib/frontend/gui/screens/question.py 2009-06-27 09:03:33 +0000 |
4168 | +++ entertainerlib/gui/screens/question.py 2010-01-23 03:34:11 +0000 |
4169 | @@ -1,9 +1,9 @@ |
4170 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
4171 | '''Question - Screen displays a question label and specified buttons.''' |
4172 | |
4173 | -from entertainerlib.frontend.gui.screens.screen import Screen |
4174 | -from entertainerlib.frontend.gui.widgets.label import Label |
4175 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
4176 | +from entertainerlib.gui.screens.screen import Screen |
4177 | +from entertainerlib.gui.widgets.label import Label |
4178 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
4179 | |
4180 | class Question(Screen): |
4181 | '''Screen is displayed when the application needs to ask a close ended |
4182 | |
4183 | === modified file 'entertainerlib/gui/screens/rss.py' |
4184 | --- entertainerlib/frontend/gui/screens/rss.py 2009-07-19 19:27:31 +0000 |
4185 | +++ entertainerlib/gui/screens/rss.py 2010-01-23 03:34:11 +0000 |
4186 | @@ -3,12 +3,12 @@ |
4187 | |
4188 | import pango |
4189 | |
4190 | -from entertainerlib.frontend.gui.screens.screen import Screen |
4191 | -from entertainerlib.frontend.gui.widgets.label import Label |
4192 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
4193 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
4194 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4195 | -from entertainerlib.utils.feed_utils import FeedEntryParser |
4196 | +from entertainerlib.gui.screens.screen import Screen |
4197 | +from entertainerlib.gui.widgets.label import Label |
4198 | +from entertainerlib.gui.widgets.texture import Texture |
4199 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
4200 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
4201 | +from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser |
4202 | |
4203 | class Rss(Screen): |
4204 | '''Screen displays RSS-feed titles and number of entries.''' |
4205 | |
4206 | === modified file 'entertainerlib/gui/screens/screen.py' |
4207 | --- entertainerlib/frontend/gui/screens/screen.py 2009-05-28 20:15:47 +0000 |
4208 | +++ entertainerlib/gui/screens/screen.py 2010-01-23 03:34:11 +0000 |
4209 | @@ -6,9 +6,9 @@ |
4210 | import clutter |
4211 | |
4212 | from entertainerlib.exceptions import ScreenException |
4213 | -from entertainerlib.frontend.gui.user_event import UserEvent |
4214 | -from entertainerlib.frontend.gui.widgets.base import Base |
4215 | -from entertainerlib.frontend.gui.widgets.tab_group import TabGroup |
4216 | +from entertainerlib.gui.user_event import UserEvent |
4217 | +from entertainerlib.gui.widgets.base import Base |
4218 | +from entertainerlib.gui.widgets.tab_group import TabGroup |
4219 | |
4220 | class Screen(Base, clutter.Group): |
4221 | """ |
4222 | @@ -61,9 +61,7 @@ |
4223 | }) |
4224 | |
4225 | rect = clutter.Rectangle() |
4226 | - rect.set_size( |
4227 | - self.config.get_stage_width(), |
4228 | - self.config.get_stage_height()) |
4229 | + rect.set_size(self.config.stage_width, self.config.stage_height) |
4230 | rect.hide() |
4231 | self.add(rect) |
4232 | |
4233 | |
4234 | === modified file 'entertainerlib/gui/screens/tv_episodes.py' |
4235 | --- entertainerlib/frontend/gui/screens/tv_episodes.py 2009-07-21 22:22:53 +0000 |
4236 | +++ entertainerlib/gui/screens/tv_episodes.py 2010-01-23 03:34:11 +0000 |
4237 | @@ -4,13 +4,12 @@ |
4238 | import gtk |
4239 | import pango |
4240 | |
4241 | -from entertainerlib.frontend.gui.screens.screen import Screen |
4242 | -from entertainerlib.frontend.gui.widgets.eyecandy_texture import ( |
4243 | - EyeCandyTexture) |
4244 | -from entertainerlib.frontend.gui.widgets.label import Label |
4245 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4246 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
4247 | -from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea |
4248 | +from entertainerlib.gui.screens.screen import Screen |
4249 | +from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture |
4250 | +from entertainerlib.gui.widgets.label import Label |
4251 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
4252 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
4253 | +from entertainerlib.gui.widgets.scroll_area import ScrollArea |
4254 | |
4255 | class TvEpisodes(Screen): |
4256 | '''Screen contains list of all episodes of one specific season.''' |
4257 | @@ -101,6 +100,7 @@ |
4258 | self.menu.selected_userdata.get_title(), |
4259 | font_weight="bold") |
4260 | self.title.set_ellipsize(pango.ELLIPSIZE_END) |
4261 | + self.title.set_line_wrap(False) |
4262 | self.title.width = 0.4 |
4263 | self.add(self.title) |
4264 | |
4265 | |
4266 | === modified file 'entertainerlib/gui/screens/tv_series.py' |
4267 | --- entertainerlib/frontend/gui/screens/tv_series.py 2009-07-12 07:57:05 +0000 |
4268 | +++ entertainerlib/gui/screens/tv_series.py 2010-01-23 03:34:11 +0000 |
4269 | @@ -3,12 +3,11 @@ |
4270 | |
4271 | import gtk |
4272 | |
4273 | -from entertainerlib.frontend.gui.screens.screen import Screen |
4274 | -from entertainerlib.frontend.gui.widgets.eyecandy_texture import ( |
4275 | - EyeCandyTexture) |
4276 | -from entertainerlib.frontend.gui.widgets.label import Label |
4277 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4278 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
4279 | +from entertainerlib.gui.screens.screen import Screen |
4280 | +from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture |
4281 | +from entertainerlib.gui.widgets.label import Label |
4282 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
4283 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
4284 | |
4285 | class TvSeries(Screen): |
4286 | '''Screen that contains all seasons of one TV series.''' |
4287 | |
4288 | === modified file 'entertainerlib/gui/screens/video.py' |
4289 | --- entertainerlib/frontend/gui/screens/video.py 2009-05-06 03:40:22 +0000 |
4290 | +++ entertainerlib/gui/screens/video.py 2010-01-23 03:34:11 +0000 |
4291 | @@ -1,11 +1,12 @@ |
4292 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
4293 | '''Video - Screen allows user to browse video library content''' |
4294 | |
4295 | -from entertainerlib.frontend.gui.screens.screen import Screen |
4296 | -from entertainerlib.frontend.gui.tabs.movies_tab import MoviesTab |
4297 | -from entertainerlib.frontend.gui.tabs.series_tab import SeriesTab |
4298 | -from entertainerlib.frontend.gui.tabs.video_clips_tab import VideoClipsTab |
4299 | -from entertainerlib.frontend.gui.widgets.label import Label |
4300 | +from entertainerlib.gui.screens.screen import Screen |
4301 | +from entertainerlib.gui.tabs.movies_tab import MoviesTab |
4302 | +from entertainerlib.gui.tabs.series_tab import SeriesTab |
4303 | +from entertainerlib.gui.tabs.video_clips_tab import VideoClipsTab |
4304 | +from entertainerlib.gui.widgets.label import Label |
4305 | + |
4306 | |
4307 | class Video(Screen): |
4308 | '''Screen contains tabs for different video types in the video library.''' |
4309 | |
4310 | === modified file 'entertainerlib/gui/screens/video_osd.py' |
4311 | --- entertainerlib/frontend/gui/screens/video_osd.py 2009-05-25 11:07:55 +0000 |
4312 | +++ entertainerlib/gui/screens/video_osd.py 2010-01-23 03:34:11 +0000 |
4313 | @@ -4,10 +4,11 @@ |
4314 | import gobject |
4315 | import clutter |
4316 | |
4317 | -from entertainerlib.frontend.gui.screens.screen import Screen |
4318 | -from entertainerlib.frontend.gui.user_event import UserEvent |
4319 | -from entertainerlib.frontend.gui.widgets.progress_bar import ProgressBar |
4320 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
4321 | +from entertainerlib.gui.screens.screen import Screen |
4322 | +from entertainerlib.gui.user_event import UserEvent |
4323 | +from entertainerlib.gui.widgets.progress_bar import ProgressBar |
4324 | +from entertainerlib.gui.widgets.texture import Texture |
4325 | + |
4326 | |
4327 | class VideoOSD(Screen): |
4328 | '''Screen is displayed when video is being watched. |
4329 | @@ -54,23 +55,38 @@ |
4330 | '''Create the pause, seek-backward & seek-forward textures.''' |
4331 | self.pause_texture = Texture( |
4332 | self.theme.getImage("media-playback-pause"), 0.5, 0.5) |
4333 | - self.pause_texture.set_anchor_point_from_gravity( |
4334 | - clutter.GRAVITY_CENTER) |
4335 | + self.pause_texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER) |
4336 | self.pause_texture.hide() |
4337 | self.add(self.pause_texture) |
4338 | |
4339 | - pause_timeline = clutter.Timeline(fps=20, duration=2000) |
4340 | - pause_timeline.set_loop(True) |
4341 | - alpha_pause = clutter.Alpha(pause_timeline, clutter.sine_func) |
4342 | + pause_in_time = clutter.Timeline(1000) |
4343 | + in_alpha_pause = clutter.Alpha(pause_in_time, clutter.EASE_IN_OUT_SINE) |
4344 | |
4345 | - self.pause_opacity = clutter.BehaviourOpacity(alpha=alpha_pause, |
4346 | + self.pause_in_opacity = clutter.BehaviourOpacity(alpha=in_alpha_pause, |
4347 | opacity_start=100, opacity_end=255) |
4348 | - self.pause_scale = clutter.BehaviourScale(1.0, 1.0, 1.4, 1.4, |
4349 | - alpha_pause) |
4350 | - |
4351 | - self.pause_opacity.apply(self.pause_texture) |
4352 | - self.pause_scale.apply(self.pause_texture) |
4353 | - pause_timeline.start() |
4354 | + self.pause_in_scale = clutter.BehaviourScale(1.0, 1.0, 1.4, 1.4, |
4355 | + in_alpha_pause) |
4356 | + |
4357 | + self.pause_in_opacity.apply(self.pause_texture) |
4358 | + self.pause_in_scale.apply(self.pause_texture) |
4359 | + |
4360 | + pause_out_time = clutter.Timeline(1000) |
4361 | + out_alpha_pause = clutter.Alpha(pause_out_time, |
4362 | + clutter.EASE_IN_OUT_SINE) |
4363 | + |
4364 | + self.pause_out_opacity = clutter.BehaviourOpacity(alpha=out_alpha_pause, |
4365 | + opacity_start=255, opacity_end=100) |
4366 | + self.pause_out_scale = clutter.BehaviourScale(1.4, 1.4, 1.0, 1.0, |
4367 | + out_alpha_pause) |
4368 | + |
4369 | + self.pause_out_opacity.apply(self.pause_texture) |
4370 | + self.pause_out_scale.apply(self.pause_texture) |
4371 | + |
4372 | + self.score = clutter.Score() |
4373 | + self.score.set_loop(True) |
4374 | + self.score.append(timeline=pause_in_time) |
4375 | + self.score.append(timeline=pause_out_time, parent=pause_in_time) |
4376 | + self.score.start() |
4377 | |
4378 | self.seekbackward_texture = Texture( |
4379 | self.theme.getImage("media-seek-backward"), 0.1, 0.5) |
4380 | @@ -79,9 +95,9 @@ |
4381 | self.seekbackward_texture.set_opacity(0) |
4382 | self.add(self.seekbackward_texture) |
4383 | |
4384 | - self.seekbackward_timeline = clutter.Timeline(fps=120, duration=1000) |
4385 | + self.seekbackward_timeline = clutter.Timeline(1000) |
4386 | alpha_seekbackward = clutter.Alpha(self.seekbackward_timeline, |
4387 | - clutter.smoothstep_inc_func) |
4388 | + clutter.EASE_IN_OUT_SINE) |
4389 | |
4390 | self.seekbackward_opacity = clutter.BehaviourOpacity( |
4391 | alpha=alpha_seekbackward, opacity_start=255, opacity_end=0) |
4392 | @@ -94,9 +110,9 @@ |
4393 | self.seekforward_texture.set_opacity(0) |
4394 | self.add(self.seekforward_texture) |
4395 | |
4396 | - self.seekforward_timeline = clutter.Timeline(fps=120, duration=1000) |
4397 | + self.seekforward_timeline = clutter.Timeline(1000) |
4398 | alpha_seekforward = clutter.Alpha(self.seekforward_timeline, |
4399 | - clutter.smoothstep_inc_func) |
4400 | + clutter.EASE_IN_OUT_SINE) |
4401 | |
4402 | self.seekforward_opacity = clutter.BehaviourOpacity( |
4403 | alpha=alpha_seekforward, opacity_start=255, opacity_end=0) |
4404 | @@ -130,8 +146,8 @@ |
4405 | self.timeout_key = None # This is used when canceling timeouts |
4406 | for texture in self.aspect_textures: |
4407 | texture.position = ( |
4408 | - float(self.config.get_stage_width() - texture.get_width()) / |
4409 | - (self.config.get_stage_width() * 2), 0.67) |
4410 | + float(self.config.stage_width - texture.get_width()) / |
4411 | + (self.config.stage_width * 2), 0.67) |
4412 | |
4413 | def _hide_aspect_ratio_logo(self, number): |
4414 | ''' |
4415 | |
4416 | === modified file 'entertainerlib/gui/screens/weather.py' |
4417 | --- entertainerlib/frontend/gui/screens/weather.py 2009-07-12 19:10:32 +0000 |
4418 | +++ entertainerlib/gui/screens/weather.py 2010-01-23 03:34:11 +0000 |
4419 | @@ -3,10 +3,11 @@ |
4420 | |
4421 | import clutter |
4422 | |
4423 | -from entertainerlib.frontend.gui.screens.screen import Screen |
4424 | -from entertainerlib.frontend.gui.widgets.label import Label |
4425 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
4426 | -from entertainerlib.utils.weather import Weather |
4427 | +from entertainerlib.gui.screens.screen import Screen |
4428 | +from entertainerlib.gui.widgets.label import Label |
4429 | +from entertainerlib.gui.widgets.texture import Texture |
4430 | +from entertainerlib.weather import Weather |
4431 | + |
4432 | |
4433 | class WeatherScreen(Screen): |
4434 | '''Screen to display the user's set weather location.''' |
4435 | @@ -17,7 +18,7 @@ |
4436 | # Screen Title |
4437 | self.add(Label(0.13, "screentitle", 0, 0.87, _("Weather"))) |
4438 | |
4439 | - self.weather = Weather(self.config.get_weather_location()) |
4440 | + self.weather = Weather(self.config.weather_location) |
4441 | |
4442 | location = Label(0.04167, "text", 0.50, 0.13, self.weather.location, |
4443 | font_weight="bold") |
4444 | |
4445 | === renamed file 'entertainerlib/utils/system_tray_icon.py' => 'entertainerlib/gui/system_tray_icon.py' |
4446 | --- entertainerlib/utils/system_tray_icon.py 2009-05-06 02:58:08 +0000 |
4447 | +++ entertainerlib/gui/system_tray_icon.py 2010-01-23 03:34:11 +0000 |
4448 | @@ -4,19 +4,16 @@ |
4449 | import os |
4450 | |
4451 | import gtk |
4452 | -import gtk.glade |
4453 | - |
4454 | -from entertainerlib.utils.log_viewer import LogViewer |
4455 | -from entertainerlib.utils.preferences_dialog import PreferencesDialog |
4456 | -from entertainerlib.utils.content_management_dialog import ( |
4457 | - ContentManagementDialog) |
4458 | -from entertainerlib.utils.configuration import Configuration |
4459 | + |
4460 | +from entertainerlib.configuration import Configuration |
4461 | +from entertainerlib.dialog import ManagerDialog, LogViewer |
4462 | + |
4463 | |
4464 | class SystemTrayIcon: |
4465 | """Implements system tray icon for entertainer.""" |
4466 | |
4467 | FILE_DIR = os.path.dirname(__file__) |
4468 | - GLADE_DIR = os.path.join(FILE_DIR, "glade") |
4469 | + UI_DIR = os.path.join(FILE_DIR, '..', 'uis') |
4470 | |
4471 | def __init__(self, quit_callback, toggle_interface_visibility_callback): |
4472 | '''Create the system tray icon and pop-up menu for it.''' |
4473 | @@ -32,24 +29,24 @@ |
4474 | self.icon_widget = gtk.StatusIcon() |
4475 | self.icon_widget.set_tooltip(_("Entertainer Server")) |
4476 | |
4477 | - # Load glade files |
4478 | - self.menu_widgets = gtk.glade.XML( |
4479 | - os.path.join(self.GLADE_DIR, "system_tray_icon_menu.glade")) |
4480 | + # Load UI with gtk.Builder |
4481 | + uifile = os.path.join(self.UI_DIR, 'system_tray_icon_menu.ui') |
4482 | + self.menu_widgets = gtk.Builder() |
4483 | + self.menu_widgets.set_translation_domain('entertainer') |
4484 | + self.menu_widgets.add_from_file(uifile) |
4485 | |
4486 | # Bind menu signals |
4487 | - callback_dic = {"on_menuitem_frontend_activate" |
4488 | - : self.on_menuitem_frontend_activate, |
4489 | - "on_menuitem_preferences_activate" |
4490 | - : self.on_menuitem_preferences_activate, |
4491 | - "on_menuitem_content_management_activate" |
4492 | - : self.on_menuitem_content_management_activate, |
4493 | + callback_dic = {"on_menuitem_client_activate" |
4494 | + : self.on_menuitem_client_activate, |
4495 | + "on_menuitem_manager_activate" |
4496 | + : self.on_menuitem_manager_activate, |
4497 | "on_menuitem_log_viewer_activate" |
4498 | : self.on_menuitem_log_viewer_activate, |
4499 | "on_menuitem_quit_activate" |
4500 | : self.on_menuitem_quit_activate |
4501 | } |
4502 | - self.menu_widgets.signal_autoconnect(callback_dic) |
4503 | - self.popup = self.menu_widgets.get_widget("SystemTrayIconMenu") |
4504 | + self.menu_widgets.connect_signals(callback_dic) |
4505 | + self.popup = self.menu_widgets.get_object("SystemTrayIconMenu") |
4506 | |
4507 | # Check if running from a branch to set the tray icon |
4508 | if (os.path.exists(self.tray_icon_url)): |
4509 | @@ -62,7 +59,7 @@ |
4510 | self.icon_widget.connect('popup-menu', self.open_popup_menu) |
4511 | |
4512 | def systray_icon_activated(self, widget, data= None): |
4513 | - """Switch visibility of frontend when system tray icon is clicked""" |
4514 | + """Switch visibility of client when system tray icon is clicked""" |
4515 | self.toggle_interface_visibility_callback() |
4516 | |
4517 | def open_popup_menu(self, widget, button, time, data = None): |
4518 | @@ -70,17 +67,13 @@ |
4519 | self.popup.show_all() |
4520 | self.popup.popup(None, None, None, 3, time) |
4521 | |
4522 | - def on_menuitem_frontend_activate(self, widget): |
4523 | - """Execute frontend here if not running. Show if running""" |
4524 | - self.set_frontend_visible(True) |
4525 | - |
4526 | - def on_menuitem_preferences_activate(self, widget): |
4527 | - """Executes preferences-tool.""" |
4528 | - PreferencesDialog(False) |
4529 | - |
4530 | - def on_menuitem_content_management_activate(self, widget): |
4531 | - """Executes content-management-tool""" |
4532 | - ContentManagementDialog(False) |
4533 | + def on_menuitem_client_activate(self, widget): |
4534 | + """Execute client here if not running. Show if running""" |
4535 | + self.set_client_visible(True) |
4536 | + |
4537 | + def on_menuitem_manager_activate(self, widget): |
4538 | + '''Executes the manager dialog.''' |
4539 | + ManagerDialog(False) |
4540 | |
4541 | def on_menuitem_log_viewer_activate(self, widget): |
4542 | """Display log viewer dialog""" |
4543 | |
4544 | === modified file 'entertainerlib/gui/tabs/albums_tab.py' |
4545 | --- entertainerlib/frontend/gui/tabs/albums_tab.py 2009-07-14 11:06:21 +0000 |
4546 | +++ entertainerlib/gui/tabs/albums_tab.py 2010-01-23 03:34:11 +0000 |
4547 | @@ -4,12 +4,11 @@ |
4548 | |
4549 | import pango |
4550 | |
4551 | -from entertainerlib.frontend.gui.tabs.tab import Tab |
4552 | -from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu |
4553 | -from entertainerlib.frontend.gui.widgets.label import Label |
4554 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4555 | -from entertainerlib.frontend.gui.widgets.loading_animation import ( |
4556 | - LoadingAnimation) |
4557 | +from entertainerlib.gui.tabs.tab import Tab |
4558 | +from entertainerlib.gui.widgets.image_menu import ImageMenu |
4559 | +from entertainerlib.gui.widgets.label import Label |
4560 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
4561 | +from entertainerlib.gui.widgets.loading_animation import LoadingAnimation |
4562 | |
4563 | class AlbumsTab(Tab): |
4564 | '''Tab to show album listings''' |
4565 | @@ -55,11 +54,13 @@ |
4566 | # Create album information (displays current menuitem information) |
4567 | self.album_title = Label(0.045, "title", 0.22, 0.79, "") |
4568 | self.album_title.set_ellipsize(pango.ELLIPSIZE_END) |
4569 | + self.album_title.set_line_wrap(False) |
4570 | self.album_title.width = 0.366 |
4571 | self.add(self.album_title) |
4572 | |
4573 | self.album_artist = Label(0.037, "subtitle", 0.22, 0.86, "") |
4574 | self.album_artist.set_ellipsize(pango.ELLIPSIZE_END) |
4575 | + self.album_artist.set_line_wrap(False) |
4576 | self.album_artist.width = 0.366 |
4577 | self.add(self.album_artist) |
4578 | |
4579 | |
4580 | === modified file 'entertainerlib/gui/tabs/artists_tab.py' |
4581 | --- entertainerlib/frontend/gui/tabs/artists_tab.py 2009-07-16 20:31:22 +0000 |
4582 | +++ entertainerlib/gui/tabs/artists_tab.py 2010-01-23 03:34:11 +0000 |
4583 | @@ -4,12 +4,11 @@ |
4584 | |
4585 | import pango |
4586 | |
4587 | -from entertainerlib.frontend.gui.tabs.tab import Tab |
4588 | -from entertainerlib.frontend.gui.widgets.label import Label |
4589 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4590 | -from entertainerlib.frontend.gui.widgets.loading_animation import ( |
4591 | - LoadingAnimation) |
4592 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
4593 | +from entertainerlib.gui.tabs.tab import Tab |
4594 | +from entertainerlib.gui.widgets.label import Label |
4595 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
4596 | +from entertainerlib.gui.widgets.loading_animation import LoadingAnimation |
4597 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
4598 | |
4599 | class ArtistsTab(Tab): |
4600 | '''Tab for the music screen to show artist listings''' |
4601 | @@ -38,6 +37,7 @@ |
4602 | # Create artist label |
4603 | self.artist_title = Label(0.0416, "title", 0.22, 0.794, "") |
4604 | self.artist_title.set_ellipsize(pango.ELLIPSIZE_END) |
4605 | + self.artist_title.set_line_wrap(False) |
4606 | self.artist_title.width = 0.366 |
4607 | self.add(self.artist_title) |
4608 | |
4609 | @@ -47,7 +47,7 @@ |
4610 | self.artist_tracks = Label(0.0365, "subtitle", 0.22, 0.911, "") |
4611 | self.add(self.artist_tracks) |
4612 | |
4613 | - # Create artis menu list indicator |
4614 | + # Create artist menu list indicator |
4615 | self.li = ListIndicator(0.77, 0.8, 0.18, 0.045, |
4616 | ListIndicator.VERTICAL) |
4617 | self.li.set_maximum(len(artists)) |
4618 | |
4619 | === modified file 'entertainerlib/gui/tabs/lyrics_tab.py' |
4620 | --- entertainerlib/frontend/gui/tabs/lyrics_tab.py 2009-07-22 20:46:29 +0000 |
4621 | +++ entertainerlib/gui/tabs/lyrics_tab.py 2010-01-23 03:34:11 +0000 |
4622 | @@ -4,11 +4,11 @@ |
4623 | import clutter |
4624 | import pango |
4625 | |
4626 | -from entertainerlib.frontend.gui.tabs.tab import Tab |
4627 | -from entertainerlib.frontend.gui.widgets.label import Label |
4628 | -from entertainerlib.frontend.gui.widgets.loading_animation import ( |
4629 | +from entertainerlib.gui.tabs.tab import Tab |
4630 | +from entertainerlib.gui.widgets.label import Label |
4631 | +from entertainerlib.gui.widgets.loading_animation import ( |
4632 | LoadingAnimation) |
4633 | -from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea |
4634 | +from entertainerlib.gui.widgets.scroll_area import ScrollArea |
4635 | |
4636 | class LyricsTab(Tab): |
4637 | '''Tab for the audio play screen to show lyrics''' |
4638 | |
4639 | === modified file 'entertainerlib/gui/tabs/movies_tab.py' |
4640 | --- entertainerlib/frontend/gui/tabs/movies_tab.py 2009-07-14 11:06:21 +0000 |
4641 | +++ entertainerlib/gui/tabs/movies_tab.py 2010-01-23 03:34:11 +0000 |
4642 | @@ -3,12 +3,11 @@ |
4643 | |
4644 | import pango |
4645 | |
4646 | -from entertainerlib.frontend.gui.tabs.tab import Tab |
4647 | -from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu |
4648 | -from entertainerlib.frontend.gui.widgets.label import Label |
4649 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4650 | -from entertainerlib.frontend.gui.widgets.loading_animation import ( |
4651 | - LoadingAnimation) |
4652 | +from entertainerlib.gui.tabs.tab import Tab |
4653 | +from entertainerlib.gui.widgets.image_menu import ImageMenu |
4654 | +from entertainerlib.gui.widgets.label import Label |
4655 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
4656 | +from entertainerlib.gui.widgets.loading_animation import LoadingAnimation |
4657 | |
4658 | class MoviesTab(Tab): |
4659 | """ |
4660 | @@ -95,11 +94,13 @@ |
4661 | self.movie_title = Label(0.042, "title", 0.2, 0.75, "", |
4662 | font_weight="bold") |
4663 | self.movie_title.set_ellipsize(pango.ELLIPSIZE_END) |
4664 | + self.movie_title.set_line_wrap(False) |
4665 | self.movie_title.width = 0.5 |
4666 | self.add(self.movie_title) |
4667 | |
4668 | self.movie_info = Label(0.034, "subtitle", 0.2, 0.8, "") |
4669 | self.movie_info.set_ellipsize(pango.ELLIPSIZE_END) |
4670 | + self.movie_info.set_line_wrap(False) |
4671 | self.movie_info.width = 0.5 |
4672 | self.add(self.movie_info) |
4673 | |
4674 | |
4675 | === modified file 'entertainerlib/gui/tabs/playing_tab.py' |
4676 | --- entertainerlib/frontend/gui/tabs/playing_tab.py 2009-05-26 17:32:56 +0000 |
4677 | +++ entertainerlib/gui/tabs/playing_tab.py 2010-01-23 03:34:11 +0000 |
4678 | @@ -3,9 +3,9 @@ |
4679 | |
4680 | import pango |
4681 | |
4682 | -from entertainerlib.frontend.gui.tabs.tab import Tab |
4683 | -from entertainerlib.frontend.gui.widgets.label import Label |
4684 | -from entertainerlib.frontend.gui.widgets.progress_bar import ProgressBar |
4685 | +from entertainerlib.gui.tabs.tab import Tab |
4686 | +from entertainerlib.gui.widgets.label import Label |
4687 | +from entertainerlib.gui.widgets.progress_bar import ProgressBar |
4688 | |
4689 | class PlayingTab(Tab): |
4690 | '''Tab for the audio play screen to show currently playing audio.''' |
4691 | |
4692 | === modified file 'entertainerlib/gui/tabs/series_tab.py' |
4693 | --- entertainerlib/frontend/gui/tabs/series_tab.py 2009-07-14 11:06:21 +0000 |
4694 | +++ entertainerlib/gui/tabs/series_tab.py 2010-01-23 03:34:11 +0000 |
4695 | @@ -3,12 +3,11 @@ |
4696 | |
4697 | import pango |
4698 | |
4699 | -from entertainerlib.frontend.gui.tabs.tab import Tab |
4700 | -from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu |
4701 | -from entertainerlib.frontend.gui.widgets.label import Label |
4702 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4703 | -from entertainerlib.frontend.gui.widgets.loading_animation import ( |
4704 | - LoadingAnimation) |
4705 | +from entertainerlib.gui.tabs.tab import Tab |
4706 | +from entertainerlib.gui.widgets.image_menu import ImageMenu |
4707 | +from entertainerlib.gui.widgets.label import Label |
4708 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
4709 | +from entertainerlib.gui.widgets.loading_animation import LoadingAnimation |
4710 | |
4711 | class SeriesTab(Tab): |
4712 | """ |
4713 | @@ -94,6 +93,7 @@ |
4714 | self.series_title = Label(0.042, "title", 0.2, 0.75, "", |
4715 | font_weight="bold") |
4716 | self.series_title.set_ellipsize(pango.ELLIPSIZE_END) |
4717 | + self.series_title.set_line_wrap(False) |
4718 | self.series_title.width = 0.5 |
4719 | self.add(self.series_title) |
4720 | |
4721 | |
4722 | === modified file 'entertainerlib/gui/tabs/tab.py' |
4723 | --- entertainerlib/frontend/gui/tabs/tab.py 2009-05-21 16:12:09 +0000 |
4724 | +++ entertainerlib/gui/tabs/tab.py 2010-01-23 03:34:11 +0000 |
4725 | @@ -4,10 +4,10 @@ |
4726 | import clutter |
4727 | import gobject |
4728 | |
4729 | -from entertainerlib.frontend.gui.user_event import UserEvent |
4730 | -from entertainerlib.frontend.gui.widgets.base import Base |
4731 | -from entertainerlib.frontend.gui.widgets.label import Label |
4732 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
4733 | +from entertainerlib.gui.user_event import UserEvent |
4734 | +from entertainerlib.gui.widgets.base import Base |
4735 | +from entertainerlib.gui.widgets.label import Label |
4736 | +from entertainerlib.gui.widgets.texture import Texture |
4737 | |
4738 | class Tab(Base, clutter.Group): |
4739 | ''' |
4740 | @@ -39,9 +39,9 @@ |
4741 | } |
4742 | |
4743 | # show/hide animation on the Tab |
4744 | - self.timeline = clutter.Timeline(30, 60) |
4745 | + self.timeline = clutter.Timeline(500) |
4746 | self.timeline.connect('completed', self._on_timeline_completed) |
4747 | - self.alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func) |
4748 | + self.alpha = clutter.Alpha(self.timeline, clutter.EASE_IN_OUT_SINE) |
4749 | self.behaviour = clutter.BehaviourOpacity(0, 255, self.alpha) |
4750 | self.behaviour.apply(self) |
4751 | |
4752 | |
4753 | === modified file 'entertainerlib/gui/tabs/tracks_tab.py' |
4754 | --- entertainerlib/frontend/gui/tabs/tracks_tab.py 2009-07-16 20:31:22 +0000 |
4755 | +++ entertainerlib/gui/tabs/tracks_tab.py 2010-01-23 03:34:11 +0000 |
4756 | @@ -3,12 +3,11 @@ |
4757 | |
4758 | import pango |
4759 | |
4760 | -from entertainerlib.frontend.gui.tabs.tab import Tab |
4761 | -from entertainerlib.frontend.gui.widgets.label import Label |
4762 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4763 | -from entertainerlib.frontend.gui.widgets.loading_animation import ( |
4764 | - LoadingAnimation) |
4765 | -from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
4766 | +from entertainerlib.gui.tabs.tab import Tab |
4767 | +from entertainerlib.gui.widgets.label import Label |
4768 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
4769 | +from entertainerlib.gui.widgets.loading_animation import LoadingAnimation |
4770 | +from entertainerlib.gui.widgets.text_menu import TextMenu |
4771 | |
4772 | class TracksTab(Tab): |
4773 | '''Tab for the artist screen to show track listings''' |
4774 | @@ -35,11 +34,13 @@ |
4775 | |
4776 | self.track_title = Label(0.045, "title", 0.22, 0.79, "") |
4777 | self.track_title.set_ellipsize(pango.ELLIPSIZE_END) |
4778 | + self.track_title.set_line_wrap(False) |
4779 | self.track_title.width = 0.366 |
4780 | self.add(self.track_title) |
4781 | |
4782 | self.track_number = Label(0.037, "subtitle", 0.22, 0.86, "") |
4783 | self.track_number.set_ellipsize(pango.ELLIPSIZE_END) |
4784 | + self.track_number.set_line_wrap(False) |
4785 | self.track_number.width = 0.366 |
4786 | self.add(self.track_number) |
4787 | |
4788 | |
4789 | === modified file 'entertainerlib/gui/tabs/video_clips_tab.py' |
4790 | --- entertainerlib/frontend/gui/tabs/video_clips_tab.py 2009-07-14 11:06:21 +0000 |
4791 | +++ entertainerlib/gui/tabs/video_clips_tab.py 2010-01-23 03:34:11 +0000 |
4792 | @@ -5,12 +5,11 @@ |
4793 | |
4794 | import pango |
4795 | |
4796 | -from entertainerlib.frontend.gui.tabs.tab import Tab |
4797 | -from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu |
4798 | -from entertainerlib.frontend.gui.widgets.label import Label |
4799 | -from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4800 | -from entertainerlib.frontend.gui.widgets.loading_animation import ( |
4801 | - LoadingAnimation) |
4802 | +from entertainerlib.gui.tabs.tab import Tab |
4803 | +from entertainerlib.gui.widgets.image_menu import ImageMenu |
4804 | +from entertainerlib.gui.widgets.label import Label |
4805 | +from entertainerlib.gui.widgets.list_indicator import ListIndicator |
4806 | +from entertainerlib.gui.widgets.loading_animation import LoadingAnimation |
4807 | |
4808 | class VideoClipsTab(Tab): |
4809 | """ |
4810 | @@ -94,11 +93,13 @@ |
4811 | self.clip_title = Label(0.042, "title", 0.15, 0.77, "", |
4812 | font_weight="bold") |
4813 | self.clip_title.set_ellipsize(pango.ELLIPSIZE_END) |
4814 | + self.clip_title.set_line_wrap(False) |
4815 | self.clip_title.width = 0.5 |
4816 | self.add(self.clip_title) |
4817 | |
4818 | self.clip_info = Label(0.034, "subtitle", 0.15, 0.85, "") |
4819 | self.clip_info.set_ellipsize(pango.ELLIPSIZE_END) |
4820 | + self.clip_info.set_line_wrap(False) |
4821 | self.clip_info.width = 0.5 |
4822 | self.add(self.clip_info) |
4823 | |
4824 | |
4825 | === renamed file 'entertainerlib/utils/theme.py' => 'entertainerlib/gui/theme.py' |
4826 | === modified file 'entertainerlib/gui/transitions/factory.py' |
4827 | --- entertainerlib/frontend/gui/transitions/factory.py 2009-04-28 22:30:06 +0000 |
4828 | +++ entertainerlib/gui/transitions/factory.py 2010-01-23 03:34:11 +0000 |
4829 | @@ -2,11 +2,12 @@ |
4830 | '''TransitionFactory - create a transition object based on the configuration |
4831 | that is in the user's preferences''' |
4832 | |
4833 | -from entertainerlib.frontend.gui.transitions.fade import Fade |
4834 | -from entertainerlib.frontend.gui.transitions.no_effect import NoEffect |
4835 | -from entertainerlib.frontend.gui.transitions.slide import Slide |
4836 | -from entertainerlib.frontend.gui.transitions.zoom_and_fade import ZoomAndFade |
4837 | -from entertainerlib.utils.configuration import Configuration |
4838 | +from entertainerlib.gui.transitions.fade import Fade |
4839 | +from entertainerlib.gui.transitions.no_effect import NoEffect |
4840 | +from entertainerlib.gui.transitions.slide import Slide |
4841 | +from entertainerlib.gui.transitions.zoom_and_fade import ZoomAndFade |
4842 | +from entertainerlib.configuration import Configuration |
4843 | + |
4844 | |
4845 | class TransitionFactory: |
4846 | '''Generates a transition object based on the configuration setting''' |
4847 | @@ -31,8 +32,8 @@ |
4848 | 'Zoom and fade' : self._generate_zoom_and_fade |
4849 | } |
4850 | |
4851 | - if self.config.show_effects(): |
4852 | - kind = self.config.transition_effect() |
4853 | + if self.config.show_effects: |
4854 | + kind = self.config.transition_effect |
4855 | else: |
4856 | kind = "No effect" |
4857 | |
4858 | |
4859 | === modified file 'entertainerlib/gui/transitions/fade.py' |
4860 | --- entertainerlib/frontend/gui/transitions/fade.py 2009-05-06 03:40:22 +0000 |
4861 | +++ entertainerlib/gui/transitions/fade.py 2010-01-23 03:34:11 +0000 |
4862 | @@ -2,8 +2,8 @@ |
4863 | '''Fade - Transition to fade out old screen and fade in the new screen''' |
4864 | |
4865 | import clutter |
4866 | +from entertainerlib.gui.transitions.transition import Transition |
4867 | |
4868 | -from entertainerlib.frontend.gui.transitions.transition import Transition |
4869 | |
4870 | class Fade(Transition): |
4871 | """ |
4872 | |
4873 | === modified file 'entertainerlib/gui/transitions/no_effect.py' |
4874 | --- entertainerlib/frontend/gui/transitions/no_effect.py 2009-05-06 03:40:22 +0000 |
4875 | +++ entertainerlib/gui/transitions/no_effect.py 2010-01-23 03:34:11 +0000 |
4876 | @@ -1,7 +1,8 @@ |
4877 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
4878 | '''NoEffect - Simple change transition from screen 1 to screen 2''' |
4879 | |
4880 | -from entertainerlib.frontend.gui.transitions.transition import Transition |
4881 | +from entertainerlib.gui.transitions.transition import Transition |
4882 | + |
4883 | |
4884 | class NoEffect(Transition): |
4885 | """ |
4886 | |
4887 | === modified file 'entertainerlib/gui/transitions/slide.py' |
4888 | --- entertainerlib/frontend/gui/transitions/slide.py 2009-05-06 03:40:22 +0000 |
4889 | +++ entertainerlib/gui/transitions/slide.py 2010-01-23 03:34:11 +0000 |
4890 | @@ -3,8 +3,8 @@ |
4891 | |
4892 | import clutter |
4893 | |
4894 | -from entertainerlib.frontend.gui.transitions.transition import Transition |
4895 | -from entertainerlib.utils.configuration import Configuration |
4896 | +from entertainerlib.gui.transitions.transition import Transition |
4897 | +from entertainerlib.configuration import Configuration |
4898 | |
4899 | class Slide(Transition): |
4900 | '''Move screens on and off the stage through horizontal movements.''' |
4901 | @@ -27,7 +27,7 @@ |
4902 | '''Execute the common sliding logic needed by each direction. |
4903 | |
4904 | Direction will set the sign of width to determine which way to slide.''' |
4905 | - width = self.config.get_stage_width() * direction |
4906 | + width = self.config.stage_width * direction |
4907 | |
4908 | # Initialize to_screen for animation |
4909 | to_screen.set_position(-width, 0) |
4910 | @@ -35,10 +35,12 @@ |
4911 | |
4912 | # Slide out timeline |
4913 | if from_screen is not None: |
4914 | - slide_out = clutter.Timeline(20, 60) |
4915 | - alpha_out = clutter.Alpha(slide_out, clutter.smoothstep_inc_func) |
4916 | - self.out_behaviour = clutter.BehaviourPath(alpha_out, ((0, 0), |
4917 | - (width, 0))) |
4918 | + slide_out = clutter.Timeline(500) |
4919 | + alpha_out = clutter.Alpha(slide_out, clutter.EASE_IN_OUT_SINE) |
4920 | + out_path = clutter.Path() |
4921 | + out_path.add_move_to(0, 0) |
4922 | + out_path.add_line_to(width, 0) |
4923 | + self.out_behaviour = clutter.BehaviourPath(alpha_out, out_path) |
4924 | self.out_behaviour.apply(from_screen) |
4925 | |
4926 | if remove_screen: |
4927 | @@ -46,10 +48,12 @@ |
4928 | from_screen) |
4929 | |
4930 | # Slide in timeline |
4931 | - slide_in = clutter.Timeline(20, 60) |
4932 | - alpha_in = clutter.Alpha(slide_in, clutter.smoothstep_inc_func) |
4933 | - self.in_behaviour = clutter.BehaviourPath(alpha_in, ((-width, 0), |
4934 | - (0, 0))) |
4935 | + slide_in = clutter.Timeline(500) |
4936 | + alpha_in = clutter.Alpha(slide_in, clutter.EASE_IN_OUT_SINE) |
4937 | + in_path = clutter.Path() |
4938 | + in_path.add_move_to(-width, 0) |
4939 | + in_path.add_line_to(0, 0) |
4940 | + self.in_behaviour = clutter.BehaviourPath(alpha_in, in_path) |
4941 | self.in_behaviour.apply(to_screen) |
4942 | |
4943 | # Start transition animating |
4944 | |
4945 | === modified file 'entertainerlib/gui/transitions/zoom_and_fade.py' |
4946 | --- entertainerlib/frontend/gui/transitions/zoom_and_fade.py 2009-05-06 03:40:22 +0000 |
4947 | +++ entertainerlib/gui/transitions/zoom_and_fade.py 2010-01-23 03:34:11 +0000 |
4948 | @@ -2,8 +2,8 @@ |
4949 | '''ZoomAndFade - Transition to zoom and fade between screens''' |
4950 | |
4951 | import clutter |
4952 | +from entertainerlib.gui.transitions.transition import Transition |
4953 | |
4954 | -from entertainerlib.frontend.gui.transitions.transition import Transition |
4955 | |
4956 | class ZoomAndFade(Transition): |
4957 | """ |
4958 | |
4959 | === modified file 'entertainerlib/gui/user_event.py' |
4960 | --- entertainerlib/frontend/gui/user_event.py 2009-05-29 10:33:32 +0000 |
4961 | +++ entertainerlib/gui/user_event.py 2010-01-23 03:34:11 +0000 |
4962 | @@ -40,7 +40,7 @@ |
4963 | USE_ASPECT_RATIO_2 = 52 |
4964 | USE_ASPECT_RATIO_3 = 53 |
4965 | USE_ASPECT_RATIO_4 = 54 |
4966 | - QUIT_FRONTEND = 56 |
4967 | + QUIT = 56 |
4968 | DEFAULT_EVENT = 57 |
4969 | |
4970 | def __init__(self, code): |
4971 | |
4972 | === modified file 'entertainerlib/gui/user_interface.py' |
4973 | --- entertainerlib/frontend/gui/user_interface.py 2009-06-29 19:41:35 +0000 |
4974 | +++ entertainerlib/gui/user_interface.py 2010-01-23 03:34:11 +0000 |
4975 | @@ -1,5 +1,5 @@ |
4976 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
4977 | -'''UserInterface - Main window of the Entertainer frontend''' |
4978 | +'''UserInterface - Main window of the Entertainer client''' |
4979 | |
4980 | # Clutter uses _1 to represent the 1 key and pylint complains about it |
4981 | # pylint: disable-msg=W0212 |
4982 | @@ -12,31 +12,32 @@ |
4983 | import gobject |
4984 | import gtk |
4985 | |
4986 | -from entertainerlib.frontend.gui.screen_history import ScreenHistory |
4987 | -from entertainerlib.frontend.gui.screens.factory import ScreenFactory |
4988 | -from entertainerlib.frontend.gui.screens.screen import Screen |
4989 | -from entertainerlib.frontend.gui.transitions.factory import TransitionFactory |
4990 | -from entertainerlib.frontend.gui.transitions.transition import Transition |
4991 | -from entertainerlib.frontend.gui.user_event import UserEvent |
4992 | -from entertainerlib.frontend.gui.widgets.menu_overlay import MenuOverlay |
4993 | -from entertainerlib.frontend.media_player import MediaPlayer |
4994 | -from entertainerlib.frontend.gui.widgets.volume_indicator import VolumeIndicator |
4995 | -from entertainerlib.utils.configuration import Configuration |
4996 | -from entertainerlib.utils.logger import Logger |
4997 | +from entertainerlib.gui.widgets.volume_indicator import VolumeIndicator |
4998 | +from entertainerlib.gui.screen_history import ScreenHistory |
4999 | +from entertainerlib.gui.screens.factory import ScreenFactory |
5000 | +from entertainerlib.gui.screens.screen import Screen |
The diff has been truncated for viewing.
So... stuff is broken. Very broken.
Trunk is broken for Karmic users and so is future. However, we're doing all of our work on future. This would be good *except* we lose the benefit of Tarmac landing our branches.
The whole reason we started using Tarmac was to avoid the problem of a human gatekeeper who may get too busy to merge code. Since we are now only using future, we totally lose what Tarmac was doing for us so there is code that has been idly sitting approved. I think we can all agree that unmerged *approved* code that is not merged is a delta that is a bad thing.
Therefore, I'm requesting we merge future into trunk, primarily for the sole benefit of not being blocked by human gatekeeper so we can make forward progress again (no offense Paul, I realize you're really busy).
I would love to use future again in the future, but I think it suffers from the human gatekeeper problem and should be configured to use Tarmac if possible.
I'm making this request at the jeopardy of the stability of trunk just because of how broken things are anyway. At this point, I think future is much better code than trunk.
-Matt
P.S. Let me try filing this against the correct branch this time. :P