Merge lp:entertainer/future into lp:entertainer

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
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.

To post a comment you must log in.
Revision history for this message
Matt Layman (mblayman) wrote :

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

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
=== modified file 'MANIFEST.in'
--- MANIFEST.in 2008-10-05 17:11:31 +0000
+++ MANIFEST.in 2010-01-23 03:34:10 +0000
@@ -1,3 +1,10 @@
1include entertainer*1include entertainer*
2include docs/COPYING docs/DEPENDENCIES
3include docs/entertainer.1 docs/entertainer.desktop
2recursive-include cfg *4recursive-include cfg *
5recursive-include entertainerlib *
6recursive-include icons *
7recursive-include themes *
8recursive-include tools *
9global-exclude *pyc
310
411
=== added file 'README'
--- README 1970-01-01 00:00:00 +0000
+++ README 2010-01-23 03:34:10 +0000
@@ -0,0 +1,6 @@
1To install Entertainer, run:
2
3python setup.py install
4
5For information on how to contribute, read docs/HACKING.
6
07
=== modified file 'cfg/content.conf'
--- cfg/content.conf 2009-05-31 17:11:16 +0000
+++ cfg/content.conf 2010-01-23 03:34:11 +0000
@@ -1,26 +1,34 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2[Images]2[Media]
3folders = 3download_lyrics = False
4display_hidden_files_folders = False4download_album_art = True
5download_metadata = True
6display_eject_in_menu = False
7folders =
58
6[Weather]9[Weather]
7location = Bath,England10location = Bath,England
8display_in_menu = True11display_in_menu = True
9metric_units = True
10
11[Music]
12folders =
13download_lyrics = False
14download_album_art = True
15
16[Videos]
17folders =
18download_metadata = True
1912
20[RSS]13[RSS]
21feeds = http://theironlion.net/feeds/blog;http://www.joshuascotton.com/main/archives/tag/entertainer/feed;http://laymanstermsdev.wordpress.com/feed14feeds = http://theironlion.net/blog/feed;http://www.joshuascotton.com/main/archives/tag/entertainer/feed;http://laymanstermsdev.wordpress.com/feed
22fetch_interval = 1515fetch_interval = 15
2316
24[CD]17[CD]
25display_eject_in_menu = False18display_eject_in_menu = False
2619
20[Photographs]
21slideshow_step = 5
22
23[General]
24stage_width = 1366
25stage_height = 768
26show_effects = True
27start_in_fullscreen = True
28theme = Default
29backend_port = 45054
30history_size = 8
31transition_effect = Slide
32start_server_auto = True
33display_icon = False
34
2735
=== removed file 'cfg/preferences.conf'
--- cfg/preferences.conf 2009-05-06 03:40:22 +0000
+++ cfg/preferences.conf 1970-01-01 00:00:00 +0000
@@ -1,16 +0,0 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2[Photographs]
3slideshow_step = 5
4
5[General]
6stage_width = 1366
7stage_height = 768
8show_effects = True
9start_in_fullscreen = True
10theme = Default
11backend_port = 45054
12history_size = 8
13transition_effect = Slide
14start_server_auto = True
15display_icon = False
16
170
=== modified file 'docs/COPYING'
--- docs/COPYING 2009-05-09 15:45:18 +0000
+++ docs/COPYING 2010-01-23 03:34:11 +0000
@@ -359,6 +359,7 @@
359GNU General Public License for more details.359GNU General Public License for more details.
360360
361Entertainer Developers, as referenced in each file's copyright, refers to:361Entertainer Developers, as referenced in each file's copyright, refers to:
362 * Francesco Marella <francesco.marella@gmail.com>
362 * Jamie Bennett <jamie@linuxuk.org>363 * Jamie Bennett <jamie@linuxuk.org>
363 * Joshua Scotton <josh@joshuascotton.com>364 * Joshua Scotton <josh@joshuascotton.com>
364 * Lauri Taimila <lauri@taimila.com>365 * Lauri Taimila <lauri@taimila.com>
365366
=== modified file 'docs/DEPENDENCIES'
--- docs/DEPENDENCIES 2009-05-09 19:08:39 +0000
+++ docs/DEPENDENCIES 2010-01-23 03:34:11 +0000
@@ -4,7 +4,6 @@
4python-ctypes4python-ctypes
5python-eyed35python-eyed3
6python-feedparser6python-feedparser
7python-glade2
8python-gobject7python-gobject
9python-gst0.108python-gst0.10
10python-gtk29python-gtk2
@@ -14,3 +13,4 @@
14python-pyvorbis13python-pyvorbis
15python-storm14python-storm
16python-twisted15python-twisted
16python-xdg
1717
=== modified file 'entertainer'
--- entertainer 2009-05-06 02:58:08 +0000
+++ entertainer 2010-01-23 03:34:10 +0000
@@ -1,8 +1,9 @@
1#!/usr/bin/env python1#!/usr/bin/env python
2# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv22# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
3'''Main client executable'''
3'''Main frontend executable'''4'''Main frontend executable'''
45
5from entertainerlib.frontend import main6from entertainerlib.client import main
67
7if __name__ == '__main__':8if __name__ == '__main__':
8 main()9 main()
910
=== modified file 'entertainer-backend'
--- entertainer-backend 2009-05-06 02:58:08 +0000
+++ entertainer-backend 2010-01-23 03:34:10 +0000
@@ -51,11 +51,7 @@
51 sys.exit(0)51 sys.exit(0)
5252
53 if len(sys.argv) > 1 and sys.argv[1] == "--foreground":53 if len(sys.argv) > 1 and sys.argv[1] == "--foreground":
54 try:54 backend = BackendServer()
55 backend = BackendServer()
56 except KeyboardInterrupt:
57 backend.quitBackend()
58 sys.exit()
59 else:55 else:
60 print "Entertainer backend starting..."56 print "Entertainer backend starting..."
61 libc = ctypes.CDLL('libc.so.6')57 libc = ctypes.CDLL('libc.so.6')
6258
=== renamed file 'entertainer-content-manager' => 'entertainer-manager'
--- entertainer-content-manager 2009-04-28 23:54:49 +0000
+++ entertainer-manager 2010-01-23 03:34:10 +0000
@@ -1,15 +1,14 @@
1#!/usr/bin/env python1#!/usr/bin/env python
2# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv22# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
3'''Content Management executable'''3'''Manager executable'''
44
5import gtk5import gtk
66
7from entertainerlib.frontend.translation_setup import TranslationSetup7from entertainerlib.client.translation_setup import TranslationSetup
8TranslationSetup()8TranslationSetup()
99
10from entertainerlib.utils.content_management_dialog import (10from entertainerlib.dialog import ManagerDialog
11 ContentManagementDialog)11
1212
1313ManagerDialog(True)
14ContentManagementDialog(True)
15gtk.main()14gtk.main()
1615
=== removed file 'entertainer-preferences'
--- entertainer-preferences 2009-04-28 23:54:49 +0000
+++ entertainer-preferences 1970-01-01 00:00:00 +0000
@@ -1,14 +0,0 @@
1#!/usr/bin/env python
2# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
3'''A preferences editing tool'''
4
5import gtk
6
7from entertainerlib.frontend.translation_setup import TranslationSetup
8TranslationSetup()
9
10from entertainerlib.utils.preferences_dialog import PreferencesDialog
11
12
13PreferencesDialog(True)
14gtk.main()
150
=== added file 'entertainer-server'
--- entertainer-server 1970-01-01 00:00:00 +0000
+++ entertainer-server 2010-01-23 03:34:10 +0000
@@ -0,0 +1,7 @@
1#!/usr/bin/env python
2'''Server executable'''
3
4from entertainerlib.network import server_main
5
6server_main()
7
08
=== modified file 'entertainerlib/backend/backend_server.py'
--- entertainerlib/backend/backend_server.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/backend/backend_server.py 2010-01-23 03:34:11 +0000
@@ -3,8 +3,8 @@
33
4import gobject4import gobject
55
6from entertainerlib.utils.configuration import Configuration6from entertainerlib.configuration import Configuration
7from entertainerlib.utils.logger import Logger7from entertainerlib.logger import Logger
88
9# Entertainer backend core9# Entertainer backend core
10from entertainerlib.backend.core.message import Message10from entertainerlib.backend.core.message import Message
@@ -39,18 +39,13 @@
39 self.config = Configuration()39 self.config = Configuration()
40 self.logger = Logger().getLogger('backend.BackendServer')40 self.logger = Logger().getLogger('backend.BackendServer')
41 self.message_bus = MessageBus()41 self.message_bus = MessageBus()
42 self._port = self.config.get_port()42 self._port = self.config.port
4343
44 # Connection server - Thread that listens incoming socket connections44 # Connection server - Thread that listens incoming socket connections
45 self.connection_server = None45 self.connection_server = None
4646
47 # Config files
48 self.content_config = None
49 self.preferences = None
50
51 self.scheduler = None47 self.scheduler = None
52 self.feed_manager = None48 self.feed_manager = None
53 self.guide_updater = None
54 self.media_manager = None49 self.media_manager = None
5550
56 # The order of the initialize method calls is significant! Don't change51 # The order of the initialize method calls is significant! Don't change
@@ -65,15 +60,13 @@
65 """Initialize configuration"""60 """Initialize configuration"""
66 cfg_dict = {61 cfg_dict = {
67 MessageType.CONTENT_CONF_UPDATED : MessagePriority.VERY_HIGH,62 MessageType.CONTENT_CONF_UPDATED : MessagePriority.VERY_HIGH,
68 MessageType.PREFERENCES_CONF_UPDATED : MessagePriority.VERY_HIGH
69 }63 }
70 self.message_bus.registerMessageHandler(self.config, cfg_dict)64 self.message_bus.registerMessageHandler(self.config, cfg_dict)
71 self.logger.debug("Configuration intialized successfully")65 self.logger.debug("Configuration intialized successfully")
7266
73 def initialize_connection_server(self):67 def initialize_connection_server(self):
74 """Initialize connection server."""68 """Initialize connection server."""
75 self.connection_server = ConnectionServer(self._port,69 self.connection_server = ConnectionServer(self._port, self.message_bus)
76 self.message_bus)
77 # Start listening incoming connections70 # Start listening incoming connections
78 self.connection_server.start()71 self.connection_server.start()
7972
@@ -81,7 +74,7 @@
81 """Initialize message scheduler."""74 """Initialize message scheduler."""
82 self.scheduler = MessageScheduler(self.message_bus)75 self.scheduler = MessageScheduler(self.message_bus)
83 self.scheduler.addMessage(Message(MessageType.UPDATE_FEEDS),76 self.scheduler.addMessage(Message(MessageType.UPDATE_FEEDS),
84 60 * self.config.get_feed_fetch_interval())77 60 * self.config.feed_fetch_interval)
85 self.logger.debug("Message scheduler intialized successfully")78 self.logger.debug("Message scheduler intialized successfully")
8679
87 def initialize_feed_manager(self):80 def initialize_feed_manager(self):
@@ -105,7 +98,3 @@
105 self.message_bus.registerMessageHandler(self.media_manager, media_dict)98 self.message_bus.registerMessageHandler(self.media_manager, media_dict)
106 self.logger.debug("Media Manager intialized successfully")99 self.logger.debug("Media Manager intialized successfully")
107100
108 def quitBackend(self):
109 '''Close the backend server'''
110 self.message_bus.unregisterAllMessageHandlers()
111
112101
=== modified file 'entertainerlib/backend/components/feeds/feed_fetcher.py'
--- entertainerlib/backend/components/feeds/feed_fetcher.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/backend/components/feeds/feed_fetcher.py 2010-01-23 03:34:11 +0000
@@ -7,8 +7,8 @@
7from datetime import datetime7from datetime import datetime
8from pysqlite2 import dbapi2 as sqlite8from pysqlite2 import dbapi2 as sqlite
99
10from entertainerlib.utils.configuration import Configuration10from entertainerlib.configuration import Configuration
11from entertainerlib.utils.logger import Logger11from entertainerlib.logger import Logger
1212
13# Messaging system13# Messaging system
14from entertainerlib.backend.core.message import Message14from entertainerlib.backend.core.message import Message
1515
=== modified file 'entertainerlib/backend/components/feeds/feed_manager.py'
--- entertainerlib/backend/components/feeds/feed_manager.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/backend/components/feeds/feed_manager.py 2010-01-23 03:34:11 +0000
@@ -5,8 +5,8 @@
5from pysqlite2 import dbapi2 as sqlite5from pysqlite2 import dbapi2 as sqlite
6from entertainerlib.backend.components.feeds.feed_fetcher import FeedFetcher6from entertainerlib.backend.components.feeds.feed_fetcher import FeedFetcher
77
8from entertainerlib.utils.configuration import Configuration8from entertainerlib.configuration import Configuration
9from entertainerlib.utils.logger import Logger9from entertainerlib.logger import Logger
1010
11# Messaging system11# Messaging system
12from entertainerlib.backend.core.message_type_priority import MessageType12from entertainerlib.backend.core.message_type_priority import MessageType
@@ -35,8 +35,7 @@
35 Update all feeds to cache in a new thread and after35 Update all feeds to cache in a new thread and after
36 that emit FEED_DB_UPDATED message to the messagebus.36 that emit FEED_DB_UPDATED message to the messagebus.
37 """37 """
38 fetch_thread = FeedFetcher(self.message_bus,38 fetch_thread = FeedFetcher(self.message_bus, self.config.feeds)
39 self.config.get_feeds())
40 fetch_thread.start()39 fetch_thread.start()
4140
42 def createFeedCacheDatabase(self):41 def createFeedCacheDatabase(self):
4342
=== renamed file 'entertainerlib/utils/feed_utils.py' => 'entertainerlib/backend/components/feeds/feed_utils.py'
--- entertainerlib/utils/feed_utils.py 2009-05-06 02:02:20 +0000
+++ entertainerlib/backend/components/feeds/feed_utils.py 2010-01-23 03:34:11 +0000
@@ -6,9 +6,8 @@
6from xml.dom import minidom6from xml.dom import minidom
77
8import gtk8import gtk
9import gtk.glade
109
11from entertainerlib.utils.configuration import Configuration10from entertainerlib.configuration import Configuration
1211
1312
14class FeedEntryParser:13class FeedEntryParser:
1514
=== modified file 'entertainerlib/backend/components/mediacache/image_cache.py'
--- entertainerlib/backend/components/mediacache/image_cache.py 2009-06-01 09:55:40 +0000
+++ entertainerlib/backend/components/mediacache/image_cache.py 2010-01-23 03:34:11 +0000
@@ -8,8 +8,8 @@
8from pysqlite2 import dbapi2 as sqlite8from pysqlite2 import dbapi2 as sqlite
99
10from entertainerlib.thumbnailer import ImageThumbnailer10from entertainerlib.thumbnailer import ImageThumbnailer
11from entertainerlib.utils.configuration import Configuration11from entertainerlib.configuration import Configuration
12from entertainerlib.utils.logger import Logger12from entertainerlib.logger import Logger
1313
14from entertainerlib.backend.components.mediacache.cache import Cache14from entertainerlib.backend.components.mediacache.cache import Cache
1515
@@ -117,15 +117,13 @@
117 "Path doesn't exist: " + path)117 "Path doesn't exist: " + path)
118 else:118 else:
119 for root, dirs, files in os.walk(path):119 for root, dirs, files in os.walk(path):
120 if os.path.split(root)[-1][0] == "." and not \120 if os.path.split(root)[-1][0] == ".":
121 self.config.display_hidden_files_folders():
122 continue121 continue
123 if not self.isDirectoryInCache(root):122 if not self.isDirectoryInCache(root):
124 self._addAlbum(root)123 self._addAlbum(root)
125124
126 for name in files:125 for name in files:
127 if os.path.split(name)[-1][0] == "." and not \126 if os.path.split(name)[-1][0] == ".":
128 self.config.display_hidden_files_folders():
129 continue127 continue
130 if self.isSupportedFormat(name):128 if self.isSupportedFormat(name):
131 self.addFile(os.path.join(root, name))129 self.addFile(os.path.join(root, name))
132130
=== modified file 'entertainerlib/backend/components/mediacache/media_cache_manager.py'
--- entertainerlib/backend/components/mediacache/media_cache_manager.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/backend/components/mediacache/media_cache_manager.py 2010-01-23 03:34:11 +0000
@@ -1,8 +1,8 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''MediaCacheManager - Downloads metadata and keeps media cache up-to-date'''2'''MediaCacheManager - Downloads metadata and keeps media cache up-to-date'''
33
4from entertainerlib.utils.configuration import Configuration4from entertainerlib.configuration import Configuration
5from entertainerlib.utils.logger import Logger5from entertainerlib.logger import Logger
66
7from entertainerlib.backend.core.message_type_priority import MessageType7from entertainerlib.backend.core.message_type_priority import MessageType
8from entertainerlib.backend.core.message_handler import MessageHandler8from entertainerlib.backend.core.message_handler import MessageHandler
@@ -13,7 +13,7 @@
13from entertainerlib.backend.components.mediacache.video_cache import VideoCache13from entertainerlib.backend.components.mediacache.video_cache import VideoCache
1414
15class MediaCacheManager(MessageHandler):15class MediaCacheManager(MessageHandler):
16 """Makes sure that frontend has all the data available."""16 """Makes sure that client has all the data available."""
1717
18 def __init__(self):18 def __init__(self):
19 """19 """
@@ -23,11 +23,11 @@
23 self.logger = Logger().getLogger(23 self.logger = Logger().getLogger(
24 'backend.components.mediacache.MediaCacheManager')24 'backend.components.mediacache.MediaCacheManager')
25 self.config = Configuration()25 self.config = Configuration()
26 self.video_folders = self.config.get_video_folders()26 self.video_folders = self.config.media_folders
27 self._index_videos(self.video_folders)27 self._index_videos(self.video_folders)
28 self.music_folders = self.config.get_music_folders()28 self.music_folders = self.config.media_folders
29 self._index_music(self.music_folders)29 self._index_music(self.music_folders)
30 self.image_folders = self.config.get_image_folders()30 self.image_folders = self.config.media_folders
31 self._index_images(self.image_folders)31 self._index_images(self.image_folders)
3232
33 # Should we rebuild to detect files that were removed while backend was33 # Should we rebuild to detect files that were removed while backend was
@@ -104,9 +104,9 @@
104 we need to index them. If folders are removed, we need to remove104 we need to index them. If folders are removed, we need to remove
105 them from the cache and also from FileSystemObeserver.105 them from the cache and also from FileSystemObeserver.
106 """106 """
107 updated_video_folders = self.config.get_video_folders()107 updated_video_folders = self.config.media_folders
108 updated_music_folders = self.config.get_music_folders()108 updated_music_folders = self.config.media_folders
109 updated_image_folders = self.config.get_image_folders()109 updated_image_folders = self.config.media_folders
110110
111 # Handle image folder changes111 # Handle image folder changes
112 current_images = set(self.image_folders)112 current_images = set(self.image_folders)
113113
=== modified file 'entertainerlib/backend/components/mediacache/music_cache.py'
--- entertainerlib/backend/components/mediacache/music_cache.py 2009-05-09 17:03:51 +0000
+++ entertainerlib/backend/components/mediacache/music_cache.py 2010-01-23 03:34:11 +0000
@@ -9,11 +9,11 @@
9import ogg.vorbis9import ogg.vorbis
10from pysqlite2 import dbapi2 as sqlite10from pysqlite2 import dbapi2 as sqlite
1111
12from entertainerlib.utils.albumart_downloader import AlbumArtDownloader
13from entertainerlib.utils.configuration import Configuration
14from entertainerlib.utils.logger import Logger
15
16from entertainerlib.backend.components.mediacache.cache import Cache12from entertainerlib.backend.components.mediacache.cache import Cache
13from entertainerlib.configuration import Configuration
14from entertainerlib.download import AlbumArtDownloader
15from entertainerlib.logger import Logger
16
1717
18class MusicCache(Cache):18class MusicCache(Cache):
19 """19 """
@@ -433,7 +433,7 @@
433 album_art_file)433 album_art_file)
434 # Local not found -> try internet434 # Local not found -> try internet
435 else:435 else:
436 if self.config.download_album_art():436 if self.config.download_album_art:
437 if album != "Unknown album" and artist != "Unknown Artist":437 if album != "Unknown album" and artist != "Unknown Artist":
438 loader_thread = AlbumArtDownloader(album, artist,438 loader_thread = AlbumArtDownloader(album, artist,
439 self.config.ALBUM_ART_DIR)439 self.config.ALBUM_ART_DIR)
440440
=== modified file 'entertainerlib/backend/components/mediacache/video_cache.py'
--- entertainerlib/backend/components/mediacache/video_cache.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/backend/components/mediacache/video_cache.py 2010-01-23 03:34:11 +0000
@@ -6,8 +6,8 @@
6from pysqlite2 import dbapi2 as sqlite6from pysqlite2 import dbapi2 as sqlite
77
8from entertainerlib.thumbnailer import VideoThumbnailer8from entertainerlib.thumbnailer import VideoThumbnailer
9from entertainerlib.utils.configuration import Configuration9from entertainerlib.configuration import Configuration
10from entertainerlib.utils.logger import Logger10from entertainerlib.logger import Logger
1111
12from entertainerlib.backend.components.mediacache.cache import Cache12from entertainerlib.backend.components.mediacache.cache import Cache
13from entertainerlib.backend.components.mediacache.video_metadata_search import (13from entertainerlib.backend.components.mediacache.video_metadata_search import (
@@ -243,7 +243,7 @@
243 VALUES (:fn)""",243 VALUES (:fn)""",
244 { "fn" : filename } )244 { "fn" : filename } )
245 self.__db_conn.commit()245 self.__db_conn.commit()
246 if self.config.download_video_metadata():246 if self.config.download_metadata:
247 self.__searchMetadata(filename)247 self.__searchMetadata(filename)
248248
249 def __searchMetadata(self, filename):249 def __searchMetadata(self, filename):
250250
=== modified file 'entertainerlib/backend/components/mediacache/video_metadata_search.py'
--- entertainerlib/backend/components/mediacache/video_metadata_search.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/backend/components/mediacache/video_metadata_search.py 2010-01-23 03:34:11 +0000
@@ -9,8 +9,8 @@
9import threading9import threading
10from pysqlite2 import dbapi2 as sqlite10from pysqlite2 import dbapi2 as sqlite
1111
12from entertainerlib.utils.logger import Logger12from entertainerlib.logger import Logger
13from entertainerlib.utils.configuration import Configuration13from entertainerlib.configuration import Configuration
1414
15class VideoMetadataSearch(threading.Thread):15class VideoMetadataSearch(threading.Thread):
16 """16 """
1717
=== modified file 'entertainerlib/backend/core/client_connection.py'
--- entertainerlib/backend/core/client_connection.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/backend/core/client_connection.py 2010-01-23 03:34:11 +0000
@@ -8,7 +8,7 @@
8# Messaging system8# Messaging system
9from entertainerlib.backend.core.message_handler import MessageHandler9from entertainerlib.backend.core.message_handler import MessageHandler
1010
11from entertainerlib.utils.logger import Logger11from entertainerlib.logger import Logger
1212
13class ClientConnection(threading.Thread, MessageHandler):13class ClientConnection(threading.Thread, MessageHandler):
14 """14 """
1515
=== modified file 'entertainerlib/backend/core/connection_server.py'
--- entertainerlib/backend/core/connection_server.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/backend/core/connection_server.py 2010-01-23 03:34:11 +0000
@@ -7,7 +7,7 @@
77
8from entertainerlib.backend.core.client_connection import ClientConnection8from entertainerlib.backend.core.client_connection import ClientConnection
99
10from entertainerlib.utils.logger import Logger10from entertainerlib.logger import Logger
1111
12class ConnectionServer(threading.Thread):12class ConnectionServer(threading.Thread):
13 """13 """
1414
=== modified file 'entertainerlib/backend/core/message_bus.py'
--- entertainerlib/backend/core/message_bus.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/backend/core/message_bus.py 2010-01-23 03:34:11 +0000
@@ -6,7 +6,7 @@
6from entertainerlib.backend.core.message import Message6from entertainerlib.backend.core.message import Message
7from entertainerlib.backend.core.message_handler import MessageHandler7from entertainerlib.backend.core.message_handler import MessageHandler
8from entertainerlib.backend.core.message_type_priority import MessageType8from entertainerlib.backend.core.message_type_priority import MessageType
9from entertainerlib.utils.logger import Logger9from entertainerlib.logger import Logger
1010
11class MessageBus:11class MessageBus:
12 """12 """
@@ -23,12 +23,7 @@
23 When MessageHandler is registered to the MessageBus there is also another23 When MessageHandler is registered to the MessageBus there is also another
24 parameter besides handler itself. Second parameter is a dictionary that24 parameter besides handler itself. Second parameter is a dictionary that
25 defines MessageTypes that registered handler wants to be notified of and25 defines MessageTypes that registered handler wants to be notified of and
26 also priorities for those message types.26 also priorities for those message types."""
27
28 Example of second parameter:
29 dict = {MessageType.FRONTEND_OPENED : MessagePriority.LOW ,
30 MessageType.FRONTEND_CLOSED : MessagePriority.NORMAL }
31 """
3227
33 # This determines number of message types avaialble. In other words, this28 # This determines number of message types avaialble. In other words, this
34 # variable tells how many variables is defined in MessageType class.29 # variable tells how many variables is defined in MessageType class.
@@ -89,12 +84,6 @@
89 self.logger.debug("MessageHandler '" + str(message_handler) +84 self.logger.debug("MessageHandler '" + str(message_handler) +
90 "' unregistered from the message bus.")85 "' unregistered from the message bus.")
9186
92 def unregisterAllMessageHandlers(self):
93 """
94 Unregisters all MessageHandler from this MessageBus.
95 """
96 self.message_handlers[:] = []
97
98 def notifyMessage(self, message):87 def notifyMessage(self, message):
99 """88 """
100 Emit a new Message to this MessageBus.89 Emit a new Message to this MessageBus.
10190
=== modified file 'entertainerlib/backend/core/message_bus_proxy.py'
--- entertainerlib/backend/core/message_bus_proxy.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/backend/core/message_bus_proxy.py 2010-01-23 03:34:11 +0000
@@ -6,7 +6,7 @@
6import threading6import threading
7from cStringIO import StringIO7from cStringIO import StringIO
88
9from entertainerlib.utils.configuration import Configuration9from entertainerlib.configuration import Configuration
1010
11class MessageBusProxy(threading.Thread):11class MessageBusProxy(threading.Thread):
12 """12 """
@@ -57,7 +57,7 @@
57 MessageHandler.57 MessageHandler.
58 """58 """
59 # Open socket59 # Open socket
60 self.socket_to_server.connect(('localhost', self.config.get_port()))60 self.socket_to_server.connect(('localhost', self.config.port))
6161
62 # Send client name62 # Send client name
63 self.socket_to_server.sendall(self.client_name + "\n")63 self.socket_to_server.sendall(self.client_name + "\n")
6464
=== modified file 'entertainerlib/backend/core/message_type_priority.py'
--- entertainerlib/backend/core/message_type_priority.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/backend/core/message_type_priority.py 2010-01-23 03:34:11 +0000
@@ -15,56 +15,27 @@
15 VERY_LOW = 4015 VERY_LOW = 40
1616
17class MessageType:17class MessageType:
18 """18 """Determines all allowed Message types. MessageHandler should use these to
19 Determines all allowed Message types. MessageHandler should use these19 determine type. This simply makes code more readable."""
20 to determine type. This simply makes code more readable.
21
22 Example:
23 if message.get_type() == MessageType.FRONTEND_OPENED:
24 do_something_useful()
25 """
26
27 # Indicates that Preferences UI has been used to update preferences.
28 PREFERENCES_CONF_UPDATED = 0
2920
30 # Indicates that Content Management UI has been used to update contents.21 # Indicates that Content Management UI has been used to update contents.
31 CONTENT_CONF_UPDATED = 122 CONTENT_CONF_UPDATED = 0
32
33 # Indicates that frontend has been opened.
34 FRONTEND_OPENED = 2
35
36 # Indicates that frontend has been closed.
37 FRONTEND_CLOSED = 3
3823
39 # Indicates that Feed cache has been updated.24 # Indicates that Feed cache has been updated.
40 FEED_DB_UPDATED = 425 FEED_DB_UPDATED = 1
41
42 NOT_USED_5 = 5
43
44 NOT_USED_1 = 6
4526
46 # Indicates that Feed cache should be updated.27 # Indicates that Feed cache should be updated.
47 UPDATE_FEEDS = 728 UPDATE_FEEDS = 2
48
49 NOT_USED_6 = 8
50
51 #This should be left in otherwise message_bus.py breaks
52 #If another message type is needed please use this
53 DO_NOT_DELETE_USE_NEXT = 9
54
55 NOT_USED_2 = 10
56 NOT_USED_3 = 11
57 NOT_USED_4 = 12
5829
59 # Require to rebuild image cache30 # Require to rebuild image cache
60 REBUILD_IMAGE_CACHE = 1331 REBUILD_IMAGE_CACHE = 3
6132
62 # Require to rebuild music cache33 # Require to rebuild music cache
63 REBUILD_MUSIC_CACHE = 1434 REBUILD_MUSIC_CACHE = 4
6435
65 # Require to rebuild video cache36 # Require to rebuild video cache
66 REBUILD_VIDEO_CACHE = 1537 REBUILD_VIDEO_CACHE = 5
6738
68 # Require to rebuild feed cache39 # Require to rebuild feed cache
69 REBUILD_FEED_CACHE = 1640 REBUILD_FEED_CACHE = 6
7041
7142
=== renamed directory 'entertainerlib/frontend' => 'entertainerlib/client'
=== modified file 'entertainerlib/client/__init__.py'
--- entertainerlib/frontend/__init__.py 2009-05-06 03:40:22 +0000
+++ entertainerlib/client/__init__.py 2010-01-23 03:34:11 +0000
@@ -1,12 +1,12 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Frontend gui to entertainer'''2'''Client code for Entertainer.'''
3# pylint: disable-msg=W06123# pylint: disable-msg=W0612
44
5def main(*args, **kwargs):5def main(*args, **kwargs):
6 '''Frontend runner'''6 '''Client code main loop.'''
77
8 # Import statements are inside the function so that they aren't imported8 # Import statements are inside the function so that they aren't imported
9 # every time something from the frontend is imported9 # every time something from the client is imported
1010
11 # cluttergtk must be imported before the first import of clutter so it11 # cluttergtk must be imported before the first import of clutter so it
12 # must be imported even though pylint complains about it not being used.12 # must be imported even though pylint complains about it not being used.
@@ -15,23 +15,23 @@
15 import gobject15 import gobject
16 import gtk16 import gtk
1717
18 from entertainerlib.frontend.translation_setup import TranslationSetup18 from entertainerlib.client.translation_setup import TranslationSetup
19 TranslationSetup()19 TranslationSetup()
2020
21 from entertainerlib.backend.backend_server import BackendServer21 from entertainerlib.backend.backend_server import BackendServer
22 from entertainerlib.utils.configuration import Configuration22 from entertainerlib.configuration import Configuration
23 from entertainerlib.frontend.frontend_client import FrontendClient23 from entertainerlib.client.client import Client
2424
25 gobject.threads_init()25 gobject.threads_init()
26 gtk.gdk.threads_init()26 gtk.gdk.threads_init()
27 clutter.threads_init()27 clutter.threads_init()
2828
29 config = Configuration()29 config = Configuration()
30 if config.start_auto_server():30 if config.start_auto_server:
31 print "Entertainer backend starting..."31 print "Entertainer backend starting..."
32 BackendServer()32 BackendServer()
3333
34 frontend_client = FrontendClient()34 client_client = Client()
35 frontend_client.start()35 client_client.start()
3636
3737
3838
=== modified file 'entertainerlib/client/backend_connection.py'
--- entertainerlib/frontend/backend_connection.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/client/backend_connection.py 2010-01-23 03:34:11 +0000
@@ -7,49 +7,21 @@
7from entertainerlib.backend.core.message_handler import MessageHandler7from entertainerlib.backend.core.message_handler import MessageHandler
88
9class BackendConnection(MessageHandler):9class BackendConnection(MessageHandler):
10 """10 """Connection to the Entertainer backend messagebus."""
11 BackendConnection - Connection to the Entertainer backend. Instance from
12 this class is a gate to backend messagebus.
13 """
1411
15 def __init__(self):12 def __init__(self):
16 """Initialize connection. Connects to backend."""
17 #messages = {
18 # MessageType.CONTENT_CONF_UPDATED : MessagePriority.NORMAL,
19 # MessageType.PREFERENCES_CONF_UPDATED : MessagePriority.NORMAL,
20 # MessageType.FEED_DB_UPDATED : MessagePriority.HIGH }
21 # XXX: rockstar - The messages above are the "real" messages, and I'm
22 # not sure why they are commented out. Anyone?
23 MessageHandler.__init__(self)13 MessageHandler.__init__(self)
24 messages = {}14 messages = {}
25 name = "Entertainer Frontend"15 name = "Entertainer Frontend"
26 self.message_bus_proxy = MessageBusProxy(messages, self, name)16 self.message_bus_proxy = MessageBusProxy(messages, self, name)
27 self.message_bus_proxy.connectToMessageBus()17 self.message_bus_proxy.connectToMessageBus()
28 self.message_bus_proxy.start()18 self.message_bus_proxy.start()
29 self.message_bus_proxy.sendMessage(
30 Message(MessageType.FRONTEND_OPENED))
3119
32 def close_connection(self):20 def close_connection(self):
33 """Close connection to backend"""21 """Close connection to backend"""
34 self.message_bus_proxy.sendMessage(
35 Message(MessageType.FRONTEND_CLOSED))
36 self.message_bus_proxy.disconnectFromMessageBus()22 self.message_bus_proxy.disconnectFromMessageBus()
3723
38 def request_feed_update(self):24 def request_feed_update(self):
39 """Request backend to fetch all feeds from the Internet."""25 """Request backend to fetch all feeds from the Internet."""
40 self.message_bus_proxy.sendMessage(Message(MessageType.UPDATE_FEEDS))26 self.message_bus_proxy.sendMessage(Message(MessageType.UPDATE_FEEDS))
4127
42 def request_weather_update(self):
43 """Request backend to update weather information from the Internet."""
44 self.message_bus_proxy.sendMessage(Message(MessageType.UPDATE_WEATHER))
45
46 # Implements MessageHandler interface
47 def handleMessage(self, message):
48 """Handle received messages. (Implements MessageHandler interface)"""
49 if message.get_type() == MessageType.CONTENT_CONF_UPDATED:
50 pass
51 elif message.get_type() == MessageType.PREFERENCES_CONF_UPDATED:
52 pass
53 elif message.get_type() == MessageType.FEED_DB_UPDATED:
54 pass
55
5628
=== renamed file 'entertainerlib/frontend/frontend_client.py' => 'entertainerlib/client/client.py'
--- entertainerlib/frontend/frontend_client.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/client/client.py 2010-01-23 03:34:11 +0000
@@ -1,71 +1,65 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Entertainer Frontend - This is a client side of Entertainer.'''2'''Entertainer client.'''
33
4import sys4import sys
55
6import gtk6import gtk
77from twisted.internet import gtk2reactor
8from entertainerlib.frontend.backend_connection import BackendConnection8gtk2reactor.install() # Install the gtk2 reactor before import the real reactor
9from entertainerlib.frontend.gui.user_interface import UserInterface9from twisted.internet import reactor
10from entertainerlib.frontend.medialibrary.feeds import FeedLibrary10from twisted.internet.protocol import ClientCreator
11from entertainerlib.frontend.medialibrary.music import MusicLibrary11from twisted.python.log import startLogging
12from entertainerlib.frontend.medialibrary.images import ImageLibrary12
13from entertainerlib.frontend.medialibrary.videos import VideoLibrary13from entertainerlib.client.backend_connection import BackendConnection
14from entertainerlib.utils.configuration import Configuration14from entertainerlib.client.medialibrary.feeds import FeedLibrary
15from entertainerlib.utils.logger import Logger15from entertainerlib.client.medialibrary.music import MusicLibrary
16from entertainerlib.utils.system_tray_icon import SystemTrayIcon16from entertainerlib.client.medialibrary.images import ImageLibrary
1717from entertainerlib.client.medialibrary.videos import VideoLibrary
18class FrontendClient:18from entertainerlib.configuration import Configuration
19 '''19from entertainerlib.gui.user_interface import UserInterface
20 Entertainer frontend20from entertainerlib.gui.system_tray_icon import SystemTrayIcon
2121from entertainerlib.network.local.client import EntertainerLocalClientProtocol
22 This is a frontend application of the Entertainer. Frontend is a GUI part22
23 that user sees on the screen. This class is a core of the frontend. Frontend23
24 connects to the backend's messagebus at startup.24class Client:
25 '''25 '''This is a client application of the Entertainer. Entertainer's client
26 hooks into the server, and then provides a user interface for the data the
27 server creates.'''
2628
27 def __init__(self):29 def __init__(self):
28 '''
29 Create a new frontend.
30
31 This initializes all frontend stuff like media librarys, remote
32 control receiver and GUI. After this we just wait user actions.
33 '''
34 config = Configuration()30 config = Configuration()
35 self.logger = Logger().getLogger('frontend.FrontendClient')31 self.backend_connection = BackendConnection()
36 self.backend_connection = self.initialize_backend_connection()
37 feed_library = FeedLibrary(self.backend_connection)32 feed_library = FeedLibrary(self.backend_connection)
38 music_library = MusicLibrary(self.backend_connection)33 music_library = MusicLibrary()
39 image_library = ImageLibrary(self.backend_connection)34 image_library = ImageLibrary()
40 video_library = VideoLibrary(self.backend_connection)35 video_library = VideoLibrary()
41 self.ui = UserInterface(36 self.ui = UserInterface(
42 feed_library, image_library, music_library, video_library,37 feed_library, image_library, music_library, video_library,
43 self.quit_frontend)38 self.quit_client)
4439
45 if config.tray_icon_enabled():40 if config.tray_icon_enabled:
46 SystemTrayIcon(41 SystemTrayIcon(self.quit_client, self.toggle_interface_visibility)
47 self.quit_frontend, self.toggle_interface_visibility)42
43 startLogging(sys.stdout)
44 client = EntertainerLocalClientProtocol
45
46 ClientCreator(reactor, client).connectTCP(
47 config.network_options['host'],
48 config.network_options['port'])
4849
49 def start(self):50 def start(self):
50 '''Start the necessary main loop.'''51 '''Start the necessary main loop.'''
51 self.ui.start_up()52 self.ui.start_up()
52 self.interface_visible = True53 self.interface_visible = True
53 gtk.gdk.threads_enter()54 gtk.gdk.threads_enter()
54 gtk.main()55 reactor.run()
55 gtk.gdk.threads_leave()56 gtk.gdk.threads_leave()
5657
57 def initialize_backend_connection(self):58 def quit_client(self):
58 '''Connect to the backend server.'''59 '''Clean up the connection to the backend then close the client.'''
59 backend_connection = BackendConnection()
60 self.logger.debug('Connected to the Entertainer backend server.')
61
62 return backend_connection
63
64 def quit_frontend(self):
65 '''Clean up the connection to the backend then close the frontend.'''
66 self.backend_connection.close_connection()60 self.backend_connection.close_connection()
6761
68 gtk.main_quit()62 reactor.stop()
69 sys.exit(0)63 sys.exit(0)
7064
71 def toggle_interface_visibility(self):65 def toggle_interface_visibility(self):
7266
=== modified file 'entertainerlib/client/media_player.py'
--- entertainerlib/frontend/media_player.py 2009-06-29 19:41:35 +0000
+++ entertainerlib/client/media_player.py 2010-01-23 03:34:11 +0000
@@ -9,10 +9,10 @@
9import gobject9import gobject
10import gst10import gst
1111
12from entertainerlib.frontend.gui.widgets.motion_buffer import MotionBuffer12from entertainerlib.gui.widgets.motion_buffer import MotionBuffer
13from entertainerlib.frontend.gui.widgets.texture import Texture13from entertainerlib.gui.widgets.texture import Texture
14from entertainerlib.frontend.medialibrary.playable import Playable14from entertainerlib.client.medialibrary.playable import Playable
15from entertainerlib.utils.logger import Logger15from entertainerlib.logger import Logger
1616
17class MediaPlayer(gobject.GObject, object):17class MediaPlayer(gobject.GObject, object):
18 '''18 '''
@@ -72,7 +72,7 @@
72 self.is_playing = False # Is media player currently playing72 self.is_playing = False # Is media player currently playing
73 self.is_reactive_allowed = False # Is the video_texture reactive73 self.is_reactive_allowed = False # Is the video_texture reactive
7474
75 self.logger = Logger().getLogger('frontend.MediaPlayer')75 self.logger = Logger().getLogger('client.MediaPlayer')
7676
77 self._internal_callback_timeout_key = None77 self._internal_callback_timeout_key = None
7878
@@ -105,7 +105,9 @@
105 self.next()105 self.next()
106 elif message.type == gst.MESSAGE_ERROR:106 elif message.type == gst.MESSAGE_ERROR:
107 self.video_texture.set_playing(False)107 self.video_texture.set_playing(False)
108 self.video_texture.set_property("position", 0)108 # XXX: laymansterms - I don't know the implications of removing the
109 # position property.
110 #self.video_texture.set_property("position", 0)
109 err, debug = message.parse_error()111 err, debug = message.parse_error()
110 self.logger.error("Error: %(err)s, %(debug)s" % \112 self.logger.error("Error: %(err)s, %(debug)s" % \
111 {'err': err, 'debug': debug})113 {'err': err, 'debug': debug})
@@ -167,7 +169,9 @@
167 or self.media.get_type() == Playable.VIDEO_STREAM:169 or self.media.get_type() == Playable.VIDEO_STREAM:
168 self.video_texture.set_playing(False)170 self.video_texture.set_playing(False)
169 self.video_texture.set_uri(playable.get_uri())171 self.video_texture.set_uri(playable.get_uri())
170 self.video_texture.set_property("position", 0)172 # XXX: laymansterms - I don't know the implications of removing the
173 # position property.
174 #self.video_texture.set_property("position", 0)
171175
172 def get_media(self):176 def get_media(self):
173 '''Get URI of the current media stream.'''177 '''Get URI of the current media stream.'''
@@ -248,7 +252,9 @@
248 self.stage.set_color(self.bgcolor)252 self.stage.set_color(self.bgcolor)
249 self.stage.remove(self.video_texture)253 self.stage.remove(self.video_texture)
250 self.video_texture.set_playing(False)254 self.video_texture.set_playing(False)
251 self.video_texture.set_property("position", 0)255 # XXX: laymansterms - I don't know the implications of removing the
256 # position property.
257 #self.video_texture.set_property("position", 0)
252 self.emit('stop')258 self.emit('stop')
253259
254 if self._internal_callback_timeout_key is not None:260 if self._internal_callback_timeout_key is not None:
@@ -510,7 +516,7 @@
510 def get_texture(self):516 def get_texture(self):
511 '''Get media's texture. This is a video texture or album art texture.'''517 '''Get media's texture. This is a video texture or album art texture.'''
512 if self.media.get_type() == Playable.VIDEO_STREAM:518 if self.media.get_type() == Playable.VIDEO_STREAM:
513 return clutter.CloneTexture(self.video_texture)519 return clutter.Clone(self.video_texture)
514520
515 elif self.media.get_type() == Playable.AUDIO_STREAM:521 elif self.media.get_type() == Playable.AUDIO_STREAM:
516 url = self.media.get_album_art_url()522 url = self.media.get_album_art_url()
517523
=== modified file 'entertainerlib/client/medialibrary/feeds.py'
--- entertainerlib/frontend/medialibrary/feeds.py 2009-06-25 19:49:48 +0000
+++ entertainerlib/client/medialibrary/feeds.py 2010-01-23 03:34:11 +0000
@@ -3,7 +3,7 @@
33
4from pysqlite2 import dbapi2 as sqlite4from pysqlite2 import dbapi2 as sqlite
55
6from entertainerlib.utils.configuration import Configuration6from entertainerlib.configuration import Configuration
77
8class FeedLibrary(object):8class FeedLibrary(object):
9 '''This library can be used to handle RSS feeds in Entertainer.'''9 '''This library can be used to handle RSS feeds in Entertainer.'''
1010
=== modified file 'entertainerlib/client/medialibrary/images.py'
--- entertainerlib/frontend/medialibrary/images.py 2009-05-06 03:40:22 +0000
+++ entertainerlib/client/medialibrary/images.py 2010-01-23 03:34:11 +0000
@@ -4,25 +4,16 @@
4import os4import os
5from pysqlite2 import dbapi2 as sqlite5from pysqlite2 import dbapi2 as sqlite
66
7from entertainerlib.utils.configuration import Configuration7from entertainerlib.configuration import Configuration
88
9class ImageLibrary:9class ImageLibrary:
10 """10 """Entertainer's image cache."""
11 Image library.11
1212 def __init__(self):
13 Entertainer's image cache.
14 """
15
16 def __init__(self, backend_connection):
17 """
18 Initialize image library
19 @param backend_connection: BackendConnection object
20 """
21 self.config = Configuration()13 self.config = Configuration()
2214
23 if not os.path.exists(self.config.IMAGE_DB):15 if not os.path.exists(self.config.IMAGE_DB):
24 raise Exception("Image database doesn't exist!")16 raise Exception("Image database doesn't exist!")
25 self.backend_connection = backend_connection
2617
27 def get_all_images(self):18 def get_all_images(self):
28 """19 """
2920
=== modified file 'entertainerlib/client/medialibrary/music.py'
--- entertainerlib/frontend/medialibrary/music.py 2009-07-19 19:27:31 +0000
+++ entertainerlib/client/medialibrary/music.py 2010-01-23 03:34:11 +0000
@@ -5,10 +5,10 @@
5import CDDB, DiscID5import CDDB, DiscID
6from pysqlite2 import dbapi2 as sqlite6from pysqlite2 import dbapi2 as sqlite
77
8from entertainerlib.utils.configuration import Configuration8from entertainerlib.configuration import Configuration
99
10from entertainerlib.frontend.medialibrary.playable import Playable10from entertainerlib.client.medialibrary.playable import Playable
11from entertainerlib.utils.lyrics_downloader import LyricsDownloader11from entertainerlib.download import LyricsDownloader
1212
1313
14class MusicLibraryException(Exception):14class MusicLibraryException(Exception):
@@ -28,22 +28,13 @@
28 pass28 pass
2929
30class MusicLibrary:30class MusicLibrary:
31 """31 """Interface for Entertainer's music cache."""
32 Music library.32
3333 def __init__(self):
34 Interface for Entertainer's music cache.
35 """
36
37 def __init__(self, backend_connection):
38 """
39 Initialize library.
40 @param backend_connection: BackendConnection object
41 """
42 self.config = Configuration()34 self.config = Configuration()
4335
44 if not os.path.exists(self.config.MUSIC_DB):36 if not os.path.exists(self.config.MUSIC_DB):
45 raise Exception("Music database doesn't exist!")37 raise Exception("Music database doesn't exist!")
46 self.backend_connection = backend_connection
47 self.db_connection = sqlite.connect(self.config.MUSIC_DB)38 self.db_connection = sqlite.connect(self.config.MUSIC_DB)
48 self.cursor = self.db_connection.cursor()39 self.cursor = self.db_connection.cursor()
4940
5041
=== modified file 'entertainerlib/client/medialibrary/videos.py'
--- entertainerlib/frontend/medialibrary/videos.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/client/medialibrary/videos.py 2010-01-23 03:34:11 +0000
@@ -4,28 +4,19 @@
4import os4import os
5from pysqlite2 import dbapi2 as sqlite5from pysqlite2 import dbapi2 as sqlite
66
7from entertainerlib.utils.configuration import Configuration7from entertainerlib.configuration import Configuration
8from entertainerlib.utils.logger import Logger8from entertainerlib.logger import Logger
99
10from entertainerlib.frontend.medialibrary.playable import Playable10from entertainerlib.client.medialibrary.playable import Playable
1111
12class VideoLibrary:12class VideoLibrary:
13 """13 """Interface for Entertainer's video cache."""
14 Video library.14
1515 def __init__(self):
16 Interface for Entertainer's video cache.
17 """
18
19 def __init__(self, backend_connection):
20 """
21 Initialize video library
22 @param backend_connection: BackendConnection object
23 """
24 self.config = Configuration()16 self.config = Configuration()
2517
26 if not os.path.exists(self.config.VIDEO_DB):18 if not os.path.exists(self.config.VIDEO_DB):
27 raise Exception("Video database doesn't exist!")19 raise Exception("Video database doesn't exist!")
28 self.backend_connection = backend_connection
2920
30 def get_movies(self):21 def get_movies(self):
31 """22 """
@@ -133,7 +124,7 @@
133124
134 #Setting default values125 #Setting default values
135 self.logger = Logger().getLogger(126 self.logger = Logger().getLogger(
136 'frontend.medialibrary.videos.VideoItem')127 'client.medialibrary.videos.VideoItem')
137 self.__title = ""128 self.__title = ""
138 self.__filename = ""129 self.__filename = ""
139 self.__length = 0130 self.__length = 0
@@ -220,12 +211,12 @@
220 else:211 else:
221 self.logger.error("Thumbnail does not exist for " + \212 self.logger.error("Thumbnail does not exist for " + \
222 self.get_filename() + ", using default art instead")213 self.get_filename() + ", using default art instead")
223 return os.path.join(self.config.get_theme_path(),214 return os.path.join(self.config.theme_path,
224 "images/default_movie_art.png")215 "images/default_movie_art.png")
225 else:216 else:
226 self.logger.error("Thumbnail does not exist for " + \217 self.logger.error("Thumbnail does not exist for " + \
227 self.get_filename() + ", using default art instead")218 self.get_filename() + ", using default art instead")
228 return os.path.join(self.config.get_theme_path(),219 return os.path.join(self.config.theme_path,
229 "images/default_movie_art.png")220 "images/default_movie_art.png")
230221
231 def has_thumbnail(self):222 def has_thumbnail(self):
232223
=== modified file 'entertainerlib/client/translation_setup.py'
--- entertainerlib/frontend/translation_setup.py 2009-07-26 07:59:56 +0000
+++ entertainerlib/client/translation_setup.py 2010-01-23 03:34:11 +0000
@@ -1,38 +1,48 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Translation Setup Code'''2'''Translation Setup Code'''
33
4import locale
4import os5import os
56
6import gettext7import gettext
7import gtk8import gtk
8import gtk.glade9from xdg import BaseDirectory
9
10TRANSLATION_SOURCE = {
11 'branch' : os.path.abspath(os.path.dirname(__file__) + '/../../locale'),
12 # Hardcoded path for a package install
13 'package' : "/usr/share/locale"
14 }
1510
16class TranslationSetup:11class TranslationSetup:
17 def __init__(self):12 def __init__(self):
13 '''Because of how early translation setup has to occur. This should be
14 the only file outside of Configuration that imports xdg.'''
15
16 def install_locale(locale_dir):
17 '''Install locale data from the provided directory.'''
18 # This sets up the _ function
19 gettext.install('entertainer', localedir=locale_dir,
20 unicode=True)
21 # Call the C library gettext functions and set the codeset
22 # to avoid locale-dependent translation of the message catalog
23 locale.bindtextdomain('entertainer', locale_dir)
24 locale.bind_textdomain_codeset('entertainer', "UTF-8")
25 # XXX: fmarl - setlocale load in current locale properly
26 # We can remove it and get feedback from users to see if
27 # this hack it's really needed.
28 try:
29 locale.setlocale(locale.LC_ALL, "")
30 except locale.Error, e:
31 pass
32
18 # Find locale data from a dev branch if we can33 # Find locale data from a dev branch if we can
19 if os.path.exists(TRANSLATION_SOURCE['branch']):34 dev_locale = os.path.abspath(os.path.dirname(__file__) +
20 #This setups the _ function35 '/../../locale')
21 gettext.install('entertainer', TRANSLATION_SOURCE['branch'])36 if os.path.exists(dev_locale):
2237 install_locale(dev_locale)
23 #This setups the glade translations38
24 gtk.glade.bindtextdomain('entertainer',39 # Install locale data from the system location
25 TRANSLATION_SOURCE['branch'])
26 # Install locale data from the hardcoded package path
27 elif os.path.exists(TRANSLATION_SOURCE['package']):
28 #This setups the _ function
29 gettext.install('entertainer', TRANSLATION_SOURCE['package'])
30
31 #This setups the glade translations
32 gtk.glade.bindtextdomain('entertainer',
33 TRANSLATION_SOURCE['package'])
34 else:40 else:
35 pass41 system_data_dirs = [data_dir for data_dir in
3642 BaseDirectory.xdg_data_dirs if not
37 gtk.glade.textdomain ('entertainer')43 data_dir.startswith(BaseDirectory.xdg_data_home)]
3844 # Since we don't know for certain where the mo files were installed,
45 # we try to install from both /usr/share and /usr/local/share.
46 for data_dir in system_data_dirs:
47 system_locale = os.path.join(data_dir, 'locale')
48 install_locale(system_locale)
3949
=== renamed file 'entertainerlib/utils/configuration.py' => 'entertainerlib/configuration.py'
--- entertainerlib/utils/configuration.py 2009-06-01 09:55:40 +0000
+++ entertainerlib/configuration.py 2010-01-23 03:34:11 +0000
@@ -2,475 +2,217 @@
2'''Configuration - Class represents Entertainer's configuration'''2'''Configuration - Class represents Entertainer's configuration'''
33
4import os4import os
5import sys
6import shutil5import shutil
7import ConfigParser6import ConfigParser
8from ConfigParser import ParsingError, NoSectionError, NoOptionError7from ConfigParser import NoSectionError, NoOptionError
98
9from xdg import BaseDirectory
10
11from entertainerlib.backend.core.message_handler import MessageHandler
10from entertainerlib.backend.core.message_type_priority import MessageType12from entertainerlib.backend.core.message_type_priority import MessageType
11from entertainerlib.backend.core.message_handler import MessageHandler13from entertainerlib.db.connection import Database
1214from entertainerlib.gui.theme import Theme
13from entertainerlib.utils.theme import Theme
14
15SOURCE_CONFIG = {
16 'branch' : os.path.abspath(os.path.dirname(__file__) + '/../../cfg'),
17 # Hardcoded path for a package install
18 'package' : "/usr/share/entertainer/cfg"
19 }
2015
21class Configuration(MessageHandler):16class Configuration(MessageHandler):
22 """17 '''Interface to all configuration parameters. All components of Entertainer
23 Configuration of Entertainer18 should get configuration values through this class.'''
24
25 This class is an interface to all configuration parameters. All components
26 of Entertainer should get configuration values through this class. Object
27 from this class reads current config files and returns values based on
28 those files. If there are missing lines or other errors in config file,
29 then object return default values defined in this class.
30 """
31
32 CFG_DIR = os.path.expanduser("~/.config/entertainer")
33 TEST_DIR = None
34
35 # This dictionary keeps track of data values that are written to config
36 # files but need to be returned to a known state while testing.
37 _tainted = {}
38
39 # This dictionary keeps track of data values that are tainted in memory
40 # instead of a config file
41 _tainted_in_memory = {}
4219
43 _shared_state = {}20 _shared_state = {}
4421
45 def __init__(self, test_dir=None):22 def __init__(self, test_dir=None):
46 """
47 Read configuration files and setup this object.
48 """
49 self.__dict__ = self._shared_state23 self.__dict__ = self._shared_state
50 MessageHandler.__init__(self)24 MessageHandler.__init__(self)
5125
52 if not self._shared_state:26 if not self._shared_state or test_dir is not None:
53 self.TEST_DIR = test_dir
54
55 # Set in a production mode or a test mode27 # Set in a production mode or a test mode
56 if self.TEST_DIR is None:28 if test_dir is None:
57 self.cfg_dir = self.CFG_DIR29 self.resources = Resources()
58 else:30 else:
59 self.cfg_dir = self.TEST_DIR31 self.resources = Resources(config_testing_dir=test_dir)
6032
61 if not os.path.exists(os.path.expanduser(self.cfg_dir)):33 self.cache_dir = self.resources.cache_dir
62 self.create_cfg_dir()34 self.config_dir = self.resources.config_dir
6335 self.data_dir = self.resources.data_dir
64 self.ENTERTAINER_LOG = os.path.join(self.cfg_dir,36
65 u'entertainer.log')37 self.LOG = os.path.join(self.cache_dir, u'entertainer.log')
6638
67 self.FEED_DB = os.path.join(self.cfg_dir, 'cache/feed.db')39 self.MEDIA_DB = Database(os.path.join(self.cache_dir, 'media'))
68 self.IMAGE_DB = os.path.join(self.cfg_dir, 'cache/image.db')40
69 self.MUSIC_DB = os.path.join(self.cfg_dir, 'cache/music.db')41 self.FEED_DB = os.path.join(self.cache_dir, 'feed.db')
70 self.VIDEO_DB = os.path.join(self.cfg_dir, 'cache/video.db')42 self.IMAGE_DB = os.path.join(self.cache_dir, 'image.db')
7143 self.MUSIC_DB = os.path.join(self.cache_dir, 'music.db')
72 self.THUMB_DIR = os.path.join(self.cfg_dir, 'cache', 'thumbnails')44 self.VIDEO_DB = os.path.join(self.cache_dir, 'video.db')
45
46 self.THUMB_DIR = os.path.join(self.cache_dir, 'thumbnails')
73 self.IMAGE_THUMB_DIR = os.path.join(self.THUMB_DIR, 'image')47 self.IMAGE_THUMB_DIR = os.path.join(self.THUMB_DIR, 'image')
74 self.VIDEO_THUMB_DIR = os.path.join(self.THUMB_DIR, 'video')48 self.VIDEO_THUMB_DIR = os.path.join(self.THUMB_DIR, 'video')
75 self.ALBUM_ART_DIR = os.path.join(self.cfg_dir, 'cache/album_art')49 self.ALBUM_ART_DIR = os.path.join(self.cache_dir, 'album_art')
76 self.MOVIE_ART_DIR = os.path.join(self.cfg_dir, 'cache/movie_art')50 self.MOVIE_ART_DIR = os.path.join(self.cache_dir, 'movie_art')
7751
78 # Preferences file52 self.read_config_file()
79 self.preferences_conf = os.path.join(self.cfg_dir,53
80 'preferences.conf')54 self.theme = Theme(self.theme_path)
8155
82 # Content file56 self._stage_width = None
83 self.content_conf = os.path.join(self.cfg_dir, 'content.conf')57 self._stage_height = None
8458
85 self.content_config = ConfigParser.ConfigParser()59 # Network options specify the server type and extra options
86 self.preferences = ConfigParser.ConfigParser()60 self.network_options = {
87 try:61 'type': 'local',
88 self.content_config.readfp(open(self.content_conf))62 'host': 'localhost',
89 self.preferences.readfp(open(self.preferences_conf))63 'port': 55545}
90 except ParsingError:64
91 print("ParsingError with configuration file.")65 def read_config_file(self):
92 sys.exit(1)66 '''Read in the config file.'''
93 except IOError:67 self.content_conf = os.path.join(self.config_dir, 'content.conf')
94 print("IOError: Couldn't read configuration file.")68 self.content = ConfigParser.ConfigParser()
95 sys.exit(1)69 self.content.readfp(open(self.content_conf))
96
97 self.theme = Theme(self.get_theme_path())
98
99 self.stage_width = None
100 self.stage_height = None
101
102 def create_cfg_dir(self):
103 '''Create a configuration directory and default config files.'''
104
105 def create_cache_hierarchy(config_dir):
106 '''Create the directories needed for the cache'''
107 directories = [
108 'cache',
109 'cache/album_art',
110 'cache/movie_art',
111 'cache/thumbnails',
112 'cache/thumbnails/image',
113 'cache/thumbnails/video'
114 ]
115
116 for directory in directories:
117 new_dir = os.path.join(config_dir, directory)
118 os.mkdir(new_dir)
119
120 config_dir = self.get_cfg_dir()
121 try:
122 # Copy configuration data from a dev branch if we can
123 if os.path.exists(SOURCE_CONFIG['branch']):
124 shutil.copytree(SOURCE_CONFIG['branch'], config_dir)
125 create_cache_hierarchy(config_dir)
126 # Install configuration data from the hardcoded package path
127 elif os.path.exists(SOURCE_CONFIG['package']):
128 shutil.copytree(SOURCE_CONFIG['package'], config_dir)
129 create_cache_hierarchy(config_dir)
130 else:
131 print "Couldn't find configuration data. Execution aborted."
132 sys.exit(1)
133 except OSError:
134 print "Couldn't copy configuration data to %s. Execution aborted." \
135 % config_dir
136 sys.exit(1)
13770
138 def update_configuration(self):71 def update_configuration(self):
139 """72 '''Read configuration file again and update this object.'''
140 Read configuration files again and update this object.73 self.content.readfp(open(self.content_conf))
141 """
142 try:
143 self.content_config.readfp(open(self.content_conf))
144 self.preferences.readfp(open(self.preferences_conf))
145 except ParsingError:
146 raise Exception("Syntax error in configuration file.")
147 except IOError:
148 raise IOError("Couldn't read config file.")
14974
150 def write_content_value(self, section, option, value, sanitize=False):75 def write_content_value(self, section, option, value):
151 """Write a new value to the content configuration file."""76 """Write a new value to the content configuration file."""
152 if self.TEST_DIR and not sanitize:
153 self.taint('content', section, option)
15477
155 try:78 def write_value(section, option, value):
156 self.content_config.set(section, option, value)79 '''Actually write the value to the section and option.'''
80 self.content.set(section, option, value)
157 cfg_file = file(self.content_conf, 'w')81 cfg_file = file(self.content_conf, 'w')
158 self.content_config.write(cfg_file)82 self.content.write(cfg_file)
83
84 try:
85 write_value(section, option, value)
159 except NoSectionError:86 except NoSectionError:
160 raise Exception("No Section to set in content.conf file")87 # Provide an upgrade path to additions of new sections.
88 shutil.rmtree(self.config_dir)
89 self.resources.create_configuration()
90 self.read_config_file()
91 write_value(section, option, value)
161 except NoOptionError:92 except NoOptionError:
162 raise Exception("No Option to set in content.conf file")93 raise Exception("No Option to set in content.conf file")
16394
164 def write_preference_value(self, section, option, value, sanitize=False):95 def _get_stage_width(self):
165 """Write a new value to the preferences configuration file."""96 '''stage_width property getter.'''
166 if self.TEST_DIR and not sanitize:97 if self._stage_width:
167 self.taint('preferences', section, option)98 return self._stage_width
16899
169 try:100 self._stage_width = self.content.getint("General", "stage_width")
170 self.preferences.set(section, option, value)101 return self._stage_width
171 cfg_file = file(self.preferences_conf, 'w')102
172 self.preferences.write(cfg_file)103 def _set_stage_width(self, new_width):
173 except NoSectionError:104 '''stage_width property setter.'''
174 raise Exception("No Section to set in preferences.conf file")105 self._stage_width = new_width
175 except NoOptionError:106
176 raise Exception("No Option to set in preferences.conf file")107 stage_width = property(_get_stage_width, _set_stage_width)
177108
178 def get_stage_width(self):109 def _get_stage_height(self):
179 '''Get the stage width from the preferences'''110 '''stage_height property getter.'''
180 if self.stage_width:111 if self._stage_height:
181 return self.stage_width112 return self._stage_height
182113
183 try:114 self._stage_height = self.content.getint("General", "stage_height")
184 self.stage_width = self.preferences.getint("General", "stage_width")115 return self._stage_height
185 except (NoSectionError, NoOptionError):116
186 self.stage_width = 1366 # Default117 def _set_stage_height(self, new_height):
187118 '''stage_height property setter.'''
188 return self.stage_width119 self._stage_height = new_height
189120
190 def set_stage_width(self, new_width, sanitize=False):121 stage_height = property(_get_stage_height, _set_stage_height)
191 '''Set the stage width for the in memory instance'''122
192 # Taint data if in test mode123 @property
193 if self.TEST_DIR and not sanitize:124 def theme_path(self):
194 kwargs = {'new_width' : self.get_stage_width(),125 '''Path to the currently selected theme.'''
195 'sanitize' : True}126 theme_path = os.path.join(self.data_dir, 'themes')
196 self.taint_in_memory(self.set_stage_width, kwargs)127 theme = self.content.get("General", "theme")
197128 return os.path.join(theme_path, theme)
198 self.stage_width = new_width129
199130 @property
200 def get_stage_height(self):
201 '''Get the stage height from the preferences'''
202 if self.stage_height:
203 return self.stage_height
204
205 try:
206 self.stage_height = self.preferences.getint("General",
207 "stage_height")
208 except (NoSectionError, NoOptionError):
209 self.stage_height = 768 # Default
210
211 return self.stage_height
212
213 def set_stage_height(self, new_height, sanitize=False):
214 '''Set the stage height for the in memory instance'''
215 # Taint data if in test mode
216 if self.TEST_DIR and not sanitize:
217 kwargs = {'new_height' : self.get_stage_height(),
218 'sanitize' : True}
219 self.taint_in_memory(self.set_stage_height, kwargs)
220
221 self.stage_height = new_height
222
223 def get_theme_path(self):
224 """
225 Get absolute path of the used theme.
226 @return: string (path)
227 """
228 theme_path = os.path.join(self.get_cfg_dir(), 'themes')
229 try:
230 theme = self.preferences.get("General", "theme")
231 except (NoSectionError, NoOptionError):
232 return os.path.join(theme_path, "Default")
233
234 if os.path.exists(os.path.join(theme_path, theme)):
235 return os.path.join(theme_path, theme)
236 else:
237 return os.path.join(theme_path, "Default")
238
239 def tray_icon_enabled(self):131 def tray_icon_enabled(self):
240 """132 '''Boolean to indicate whether or not to show the tray icon.'''
241 Display tray icon.133 return self.content.getboolean("General", "display_icon")
242 @return: boolean134
243 """135 @property
244 try:136 def feed_fetch_interval(self):
245 result = self.preferences.getboolean("General", "display_icon")137 '''Time interval (in minutes) for feed fetching.'''
246 except (NoSectionError, NoOptionError):138 return self.content.getint("RSS", "fetch_interval")
247 return False139
248 return result140 @property
249141 def port(self):
250 def get_feed_fetch_interval(self):142 '''Server's port number.'''
251 """143 return self.content.getint("General", "backend_port")
252 Get time interval (in minutes) for feed fetching.144
253 @return: Integer145 @property
254 """146 def media_folders(self):
255 try:147 '''Return a list of folders for media.'''
256 result = self.content_config.getint("RSS", "fetch_interval")148 media = self.content.get("Media", "folders")
257 except (NoSectionError, NoOptionError):149 return self._is_valid_media_folder(media.split(';'))
258 return 60 # Default is one hour150
259 return result151 @property
260152 def feeds(self):
261 def get_port(self):153 '''List of feeds.'''
262 """154 rss_feeds = self.content.get("RSS", "feeds")
263 Get backend server's port number.155 return rss_feeds.split(';')
264 @return: Integer156
265 """157 @property
266 try:158 def weather_location(self):
267 result = self.preferences.getint("General", "backend_port")159 '''User's weather location.'''
268 except (NoSectionError, NoOptionError):160 return self.content.get("Weather", "location")
269 return 45054 # Default port161
270 return result162 @property
271163 def display_weather_in_client(self):
272 def get_video_folders(self):164 '''Boolean to tell if weather should be displayed.'''
273 """165 return self.content.getboolean("Weather", "display_in_menu")
274 Get list of video folders166
275 @return:String Array167 @property
276 """168 def download_metadata(self):
277 try:169 '''Boolean to tell if metadata should be downloaded.'''
278 video_list = self.content_config.get("Videos", "folders")170 return self.content.getboolean("Media", "download_metadata")
279 result = self._is_valid_media_folder(video_list.split(';'))171
280 except (NoSectionError, NoOptionError):172 @property
281 return []
282 return result
283
284 def get_music_folders(self):
285 """
286 Get list of music folders
287 @return: String Array
288 """
289 try:
290 music_list = self.content_config.get("Music", "folders")
291 result = music_list.split(';')
292 except (NoSectionError, NoOptionError):
293 return []
294 return result
295
296 def get_image_folders(self):
297 """
298 Get list of image folders
299 @return: String Array
300 """
301 try:
302 image_list = self.content_config.get("Images", "folders")
303 result = self._is_valid_media_folder(image_list.split(';'))
304 except (NoSectionError, NoOptionError):
305 return []
306 return result
307
308 def get_feeds(self):
309 """
310 Get list of feeds.
311 @return: String Array
312 """
313 try:
314 rss_feeds = self.content_config.get("RSS", "feeds")
315 result = rss_feeds.split(';')
316 except (NoSectionError, NoOptionError):
317 return []
318 return result
319
320 def get_weather_location(self):
321 """
322 Get list of weather location codes.
323 @return: List of strings
324 """
325 try:
326 location = self.content_config.get("Weather", "location")
327 except (NoSectionError, NoOptionError):
328 location = ''
329 return location
330
331 def display_weather_in_frontend(self):
332 """
333 Should we display weather in frontend
334 @return: Boolean
335 """
336 try:
337 result = self.content_config.getboolean("Weather",
338 "display_in_menu")
339 except (NoSectionError, NoOptionError):
340 return False
341 return result
342
343 def display_cd_eject_in_frontend(self):
344 """
345 Should we display the cd eject button in frontend
346 @return: Boolean
347 """
348 try:
349 result = self.content_config.getboolean("CD",
350 "display_eject_in_menu")
351 except (NoSectionError, NoOptionError):
352 return False
353 return result
354
355 def download_video_metadata(self):
356 """
357 Get True if video metadata should be downloaded, otherwise False
358 """
359 try:
360 result = self.content_config.getboolean("Videos",
361 "download_metadata")
362 except (NoSectionError, NoOptionError):
363 return False
364 return result
365
366 def download_album_art(self):173 def download_album_art(self):
367 """174 '''Boolean to tell if album art should be downloaded.'''
368 Download album art from the Internet.175 return self.content.getboolean("Media", "download_album_art")
369 @return: boolean
370 """
371 try:
372 result = self.content_config.getboolean("Music",
373 "download_album_art")
374 except (NoSectionError, NoOptionError):
375 return False
376 return result
377176
177 @property
378 def download_lyrics(self):178 def download_lyrics(self):
379 """179 '''Boolean to tell if lyrics should be downloaded.'''
380 Download song lyrics from the Internet.180 return self.content.getboolean("Media", "download_lyrics")
381 @return: boolean181
382 """182 @property
383 try:
384 result = self.content_config.getboolean("Music", "download_lyrics")
385 except (NoSectionError, NoOptionError):
386 return False
387 return result
388
389 def display_hidden_files_folders(self):
390 """If True hidden files and folders will be added to image library."""
391 try:
392 result = self.content_config.getboolean("Images",
393 "display_hidden_files_folders")
394 except (NoSectionError, NoOptionError):
395 return False
396 return result
397
398 def show_effects(self):183 def show_effects(self):
399 """184 '''Boolean to tell if effects should be shown.'''
400 Use animations on user interface.185 return self.content.getboolean("General", "show_effects")
401 @return: boolean
402 """
403 try:
404 result = self.preferences.getboolean("General", "show_effects")
405 except (NoSectionError, NoOptionError):
406 return False
407 return result
408186
187 @property
409 def transition_effect(self):188 def transition_effect(self):
410 """189 '''Internal name of the user's selected screen transition style.'''
411 Which transition effect should be used when switching screens.190 return self.content.get("General", "transition_effect")
412 @return: string (name of the effect)191
413 """192 @property
414 try:193 def theme_name(self):
415 result = self.preferences.get("General", "transition_effect")194 '''Name of the current theme.'''
416 except (NoSectionError, NoOptionError):195 return self.content.get("General", "theme")
417 return False196
418 return result197 @property
419
420 def get_theme_name(self):
421 """
422 Get the name of the current theme.
423 @return: string (theme name)
424 """
425 try:
426 result = self.preferences.get("General", "theme")
427 except (NoSectionError, NoOptionError):
428 result = "Default"
429 return result
430
431 def start_in_fullscreen(self):198 def start_in_fullscreen(self):
432 '''Boolean to determine whether to start in fullscreen mode or not.'''199 '''Boolean to determine whether to start in fullscreen mode or not.'''
433 try:200 return self.content.getboolean("General", "start_in_fullscreen")
434 result = self.preferences.getboolean("General",
435 "start_in_fullscreen")
436 except (NoSectionError, NoOptionError):
437 return True
438 return result
439201
202 @property
440 def start_auto_server(self):203 def start_auto_server(self):
441 """204 '''Boolean to tell if the server should be auto started.'''
442 Return True if backend server should auto start205 return self.content.getboolean("General", "start_server_auto")
443 @return: boolean
444 """
445 try:
446 result = self.preferences.getboolean("General",
447 "start_server_auto")
448 except (NoSectionError, NoOptionError):
449 return False
450 return result
451206
207 @property
452 def history_size(self):208 def history_size(self):
453 """209 '''Number of screens to hold in history.'''
454 Get screen history size. This determines how many screen are kept in210 return self.content.getint("General", "history_size")
455 memory when scrolling.
456 @return: Integer
457 """
458 try:
459 result = self.preferences.getint("General", "history_size")
460 except (NoSectionError, NoOptionError):
461 return 10
462 return result
463211
464 def get_slideshow_step(self):212 @property
465 """213 def slideshow_step(self):
466 Get the slideshow step in seconds used by the photo slideshow214 '''Amount of seconds before the slideshow steps to the next image.'''
467 @return: Integer215 return self.content.getint("Photographs", "slideshow_step")
468 """
469 try:
470 result = self.preferences.getint("Photographs", "slideshow_step")
471 except (NoSectionError, NoOptionError):
472 return 5
473 return result
474216
475 # Implements MessageHandler interface217 # Implements MessageHandler interface
476 def handleMessage(self, message):218 def handleMessage(self, message):
@@ -480,73 +222,99 @@
480 """222 """
481 if message.get_type() == MessageType.CONTENT_CONF_UPDATED:223 if message.get_type() == MessageType.CONTENT_CONF_UPDATED:
482 self.update_configuration()224 self.update_configuration()
483 elif message.get_type() == MessageType.PREFERENCES_CONF_UPDATED:
484 self.update_configuration()
485
486 def get_cfg_dir(self):
487 """
488 Get the entertainer configuration dir.
489 @return: strings
490 """
491 return self.cfg_dir
492
493 def taint(self, kind, section, option):
494 '''Taint any data that is written to config files so that it can be
495 returned to a known state later'''
496 if kind == 'content':
497 value = self.content_config.get(section, option)
498 elif kind == 'preferences':
499 value = self.preferences.get(section, option)
500 else:
501 raise Exception(
502 "Taint type must be either 'content' or 'preferences'")
503
504 key = "%s %s %s" % (kind, section, option)
505 self._tainted[key] = value
506
507 def taint_in_memory(self, callback, kwargs):
508 '''Taint any data that is stored in memory. Callback is used as the
509 key to a tainted_in_memory dictionary whose stored value is a
510 dictionary of arguments (kwargs) needed to restore the original
511 value'''
512 self._tainted_in_memory[callback] = kwargs
513
514 def sanitize(self):
515 '''Restore all tainted values to their original state and clear the
516 tainted dictionary'''
517 # Sanitize values in config files
518 for key in self._tainted.keys():
519 # tokens will contain type, section, and option
520 tokens = key.split()
521 if tokens[0] == 'content':
522 # Write in the sanitize mode so taint checking will be skipped
523 self.write_content_value(tokens[1], tokens[2],
524 self._tainted[key], sanitize=True)
525 elif tokens[0] == 'preferences':
526 # Write in the sanitize mode so taint checking will be skipped
527 self.write_preference_value(tokens[1], tokens[2],
528 self._tainted[key], sanitize=True)
529
530 self.update_configuration()
531 self._tainted = {}
532
533 # Sanitize values in memory
534 for callback in self._tainted_in_memory.keys():
535 kwargs = self._tainted_in_memory[callback]
536 callback(**kwargs)
537
538 self._tainted_in_memory = {}
539225
540 def _is_valid_media_folder(self, folder_list):226 def _is_valid_media_folder(self, folder_list):
541 """Return a folder list where eventual cache folders are removed"""227 """Return a folder list where eventual cache folders are removed"""
542 cache_dir = os.path.join(self.get_cfg_dir(), 'cache')
543 valid_folder_list = []228 valid_folder_list = []
544229
545 for folder in folder_list:230 for folder in folder_list:
546 common_prefix = os.path.commonprefix([cache_dir, folder])231 common_prefix = os.path.commonprefix([self.cache_dir, folder])
547 if common_prefix != cache_dir:232 if common_prefix != self.cache_dir:
548 # folder is not a subfolder of cache_dir we accept it233 # if folder is not a subfolder of cache_dir, we accept it
549 valid_folder_list.append(folder)234 valid_folder_list.append(folder)
550235
551 return valid_folder_list236 return valid_folder_list
552237
238
239class Resources(object):
240 '''A Wrapper for the XDG directories. Also handles creation of a new setup
241 if the Entertainer directories within the XDG directories are missing. This
242 class is meant solely to support Configuration and should NOT be publicly
243 used because of testing conflicts that would occur.'''
244
245 def __init__(self, resource='entertainer', config_testing_dir=None):
246 if config_testing_dir is None:
247 self.cache_dir = os.path.join(BaseDirectory.xdg_cache_home,
248 resource)
249 self.config_dir = os.path.join(BaseDirectory.xdg_config_home,
250 resource)
251 self.data_dir = os.path.join(BaseDirectory.xdg_data_home, resource)
252 else:
253 # Running in the test suite so don't create the XDG locations.
254 self.cache_dir = os.path.join(config_testing_dir, 'cache')
255 self.config_dir = os.path.join(config_testing_dir, 'config')
256 self.data_dir = os.path.join(config_testing_dir, 'data')
257
258 # Ensure that the directories exist.
259 if not os.path.exists(self.cache_dir):
260 self.create_cache_hierarchy()
261 if not os.path.exists(self.config_dir):
262 self.create_configuration()
263 if not os.path.exists(self.data_dir):
264 self.create_initial_data()
265
266 def create_cache_hierarchy(self):
267 '''Create the cache hierarchy that is assumed to exist.'''
268 os.makedirs(os.path.join(self.cache_dir, 'album_art'))
269 os.makedirs(os.path.join(self.cache_dir, 'movie_art'))
270 os.makedirs(os.path.join(self.cache_dir, 'thumbnails', 'image'))
271 os.makedirs(os.path.join(self.cache_dir, 'thumbnails', 'video'))
272
273 def create_configuration(self):
274 '''Create the user's configuration area and populate with the default
275 content configuration.'''
276
277 # Copy configuration data from a dev branch if we can
278 dev_config = os.path.abspath(os.path.dirname(__file__) + '/../cfg')
279 if os.path.exists(dev_config):
280 shutil.copytree(dev_config, self.config_dir)
281 return
282
283 # Must be a proper installation so install from the system location.
284 installed_config = os.path.join(self.installed_data_dir, 'cfg')
285 shutil.copytree(installed_config, self.config_dir)
286
287 def create_initial_data(self):
288 '''Create the initial data directory and populate with the default data
289 used by Entertainer.'''
290 os.mkdir(self.data_dir)
291
292 # Copy configuration data from a dev branch if we can
293 dev_config = os.path.abspath(os.path.dirname(__file__) + '/../themes')
294 if os.path.exists(dev_config):
295 shutil.copytree(dev_config, os.path.join(self.data_dir, 'themes'))
296 return
297
298 # Must be a proper installation so install from the system location.
299 themes_dir = os.path.join(self.installed_data_dir, 'themes')
300 shutil.copytree(themes_dir, os.path.join(self.data_dir, 'themes'))
301
302 @property
303 def installed_data_dir(self):
304 '''Since different distros decide on the install path differently, scan
305 through the system data directories to find where Entertainer was
306 installed.'''
307
308 # Get rid of the user's home data directory because we don't want it.
309 system_data_dirs = [data_dir for data_dir in BaseDirectory.xdg_data_dirs
310 if not data_dir.startswith(BaseDirectory.xdg_data_home)]
311
312 installed_data_dir = None
313 for directory in system_data_dirs:
314 possible_data_dir = os.path.join(directory, 'entertainer')
315 if os.path.exists(possible_data_dir):
316 installed_data_dir = possible_data_dir
317 break
318
319 return installed_data_dir
320
553321
=== renamed directory 'entertainerlib/backend/core/db' => 'entertainerlib/db'
=== modified file 'entertainerlib/db/connection.py'
--- entertainerlib/backend/core/db/connection.py 2009-05-11 23:37:27 +0000
+++ entertainerlib/db/connection.py 2010-01-23 03:34:11 +0000
@@ -27,7 +27,7 @@
27 'PhotoImage': '''CREATE TABLE `photoimage` (27 'PhotoImage': '''CREATE TABLE `photoimage` (
28 id INTEGER PRIMARY KEY AUTOINCREMENT,28 id INTEGER PRIMARY KEY AUTOINCREMENT,
29 filename TEXT,29 filename TEXT,
30 thumb VARCHAR(32),30 thumbnail VARCHAR(32),
31 title TEXT,31 title TEXT,
32 description TEXT,32 description TEXT,
33 creation_date DATETIME,33 creation_date DATETIME,
3434
=== modified file 'entertainerlib/db/models.py'
--- entertainerlib/backend/core/db/models.py 2009-04-28 23:54:49 +0000
+++ entertainerlib/db/models.py 2010-01-23 03:34:11 +0000
@@ -6,6 +6,7 @@
6from storm.properties import Bool, DateTime, Int, Unicode6from storm.properties import Bool, DateTime, Int, Unicode
7from storm.references import Reference, ReferenceSet7from storm.references import Reference, ReferenceSet
88
9
9class BaseModel(Storm):10class BaseModel(Storm):
10 '''Abstract class from which all Entertainer models inherit.'''11 '''Abstract class from which all Entertainer models inherit.'''
1112
@@ -96,7 +97,7 @@
96 __storm_table__ = 'photoimage'97 __storm_table__ = 'photoimage'
97 id = Int(primary=True)98 id = Int(primary=True)
98 filename = Unicode()99 filename = Unicode()
99 thumb = Unicode()100 thumbnail = Unicode()
100 title = Unicode()101 title = Unicode()
101 description = Unicode()102 description = Unicode()
102 creation_date = DateTime()103 creation_date = DateTime()
@@ -108,7 +109,7 @@
108 ret = {109 ret = {
109 'id': self.id,110 'id': self.id,
110 'filename': self.filename,111 'filename': self.filename,
111 'thumb': self.thumb,112 'thumbnail': self.thumbnail,
112 'title': self.title,113 'title': self.title,
113 'description': self.description,114 'description': self.description,
114 'creation_date': self.creation_date,115 'creation_date': self.creation_date,
115116
=== added file 'entertainerlib/dialog.py'
--- entertainerlib/dialog.py 1970-01-01 00:00:00 +0000
+++ entertainerlib/dialog.py 2010-01-23 03:34:11 +0000
@@ -0,0 +1,1113 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Dialogs for Entertainer.'''
3# pylint: disable-msg=C0302
4
5import os
6import shutil
7import socket
8import sys
9import tarfile
10
11import gtk
12
13from entertainerlib.backend.core.message import Message
14from entertainerlib.backend.core.message_bus_proxy import MessageBusProxy
15from entertainerlib.backend.core.message_type_priority import MessageType
16from entertainerlib.configuration import Configuration
17from entertainerlib.logger import Logger
18from entertainerlib.gui.theme import Theme
19from entertainerlib.backend.components.feeds.feed_utils import (OPMLParser,
20 FeedConfigTools)
21from entertainerlib.weather import Weather
22
23
24class ManagerDialog:
25 """
26 This is a content management tool for Entertainer media center application.
27 """
28
29 # Temporary storage for entered URL
30 url = ""
31 UI_DIR = os.path.join(os.path.dirname(__file__), "uis")
32
33 def __init__(self, stand_alone):
34 """
35 Initialize content management dialog
36 @param stand_alone: Boolean, Is this dialog running as a stand alone
37 process
38 """
39 self.stand_alone = stand_alone
40 self.config = Configuration()
41 self.themes = []
42 self.weather = Weather()
43
44 # Load UI with gtk.Builder
45 uifile = os.path.join(self.UI_DIR, 'manager.ui')
46 self.builder = gtk.Builder()
47 self.builder.set_translation_domain('entertainer')
48 self.builder.add_from_file(uifile)
49
50 # Get content management dialog and bind signal callbacks
51 self.dialog = self.builder.get_object("ManagerDialog")
52 if (self.dialog):
53 callback_dic = {
54 # Dialog-wide callbacks
55 "on_button_open_list_clicked" :
56 self.on_button_open_list_clicked,
57 "on_close_button_clicked" : self.on_close_button_clicked,
58 "on_ManagerDialog_destroy" : self.on_dialog_closed,
59
60 # Media tab
61 "on_button_remove_media_clicked" :
62 self.on_button_remove_media_clicked,
63 "on_button_add_media_clicked" :
64 self.on_button_add_media_clicked,
65 "on_button_edit_media_clicked" :
66 self.on_button_edit_media_clicked,
67 "on_checkbutton_video_metadata_toggled" :
68 self.on_checkbutton_video_metadata_toggled,
69 "on_lyrics_checkbox_toggled" : self.on_lyrics_checkbox_toggled,
70 "on_art_checkbox_toggled" : self.on_art_checkbox_toggled,
71 "on_button_media_rebuild_clicked" :
72 self.on_button_media_rebuild_clicked,
73
74 # Feed tab
75 "on_button_add_feed_clicked" :
76 self.on_button_add_feed_clicked,
77 "on_button_remove_feed_clicked" :
78 self.on_button_remove_feed_clicked,
79 "on_button_edit_feed_clicked" :
80 self.on_button_edit_feed_clicked,
81 "on_fetch_interval_spinbutton_value_changed" :
82 self.on_fetch_interval_spinbutton_value_changed,
83 "on_url_dialog_delete_event" : self.on_url_dialog_delete_event,
84 "on_url_dialog_ok_button_clicked" :
85 self.on_url_dialog_ok_button_clicked,
86 "on_url_dialog_cancel_button_clicked" :
87 self.on_url_dialog_cancel_button_clicked,
88 "on_button_feed_rebuild_clicked" :
89 self.on_button_feed_rebuild_clicked,
90
91 # Weather tab
92 "on_button_add_weather_clicked" :
93 self.on_button_add_weather_clicked,
94 "on_button_remove_weather_clicked" :
95 self.on_button_remove_weather_clicked,
96 "on_weather_display_checkbox_toggled" :
97 self.on_weather_display_checkbox_toggled,
98 "on_location_find_button_clicked" :
99 self.on_location_find_button_clicked,
100 "on_location_cancel_button_clicked" :
101 self.on_location_cancel_button_clicked,
102 "on_location_add_button_clicked" :
103 self.on_location_add_button_clicked,
104 "on_location_entry_activate" : self.on_location_entry_activate,
105
106 # User Interface tab
107 "on_theme_list_cursor_changed" :
108 self.on_theme_list_cursor_changed,
109 "on_theme_add_button_clicked" :
110 self.on_theme_add_button_clicked,
111 "on_theme_remove_button_clicked" :
112 self.on_theme_remove_button_clicked,
113 "on_checkbutton_effects_toggled" :
114 self.on_checkbutton_effects_toggled,
115 "on_combobox_effects_changed" :
116 self.on_combobox_effects_changed,
117
118 # General tab
119 "on_checkbutton_fullscreen_toggled" :
120 self.on_checkbutton_fullscreen_toggled,
121 "on_checkbutton_autostart_toggled" :
122 self.on_checkbutton_autostart_toggled,
123 "on_checkbutton_systray_icon_toggled" :
124 self.on_checkbutton_systray_icon_toggled,
125 "on_spinbutton_slideshow_step_value_changed":
126 self.on_spinbutton_slideshow_step_value_changed
127 }
128
129 self.builder.connect_signals(callback_dic)
130
131 # Initialize dialog widgets with correct values and show dialog
132 self.init_dialog_values_from_configure_file()
133 self.dialog.resize(500, 300)
134 self.dialog.show()
135
136 # Initialize location list in search dialog
137 result_list = self.builder.get_object("location_results_treeview")
138 store = gtk.ListStore(str)
139 result_list.set_model(store)
140 cell_renderer = gtk.CellRendererText()
141 column = gtk.TreeViewColumn(_("Location"), cell_renderer, text=0)
142 result_list.append_column(column)
143
144 def on_dialog_closed(self, widget):
145 """Callback function for dialog's close button"""
146 try:
147 proxy = MessageBusProxy(client_name = "Manager GUI")
148 proxy.connectToMessageBus()
149 proxy.sendMessage(Message(MessageType.CONTENT_CONF_UPDATED))
150 proxy.disconnectFromMessageBus()
151 except socket.error:
152 error = gtk.MessageDialog(
153 None, gtk.DIALOG_MODAL,
154 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
155 "Entertainer backend is not running. "
156 "Cache cannot be rebuilt."
157 ))
158 error.run()
159 error.destroy()
160
161 if(self.stand_alone):
162 self.dialog.hide()
163 self.dialog.destroy()
164 gtk.main_quit()
165 else:
166 self.dialog.hide()
167 self.dialog.destroy()
168
169 def on_close_button_clicked(self, widget):
170 """Callback function for dialog's close button"""
171 if(self.stand_alone):
172 self.dialog.hide()
173 self.dialog.destroy()
174 gtk.main_quit()
175 else:
176 self.dialog.hide()
177 self.dialog.destroy()
178
179 def on_button_add_media_clicked(self, widget):
180 """Opens add URL dialog. """
181 widget = self.builder.get_object("treeview_media")
182 model = widget.get_model()
183 # Open "Select folder" dialog
184 dialog = gtk.FileChooserDialog(_("Select folder"), None,
185 gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
186 (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,
187 gtk.STOCK_OPEN,gtk.RESPONSE_OK),
188 None)
189 status = dialog.run()
190 # If folder was selected we add it to model and update config file
191 if(status == gtk.RESPONSE_OK):
192 self.add_to_model_and_config(dialog.get_current_folder(), model,
193 self.media_folders, "Media")
194 dialog.destroy()
195
196 def on_button_remove_media_clicked(self, widget):
197 """Remove currently selected folder from media folders"""
198 widget = self.builder.get_object("treeview_media")
199 model = widget.get_model()
200 selection = widget.get_selection().get_selected()
201 if selection[1] == None:
202 return
203 rm_folder = model.get_value(selection[1], 0)
204 self.media_folders.remove(rm_folder)
205 str_folders = ";".join(self.media_folders)
206 self.config.write_content_value("Media", "folders", str_folders)
207 model.remove(selection[1])
208
209 def on_button_edit_media_clicked(self, widget):
210 """Edit currently selected folder"""
211 widget = self.builder.get_object("treeview_media")
212 url_dialog = self.builder.get_object("url_dialog")
213 url_entry = self.builder.get_object("url_entry")
214 model = widget.get_model()
215 selection = widget.get_selection().get_selected()
216 if selection[1] == None:
217 return
218 folder = model.get_value(selection[1], 0)
219 url_entry.set_text(folder)
220 url_dialog.set_title(_("Edit URL"))
221 status = url_dialog.run()
222 if status == gtk.RESPONSE_OK and os.path.exists(self.url):
223 # Update list model
224 model.set_value(selection[1], 0, self.url)
225 # Update configure file
226 pos = self.media_folders.index(folder)
227 self.media_folders.remove(folder)
228 self.media_folders.insert(pos, self.url)
229 str_folders = ";".join(self.media_folders)
230 self.config.write_content_value("Media", "folders",
231 str_folders)
232
233 def on_checkbutton_autostart_toggled(self, widget):
234 '''Server autostart checkbox toggled.'''
235 self.config.write_content_value("General", "start_server_auto",
236 widget.get_active())
237
238 def on_checkbutton_fullscreen_toggled(self, widget):
239 '''Start in fullscreen checkbox toggled.'''
240 self.config.write_content_value("General", "start_in_fullscreen",
241 widget.get_active())
242
243 def on_checkbutton_systray_icon_toggled(self, widget):
244 """System Tray Icon checkbox toggled"""
245 self.config.write_content_value("General", "display_icon",
246 widget.get_active())
247
248 def on_checkbutton_video_metadata_toggled(self, widget):
249 """
250 Download video file metadata from internet
251 @param widget: GTK-Widget
252 """
253 self.config.write_content_value("Media", "download_metadata",
254 widget.get_active())
255
256 def on_button_add_feed_clicked(self, widget):
257 """Opens add feed dialog. """
258 widget = self.builder.get_object("treeview_feeds")
259 url_dialog = self.builder.get_object("url_dialog")
260 model = widget.get_model()
261 # Open dialog
262 url_dialog.set_title(_("Add RSS-feed"))
263 status = url_dialog.run()
264 # If folder was selected we add it to model and update config file
265 if(status == gtk.RESPONSE_OK):
266 model.append([self.url])
267 self.feeds.append(self.url)
268 str_folders = ";".join(self.feeds)
269 self.config.write_content_value("RSS", "feeds", str_folders)
270
271 def on_button_remove_feed_clicked(self, widget):
272 """Remove currently selected reed from RSS-feeds"""
273 widget = self.builder.get_object("treeview_feeds")
274 model = widget.get_model()
275 selection = widget.get_selection().get_selected()
276 if selection[1] == None:
277 return
278 rm_folder = model.get_value(selection[1], 0)
279 self.feeds.remove(rm_folder)
280 str_folders = ";".join(self.feeds)
281 self.config.write_content_value("RSS", "feeds", str_folders)
282 model.remove(selection[1])
283
284 def on_button_edit_feed_clicked(self, widget):
285 """Edit currently selected feed"""
286 widget = self.builder.get_object("treeview_feeds")
287 url_dialog = self.builder.get_object("url_dialog")
288 url_entry = self.builder.get_object("url_entry")
289 model = widget.get_model()
290 selection = widget.get_selection().get_selected()
291 if selection[1] == None:
292 return
293 feed = model.get_value(selection[1], 0)
294 url_entry.set_text(feed)
295 url_dialog.set_title(_("Edit feed"))
296 status = url_dialog.run()
297 if status == gtk.RESPONSE_OK:
298 # Update list model
299 model.set_value(selection[1], 0, self.url)
300 # Update configure file
301 pos = self.feeds.index(feed)
302 self.feeds.remove(feed)
303 self.feeds.insert(pos, self.url)
304 str_feeds = ";".join(self.feeds)
305 self.config.write_content_value("RSS", "feeds", str_feeds)
306
307 def on_button_open_list_clicked(self, widget):
308 """Opens the open feed source dialog"""
309 open_dialog = OpenFeedSourceDialog(
310 self.builder.get_object("treeview_feeds"), self.feeds)
311 open_dialog.dialog.connect("destroy",
312 open_dialog.on_closeButton_clicked)
313
314 def on_fetch_interval_spinbutton_value_changed(self, widget):
315 self.config.write_content_value("RSS", "fetch_interval",
316 widget.get_value_as_int())
317
318 def on_spinbutton_slideshow_step_value_changed(self, widget):
319 """Activation of slideshow effects"""
320 self.config.write_content_value("Photographs", "slideshow_step",
321 int(widget.get_value()))
322
323 def on_lyrics_checkbox_toggled(self, widget):
324 self.config.write_content_value("Media", "download_lyrics",
325 widget.get_active())
326
327 def on_art_checkbox_toggled(self, widget):
328 self.config.write_content_value("Media", "download_album_art",
329 widget.get_active())
330
331 def on_url_dialog_ok_button_clicked(self, widget):
332 """URL dialog OK button pressed. Sets self.url"""
333 url_dialog = self.builder.get_object("url_dialog")
334 url_entry = self.builder.get_object("url_entry")
335 url_dialog.hide()
336 self.url = url_entry.get_text()
337 url_entry.set_text("")
338 url_dialog.response(gtk.RESPONSE_OK)
339
340 def on_url_dialog_cancel_button_clicked(self, widget):
341 """URL dialog cancelled. Hides dialog"""
342 url_dialog = self.builder.get_object("url_dialog")
343 url_entry = self.builder.get_object("url_entry")
344 url_dialog.hide()
345 url_entry.set_text("")
346 url_dialog.response(gtk.RESPONSE_CANCEL)
347
348 def on_url_dialog_delete_event(self, widget, data):
349 """Dialog's X clicked. Hides dialog"""
350 url_dialog = self.builder.get_object("url_dialog")
351 url_entry = self.builder.get_object("url_entry")
352 url_dialog.hide()
353 url_entry.set_text("")
354 url_dialog.response(gtk.RESPONSE_CANCEL)
355 return True
356
357 def on_button_add_weather_clicked(self, widget):
358 """
359 Open location search dialog
360 @param widget: GTK-Widget
361 """
362 location_dialog = self.builder.get_object("weather_search_dialog")
363 location_dialog.set_title(_("Add location"))
364
365 # Clear results
366 result_list = self.builder.get_object("location_results_treeview")
367 model = result_list.get_model()
368 model.clear()
369
370 status = location_dialog.run()
371 if(status == gtk.RESPONSE_OK):
372 print "Added"
373
374 def on_button_remove_weather_clicked(self, widget):
375 """
376 Remove currently selected weather location from the location list
377 @param widget: GTK-Widget
378 """
379 widget = self.builder.get_object("treeview_locations")
380 model = widget.get_model()
381 self.weather_locations = []
382 str_folders = ""
383 self.config.write_content_value("Weather", "location", str_folders)
384 model.clear()
385
386 def on_weather_display_checkbox_toggled(self, widget):
387 """
388 Checkbox that defines should we use weather conditions
389 @param widget: GTK-Widget
390 """
391 self.config.write_content_value("Weather", "display_in_menu",
392 widget.get_active())
393 if widget.get_active():
394 self.builder.get_object("button_add_weather").set_sensitive(True)
395 self.builder.get_object(
396 "button_remove_weather").set_sensitive(True)
397 self.builder.get_object("treeview_locations").set_sensitive(True)
398 else:
399 self.builder.get_object("button_add_weather").set_sensitive(False)
400 self.builder.get_object(
401 "button_remove_weather").set_sensitive(False)
402 self.builder.get_object("treeview_locations").set_sensitive(False)
403
404 def on_location_find_button_clicked(self, widget):
405 """
406 Find location by search string
407 @param widget: GTK-Widget
408 """
409 add_button = self.builder.get_object("location_add_button")
410 search_term = self.builder.get_object("location_entry").get_text()
411 result_list = self.builder.get_object("location_results_treeview")
412 model = result_list.get_model()
413 model.clear()
414 if search_term != "":
415 self.weather.location = search_term
416 self.weather.refresh()
417 results = self.weather.forecasts
418 if len(results) > 0:
419 add_button.set_sensitive(True)
420 model.append([search_term])
421 result_list.set_cursor(0)
422 else:
423 model.clear()
424 model.append([_("Location Not Found!")])
425 add_button.set_sensitive(False)
426
427 def on_location_cancel_button_clicked(self, widget):
428 """
429 Close location search dialog without taking any actions.0
430 @param widget: GTK-Widget
431 """
432 location_dialog = self.builder.get_object("weather_search_dialog")
433 location_entry = self.builder.get_object("location_entry")
434 location_dialog.hide()
435 location_entry.set_text("")
436 location_dialog.response(gtk.RESPONSE_CANCEL)
437
438 def on_location_add_button_clicked(self, widget):
439 """
440 Add selected location to location list and close search dialog
441 @param widget: GTK-Widget
442 """
443 self.weather_locations = []
444 result_list = self.builder.get_object("location_results_treeview")
445 model = result_list.get_model()
446 selection = result_list.get_selection().get_selected()
447 if selection[1] == None:
448 return
449 location_string = model.get_value(selection[1], 0)
450
451 location_list = self.builder.get_object("treeview_locations")
452 loc_model = location_list.get_model()
453 loc_model.clear()
454 loc_model.append([location_string])
455
456 self.weather_locations.append(location_string)
457 str_locations = ";".join(self.weather_locations)
458 self.config.write_content_value("Weather", "location", str_locations)
459
460 location_dialog = self.builder.get_object("weather_search_dialog")
461 location_entry = self.builder.get_object("location_entry")
462 location_dialog.hide()
463 location_entry.set_text("")
464 location_dialog.response(gtk.RESPONSE_CANCEL)
465
466 def on_location_entry_activate(self, widget):
467 """
468 User hit enter on location entry to start search
469 @param widget: GTK-Widget
470 """
471 self.on_location_find_button_clicked(widget)
472
473 def on_button_media_rebuild_clicked(self, widget):
474 '''Rebuild media cache requested.'''
475 try:
476 proxy = MessageBusProxy(client_name = "Manager GUI")
477 proxy.connectToMessageBus()
478 proxy.sendMessage(Message(MessageType.REBUILD_IMAGE_CACHE))
479 proxy.sendMessage(Message(MessageType.REBUILD_MUSIC_CACHE))
480 proxy.sendMessage(Message(MessageType.REBUILD_VIDEO_CACHE))
481 proxy.disconnectFromMessageBus()
482 except socket.error:
483 error = gtk.MessageDialog(
484 None, gtk.DIALOG_MODAL,
485 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
486 "Entertainer backend is not running. "
487 "Cache cannot be rebuilt."
488 ))
489 error.run()
490 error.destroy()
491
492 def on_button_feed_rebuild_clicked(self, widget):
493 """
494 Rebuild feed cache requested
495 @param widget: GTK-Widget
496 """
497 #We need the user to confirm the rebuild feed cache request
498 dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING,
499 gtk.BUTTONS_OK_CANCEL,
500 _("This will completely remove any feed entries in the cache!"))
501 status = dialog.run()
502 #If user has ok'd the request send the message to the message bus
503 if(status == gtk.RESPONSE_OK):
504 try:
505 proxy = MessageBusProxy(client_name = "Manager GUI")
506 proxy.connectToMessageBus()
507 proxy.sendMessage(Message(MessageType.REBUILD_FEED_CACHE))
508 proxy.disconnectFromMessageBus()
509 except socket.error:
510 error = gtk.MessageDialog(
511 None, gtk.DIALOG_MODAL,
512 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
513 "Entertainer backend is not running. "
514 "Cache cannot be rebuilt."
515 ))
516 error.run()
517 error.destroy()
518
519 dialog.destroy()
520
521 def on_theme_add_button_clicked(self, widget):
522 """Add theme button clicked"""
523 themelist = self.builder.get_object("theme_list")
524 model = themelist.get_model()
525 # Open "Select folder" dialog
526 dialog = gtk.FileChooserDialog(_("Select theme package file"),
527 None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL,
528 gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK), None)
529 file_filter = gtk.FileFilter()
530 file_filter.set_name(_("Theme package (tar.gz)"))
531 file_filter.add_pattern("*.tar.gz")
532 dialog.add_filter(file_filter)
533 status = dialog.run()
534
535 # If theme was selected with file chooser
536 if(status == gtk.RESPONSE_OK):
537 package = dialog.get_filename()
538 tar = tarfile.open(package, 'r:gz') # Open tar.gz package
539
540 # Make sure that package contains configuration file (is theme)
541 content = tar.getnames()
542 theme_name = None
543 is_theme = False
544 for element in content:
545 if element[-10:] == "theme.conf":
546 theme_name = element[:-11]
547 is_theme = True
548
549 # Install them
550 if is_theme:
551 tar.extractall(os.path.join(self.config.data_dir, 'themes'))
552 model.insert(len(model), [theme_name])
553 else:
554 error = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
555 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("Invalid theme file!"))
556 error.run()
557 error.destroy()
558
559 dialog.destroy()
560
561 def on_theme_list_cursor_changed(self, widget):
562 """Executed when theme is changed in theme list. Update preview."""
563 # Get currently selected theme
564 themelist = self.builder.get_object("theme_list")
565 model = themelist.get_model()
566 selection = themelist.get_selection().get_selected()
567 name = model.get_value(selection[1], 0)
568 themedir = os.path.join(self.config.data_dir, 'themes', name)
569 theme = Theme(theme_path=themedir)
570
571 # Update preview
572 image = self.builder.get_object("theme_image")
573 image.set_from_file(os.path.join(themedir, "thumbnail.png"))
574 name = self.builder.get_object("name_label")
575 name.set_text(theme.getName())
576 author = self.builder.get_object("author_label")
577 author.set_text(theme.getAuthor())
578 license_label = self.builder.get_object("license_label")
579 license_label.set_text(theme.getLicence())
580 copyright_label = self.builder.get_object("copyright_label")
581 copyright_label.set_text(theme.getCopyright())
582 comment = self.builder.get_object("comment_label")
583 comment.set_text(theme.getComment())
584
585 self.config.write_content_value("General", "theme", name.get_text())
586
587 def on_theme_remove_button_clicked(self, widget):
588 """Remove theme button clicked"""
589 # Get currently selected theme
590 themelist = self.builder.get_object("theme_list")
591 model = themelist.get_model()
592 selection = themelist.get_selection().get_selected()
593 name = model.get_value(selection[1], 0)
594
595 confirm = gtk.MessageDialog(None,
596 gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
597 _("Are you sure you want to delete\ntheme: %(name)s") % \
598 {'name': name})
599 status = confirm.run()
600 confirm.destroy()
601 if(status == gtk.RESPONSE_YES):
602 themedir = os.path.join(self.config.data_dir, 'themes', name)
603 shutil.rmtree(themedir)
604 model.remove(selection[1])
605 themelist.set_cursor(0)
606 self.themes.remove(name)
607
608 def on_checkbutton_effects_toggled(self, widget):
609 """Effect checkbox toggled"""
610 combobox = self.builder.get_object("combobox_effects")
611 combobox.set_sensitive(widget.get_active())
612 self.config.write_content_value("General", "show_effects",
613 widget.get_active())
614
615 def on_combobox_effects_changed(self, widget):
616 """User changed effect for screen transitions"""
617 text = widget.get_active_text()
618 if text == _("No effect"):
619 english_text = "No effect"
620 if text == _("Crossfade"):
621 english_text = "Crossfade"
622 if text == _("Zoom and fade"):
623 english_text = "Zoom and fade"
624 if text == _("Slide"):
625 english_text = "Slide"
626 self.config.write_content_value("General", "transition_effect",
627 english_text)
628
629 def init_dialog_values_from_configure_file(self):
630 """Read configuration and set dialog widget values with read values.
631 """
632 # == Videos ==
633 medialist_widget = self.builder.get_object("treeview_media")
634 mediastore = gtk.ListStore(str)
635
636 cell_renderer = gtk.CellRendererText()
637 column = gtk.TreeViewColumn(_("Folders"), cell_renderer, text=0)
638 medialist_widget.append_column(column)
639
640 self.media_folders = self.config.media_folders
641
642 # Fill model with folders read from config file
643 self.init_model(mediastore, self.media_folders)
644
645 medialist_widget.set_model(mediastore)
646
647 # Checkboxes
648 metadata_checkbox = self.builder.get_object("video_metadata_checkbox")
649 metadata_checkbox.set_active(self.config.download_metadata)
650
651 art_checkbox = self.builder.get_object("art_checkbox")
652 art_checkbox.set_active(self.config.download_album_art)
653
654 lyrics_checkbox = self.builder.get_object("lyrics_checkbox")
655 lyrics_checkbox.set_active(self.config.download_lyrics)
656
657 # == RSS-feeds ==
658 feedlist_widget = self.builder.get_object("treeview_feeds")
659 feed_model = gtk.ListStore(str)
660
661 rss_cell = gtk.CellRendererText()
662 rss_column = gtk.TreeViewColumn(_("Feeds"), rss_cell, text=0)
663 feedlist_widget.append_column(rss_column)
664
665 self.feeds = self.config.feeds
666
667 # Fill model with folders read from config file
668 for i in range(len(self.feeds)):
669 feed_model.insert(i, [self.feeds[i]])
670
671 feedlist_widget.set_model(feed_model)
672
673 # Interval spinner
674 interval_spinner = self.builder.get_object("fetch_interval_spinbutton")
675 interval_val = self.config.feed_fetch_interval
676 if interval_val < 15:
677 interval_val = 15
678 elif interval_val > 900:
679 interval_val = 900
680 interval_spinner.set_value(interval_val)
681
682 # == Weather ==
683 locationlist_widget = self.builder.get_object("treeview_locations")
684 location_model = gtk.ListStore(str)
685
686 loc_cell = gtk.CellRendererText()
687 location_column = gtk.TreeViewColumn(_("Location"), loc_cell, text=0)
688 locationlist_widget.append_column(location_column)
689
690 self.weather_location = self.config.weather_location
691
692 # Fill model with location read from config file
693 location_model.insert(0, [self.weather_location])
694 locationlist_widget.set_model(location_model)
695
696 weather_display_checkbox = self.builder.get_object(
697 "weather_display_checkbox")
698 display_val = self.config.display_weather_in_client
699 weather_display_checkbox.set_active(display_val)
700 if not display_val:
701 self.builder.get_object("button_add_weather").set_sensitive(False)
702 self.builder.get_object("button_remove_weather").set_sensitive(
703 False)
704 self.builder.get_object("treeview_locations").set_sensitive(False)
705
706 # == User Interface ==
707 self.load_themes()
708 current_theme = self.config.theme_name
709
710 themelist_widget = self.builder.get_object("theme_list")
711 model = gtk.ListStore(str)
712
713 cell_renderer = gtk.CellRendererText()
714 column = gtk.TreeViewColumn("Themes", cell_renderer, text=0)
715 themelist_widget.append_column(column)
716
717 # Fill model with installed themes
718 for i in range(len(self.themes)):
719 model.insert(i, [self.themes[i]])
720
721 themelist_widget.set_model(model)
722
723 # Set current theme selected in theme list
724 index = model.get_iter_first()
725 unselected = True
726 index_counter = 0
727 while(unselected):
728 name = model.get_value(index, 0)
729 if name == current_theme:
730 unselected = False
731 themelist_widget.set_cursor(index_counter)
732 index = model.iter_next(index)
733 index_counter += 1
734
735 effect_checkbox = self.builder.get_object("checkbutton_effects")
736 effect_combobox = self.builder.get_object("combobox_effects")
737 if self.config.show_effects:
738 effect_checkbox.set_active(True)
739 effect_combobox.set_sensitive(True)
740 else:
741 effect_checkbox.set_active(False)
742 effect_combobox.set_sensitive(False)
743
744 # Set Effect Combobox value (Text values are set in ui file)
745 effect = self.config.transition_effect
746 if effect == "No effect":
747 effect_combobox.set_active(0)
748 if effect == "Crossfade":
749 effect_combobox.set_active(1)
750 if effect == "Zoom and fade":
751 effect_combobox.set_active(2)
752 if effect == "Slide":
753 effect_combobox.set_active(3)
754
755 # == General ==
756 checkbutton_fullscreen = self.builder.get_object(
757 "checkbutton_fullscreen")
758 if self.config.start_in_fullscreen:
759 checkbutton_fullscreen.set_active(True)
760 else:
761 checkbutton_fullscreen.set_active(False)
762
763 checkbutton_autostart = self.builder.get_object("checkbutton_autostart")
764 if self.config.start_auto_server:
765 checkbutton_autostart.set_active(True)
766 else:
767 checkbutton_autostart.set_active(False)
768
769 checkbutton_systray_icon = self.builder.get_object(
770 "checkbutton_systray_icon")
771 if self.config.tray_icon_enabled:
772 checkbutton_systray_icon.set_active(True)
773 else:
774 checkbutton_systray_icon.set_active(False)
775
776 spinbutton_slideshow_step = self.builder.get_object(
777 "spinbutton_slideshow_step")
778 spinbutton_slideshow_step.set_value(self.config.slideshow_step)
779
780 def add_to_model_and_config(self, selected_folder, model, folders, kind):
781 """
782 Add selected_folder to the model and the folders list while updating
783 the configuration item section specified by type
784 """
785 if not selected_folder in folders:
786 model.append([selected_folder])
787
788 if(folders == None):
789 folders = [selected_folder]
790 else:
791 folders.append(selected_folder)
792
793 if "" in folders:
794 folders.remove("")
795 str_folders = ";".join(folders)
796 self.config.write_content_value(kind, "folders", str_folders)
797
798 def init_model(self, model, items):
799 """Fill model with items from supplied list"""
800 for i in range(len(items)):
801 if not str(items[i]).strip() == "":
802 model.insert(i, [items[i]])
803
804 def load_themes(self):
805 """Load themes"""
806 themes = os.listdir(os.path.join(self.config.data_dir, 'themes'))
807 for element in themes:
808 theme = os.path.join(self.config.data_dir, 'themes', element)
809 if os.path.isdir(theme):
810 self.themes.append(element)
811
812
813class LogViewer:
814 """
815 Implements dialog that allows user to see logged events.
816
817 This dialog is used to check Entertainer logfiles. It reads all data from
818 selected file and saves rows to self.log_rows. Then it filters unwanted
819 rows away by calling self.filterMessages(). This method adds rows to
820 ListStore, which is the model of TreeView object.
821
822 Combobox and refresh -button actions read files again
823 Checkbox actions just filter current rows again
824 """
825
826 UI_DIR = os.path.join(os.path.dirname(__file__), "uis")
827
828 # Is this dialog running as a stand alone process
829 __STAND_ALONE = None
830
831 widgets = None
832 dialog = None
833 log_store = None
834 log_rows = []
835
836 def __init__(self, stand_alone):
837 self.logfile_entertainer = Configuration().LOG
838 self.logger = Logger().getLogger('utils.log_viewer')
839
840 self.__STAND_ALONE = stand_alone
841 try:
842 uifile = os.path.join(self.UI_DIR, "log_dialog.ui")
843 self.builder = gtk.Builder()
844 self.builder.set_translation_domain('entertainer')
845 self.builder.add_from_file(uifile)
846 except RuntimeError:
847 self.logger.critical("Couldn't open ui file: " + uifile)
848 sys.exit(1)
849 callback_dic = {
850 "on_close_log_button_clicked" : self.on_close_log_button_clicked,
851 "on_log_refresh_button_clicked" : self.update_log_rows,
852 "on_checkbutton_debug_toggled" : self.filter_messages,
853 "on_checkbutton_critical_toggled" : self.filter_messages,
854 "on_checkbutton_error_toggled" : self.filter_messages,
855 "on_checkbutton_warning_toggled" : self.filter_messages,
856 "on_checkbutton_info_toggled" : self.filter_messages }
857
858 self.builder.connect_signals(callback_dic)
859
860 # Create log treeview
861 treeview = self.builder.get_object("treeview_log")
862 cell_renderer1 = gtk.CellRendererText()
863 cell_renderer2 = gtk.CellRendererText()
864 cell_renderer3 = gtk.CellRendererText()
865 cell_renderer4 = gtk.CellRendererText()
866
867 column1 = gtk.TreeViewColumn("Date")
868 column1.pack_start(cell_renderer1, True)
869 column1.set_attributes(cell_renderer1, text = 0)
870
871 column2 = gtk.TreeViewColumn("Time")
872 column2.pack_start(cell_renderer2, True)
873 column2.set_attributes(cell_renderer2, text = 1)
874
875 column3 = gtk.TreeViewColumn("Type")
876 column3.pack_start(cell_renderer3, True)
877 column3.set_attributes(cell_renderer3, text = 2)
878
879 column4 = gtk.TreeViewColumn("Message")
880 column4.pack_end(cell_renderer4, True)
881 column4.set_attributes(cell_renderer4, text = 3)
882
883 treeview.append_column(column1)
884 treeview.append_column(column2)
885 treeview.append_column(column3)
886 treeview.append_column(column4)
887 treeview.set_headers_visible(True)
888
889 # Set model to view and read data from logfile
890 self.log_store = gtk.ListStore(str, str, str, str)
891 treeview.set_model(self.log_store)
892 self.update_log_rows()
893
894 # Show Log viewer dialog
895 self.dialog = self.builder.get_object("LogDialog")
896 self.dialog.resize(750, 500)
897 self.dialog.connect("destroy", self.on_close_log_button_clicked)
898 self.dialog.show()
899
900 def update_log_rows(self, widget=None):
901 """Read logfile and udpate treeview"""
902 self.log_rows[:] = []
903
904 try:
905 for line in open(self.logfile_entertainer, 'r'):
906 try:
907 line_table = line.split()
908 message = ' '.join(line_table[3:])
909 row = line_table[:3] + [message]
910 parsed_row = parse_row(row)
911 self.log_rows.append(parsed_row)
912 except IndexError:
913 print "Cannot parse log line: ", line
914 except IOError:
915 print "Cannot find logfile: ", self.logfile_entertainer
916
917 # Reverse so that the latest message is at top
918 self.log_rows.reverse()
919 # Filter unwated message types
920 self.filter_messages()
921
922 def filter_messages(self, widget = None):
923 """Checks which message types should be displayed on treeview"""
924 if self.log_store:
925 self.log_store.clear()
926
927 debug = self.builder.get_object("checkbutton_debug").get_active()
928 critical = self.builder.get_object("checkbutton_critical").get_active()
929 error = self.builder.get_object("checkbutton_error").get_active()
930 warning = self.builder.get_object("checkbutton_warning").get_active()
931 info = self.builder.get_object("checkbutton_info").get_active()
932
933 for element in self.log_rows:
934 if element[2] == "DEBUG" and debug:
935 self.log_store.append(element)
936 elif element[2] == "CRITICAL" and critical:
937 self.log_store.append(element)
938 elif element[2] == "ERROR" and error:
939 self.log_store.append(element)
940 elif element[2] == "WARNING" and warning:
941 self.log_store.append(element)
942 elif element[2] == "INFO" and info:
943 self.log_store.append(element)
944
945 # Signal handlers
946 def on_close_log_button_clicked(self, widget):
947 """
948 If running as a stand alone process, quit.
949 Otherwise only destroy dialog.
950 """
951 self.dialog.hide()
952 self.dialog.destroy()
953 if(self.__STAND_ALONE):
954 gtk.main_quit()
955
956
957def parse_row(row):
958 """
959 This parses the input list into a list suitable for the logviewer
960 @author Joshua Scotton
961 @param row The input list [Date, Time, Class, Type + Description]
962 """
963 if row[3][:5] == "DEBUG":
964 return [row[0], row[1], "DEBUG",
965 row[2] + ": " + row[3][5:]]
966 elif row[3][:8] == "CRITICAL":
967 return [row[0], row[1], "CRITICAL",
968 row[2] + ": " + row[3][8:]]
969 elif row[3][:5] == "ERROR":
970 return [row[0], row[1], "ERROR",
971 row[2] + ": " + row[3][5:]]
972 elif row[3][:7] == "WARNING":
973 return [row[0], row[1], "WARNING",
974 row[2] + ": " + row[3][7:]]
975 elif row[3][:4] == "INFO":
976 return [row[0], row[1], "INFO",
977 row[2] + ": " + row[3][4:]]
978
979
980class OpenFeedSourceDialog:
981 '''Feed source reader dialog'''
982
983 UI_DIR = os.path.join(os.path.dirname(__file__), "uis")
984
985 widgets = None
986 dialog = None
987 tree_widget = None
988 feeds = None
989 url = None
990
991 def __init__(self, the_widget, the_feeds):
992 """initialises the gtk window and displays it"""
993
994 #feeds is a pointer to a list of feeds from the config file
995 self.feeds = the_feeds
996
997 #needed so we can add feeds to the feed list widget
998 self.tree_widget = the_widget
999
1000 # Load UI with gtk.Builder
1001 uifile = os.path.join(self.UI_DIR, "open_feed_source_dialog.ui")
1002 self.builder = gtk.Builder()
1003 self.builder.set_translation_domain('entertainer')
1004 self.builder.add_from_file(uifile)
1005
1006 # Get content management dialog and bind signal callbacks
1007 self.dialog = self.builder.get_object("open_source_dialog")
1008 if (self.dialog):
1009 callback_dic = {
1010 "on_fileOpen_clicked" : self.on_fileOpen_clicked,
1011 "on_lifereaButton_clicked" : self.on_lifereaButton_clicked,
1012 "on_enterURL_clicked" : self.on_enterURL_clicked,
1013 "on_closeButton_clicked" : self.on_closeButton_clicked,
1014 "on_url_dialog_ok_button_clicked" :
1015 self.on_url_dialog_ok_button_clicked,
1016 "on_url_dialog_cancel_button_clicked" :
1017 self.on_url_dialog_cancel_button_clicked,
1018 "on_url_dialog_delete_event" : self.on_url_dialog_delete_event,
1019 "on_destroy" : self.on_closeButton_clicked
1020 }
1021
1022 self.builder.connect_signals(callback_dic)
1023
1024 # Initilize dialog widgets with correct values and show dialog
1025 self.dialog.resize(300, 200)
1026 self.dialog.run()
1027
1028 def on_fileOpen_clicked(self, widget):
1029 """Opens add file dialog and then adds all feeds in the opml file
1030 selected """
1031
1032 #get the model for the feed list widget so we can add the new feeds
1033 model = self.tree_widget.get_model()
1034
1035 #create select file dialog
1036 dialog = gtk.FileChooserDialog(_("Select OPML file"), None,
1037 gtk.FILE_CHOOSER_ACTION_OPEN,
1038 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
1039 gtk.RESPONSE_OK))
1040
1041 #set dialog up to filter for only opml files
1042 file_filter = gtk.FileFilter()
1043 file_filter.set_name(_("OPML files"))
1044 file_filter.add_pattern("*.opml")
1045 dialog.add_filter(file_filter)
1046
1047 #set dialog up to allow multiple selections
1048 dialog.set_select_multiple(True)
1049
1050 #run the dialog
1051 status = dialog.run()
1052
1053 # if file was selected, get list of feeds from it and add to
1054 #model and update config file
1055 if(status == gtk.RESPONSE_OK):
1056 FeedConfigTools().add_file_feeds_to_widget(
1057 dialog.get_filenames(), model, self.feeds)
1058 dialog.destroy()
1059
1060 def on_lifereaButton_clicked(self, widget):
1061 """Adds any liferea feeds it finds to the feed widget and config file
1062 """
1063 #get the model
1064 model = self.tree_widget.get_model()
1065 #get the liferea feeds and then send everything to the
1066 #add_file_feeds_to_widget method
1067 FeedConfigTools().add_file_feeds_to_widget(
1068 [OPMLParser().get_liferea_opml()], model, self.feeds)
1069
1070 def on_enterURL_clicked(self, widget):
1071 """gets a opml file link from a user and adds any feeds it finds to the
1072 feed widget and config file"""
1073 url_dialog = self.builder.get_object("url_dialog")
1074 model = self.tree_widget.get_model()
1075 # Open dialog
1076 url_dialog.set_title(_("Add OPML File"))
1077 status = url_dialog.run()
1078 # If folder was selected we add it to model and update config file
1079 if(status == gtk.RESPONSE_OK):
1080 FeedConfigTools().add_file_feeds_to_widget([self.url],
1081 model, self.feeds)
1082
1083 def on_closeButton_clicked(self, widget):
1084 self.dialog.hide()
1085 self.dialog.destroy()
1086
1087 def on_url_dialog_ok_button_clicked(self, widget):
1088 """URL dialog OK button pressed. Sets self.url"""
1089 url_dialog = self.builder.get_object("url_dialog")
1090 url_entry = self.builder.get_object("url_entry")
1091 url_dialog.hide()
1092 self.url = url_entry.get_text()
1093 url_entry.set_text("")
1094 url_dialog.response(gtk.RESPONSE_OK)
1095
1096 def on_url_dialog_cancel_button_clicked(self, widget):
1097 """URL dialog cancelled. Hides dialog"""
1098 url_dialog = self.builder.get_object("url_dialog")
1099 url_entry = self.builder.get_object("url_entry")
1100 url_dialog.hide()
1101 url_entry.set_text("")
1102 url_dialog.response(gtk.RESPONSE_CANCEL)
1103
1104 def on_url_dialog_delete_event(self, widget, data):
1105 """Dialog's X clicked. Hides dialog"""
1106 url_dialog = self.builder.get_object("url_dialog")
1107 url_entry = self.builder.get_object("url_entry")
1108 url_dialog.hide()
1109 url_entry.set_text("")
1110 url_dialog.response(gtk.RESPONSE_CANCEL)
1111 return True
1112
1113
01114
=== added file 'entertainerlib/download.py'
--- entertainerlib/download.py 1970-01-01 00:00:00 +0000
+++ entertainerlib/download.py 2010-01-23 03:34:11 +0000
@@ -0,0 +1,455 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Downloader classes.'''
3
4import locale
5import os
6import re
7import socket
8import threading
9import urllib
10from xml.dom import minidom
11
12import gobject
13
14# Amazon licence for Entertainer
15LICENSE_KEY = "1YCWYZ0SPPAJ27YZZ482"
16DEFAULT_LOCALE = "en_US"
17ASSOCIATE = "webservices-20"
18
19# We are not allowed to batch more than 2 requests at once
20# http://docs.amazonwebservices.com/AWSEcommerceService/4-0/
21# PgCombiningOperations.html
22MAX_BATCH_JOBS = 2
23
24
25class AlbumArtDownloader(threading.Thread):
26 """
27 Search and download album art from the internet.
28
29 This class is heavily based on Rhythmbox - AlbumArt plugin's class
30 'AmazonCoverArtSearch'. That plugin is released under GPLv2 (or higher)
31 and it's copyrights belong to Gareth Murphy and Martin Szulecki.
32
33 See more: http://www.gnome.org/projects/rhythmbox/
34
35 If you want better cover search, please contribute to Rhyhtmbox project.
36 """
37
38 def __init__(self, album, artist, art_file_path, callback = None):
39 """
40 Initialize album art downloader
41 @param album: Album title
42 @param artist: Artist name
43 @param art_file_path: Path where albumart is saved
44 @param callback: Callback function that is called after search if set
45 """
46 threading.Thread.__init__(self)
47 self.setName("AlbumArt Downloader")
48 self.callback_function = callback # Callback function
49 self.album = album # Album title
50 self.artist = artist # Artist name
51 # Album art files are in this directory
52 self.path = art_file_path
53 (self.tld, self.encoding) = self.__get_locale ()
54
55 def run(self):
56 """Start searching and downloading albumart."""
57 self.search()
58
59 def __get_locale (self):
60 '''Get locale information from user\'s machine'''
61 # "JP is the only locale that correctly takes UTF8 input.
62 # All other locales use LATIN1."
63 # http://developer.amazonwebservices.com/connect/
64 # entry.jspa?externalID=1295&categoryID=117
65 supported_locales = {
66 "en_US" : ("com", "latin1"),
67 "en_GB" : ("co.uk", "latin1"),
68 "de" : ("de", "latin1"),
69 "ja" : ("jp", "utf8")
70 }
71
72 lc_id = DEFAULT_LOCALE
73 default = locale.getdefaultlocale ()[0]
74 if default:
75 if supported_locales.has_key (default):
76 lc_id = default
77 else:
78 lang = default.split("_")[0]
79 if supported_locales.has_key (lang):
80 lc_id = lang
81
82 return supported_locales[lc_id]
83
84 def __valid_match (self, item):
85 '''Determine if item matches tag criteria'''
86 return (hasattr (item, "LargeImage") or hasattr (item, "MediumImage")) \
87 and hasattr (item, "ItemAttributes")
88
89 def __tidy_up_string (self, str_input):
90 """
91 Tidy up string. Remove spaces, convert to lowercase and replace chars.
92 """
93 # Lowercase
94 str_input = str_input.lower ()
95 # Strip
96 str_input = str_input.strip ()
97
98 # TODO: Convert accented to unaccented
99 str_input = str_input.replace (" - ", " ")
100 str_input = str_input.replace (": ", " ")
101 str_input = str_input.replace (" & ", " and ")
102
103 return str_input
104
105 def search(self):
106 """Search album art from Amazon"""
107 self.searching = True
108 self.cancel = False
109 self.keywords = []
110
111 st_artist = self.artist or u'Unknown'
112 st_album = self.album or u'Unknown'
113
114 if st_artist == st_album == u'Unknown':
115 self.on_search_completed (None)
116 return
117
118 # Tidy up
119
120 # Replace quote characters
121 # don't replace single quote: could be important punctuation
122 for char in ["\""]:
123 st_artist = st_artist.replace (char, '')
124 st_album = st_album.replace (char, '')
125
126
127 self.st_album = st_album
128 self.st_artist = st_artist
129
130 # Remove variants of Disc/CD [1-9] from album title before search
131 for exp in ["\([Dd]isc *[1-9]+\)", "\([Cc][Dd] *[1-9]+\)"]:
132 p = re.compile (exp)
133 st_album = p.sub ('', st_album)
134
135 st_album_no_vol = st_album
136 for exp in ["\(*[Vv]ol.*[1-9]+\)*"]:
137 p = re.compile (exp)
138 st_album_no_vol = p.sub ('', st_album_no_vol)
139
140 self.st_album_no_vol = st_album_no_vol
141
142 # Save current search's entry properties
143 self.search_album = st_album
144 self.search_artist = st_artist
145 self.search_album_no_vol = st_album_no_vol
146
147 # TODO: Improve to decrease wrong cover downloads, maybe add severity?
148 # Assemble list of search keywords (and thus search queries)
149 if st_album == u'Unknown':
150 self.keywords.append ("%s Best of" % (st_artist))
151 self.keywords.append ("%s Greatest Hits" % (st_artist))
152 self.keywords.append ("%s Essential" % (st_artist))
153 self.keywords.append ("%s Collection" % (st_artist))
154 self.keywords.append ("%s" % (st_artist))
155 elif st_artist == u'Unknown':
156 self.keywords.append ("%s" % (st_album))
157 if st_album_no_vol != st_artist:
158 self.keywords.append ("%s" % (st_album_no_vol))
159 self.keywords.append ("Various %s" % (st_album))
160 else:
161 if st_album != st_artist:
162 self.keywords.append ("%s %s" % (st_artist, st_album))
163 if st_album_no_vol != st_album:
164 self.keywords.append ("%s %s" %
165 (st_artist, st_album_no_vol))
166 self.keywords.append ("Various %s" % (st_album))
167 self.keywords.append ("%s" % (st_artist))
168
169 # Initiate asynchronous search
170 self.search_next ()
171
172 def search_next(self):
173 """Search again, because the last one didn't find any covers."""
174 if len (self.keywords) == 0:
175 # No keywords left to search -> no results
176 self.on_search_completed (None)
177 return False
178
179 self.searching = True
180
181 url = "http://ecs.amazonaws." + self.tld + "/onca/xml" \
182 "?Service=AWSECommerceService" \
183 "&AWSAccessKeyId=" + LICENSE_KEY + \
184 "&AssociateTag=" + ASSOCIATE + \
185 "&ResponseGroup=Images,ItemAttributes" \
186 "&Operation=ItemSearch" \
187 "&ItemSearch.Shared.SearchIndex=Music"
188
189 job = 1
190 while job <= MAX_BATCH_JOBS and len (self.keywords) > 0:
191 keyword = self.keywords.pop (0)
192 keyword = keyword.encode (self.encoding, "ignore")
193 keyword = keyword.strip ()
194 keyword = urllib.quote (keyword)
195 url += "&ItemSearch.%d.Keywords=%s" % (job, keyword)
196 job += 1
197
198 # Retrieve search for keyword
199 temp = urllib.urlopen(url)
200 search_results = temp.read()
201 self.on_search_response(search_results)
202 return True
203
204 def __unmarshal(self, element):
205 rc = object()
206 child_elements = [e for e in element.childNodes if isinstance (e,
207 minidom.Element)]
208 if child_elements:
209 for child in child_elements:
210 key = child.tagName
211 if hasattr (rc, key):
212 if not isinstance (getattr (rc, key), list):
213 setattr (rc, key, [getattr (rc, key)])
214 getattr (rc, key).append (self.__unmarshal (child))
215 # get_best_match_urls() wants a list, even if there is only
216 # one item/artist
217 elif child.tagName in ("Items", "Item", "Artist"):
218 setattr (rc, key, [self.__unmarshal(child)])
219 else:
220 setattr (rc, key, self.__unmarshal(child))
221 else:
222 rc = "".join ([e.data for e in element.childNodes if isinstance (e,
223 minidom.Text)])
224 return rc
225
226 def on_search_response (self, result_data):
227 '''Check search results
228
229 If results are not good, we search again with the next keyword.
230 '''
231 if result_data is None:
232 self.search_next()
233 return
234
235 try:
236 xmldoc = minidom.parseString(result_data)
237 except (TypeError, AttributeError):
238 self.search_next()
239 return
240
241 data = self.__unmarshal (xmldoc)
242 if not hasattr (data, "ItemSearchResponse") or \
243 not hasattr (data.ItemSearchResponse, "Items"):
244 # Something went wrong ...
245 self.search_next ()
246 else:
247 # We got some search results
248 self.on_search_results (data.ItemSearchResponse.Items)
249
250 def on_search_results (self, results):
251 '''Results were found, now we need to take action.'''
252 self.on_search_completed (results)
253
254 def on_search_completed (self, result):
255 """
256 Search completed and results found.
257
258 Download large album art image from the first result and save it to
259 the disk. This function diverges greatly from the rhythmbox
260 implementation in order to avoid their loader and CoverArtDatabase
261 """
262 self.searching = False
263 image_urls = self.get_best_match_urls(result)
264 if len(image_urls) == 0:
265 return
266 image_url = image_urls[0]
267 image_file = urllib.urlopen(image_url)
268 # base64 encode artist and album so there can be a '/' in the artist
269 # or album
270 artist_album = self.artist + " - " + self.album
271 artist_album = artist_album.encode("base64")
272
273 dest = open(os.path.join(self.path, artist_album + ".jpg"),'w')
274 dest.write(image_file.read())
275 dest.close()
276
277 if self.callback_function is not None:
278 self.callback_function(self.artist, self.album)
279
280 def get_best_match_urls (self, search_results):
281 """Return tuple of URL's to large and medium cover of the best match"""
282 # Default to "no match", our results must match our criteria
283
284 # This code comes from Rhythmbox so we can't control the use of 'filter'
285 # pylint: disable-msg=W0141
286
287 if not search_results:
288 return []
289
290 best_match = None
291
292 for result in search_results:
293 if not hasattr (result, "Item"):
294 # Search was unsuccessful, try next batch job
295 continue
296
297 items = filter(self.__valid_match, result.Item)
298 if self.search_album != u'Unknown':
299 album_check = self.__tidy_up_string (self.search_album)
300 for item in items:
301 if not hasattr (item.ItemAttributes, "Title"):
302 continue
303
304 album = self.__tidy_up_string (item.ItemAttributes.Title)
305 if album == album_check:
306 # Found exact album, can not get better than that
307 best_match = item
308 break
309 # If we already found a best_match, just keep checking for
310 # exact one. Check the results for both an album name that
311 # contains the name we're searching for, and an album name
312 # that's a substring of the name we're searching for
313 elif (best_match is None) and \
314 (album.find (album_check) != -1 or
315 album_check.find (album) != -1):
316 best_match = item
317
318 # If we still have no definite hit, use first result where artist
319 # matches
320 if (self.search_album == u'Unknown' and \
321 self.search_artist != u'Unknown'):
322 artist_check = self.__tidy_up_string (self.search_artist)
323 if best_match is None:
324 # Check if artist appears in the Artists list
325 hit = False
326 for item in items:
327 if not hasattr (item.ItemAttributes, "Artist"):
328 continue
329
330 for artist in item.ItemAttributes.Artist:
331 artist = self.__tidy_up_string (artist)
332 if artist.find (artist_check) != -1:
333 best_match = item
334 hit = True
335 break
336 if hit:
337 break
338
339 urls = [getattr (best_match, size).URL for size in ("LargeImage",
340 "MediumImage")
341 if hasattr (best_match, size)]
342 if urls:
343 return urls
344
345 # No search was successful
346 return []
347
348
349class LyricsDownloader(object):
350 """
351 Search and download song lyrics from the internet.
352 Update music cache if lyrics found.
353 """
354
355 # The permanent user ID from Lyricsfly
356 # NOTICE: This is the personal user ID for Entertainer, if you want to
357 # experiment with the API from lyricsfly you can get an ID here =>
358 # http://lyricsfly.com/api/, don't use this one as abuse of our key may
359 # invalidate it.
360 _LYRICSFLY_KEY = 'YzIxOTM4M2NkNGQ4MmRmODEtZW50ZXJ0YWluZXItcHJvamVjdC5jb20='
361
362 def __init__(self, title, artist, callback):
363 """
364 Initialize lyrics downloader.
365 @param title: Title of the track
366 @param artist: Artist of the track
367 @param callback: Callback function which is called when search is over.
368 lyrics are given as a parameter to this callback function.
369 """
370 self.title = title.lower()
371 self.artist = artist.lower()
372 self.callback = callback
373
374 def search(self):
375 """
376 Search lyrics and download if found. Search is done asynchronously.
377 This method returns immediately and set callback is called when search
378 is over.
379 """
380 gobject.timeout_add(2000, self._async_search)
381
382 def _async_search(self):
383 """
384 Search lyrics and download if found
385 """
386 lyrics = ""
387 self._clean_up_artist_title()
388 lyrics_xml = self._get_lyrics_xml()
389 if lyrics_xml is not None:
390 lyrics = self._parse_lyrics_xml(lyrics_xml)
391 self.callback(lyrics)
392
393 def _clean_up_artist_title(self):
394 """
395 Clean up the artist and title.
396 """
397 # Clean spaces
398 self.title = self.title.strip()
399 self.artist = self.artist.strip()
400
401 # Convert title and artist to use in url, special symbols have to be
402 # replaced by a '%' not '%xx'
403 # TODO: Find out what the special symbols are (', &, ...)
404 # not letters, digits, spaces and ()$^*=:;|#@}{][!,.-_\
405 self.artist = urllib.quote(self.artist.encode('utf-8'),
406 "'&()$^*=:;|#@}{][!,\\")
407 self.title = urllib.quote(self.title.encode('utf-8'),
408 "'&()$^*=:;|#@}{][!,\\")
409
410 self.artist = self.artist.replace("'", "%").replace("&", "%")
411 self.title = self.title.replace("'", "%").replace("&", "%")
412
413 def _get_lyrics_xml(self):
414 """
415 Download the lyrics XML-file.
416 """
417 lyrics_xml = None
418
419 # timeout in seconds
420 timeout = 5
421 socket.setdefaulttimeout(timeout)
422
423 url = "http://lyricsfly.com/api/api.php?i=%s&a=%s&t=%s" \
424 % (self._LYRICSFLY_KEY.decode('base64'), self.artist, self.title)
425 try:
426 temp = urllib.urlopen(url)
427 lyrics_xml = temp.read()
428 except IOError:
429 return None
430
431 return lyrics_xml
432
433 def _parse_lyrics_xml(self, lyrics_xml):
434 """Parse lyrics XML and return lyrics string"""
435 xmldoc = minidom.parseString(lyrics_xml).documentElement
436
437 # Get the lyric from the XML file
438 try:
439 lyrics = xmldoc.getElementsByTagName('tx')[0].firstChild.nodeValue
440 except IndexError:
441 return ''
442
443 # Clean spaces and enters
444 lyrics = lyrics.strip().replace('\n', '')
445 lyrics = lyrics.replace('[br]', '\n')
446
447 # Add the artist and title to the top of the lyric
448 lyrics = xmldoc.getElementsByTagName('ar')[0].firstChild.nodeValue + \
449 ' - ' + xmldoc.getElementsByTagName('tt')[0].firstChild.nodeValue + \
450 '\n\n' + lyrics
451
452 xmldoc.unlink()
453
454 return lyrics
455
0456
=== renamed directory 'entertainerlib/frontend/gui' => 'entertainerlib/gui'
=== modified file 'entertainerlib/gui/screen_history.py'
--- entertainerlib/frontend/gui/screen_history.py 2009-06-03 01:54:03 +0000
+++ entertainerlib/gui/screen_history.py 2010-01-23 03:34:11 +0000
@@ -1,7 +1,7 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Stack container for screen objects'''2'''Stack container for screen objects'''
33
4from entertainerlib.utils.configuration import Configuration4from entertainerlib.configuration import Configuration
55
6class ScreenHistory(object):6class ScreenHistory(object):
7 '''ScreenHistory contains the latest screens in is a stack.'''7 '''ScreenHistory contains the latest screens in is a stack.'''
@@ -13,7 +13,7 @@
1313
14 def add_screen(self, screen):14 def add_screen(self, screen):
15 '''Push the provided screen onto the history stack.'''15 '''Push the provided screen onto the history stack.'''
16 if len(self.screens) < self.config.history_size():16 if len(self.screens) < self.config.history_size:
17 self.screens.append(screen)17 self.screens.append(screen)
18 else:18 else:
19 self.remove_from_stage_callback(self.screens[0])19 self.remove_from_stage_callback(self.screens[0])
2020
=== modified file 'entertainerlib/gui/screens/album.py'
--- entertainerlib/frontend/gui/screens/album.py 2009-07-14 10:46:47 +0000
+++ entertainerlib/gui/screens/album.py 2010-01-23 03:34:11 +0000
@@ -5,12 +5,11 @@
5import clutter5import clutter
6import pango6import pango
77
8from entertainerlib.frontend.gui.screens.screen import Screen8from entertainerlib.gui.screens.screen import Screen
9from entertainerlib.frontend.gui.widgets.eyecandy_texture import (9from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
10 EyeCandyTexture)10from entertainerlib.gui.widgets.label import Label
11from entertainerlib.frontend.gui.widgets.label import Label11from entertainerlib.gui.widgets.list_indicator import ListIndicator
12from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator12from entertainerlib.gui.widgets.text_menu import TextMenu
13from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
1413
15class Album(Screen):14class Album(Screen):
16 '''Screen that allows user to browse and play tracks of the music album.'''15 '''Screen that allows user to browse and play tracks of the music album.'''
1716
=== modified file 'entertainerlib/gui/screens/artist.py'
--- entertainerlib/frontend/gui/screens/artist.py 2009-05-12 17:18:10 +0000
+++ entertainerlib/gui/screens/artist.py 2010-01-23 03:34:11 +0000
@@ -3,11 +3,11 @@
33
4import pango4import pango
55
6from entertainerlib.frontend.gui.screens.screen import Screen6from entertainerlib.gui.screens.screen import Screen
7from entertainerlib.frontend.gui.tabs.albums_tab import AlbumsTab7from entertainerlib.gui.tabs.albums_tab import AlbumsTab
8from entertainerlib.frontend.gui.tabs.tracks_tab import TracksTab8from entertainerlib.gui.tabs.tracks_tab import TracksTab
9from entertainerlib.frontend.gui.widgets.label import Label9from entertainerlib.gui.widgets.label import Label
10from entertainerlib.frontend.medialibrary.playlist import Playlist10from entertainerlib.client.medialibrary.playlist import Playlist
1111
12class Artist(Screen):12class Artist(Screen):
13 '''Screen that allows user to browse music by artist.'''13 '''Screen that allows user to browse music by artist.'''
1414
=== modified file 'entertainerlib/gui/screens/audio_play.py'
--- entertainerlib/frontend/gui/screens/audio_play.py 2009-05-28 20:23:17 +0000
+++ entertainerlib/gui/screens/audio_play.py 2010-01-23 03:34:11 +0000
@@ -5,10 +5,10 @@
5import gtk5import gtk
6import clutter6import clutter
77
8from entertainerlib.frontend.gui.screens.screen import Screen8from entertainerlib.gui.screens.screen import Screen
9from entertainerlib.frontend.gui.tabs.lyrics_tab import LyricsTab9from entertainerlib.gui.tabs.lyrics_tab import LyricsTab
10from entertainerlib.frontend.gui.tabs.playing_tab import PlayingTab10from entertainerlib.gui.tabs.playing_tab import PlayingTab
11from entertainerlib.frontend.gui.widgets.eyecandy_texture import (11from entertainerlib.gui.widgets.eyecandy_texture import (
12 EyeCandyTexture)12 EyeCandyTexture)
1313
14class AudioPlay(Screen):14class AudioPlay(Screen):
1515
=== modified file 'entertainerlib/gui/screens/disc.py'
--- entertainerlib/frontend/gui/screens/disc.py 2009-07-19 20:23:56 +0000
+++ entertainerlib/gui/screens/disc.py 2010-01-23 03:34:11 +0000
@@ -9,17 +9,15 @@
9import pango9import pango
10import clutter10import clutter
1111
12from entertainerlib.frontend.gui.screens.screen import Screen12from entertainerlib.gui.screens.screen import Screen
13from entertainerlib.frontend.gui.widgets.eyecandy_texture import (13from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
14 EyeCandyTexture)14from entertainerlib.gui.widgets.label import Label
15from entertainerlib.frontend.gui.widgets.label import Label15from entertainerlib.gui.widgets.list_indicator import ListIndicator
16from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator16from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
17from entertainerlib.frontend.gui.widgets.loading_animation import (17from entertainerlib.gui.widgets.texture import Texture
18 LoadingAnimation)18from entertainerlib.gui.widgets.text_menu import TextMenu
19from entertainerlib.frontend.gui.widgets.texture import Texture19from entertainerlib.client.medialibrary.playlist import Playlist
20from entertainerlib.frontend.gui.widgets.text_menu import TextMenu20from entertainerlib.download import AlbumArtDownloader
21from entertainerlib.frontend.medialibrary.playlist import Playlist
22from entertainerlib.utils.albumart_downloader import AlbumArtDownloader
2321
24class Disc(Screen):22class Disc(Screen):
25 '''Screen allows user to play tracks from the current Audio CD.'''23 '''Screen allows user to play tracks from the current Audio CD.'''
2624
=== modified file 'entertainerlib/gui/screens/factory.py'
--- entertainerlib/frontend/gui/screens/factory.py 2009-06-02 01:16:03 +0000
+++ entertainerlib/gui/screens/factory.py 2010-01-23 03:34:11 +0000
@@ -1,25 +1,26 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''ScreenFactory - Create correct screen based on the type provided.'''2'''ScreenFactory - Create correct screen based on the type provided.'''
33
4from entertainerlib.frontend.gui.screens.artist import Artist4from entertainerlib.gui.screens.artist import Artist
5from entertainerlib.frontend.gui.screens.album import Album5from entertainerlib.gui.screens.album import Album
6from entertainerlib.frontend.gui.screens.audio_play import AudioPlay6from entertainerlib.gui.screens.audio_play import AudioPlay
7from entertainerlib.frontend.gui.screens.disc import Disc7from entertainerlib.gui.screens.disc import Disc
8from entertainerlib.frontend.gui.screens.feed import Feed8from entertainerlib.gui.screens.feed import Feed
9from entertainerlib.frontend.gui.screens.feed_entry import FeedEntry9from entertainerlib.gui.screens.feed_entry import FeedEntry
10from entertainerlib.frontend.gui.screens.main import Main10from entertainerlib.gui.screens.main import Main
11from entertainerlib.frontend.gui.screens.movie import Movie11from entertainerlib.gui.screens.movie import Movie
12from entertainerlib.frontend.gui.screens.music import Music12from entertainerlib.gui.screens.music import Music
13from entertainerlib.frontend.gui.screens.photo import Photo13from entertainerlib.gui.screens.photo import Photo
14from entertainerlib.frontend.gui.screens.photo_albums import PhotoAlbums14from entertainerlib.gui.screens.photo_albums import PhotoAlbums
15from entertainerlib.frontend.gui.screens.photographs import Photographs15from entertainerlib.gui.screens.photographs import Photographs
16from entertainerlib.frontend.gui.screens.question import Question16from entertainerlib.gui.screens.question import Question
17from entertainerlib.frontend.gui.screens.rss import Rss17from entertainerlib.gui.screens.rss import Rss
18from entertainerlib.frontend.gui.screens.tv_episodes import TvEpisodes18from entertainerlib.gui.screens.tv_episodes import TvEpisodes
19from entertainerlib.frontend.gui.screens.tv_series import TvSeries19from entertainerlib.gui.screens.tv_series import TvSeries
20from entertainerlib.frontend.gui.screens.video import Video20from entertainerlib.gui.screens.video import Video
21from entertainerlib.frontend.gui.screens.video_osd import VideoOSD21from entertainerlib.gui.screens.video_osd import VideoOSD
22from entertainerlib.frontend.gui.screens.weather import WeatherScreen22from entertainerlib.gui.screens.weather import WeatherScreen
23
2324
24class ScreenFactory(object):25class ScreenFactory(object):
25 '''Generate a screen based on the type provided.'''26 '''Generate a screen based on the type provided.'''
2627
=== modified file 'entertainerlib/gui/screens/feed.py'
--- entertainerlib/frontend/gui/screens/feed.py 2009-07-12 07:57:05 +0000
+++ entertainerlib/gui/screens/feed.py 2010-01-23 03:34:11 +0000
@@ -3,12 +3,12 @@
33
4import pango4import pango
55
6from entertainerlib.frontend.gui.screens.screen import Screen6from entertainerlib.gui.screens.screen import Screen
7from entertainerlib.frontend.gui.widgets.label import Label7from entertainerlib.gui.widgets.label import Label
8from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator8from entertainerlib.gui.widgets.list_indicator import ListIndicator
9from entertainerlib.frontend.gui.widgets.texture import Texture9from entertainerlib.gui.widgets.texture import Texture
10from entertainerlib.frontend.gui.widgets.text_menu import TextMenu10from entertainerlib.gui.widgets.text_menu import TextMenu
11from entertainerlib.utils.feed_utils import FeedEntryParser11from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
1212
13class Feed(Screen):13class Feed(Screen):
14 '''Screen is displayed when headlines are accessed for a specific feed.'''14 '''Screen is displayed when headlines are accessed for a specific feed.'''
1515
=== modified file 'entertainerlib/gui/screens/feed_entry.py'
--- entertainerlib/frontend/gui/screens/feed_entry.py 2009-06-25 18:56:15 +0000
+++ entertainerlib/gui/screens/feed_entry.py 2010-01-23 03:34:11 +0000
@@ -3,12 +3,12 @@
33
4import pango4import pango
55
6from entertainerlib.frontend.gui.screens.screen import Screen6from entertainerlib.gui.screens.screen import Screen
7from entertainerlib.frontend.gui.user_event import UserEvent7from entertainerlib.gui.user_event import UserEvent
8from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.gui.widgets.label import Label
9from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea9from entertainerlib.gui.widgets.scroll_area import ScrollArea
10from entertainerlib.frontend.gui.widgets.texture import Texture10from entertainerlib.gui.widgets.texture import Texture
11from entertainerlib.utils.feed_utils import FeedEntryParser11from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
1212
13class FeedEntry(Screen):13class FeedEntry(Screen):
14 '''Screen displays one feed entry.'''14 '''Screen displays one feed entry.'''
1515
=== modified file 'entertainerlib/gui/screens/main.py'
--- entertainerlib/frontend/gui/screens/main.py 2009-07-19 19:27:31 +0000
+++ entertainerlib/gui/screens/main.py 2010-01-23 03:34:11 +0000
@@ -3,14 +3,13 @@
33
4import clutter4import clutter
55
6from entertainerlib.frontend.gui.screens.screen import Screen6from entertainerlib.gui.screens.screen import Screen
7from entertainerlib.frontend.gui.widgets.clock_label import ClockLabel7from entertainerlib.gui.widgets.clock_label import ClockLabel
8from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.gui.widgets.label import Label
9from entertainerlib.frontend.gui.widgets.scroll_menu import ScrollMenu9from entertainerlib.gui.widgets.scroll_menu import ScrollMenu
10from entertainerlib.frontend.gui.widgets.texture import Texture10from entertainerlib.gui.widgets.texture import Texture
11from entertainerlib.frontend.gui.widgets.text_menu import TextMenu11from entertainerlib.gui.widgets.text_menu import TextMenu
12from entertainerlib.utils.cd_utils import eject_cd12from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
13from entertainerlib.utils.feed_utils import FeedEntryParser
1413
15class Main(Screen):14class Main(Screen):
16 '''Screen displayed when frontend is opened and provides main navigation.'''15 '''Screen displayed when frontend is opened and provides main navigation.'''
@@ -76,12 +75,9 @@
76 menu.add_item(_("Photographs"), "photo")75 menu.add_item(_("Photographs"), "photo")
77 menu.add_item(_("Headlines"), "rss")76 menu.add_item(_("Headlines"), "rss")
7877
79 if self.config.display_weather_in_frontend():78 if self.config.display_weather_in_client:
80 menu.add_item(_("Weather"), "weather")79 menu.add_item(_("Weather"), "weather")
8180
82 if self.config.display_cd_eject_in_frontend():
83 menu.add_item(_("Eject CD"), "eject_cd")
84
85 if self.media_player.has_media():81 if self.media_player.has_media():
86 menu.add_item(_("Playing now..."), "playing")82 menu.add_item(_("Playing now..."), "playing")
8783
@@ -90,7 +86,7 @@
9086
91 # Menu position87 # Menu position
92 menu_clip = menu.visible_items * 7088 menu_clip = menu.visible_items * 70
93 menu_y = int((self.config.get_stage_height() - menu_clip + 10) / 2)89 menu_y = int((self.config.stage_height - menu_clip + 10) / 2)
94 menu.set_position(self.get_abs_x(0.75), menu_y)90 menu.set_position(self.get_abs_x(0.75), menu_y)
9591
96 return menu92 return menu
@@ -222,8 +218,8 @@
222218
223 # If the preview was updated fade it in.219 # If the preview was updated fade it in.
224 if update:220 if update:
225 fade_in = clutter.Timeline(20, 60)221 fade_in = clutter.Timeline(500)
226 alpha_in = clutter.Alpha(fade_in, clutter.smoothstep_inc_func)222 alpha_in = clutter.Alpha(fade_in, clutter.EASE_IN_OUT_SINE)
227 self.behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)223 self.behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)
228 self.behaviour.apply(self.preview)224 self.behaviour.apply(self.preview)
229 fade_in.start()225 fade_in.start()
@@ -288,8 +284,6 @@
288 self.callback("video")284 self.callback("video")
289 elif item.get_name() == "weather":285 elif item.get_name() == "weather":
290 self.callback("weather")286 self.callback("weather")
291 elif item.get_name() == "eject_cd":
292 eject_cd()
293 elif item.get_name() == "photo":287 elif item.get_name() == "photo":
294 self.callback("photo_albums")288 self.callback("photo_albums")
295 elif item.get_name() == "rss":289 elif item.get_name() == "rss":
296290
=== modified file 'entertainerlib/gui/screens/movie.py'
--- entertainerlib/frontend/gui/screens/movie.py 2009-07-21 22:22:53 +0000
+++ entertainerlib/gui/screens/movie.py 2010-01-23 03:34:11 +0000
@@ -5,14 +5,13 @@
5import gtk5import gtk
6import pango6import pango
77
8from entertainerlib.frontend.gui.screens.screen import Screen8from entertainerlib.gui.screens.screen import Screen
9from entertainerlib.frontend.gui.user_event import UserEvent9from entertainerlib.gui.user_event import UserEvent
10from entertainerlib.frontend.gui.widgets.eyecandy_texture import (10from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
11 EyeCandyTexture)11from entertainerlib.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.label import Label12from entertainerlib.gui.widgets.scroll_area import ScrollArea
13from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea13from entertainerlib.gui.widgets.texture import Texture
14from entertainerlib.frontend.gui.widgets.texture import Texture14from entertainerlib.gui.widgets.text_menu import TextMenu
15from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
1615
17class Movie(Screen):16class Movie(Screen):
18 '''Screen contains information of one movie.'''17 '''Screen contains information of one movie.'''
@@ -85,10 +84,14 @@
8584
86 # Stars (rating)85 # Stars (rating)
87 star = Texture(self.theme.getImage("star"))86 star = Texture(self.theme.getImage("star"))
87 star.hide()
88 self.add(star)
88 star2 = Texture(self.theme.getImage("star2"))89 star2 = Texture(self.theme.getImage("star2"))
90 star2.hide()
91 self.add(star2)
8992
90 for i in range(self.movie.get_rating()):93 for i in range(self.movie.get_rating()):
91 tex = clutter.CloneTexture(star)94 tex = clutter.Clone(star)
92 tex.set_position(95 tex.set_position(
93 self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * i),96 self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * i),
94 self.get_abs_y(0.17))97 self.get_abs_y(0.17))
@@ -97,7 +100,7 @@
97100
98 dark_star = 5 - self.movie.get_rating()101 dark_star = 5 - self.movie.get_rating()
99 for i in range(dark_star):102 for i in range(dark_star):
100 tex = clutter.CloneTexture(star2)103 tex = clutter.Clone(star2)
101 tex.set_position(self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * \104 tex.set_position(self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * \
102 (i+ self.movie.get_rating())), self.get_abs_y(0.17))105 (i+ self.movie.get_rating())), self.get_abs_y(0.17))
103 tex.set_size(self.get_abs_x(0.024), self.get_abs_y(0.04))106 tex.set_size(self.get_abs_x(0.024), self.get_abs_y(0.04))
104107
=== modified file 'entertainerlib/gui/screens/music.py'
--- entertainerlib/frontend/gui/screens/music.py 2009-05-11 11:43:56 +0000
+++ entertainerlib/gui/screens/music.py 2010-01-23 03:34:11 +0000
@@ -1,12 +1,13 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Music - Screen allows user to select music he/she wants to listen'''2'''Music - Screen allows user to select music he/she wants to listen'''
33
4from entertainerlib.frontend.gui.screens.screen import Screen4from entertainerlib.gui.screens.screen import Screen
5from entertainerlib.frontend.gui.tabs.albums_tab import AlbumsTab5from entertainerlib.gui.tabs.albums_tab import AlbumsTab
6from entertainerlib.frontend.gui.tabs.artists_tab import ArtistsTab6from entertainerlib.gui.tabs.artists_tab import ArtistsTab
7from entertainerlib.frontend.gui.widgets.label import Label7from entertainerlib.gui.widgets.label import Label
8from entertainerlib.frontend.gui.widgets.texture import Texture8from entertainerlib.gui.widgets.texture import Texture
9from entertainerlib.frontend.medialibrary.playlist import Playlist9from entertainerlib.client.medialibrary.playlist import Playlist
10
1011
11class Music(Screen):12class Music(Screen):
12 '''Screen that allows user to browse music library content.'''13 '''Screen that allows user to browse music library content.'''
1314
=== modified file 'entertainerlib/gui/screens/photo.py'
--- entertainerlib/frontend/gui/screens/photo.py 2009-05-21 16:12:09 +0000
+++ entertainerlib/gui/screens/photo.py 2010-01-23 03:34:11 +0000
@@ -4,9 +4,9 @@
4import clutter4import clutter
5import gobject5import gobject
66
7from entertainerlib.frontend.gui.screens.screen import Screen7from entertainerlib.gui.screens.screen import Screen
8from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.gui.widgets.label import Label
9from entertainerlib.frontend.gui.widgets.texture import Texture9from entertainerlib.gui.widgets.texture import Texture
1010
11class Photo(Screen):11class Photo(Screen):
12 '''Screen displays photograph in fullscreen and allows user to zoom in.'''12 '''Screen displays photograph in fullscreen and allows user to zoom in.'''
@@ -18,8 +18,8 @@
18 def __init__(self, current_photo_index, images):18 def __init__(self, current_photo_index, images):
19 Screen.__init__(self, 'Photo')19 Screen.__init__(self, 'Photo')
2020
21 self.animate = self.config.show_effects()21 self.animate = self.config.show_effects
22 self.slideshow_step = self.config.get_slideshow_step()22 self.slideshow_step = self.config.slideshow_step
2323
24 self.zoom_level = 124 self.zoom_level = 1
25 self.index = current_photo_index25 self.index = current_photo_index
@@ -30,8 +30,8 @@
3030
31 # Create black background31 # Create black background
32 self.background = clutter.Rectangle()32 self.background = clutter.Rectangle()
33 self.background.set_size(self.config.get_stage_width(),33 self.background.set_size(
34 self.config.get_stage_height())34 self.config.stage_width, self.config.stage_height)
35 self.background.set_color((0, 0, 0, 255))35 self.background.set_color((0, 0, 0, 255))
36 self.add(self.background)36 self.add(self.background)
3737
@@ -60,9 +60,9 @@
60 self.texture = Texture(image.get_filename())60 self.texture = Texture(image.get_filename())
61 self._scale_image(self.texture)61 self._scale_image(self.texture)
6262
63 timeline = clutter.Timeline(fps=120, duration=1000)63 timeline = clutter.Timeline(1000)
64 alpha = clutter.Alpha(timeline, clutter.smoothstep_inc_func)64 alpha = clutter.Alpha(timeline, clutter.EASE_IN_OUT_SINE)
65 self.opacity_behaviour = clutter.BehaviourOpacity(alpha=alpha, 65 self.opacity_behaviour = clutter.BehaviourOpacity(alpha=alpha,
66 opacity_start=0, opacity_end=255)66 opacity_start=0, opacity_end=255)
67 self.opacity_behaviour.apply(self.texture)67 self.opacity_behaviour.apply(self.texture)
68 self.texture.set_opacity(0)68 self.texture.set_opacity(0)
@@ -79,34 +79,33 @@
79 # Center position when zoomed79 # Center position when zoomed
80 width = texture.get_width()80 width = texture.get_width()
81 height = texture.get_height()81 height = texture.get_height()
82 x_ratio = self.config.get_stage_width() / float(width)82 x_ratio = self.config.stage_width / float(width)
83 y_ratio = self.config.get_stage_height() / float(height)83 y_ratio = self.config.stage_height / float(height)
8484
85 if x_ratio > y_ratio:85 if x_ratio > y_ratio:
86 texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)86 texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
87 texture.set_scale(87 texture.set_scale(
88 self.config.get_stage_height() / float(height) * zoom_level,88 self.config.stage_height / float(height) * zoom_level,
89 self.config.get_stage_height() / float(height) * zoom_level)89 self.config.stage_height / float(height) * zoom_level)
90 texture.set_anchor_point(0, 0)90 texture.set_anchor_point(0, 0)
9191
92 if zoom_level == 1: # Center image if in normal size92 if zoom_level == 1: # Center image if in normal size
93 new_width = int(width *93 new_width = int(width *
94 (self.config.get_stage_height() / float(height)))94 (self.config.stage_height / float(height)))
95 new_x = int(95 new_x = int(
96 (self.config.get_stage_width() - new_width) / float(2))96 (self.config.stage_width - new_width) / float(2))
97 texture.set_position(new_x, 0)97 texture.set_position(new_x, 0)
98 else:98 else:
99 texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)99 texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
100 texture.set_scale(100 texture.set_scale(
101 self.config.get_stage_width() / float(width) * zoom_level,101 self.config.stage_width / float(width) * zoom_level,
102 self.config.get_stage_width() / float(width) * zoom_level)102 self.config.stage_width / float(width) * zoom_level)
103 texture.set_anchor_point(0, 0)103 texture.set_anchor_point(0, 0)
104104
105 if zoom_level == 1: # Center image if in normal size105 if zoom_level == 1: # Center image if in normal size
106 new_height = int(106 new_height = int(
107 height * (self.config.get_stage_width() / float(width)))107 height * (self.config.stage_width / float(width)))
108 new_y = int(108 new_y = int((self.config.stage_height - new_height) / float(2))
109 (self.config.get_stage_height() - new_height) / float(2))
110 texture.set_position(0, new_y)109 texture.set_position(0, new_y)
111110
112 def is_interested_in_play_action(self):111 def is_interested_in_play_action(self):
113112
=== modified file 'entertainerlib/gui/screens/photo_albums.py'
--- entertainerlib/frontend/gui/screens/photo_albums.py 2009-07-19 19:27:31 +0000
+++ entertainerlib/gui/screens/photo_albums.py 2010-01-23 03:34:11 +0000
@@ -6,13 +6,12 @@
6import gobject6import gobject
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.screens.screen import Screen9from entertainerlib.gui.screens.screen import Screen
10from entertainerlib.frontend.gui.widgets.eyecandy_texture import (10from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
11 EyeCandyTexture)11from entertainerlib.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.label import Label12from entertainerlib.gui.widgets.list_indicator import ListIndicator
13from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator13from entertainerlib.gui.widgets.texture import Texture
14from entertainerlib.frontend.gui.widgets.texture import Texture14from entertainerlib.gui.widgets.text_menu import TextMenu
15from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
1615
17class PhotoAlbums(Screen):16class PhotoAlbums(Screen):
18 '''Screen contains a list of photo albums and album previews.'''17 '''Screen contains a list of photo albums and album previews.'''
@@ -159,15 +158,15 @@
159158
160 new = self._create_album_preview(album)159 new = self._create_album_preview(album)
161160
162 if self.config.show_effects():161 if self.config.show_effects:
163 old = self.preview162 old = self.preview
164 new.set_opacity(0)163 new.set_opacity(0)
165 self.preview = new164 self.preview = new
166 self.add(self.preview)165 self.add(self.preview)
167166
168 #Fade out timeline167 #Fade out timeline
169 timeline1 = clutter.Timeline(10, 26)168 timeline1 = clutter.Timeline(500)
170 alpha1 = clutter.Alpha(timeline1, clutter.smoothstep_inc_func)169 alpha1 = clutter.Alpha(timeline1, clutter.EASE_IN_OUT_SINE)
171 self.out_opacity = clutter.BehaviourOpacity(255, 0, alpha1)170 self.out_opacity = clutter.BehaviourOpacity(255, 0, alpha1)
172 self.out_opacity.apply(old)171 self.out_opacity.apply(old)
173172
@@ -175,8 +174,8 @@
175 old)174 old)
176175
177 # Fade in timeline176 # Fade in timeline
178 timeline2 = clutter.Timeline(10, 26)177 timeline2 = clutter.Timeline(500)
179 alpha2 = clutter.Alpha(timeline2, clutter.smoothstep_inc_func)178 alpha2 = clutter.Alpha(timeline2, clutter.EASE_IN_OUT_SINE)
180 self.in_opacity = clutter.BehaviourOpacity(0, 255, alpha2)179 self.in_opacity = clutter.BehaviourOpacity(0, 255, alpha2)
181 self.in_opacity.apply(new)180 self.in_opacity.apply(new)
182181
@@ -211,16 +210,16 @@
211 """210 """
212 if len(self.preview_textures)<=1:211 if len(self.preview_textures)<=1:
213 self.preview_textures[0].set_opacity(255)212 self.preview_textures[0].set_opacity(255)
214 elif self.config.show_effects():213 elif self.config.show_effects:
215 #Fade out timeline214 #Fade out timeline
216 fade_out = clutter.Timeline(40, 26)215 fade_out = clutter.Timeline(500)
217 alpha_out = clutter.Alpha(fade_out, clutter.smoothstep_inc_func)216 alpha_out = clutter.Alpha(fade_out, clutter.EASE_IN_OUT_SINE)
218 self.out_behaviour = clutter.BehaviourOpacity(255, 0, alpha_out)217 self.out_behaviour = clutter.BehaviourOpacity(255, 0, alpha_out)
219 self.out_behaviour.apply(self.preview_textures[0])218 self.out_behaviour.apply(self.preview_textures[0])
220219
221 # Fade in timeline220 # Fade in timeline
222 fade_in = clutter.Timeline(40, 26)221 fade_in = clutter.Timeline(500)
223 alpha_in = clutter.Alpha(fade_in, clutter.smoothstep_inc_func)222 alpha_in = clutter.Alpha(fade_in, clutter.EASE_IN_OUT_SINE)
224 self.in_behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)223 self.in_behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)
225 self.in_behaviour.apply(self.preview_textures[1])224 self.in_behaviour.apply(self.preview_textures[1])
226225
227226
=== modified file 'entertainerlib/gui/screens/photographs.py'
--- entertainerlib/frontend/gui/screens/photographs.py 2009-07-14 11:06:21 +0000
+++ entertainerlib/gui/screens/photographs.py 2010-01-23 03:34:11 +0000
@@ -3,13 +3,12 @@
33
4import pango4import pango
55
6from entertainerlib.frontend.gui.screens.screen import Screen6from entertainerlib.gui.screens.screen import Screen
7from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu7from entertainerlib.gui.widgets.image_menu import ImageMenu
8from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.gui.widgets.label import Label
9from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator9from entertainerlib.gui.widgets.list_indicator import ListIndicator
10from entertainerlib.frontend.gui.widgets.loading_animation import (10from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
11 LoadingAnimation)11from entertainerlib.gui.widgets.texture import Texture
12from entertainerlib.frontend.gui.widgets.texture import Texture
1312
14class Photographs(Screen):13class Photographs(Screen):
15 '''Screen displays a grid of selectable photograph thumbnails.'''14 '''Screen displays a grid of selectable photograph thumbnails.'''
1615
=== modified file 'entertainerlib/gui/screens/question.py'
--- entertainerlib/frontend/gui/screens/question.py 2009-06-27 09:03:33 +0000
+++ entertainerlib/gui/screens/question.py 2010-01-23 03:34:11 +0000
@@ -1,9 +1,9 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Question - Screen displays a question label and specified buttons.'''2'''Question - Screen displays a question label and specified buttons.'''
33
4from entertainerlib.frontend.gui.screens.screen import Screen4from entertainerlib.gui.screens.screen import Screen
5from entertainerlib.frontend.gui.widgets.label import Label5from entertainerlib.gui.widgets.label import Label
6from entertainerlib.frontend.gui.widgets.text_menu import TextMenu6from entertainerlib.gui.widgets.text_menu import TextMenu
77
8class Question(Screen):8class Question(Screen):
9 '''Screen is displayed when the application needs to ask a close ended9 '''Screen is displayed when the application needs to ask a close ended
1010
=== modified file 'entertainerlib/gui/screens/rss.py'
--- entertainerlib/frontend/gui/screens/rss.py 2009-07-19 19:27:31 +0000
+++ entertainerlib/gui/screens/rss.py 2010-01-23 03:34:11 +0000
@@ -3,12 +3,12 @@
33
4import pango4import pango
55
6from entertainerlib.frontend.gui.screens.screen import Screen6from entertainerlib.gui.screens.screen import Screen
7from entertainerlib.frontend.gui.widgets.label import Label7from entertainerlib.gui.widgets.label import Label
8from entertainerlib.frontend.gui.widgets.texture import Texture8from entertainerlib.gui.widgets.texture import Texture
9from entertainerlib.frontend.gui.widgets.text_menu import TextMenu9from entertainerlib.gui.widgets.text_menu import TextMenu
10from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator10from entertainerlib.gui.widgets.list_indicator import ListIndicator
11from entertainerlib.utils.feed_utils import FeedEntryParser11from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
1212
13class Rss(Screen):13class Rss(Screen):
14 '''Screen displays RSS-feed titles and number of entries.'''14 '''Screen displays RSS-feed titles and number of entries.'''
1515
=== modified file 'entertainerlib/gui/screens/screen.py'
--- entertainerlib/frontend/gui/screens/screen.py 2009-05-28 20:15:47 +0000
+++ entertainerlib/gui/screens/screen.py 2010-01-23 03:34:11 +0000
@@ -6,9 +6,9 @@
6import clutter6import clutter
77
8from entertainerlib.exceptions import ScreenException8from entertainerlib.exceptions import ScreenException
9from entertainerlib.frontend.gui.user_event import UserEvent9from entertainerlib.gui.user_event import UserEvent
10from entertainerlib.frontend.gui.widgets.base import Base10from entertainerlib.gui.widgets.base import Base
11from entertainerlib.frontend.gui.widgets.tab_group import TabGroup11from entertainerlib.gui.widgets.tab_group import TabGroup
1212
13class Screen(Base, clutter.Group):13class Screen(Base, clutter.Group):
14 """14 """
@@ -61,9 +61,7 @@
61 })61 })
6262
63 rect = clutter.Rectangle()63 rect = clutter.Rectangle()
64 rect.set_size(64 rect.set_size(self.config.stage_width, self.config.stage_height)
65 self.config.get_stage_width(),
66 self.config.get_stage_height())
67 rect.hide()65 rect.hide()
68 self.add(rect)66 self.add(rect)
6967
7068
=== modified file 'entertainerlib/gui/screens/tv_episodes.py'
--- entertainerlib/frontend/gui/screens/tv_episodes.py 2009-07-21 22:22:53 +0000
+++ entertainerlib/gui/screens/tv_episodes.py 2010-01-23 03:34:11 +0000
@@ -4,13 +4,12 @@
4import gtk4import gtk
5import pango5import pango
66
7from entertainerlib.frontend.gui.screens.screen import Screen7from entertainerlib.gui.screens.screen import Screen
8from entertainerlib.frontend.gui.widgets.eyecandy_texture import (8from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
9 EyeCandyTexture)9from entertainerlib.gui.widgets.label import Label
10from entertainerlib.frontend.gui.widgets.label import Label10from entertainerlib.gui.widgets.list_indicator import ListIndicator
11from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator11from entertainerlib.gui.widgets.text_menu import TextMenu
12from entertainerlib.frontend.gui.widgets.text_menu import TextMenu12from entertainerlib.gui.widgets.scroll_area import ScrollArea
13from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea
1413
15class TvEpisodes(Screen):14class TvEpisodes(Screen):
16 '''Screen contains list of all episodes of one specific season.'''15 '''Screen contains list of all episodes of one specific season.'''
@@ -101,6 +100,7 @@
101 self.menu.selected_userdata.get_title(),100 self.menu.selected_userdata.get_title(),
102 font_weight="bold")101 font_weight="bold")
103 self.title.set_ellipsize(pango.ELLIPSIZE_END)102 self.title.set_ellipsize(pango.ELLIPSIZE_END)
103 self.title.set_line_wrap(False)
104 self.title.width = 0.4104 self.title.width = 0.4
105 self.add(self.title)105 self.add(self.title)
106106
107107
=== modified file 'entertainerlib/gui/screens/tv_series.py'
--- entertainerlib/frontend/gui/screens/tv_series.py 2009-07-12 07:57:05 +0000
+++ entertainerlib/gui/screens/tv_series.py 2010-01-23 03:34:11 +0000
@@ -3,12 +3,11 @@
33
4import gtk4import gtk
55
6from entertainerlib.frontend.gui.screens.screen import Screen6from entertainerlib.gui.screens.screen import Screen
7from entertainerlib.frontend.gui.widgets.eyecandy_texture import (7from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
8 EyeCandyTexture)8from entertainerlib.gui.widgets.label import Label
9from entertainerlib.frontend.gui.widgets.label import Label9from entertainerlib.gui.widgets.list_indicator import ListIndicator
10from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator10from entertainerlib.gui.widgets.text_menu import TextMenu
11from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
1211
13class TvSeries(Screen):12class TvSeries(Screen):
14 '''Screen that contains all seasons of one TV series.'''13 '''Screen that contains all seasons of one TV series.'''
1514
=== modified file 'entertainerlib/gui/screens/video.py'
--- entertainerlib/frontend/gui/screens/video.py 2009-05-06 03:40:22 +0000
+++ entertainerlib/gui/screens/video.py 2010-01-23 03:34:11 +0000
@@ -1,11 +1,12 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Video - Screen allows user to browse video library content'''2'''Video - Screen allows user to browse video library content'''
33
4from entertainerlib.frontend.gui.screens.screen import Screen4from entertainerlib.gui.screens.screen import Screen
5from entertainerlib.frontend.gui.tabs.movies_tab import MoviesTab5from entertainerlib.gui.tabs.movies_tab import MoviesTab
6from entertainerlib.frontend.gui.tabs.series_tab import SeriesTab6from entertainerlib.gui.tabs.series_tab import SeriesTab
7from entertainerlib.frontend.gui.tabs.video_clips_tab import VideoClipsTab7from entertainerlib.gui.tabs.video_clips_tab import VideoClipsTab
8from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.gui.widgets.label import Label
9
910
10class Video(Screen):11class Video(Screen):
11 '''Screen contains tabs for different video types in the video library.'''12 '''Screen contains tabs for different video types in the video library.'''
1213
=== modified file 'entertainerlib/gui/screens/video_osd.py'
--- entertainerlib/frontend/gui/screens/video_osd.py 2009-05-25 11:07:55 +0000
+++ entertainerlib/gui/screens/video_osd.py 2010-01-23 03:34:11 +0000
@@ -4,10 +4,11 @@
4import gobject4import gobject
5import clutter5import clutter
66
7from entertainerlib.frontend.gui.screens.screen import Screen7from entertainerlib.gui.screens.screen import Screen
8from entertainerlib.frontend.gui.user_event import UserEvent8from entertainerlib.gui.user_event import UserEvent
9from entertainerlib.frontend.gui.widgets.progress_bar import ProgressBar9from entertainerlib.gui.widgets.progress_bar import ProgressBar
10from entertainerlib.frontend.gui.widgets.texture import Texture10from entertainerlib.gui.widgets.texture import Texture
11
1112
12class VideoOSD(Screen):13class VideoOSD(Screen):
13 '''Screen is displayed when video is being watched.14 '''Screen is displayed when video is being watched.
@@ -54,23 +55,38 @@
54 '''Create the pause, seek-backward & seek-forward textures.'''55 '''Create the pause, seek-backward & seek-forward textures.'''
55 self.pause_texture = Texture(56 self.pause_texture = Texture(
56 self.theme.getImage("media-playback-pause"), 0.5, 0.5)57 self.theme.getImage("media-playback-pause"), 0.5, 0.5)
57 self.pause_texture.set_anchor_point_from_gravity(58 self.pause_texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
58 clutter.GRAVITY_CENTER)
59 self.pause_texture.hide()59 self.pause_texture.hide()
60 self.add(self.pause_texture)60 self.add(self.pause_texture)
6161
62 pause_timeline = clutter.Timeline(fps=20, duration=2000)62 pause_in_time = clutter.Timeline(1000)
63 pause_timeline.set_loop(True)63 in_alpha_pause = clutter.Alpha(pause_in_time, clutter.EASE_IN_OUT_SINE)
64 alpha_pause = clutter.Alpha(pause_timeline, clutter.sine_func)
6564
66 self.pause_opacity = clutter.BehaviourOpacity(alpha=alpha_pause,65 self.pause_in_opacity = clutter.BehaviourOpacity(alpha=in_alpha_pause,
67 opacity_start=100, opacity_end=255)66 opacity_start=100, opacity_end=255)
68 self.pause_scale = clutter.BehaviourScale(1.0, 1.0, 1.4, 1.4,67 self.pause_in_scale = clutter.BehaviourScale(1.0, 1.0, 1.4, 1.4,
69 alpha_pause)68 in_alpha_pause)
7069
71 self.pause_opacity.apply(self.pause_texture)70 self.pause_in_opacity.apply(self.pause_texture)
72 self.pause_scale.apply(self.pause_texture)71 self.pause_in_scale.apply(self.pause_texture)
73 pause_timeline.start()72
73 pause_out_time = clutter.Timeline(1000)
74 out_alpha_pause = clutter.Alpha(pause_out_time,
75 clutter.EASE_IN_OUT_SINE)
76
77 self.pause_out_opacity = clutter.BehaviourOpacity(alpha=out_alpha_pause,
78 opacity_start=255, opacity_end=100)
79 self.pause_out_scale = clutter.BehaviourScale(1.4, 1.4, 1.0, 1.0,
80 out_alpha_pause)
81
82 self.pause_out_opacity.apply(self.pause_texture)
83 self.pause_out_scale.apply(self.pause_texture)
84
85 self.score = clutter.Score()
86 self.score.set_loop(True)
87 self.score.append(timeline=pause_in_time)
88 self.score.append(timeline=pause_out_time, parent=pause_in_time)
89 self.score.start()
7490
75 self.seekbackward_texture = Texture(91 self.seekbackward_texture = Texture(
76 self.theme.getImage("media-seek-backward"), 0.1, 0.5)92 self.theme.getImage("media-seek-backward"), 0.1, 0.5)
@@ -79,9 +95,9 @@
79 self.seekbackward_texture.set_opacity(0)95 self.seekbackward_texture.set_opacity(0)
80 self.add(self.seekbackward_texture)96 self.add(self.seekbackward_texture)
8197
82 self.seekbackward_timeline = clutter.Timeline(fps=120, duration=1000)98 self.seekbackward_timeline = clutter.Timeline(1000)
83 alpha_seekbackward = clutter.Alpha(self.seekbackward_timeline,99 alpha_seekbackward = clutter.Alpha(self.seekbackward_timeline,
84 clutter.smoothstep_inc_func)100 clutter.EASE_IN_OUT_SINE)
85101
86 self.seekbackward_opacity = clutter.BehaviourOpacity(102 self.seekbackward_opacity = clutter.BehaviourOpacity(
87 alpha=alpha_seekbackward, opacity_start=255, opacity_end=0)103 alpha=alpha_seekbackward, opacity_start=255, opacity_end=0)
@@ -94,9 +110,9 @@
94 self.seekforward_texture.set_opacity(0)110 self.seekforward_texture.set_opacity(0)
95 self.add(self.seekforward_texture)111 self.add(self.seekforward_texture)
96112
97 self.seekforward_timeline = clutter.Timeline(fps=120, duration=1000)113 self.seekforward_timeline = clutter.Timeline(1000)
98 alpha_seekforward = clutter.Alpha(self.seekforward_timeline,114 alpha_seekforward = clutter.Alpha(self.seekforward_timeline,
99 clutter.smoothstep_inc_func)115 clutter.EASE_IN_OUT_SINE)
100116
101 self.seekforward_opacity = clutter.BehaviourOpacity(117 self.seekforward_opacity = clutter.BehaviourOpacity(
102 alpha=alpha_seekforward, opacity_start=255, opacity_end=0)118 alpha=alpha_seekforward, opacity_start=255, opacity_end=0)
@@ -130,8 +146,8 @@
130 self.timeout_key = None # This is used when canceling timeouts146 self.timeout_key = None # This is used when canceling timeouts
131 for texture in self.aspect_textures:147 for texture in self.aspect_textures:
132 texture.position = (148 texture.position = (
133 float(self.config.get_stage_width() - texture.get_width()) /149 float(self.config.stage_width - texture.get_width()) /
134 (self.config.get_stage_width() * 2), 0.67)150 (self.config.stage_width * 2), 0.67)
135151
136 def _hide_aspect_ratio_logo(self, number):152 def _hide_aspect_ratio_logo(self, number):
137 '''153 '''
138154
=== modified file 'entertainerlib/gui/screens/weather.py'
--- entertainerlib/frontend/gui/screens/weather.py 2009-07-12 19:10:32 +0000
+++ entertainerlib/gui/screens/weather.py 2010-01-23 03:34:11 +0000
@@ -3,10 +3,11 @@
33
4import clutter4import clutter
55
6from entertainerlib.frontend.gui.screens.screen import Screen6from entertainerlib.gui.screens.screen import Screen
7from entertainerlib.frontend.gui.widgets.label import Label7from entertainerlib.gui.widgets.label import Label
8from entertainerlib.frontend.gui.widgets.texture import Texture8from entertainerlib.gui.widgets.texture import Texture
9from entertainerlib.utils.weather import Weather9from entertainerlib.weather import Weather
10
1011
11class WeatherScreen(Screen):12class WeatherScreen(Screen):
12 '''Screen to display the user's set weather location.'''13 '''Screen to display the user's set weather location.'''
@@ -17,7 +18,7 @@
17 # Screen Title18 # Screen Title
18 self.add(Label(0.13, "screentitle", 0, 0.87, _("Weather")))19 self.add(Label(0.13, "screentitle", 0, 0.87, _("Weather")))
1920
20 self.weather = Weather(self.config.get_weather_location())21 self.weather = Weather(self.config.weather_location)
2122
22 location = Label(0.04167, "text", 0.50, 0.13, self.weather.location,23 location = Label(0.04167, "text", 0.50, 0.13, self.weather.location,
23 font_weight="bold")24 font_weight="bold")
2425
=== renamed file 'entertainerlib/utils/system_tray_icon.py' => 'entertainerlib/gui/system_tray_icon.py'
--- entertainerlib/utils/system_tray_icon.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/gui/system_tray_icon.py 2010-01-23 03:34:11 +0000
@@ -4,19 +4,16 @@
4import os4import os
55
6import gtk6import gtk
7import gtk.glade7
88from entertainerlib.configuration import Configuration
9from entertainerlib.utils.log_viewer import LogViewer9from entertainerlib.dialog import ManagerDialog, LogViewer
10from entertainerlib.utils.preferences_dialog import PreferencesDialog10
11from entertainerlib.utils.content_management_dialog import (
12 ContentManagementDialog)
13from entertainerlib.utils.configuration import Configuration
1411
15class SystemTrayIcon:12class SystemTrayIcon:
16 """Implements system tray icon for entertainer."""13 """Implements system tray icon for entertainer."""
1714
18 FILE_DIR = os.path.dirname(__file__)15 FILE_DIR = os.path.dirname(__file__)
19 GLADE_DIR = os.path.join(FILE_DIR, "glade")16 UI_DIR = os.path.join(FILE_DIR, '..', 'uis')
2017
21 def __init__(self, quit_callback, toggle_interface_visibility_callback):18 def __init__(self, quit_callback, toggle_interface_visibility_callback):
22 '''Create the system tray icon and pop-up menu for it.'''19 '''Create the system tray icon and pop-up menu for it.'''
@@ -32,24 +29,24 @@
32 self.icon_widget = gtk.StatusIcon()29 self.icon_widget = gtk.StatusIcon()
33 self.icon_widget.set_tooltip(_("Entertainer Server"))30 self.icon_widget.set_tooltip(_("Entertainer Server"))
3431
35 # Load glade files32 # Load UI with gtk.Builder
36 self.menu_widgets = gtk.glade.XML(33 uifile = os.path.join(self.UI_DIR, 'system_tray_icon_menu.ui')
37 os.path.join(self.GLADE_DIR, "system_tray_icon_menu.glade"))34 self.menu_widgets = gtk.Builder()
35 self.menu_widgets.set_translation_domain('entertainer')
36 self.menu_widgets.add_from_file(uifile)
3837
39 # Bind menu signals38 # Bind menu signals
40 callback_dic = {"on_menuitem_frontend_activate"39 callback_dic = {"on_menuitem_client_activate"
41 : self.on_menuitem_frontend_activate,40 : self.on_menuitem_client_activate,
42 "on_menuitem_preferences_activate"41 "on_menuitem_manager_activate"
43 : self.on_menuitem_preferences_activate,42 : self.on_menuitem_manager_activate,
44 "on_menuitem_content_management_activate"
45 : self.on_menuitem_content_management_activate,
46 "on_menuitem_log_viewer_activate"43 "on_menuitem_log_viewer_activate"
47 : self.on_menuitem_log_viewer_activate,44 : self.on_menuitem_log_viewer_activate,
48 "on_menuitem_quit_activate"45 "on_menuitem_quit_activate"
49 : self.on_menuitem_quit_activate46 : self.on_menuitem_quit_activate
50 }47 }
51 self.menu_widgets.signal_autoconnect(callback_dic)48 self.menu_widgets.connect_signals(callback_dic)
52 self.popup = self.menu_widgets.get_widget("SystemTrayIconMenu")49 self.popup = self.menu_widgets.get_object("SystemTrayIconMenu")
5350
54 # Check if running from a branch to set the tray icon51 # Check if running from a branch to set the tray icon
55 if (os.path.exists(self.tray_icon_url)):52 if (os.path.exists(self.tray_icon_url)):
@@ -62,7 +59,7 @@
62 self.icon_widget.connect('popup-menu', self.open_popup_menu)59 self.icon_widget.connect('popup-menu', self.open_popup_menu)
6360
64 def systray_icon_activated(self, widget, data= None):61 def systray_icon_activated(self, widget, data= None):
65 """Switch visibility of frontend when system tray icon is clicked"""62 """Switch visibility of client when system tray icon is clicked"""
66 self.toggle_interface_visibility_callback()63 self.toggle_interface_visibility_callback()
6764
68 def open_popup_menu(self, widget, button, time, data = None):65 def open_popup_menu(self, widget, button, time, data = None):
@@ -70,17 +67,13 @@
70 self.popup.show_all()67 self.popup.show_all()
71 self.popup.popup(None, None, None, 3, time)68 self.popup.popup(None, None, None, 3, time)
7269
73 def on_menuitem_frontend_activate(self, widget):70 def on_menuitem_client_activate(self, widget):
74 """Execute frontend here if not running. Show if running"""71 """Execute client here if not running. Show if running"""
75 self.set_frontend_visible(True)72 self.set_client_visible(True)
7673
77 def on_menuitem_preferences_activate(self, widget):74 def on_menuitem_manager_activate(self, widget):
78 """Executes preferences-tool."""75 '''Executes the manager dialog.'''
79 PreferencesDialog(False)76 ManagerDialog(False)
80
81 def on_menuitem_content_management_activate(self, widget):
82 """Executes content-management-tool"""
83 ContentManagementDialog(False)
8477
85 def on_menuitem_log_viewer_activate(self, widget):78 def on_menuitem_log_viewer_activate(self, widget):
86 """Display log viewer dialog"""79 """Display log viewer dialog"""
8780
=== modified file 'entertainerlib/gui/tabs/albums_tab.py'
--- entertainerlib/frontend/gui/tabs/albums_tab.py 2009-07-14 11:06:21 +0000
+++ entertainerlib/gui/tabs/albums_tab.py 2010-01-23 03:34:11 +0000
@@ -4,12 +4,11 @@
44
5import pango5import pango
66
7from entertainerlib.frontend.gui.tabs.tab import Tab7from entertainerlib.gui.tabs.tab import Tab
8from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu8from entertainerlib.gui.widgets.image_menu import ImageMenu
9from entertainerlib.frontend.gui.widgets.label import Label9from entertainerlib.gui.widgets.label import Label
10from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator10from entertainerlib.gui.widgets.list_indicator import ListIndicator
11from entertainerlib.frontend.gui.widgets.loading_animation import (11from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
12 LoadingAnimation)
1312
14class AlbumsTab(Tab):13class AlbumsTab(Tab):
15 '''Tab to show album listings'''14 '''Tab to show album listings'''
@@ -55,11 +54,13 @@
55 # Create album information (displays current menuitem information)54 # Create album information (displays current menuitem information)
56 self.album_title = Label(0.045, "title", 0.22, 0.79, "")55 self.album_title = Label(0.045, "title", 0.22, 0.79, "")
57 self.album_title.set_ellipsize(pango.ELLIPSIZE_END)56 self.album_title.set_ellipsize(pango.ELLIPSIZE_END)
57 self.album_title.set_line_wrap(False)
58 self.album_title.width = 0.36658 self.album_title.width = 0.366
59 self.add(self.album_title)59 self.add(self.album_title)
6060
61 self.album_artist = Label(0.037, "subtitle", 0.22, 0.86, "")61 self.album_artist = Label(0.037, "subtitle", 0.22, 0.86, "")
62 self.album_artist.set_ellipsize(pango.ELLIPSIZE_END)62 self.album_artist.set_ellipsize(pango.ELLIPSIZE_END)
63 self.album_artist.set_line_wrap(False)
63 self.album_artist.width = 0.36664 self.album_artist.width = 0.366
64 self.add(self.album_artist)65 self.add(self.album_artist)
6566
6667
=== modified file 'entertainerlib/gui/tabs/artists_tab.py'
--- entertainerlib/frontend/gui/tabs/artists_tab.py 2009-07-16 20:31:22 +0000
+++ entertainerlib/gui/tabs/artists_tab.py 2010-01-23 03:34:11 +0000
@@ -4,12 +4,11 @@
44
5import pango5import pango
66
7from entertainerlib.frontend.gui.tabs.tab import Tab7from entertainerlib.gui.tabs.tab import Tab
8from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.gui.widgets.label import Label
9from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator9from entertainerlib.gui.widgets.list_indicator import ListIndicator
10from entertainerlib.frontend.gui.widgets.loading_animation import (10from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
11 LoadingAnimation)11from entertainerlib.gui.widgets.text_menu import TextMenu
12from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
1312
14class ArtistsTab(Tab):13class ArtistsTab(Tab):
15 '''Tab for the music screen to show artist listings'''14 '''Tab for the music screen to show artist listings'''
@@ -38,6 +37,7 @@
38 # Create artist label37 # Create artist label
39 self.artist_title = Label(0.0416, "title", 0.22, 0.794, "")38 self.artist_title = Label(0.0416, "title", 0.22, 0.794, "")
40 self.artist_title.set_ellipsize(pango.ELLIPSIZE_END)39 self.artist_title.set_ellipsize(pango.ELLIPSIZE_END)
40 self.artist_title.set_line_wrap(False)
41 self.artist_title.width = 0.36641 self.artist_title.width = 0.366
42 self.add(self.artist_title)42 self.add(self.artist_title)
4343
@@ -47,7 +47,7 @@
47 self.artist_tracks = Label(0.0365, "subtitle", 0.22, 0.911, "")47 self.artist_tracks = Label(0.0365, "subtitle", 0.22, 0.911, "")
48 self.add(self.artist_tracks)48 self.add(self.artist_tracks)
4949
50 # Create artis menu list indicator50 # Create artist menu list indicator
51 self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,51 self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
52 ListIndicator.VERTICAL)52 ListIndicator.VERTICAL)
53 self.li.set_maximum(len(artists))53 self.li.set_maximum(len(artists))
5454
=== modified file 'entertainerlib/gui/tabs/lyrics_tab.py'
--- entertainerlib/frontend/gui/tabs/lyrics_tab.py 2009-07-22 20:46:29 +0000
+++ entertainerlib/gui/tabs/lyrics_tab.py 2010-01-23 03:34:11 +0000
@@ -4,11 +4,11 @@
4import clutter4import clutter
5import pango5import pango
66
7from entertainerlib.frontend.gui.tabs.tab import Tab7from entertainerlib.gui.tabs.tab import Tab
8from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.gui.widgets.label import Label
9from entertainerlib.frontend.gui.widgets.loading_animation import (9from entertainerlib.gui.widgets.loading_animation import (
10 LoadingAnimation)10 LoadingAnimation)
11from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea11from entertainerlib.gui.widgets.scroll_area import ScrollArea
1212
13class LyricsTab(Tab):13class LyricsTab(Tab):
14 '''Tab for the audio play screen to show lyrics'''14 '''Tab for the audio play screen to show lyrics'''
1515
=== modified file 'entertainerlib/gui/tabs/movies_tab.py'
--- entertainerlib/frontend/gui/tabs/movies_tab.py 2009-07-14 11:06:21 +0000
+++ entertainerlib/gui/tabs/movies_tab.py 2010-01-23 03:34:11 +0000
@@ -3,12 +3,11 @@
33
4import pango4import pango
55
6from entertainerlib.frontend.gui.tabs.tab import Tab6from entertainerlib.gui.tabs.tab import Tab
7from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu7from entertainerlib.gui.widgets.image_menu import ImageMenu
8from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.gui.widgets.label import Label
9from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator9from entertainerlib.gui.widgets.list_indicator import ListIndicator
10from entertainerlib.frontend.gui.widgets.loading_animation import (10from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
11 LoadingAnimation)
1211
13class MoviesTab(Tab):12class MoviesTab(Tab):
14 """13 """
@@ -95,11 +94,13 @@
95 self.movie_title = Label(0.042, "title", 0.2, 0.75, "",94 self.movie_title = Label(0.042, "title", 0.2, 0.75, "",
96 font_weight="bold")95 font_weight="bold")
97 self.movie_title.set_ellipsize(pango.ELLIPSIZE_END)96 self.movie_title.set_ellipsize(pango.ELLIPSIZE_END)
97 self.movie_title.set_line_wrap(False)
98 self.movie_title.width = 0.598 self.movie_title.width = 0.5
99 self.add(self.movie_title)99 self.add(self.movie_title)
100100
101 self.movie_info = Label(0.034, "subtitle", 0.2, 0.8, "")101 self.movie_info = Label(0.034, "subtitle", 0.2, 0.8, "")
102 self.movie_info.set_ellipsize(pango.ELLIPSIZE_END)102 self.movie_info.set_ellipsize(pango.ELLIPSIZE_END)
103 self.movie_info.set_line_wrap(False)
103 self.movie_info.width = 0.5104 self.movie_info.width = 0.5
104 self.add(self.movie_info)105 self.add(self.movie_info)
105106
106107
=== modified file 'entertainerlib/gui/tabs/playing_tab.py'
--- entertainerlib/frontend/gui/tabs/playing_tab.py 2009-05-26 17:32:56 +0000
+++ entertainerlib/gui/tabs/playing_tab.py 2010-01-23 03:34:11 +0000
@@ -3,9 +3,9 @@
33
4import pango4import pango
55
6from entertainerlib.frontend.gui.tabs.tab import Tab6from entertainerlib.gui.tabs.tab import Tab
7from entertainerlib.frontend.gui.widgets.label import Label7from entertainerlib.gui.widgets.label import Label
8from entertainerlib.frontend.gui.widgets.progress_bar import ProgressBar8from entertainerlib.gui.widgets.progress_bar import ProgressBar
99
10class PlayingTab(Tab):10class PlayingTab(Tab):
11 '''Tab for the audio play screen to show currently playing audio.'''11 '''Tab for the audio play screen to show currently playing audio.'''
1212
=== modified file 'entertainerlib/gui/tabs/series_tab.py'
--- entertainerlib/frontend/gui/tabs/series_tab.py 2009-07-14 11:06:21 +0000
+++ entertainerlib/gui/tabs/series_tab.py 2010-01-23 03:34:11 +0000
@@ -3,12 +3,11 @@
33
4import pango4import pango
55
6from entertainerlib.frontend.gui.tabs.tab import Tab6from entertainerlib.gui.tabs.tab import Tab
7from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu7from entertainerlib.gui.widgets.image_menu import ImageMenu
8from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.gui.widgets.label import Label
9from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator9from entertainerlib.gui.widgets.list_indicator import ListIndicator
10from entertainerlib.frontend.gui.widgets.loading_animation import (10from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
11 LoadingAnimation)
1211
13class SeriesTab(Tab):12class SeriesTab(Tab):
14 """13 """
@@ -94,6 +93,7 @@
94 self.series_title = Label(0.042, "title", 0.2, 0.75, "",93 self.series_title = Label(0.042, "title", 0.2, 0.75, "",
95 font_weight="bold")94 font_weight="bold")
96 self.series_title.set_ellipsize(pango.ELLIPSIZE_END)95 self.series_title.set_ellipsize(pango.ELLIPSIZE_END)
96 self.series_title.set_line_wrap(False)
97 self.series_title.width = 0.597 self.series_title.width = 0.5
98 self.add(self.series_title)98 self.add(self.series_title)
9999
100100
=== modified file 'entertainerlib/gui/tabs/tab.py'
--- entertainerlib/frontend/gui/tabs/tab.py 2009-05-21 16:12:09 +0000
+++ entertainerlib/gui/tabs/tab.py 2010-01-23 03:34:11 +0000
@@ -4,10 +4,10 @@
4import clutter4import clutter
5import gobject5import gobject
66
7from entertainerlib.frontend.gui.user_event import UserEvent7from entertainerlib.gui.user_event import UserEvent
8from entertainerlib.frontend.gui.widgets.base import Base8from entertainerlib.gui.widgets.base import Base
9from entertainerlib.frontend.gui.widgets.label import Label9from entertainerlib.gui.widgets.label import Label
10from entertainerlib.frontend.gui.widgets.texture import Texture10from entertainerlib.gui.widgets.texture import Texture
1111
12class Tab(Base, clutter.Group):12class Tab(Base, clutter.Group):
13 '''13 '''
@@ -39,9 +39,9 @@
39 }39 }
4040
41 # show/hide animation on the Tab41 # show/hide animation on the Tab
42 self.timeline = clutter.Timeline(30, 60)42 self.timeline = clutter.Timeline(500)
43 self.timeline.connect('completed', self._on_timeline_completed)43 self.timeline.connect('completed', self._on_timeline_completed)
44 self.alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func)44 self.alpha = clutter.Alpha(self.timeline, clutter.EASE_IN_OUT_SINE)
45 self.behaviour = clutter.BehaviourOpacity(0, 255, self.alpha)45 self.behaviour = clutter.BehaviourOpacity(0, 255, self.alpha)
46 self.behaviour.apply(self)46 self.behaviour.apply(self)
4747
4848
=== modified file 'entertainerlib/gui/tabs/tracks_tab.py'
--- entertainerlib/frontend/gui/tabs/tracks_tab.py 2009-07-16 20:31:22 +0000
+++ entertainerlib/gui/tabs/tracks_tab.py 2010-01-23 03:34:11 +0000
@@ -3,12 +3,11 @@
33
4import pango4import pango
55
6from entertainerlib.frontend.gui.tabs.tab import Tab6from entertainerlib.gui.tabs.tab import Tab
7from entertainerlib.frontend.gui.widgets.label import Label7from entertainerlib.gui.widgets.label import Label
8from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator8from entertainerlib.gui.widgets.list_indicator import ListIndicator
9from entertainerlib.frontend.gui.widgets.loading_animation import (9from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
10 LoadingAnimation)10from entertainerlib.gui.widgets.text_menu import TextMenu
11from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
1211
13class TracksTab(Tab):12class TracksTab(Tab):
14 '''Tab for the artist screen to show track listings'''13 '''Tab for the artist screen to show track listings'''
@@ -35,11 +34,13 @@
3534
36 self.track_title = Label(0.045, "title", 0.22, 0.79, "")35 self.track_title = Label(0.045, "title", 0.22, 0.79, "")
37 self.track_title.set_ellipsize(pango.ELLIPSIZE_END)36 self.track_title.set_ellipsize(pango.ELLIPSIZE_END)
37 self.track_title.set_line_wrap(False)
38 self.track_title.width = 0.36638 self.track_title.width = 0.366
39 self.add(self.track_title)39 self.add(self.track_title)
4040
41 self.track_number = Label(0.037, "subtitle", 0.22, 0.86, "")41 self.track_number = Label(0.037, "subtitle", 0.22, 0.86, "")
42 self.track_number.set_ellipsize(pango.ELLIPSIZE_END)42 self.track_number.set_ellipsize(pango.ELLIPSIZE_END)
43 self.track_number.set_line_wrap(False)
43 self.track_number.width = 0.36644 self.track_number.width = 0.366
44 self.add(self.track_number)45 self.add(self.track_number)
4546
4647
=== modified file 'entertainerlib/gui/tabs/video_clips_tab.py'
--- entertainerlib/frontend/gui/tabs/video_clips_tab.py 2009-07-14 11:06:21 +0000
+++ entertainerlib/gui/tabs/video_clips_tab.py 2010-01-23 03:34:11 +0000
@@ -5,12 +5,11 @@
55
6import pango6import pango
77
8from entertainerlib.frontend.gui.tabs.tab import Tab8from entertainerlib.gui.tabs.tab import Tab
9from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu9from entertainerlib.gui.widgets.image_menu import ImageMenu
10from entertainerlib.frontend.gui.widgets.label import Label10from entertainerlib.gui.widgets.label import Label
11from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator11from entertainerlib.gui.widgets.list_indicator import ListIndicator
12from entertainerlib.frontend.gui.widgets.loading_animation import (12from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
13 LoadingAnimation)
1413
15class VideoClipsTab(Tab):14class VideoClipsTab(Tab):
16 """15 """
@@ -94,11 +93,13 @@
94 self.clip_title = Label(0.042, "title", 0.15, 0.77, "",93 self.clip_title = Label(0.042, "title", 0.15, 0.77, "",
95 font_weight="bold")94 font_weight="bold")
96 self.clip_title.set_ellipsize(pango.ELLIPSIZE_END)95 self.clip_title.set_ellipsize(pango.ELLIPSIZE_END)
96 self.clip_title.set_line_wrap(False)
97 self.clip_title.width = 0.597 self.clip_title.width = 0.5
98 self.add(self.clip_title)98 self.add(self.clip_title)
9999
100 self.clip_info = Label(0.034, "subtitle", 0.15, 0.85, "")100 self.clip_info = Label(0.034, "subtitle", 0.15, 0.85, "")
101 self.clip_info.set_ellipsize(pango.ELLIPSIZE_END)101 self.clip_info.set_ellipsize(pango.ELLIPSIZE_END)
102 self.clip_info.set_line_wrap(False)
102 self.clip_info.width = 0.5103 self.clip_info.width = 0.5
103 self.add(self.clip_info)104 self.add(self.clip_info)
104105
105106
=== renamed file 'entertainerlib/utils/theme.py' => 'entertainerlib/gui/theme.py'
=== modified file 'entertainerlib/gui/transitions/factory.py'
--- entertainerlib/frontend/gui/transitions/factory.py 2009-04-28 22:30:06 +0000
+++ entertainerlib/gui/transitions/factory.py 2010-01-23 03:34:11 +0000
@@ -2,11 +2,12 @@
2'''TransitionFactory - create a transition object based on the configuration2'''TransitionFactory - create a transition object based on the configuration
3that is in the user's preferences'''3that is in the user's preferences'''
44
5from entertainerlib.frontend.gui.transitions.fade import Fade5from entertainerlib.gui.transitions.fade import Fade
6from entertainerlib.frontend.gui.transitions.no_effect import NoEffect6from entertainerlib.gui.transitions.no_effect import NoEffect
7from entertainerlib.frontend.gui.transitions.slide import Slide7from entertainerlib.gui.transitions.slide import Slide
8from entertainerlib.frontend.gui.transitions.zoom_and_fade import ZoomAndFade8from entertainerlib.gui.transitions.zoom_and_fade import ZoomAndFade
9from entertainerlib.utils.configuration import Configuration9from entertainerlib.configuration import Configuration
10
1011
11class TransitionFactory:12class TransitionFactory:
12 '''Generates a transition object based on the configuration setting'''13 '''Generates a transition object based on the configuration setting'''
@@ -31,8 +32,8 @@
31 'Zoom and fade' : self._generate_zoom_and_fade32 'Zoom and fade' : self._generate_zoom_and_fade
32 }33 }
3334
34 if self.config.show_effects():35 if self.config.show_effects:
35 kind = self.config.transition_effect()36 kind = self.config.transition_effect
36 else:37 else:
37 kind = "No effect"38 kind = "No effect"
3839
3940
=== modified file 'entertainerlib/gui/transitions/fade.py'
--- entertainerlib/frontend/gui/transitions/fade.py 2009-05-06 03:40:22 +0000
+++ entertainerlib/gui/transitions/fade.py 2010-01-23 03:34:11 +0000
@@ -2,8 +2,8 @@
2'''Fade - Transition to fade out old screen and fade in the new screen'''2'''Fade - Transition to fade out old screen and fade in the new screen'''
33
4import clutter4import clutter
5from entertainerlib.gui.transitions.transition import Transition
56
6from entertainerlib.frontend.gui.transitions.transition import Transition
77
8class Fade(Transition):8class Fade(Transition):
9 """9 """
1010
=== modified file 'entertainerlib/gui/transitions/no_effect.py'
--- entertainerlib/frontend/gui/transitions/no_effect.py 2009-05-06 03:40:22 +0000
+++ entertainerlib/gui/transitions/no_effect.py 2010-01-23 03:34:11 +0000
@@ -1,7 +1,8 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''NoEffect - Simple change transition from screen 1 to screen 2'''2'''NoEffect - Simple change transition from screen 1 to screen 2'''
33
4from entertainerlib.frontend.gui.transitions.transition import Transition4from entertainerlib.gui.transitions.transition import Transition
5
56
6class NoEffect(Transition):7class NoEffect(Transition):
7 """8 """
89
=== modified file 'entertainerlib/gui/transitions/slide.py'
--- entertainerlib/frontend/gui/transitions/slide.py 2009-05-06 03:40:22 +0000
+++ entertainerlib/gui/transitions/slide.py 2010-01-23 03:34:11 +0000
@@ -3,8 +3,8 @@
33
4import clutter4import clutter
55
6from entertainerlib.frontend.gui.transitions.transition import Transition6from entertainerlib.gui.transitions.transition import Transition
7from entertainerlib.utils.configuration import Configuration7from entertainerlib.configuration import Configuration
88
9class Slide(Transition):9class Slide(Transition):
10 '''Move screens on and off the stage through horizontal movements.'''10 '''Move screens on and off the stage through horizontal movements.'''
@@ -27,7 +27,7 @@
27 '''Execute the common sliding logic needed by each direction.27 '''Execute the common sliding logic needed by each direction.
2828
29 Direction will set the sign of width to determine which way to slide.'''29 Direction will set the sign of width to determine which way to slide.'''
30 width = self.config.get_stage_width() * direction30 width = self.config.stage_width * direction
3131
32 # Initialize to_screen for animation32 # Initialize to_screen for animation
33 to_screen.set_position(-width, 0)33 to_screen.set_position(-width, 0)
@@ -35,10 +35,12 @@
3535
36 # Slide out timeline36 # Slide out timeline
37 if from_screen is not None:37 if from_screen is not None:
38 slide_out = clutter.Timeline(20, 60)38 slide_out = clutter.Timeline(500)
39 alpha_out = clutter.Alpha(slide_out, clutter.smoothstep_inc_func)39 alpha_out = clutter.Alpha(slide_out, clutter.EASE_IN_OUT_SINE)
40 self.out_behaviour = clutter.BehaviourPath(alpha_out, ((0, 0),40 out_path = clutter.Path()
41 (width, 0)))41 out_path.add_move_to(0, 0)
42 out_path.add_line_to(width, 0)
43 self.out_behaviour = clutter.BehaviourPath(alpha_out, out_path)
42 self.out_behaviour.apply(from_screen)44 self.out_behaviour.apply(from_screen)
4345
44 if remove_screen:46 if remove_screen:
@@ -46,10 +48,12 @@
46 from_screen)48 from_screen)
4749
48 # Slide in timeline50 # Slide in timeline
49 slide_in = clutter.Timeline(20, 60)51 slide_in = clutter.Timeline(500)
50 alpha_in = clutter.Alpha(slide_in, clutter.smoothstep_inc_func)52 alpha_in = clutter.Alpha(slide_in, clutter.EASE_IN_OUT_SINE)
51 self.in_behaviour = clutter.BehaviourPath(alpha_in, ((-width, 0),53 in_path = clutter.Path()
52 (0, 0)))54 in_path.add_move_to(-width, 0)
55 in_path.add_line_to(0, 0)
56 self.in_behaviour = clutter.BehaviourPath(alpha_in, in_path)
53 self.in_behaviour.apply(to_screen)57 self.in_behaviour.apply(to_screen)
5458
55 # Start transition animating59 # Start transition animating
5660
=== modified file 'entertainerlib/gui/transitions/zoom_and_fade.py'
--- entertainerlib/frontend/gui/transitions/zoom_and_fade.py 2009-05-06 03:40:22 +0000
+++ entertainerlib/gui/transitions/zoom_and_fade.py 2010-01-23 03:34:11 +0000
@@ -2,8 +2,8 @@
2'''ZoomAndFade - Transition to zoom and fade between screens'''2'''ZoomAndFade - Transition to zoom and fade between screens'''
33
4import clutter4import clutter
5from entertainerlib.gui.transitions.transition import Transition
56
6from entertainerlib.frontend.gui.transitions.transition import Transition
77
8class ZoomAndFade(Transition):8class ZoomAndFade(Transition):
9 """9 """
1010
=== modified file 'entertainerlib/gui/user_event.py'
--- entertainerlib/frontend/gui/user_event.py 2009-05-29 10:33:32 +0000
+++ entertainerlib/gui/user_event.py 2010-01-23 03:34:11 +0000
@@ -40,7 +40,7 @@
40 USE_ASPECT_RATIO_2 = 5240 USE_ASPECT_RATIO_2 = 52
41 USE_ASPECT_RATIO_3 = 5341 USE_ASPECT_RATIO_3 = 53
42 USE_ASPECT_RATIO_4 = 5442 USE_ASPECT_RATIO_4 = 54
43 QUIT_FRONTEND = 5643 QUIT = 56
44 DEFAULT_EVENT = 5744 DEFAULT_EVENT = 57
4545
46 def __init__(self, code):46 def __init__(self, code):
4747
=== modified file 'entertainerlib/gui/user_interface.py'
--- entertainerlib/frontend/gui/user_interface.py 2009-06-29 19:41:35 +0000
+++ entertainerlib/gui/user_interface.py 2010-01-23 03:34:11 +0000
@@ -1,5 +1,5 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''UserInterface - Main window of the Entertainer frontend'''2'''UserInterface - Main window of the Entertainer client'''
33
4# Clutter uses _1 to represent the 1 key and pylint complains about it4# Clutter uses _1 to represent the 1 key and pylint complains about it
5# pylint: disable-msg=W02125# pylint: disable-msg=W0212
@@ -12,31 +12,32 @@
12import gobject12import gobject
13import gtk13import gtk
1414
15from entertainerlib.frontend.gui.screen_history import ScreenHistory15from entertainerlib.gui.widgets.volume_indicator import VolumeIndicator
16from entertainerlib.frontend.gui.screens.factory import ScreenFactory16from entertainerlib.gui.screen_history import ScreenHistory
17from entertainerlib.frontend.gui.screens.screen import Screen17from entertainerlib.gui.screens.factory import ScreenFactory
18from entertainerlib.frontend.gui.transitions.factory import TransitionFactory18from entertainerlib.gui.screens.screen import Screen
19from entertainerlib.frontend.gui.transitions.transition import Transition
20from entertainerlib.frontend.gui.user_event import UserEvent
21from entertainerlib.frontend.gui.widgets.menu_overlay import MenuOverlay
22from entertainerlib.frontend.media_player import MediaPlayer
23from entertainerlib.frontend.gui.widgets.volume_indicator import VolumeIndicator
24from entertainerlib.utils.configuration import Configuration
25from entertainerlib.utils.logger import Logger
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches