Merge lp:~rockstar/entertainer/package-structure-apocalypse into lp:entertainer/future

Proposed by Paul Hummer
Status: Merged
Approved by: Paul Hummer
Approved revision: 382
Merged at revision: not available
Proposed branch: lp:~rockstar/entertainer/package-structure-apocalypse
Merge into: lp:entertainer/future
Diff against target: None lines
To merge this branch: bzr merge lp:~rockstar/entertainer/package-structure-apocalypse
Reviewer Review Type Date Requested Status
Samuel Buffet (community) Approve
Matt Layman Approve
Review via email: mp+6249@code.launchpad.net

Commit message

Entertainer's package structure has been re-organized

To post a comment you must log in.
Revision history for this message
Paul Hummer (rockstar) wrote :

This branch is the apocalyptical package structure changes. entertainerlib.backend will disappear as soon as the new indexer lands. There are all sorts of changes here. The only real code change was the complete removal of entertainerlib.utils.cd_utils, because there was a single line single function, which I easily removed. Otherwise, I consolidated a lot of packages into single modules, and flattened the tree a bunch.

This specifically needs to land in future, because it's unnecessary when there is still a backend/frontend. Frontend was renamed to client, and will become more of a client in later branches. entertainerlib.utils was removed, and its contents shifted around.

This is probably going to cause a lot of conflicts, which is another big reason why this needs to land in future. The future branch is there solely to be liberal in the changes that need to be made, and, well, these changes are quite liberal. Tests, lint, and running entertainer all indicate everything is good, but the jury is still out, and will require A LOT of changes.

Revision history for this message
Samuel Buffet (samuel-buffet) wrote :

Hi Paul,

Good to see Entertainer becoming a client/server application.

I have some points to discuss with you before I go on deeper in the review. Most of them are related with the tree structure you've chosen.

Under entertainerlib we have :

backend
client
db
glade
gui
network
test

My questions are.

Why db isn't under backend? and why gui is not under client?

I think it's more readable and more logic because gui which contains widgets/screens ... are all used only by the *client* and *db* is used by the server (I guess).

You write that you're about to remove entertainerlib.backend but I don't understand right now why you don't rename it in entertainerlib.server? Does that mean that the server's code will take place elsewhere?

That's the first set of questions I have taking a first look to this branch.

Samuel-

Note :

Entertainer is broken if the notification icon is activated. See below the traceback :

(/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainer:31123): libglade-WARNING **: could not find glade file '/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainerlib/gui/glade/system_tray_icon_menu.glade'
Traceback (most recent call last):
  File "/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainer", line 12, in <module>
    main()
  File "/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainerlib/client/__init__.py", line 33, in main
    client_client = Client()
  File "/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainerlib/client/client.py", line 51, in __init__
    self.quit_client, self.toggle_interface_visibility)
  File "/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainerlib/gui/system_tray_icon.py", line 42, in __init__
    os.path.join(self.GLADE_DIR, "system_tray_icon_menu.glade"))
RuntimeError: could not create GladeXML object

Revision history for this message
Matt Layman (mblayman) wrote :

> Why db isn't under backend? and why gui is not under client?
>
> I think it's more readable and more logic because gui which contains
> widgets/screens ... are all used only by the *client* and *db* is used by the
> server (I guess).

I can't speak for db, but moving gui out of frontend was my request. I know that the frontend is currently the only thing that uses the gui module, but there is no true reason that gui needs to be a sub-module of frontend (it could theoretically be totally independent of the frontend). Moving gui out of frontend flattens the depth of the tree which I think is a good thing here. I have a feeling Paul would say the same thing about the db and the backend.

> Note :
> Entertainer is broken if the notification icon is activated. See below the
> traceback :

I agree that this needs to be fixed before the branch lands.

Revision history for this message
Matt Layman (mblayman) wrote :

Paul,

Here are all my comments that I recorded as I went through the diff:

What was the rationale behind putting all the dialog classes into a single file? I would suggest that we at least call it dialogs if there are going to be multiple, but it doesn't make too much sense to me to put them all in the same file, especially if our dialogs might only grow more capabilities over time (like TV stuff).

entertainerlib/backend/core/message_type_priority.py
 * FRONTEND_OPENED and FRONTEND_CLOSED should probably change to CLIENT_*. The comments with those lines don't make sense because frontend was substituted with client.

entertainerlib/client/__init__.py
 * Initial comment isn't valid.
 * client_client is weird.

entertainerlib/client/client.py
 * import order
 * class doc string has Frontend
 * logger name isn't correct, would client.Client

entertainerlib/client/media_player.py
 * import order

entertainerlib/client/medialibrary/music.py
 * import order

entertainerlib/client/medialibrary/videos.py
 * import order

entertainerlib/dialog.py
 * import order

entertainerlib/gui/screens/artist.py
 * import order

entertainerlib/gui/screens/disc.py
 * import order

entertainerlib/gui/screens/feed.py
 * import order

entertainerlib/gui/screens/feed_entry.py
 * import order

entertainerlib/gui/screens/main.py
 * import order

entertainerlib/gui/screens/music.py
 * import order

entertainerlib/gui/screens/rss.py
 * import order

entertainerlib/gui/system_tray_icon.py
 * Switch from frontend to client revealed some code that must is broken. set_client_visible has no implementation.

entertainerlib/gui/transitions/factory.py
 * import order

entertainerlib/gui/transitions/slide.py
 * import order

entertainerlib/gui/user_interface.py
 * import order
 * Uses UserEvent.QUIT_FRONTEND. That event should change to QUIT_CLIENT.

entertainerlib/gui/widgets/image_menu.py
 * import order

entertainerlib/gui/widgets/list_indicator.py
 * import order

entertainerlib/gui/widgets/tab_group.py
 * import order

entertainerlib/gui/widgets/text_menu.py
 * import order

entertainerlib/gui/widgets/texture.py
 * logger name doesn't need to refer to client anymore

entertainerlib/tests/test_logger.py
 * import order

entertainerlib/tests/test_theme.py
 * import order

entertainerlib/thumbnailer.py
 * import order

entertainerlib/utils/cd_utils.py
 * Removed, so you can link to bug 313815

setup.py
 * There is no longer a client/glade directory so 'client/glade/*' shouldn't be needed anymore.

Revision history for this message
Paul Hummer (rockstar) wrote :

On Thu, 07 May 2009 19:59:33 -0000, Samuel Buffet <email address hidden>
> My questions are.
>
> Why db isn't under backend? and why gui is not under client?

db is a core part of entertainer. I tried to make thinngs more easily
accessible, and once I get the network stuff in, it won't be used by just one
package.

> You write that you're about to remove entertainerlib.backend but I don't
> understand right now why you don't rename it in entertainerlib.server? Does
> that mean that the server's code will take place elsewhere?

Because there won't be just one entertainer server. There will be with 0.5,
but the end goal is to provide more options. I figure something like
entertainerlib.network.local.Server and entertainerlib.network.dlna.Server,
etc.

Now you'll see why db didn't stay in the backend.

Also, most of the backend code is pure crap now, being replaced, and will be
completely removed. THAT's why I didn't move it to a new location. :)

>> Note :
>
> Entertainer is broken if the notification icon is activated. See below the
> traceback :
>
> (/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainer:31123):
> libglade-WARNING **: could not find glade file
> '/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainerlib/gui/glade/system_tray_icon_menu.glade'
> Traceback (most recent call last): File
> "/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainer",
> line 12, in <module> main() File
> "/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainerlib/client/__init__.py",
> line 33, in main client_client = Client() File
> "/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainerlib/client/client.py",
> line 51, in __init__ self.quit_client, self.toggle_interface_visibility)
> File
> "/mnt/raid/Programmes/branches_entertainer/package-structure-apocalypse/entertainerlib/gui/system_tray_icon.py",
> line 42, in __init__ os.path.join(self.GLADE_DIR,
> "system_tray_icon_menu.glade")) RuntimeError: could not create GladeXML
> object
>

I don't know yet what to with the system tray icon. No one uses it, it
doesn't really fit in with what Entertainer is (a media center, not a desktop
application) and I currently don't have any plans to support it in the local
server.

--
Paul Hummer
http://theironlion.net
1024/862FF08F C921 E962 58F8 5547 6723 0E8C 1C4D 8AC5 862F F08F

Revision history for this message
Paul Hummer (rockstar) wrote :
Download full text (8.0 KiB)

> What was the rationale behind putting all the dialog classes into a single
> file? I would suggest that we at least call it dialogs if there are going
> to be multiple, but it doesn't make too much sense to me to put them all in
> the same file, especially if our dialogs might only grow more capabilities
> over time (like TV stuff).

They depend on similar things. As I was working on everything else, it only
made sense to consolidate those dialogs. I think the next logical step would
be to create an EntertainerDialog, and extend the dialogs that way. That code
is an utter mess. I think, for the time being, it should stay as the same
module, and get it cleaned up. If (and this is a big IF) we see a need to make
a package out of this, we can be confident that the code is being maintained.

> entertainerlib/backend/core/message_type_priority.py
> * FRONTEND_OPENED and FRONTEND_CLOSED should probably change to CLIENT_*.
> The comments with those lines don't make sense because frontend was
> substituted with client.

Nope, I'm not touching that message passing crap. It never worked right, and
will just get deleted when the server is implemented.

>
> entertainerlib/client/__init__.py
> * Initial comment isn't valid.
> * client_client is weird.

Yeah, I thought that too. client.client_client vs client.client, either way,
it's a bit repetitive. I was going to see if you wanted to root around in the
client code and see what you'd need.

>
> entertainerlib/client/client.py
> * import order
> * class doc string has Frontend
> * logger name isn't correct, would client.Client

I've made the changes to everything but import order. The import order will be
re-arranged before this lands in trunk, but the imports are going to be hell to
merge from trunk as it is. I'd rather do a second branch with the import order
stuff. This is the beauty of having this HIGHLY experimental branch. I think
this is an acceptable solution.

> entertainerlib/gui/system_tray_icon.py
> * Switch from frontend to client revealed some code that must is broken.
> set_client_visible has no implementation.

I don't plan on fixing this in this branch. If it's broken, it's broken. It
may not even be in the next version of Entertainer.

> entertainerlib/gui/user_interface.py
> * import order
> * Uses UserEvent.QUIT_FRONTEND. That event should change to QUIT_CLIENT.

I changed this to just QUIT. These events are only used by the client anyway,
so QUIT_CLIENT is rather redundant.

> entertainerlib/gui/widgets/texture.py
> * logger name doesn't need to refer to client anymore

Fixed.

> entertainerlib/utils/cd_utils.py
> * Removed, so you can link to bug 313815

Sweet, thanks. I figured there was a bug, but I was too lazy to go find it.
:)

>
> setup.py
> * There is no longer a client/glade directory so 'client/glade/*'
> shouldn't be needed anymore.

Fixed.

Here's the incremental diff:

=== modified file 'entertainerlib/client/__init__.py'
--- entertainerlib/client/__init__.py 2009-05-05 04:43:57 +0000
+++ entertainerlib/client/__init__.py 2009-05-09 03:14:44 +0000
@@ -1,8 +1,8 @@
-'''Frontend gui to entertainer'''
+'''Client code for Entertainer.'''
 # py...

Read more...

382. By Paul Hummer

Responded to Matt's review

Revision history for this message
Samuel Buffet (samuel-buffet) wrote :

Hi Paul,

I'm going to approve this branch as I can see on it the seeds of Entertainer's future.

> Also, most of the backend code is pure crap now, being replaced, and will be
> completely removed. THAT's why I didn't move it to a new location. :)

Okay, with those explanations.

> I don't know yet what to with the system tray icon. No one uses it, it
> doesn't really fit in with what Entertainer is (a media center, not a desktop
> application) and I currently don't have any plans to support it in the local
> server.

Well, I do use it. Also, I think this is not good at all to have *broken* code.
So it turns out to me that either we have to fix this or we remove all the code
related to this notification icon. But, as we're not really sure of the future of
this feature and as fixing this should be rather trivial, I'd prefer the fix
solution.

> They depend on similar things. As I was working on everything else, it only
> made sense to consolidate those dialogs. I think the next logical step would
> be to create an EntertainerDialog, and extend the dialogs that way. That code
> is an utter mess. I think, for the time being, it should stay as the same
> module, and get it cleaned up. If (and this is a big IF) we see a need to
> make
> a package out of this, we can be confident that the code is being maintained.

I'm not sure it's a good idea. I tempted to say "+1 with Matt".
Even if the *one Class per file* rule is nonsense sometime but here ...??

>=== added file 'entertainerlib/download.py'
>--- entertainerlib/download.py 1970-01-01 00:00:00 +0000
>+++ entertainerlib/download.py 2009-05-07 19:37:31 +0000
>@@ -0,0 +1,453 @@
>+'''Downloader classes.'''
>+
>+__licence__ = "GPLv2"
>+__copyright__ = "2009 Entertainer Developers"
>+

I like the idea to group our downloaders. What about weather downloader? Is it
planned in the future?

> self.window.connect('destroy', self.destroy_callback)
>@@ -53,7 +53,7 @@
> except gobject.GError:
> # Must not be installed from a package, get icon from the branch
> file_dir = os.path.dirname(__file__)
>- icon_path = os.path.join(file_dir, '..', '..', '..', 'icons',
>+ icon_path = os.path.join(file_dir, '..', '..', 'icons',
> 'hicolor', '48x48', 'apps', 'entertainer.png')
> icon = gtk.gdk.pixbuf_new_from_file(icon_path)
> self.window.set_icon(icon)

(just me thinking loudly) => If we want to improve our flexibility with folder
tree modifications, we have to find a way to avoid those '..', '..'.
What about adding more xxx_dir (root_dir, data_dir, etc). Those could be calculated
attributes of our config object.

Thanks for the great work Paul. Entertainer will rock with the future client/server
architecture and a robust and powerfull indexer.

Samuel-

Revision history for this message
Matt Layman (mblayman) wrote :

After looking over the conversations in this thread, I've concluded that I have to think differently about my normal standards and realize that this isn't trunk. I do wonder how we can capture the problems that start to arise as we merge branches onto future. Some problems will go away when future gets closer to merging into trunk, but what if we just forget about some of these things and merge them onto trunk by accident. That's my one reservation about approving this branch.

However, I do like the bulk of this work and since it is such a disruptive change, I would rather it land sooner than later. Therefore, I'm going to approve in spite of my concerns.

review: Approve
Revision history for this message
Samuel Buffet (samuel-buffet) wrote :

Go Paul! Go!

This is really something we need and like Matt said, this is not trunk. So you have a STRONG approval from me on this work. I can add that I'm very happy with the goal of this branch.

Cheers,
Samuel-

review: Approve
383. By Paul Hummer

Merged from entertainer-future

384. By Paul Hummer

Added copyright stuff to the new files

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'entertainer'
--- entertainer 2008-10-25 16:41:22 +0000
+++ entertainer 2009-05-06 03:37:34 +0000
@@ -1,12 +1,12 @@
1#!/usr/bin/env python1#!/usr/bin/env python
2'''Main frontend executable'''2'''Main client executable'''
33
4__licence__ = "GPLv2"4__licence__ = "GPLv2"
5__copyright__ = "2007, Lauri Taimila"5__copyright__ = "2007, Lauri Taimila"
6__author__ = "Lauri Taimila <lauri@taimila.com>"6__author__ = "Lauri Taimila <lauri@taimila.com>"
7__version__ = "0.2"7__version__ = "0.2"
88
9from entertainerlib.frontend import main9from entertainerlib.client import main
1010
11if __name__ == '__main__':11if __name__ == '__main__':
12 main()12 main()
1313
=== modified file 'entertainer-client'
--- entertainer-client 2009-04-04 05:30:04 +0000
+++ entertainer-client 2009-05-06 03:37:34 +0000
@@ -2,7 +2,7 @@
2'''Test client for Entertainer's server.2'''Test client for Entertainer's server.
33
4This code will go away when the client code is integrated into what is now the4This code will go away when the client code is integrated into what is now the
5frontend.5client.
6'''6'''
77
8from entertainerlib.network import client_main8from entertainerlib.network import client_main
99
=== modified file 'entertainer-content-manager'
--- entertainer-content-manager 2008-12-19 04:06:18 +0000
+++ entertainer-content-manager 2009-05-06 03:37:34 +0000
@@ -3,11 +3,10 @@
33
4import gtk4import gtk
55
6from entertainerlib.frontend.translation_setup import TranslationSetup6from entertainerlib.client.translation_setup import TranslationSetup
7TranslationSetup()7TranslationSetup()
88
9from entertainerlib.utils.content_management_dialog import (9from entertainerlib.dialog import ContentManagementDialog
10 ContentManagementDialog)
1110
1211
13ContentManagementDialog(True)12ContentManagementDialog(True)
1413
=== modified file 'entertainer-preferences'
--- entertainer-preferences 2008-12-19 04:06:18 +0000
+++ entertainer-preferences 2009-05-06 03:37:34 +0000
@@ -3,10 +3,10 @@
33
4import gtk4import gtk
55
6from entertainerlib.frontend.translation_setup import TranslationSetup6from entertainerlib.client.translation_setup import TranslationSetup
7TranslationSetup()7TranslationSetup()
88
9from entertainerlib.utils.preferences_dialog import PreferencesDialog9from entertainerlib.dialog import PreferencesDialog
1010
1111
12PreferencesDialog(True)12PreferencesDialog(True)
1313
=== modified file 'entertainerlib/backend/backend_server.py'
--- entertainerlib/backend/backend_server.py 2009-02-08 07:01:25 +0000
+++ entertainerlib/backend/backend_server.py 2009-05-05 04:43:57 +0000
@@ -8,8 +8,8 @@
88
9import gobject9import gobject
1010
11from entertainerlib.utils.configuration import Configuration11from entertainerlib.configuration import Configuration
12from entertainerlib.utils.logger import Logger12from entertainerlib.logger import Logger
1313
14# Entertainer backend core14# Entertainer backend core
15from entertainerlib.backend.core.message import Message15from entertainerlib.backend.core.message import Message
1616
=== modified file 'entertainerlib/backend/components/feeds/feed_fetcher.py'
--- entertainerlib/backend/components/feeds/feed_fetcher.py 2009-02-07 21:43:35 +0000
+++ entertainerlib/backend/components/feeds/feed_fetcher.py 2009-05-05 04:43:57 +0000
@@ -10,8 +10,8 @@
10from datetime import datetime10from datetime import datetime
11from pysqlite2 import dbapi2 as sqlite11from pysqlite2 import dbapi2 as sqlite
1212
13from entertainerlib.utils.configuration import Configuration13from entertainerlib.configuration import Configuration
14from entertainerlib.utils.logger import Logger14from entertainerlib.logger import Logger
1515
16# Messaging system16# Messaging system
17from entertainerlib.backend.core.message import Message17from entertainerlib.backend.core.message import Message
1818
=== modified file 'entertainerlib/backend/components/feeds/feed_manager.py'
--- entertainerlib/backend/components/feeds/feed_manager.py 2008-08-15 23:52:30 +0000
+++ entertainerlib/backend/components/feeds/feed_manager.py 2009-05-05 04:43:57 +0000
@@ -8,8 +8,8 @@
8from pysqlite2 import dbapi2 as sqlite8from pysqlite2 import dbapi2 as sqlite
9from entertainerlib.backend.components.feeds.feed_fetcher import FeedFetcher9from entertainerlib.backend.components.feeds.feed_fetcher import FeedFetcher
1010
11from entertainerlib.utils.configuration import Configuration11from entertainerlib.configuration import Configuration
12from entertainerlib.utils.logger import Logger12from entertainerlib.logger import Logger
1313
14# Messaging system14# Messaging system
15from entertainerlib.backend.core.message_type_priority import MessageType15from entertainerlib.backend.core.message_type_priority import MessageType
1616
=== renamed file 'entertainerlib/utils/feed_utils.py' => 'entertainerlib/backend/components/feeds/feed_utils.py'
--- entertainerlib/utils/feed_utils.py 2009-02-08 07:43:25 +0000
+++ entertainerlib/backend/components/feeds/feed_utils.py 2009-05-06 04:00:53 +0000
@@ -11,7 +11,7 @@
11import gtk11import gtk
12import gtk.glade12import gtk.glade
1313
14from entertainerlib.utils.configuration import Configuration14from entertainerlib.configuration import Configuration
1515
1616
17class FeedEntryParser:17class FeedEntryParser:
1818
=== modified file 'entertainerlib/backend/components/mediacache/image_cache.py'
--- entertainerlib/backend/components/mediacache/image_cache.py 2009-02-19 14:30:24 +0000
+++ entertainerlib/backend/components/mediacache/image_cache.py 2009-05-05 04:43:57 +0000
@@ -11,8 +11,8 @@
11from pysqlite2 import dbapi2 as sqlite11from pysqlite2 import dbapi2 as sqlite
1212
13from entertainerlib.thumbnailer import ImageThumbnailer13from entertainerlib.thumbnailer import ImageThumbnailer
14from entertainerlib.utils.configuration import Configuration14from entertainerlib.configuration import Configuration
15from entertainerlib.utils.logger import Logger15from entertainerlib.logger import Logger
1616
17from entertainerlib.backend.components.mediacache.cache import Cache17from entertainerlib.backend.components.mediacache.cache import Cache
1818
1919
=== modified file 'entertainerlib/backend/components/mediacache/media_cache_manager.py'
--- entertainerlib/backend/components/mediacache/media_cache_manager.py 2009-01-06 23:51:38 +0000
+++ entertainerlib/backend/components/mediacache/media_cache_manager.py 2009-05-05 04:43:57 +0000
@@ -4,8 +4,8 @@
4__copyright__ = "2007, Lauri Taimila"4__copyright__ = "2007, Lauri Taimila"
5__author__ = "Lauri Taimila <lauri@taimila.com>"5__author__ = "Lauri Taimila <lauri@taimila.com>"
66
7from entertainerlib.utils.configuration import Configuration7from entertainerlib.configuration import Configuration
8from entertainerlib.utils.logger import Logger8from entertainerlib.logger import Logger
99
10from entertainerlib.backend.core.message_type_priority import MessageType10from entertainerlib.backend.core.message_type_priority import MessageType
11from entertainerlib.backend.core.message_handler import MessageHandler11from entertainerlib.backend.core.message_handler import MessageHandler
@@ -16,7 +16,7 @@
16from entertainerlib.backend.components.mediacache.video_cache import VideoCache16from entertainerlib.backend.components.mediacache.video_cache import VideoCache
1717
18class MediaCacheManager(MessageHandler):18class MediaCacheManager(MessageHandler):
19 """Makes sure that frontend has all the data available."""19 """Makes sure that client has all the data available."""
2020
21 def __init__(self):21 def __init__(self):
22 """22 """
2323
=== modified file 'entertainerlib/backend/components/mediacache/music_cache.py'
--- entertainerlib/backend/components/mediacache/music_cache.py 2009-04-24 20:41:49 +0000
+++ entertainerlib/backend/components/mediacache/music_cache.py 2009-05-06 04:46:51 +0000
@@ -12,11 +12,11 @@
12import ogg.vorbis12import ogg.vorbis
13from pysqlite2 import dbapi2 as sqlite13from pysqlite2 import dbapi2 as sqlite
1414
15from entertainerlib.utils.albumart_downloader import AlbumArtDownloader
16from entertainerlib.utils.configuration import Configuration
17from entertainerlib.utils.logger import Logger
18
19from entertainerlib.backend.components.mediacache.cache import Cache15from entertainerlib.backend.components.mediacache.cache import Cache
16from entertainerlib.configuration import Configuration
17from entertainerlib.download import AlbumArtDownloader
18from entertainerlib.logger import Logger
19
2020
21class MusicCache(Cache):21class MusicCache(Cache):
22 """22 """
2323
=== modified file 'entertainerlib/backend/components/mediacache/video_cache.py'
--- entertainerlib/backend/components/mediacache/video_cache.py 2009-02-19 14:30:24 +0000
+++ entertainerlib/backend/components/mediacache/video_cache.py 2009-05-05 04:43:57 +0000
@@ -9,8 +9,8 @@
9from pysqlite2 import dbapi2 as sqlite9from pysqlite2 import dbapi2 as sqlite
1010
11from entertainerlib.thumbnailer import VideoThumbnailer11from entertainerlib.thumbnailer import VideoThumbnailer
12from entertainerlib.utils.configuration import Configuration12from entertainerlib.configuration import Configuration
13from entertainerlib.utils.logger import Logger13from entertainerlib.logger import Logger
1414
15from entertainerlib.backend.components.mediacache.cache import Cache15from entertainerlib.backend.components.mediacache.cache import Cache
16from entertainerlib.backend.components.mediacache.video_metadata_search import (16from entertainerlib.backend.components.mediacache.video_metadata_search import (
1717
=== modified file 'entertainerlib/backend/components/mediacache/video_metadata_search.py'
--- entertainerlib/backend/components/mediacache/video_metadata_search.py 2009-02-07 22:12:25 +0000
+++ entertainerlib/backend/components/mediacache/video_metadata_search.py 2009-05-05 04:24:32 +0000
@@ -12,8 +12,8 @@
12import threading12import threading
13from pysqlite2 import dbapi2 as sqlite13from pysqlite2 import dbapi2 as sqlite
1414
15from entertainerlib.utils.logger import Logger15from entertainerlib.logger import Logger
16from entertainerlib.utils.configuration import Configuration16from entertainerlib.configuration import Configuration
1717
18class VideoMetadataSearch(threading.Thread):18class VideoMetadataSearch(threading.Thread):
19 """19 """
2020
=== modified file 'entertainerlib/backend/core/client_connection.py'
--- entertainerlib/backend/core/client_connection.py 2009-02-07 21:43:35 +0000
+++ entertainerlib/backend/core/client_connection.py 2009-05-05 04:24:32 +0000
@@ -11,7 +11,7 @@
11# Messaging system11# Messaging system
12from entertainerlib.backend.core.message_handler import MessageHandler12from entertainerlib.backend.core.message_handler import MessageHandler
1313
14from entertainerlib.utils.logger import Logger14from entertainerlib.logger import Logger
1515
16class ClientConnection(threading.Thread, MessageHandler):16class ClientConnection(threading.Thread, MessageHandler):
17 """17 """
1818
=== modified file 'entertainerlib/backend/core/connection_server.py'
--- entertainerlib/backend/core/connection_server.py 2009-02-06 08:13:48 +0000
+++ entertainerlib/backend/core/connection_server.py 2009-05-05 04:24:32 +0000
@@ -10,7 +10,7 @@
1010
11from entertainerlib.backend.core.client_connection import ClientConnection11from entertainerlib.backend.core.client_connection import ClientConnection
1212
13from entertainerlib.utils.logger import Logger13from entertainerlib.logger import Logger
1414
15class ConnectionServer(threading.Thread):15class ConnectionServer(threading.Thread):
16 """16 """
1717
=== modified file 'entertainerlib/backend/core/message_bus.py'
--- entertainerlib/backend/core/message_bus.py 2009-02-06 08:13:48 +0000
+++ entertainerlib/backend/core/message_bus.py 2009-05-05 04:24:32 +0000
@@ -5,7 +5,7 @@
5from entertainerlib.backend.core.message import Message5from entertainerlib.backend.core.message import Message
6from entertainerlib.backend.core.message_handler import MessageHandler6from entertainerlib.backend.core.message_handler import MessageHandler
7from entertainerlib.backend.core.message_type_priority import MessageType7from entertainerlib.backend.core.message_type_priority import MessageType
8from entertainerlib.utils.logger import Logger8from entertainerlib.logger import Logger
99
10__licence__ = "GPLv2"10__licence__ = "GPLv2"
11__copyright__ = "2007, Lauri Taimila"11__copyright__ = "2007, Lauri Taimila"
1212
=== modified file 'entertainerlib/backend/core/message_bus_proxy.py'
--- entertainerlib/backend/core/message_bus_proxy.py 2009-01-05 03:25:03 +0000
+++ entertainerlib/backend/core/message_bus_proxy.py 2009-05-05 04:11:48 +0000
@@ -9,7 +9,7 @@
9import threading9import threading
10from cStringIO import StringIO10from cStringIO import StringIO
1111
12from entertainerlib.utils.configuration import Configuration12from entertainerlib.configuration import Configuration
1313
14class MessageBusProxy(threading.Thread):14class MessageBusProxy(threading.Thread):
15 """15 """
1616
=== modified file 'entertainerlib/backend/core/message_type_priority.py'
--- entertainerlib/backend/core/message_type_priority.py 2009-01-31 15:46:36 +0000
+++ entertainerlib/backend/core/message_type_priority.py 2009-04-30 01:11:22 +0000
@@ -33,10 +33,10 @@
33 # Indicates that Content Management UI has been used to update contents.33 # Indicates that Content Management UI has been used to update contents.
34 CONTENT_CONF_UPDATED = 134 CONTENT_CONF_UPDATED = 1
3535
36 # Indicates that frontend has been opened.36 # Indicates that client has been opened.
37 FRONTEND_OPENED = 237 FRONTEND_OPENED = 2
3838
39 # Indicates that frontend has been closed.39 # Indicates that client has been closed.
40 FRONTEND_CLOSED = 340 FRONTEND_CLOSED = 3
4141
42 # Indicates that Feed cache has been updated.42 # Indicates that Feed cache has been updated.
4343
=== renamed directory 'entertainerlib/frontend' => 'entertainerlib/client'
=== modified file 'entertainerlib/client/__init__.py'
--- entertainerlib/frontend/__init__.py 2009-03-08 03:16:25 +0000
+++ entertainerlib/client/__init__.py 2009-05-05 04:43:57 +0000
@@ -5,7 +5,7 @@
5 '''Frontend runner'''5 '''Frontend runner'''
66
7 # Import statements are inside the function so that they aren't imported7 # Import statements are inside the function so that they aren't imported
8 # every time something from the frontend is imported8 # every time something from the client is imported
99
10 # cluttergtk must be imported before the first import of clutter so it10 # cluttergtk must be imported before the first import of clutter so it
11 # must be imported even though pylint complains about it not being used.11 # must be imported even though pylint complains about it not being used.
@@ -14,12 +14,12 @@
14 import gobject14 import gobject
15 import gtk15 import gtk
1616
17 from entertainerlib.frontend.translation_setup import TranslationSetup17 from entertainerlib.client.translation_setup import TranslationSetup
18 TranslationSetup()18 TranslationSetup()
1919
20 from entertainerlib.backend.backend_server import BackendServer20 from entertainerlib.backend.backend_server import BackendServer
21 from entertainerlib.utils.configuration import Configuration21 from entertainerlib.configuration import Configuration
22 from entertainerlib.frontend.frontend_client import FrontendClient22 from entertainerlib.client.client import Client
2323
24 gobject.threads_init()24 gobject.threads_init()
25 gtk.gdk.threads_init()25 gtk.gdk.threads_init()
@@ -30,7 +30,7 @@
30 print "Entertainer backend starting..."30 print "Entertainer backend starting..."
31 BackendServer()31 BackendServer()
3232
33 frontend_client = FrontendClient()33 client_client = Client()
34 frontend_client.start()34 client_client.start()
3535
3636
3737
=== renamed file 'entertainerlib/frontend/frontend_client.py' => 'entertainerlib/client/client.py'
--- entertainerlib/frontend/frontend_client.py 2009-02-10 00:59:03 +0000
+++ entertainerlib/client/client.py 2009-05-05 04:43:57 +0000
@@ -9,34 +9,34 @@
99
10import gtk10import gtk
1111
12from entertainerlib.frontend.backend_connection import BackendConnection12from entertainerlib.client.backend_connection import BackendConnection
13from entertainerlib.frontend.gui.user_interface import UserInterface13from entertainerlib.gui.user_interface import UserInterface
14from entertainerlib.frontend.medialibrary.feeds import FeedLibrary14from entertainerlib.client.medialibrary.feeds import FeedLibrary
15from entertainerlib.frontend.medialibrary.music import MusicLibrary15from entertainerlib.client.medialibrary.music import MusicLibrary
16from entertainerlib.frontend.medialibrary.images import ImageLibrary16from entertainerlib.client.medialibrary.images import ImageLibrary
17from entertainerlib.frontend.medialibrary.videos import VideoLibrary17from entertainerlib.client.medialibrary.videos import VideoLibrary
18from entertainerlib.utils.configuration import Configuration18from entertainerlib.configuration import Configuration
19from entertainerlib.utils.logger import Logger19from entertainerlib.logger import Logger
20from entertainerlib.utils.system_tray_icon import SystemTrayIcon20from entertainerlib.gui.system_tray_icon import SystemTrayIcon
2121
22class FrontendClient:22class Client:
23 '''23 '''
24 Entertainer frontend24 Entertainer client
2525
26 This is a frontend application of the Entertainer. Frontend is a GUI part26 This is a client application of the Entertainer. Frontend is a GUI part
27 that user sees on the screen. This class is a core of the frontend. Frontend27 that user sees on the screen. This class is a core of the client. Frontend
28 connects to the backend's messagebus at startup.28 connects to the backend's messagebus at startup.
29 '''29 '''
3030
31 def __init__(self):31 def __init__(self):
32 '''32 '''
33 Create a new frontend.33 Create a new client.
3434
35 This initializes all frontend stuff like media librarys, remote35 This initializes all client stuff like media librarys, remote
36 control receiver and GUI. After this we just wait user actions.36 control receiver and GUI. After this we just wait user actions.
37 '''37 '''
38 config = Configuration()38 config = Configuration()
39 self.logger = Logger().getLogger('frontend.FrontendClient')39 self.logger = Logger().getLogger('client.FrontendClient')
40 self.backend_connection = self.initialize_backend_connection()40 self.backend_connection = self.initialize_backend_connection()
41 feed_library = FeedLibrary(self.backend_connection)41 feed_library = FeedLibrary(self.backend_connection)
42 music_library = MusicLibrary(self.backend_connection)42 music_library = MusicLibrary(self.backend_connection)
@@ -44,11 +44,11 @@
44 video_library = VideoLibrary(self.backend_connection)44 video_library = VideoLibrary(self.backend_connection)
45 self.ui = UserInterface(45 self.ui = UserInterface(
46 feed_library, image_library, music_library, video_library,46 feed_library, image_library, music_library, video_library,
47 self.quit_frontend)47 self.quit_client)
4848
49 if config.tray_icon_enabled():49 if config.tray_icon_enabled():
50 SystemTrayIcon(50 SystemTrayIcon(
51 self.quit_frontend, self.toggle_interface_visibility)51 self.quit_client, self.toggle_interface_visibility)
5252
53 def start(self):53 def start(self):
54 '''Start the necessary main loop.'''54 '''Start the necessary main loop.'''
@@ -65,8 +65,8 @@
6565
66 return backend_connection66 return backend_connection
6767
68 def quit_frontend(self):68 def quit_client(self):
69 '''Clean up the connection to the backend then close the frontend.'''69 '''Clean up the connection to the backend then close the client.'''
70 self.backend_connection.close_connection()70 self.backend_connection.close_connection()
7171
72 gtk.main_quit()72 gtk.main_quit()
7373
=== modified file 'entertainerlib/client/media_player.py'
--- entertainerlib/frontend/media_player.py 2009-02-08 08:44:46 +0000
+++ entertainerlib/client/media_player.py 2009-05-05 04:24:32 +0000
@@ -11,9 +11,9 @@
11import clutter11import clutter
12import gst12import gst
1313
14from entertainerlib.frontend.gui.widgets.texture import Texture14from entertainerlib.gui.widgets.texture import Texture
15from entertainerlib.frontend.medialibrary.playable import Playable15from entertainerlib.client.medialibrary.playable import Playable
16from entertainerlib.utils.logger import Logger16from entertainerlib.logger import Logger
1717
18class MediaPlayer(object):18class MediaPlayer(object):
19 """19 """
@@ -59,7 +59,7 @@
59 self.repeat = False # Repeat mode59 self.repeat = False # Repeat mode
60 self.playing = False # Is media player currently playing60 self.playing = False # Is media player currently playing
6161
62 self.logger = Logger().getLogger('frontend.MediaPlayer')62 self.logger = Logger().getLogger('client.MediaPlayer')
6363
64 self.video_texture = cluttergst.VideoTexture()64 self.video_texture = cluttergst.VideoTexture()
65 self.playbin = self.video_texture.get_playbin()65 self.playbin = self.video_texture.get_playbin()
6666
=== modified file 'entertainerlib/client/medialibrary/feeds.py'
--- entertainerlib/frontend/medialibrary/feeds.py 2009-01-26 18:28:26 +0000
+++ entertainerlib/client/medialibrary/feeds.py 2009-05-05 04:11:48 +0000
@@ -7,7 +7,7 @@
77
8from pysqlite2 import dbapi2 as sqlite8from pysqlite2 import dbapi2 as sqlite
99
10from entertainerlib.utils.configuration import Configuration10from entertainerlib.configuration import Configuration
1111
12class FeedLibrary(object):12class FeedLibrary(object):
13 """13 """
1414
=== modified file 'entertainerlib/client/medialibrary/images.py'
--- entertainerlib/frontend/medialibrary/images.py 2009-01-31 17:57:51 +0000
+++ entertainerlib/client/medialibrary/images.py 2009-05-05 04:11:48 +0000
@@ -7,7 +7,7 @@
7import os7import os
8from pysqlite2 import dbapi2 as sqlite8from pysqlite2 import dbapi2 as sqlite
99
10from entertainerlib.utils.configuration import Configuration10from entertainerlib.configuration import Configuration
1111
12class ImageLibrary:12class ImageLibrary:
13 """13 """
1414
=== modified file 'entertainerlib/client/medialibrary/music.py'
--- entertainerlib/frontend/medialibrary/music.py 2009-03-15 16:01:39 +0000
+++ entertainerlib/client/medialibrary/music.py 2009-05-06 04:46:51 +0000
@@ -8,10 +8,10 @@
8import CDDB, DiscID8import CDDB, DiscID
9from pysqlite2 import dbapi2 as sqlite9from pysqlite2 import dbapi2 as sqlite
1010
11from entertainerlib.utils.configuration import Configuration11from entertainerlib.configuration import Configuration
1212
13from entertainerlib.frontend.medialibrary.playable import Playable13from entertainerlib.client.medialibrary.playable import Playable
14from entertainerlib.utils.lyrics_downloader import LyricsDownloader14from entertainerlib.download import LyricsDownloader
1515
1616
17class MusicLibraryException(Exception):17class MusicLibraryException(Exception):
1818
=== modified file 'entertainerlib/client/medialibrary/videos.py'
--- entertainerlib/frontend/medialibrary/videos.py 2009-02-01 19:15:02 +0000
+++ entertainerlib/client/medialibrary/videos.py 2009-05-05 04:24:32 +0000
@@ -8,10 +8,10 @@
8import os8import os
9from pysqlite2 import dbapi2 as sqlite9from pysqlite2 import dbapi2 as sqlite
1010
11from entertainerlib.utils.configuration import Configuration11from entertainerlib.configuration import Configuration
12from entertainerlib.utils.logger import Logger12from entertainerlib.logger import Logger
1313
14from entertainerlib.frontend.medialibrary.playable import Playable14from entertainerlib.client.medialibrary.playable import Playable
1515
16class VideoLibrary:16class VideoLibrary:
17 """17 """
@@ -137,7 +137,7 @@
137137
138 #Setting default values138 #Setting default values
139 self.logger = Logger().getLogger(139 self.logger = Logger().getLogger(
140 'frontend.medialibrary.videos.VideoItem')140 'client.medialibrary.videos.VideoItem')
141 self.__title = ""141 self.__title = ""
142 self.__filename = ""142 self.__filename = ""
143 self.__length = 0143 self.__length = 0
144144
=== renamed file 'entertainerlib/utils/configuration.py' => 'entertainerlib/configuration.py'
--- entertainerlib/utils/configuration.py 2009-03-08 21:29:32 +0000
+++ entertainerlib/configuration.py 2009-05-06 05:37:53 +0000
@@ -14,10 +14,10 @@
14from entertainerlib.backend.core.message_type_priority import MessageType14from entertainerlib.backend.core.message_type_priority import MessageType
15from entertainerlib.backend.core.message_handler import MessageHandler15from entertainerlib.backend.core.message_handler import MessageHandler
1616
17from entertainerlib.utils.theme import Theme17from entertainerlib.gui.theme import Theme
1818
19SOURCE_CONFIG = {19SOURCE_CONFIG = {
20 'branch' : os.path.abspath(os.path.dirname(__file__) + '/../../cfg'),20 'branch' : os.path.abspath(os.path.dirname(__file__) + '/../cfg'),
21 # Hardcoded path for a package install21 # Hardcoded path for a package install
22 'package' : "/usr/share/entertainer/cfg"22 'package' : "/usr/share/entertainer/cfg"
23 }23 }
@@ -332,9 +332,9 @@
332 location = ''332 location = ''
333 return location333 return location
334334
335 def display_weather_in_frontend(self):335 def display_weather_in_client(self):
336 """336 """
337 Should we display weather in frontend337 Should we display weather in client
338 @return: Boolean338 @return: Boolean
339 """339 """
340 try:340 try:
@@ -344,9 +344,9 @@
344 return False344 return False
345 return result345 return result
346346
347 def display_cd_eject_in_frontend(self):347 def display_cd_eject_in_client(self):
348 """348 """
349 Should we display the cd eject button in frontend349 Should we display the cd eject button in client
350 @return: Boolean350 @return: Boolean
351 @author: Joshua Scotton351 @author: Joshua Scotton
352 """352 """
353353
=== renamed directory 'entertainerlib/backend/core/db' => 'entertainerlib/db'
=== added file 'entertainerlib/dialog.py'
--- entertainerlib/dialog.py 1970-01-01 00:00:00 +0000
+++ entertainerlib/dialog.py 2009-05-06 05:10:14 +0000
@@ -0,0 +1,1374 @@
1'''Dialogs for Entertainer.'''
2# pylint: disable-msg=C0302
3
4__licence__ = "GPLv2"
5__copyright__ = '2009 Entertainer Developers, 2008 Joshua Scotton'
6
7import os
8import shutil
9import socket
10import sys
11import tarfile
12
13import gtk
14import gtk.glade
15
16from entertainerlib.backend.core.message import Message
17from entertainerlib.backend.core.message_bus_proxy import MessageBusProxy
18from entertainerlib.backend.core.message_type_priority import MessageType
19from entertainerlib.configuration import Configuration
20from entertainerlib.logger import Logger
21from entertainerlib.gui.theme import Theme
22from entertainerlib.backend.components.feeds.feed_utils import (OPMLParser,
23 FeedConfigTools)
24from entertainerlib.weather import Weather
25
26
27class ContentManagementDialog:
28 """
29 This is a content management tool for Entertainer media center application.
30 """
31
32 # Temporary storage for entered URL
33 url = ""
34 GLADE_DIR = os.path.join(os.path.dirname(__file__), "glade")
35
36 def __init__(self, stand_alone):
37 """
38 Initialize content management dialog
39 @param stand_alone: Boolean, Is this dialog running as a stand alone
40 process
41 """
42 self.stand_alone = stand_alone
43 self.config = Configuration()
44 self.weather = Weather()
45
46 # Load glade UI
47 self.gladefile = os.path.join(self.GLADE_DIR,
48 "entertainer-content-management.glade")
49 self.widgets = gtk.glade.XML(self.gladefile)
50
51 # Get content management dialog and bind signal callbacks
52 self.dialog = self.widgets.get_widget("ContentManagementDialog")
53 if (self.dialog):
54 callback_dic = {
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_button_remove_videos_clicked" :
59 self.on_button_remove_videos_clicked,
60 "on_button_add_videos_clicked" :
61 self.on_button_add_videos_clicked,
62 "on_button_edit_videos_clicked" :
63 self.on_button_edit_videos_clicked,
64 "on_checkbutton_video_metadata_toggled" :
65 self.on_checkbutton_video_metadata_toggled,
66 "on_button_add_music_clicked" :
67 self.on_button_add_music_clicked,
68 "on_button_remove_music_clicked" :
69 self.on_button_remove_music_clicked,
70 "on_button_edit_music_clicked" :
71 self.on_button_edit_music_clicked,
72 "on_lyrics_checkbox_toggled" : self.on_lyrics_checkbox_toggled,
73 "on_art_checkbox_toggled" : self.on_art_checkbox_toggled,
74 "on_button_add_images_clicked" :
75 self.on_button_add_images_clicked,
76 "on_button_remove_images_clicked" :
77 self.on_button_remove_images_clicked,
78 "on_button_edit_images_clicked" :
79 self.on_button_edit_images_clicked,
80 "on_button_add_feed_clicked" :
81 self.on_button_add_feed_clicked,
82 "on_button_remove_feed_clicked" :
83 self.on_button_remove_feed_clicked,
84 "on_button_edit_feed_clicked" :
85 self.on_button_edit_feed_clicked,
86 "on_fetch_interval_spinbutton_value_changed" :
87 self.on_fetch_interval_spinbutton_value_changed,
88 "on_ContentManagementDialog_destroy" : self.on_dialog_closed,
89 "on_url_dialog_delete_event" : self.on_url_dialog_delete_event,
90 "on_url_dialog_ok_button_clicked" :
91 self.on_url_dialog_ok_button_clicked,
92 "on_url_dialog_cancel_button_clicked" :
93 self.on_url_dialog_cancel_button_clicked,
94 "on_button_video_rebuild_clicked" :
95 self.on_button_video_rebuild_clicked,
96 "on_button_music_rebuild_clicked" :
97 self.on_button_music_rebuild_clicked,
98 "on_button_image_rebuild_clicked" :
99 self.on_button_image_rebuild_clicked,
100 "on_button_feed_rebuild_clicked" :
101 self.on_button_feed_rebuild_clicked,
102 "on_button_add_weather_clicked" :
103 self.on_button_add_weather_clicked,
104 "on_button_remove_weather_clicked" :
105 self.on_button_remove_weather_clicked,
106 "on_weather_display_checkbox_toggled" :
107 self.on_weather_display_checkbox_toggled,
108 "on_location_find_button_clicked" :
109 self.on_location_find_button_clicked,
110 "on_location_cancel_button_clicked" :
111 self.on_location_cancel_button_clicked,
112 "on_location_add_button_clicked" :
113 self.on_location_add_button_clicked,
114 "on_location_entry_activate" : self.on_location_entry_activate}
115 self.widgets.signal_autoconnect(callback_dic)
116
117 # Initialize dialog widgets with correct values and show dialog
118 self.init_dialog_values_from_configure_file()
119 self.dialog.resize(500, 300)
120 self.dialog.show()
121
122 # Initialize location list in search dialog
123 result_list = self.widgets.get_widget("location_results_treeview")
124 store = gtk.ListStore(str)
125 result_list.set_model(store)
126 cell_renderer = gtk.CellRendererText()
127 column = gtk.TreeViewColumn(_("Location"), cell_renderer, text=0)
128 result_list.append_column(column)
129
130# Signal handlers
131
132 def on_dialog_closed(self, widget):
133 """Callback function for dialog's close button"""
134 try:
135 proxy = MessageBusProxy(client_name = "Content Management GUI")
136 proxy.connectToMessageBus()
137 proxy.sendMessage(Message(MessageType.CONTENT_CONF_UPDATED))
138 proxy.disconnectFromMessageBus()
139 except socket.error:
140 error = gtk.MessageDialog(
141 None, gtk.DIALOG_MODAL,
142 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
143 "Entertainer backend is not running. "
144 "Cache cannot be rebuilt."
145 ))
146 error.run()
147 error.destroy()
148
149
150 if(self.stand_alone):
151 self.dialog.hide()
152 self.dialog.destroy()
153 gtk.main_quit()
154 else:
155 self.dialog.hide()
156 self.dialog.destroy()
157
158 def on_close_button_clicked(self, widget):
159 """Callback function for dialog's close button"""
160 if(self.stand_alone):
161 self.dialog.hide()
162 self.dialog.destroy()
163 gtk.main_quit()
164 else:
165 self.dialog.hide()
166 self.dialog.destroy()
167
168 def on_button_add_videos_clicked(self, widget):
169 """Opens add URL dialog. """
170 widget = self.widgets.get_widget("treeview_videos")
171 model = widget.get_model()
172 # Open "Select folder" dialog
173 dialog = gtk.FileChooserDialog(_("Select video folder"), None,
174 gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
175 (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,
176 gtk.STOCK_OPEN,gtk.RESPONSE_OK),
177 None)
178 status = dialog.run()
179 # If folder was selected we add it to model and update config file
180 if(status == gtk.RESPONSE_OK):
181 self.add_to_model_and_config(dialog.get_current_folder(), model,
182 self.video_folders, "Videos")
183 dialog.destroy()
184
185 def on_button_remove_videos_clicked(self, widget):
186 """Remove currently selected folder from video folders"""
187 widget = self.widgets.get_widget("treeview_videos")
188 model = widget.get_model()
189 selection = widget.get_selection().get_selected()
190 if selection[1] == None:
191 return
192 rm_folder = model.get_value(selection[1], 0)
193 self.video_folders.remove(rm_folder)
194 str_folders = ";".join(self.video_folders)
195 self.config.write_content_value("Videos", "folders", str_folders)
196 model.remove(selection[1])
197
198 def on_button_edit_videos_clicked(self, widget):
199 """Edit currently selected folder"""
200 widget = self.widgets.get_widget("treeview_videos")
201 url_dialog = self.widgets.get_widget("url_dialog")
202 url_entry = self.widgets.get_widget("url_entry")
203 model = widget.get_model()
204 selection = widget.get_selection().get_selected()
205 if selection[1] == None:
206 return
207 folder = model.get_value(selection[1], 0)
208 url_entry.set_text(folder)
209 url_dialog.set_title(_("Edit URL"))
210 status = url_dialog.run()
211 if status == gtk.RESPONSE_OK and os.path.exists(self.url):
212 # Update list model
213 model.set_value(selection[1], 0, self.url)
214 # Update configure file
215 pos = self.video_folders.index(folder)
216 self.video_folders.remove(folder)
217 self.video_folders.insert(pos, self.url)
218 str_folders = ";".join(self.video_folders)
219 self.config.write_content_value("Videos", "folders",
220 str_folders)
221
222 def on_checkbutton_video_metadata_toggled(self, widget):
223 """
224 Download video file metadata from internet
225 @param widget: GTK-Widget
226 """
227 self.config.write_content_value("Videos", "download_metadata",
228 widget.get_active())
229
230 def on_button_add_music_clicked(self, widget):
231 """
232 Opens add URL dialog
233 @param widget: GTK-Widget
234 """
235 widget = self.widgets.get_widget("treeview_music")
236 model = widget.get_model()
237 # Open "Select folder" dialog
238 dialog = gtk.FileChooserDialog(_("Select music folder"), None,
239 gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
240 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
241 gtk.RESPONSE_OK),
242 None)
243 status = dialog.run()
244 # If folder was selected we add it to model and update config file
245 if(status == gtk.RESPONSE_OK):
246 self.add_to_model_and_config(dialog.get_current_folder(), model,
247 self.music_folders, "Music")
248 dialog.destroy()
249
250 def on_button_remove_music_clicked(self, widget):
251 """Remove currently selected folder from music folders"""
252 widget = self.widgets.get_widget("treeview_music")
253 model = widget.get_model()
254 selection = widget.get_selection().get_selected()
255 if selection[1] == None:
256 return
257 rm_folder = model.get_value(selection[1], 0)
258 self.music_folders.remove(rm_folder)
259 str_folders = ";".join(self.music_folders)
260 self.config.write_content_value("Music", "folders", str_folders)
261 model.remove(selection[1])
262
263 def on_button_edit_music_clicked(self, widget):
264 """Edit currently selected music folder"""
265 widget = self.widgets.get_widget("treeview_music")
266 url_dialog = self.widgets.get_widget("url_dialog")
267 url_entry = self.widgets.get_widget("url_entry")
268 model = widget.get_model()
269 selection = widget.get_selection().get_selected()
270 if selection[1] == None:
271 return
272 folder = model.get_value(selection[1], 0)
273 url_entry.set_text(folder)
274 url_dialog.set_title(_("Edit URL"))
275 status = url_dialog.run()
276 if status == gtk.RESPONSE_OK and os.path.exists(self.url):
277 # Update list model
278 model.set_value(selection[1], 0, self.url)
279 # Update configure file
280 pos = self.music_folders.index(folder)
281 self.music_folders.remove(folder)
282 self.music_folders.insert(pos, self.url)
283 str_folders = ";".join(self.music_folders)
284 self.config.write_content_value("Music", "folders", str_folders)
285
286 def on_button_add_images_clicked(self, widget):
287 """Opens add URL dialog. """
288 widget = self.widgets.get_widget("treeview_images")
289 model = widget.get_model()
290 # Open "Select folder" dialog
291 dialog = gtk.FileChooserDialog(_("Select image folder"), None,
292 gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
293 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
294 gtk.RESPONSE_OK),
295 None)
296 status = dialog.run()
297 # If folder was selected we add it to model and update config file
298 if(status == gtk.RESPONSE_OK):
299 self.add_to_model_and_config(dialog.get_current_folder(), model,
300 self.image_folders, "Images")
301 dialog.destroy()
302
303 def on_button_remove_images_clicked(self, widget):
304 """Remove currently selected folder from images folders"""
305 widget = self.widgets.get_widget("treeview_images")
306 model = widget.get_model()
307 selection = widget.get_selection().get_selected()
308 if selection[1] == None:
309 return
310 rm_folder = model.get_value(selection[1], 0)
311 self.image_folders.remove(rm_folder)
312 str_folders = ";".join(self.image_folders)
313 self.config.write_content_value("Images", "folders", str_folders)
314 model.remove(selection[1])
315
316 def on_button_edit_images_clicked(self, widget):
317 """Edit currently selected music folder"""
318 widget = self.widgets.get_widget("treeview_images")
319 url_dialog = self.widgets.get_widget("url_dialog")
320 url_entry = self.widgets.get_widget("url_entry")
321 model = widget.get_model()
322 selection = widget.get_selection().get_selected()
323 if selection[1] == None:
324 return
325 folder = model.get_value(selection[1], 0)
326 url_entry.set_text(folder)
327 url_dialog.set_title(_("Edit URL"))
328 status = url_dialog.run()
329 if status == gtk.RESPONSE_OK and os.path.exists(self.url):
330 # Update list model
331 model.set_value(selection[1], 0, self.url)
332 # Update configure file
333 pos = self.image_folders.index(folder)
334 self.image_folders.remove(folder)
335 self.image_folders.insert(pos, self.url)
336 str_folders = ";".join(self.image_folders)
337 self.config.write_content_value("Images", "folders",
338 str_folders)
339
340 def on_button_add_feed_clicked(self, widget):
341 """Opens add feed dialog. """
342 widget = self.widgets.get_widget("treeview_feeds")
343 url_dialog = self.widgets.get_widget("url_dialog")
344 model = widget.get_model()
345 # Open dialog
346 url_dialog.set_title(_("Add RSS-feed"))
347 status = url_dialog.run()
348 # If folder was selected we add it to model and update config file
349 if(status == gtk.RESPONSE_OK):
350 model.append([self.url])
351 self.feeds.append(self.url)
352 str_folders = ";".join(self.feeds)
353 self.config.write_content_value("RSS", "feeds", str_folders)
354
355 def on_button_remove_feed_clicked(self, widget):
356 """Remove currently selected reed from RSS-feeds"""
357 widget = self.widgets.get_widget("treeview_feeds")
358 model = widget.get_model()
359 selection = widget.get_selection().get_selected()
360 if selection[1] == None:
361 return
362 rm_folder = model.get_value(selection[1], 0)
363 self.feeds.remove(rm_folder)
364 str_folders = ";".join(self.feeds)
365 self.config.write_content_value("RSS", "feeds", str_folders)
366 model.remove(selection[1])
367
368 def on_button_edit_feed_clicked(self, widget):
369 """Edit currently selected feed"""
370 widget = self.widgets.get_widget("treeview_feeds")
371 url_dialog = self.widgets.get_widget("url_dialog")
372 url_entry = self.widgets.get_widget("url_entry")
373 model = widget.get_model()
374 selection = widget.get_selection().get_selected()
375 if selection[1] == None:
376 return
377 feed = model.get_value(selection[1], 0)
378 url_entry.set_text(feed)
379 url_dialog.set_title(_("Edit feed"))
380 status = url_dialog.run()
381 if status == gtk.RESPONSE_OK:
382 # Update list model
383 model.set_value(selection[1], 0, self.url)
384 # Update configure file
385 pos = self.feeds.index(feed)
386 self.feeds.remove(feed)
387 self.feeds.insert(pos, self.url)
388 str_feeds = ";".join(self.feeds)
389 self.config.write_content_value("RSS", "feeds", str_feeds)
390
391 def on_button_open_list_clicked(self, widget):
392 """Opens the open feed source dialog"""
393 open_dialog = OpenFeedSourceDialog(
394 self.widgets.get_widget("treeview_feeds"), self.feeds)
395 open_dialog.dialog.connect("destroy", open.on_closeButton_clicked)
396 open_dialog.dialog.hide()
397 open_dialog.dialog.destroy()
398
399 def on_fetch_interval_spinbutton_value_changed(self, widget):
400 self.config.write_content_value("RSS", "fetch_interval",
401 widget.get_value_as_int())
402
403 def on_lyrics_checkbox_toggled(self, widget):
404 self.config.write_content_value("Music", "download_lyrics",
405 widget.get_active())
406
407 def on_art_checkbox_toggled(self, widget):
408 self.config.write_content_value("Music", "download_album_art",
409 widget.get_active())
410
411 def on_url_dialog_ok_button_clicked(self, widget):
412 """URL dialog OK button pressed. Sets self.url"""
413 url_dialog = self.widgets.get_widget("url_dialog")
414 url_entry = self.widgets.get_widget("url_entry")
415 url_dialog.hide()
416 self.url = url_entry.get_text()
417 url_entry.set_text("")
418 url_dialog.response(gtk.RESPONSE_OK)
419
420 def on_url_dialog_cancel_button_clicked(self, widget):
421 """URL dialog cancelled. Hides dialog"""
422 url_dialog = self.widgets.get_widget("url_dialog")
423 url_entry = self.widgets.get_widget("url_entry")
424 url_dialog.hide()
425 url_entry.set_text("")
426 url_dialog.response(gtk.RESPONSE_CANCEL)
427
428 def on_url_dialog_delete_event(self, widget, data):
429 """Dialog's X clicked. Hides dialog"""
430 url_dialog = self.widgets.get_widget("url_dialog")
431 url_entry = self.widgets.get_widget("url_entry")
432 url_dialog.hide()
433 url_entry.set_text("")
434 url_dialog.response(gtk.RESPONSE_CANCEL)
435 return True
436
437 def on_button_add_weather_clicked(self, widget):
438 """
439 Open location search dialog
440 @param widget: GTK-Widget
441 """
442 location_dialog = self.widgets.get_widget("weather_search_dialog")
443 location_dialog.set_title(_("Add location"))
444
445 # Clear results
446 result_list = self.widgets.get_widget("location_results_treeview")
447 model = result_list.get_model()
448 model.clear()
449
450 status = location_dialog.run()
451 if(status == gtk.RESPONSE_OK):
452 print "Added"
453
454 def on_button_remove_weather_clicked(self, widget):
455 """
456 Remove currently selected weather location from the location list
457 @param widget: GTK-Widget
458 """
459 widget = self.widgets.get_widget("treeview_locations")
460 model = widget.get_model()
461 self.weather_locations = []
462 str_folders = ""
463 self.config.write_content_value("Weather", "location", str_folders)
464 model.clear()
465
466 def on_weather_display_checkbox_toggled(self, widget):
467 """
468 Checkbox that defines should we use weather conditions
469 @param widget: GTK-Widget
470 """
471 self.config.write_content_value("Weather", "display_in_menu",
472 widget.get_active())
473 if widget.get_active():
474 self.widgets.get_widget("button_add_weather").set_sensitive(True)
475 self.widgets.get_widget(
476 "button_remove_weather").set_sensitive(True)
477 self.widgets.get_widget("treeview_locations").set_sensitive(True)
478 else:
479 self.widgets.get_widget("button_add_weather").set_sensitive(False)
480 self.widgets.get_widget(
481 "button_remove_weather").set_sensitive(False)
482 self.widgets.get_widget("treeview_locations").set_sensitive(False)
483
484 def on_location_find_button_clicked(self, widget):
485 """
486 Find location by search string
487 @param widget: GTK-Widget
488 """
489 add_button = self.widgets.get_widget("location_add_button")
490 search_term = self.widgets.get_widget("location_entry").get_text()
491 result_list = self.widgets.get_widget("location_results_treeview")
492 model = result_list.get_model()
493 model.clear()
494 if search_term != "":
495 self.weather.set_location(search_term)
496 self.weather.refresh()
497 results = self.weather.get_forecasts()
498 if len(results) > 0:
499 add_button.set_sensitive(True)
500 model.append([search_term])
501 result_list.set_cursor(0)
502 else:
503 model.clear()
504 model.append([_("Location Not Found!")])
505 add_button.set_sensitive(False)
506
507 def on_location_cancel_button_clicked(self, widget):
508 """
509 Close location search dialog without taking any actions.0
510 @param widget: GTK-Widget
511 """
512 location_dialog = self.widgets.get_widget("weather_search_dialog")
513 location_entry = self.widgets.get_widget("location_entry")
514 location_dialog.hide()
515 location_entry.set_text("")
516 location_dialog.response(gtk.RESPONSE_CANCEL)
517
518 def on_location_add_button_clicked(self, widget):
519 """
520 Add selected location to location list and close search dialog
521 @param widget: GTK-Widget
522 """
523 self.weather_locations = []
524 result_list = self.widgets.get_widget("location_results_treeview")
525 model = result_list.get_model()
526 selection = result_list.get_selection().get_selected()
527 if selection[1] == None:
528 return
529 location_string = model.get_value(selection[1], 0)
530
531 location_list = self.widgets.get_widget("treeview_locations")
532 loc_model = location_list.get_model()
533 loc_model.clear()
534 loc_model.append([location_string])
535
536 self.weather_locations.append(location_string)
537 str_locations = ";".join(self.weather_locations)
538 self.config.write_content_value("Weather", "location", str_locations)
539
540 location_dialog = self.widgets.get_widget("weather_search_dialog")
541 location_entry = self.widgets.get_widget("location_entry")
542 location_dialog.hide()
543 location_entry.set_text("")
544 location_dialog.response(gtk.RESPONSE_CANCEL)
545
546 def on_location_entry_activate(self, widget):
547 """
548 User hit enter on location entry to start search
549 @param widget: GTK-Widget
550 """
551 self.on_location_find_button_clicked(widget)
552
553 def on_button_video_rebuild_clicked(self, widget):
554 """
555 Rebuild video cache requested
556 @param widget: GTK-Widget
557 """
558 try:
559 proxy = MessageBusProxy(client_name = "Content Management GUI")
560 proxy.connectToMessageBus()
561 proxy.sendMessage(Message(MessageType.REBUILD_VIDEO_CACHE))
562 proxy.disconnectFromMessageBus()
563 except socket.error:
564 error = gtk.MessageDialog(
565 None, gtk.DIALOG_MODAL,
566 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
567 "Entertainer backend is not running. "
568 "Cache cannot be rebuilt."
569 ))
570 error.run()
571 error.destroy()
572
573 def on_button_music_rebuild_clicked(self, widget):
574 """
575 Rebuild music cache requested
576 @param widget: GTK-Widget
577 """
578 try:
579 proxy = MessageBusProxy(client_name = "Content Management GUI")
580 proxy.connectToMessageBus()
581 proxy.sendMessage(Message(MessageType.REBUILD_MUSIC_CACHE))
582 proxy.disconnectFromMessageBus()
583 except socket.error:
584 error = gtk.MessageDialog(
585 None, gtk.DIALOG_MODAL,
586 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
587 "Entertainer backend is not running. "
588 "Cache cannot be rebuilt."
589 ))
590 error.run()
591 error.destroy()
592
593 def on_button_image_rebuild_clicked(self, widget):
594 """
595 Rebuild image cache requested
596 @param widget: GTK-Widget
597 """
598 try:
599 proxy = MessageBusProxy(client_name = "Content Management GUI")
600 proxy.connectToMessageBus()
601 proxy.sendMessage(Message(MessageType.REBUILD_IMAGE_CACHE))
602 proxy.disconnectFromMessageBus()
603 except socket.error:
604 error = gtk.MessageDialog(
605 None, gtk.DIALOG_MODAL,
606 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
607 "Entertainer backend is not running. "
608 "Cache cannot be rebuilt."
609 ))
610 error.run()
611 error.destroy()
612
613 def on_button_feed_rebuild_clicked(self, widget):
614 """
615 Rebuild feed cache requested
616 @param widget: GTK-Widget
617 """
618 #We need the user to confirm the rebuild feed cache request
619 dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING,
620 gtk.BUTTONS_OK_CANCEL,
621 _("This will completely remove any feed entries in the cache!"))
622 status = dialog.run()
623 #If user has ok'd the request send the message to the message bus
624 if(status == gtk.RESPONSE_OK):
625 try:
626 proxy = MessageBusProxy(client_name = "Content Management GUI")
627 proxy.connectToMessageBus()
628 proxy.sendMessage(Message(MessageType.REBUILD_FEED_CACHE))
629 proxy.disconnectFromMessageBus()
630 except socket.error:
631 error = gtk.MessageDialog(
632 None, gtk.DIALOG_MODAL,
633 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
634 "Entertainer backend is not running. "
635 "Cache cannot be rebuilt."
636 ))
637 error.run()
638 error.destroy()
639
640 dialog.destroy()
641
642 def init_dialog_values_from_configure_file(self):
643 """Read configuration and set dialog widget values with read values.
644 """
645 # == Videos ==
646 videolist_widget = self.widgets.get_widget("treeview_videos")
647 videostore = gtk.ListStore(str)
648
649 cell_renderer = gtk.CellRendererText()
650 column = gtk.TreeViewColumn(_("Folders"), cell_renderer, text=0)
651 videolist_widget.append_column(column)
652
653 self.video_folders = self.config.get_video_folders()
654
655 # Fill model with folders read from config file
656 self.init_model(videostore, self.video_folders)
657
658 videolist_widget.set_model(videostore)
659
660 # Checkboxes
661 metadata_checkbox = self.widgets.get_widget("video_metadata_checkbox")
662 md_val = self.config.download_video_metadata()
663 metadata_checkbox.set_active(md_val)
664
665 # == Music ==
666 musiclist_widget = self.widgets.get_widget("treeview_music")
667 music_model = gtk.ListStore(str)
668
669 music_cell = gtk.CellRendererText()
670 music_column = gtk.TreeViewColumn(_("Folders"), music_cell, text=0)
671 musiclist_widget.append_column(music_column)
672
673 self.music_folders = self.config.get_music_folders()
674
675 # Fill model with folders read from config file
676 self.init_model(music_model, self.music_folders)
677
678 musiclist_widget.set_model(music_model)
679
680 # Checkboxes
681 art_checkbox = self.widgets.get_widget("art_checkbox")
682 art_val = self.config.download_album_art()
683 art_checkbox.set_active(art_val)
684
685 lyrics_checkbox = self.widgets.get_widget("lyrics_checkbox")
686 lyrics_val = self.config.download_lyrics()
687 lyrics_checkbox.set_active(lyrics_val)
688
689 # == Images ==
690 imagelist_widget = self.widgets.get_widget("treeview_images")
691 images_model = gtk.ListStore(str)
692
693 img_cell = gtk.CellRendererText()
694 img_column = gtk.TreeViewColumn(_("Folders"), img_cell, text=0)
695 imagelist_widget.append_column(img_column)
696
697 self.image_folders = self.config.get_image_folders()
698
699 # Fill model with folders read from config file
700 self.init_model(images_model, self.image_folders)
701
702 imagelist_widget.set_model(images_model)
703
704 # == RSS-feeds ==
705 feedlist_widget = self.widgets.get_widget("treeview_feeds")
706 feed_model = gtk.ListStore(str)
707
708 rss_cell = gtk.CellRendererText()
709 rss_column = gtk.TreeViewColumn(_("Feeds"), rss_cell, text=0)
710 feedlist_widget.append_column(rss_column)
711
712 self.feeds = self.config.get_feeds()
713
714 # Fill model with folders read from config file
715 for i in range(len(self.feeds)):
716 feed_model.insert(i, [self.feeds[i]])
717
718 feedlist_widget.set_model(feed_model)
719
720 # Interval spinner
721 interval_spinner = self.widgets.get_widget("fetch_interval_spinbutton")
722 interval_val = self.config.get_feed_fetch_interval()
723 if interval_val < 15:
724 interval_val = 15
725 elif interval_val > 900:
726 interval_val = 900
727 interval_spinner.set_value(interval_val)
728
729 # == Weather location ==
730 locationlist_widget = self.widgets.get_widget("treeview_locations")
731 location_model = gtk.ListStore(str)
732
733 loc_cell = gtk.CellRendererText()
734 location_column = gtk.TreeViewColumn(_("Location"), loc_cell, text=0)
735 locationlist_widget.append_column(location_column)
736
737 self.weather_location = self.config.get_weather_location()
738
739 # Fill model with location read from config file
740 location_model.insert(0, [self.weather_location])
741
742 locationlist_widget.set_model(location_model)
743
744 # Checkboxes
745 weather_display_checkbox = self.widgets.get_widget(
746 "weather_display_checkbox")
747 display_val = self.config.display_weather_in_client()
748 weather_display_checkbox.set_active(display_val)
749 if not display_val:
750 self.widgets.get_widget("button_add_weather").set_sensitive(False)
751 self.widgets.get_widget("button_remove_weather").set_sensitive(
752 False)
753 self.widgets.get_widget("treeview_locations").set_sensitive(False)
754
755 def add_to_model_and_config(self, selected_folder, model, folders, kind):
756 """
757 Add selected_folder to the model and the folders list while updating
758 the configuration item section specified by type
759 """
760 if not selected_folder in folders:
761 model.append([selected_folder])
762
763 if(folders == None):
764 folders = [selected_folder]
765 else:
766 folders.append(selected_folder)
767
768 if "" in folders:
769 folders.remove("")
770 str_folders = ";".join(folders)
771 self.config.write_content_value(kind, "folders", str_folders)
772
773 def init_model(self, model, items):
774 """Fill model with items from supplied list"""
775 for i in range(len(items)):
776 if not str(items[i]).strip() == "":
777 model.insert(i, [items[i]])
778
779
780class LogViewer:
781 """
782 Implements dialog that allows user to see logged events.
783
784 This dialog is used to check Entertainer logfiles. It reads all data from
785 selected file and saves rows to self.log_rows. Then it filters unwanted
786 rows away by calling self.filterMessages(). This method adds rows to
787 ListStore, which is the model of TreeView object.
788
789 Combobox and refresh -button actions read files again
790 Checkbox actions just filter current rows again
791 """
792
793 GLADE_DIR = os.path.join(os.path.dirname(__file__), "glade")
794
795 # Is this dialog running as a stand alone process
796 __STAND_ALONE = None
797
798 widgets = None
799 dialog = None
800 log_store = None
801 log_rows = []
802
803 gladefile = os.path.join(GLADE_DIR, "log_dialog.glade")
804
805 def __init__(self, stand_alone):
806 self.logfile_entertainer = Configuration().ENTERTAINER_LOG
807 self.logger = Logger().getLogger('utils.log_viewer')
808
809 self.__STAND_ALONE = stand_alone
810 try:
811 self.widgets = gtk.glade.XML(self.gladefile)
812 except RuntimeError:
813 self.logger.critical("Couldn't open glade file: " + self.gladefile)
814 sys.exit(1)
815 callback_dic = {
816 "on_close_log_button_clicked" : self.on_close_log_button_clicked,
817 "on_log_refresh_button_clicked" : self.update_log_rows,
818 "on_checkbutton_debug_toggled" : self.filter_messages,
819 "on_checkbutton_critical_toggled" : self.filter_messages,
820 "on_checkbutton_error_toggled" : self.filter_messages,
821 "on_checkbutton_warning_toggled" : self.filter_messages,
822 "on_checkbutton_info_toggled" : self.filter_messages }
823
824 self.widgets.signal_autoconnect(callback_dic)
825
826 # Create log treeview
827 treeview = self.widgets.get_widget("treeview_log")
828 cell_renderer1 = gtk.CellRendererText()
829 cell_renderer2 = gtk.CellRendererText()
830 cell_renderer3 = gtk.CellRendererText()
831 cell_renderer4 = gtk.CellRendererText()
832
833 column1 = gtk.TreeViewColumn("Date")
834 column1.pack_start(cell_renderer1, True)
835 column1.set_attributes(cell_renderer1, text = 0)
836
837 column2 = gtk.TreeViewColumn("Time")
838 column2.pack_start(cell_renderer2, True)
839 column2.set_attributes(cell_renderer2, text = 1)
840
841 column3 = gtk.TreeViewColumn("Type")
842 column3.pack_start(cell_renderer3, True)
843 column3.set_attributes(cell_renderer3, text = 2)
844
845 column4 = gtk.TreeViewColumn("Message")
846 column4.pack_end(cell_renderer4, True)
847 column4.set_attributes(cell_renderer4, text = 3)
848
849 treeview.append_column(column1)
850 treeview.append_column(column2)
851 treeview.append_column(column3)
852 treeview.append_column(column4)
853 treeview.set_headers_visible(True)
854
855 # Set model to view and read data from logfile
856 self.log_store = gtk.ListStore(str, str, str, str)
857 treeview.set_model(self.log_store)
858 self.update_log_rows()
859
860 # Show Log viewer dialog
861 self.dialog = self.widgets.get_widget("LogDialog")
862 self.dialog.resize(750, 500)
863 self.dialog.connect("destroy", self.on_close_log_button_clicked)
864 self.dialog.show()
865
866 def update_log_rows(self, widget=None):
867 """Read logfile and udpate treeview"""
868 self.log_rows[:] = []
869
870 try:
871 for line in open(self.logfile_entertainer, 'r'):
872 try:
873 line_table = line.split()
874 message = ' '.join(line_table[3:])
875 row = line_table[:3] + [message]
876 parsed_row = parse_row(row)
877 self.log_rows.append(parsed_row)
878 except IndexError:
879 print "Cannot parse log line: ", line
880 except IOError:
881 print "Cannot find logfile: ", self.logfile_entertainer
882
883 # Reverse so that the latest message is at top
884 self.log_rows.reverse()
885 # Filter unwated message types
886 self.filter_messages()
887
888 def filter_messages(self, widget = None):
889 """Checks which message types should be displayed on treeview"""
890 if self.log_store:
891 self.log_store.clear()
892
893 debug = self.widgets.get_widget("checkbutton_debug").get_active()
894 critical = self.widgets.get_widget("checkbutton_critical").get_active()
895 error = self.widgets.get_widget("checkbutton_error").get_active()
896 warning = self.widgets.get_widget("checkbutton_warning").get_active()
897 info = self.widgets.get_widget("checkbutton_info").get_active()
898
899 for element in self.log_rows:
900 if element[2] == "DEBUG" and debug:
901 self.log_store.append(element)
902 elif element[2] == "CRITICAL" and critical:
903 self.log_store.append(element)
904 elif element[2] == "ERROR" and error:
905 self.log_store.append(element)
906 elif element[2] == "WARNING" and warning:
907 self.log_store.append(element)
908 elif element[2] == "INFO" and info:
909 self.log_store.append(element)
910
911 # Signal handlers
912 def on_close_log_button_clicked(self, widget):
913 """
914 If running as a stand alone process, quit.
915 Otherwise only destroy dialog.
916 """
917 self.dialog.hide()
918 self.dialog.destroy()
919 if(self.__STAND_ALONE):
920 gtk.main_quit()
921
922
923def parse_row(row):
924 """
925 This parses the input list into a list suitable for the logviewer
926 @author Joshua Scotton
927 @param row The input list [Date, Time, Class, Type + Description]
928 """
929 if row[3][:5] == "DEBUG":
930 return [row[0], row[1], "DEBUG",
931 row[2] + ": " + row[3][5:]]
932 elif row[3][:8] == "CRITICAL":
933 return [row[0], row[1], "CRITICAL",
934 row[2] + ": " + row[3][8:]]
935 elif row[3][:5] == "ERROR":
936 return [row[0], row[1], "ERROR",
937 row[2] + ": " + row[3][5:]]
938 elif row[3][:7] == "WARNING":
939 return [row[0], row[1], "WARNING",
940 row[2] + ": " + row[3][7:]]
941 elif row[3][:4] == "INFO":
942 return [row[0], row[1], "INFO",
943 row[2] + ": " + row[3][4:]]
944
945
946class OpenFeedSourceDialog:
947 '''Feed source reader dialog'''
948
949 GLADE_DIR = os.path.join(os.path.dirname(__file__), "glade")
950
951 widgets = None
952 dialog = None
953 tree_widget = None
954 feeds = None
955 url = None
956
957 def __init__(self, the_widget, the_feeds):
958 """initialises the gtk window and displays it"""
959
960 #feeds is a pointer to a list of feeds from the config file
961 self.feeds = the_feeds
962
963 #needed so we can add feeds to the feed list widget
964 self.tree_widget = the_widget
965
966 # Load glade UI
967 self.gladefile = os.path.join(self.GLADE_DIR,
968 "open_feed_source_dialog.glade")
969 self.widgets = gtk.glade.XML(self.gladefile)
970
971 # Get content management dialog and bind signal callbacks
972 self.dialog = self.widgets.get_widget("open_source_dialog")
973 if (self.dialog):
974 callback_dic = {
975 "on_fileOpen_clicked" : self.on_fileOpen_clicked,
976 "on_lifereaButton_clicked" : self.on_lifereaButton_clicked,
977 "on_enterURL_clicked" : self.on_enterURL_clicked,
978 "on_closeButton_clicked" : self.on_closeButton_clicked,
979 "on_url_dialog_ok_button_clicked" :
980 self.on_url_dialog_ok_button_clicked,
981 "on_url_dialog_cancel_button_clicked" :
982 self.on_url_dialog_cancel_button_clicked,
983 "on_url_dialog_delete_event" : self.on_url_dialog_delete_event
984 }
985
986 self.widgets.signal_autoconnect(callback_dic)
987
988 # Initilize dialog widgets with correct values and show dialog
989 self.dialog.resize(300, 200)
990 self.dialog.run()
991
992 def on_fileOpen_clicked(self, widget):
993 """Opens add file dialog and then adds all feeds in the opml file
994 selected """
995
996 #get the model for the feed list widget so we can add the new feeds
997 model = self.tree_widget.get_model()
998
999 #create select file dialog
1000 dialog = gtk.FileChooserDialog(_("Select OPML file"), None,
1001 gtk.FILE_CHOOSER_ACTION_OPEN,
1002 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
1003 gtk.RESPONSE_OK))
1004
1005 #set dialog up to filter for only opml files
1006 file_filter = gtk.FileFilter()
1007 file_filter.set_name(_("OPML files"))
1008 file_filter.add_pattern("*.opml")
1009 dialog.add_filter(file_filter)
1010
1011 #set dialog up to allow multiple selections
1012 dialog.set_select_multiple(True)
1013
1014 #run the dialog
1015 status = dialog.run()
1016
1017 # if file was selected, get list of feeds from it and add to
1018 #model and update config file
1019 if(status == gtk.RESPONSE_OK):
1020 FeedConfigTools().add_file_feeds_to_widget(
1021 dialog.get_filenames(), model, self.feeds)
1022 dialog.destroy()
1023
1024 def on_lifereaButton_clicked(self, widget):
1025 """Adds any liferea feeds it finds to the feed widget and config file
1026 """
1027 #get the model
1028 model = self.tree_widget.get_model()
1029 #get the liferea feeds and then send everything to the
1030 #add_file_feeds_to_widget method
1031 FeedConfigTools().add_file_feeds_to_widget(
1032 [OPMLParser().get_liferea_opml()], model, self.feeds)
1033
1034 def on_enterURL_clicked(self, widget):
1035 """gets a opml file link from a user and adds any feeds it finds to the
1036 feed widget and config file"""
1037 url_dialog = self.widgets.get_widget("url_dialog")
1038 model = self.tree_widget.get_model()
1039 # Open dialog
1040 url_dialog.set_title(_("Add OPML File"))
1041 status = url_dialog.run()
1042 # If folder was selected we add it to model and update config file
1043 if(status == gtk.RESPONSE_OK):
1044 FeedConfigTools().add_file_feeds_to_widget([self.url],
1045 model, self.feeds)
1046
1047 def on_closeButton_clicked(self, widget):
1048 self.dialog.hide()
1049 self.dialog.destroy()
1050
1051 def on_url_dialog_ok_button_clicked(self, widget):
1052 """URL dialog OK button pressed. Sets self.url"""
1053 url_dialog = self.widgets.get_widget("url_dialog")
1054 url_entry = self.widgets.get_widget("url_entry")
1055 url_dialog.hide()
1056 self.url = url_entry.get_text()
1057 url_entry.set_text("")
1058 url_dialog.response(gtk.RESPONSE_OK)
1059
1060 def on_url_dialog_cancel_button_clicked(self, widget):
1061 """URL dialog cancelled. Hides dialog"""
1062 url_dialog = self.widgets.get_widget("url_dialog")
1063 url_entry = self.widgets.get_widget("url_entry")
1064 url_dialog.hide()
1065 url_entry.set_text("")
1066 url_dialog.response(gtk.RESPONSE_CANCEL)
1067
1068 def on_url_dialog_delete_event(self, widget, data):
1069 """Dialog's X clicked. Hides dialog"""
1070 url_dialog = self.widgets.get_widget("url_dialog")
1071 url_entry = self.widgets.get_widget("url_entry")
1072 url_dialog.hide()
1073 url_entry.set_text("")
1074 url_dialog.response(gtk.RESPONSE_CANCEL)
1075 return True
1076
1077
1078class PreferencesDialog:
1079 """This is a preferences editing tool for Entertainer media center."""
1080
1081 # Glade file location
1082 GLADE_DIR = os.path.join(os.path.dirname(__file__), "glade")
1083
1084 def __init__(self, stand_alone):
1085 """
1086 Initialize Preferenced dialog
1087 @param stand_alone: Is this dialog stand alone process or part of
1088 client
1089 """
1090 # Is this dialog running as a stand alone process
1091 self.STAND_ALONE = stand_alone
1092 self.themes = []
1093 self.config = Configuration()
1094
1095 # Load glade UI
1096 self.gladefile = os.path.join(self.GLADE_DIR,
1097 "entertainer-preferences.glade")
1098 self.widgets = gtk.glade.XML(self.gladefile)
1099
1100 # Get preferences dialog and bind signal callbacks
1101 self.pref_dialog = self.widgets.get_widget("PreferencesDialog")
1102 if (self.pref_dialog):
1103 callback_dic = {
1104 "on_close_button_clicked" : self.on_close_button_clicked,
1105 "on_PreferencesDialog_delete_event" : self.on_dialog_closed,
1106 "on_theme_list_cursor_changed" :
1107 self.on_theme_list_cursor_changed,
1108 "on_checkbutton_autostart_toggled" :
1109 self.on_checkbutton_autostart_toggled,
1110 "on_checkbutton_fullscreen_toggled" :
1111 self.on_checkbutton_fullscreen_toggled,
1112 "on_checkbutton_systray_icon_toggled" :
1113 self.on_checkbutton_systray_icon_toggled,
1114 "on_effect_checkbox_toggled" : self.on_effect_checkbox_toggled,
1115 "on_effect_combobox_changed" : self.on_effect_combobox_changed,
1116 "on_add_button_clicked" : self.on_add_button_clicked,
1117 "on_remove_button_clicked" : self.on_remove_button_clicked,
1118 "on_spinbutton_slideshow_step_value_changed":
1119 self.on_spinbutton_slideshow_step_value_changed
1120 }
1121 self.widgets.signal_autoconnect(callback_dic)
1122
1123 # Initilize dialog widgets with correct values and show dialog
1124 self.init_dialog_values_from_configure_file()
1125 #self.pref_dialog.resize(500,400)
1126 self.pref_dialog.show()
1127
1128 def load_themes(self):
1129 """Load themes"""
1130 themes = os.listdir(os.path.join(self.config.get_cfg_dir(), 'themes'))
1131 for element in themes:
1132 theme = os.path.join(self.config.get_cfg_dir(), 'themes', element)
1133 if os.path.isdir(theme):
1134 self.themes.append(element)
1135
1136 def on_add_button_clicked(self, widget):
1137 """Add theme button clicked"""
1138 themelist = self.widgets.get_widget("theme_list")
1139 model = themelist.get_model()
1140 # Open "Select folder" dialog
1141 dialog = gtk.FileChooserDialog(_("Select theme package file"),
1142 None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL,
1143 gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK), None)
1144 file_filter = gtk.FileFilter()
1145 file_filter.set_name(_("Theme package (tar.gz)"))
1146 file_filter.add_pattern("*.tar.gz")
1147 dialog.add_filter(file_filter)
1148 status = dialog.run()
1149
1150 # If theme was selected with file chooser
1151 if(status == gtk.RESPONSE_OK):
1152 package = dialog.get_filename()
1153 tar = tarfile.open(package, 'r:gz') # Open tar.gz package
1154
1155 # Make sure that package contains configuration file (is theme)
1156 content = tar.getnames()
1157 theme_name = None
1158 is_theme = False
1159 for element in content:
1160 if element[-10:] == "theme.conf":
1161 theme_name = element[:-11]
1162 is_theme = True
1163
1164 # Install them
1165 if is_theme:
1166 tar.extractall(os.path.join(self.config.get_cfg_dir(),
1167 'themes'))
1168 model.insert(len(model), [theme_name])
1169 else:
1170 error = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
1171 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("Invalid theme file!"))
1172 error.run()
1173 error.destroy()
1174
1175 dialog.destroy()
1176
1177 def on_remove_button_clicked(self, widget):
1178 """Remove theme button clicked"""
1179 # Get currently selected theme
1180 themelist = self.widgets.get_widget("theme_list")
1181 model = themelist.get_model()
1182 selection = themelist.get_selection().get_selected()
1183 name = model.get_value(selection[1], 0)
1184
1185 confirm = gtk.MessageDialog(None,
1186 gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
1187 _("Are you sure you want to delete\ntheme: %(name)s") % \
1188 {'name': name})
1189 status = confirm.run()
1190 confirm.destroy()
1191 if(status == gtk.RESPONSE_YES):
1192 themedir = os.path.join(self.config.get_cfg_dir(), 'themes', name)
1193 shutil.rmtree(themedir)
1194 model.remove(selection[1])
1195 themelist.set_cursor(0)
1196 self.themes.remove(name)
1197
1198 def on_theme_list_cursor_changed(self, widget):
1199 """Executed when theme is changed in theme list. Update preview."""
1200 # Get currently selected theme
1201 themelist = self.widgets.get_widget("theme_list")
1202 model = themelist.get_model()
1203 selection = themelist.get_selection().get_selected()
1204 name = model.get_value(selection[1], 0)
1205 themedir = os.path.join(self.config.get_cfg_dir(), 'themes', name)
1206 theme = Theme(theme_path=themedir)
1207
1208 # Update preview
1209 image = self.widgets.get_widget("theme_image")
1210 image.set_from_file(os.path.join(themedir, "thumbnail.png"))
1211 name = self.widgets.get_widget("name_label")
1212 name.set_text(theme.getName())
1213 author = self.widgets.get_widget("author_label")
1214 author.set_text(theme.getAuthor())
1215 licence = self.widgets.get_widget("licence_label")
1216 licence.set_text(theme.getLicence())
1217 copyright_label = self.widgets.get_widget("copyright_label")
1218 copyright_label.set_text(theme.getCopyright())
1219 comment = self.widgets.get_widget("comment_label")
1220 comment.set_text(theme.getComment())
1221
1222 self.config.write_preference_value("General", "theme", name.get_text())
1223
1224 def on_checkbutton_autostart_toggled(self, widget):
1225 """Server Autostart checkbox toggled"""
1226 self.config.write_preference_value("General", "start_server_auto",
1227 widget.get_active())
1228
1229 def on_checkbutton_fullscreen_toggled(self, widget):
1230 """Start in fullscreen checkbox toggled"""
1231 self.config.write_preference_value("General", "start_in_fullscreen",
1232 widget.get_active())
1233
1234 def on_checkbutton_systray_icon_toggled(self, widget):
1235 """System Tray Icon checkbox toggled"""
1236 self.config.write_preference_value("General", "display_icon",
1237 widget.get_active())
1238
1239 def on_effect_checkbox_toggled(self, widget):
1240 """Effect checkbox toggled"""
1241 combobox = self.widgets.get_widget("effect_combobox")
1242 combobox.set_sensitive(widget.get_active())
1243 self.config.write_preference_value("General", "show_effects",
1244 widget.get_active())
1245
1246 def on_effect_combobox_changed(self, widget):
1247 """User changed effect for screen transitions"""
1248 text = widget.get_active_text()
1249 if text == _("No effect"):
1250 english_text = "No effect"
1251 if text == _("Crossfade"):
1252 english_text = "Crossfade"
1253 if text == _("Zoom and fade"):
1254 english_text = "Zoom and fade"
1255 if text == _("Slide"):
1256 english_text = "Slide"
1257 self.config.write_preference_value("General", "transition_effect",
1258 english_text)
1259
1260 def on_spinbutton_slideshow_step_value_changed(self, widget):
1261 """Activation of slideshow effects"""
1262 self.config.write_preference_value("Photographs", "slideshow_step",
1263 int(widget.get_value()))
1264
1265 def on_dialog_closed(self, widget, after):
1266 """Callback function for dialog's close button"""
1267 self.close()
1268
1269 def on_close_button_clicked(self, widget):
1270 """Callback function for dialog's close button"""
1271 self.close()
1272
1273 def close(self):
1274 """Close Dialog"""
1275 try:
1276 proxy = MessageBusProxy(client_name = "Preferences GUI")
1277 proxy.connectToMessageBus()
1278 proxy.sendMessage(Message(MessageType.PREFERENCES_CONF_UPDATED))
1279 proxy.disconnectFromMessageBus()
1280 except socket.error:
1281 error = gtk.MessageDialog(
1282 None, gtk.DIALOG_MODAL,
1283 gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, _(
1284 "Entertainer backend is not running. "
1285 "Config updates will happen on backend restart."
1286 ))
1287 error.run()
1288 error.destroy()
1289
1290
1291 if(self.STAND_ALONE):
1292 self.pref_dialog.hide()
1293 self.pref_dialog.destroy()
1294 gtk.main_quit()
1295 else:
1296 self.pref_dialog.hide()
1297 self.pref_dialog.destroy()
1298
1299 def init_dialog_values_from_configure_file(self):
1300 """Read configure file and set dialog widget values with read values.
1301 """
1302 self.load_themes()
1303 current_theme = self.config.get_theme_name()
1304
1305 themelist_widget = self.widgets.get_widget("theme_list")
1306 model = gtk.ListStore(str)
1307
1308 cell_renderer = gtk.CellRendererText()
1309 column = gtk.TreeViewColumn("Themes", cell_renderer, text=0)
1310 themelist_widget.append_column(column)
1311
1312 # Fill model with installed themes
1313 for i in range(len(self.themes)):
1314 model.insert(i, [self.themes[i]])
1315
1316 themelist_widget.set_model(model)
1317
1318 # Set current theme selected in theme list
1319 index = model.get_iter_first()
1320 unselected = True
1321 index_counter = 0
1322 while(unselected):
1323 name = model.get_value(index, 0)
1324 if name == current_theme:
1325 unselected = False
1326 themelist_widget.set_cursor(index_counter)
1327 index = model.iter_next(index)
1328 index_counter += 1
1329
1330 # Checkboxes
1331 checkbutton_systray_icon = self.widgets.get_widget(
1332 "checkbutton_systray_icon")
1333 if self.config.tray_icon_enabled():
1334 checkbutton_systray_icon.set_active(True)
1335 else:
1336 checkbutton_systray_icon.set_active(False)
1337
1338 checkbutton_autostart = self.widgets.get_widget("checkbutton_autostart")
1339 if self.config.start_auto_server():
1340 checkbutton_autostart.set_active(True)
1341 else:
1342 checkbutton_autostart.set_active(False)
1343
1344 checkbutton_fullscreen = self.widgets.get_widget(
1345 "checkbutton_fullscreen")
1346 if self.config.start_in_fullscreen():
1347 checkbutton_fullscreen.set_active(True)
1348 else:
1349 checkbutton_fullscreen.set_active(False)
1350
1351 effect_checkbox = self.widgets.get_widget("effect_checkbox")
1352 effect_combobox = self.widgets.get_widget("effect_combobox")
1353 if self.config.show_effects():
1354 effect_checkbox.set_active(True)
1355 effect_combobox.set_sensitive(True)
1356 else:
1357 effect_checkbox.set_active(False)
1358 effect_combobox.set_sensitive(False)
1359
1360 spinbutton_slideshow_step = self.widgets.get_widget(
1361 "spinbutton_slideshow_step")
1362 spinbutton_slideshow_step.set_value(self.config.get_slideshow_step())
1363
1364 # Set Effect Combobox value (Text values are set in glade file)
1365 effect = self.config.transition_effect()
1366 if effect == "No effect":
1367 effect_combobox.set_active(0)
1368 if effect == "Crossfade":
1369 effect_combobox.set_active(1)
1370 if effect == "Zoom and fade":
1371 effect_combobox.set_active(2)
1372 if effect == "Slide":
1373 effect_combobox.set_active(3)
1374
01375
=== added file 'entertainerlib/download.py'
--- entertainerlib/download.py 1970-01-01 00:00:00 +0000
+++ entertainerlib/download.py 2009-05-06 04:46:51 +0000
@@ -0,0 +1,453 @@
1'''Downloader classes.'''
2
3__licence__ = "GPLv2"
4__copyright__ = "2009 Entertainer Developers"
5
6import locale
7import os
8import re
9import socket
10import threading
11import urllib
12from xml.dom import minidom
13
14import gobject
15
16# Amazon licence for Entertainer
17LICENSE_KEY = "1YCWYZ0SPPAJ27YZZ482"
18DEFAULT_LOCALE = "en_US"
19ASSOCIATE = "webservices-20"
20
21# We are not allowed to batch more than 2 requests at once
22# http://docs.amazonwebservices.com/AWSEcommerceService/4-0/
23# PgCombiningOperations.html
24MAX_BATCH_JOBS = 2
25
26
27class AlbumArtDownloader(threading.Thread):
28 """
29 Search and download album art from the internet.
30
31 This class is heavily based on Rhythmbox - AlbumArt plugin's class
32 'AmazonCoverArtSearch'. That plugin is released under GPLv2 (or higher)
33 and it's copyrights belong to Gareth Murphy and Martin Szulecki.
34
35 See more: http://www.gnome.org/projects/rhythmbox/
36
37 If you want better cover search, please contribute to Rhyhtmbox project.
38 """
39
40 def __init__(self, album, artist, art_file_path, callback = None):
41 """
42 Initialize album art downloader
43 @param album: Album title
44 @param artist: Artist name
45 @param art_file_path: Path where albumart is saved
46 @param callback: Callback function that is called after search if set
47 """
48 threading.Thread.__init__(self)
49 self.setName("AlbumArt Downloader")
50 self.callback_function = callback # Callback function
51 self.album = album # Album title
52 self.artist = artist # Artist name
53 # Album art files are in this directory
54 self.path = art_file_path
55 (self.tld, self.encoding) = self.__get_locale ()
56
57 def run(self):
58 """Start searching and downloading albumart."""
59 self.search()
60
61 def __get_locale (self):
62 '''Get locale information from user\'s machine'''
63 # "JP is the only locale that correctly takes UTF8 input.
64 # All other locales use LATIN1."
65 # http://developer.amazonwebservices.com/connect/
66 # entry.jspa?externalID=1295&categoryID=117
67 supported_locales = {
68 "en_US" : ("com", "latin1"),
69 "en_GB" : ("co.uk", "latin1"),
70 "de" : ("de", "latin1"),
71 "ja" : ("jp", "utf8")
72 }
73
74 lc_id = DEFAULT_LOCALE
75 default = locale.getdefaultlocale ()[0]
76 if default:
77 if supported_locales.has_key (default):
78 lc_id = default
79 else:
80 lang = default.split("_")[0]
81 if supported_locales.has_key (lang):
82 lc_id = lang
83
84 return supported_locales[lc_id]
85
86 def __valid_match (self, item):
87 '''Determine if item matches tag criteria'''
88 return (hasattr (item, "LargeImage") or hasattr (item, "MediumImage")) \
89 and hasattr (item, "ItemAttributes")
90
91 def __tidy_up_string (self, str_input):
92 """
93 Tidy up string. Remove spaces, convert to lowercase and replace chars.
94 """
95 # Lowercase
96 str_input = str_input.lower ()
97 # Strip
98 str_input = str_input.strip ()
99
100 # TODO: Convert accented to unaccented
101 str_input = str_input.replace (" - ", " ")
102 str_input = str_input.replace (": ", " ")
103 str_input = str_input.replace (" & ", " and ")
104
105 return str_input
106
107 def search(self):
108 """Search album art from Amazon"""
109 self.searching = True
110 self.cancel = False
111 self.keywords = []
112
113 st_artist = self.artist or u'Unknown'
114 st_album = self.album or u'Unknown'
115
116 if st_artist == st_album == u'Unknown':
117 self.on_search_completed (None)
118 return
119
120 # Tidy up
121
122 # Replace quote characters
123 # don't replace single quote: could be important punctuation
124 for char in ["\""]:
125 st_artist = st_artist.replace (char, '')
126 st_album = st_album.replace (char, '')
127
128
129 self.st_album = st_album
130 self.st_artist = st_artist
131
132 # Remove variants of Disc/CD [1-9] from album title before search
133 for exp in ["\([Dd]isc *[1-9]+\)", "\([Cc][Dd] *[1-9]+\)"]:
134 p = re.compile (exp)
135 st_album = p.sub ('', st_album)
136
137 st_album_no_vol = st_album
138 for exp in ["\(*[Vv]ol.*[1-9]+\)*"]:
139 p = re.compile (exp)
140 st_album_no_vol = p.sub ('', st_album_no_vol)
141
142 self.st_album_no_vol = st_album_no_vol
143
144 # Save current search's entry properties
145 self.search_album = st_album
146 self.search_artist = st_artist
147 self.search_album_no_vol = st_album_no_vol
148
149 # TODO: Improve to decrease wrong cover downloads, maybe add severity?
150 # Assemble list of search keywords (and thus search queries)
151 if st_album == u'Unknown':
152 self.keywords.append ("%s Best of" % (st_artist))
153 self.keywords.append ("%s Greatest Hits" % (st_artist))
154 self.keywords.append ("%s Essential" % (st_artist))
155 self.keywords.append ("%s Collection" % (st_artist))
156 self.keywords.append ("%s" % (st_artist))
157 elif st_artist == u'Unknown':
158 self.keywords.append ("%s" % (st_album))
159 if st_album_no_vol != st_artist:
160 self.keywords.append ("%s" % (st_album_no_vol))
161 self.keywords.append ("Various %s" % (st_album))
162 else:
163 if st_album != st_artist:
164 self.keywords.append ("%s %s" % (st_artist, st_album))
165 if st_album_no_vol != st_album:
166 self.keywords.append ("%s %s" %
167 (st_artist, st_album_no_vol))
168 self.keywords.append ("Various %s" % (st_album))
169 self.keywords.append ("%s" % (st_artist))
170
171 # Initiate asynchronous search
172 self.search_next ()
173
174 def search_next(self):
175 """Search again, because the last one didn't find any covers."""
176 if len (self.keywords) == 0:
177 # No keywords left to search -> no results
178 self.on_search_completed (None)
179 return False
180
181 self.searching = True
182
183 url = "http://ecs.amazonaws." + self.tld + "/onca/xml" \
184 "?Service=AWSECommerceService" \
185 "&AWSAccessKeyId=" + LICENSE_KEY + \
186 "&AssociateTag=" + ASSOCIATE + \
187 "&ResponseGroup=Images,ItemAttributes" \
188 "&Operation=ItemSearch" \
189 "&ItemSearch.Shared.SearchIndex=Music"
190
191 job = 1
192 while job <= MAX_BATCH_JOBS and len (self.keywords) > 0:
193 keyword = self.keywords.pop (0)
194 keyword = keyword.encode (self.encoding, "ignore")
195 keyword = keyword.strip ()
196 keyword = urllib.quote (keyword)
197 url += "&ItemSearch.%d.Keywords=%s" % (job, keyword)
198 job += 1
199
200 # Retrieve search for keyword
201 temp = urllib.urlopen(url)
202 search_results = temp.read()
203 self.on_search_response(search_results)
204 return True
205
206 def __unmarshal(self, element):
207 rc = object()
208 child_elements = [e for e in element.childNodes if isinstance (e,
209 minidom.Element)]
210 if child_elements:
211 for child in child_elements:
212 key = child.tagName
213 if hasattr (rc, key):
214 if not isinstance (getattr (rc, key), list):
215 setattr (rc, key, [getattr (rc, key)])
216 getattr (rc, key).append (self.__unmarshal (child))
217 # get_best_match_urls() wants a list, even if there is only
218 # one item/artist
219 elif child.tagName in ("Items", "Item", "Artist"):
220 setattr (rc, key, [self.__unmarshal(child)])
221 else:
222 setattr (rc, key, self.__unmarshal(child))
223 else:
224 rc = "".join ([e.data for e in element.childNodes if isinstance (e,
225 minidom.Text)])
226 return rc
227
228 def on_search_response (self, result_data):
229 '''Check search results
230
231 If results are not good, we search again with the next keyword.
232 '''
233 if result_data is None:
234 self.search_next()
235 return
236
237 try:
238 xmldoc = minidom.parseString(result_data)
239 except (TypeError, AttributeError):
240 self.search_next()
241 return
242
243 data = self.__unmarshal (xmldoc)
244 if not hasattr (data, "ItemSearchResponse") or \
245 not hasattr (data.ItemSearchResponse, "Items"):
246 # Something went wrong ...
247 self.search_next ()
248 else:
249 # We got some search results
250 self.on_search_results (data.ItemSearchResponse.Items)
251
252 def on_search_results (self, results):
253 '''Results were found, now we need to take action.'''
254 self.on_search_completed (results)
255
256 def on_search_completed (self, result):
257 """
258 Search completed and results found.
259
260 Download large album art image from the first result and save it to
261 the disk. This function diverges greatly from the rhythmbox
262 implementation in order to avoid their loader and CoverArtDatabase
263 """
264 self.searching = False
265 image_urls = self.get_best_match_urls(result)
266 if len(image_urls) == 0:
267 return
268 image_url = image_urls[0]
269 image_file = urllib.urlopen(image_url)
270 # base64 encode artist and album so there can be a '/' in the artist
271 # or album
272 artist_album = self.artist + " - " + self.album
273 artist_album = artist_album.encode("base64")
274
275 dest = open(os.path.join(self.path, artist_album + ".jpg"),'w')
276 dest.write(image_file.read())
277 dest.close()
278
279 if self.callback_function is not None:
280 self.callback_function(self.artist, self.album)
281
282 def get_best_match_urls (self, search_results):
283 """Return tuple of URL's to large and medium cover of the best match"""
284 # Default to "no match", our results must match our criteria
285
286 # This code comes from Rhythmbox so we can't control the use of 'filter'
287 # pylint: disable-msg=W0141
288
289 if not search_results:
290 return []
291
292 best_match = None
293
294 for result in search_results:
295 if not hasattr (result, "Item"):
296 # Search was unsuccessful, try next batch job
297 continue
298
299 items = filter(self.__valid_match, result.Item)
300 if self.search_album != u'Unknown':
301 album_check = self.__tidy_up_string (self.search_album)
302 for item in items:
303 if not hasattr (item.ItemAttributes, "Title"):
304 continue
305
306 album = self.__tidy_up_string (item.ItemAttributes.Title)
307 if album == album_check:
308 # Found exact album, can not get better than that
309 best_match = item
310 break
311 # If we already found a best_match, just keep checking for
312 # exact one. Check the results for both an album name that
313 # contains the name we're searching for, and an album name
314 # that's a substring of the name we're searching for
315 elif (best_match is None) and \
316 (album.find (album_check) != -1 or
317 album_check.find (album) != -1):
318 best_match = item
319
320 # If we still have no definite hit, use first result where artist
321 # matches
322 if (self.search_album == u'Unknown' and \
323 self.search_artist != u'Unknown'):
324 artist_check = self.__tidy_up_string (self.search_artist)
325 if best_match is None:
326 # Check if artist appears in the Artists list
327 hit = False
328 for item in items:
329 if not hasattr (item.ItemAttributes, "Artist"):
330 continue
331
332 for artist in item.ItemAttributes.Artist:
333 artist = self.__tidy_up_string (artist)
334 if artist.find (artist_check) != -1:
335 best_match = item
336 hit = True
337 break
338 if hit:
339 break
340
341 urls = [getattr (best_match, size).URL for size in ("LargeImage",
342 "MediumImage")
343 if hasattr (best_match, size)]
344 if urls:
345 return urls
346
347 # No search was successful
348 return []
349
350
351class LyricsDownloader(object):
352 """
353 Search and download song lyrics from the internet.
354 Update music cache if lyrics found.
355 """
356
357 # The permanent user ID from Lyricsfly
358 # NOTICE: This is the personal user ID for Entertainer, if you want to
359 # experiment with the API from lyricsfly you can get an ID here =>
360 # http://lyricsfly.com/api/, don't use this one as abuse of our key may
361 # invalidate it.
362 _LYRICSFLY_KEY = 'YzIxOTM4M2NkNGQ4MmRmODEtZW50ZXJ0YWluZXItcHJvamVjdC5jb20='
363
364 def __init__(self, title, artist, callback):
365 """
366 Initialize lyrics downloader.
367 @param title: Title of the track
368 @param artist: Artist of the track
369 @param callback: Callback function which is called when search is over.
370 lyrics are given as a parameter to this callback function.
371 """
372 self.title = title.lower()
373 self.artist = artist.lower()
374 self.callback = callback
375
376 def search(self):
377 """
378 Search lyrics and download if found. Search is done asynchronously.
379 This method returns immediately and set callback is called when search
380 is over.
381 """
382 gobject.timeout_add(2000, self._async_search)
383
384 def _async_search(self):
385 """
386 Search lyrics and download if found
387 """
388 lyrics = ""
389 self._clean_up_artist_title()
390 lyrics_xml = self._get_lyrics_xml()
391 lyrics = self._parse_lyrics_xml(lyrics_xml)
392 self.callback(lyrics)
393
394 def _clean_up_artist_title(self):
395 """
396 Clean up the artist and title.
397 """
398 # Clean spaces
399 self.title = self.title.strip()
400 self.artist = self.artist.strip()
401
402 # Convert title and artist to use in url, special symbols have to be
403 # replaced by a '%' not '%xx'
404 # TODO: Find out what the special symbols are (', &, ...)
405 # not letters, digits, spaces and ()$^*=:;|#@}{][!,.-_\
406 self.artist = urllib.quote(self.artist.encode('utf-8'),
407 "'&()$^*=:;|#@}{][!,\\")
408 self.title = urllib.quote(self.title.encode('utf-8'),
409 "'&()$^*=:;|#@}{][!,\\")
410
411 self.artist = self.artist.replace("'", "%").replace("&", "%")
412 self.title = self.title.replace("'", "%").replace("&", "%")
413
414 def _get_lyrics_xml(self):
415 """
416 Download the lyrics XML-file.
417 """
418 lyrics_xml = None
419
420 # timeout in seconds
421 timeout = 5
422 socket.setdefaulttimeout(timeout)
423
424 url = "http://lyricsfly.com/api/api.php?i=%s&a=%s&t=%s" \
425 % (self._LYRICSFLY_KEY.decode('base64'), self.artist, self.title)
426 temp = urllib.urlopen(url)
427 lyrics_xml = temp.read()
428
429 return lyrics_xml
430
431 def _parse_lyrics_xml(self, lyrics_xml):
432 """Parse lyrics XML and return lyrics string"""
433 xmldoc = minidom.parseString(lyrics_xml).documentElement
434
435 # Get the lyric from the XML file
436 try:
437 lyrics = xmldoc.getElementsByTagName('tx')[0].firstChild.nodeValue
438 except IndexError:
439 return ''
440
441 # Clean spaces and enters
442 lyrics = lyrics.strip().replace('\n', '')
443 lyrics = lyrics.replace('[br]', '\n')
444
445 # Add the artist and title to the top of the lyric
446 lyrics = xmldoc.getElementsByTagName('ar')[0].firstChild.nodeValue + \
447 ' - ' + xmldoc.getElementsByTagName('tt')[0].firstChild.nodeValue + \
448 '\n\n' + lyrics
449
450 xmldoc.unlink()
451
452 return lyrics
453
0454
=== renamed directory 'entertainerlib/utils/glade' => 'entertainerlib/glade'
=== modified file 'entertainerlib/glade/system_tray_icon_menu.glade'
--- entertainerlib/utils/glade/system_tray_icon_menu.glade 2008-08-16 04:28:47 +0000
+++ entertainerlib/glade/system_tray_icon_menu.glade 2009-04-30 01:11:22 +0000
@@ -6,12 +6,12 @@
6 <property name="visible">True</property>6 <property name="visible">True</property>
7 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>7 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
8 <child>8 <child>
9 <widget class="GtkImageMenuItem" id="menuitem_frontend">9 <widget class="GtkImageMenuItem" id="menuitem_client">
10 <property name="visible">True</property>10 <property name="visible">True</property>
11 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>11 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
12 <property name="label" translatable="yes">Open media center GUI</property>12 <property name="label" translatable="yes">Open media center GUI</property>
13 <property name="use_underline">True</property>13 <property name="use_underline">True</property>
14 <signal name="activate" handler="on_menuitem_frontend_activate"/>14 <signal name="activate" handler="on_menuitem_client_activate"/>
15 <child internal-child="image">15 <child internal-child="image">
16 <widget class="GtkImage" id="menu-item-image3">16 <widget class="GtkImage" id="menu-item-image3">
17 <property name="visible">True</property>17 <property name="visible">True</property>
1818
=== renamed directory 'entertainerlib/frontend/gui' => 'entertainerlib/gui'
=== modified file 'entertainerlib/gui/screens/album.py'
--- entertainerlib/frontend/gui/screens/album.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/album.py 2009-05-05 03:31:34 +0000
@@ -8,13 +8,13 @@
8import clutter8import clutter
9import pango9import pango
1010
11from entertainerlib.frontend.gui.screens.screen import Screen11from entertainerlib.gui.screens.screen import Screen
12from entertainerlib.frontend.gui.widgets.eyecandy_texture import (12from entertainerlib.gui.widgets.eyecandy_texture import (
13 EyeCandyTexture)13 EyeCandyTexture)
14from entertainerlib.frontend.gui.widgets.label import Label14from entertainerlib.gui.widgets.label import Label
15from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator15from entertainerlib.gui.widgets.list_indicator import ListIndicator
16from entertainerlib.frontend.gui.widgets.text_menu import TextMenu16from entertainerlib.gui.widgets.text_menu import TextMenu
17from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem17from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
1818
19class Album(Screen):19class Album(Screen):
20 '''Screen that allows user to browse and play tracks of the music album.'''20 '''Screen that allows user to browse and play tracks of the music album.'''
2121
=== modified file 'entertainerlib/gui/screens/artist.py'
--- entertainerlib/frontend/gui/screens/artist.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/artist.py 2009-05-05 03:31:34 +0000
@@ -6,11 +6,11 @@
66
7import pango7import pango
88
9from entertainerlib.frontend.gui.screens.screen import Screen9from entertainerlib.gui.screens.screen import Screen
10from entertainerlib.frontend.gui.tabs.albums_tab import AlbumsTab10from entertainerlib.gui.tabs.albums_tab import AlbumsTab
11from entertainerlib.frontend.gui.tabs.tracks_tab import TracksTab11from entertainerlib.gui.tabs.tracks_tab import TracksTab
12from entertainerlib.frontend.gui.widgets.label import Label12from entertainerlib.gui.widgets.label import Label
13from entertainerlib.frontend.medialibrary.playlist import Playlist13from entertainerlib.client.medialibrary.playlist import Playlist
1414
15class Artist(Screen):15class Artist(Screen):
16 '''Screen that allows user to browse music by artist.'''16 '''Screen that allows user to browse music by artist.'''
1717
=== modified file 'entertainerlib/gui/screens/audio_play.py'
--- entertainerlib/frontend/gui/screens/audio_play.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/audio_play.py 2009-05-05 03:31:34 +0000
@@ -9,10 +9,10 @@
9import gobject9import gobject
10import clutter10import clutter
1111
12from entertainerlib.frontend.gui.screens.screen import Screen12from entertainerlib.gui.screens.screen import Screen
13from entertainerlib.frontend.gui.tabs.lyrics_tab import LyricsTab13from entertainerlib.gui.tabs.lyrics_tab import LyricsTab
14from entertainerlib.frontend.gui.tabs.playing_tab import PlayingTab14from entertainerlib.gui.tabs.playing_tab import PlayingTab
15from entertainerlib.frontend.gui.widgets.eyecandy_texture import (15from entertainerlib.gui.widgets.eyecandy_texture import (
16 EyeCandyTexture)16 EyeCandyTexture)
1717
18class AudioPlay(Screen):18class AudioPlay(Screen):
1919
=== modified file 'entertainerlib/gui/screens/disc.py'
--- entertainerlib/frontend/gui/screens/disc.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/disc.py 2009-05-06 04:46:51 +0000
@@ -12,18 +12,18 @@
12import pango12import pango
13import clutter13import clutter
1414
15from entertainerlib.frontend.gui.screens.screen import Screen15from entertainerlib.gui.screens.screen import Screen
16from entertainerlib.frontend.gui.widgets.eyecandy_texture import (16from entertainerlib.gui.widgets.eyecandy_texture import (
17 EyeCandyTexture)17 EyeCandyTexture)
18from entertainerlib.frontend.gui.widgets.label import Label18from entertainerlib.gui.widgets.label import Label
19from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator19from entertainerlib.gui.widgets.list_indicator import ListIndicator
20from entertainerlib.frontend.gui.widgets.loading_animation import (20from entertainerlib.gui.widgets.loading_animation import (
21 LoadingAnimation)21 LoadingAnimation)
22from entertainerlib.frontend.gui.widgets.texture import Texture22from entertainerlib.gui.widgets.texture import Texture
23from entertainerlib.frontend.gui.widgets.text_menu import TextMenu23from entertainerlib.gui.widgets.text_menu import TextMenu
24from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem24from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
25from entertainerlib.frontend.medialibrary.playlist import Playlist25from entertainerlib.client.medialibrary.playlist import Playlist
26from entertainerlib.utils.albumart_downloader import AlbumArtDownloader26from entertainerlib.download import AlbumArtDownloader
2727
28class Disc(Screen):28class Disc(Screen):
29 '''Screen allows user to play tracks from the current Audio CD.'''29 '''Screen allows user to play tracks from the current Audio CD.'''
3030
=== modified file 'entertainerlib/gui/screens/factory.py'
--- entertainerlib/frontend/gui/screens/factory.py 2009-02-07 17:12:21 +0000
+++ entertainerlib/gui/screens/factory.py 2009-05-05 03:31:34 +0000
@@ -4,25 +4,25 @@
4__copyright__ = '2009, Matt Layman'4__copyright__ = '2009, Matt Layman'
5__author__ = 'Matt Layman <laymansterms.dev@gmail.com>'5__author__ = 'Matt Layman <laymansterms.dev@gmail.com>'
66
7from entertainerlib.frontend.gui.screens.artist import Artist7from entertainerlib.gui.screens.artist import Artist
8from entertainerlib.frontend.gui.screens.album import Album8from entertainerlib.gui.screens.album import Album
9from entertainerlib.frontend.gui.screens.audio_play import AudioPlay9from entertainerlib.gui.screens.audio_play import AudioPlay
10from entertainerlib.frontend.gui.screens.disc import Disc10from entertainerlib.gui.screens.disc import Disc
11from entertainerlib.frontend.gui.screens.feed import Feed11from entertainerlib.gui.screens.feed import Feed
12from entertainerlib.frontend.gui.screens.feed_entry import FeedEntry12from entertainerlib.gui.screens.feed_entry import FeedEntry
13from entertainerlib.frontend.gui.screens.main import Main13from entertainerlib.gui.screens.main import Main
14from entertainerlib.frontend.gui.screens.movie import Movie14from entertainerlib.gui.screens.movie import Movie
15from entertainerlib.frontend.gui.screens.music import Music15from entertainerlib.gui.screens.music import Music
16from entertainerlib.frontend.gui.screens.photo import Photo16from entertainerlib.gui.screens.photo import Photo
17from entertainerlib.frontend.gui.screens.photo_albums import PhotoAlbums17from entertainerlib.gui.screens.photo_albums import PhotoAlbums
18from entertainerlib.frontend.gui.screens.photographs import Photographs18from entertainerlib.gui.screens.photographs import Photographs
19from entertainerlib.frontend.gui.screens.question import Question19from entertainerlib.gui.screens.question import Question
20from entertainerlib.frontend.gui.screens.rss import Rss20from entertainerlib.gui.screens.rss import Rss
21from entertainerlib.frontend.gui.screens.tv_episodes import TvEpisodes21from entertainerlib.gui.screens.tv_episodes import TvEpisodes
22from entertainerlib.frontend.gui.screens.tv_series import TvSeries22from entertainerlib.gui.screens.tv_series import TvSeries
23from entertainerlib.frontend.gui.screens.video import Video23from entertainerlib.gui.screens.video import Video
24from entertainerlib.frontend.gui.screens.video_osd import VideoOSD24from entertainerlib.gui.screens.video_osd import VideoOSD
25from entertainerlib.frontend.gui.screens.weather import WeatherScreen25from entertainerlib.gui.screens.weather import WeatherScreen
2626
27class ScreenFactory(object):27class ScreenFactory(object):
28 '''Generate a screen based on the type provided.'''28 '''Generate a screen based on the type provided.'''
2929
=== modified file 'entertainerlib/gui/screens/feed.py'
--- entertainerlib/frontend/gui/screens/feed.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/feed.py 2009-05-06 04:00:53 +0000
@@ -6,13 +6,13 @@
66
7import pango7import pango
88
9from entertainerlib.frontend.gui.screens.screen import Screen9from entertainerlib.gui.screens.screen import Screen
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.texture import Texture12from entertainerlib.gui.widgets.texture import Texture
13from entertainerlib.frontend.gui.widgets.text_menu import TextMenu13from entertainerlib.gui.widgets.text_menu import TextMenu
14from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem14from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
15from entertainerlib.utils.feed_utils import FeedEntryParser15from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
1616
17class Feed(Screen):17class Feed(Screen):
18 '''Screen is displayed when headlines are accessed for a specific feed.'''18 '''Screen is displayed when headlines are accessed for a specific feed.'''
1919
=== modified file 'entertainerlib/gui/screens/feed_entry.py'
--- entertainerlib/frontend/gui/screens/feed_entry.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/feed_entry.py 2009-05-06 04:00:53 +0000
@@ -6,12 +6,12 @@
66
7import pango7import pango
88
9from entertainerlib.frontend.gui.screens.screen import Screen9from entertainerlib.gui.screens.screen import Screen
10from entertainerlib.frontend.gui.user_event import UserEvent10from entertainerlib.gui.user_event import UserEvent
11from entertainerlib.frontend.gui.widgets.label import Label11from entertainerlib.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea12from entertainerlib.gui.widgets.scroll_area import ScrollArea
13from entertainerlib.frontend.gui.widgets.texture import Texture13from entertainerlib.gui.widgets.texture import Texture
14from entertainerlib.utils.feed_utils import FeedEntryParser14from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
1515
16class FeedEntry(Screen):16class FeedEntry(Screen):
17 '''Screen displays one feed entry.'''17 '''Screen displays one feed entry.'''
1818
=== modified file 'entertainerlib/gui/screens/main.py'
--- entertainerlib/frontend/gui/screens/main.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/main.py 2009-05-06 04:15:18 +0000
@@ -4,17 +4,18 @@
4__copyright__ = "2007, Lauri Taimila"4__copyright__ = "2007, Lauri Taimila"
5__author__ = "Lauri Taimila <lauri@taimila.com>"5__author__ = "Lauri Taimila <lauri@taimila.com>"
66
7import os
8
7import clutter9import clutter
810
9from entertainerlib.frontend.gui.screens.screen import Screen11from entertainerlib.gui.screens.screen import Screen
10from entertainerlib.frontend.gui.widgets.clock_label import ClockLabel12from entertainerlib.gui.widgets.clock_label import ClockLabel
11from entertainerlib.frontend.gui.widgets.label import Label13from entertainerlib.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.scroll_menu import ScrollMenu14from entertainerlib.gui.widgets.scroll_menu import ScrollMenu
13from entertainerlib.frontend.gui.widgets.texture import Texture15from entertainerlib.gui.widgets.texture import Texture
14from entertainerlib.frontend.gui.widgets.text_menu import TextMenu16from entertainerlib.gui.widgets.text_menu import TextMenu
15from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem17from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
16from entertainerlib.utils.cd_utils import eject_cd18from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
17from entertainerlib.utils.feed_utils import FeedEntryParser
1819
19class Main(Screen):20class Main(Screen):
20 '''Screen displayed when frontend is opened and provides main navigation.'''21 '''Screen displayed when frontend is opened and provides main navigation.'''
@@ -102,11 +103,11 @@
102 item5 = Label(size, color, 0, 0, _("Headlines"), "rss")103 item5 = Label(size, color, 0, 0, _("Headlines"), "rss")
103 self.menu.add(item5)104 self.menu.add(item5)
104105
105 if self.config.display_weather_in_frontend():106 if self.config.display_weather_in_client():
106 item6 = Label(size, color, 0, 0, _("Weather"), "weather")107 item6 = Label(size, color, 0, 0, _("Weather"), "weather")
107 self.menu.add(item6)108 self.menu.add(item6)
108109
109 if self.config.display_cd_eject_in_frontend():110 if self.config.display_cd_eject_in_client():
110 item7 = Label(size, color, 0, 0, _("Eject CD"), "eject_cd")111 item7 = Label(size, color, 0, 0, _("Eject CD"), "eject_cd")
111 self.menu.add(item7)112 self.menu.add(item7)
112113
@@ -310,7 +311,7 @@
310 elif item.get_name() == "weather":311 elif item.get_name() == "weather":
311 self.callback("weather")312 self.callback("weather")
312 elif item.get_name() == "eject_cd":313 elif item.get_name() == "eject_cd":
313 eject_cd()314 os.system('eject')
314 elif item.get_name() == "photo":315 elif item.get_name() == "photo":
315 self.callback("photo_albums")316 self.callback("photo_albums")
316 elif item.get_name() == "rss":317 elif item.get_name() == "rss":
317318
=== modified file 'entertainerlib/gui/screens/movie.py'
--- entertainerlib/frontend/gui/screens/movie.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/movie.py 2009-05-05 03:31:34 +0000
@@ -8,15 +8,15 @@
8import gtk8import gtk
9import pango9import pango
1010
11from entertainerlib.frontend.gui.screens.screen import Screen11from entertainerlib.gui.screens.screen import Screen
12from entertainerlib.frontend.gui.user_event import UserEvent12from entertainerlib.gui.user_event import UserEvent
13from entertainerlib.frontend.gui.widgets.eyecandy_texture import (13from entertainerlib.gui.widgets.eyecandy_texture import (
14 EyeCandyTexture)14 EyeCandyTexture)
15from entertainerlib.frontend.gui.widgets.label import Label15from entertainerlib.gui.widgets.label import Label
16from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea16from entertainerlib.gui.widgets.scroll_area import ScrollArea
17from entertainerlib.frontend.gui.widgets.texture import Texture17from entertainerlib.gui.widgets.texture import Texture
18from entertainerlib.frontend.gui.widgets.text_menu import TextMenu18from entertainerlib.gui.widgets.text_menu import TextMenu
19from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem19from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
2020
21class Movie(Screen):21class Movie(Screen):
22 '''Screen contains information of one movie.'''22 '''Screen contains information of one movie.'''
2323
=== modified file 'entertainerlib/gui/screens/music.py'
--- entertainerlib/frontend/gui/screens/music.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/music.py 2009-05-05 03:31:34 +0000
@@ -4,12 +4,12 @@
4__copyright__ = "2007, Lauri Taimila"4__copyright__ = "2007, Lauri Taimila"
5__author__ = "Lauri Taimila <lauri@taimila.com>"5__author__ = "Lauri Taimila <lauri@taimila.com>"
66
7from entertainerlib.frontend.gui.screens.screen import Screen7from entertainerlib.gui.screens.screen import Screen
8from entertainerlib.frontend.gui.tabs.albums_tab import AlbumsTab8from entertainerlib.gui.tabs.albums_tab import AlbumsTab
9from entertainerlib.frontend.gui.tabs.artists_tab import ArtistsTab9from entertainerlib.gui.tabs.artists_tab import ArtistsTab
10from entertainerlib.frontend.gui.widgets.label import Label10from entertainerlib.gui.widgets.label import Label
11from entertainerlib.frontend.gui.widgets.texture import Texture11from entertainerlib.gui.widgets.texture import Texture
12from entertainerlib.frontend.medialibrary.playlist import Playlist12from entertainerlib.client.medialibrary.playlist import Playlist
1313
14class Music(Screen):14class Music(Screen):
15 '''Screen that allows user to browse music library content.'''15 '''Screen that allows user to browse music library content.'''
1616
=== modified file 'entertainerlib/gui/screens/photo.py'
--- entertainerlib/frontend/gui/screens/photo.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/photo.py 2009-05-05 03:31:34 +0000
@@ -7,9 +7,9 @@
7import clutter7import clutter
8import gobject8import gobject
99
10from entertainerlib.frontend.gui.screens.screen import Screen10from entertainerlib.gui.screens.screen import Screen
11from entertainerlib.frontend.gui.widgets.label import Label11from entertainerlib.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.texture import Texture12from entertainerlib.gui.widgets.texture import Texture
1313
14class Photo(Screen):14class Photo(Screen):
15 '''Screen displays photograph in fullscreen and allows user to zoom in.'''15 '''Screen displays photograph in fullscreen and allows user to zoom in.'''
1616
=== modified file 'entertainerlib/gui/screens/photo_albums.py'
--- entertainerlib/frontend/gui/screens/photo_albums.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/photo_albums.py 2009-05-05 03:31:34 +0000
@@ -9,14 +9,14 @@
9import gobject9import gobject
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 (
14 EyeCandyTexture)14 EyeCandyTexture)
15from entertainerlib.frontend.gui.widgets.label import Label15from entertainerlib.gui.widgets.label import Label
16from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator16from entertainerlib.gui.widgets.list_indicator import ListIndicator
17from entertainerlib.frontend.gui.widgets.texture import Texture17from entertainerlib.gui.widgets.texture import Texture
18from entertainerlib.frontend.gui.widgets.text_menu import TextMenu18from entertainerlib.gui.widgets.text_menu import TextMenu
19from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem19from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
2020
21class PhotoAlbums(Screen):21class PhotoAlbums(Screen):
22 '''Screen contains a list of photo albums and album previews.'''22 '''Screen contains a list of photo albums and album previews.'''
2323
=== modified file 'entertainerlib/gui/screens/photographs.py'
--- entertainerlib/frontend/gui/screens/photographs.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/photographs.py 2009-05-05 03:31:34 +0000
@@ -8,14 +8,14 @@
8import clutter8import clutter
9import gobject9import gobject
1010
11from entertainerlib.frontend.gui.screens.screen import Screen11from entertainerlib.gui.screens.screen import Screen
12from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu12from entertainerlib.gui.widgets.image_menu import ImageMenu
13from entertainerlib.frontend.gui.widgets.label import Label13from entertainerlib.gui.widgets.label import Label
14from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator14from entertainerlib.gui.widgets.list_indicator import ListIndicator
15from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem15from entertainerlib.gui.widgets.image_menu_item import ImageMenuItem
16from entertainerlib.frontend.gui.widgets.loading_animation import (16from entertainerlib.gui.widgets.loading_animation import (
17 LoadingAnimation)17 LoadingAnimation)
18from entertainerlib.frontend.gui.widgets.texture import Texture18from entertainerlib.gui.widgets.texture import Texture
1919
20class Photographs(Screen):20class Photographs(Screen):
21 '''Screen displays a grid of selectable photograph thumbnails.'''21 '''Screen displays a grid of selectable photograph thumbnails.'''
2222
=== modified file 'entertainerlib/gui/screens/question.py'
--- entertainerlib/frontend/gui/screens/question.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/question.py 2009-05-05 03:31:34 +0000
@@ -4,11 +4,11 @@
4__copyright__ = "2008, Teje Rodgers"4__copyright__ = "2008, Teje Rodgers"
5__author__ = "Teje Rodgers <contact@tejerodgers.com>"5__author__ = "Teje Rodgers <contact@tejerodgers.com>"
66
7from entertainerlib.frontend.gui.screens.screen import Screen7from entertainerlib.gui.screens.screen import Screen
8from entertainerlib.frontend.gui.transitions.transition import Transition8from entertainerlib.gui.transitions.transition import Transition
9from entertainerlib.frontend.gui.widgets.label import Label9from entertainerlib.gui.widgets.label import Label
10from entertainerlib.frontend.gui.widgets.text_menu import TextMenu10from entertainerlib.gui.widgets.text_menu import TextMenu
11from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem11from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
1212
13class Question(Screen):13class Question(Screen):
14 '''Screen is displayed when the application needs to ask a close ended14 '''Screen is displayed when the application needs to ask a close ended
1515
=== modified file 'entertainerlib/gui/screens/rss.py'
--- entertainerlib/frontend/gui/screens/rss.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/rss.py 2009-05-06 04:00:53 +0000
@@ -6,16 +6,16 @@
66
7import pango7import pango
88
9from entertainerlib.frontend.gui.screens.screen import Screen9from entertainerlib.gui.screens.screen import Screen
10from entertainerlib.frontend.gui.widgets.label import Label10from entertainerlib.gui.widgets.label import Label
11from entertainerlib.frontend.gui.widgets.menu import Menu11from entertainerlib.gui.widgets.menu import Menu
12from entertainerlib.frontend.gui.widgets.selector import Selector12from entertainerlib.gui.widgets.selector import Selector
13from entertainerlib.frontend.gui.widgets.texture import Texture13from entertainerlib.gui.widgets.texture import Texture
14from entertainerlib.frontend.gui.widgets.text_menu import TextMenu14from entertainerlib.gui.widgets.text_menu import TextMenu
15from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem15from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
16from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator16from entertainerlib.gui.widgets.list_indicator import ListIndicator
17from entertainerlib.frontend.medialibrary.feeds import Feed17from entertainerlib.client.medialibrary.feeds import Feed
18from entertainerlib.utils.feed_utils import FeedEntryParser18from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
1919
20class Rss(Screen):20class Rss(Screen):
21 '''Screen displays RSS-feed titles and number of entries.'''21 '''Screen displays RSS-feed titles and number of entries.'''
2222
=== modified file 'entertainerlib/gui/screens/screen.py'
--- entertainerlib/frontend/gui/screens/screen.py 2009-04-30 01:16:33 +0000
+++ entertainerlib/gui/screens/screen.py 2009-05-05 03:31:34 +0000
@@ -9,9 +9,9 @@
9import clutter9import clutter
1010
11from entertainerlib.exceptions import ScreenException11from entertainerlib.exceptions import ScreenException
12from entertainerlib.frontend.gui.user_event import UserEvent12from entertainerlib.gui.user_event import UserEvent
13from entertainerlib.frontend.gui.widgets.base import Base13from entertainerlib.gui.widgets.base import Base
14from entertainerlib.frontend.gui.widgets.tab_group import TabGroup14from entertainerlib.gui.widgets.tab_group import TabGroup
1515
16class Screen(Base, clutter.Group):16class Screen(Base, clutter.Group):
17 """17 """
1818
=== modified file 'entertainerlib/gui/screens/tv_episodes.py'
--- entertainerlib/frontend/gui/screens/tv_episodes.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/tv_episodes.py 2009-05-05 03:31:34 +0000
@@ -7,15 +7,15 @@
7import gtk7import gtk
8import pango8import pango
99
10from entertainerlib.frontend.gui.screens.screen import Screen10from entertainerlib.gui.screens.screen import Screen
11from entertainerlib.frontend.gui.user_event import UserEvent11from entertainerlib.gui.user_event import UserEvent
12from entertainerlib.frontend.gui.widgets.eyecandy_texture import (12from entertainerlib.gui.widgets.eyecandy_texture import (
13 EyeCandyTexture)13 EyeCandyTexture)
14from entertainerlib.frontend.gui.widgets.label import Label14from entertainerlib.gui.widgets.label import Label
15from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator15from entertainerlib.gui.widgets.list_indicator import ListIndicator
16from entertainerlib.frontend.gui.widgets.text_menu import TextMenu16from entertainerlib.gui.widgets.text_menu import TextMenu
17from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea17from entertainerlib.gui.widgets.scroll_area import ScrollArea
18from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem18from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
1919
20class TvEpisodes(Screen):20class TvEpisodes(Screen):
21 '''Screen contains list of all episodes of one specific season.'''21 '''Screen contains list of all episodes of one specific season.'''
2222
=== modified file 'entertainerlib/gui/screens/tv_series.py'
--- entertainerlib/frontend/gui/screens/tv_series.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/tv_series.py 2009-05-05 03:31:34 +0000
@@ -6,13 +6,13 @@
66
7import gtk7import gtk
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 (
11 EyeCandyTexture)11 EyeCandyTexture)
12from entertainerlib.frontend.gui.widgets.label import Label12from entertainerlib.gui.widgets.label import Label
13from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator13from entertainerlib.gui.widgets.list_indicator import ListIndicator
14from entertainerlib.frontend.gui.widgets.text_menu import TextMenu14from entertainerlib.gui.widgets.text_menu import TextMenu
15from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem15from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
1616
17class TvSeries(Screen):17class TvSeries(Screen):
18 '''Screen that contains all seasons of one TV series.'''18 '''Screen that contains all seasons of one TV series.'''
1919
=== modified file 'entertainerlib/gui/screens/video.py'
--- entertainerlib/frontend/gui/screens/video.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/video.py 2009-05-05 03:31:34 +0000
@@ -4,11 +4,11 @@
4__copyright__ = "2008, Lauri Taimila"4__copyright__ = "2008, Lauri Taimila"
5__author__ = "Lauri Taimila <lauri@taimila.com>"5__author__ = "Lauri Taimila <lauri@taimila.com>"
66
7from entertainerlib.frontend.gui.screens.screen import Screen7from entertainerlib.gui.screens.screen import Screen
8from entertainerlib.frontend.gui.tabs.movies_tab import MoviesTab8from entertainerlib.gui.tabs.movies_tab import MoviesTab
9from entertainerlib.frontend.gui.tabs.series_tab import SeriesTab9from entertainerlib.gui.tabs.series_tab import SeriesTab
10from entertainerlib.frontend.gui.tabs.video_clips_tab import VideoClipsTab10from entertainerlib.gui.tabs.video_clips_tab import VideoClipsTab
11from entertainerlib.frontend.gui.widgets.label import Label11from entertainerlib.gui.widgets.label import Label
1212
13class Video(Screen):13class Video(Screen):
14 '''Screen contains tabs for different video types in the video library.'''14 '''Screen contains tabs for different video types in the video library.'''
1515
=== modified file 'entertainerlib/gui/screens/video_osd.py'
--- entertainerlib/frontend/gui/screens/video_osd.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/video_osd.py 2009-05-05 03:31:34 +0000
@@ -7,11 +7,11 @@
7import gobject7import gobject
8import clutter8import clutter
99
10from entertainerlib.frontend.gui.screens.screen import Screen10from entertainerlib.gui.screens.screen import Screen
11from entertainerlib.frontend.gui.user_event import UserEvent11from entertainerlib.gui.user_event import UserEvent
12from entertainerlib.frontend.gui.widgets.label import Label12from entertainerlib.gui.widgets.label import Label
13from entertainerlib.frontend.gui.widgets.progress_bar import ProgressBar13from entertainerlib.gui.widgets.progress_bar import ProgressBar
14from entertainerlib.frontend.gui.widgets.texture import Texture14from entertainerlib.gui.widgets.texture import Texture
1515
16class VideoOSD(Screen):16class VideoOSD(Screen):
17 '''Screen is displayed when video is being watched.17 '''Screen is displayed when video is being watched.
1818
=== modified file 'entertainerlib/gui/screens/weather.py'
--- entertainerlib/frontend/gui/screens/weather.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/screens/weather.py 2009-05-06 05:10:14 +0000
@@ -4,10 +4,10 @@
4__copyright__ = "2008, Jamie Bennett"4__copyright__ = "2008, Jamie Bennett"
5__author__ = "Jamie Bennett <jamie@linuxuk.org>"5__author__ = "Jamie Bennett <jamie@linuxuk.org>"
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
10from entertainerlib.utils.weather import Weather10from entertainerlib.weather import Weather
1111
12class WeatherScreen(Screen):12class WeatherScreen(Screen):
13 '''Screen to display the user's set weather location.'''13 '''Screen to display the user's set weather location.'''
1414
=== renamed file 'entertainerlib/utils/system_tray_icon.py' => 'entertainerlib/gui/system_tray_icon.py'
--- entertainerlib/utils/system_tray_icon.py 2009-02-07 00:28:11 +0000
+++ entertainerlib/gui/system_tray_icon.py 2009-05-06 03:08:19 +0000
@@ -9,11 +9,10 @@
9import gtk9import gtk
10import gtk.glade10import gtk.glade
1111
12from entertainerlib.utils.log_viewer import LogViewer12from entertainerlib.configuration import Configuration
13from entertainerlib.utils.preferences_dialog import PreferencesDialog13from entertainerlib.dialog import (ContentManagementDialog, LogViewer,
14from entertainerlib.utils.content_management_dialog import (14 PreferencesDialog)
15 ContentManagementDialog)15
16from entertainerlib.utils.configuration import Configuration
1716
18class SystemTrayIcon:17class SystemTrayIcon:
19 """Implements system tray icon for entertainer."""18 """Implements system tray icon for entertainer."""
@@ -43,8 +42,8 @@
43 os.path.join(self.GLADE_DIR, "system_tray_icon_menu.glade"))42 os.path.join(self.GLADE_DIR, "system_tray_icon_menu.glade"))
4443
45 # Bind menu signals44 # Bind menu signals
46 callback_dic = {"on_menuitem_frontend_activate"45 callback_dic = {"on_menuitem_client_activate"
47 : self.on_menuitem_frontend_activate,46 : self.on_menuitem_client_activate,
48 "on_menuitem_preferences_activate"47 "on_menuitem_preferences_activate"
49 : self.on_menuitem_preferences_activate,48 : self.on_menuitem_preferences_activate,
50 "on_menuitem_content_management_activate"49 "on_menuitem_content_management_activate"
@@ -68,7 +67,7 @@
68 self.icon_widget.connect('popup-menu', self.open_popup_menu)67 self.icon_widget.connect('popup-menu', self.open_popup_menu)
6968
70 def systray_icon_activated(self, widget, data= None):69 def systray_icon_activated(self, widget, data= None):
71 """Switch visibility of frontend when system tray icon is clicked"""70 """Switch visibility of client when system tray icon is clicked"""
72 self.toggle_interface_visibility_callback()71 self.toggle_interface_visibility_callback()
7372
74 def open_popup_menu(self, widget, button, time, data = None):73 def open_popup_menu(self, widget, button, time, data = None):
@@ -76,9 +75,9 @@
76 self.popup.show_all()75 self.popup.show_all()
77 self.popup.popup(None, None, None, 3, time)76 self.popup.popup(None, None, None, 3, time)
7877
79 def on_menuitem_frontend_activate(self, widget):78 def on_menuitem_client_activate(self, widget):
80 """Execute frontend here if not running. Show if running"""79 """Execute client here if not running. Show if running"""
81 self.set_frontend_visible(True)80 self.set_client_visible(True)
8281
83 def on_menuitem_preferences_activate(self, widget):82 def on_menuitem_preferences_activate(self, widget):
84 """Executes preferences-tool."""83 """Executes preferences-tool."""
8584
=== modified file 'entertainerlib/gui/tabs/albums_tab.py'
--- entertainerlib/frontend/gui/tabs/albums_tab.py 2009-02-03 23:37:57 +0000
+++ entertainerlib/gui/tabs/albums_tab.py 2009-05-05 03:31:34 +0000
@@ -9,14 +9,14 @@
9import gobject9import gobject
10import pango10import pango
1111
12from entertainerlib.frontend.gui.tabs.tab import Tab12from entertainerlib.gui.tabs.tab import Tab
13from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu13from entertainerlib.gui.widgets.image_menu import ImageMenu
14from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem14from entertainerlib.gui.widgets.image_menu_item import ImageMenuItem
15from entertainerlib.frontend.gui.widgets.label import Label15from entertainerlib.gui.widgets.label import Label
16from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator16from entertainerlib.gui.widgets.list_indicator import ListIndicator
17from entertainerlib.frontend.gui.widgets.loading_animation import (17from entertainerlib.gui.widgets.loading_animation import (
18 LoadingAnimation)18 LoadingAnimation)
19from entertainerlib.frontend.gui.widgets.texture import Texture19from entertainerlib.gui.widgets.texture import Texture
2020
21class AlbumsTab(Tab):21class AlbumsTab(Tab):
22 '''Tab to show album listings'''22 '''Tab to show album listings'''
2323
=== modified file 'entertainerlib/gui/tabs/artists_tab.py'
--- entertainerlib/frontend/gui/tabs/artists_tab.py 2009-02-03 23:37:57 +0000
+++ entertainerlib/gui/tabs/artists_tab.py 2009-05-05 03:31:34 +0000
@@ -9,13 +9,13 @@
9import gobject9import gobject
10import pango10import pango
1111
12from entertainerlib.frontend.gui.tabs.tab import Tab12from entertainerlib.gui.tabs.tab import Tab
13from entertainerlib.frontend.gui.widgets.label import Label13from entertainerlib.gui.widgets.label import Label
14from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator14from entertainerlib.gui.widgets.list_indicator import ListIndicator
15from entertainerlib.frontend.gui.widgets.loading_animation import (15from entertainerlib.gui.widgets.loading_animation import (
16 LoadingAnimation)16 LoadingAnimation)
17from entertainerlib.frontend.gui.widgets.text_menu import TextMenu17from entertainerlib.gui.widgets.text_menu import TextMenu
18from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem18from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
1919
20class ArtistsTab(Tab):20class ArtistsTab(Tab):
21 '''Tab for the music screen to show artist listings'''21 '''Tab for the music screen to show artist listings'''
2222
=== modified file 'entertainerlib/gui/tabs/lyrics_tab.py'
--- entertainerlib/frontend/gui/tabs/lyrics_tab.py 2009-02-03 23:37:57 +0000
+++ entertainerlib/gui/tabs/lyrics_tab.py 2009-05-05 03:31:34 +0000
@@ -7,11 +7,11 @@
7import pango7import pango
8import clutter8import clutter
99
10from entertainerlib.frontend.gui.tabs.tab import Tab10from entertainerlib.gui.tabs.tab import Tab
11from entertainerlib.frontend.gui.widgets.label import Label11from entertainerlib.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.loading_animation import (12from entertainerlib.gui.widgets.loading_animation import (
13 LoadingAnimation)13 LoadingAnimation)
14from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea14from entertainerlib.gui.widgets.scroll_area import ScrollArea
1515
16class LyricsTab(Tab):16class LyricsTab(Tab):
17 '''Tab for the audio play screen to show lyrics'''17 '''Tab for the audio play screen to show lyrics'''
1818
=== modified file 'entertainerlib/gui/tabs/movies_tab.py'
--- entertainerlib/frontend/gui/tabs/movies_tab.py 2009-02-03 23:37:57 +0000
+++ entertainerlib/gui/tabs/movies_tab.py 2009-05-05 03:31:34 +0000
@@ -8,13 +8,13 @@
8import gtk8import gtk
9import pango9import pango
1010
11from entertainerlib.frontend.gui.tabs.tab import Tab11from entertainerlib.gui.tabs.tab import Tab
12from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu12from entertainerlib.gui.widgets.image_menu import ImageMenu
13from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem13from entertainerlib.gui.widgets.image_menu_item import ImageMenuItem
14from entertainerlib.frontend.gui.widgets.label import Label14from entertainerlib.gui.widgets.label import Label
15from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator15from entertainerlib.gui.widgets.list_indicator import ListIndicator
16from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture16from entertainerlib.gui.widgets.rounded_texture import RoundedTexture
17from entertainerlib.frontend.gui.widgets.texture import Texture17from entertainerlib.gui.widgets.texture import Texture
1818
19class MoviesTab(Tab):19class MoviesTab(Tab):
20 """20 """
2121
=== modified file 'entertainerlib/gui/tabs/playing_tab.py'
--- entertainerlib/frontend/gui/tabs/playing_tab.py 2009-02-06 08:33:50 +0000
+++ entertainerlib/gui/tabs/playing_tab.py 2009-05-05 03:31:34 +0000
@@ -6,9 +6,9 @@
66
7import pango7import pango
88
9from entertainerlib.frontend.gui.tabs.tab import Tab9from entertainerlib.gui.tabs.tab import Tab
10from entertainerlib.frontend.gui.widgets.label import Label10from entertainerlib.gui.widgets.label import Label
11from entertainerlib.frontend.gui.widgets.progress_bar import ProgressBar11from entertainerlib.gui.widgets.progress_bar import ProgressBar
1212
13class PlayingTab(Tab):13class PlayingTab(Tab):
14 '''Tab for the audio play screen to show currently playing audio'''14 '''Tab for the audio play screen to show currently playing audio'''
1515
=== modified file 'entertainerlib/gui/tabs/series_tab.py'
--- entertainerlib/frontend/gui/tabs/series_tab.py 2009-02-03 23:37:57 +0000
+++ entertainerlib/gui/tabs/series_tab.py 2009-05-05 03:31:34 +0000
@@ -8,13 +8,13 @@
8import gtk8import gtk
9import pango9import pango
1010
11from entertainerlib.frontend.gui.tabs.tab import Tab11from entertainerlib.gui.tabs.tab import Tab
12from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu12from entertainerlib.gui.widgets.image_menu import ImageMenu
13from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem13from entertainerlib.gui.widgets.image_menu_item import ImageMenuItem
14from entertainerlib.frontend.gui.widgets.label import Label14from entertainerlib.gui.widgets.label import Label
15from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator15from entertainerlib.gui.widgets.list_indicator import ListIndicator
16from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture16from entertainerlib.gui.widgets.rounded_texture import RoundedTexture
17from entertainerlib.frontend.gui.widgets.texture import Texture17from entertainerlib.gui.widgets.texture import Texture
1818
19class SeriesTab(Tab):19class SeriesTab(Tab):
20 """20 """
2121
=== modified file 'entertainerlib/gui/tabs/tab.py'
--- entertainerlib/frontend/gui/tabs/tab.py 2009-02-28 17:22:46 +0000
+++ entertainerlib/gui/tabs/tab.py 2009-05-05 03:31:34 +0000
@@ -6,10 +6,10 @@
66
7import clutter7import clutter
88
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.label import Label11from entertainerlib.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.texture import Texture12from entertainerlib.gui.widgets.texture import Texture
1313
14class Tab(Base, clutter.Group):14class Tab(Base, clutter.Group):
15 """15 """
1616
=== modified file 'entertainerlib/gui/tabs/tracks_tab.py'
--- entertainerlib/frontend/gui/tabs/tracks_tab.py 2009-02-03 23:37:57 +0000
+++ entertainerlib/gui/tabs/tracks_tab.py 2009-05-05 03:31:34 +0000
@@ -7,11 +7,11 @@
7import clutter7import clutter
8import pango8import pango
99
10from entertainerlib.frontend.gui.tabs.tab import Tab10from entertainerlib.gui.tabs.tab import Tab
11from entertainerlib.frontend.gui.widgets.label import Label11from entertainerlib.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator12from entertainerlib.gui.widgets.list_indicator import ListIndicator
13from entertainerlib.frontend.gui.widgets.text_menu import TextMenu13from entertainerlib.gui.widgets.text_menu import TextMenu
14from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem14from entertainerlib.gui.widgets.text_menu_item import TextMenuItem
1515
16class TracksTab(Tab):16class TracksTab(Tab):
17 '''Tab for the artist screen to show track listings'''17 '''Tab for the artist screen to show track listings'''
1818
=== modified file 'entertainerlib/gui/tabs/video_clips_tab.py'
--- entertainerlib/frontend/gui/tabs/video_clips_tab.py 2009-02-03 23:37:57 +0000
+++ entertainerlib/gui/tabs/video_clips_tab.py 2009-05-05 03:31:34 +0000
@@ -9,12 +9,12 @@
9import gtk9import gtk
10import pango10import pango
1111
12from entertainerlib.frontend.gui.tabs.tab import Tab12from entertainerlib.gui.tabs.tab import Tab
13from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu13from entertainerlib.gui.widgets.image_menu import ImageMenu
14from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem14from entertainerlib.gui.widgets.image_menu_item import ImageMenuItem
15from entertainerlib.frontend.gui.widgets.label import Label15from entertainerlib.gui.widgets.label import Label
16from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator16from entertainerlib.gui.widgets.list_indicator import ListIndicator
17from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture17from entertainerlib.gui.widgets.rounded_texture import RoundedTexture
1818
19class VideoClipsTab(Tab):19class VideoClipsTab(Tab):
20 """20 """
2121
=== renamed file 'entertainerlib/utils/theme.py' => 'entertainerlib/gui/theme.py'
=== modified file 'entertainerlib/gui/transitions/factory.py'
--- entertainerlib/frontend/gui/transitions/factory.py 2009-02-07 17:12:21 +0000
+++ entertainerlib/gui/transitions/factory.py 2009-05-05 04:11:48 +0000
@@ -5,11 +5,11 @@
5__copyright__ = '2009, Matt Layman'5__copyright__ = '2009, Matt Layman'
6__author__ = 'Matt Layman <laymansterms.dev@gmail.com>'6__author__ = 'Matt Layman <laymansterms.dev@gmail.com>'
77
8from entertainerlib.frontend.gui.transitions.fade import Fade8from entertainerlib.gui.transitions.fade import Fade
9from entertainerlib.frontend.gui.transitions.no_effect import NoEffect9from entertainerlib.gui.transitions.no_effect import NoEffect
10from entertainerlib.frontend.gui.transitions.slide import Slide10from entertainerlib.gui.transitions.slide import Slide
11from entertainerlib.frontend.gui.transitions.zoom_and_fade import ZoomAndFade11from entertainerlib.gui.transitions.zoom_and_fade import ZoomAndFade
12from entertainerlib.utils.configuration import Configuration12from entertainerlib.configuration import Configuration
1313
14class TransitionFactory:14class TransitionFactory:
15 '''Generates a transition object based on the configuration setting'''15 '''Generates a transition object based on the configuration setting'''
1616
=== modified file 'entertainerlib/gui/transitions/fade.py'
--- entertainerlib/frontend/gui/transitions/fade.py 2009-01-26 23:11:40 +0000
+++ entertainerlib/gui/transitions/fade.py 2009-05-05 03:31:34 +0000
@@ -5,7 +5,7 @@
5__author__ = "Lauri Taimila <lauri@taimila.com>"5__author__ = "Lauri Taimila <lauri@taimila.com>"
66
7import clutter7import clutter
8from entertainerlib.frontend.gui.transitions.transition import Transition8from entertainerlib.gui.transitions.transition import Transition
99
10class Fade(Transition):10class Fade(Transition):
11 """11 """
1212
=== modified file 'entertainerlib/gui/transitions/no_effect.py'
--- entertainerlib/frontend/gui/transitions/no_effect.py 2009-01-26 23:11:40 +0000
+++ entertainerlib/gui/transitions/no_effect.py 2009-05-05 03:31:34 +0000
@@ -4,7 +4,7 @@
4__copyright__ = "2007, Lauri Taimila"4__copyright__ = "2007, Lauri Taimila"
5__author__ = "Lauri Taimila <lauri@taimila.com>"5__author__ = "Lauri Taimila <lauri@taimila.com>"
66
7from entertainerlib.frontend.gui.transitions.transition import Transition7from entertainerlib.gui.transitions.transition import Transition
88
9class NoEffect(Transition):9class NoEffect(Transition):
10 """10 """
1111
=== modified file 'entertainerlib/gui/transitions/slide.py'
--- entertainerlib/frontend/gui/transitions/slide.py 2009-04-27 02:33:38 +0000
+++ entertainerlib/gui/transitions/slide.py 2009-05-05 04:11:48 +0000
@@ -6,8 +6,8 @@
66
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.transitions.transition import Transition9from entertainerlib.gui.transitions.transition import Transition
10from entertainerlib.utils.configuration import Configuration10from entertainerlib.configuration import Configuration
1111
12class Slide(Transition):12class Slide(Transition):
13 '''Move screens on and off the stage through horizontal movements.'''13 '''Move screens on and off the stage through horizontal movements.'''
1414
=== modified file 'entertainerlib/gui/transitions/zoom_and_fade.py'
--- entertainerlib/frontend/gui/transitions/zoom_and_fade.py 2009-01-26 23:11:40 +0000
+++ entertainerlib/gui/transitions/zoom_and_fade.py 2009-05-05 03:31:34 +0000
@@ -5,7 +5,7 @@
5__author__ = "Lauri Taimila <lauri@taimila.com>"5__author__ = "Lauri Taimila <lauri@taimila.com>"
66
7import clutter7import clutter
8from entertainerlib.frontend.gui.transitions.transition import Transition8from entertainerlib.gui.transitions.transition import Transition
99
10class ZoomAndFade(Transition):10class ZoomAndFade(Transition):
11 """11 """
1212
=== modified file 'entertainerlib/gui/user_interface.py'
--- entertainerlib/frontend/gui/user_interface.py 2009-04-27 03:32:30 +0000
+++ entertainerlib/gui/user_interface.py 2009-05-05 04:24:32 +0000
@@ -1,4 +1,4 @@
1'''UserInterface - Main window of the Entertainer frontend'''1'''UserInterface - Main window of the Entertainer client'''
22
3# Clutter uses _1 to represent the 1 key and pylint complains about it3# Clutter uses _1 to represent the 1 key and pylint complains about it
4# pylint: disable-msg=W02124# pylint: disable-msg=W0212
@@ -16,30 +16,30 @@
16import gobject16import gobject
17import gtk17import gtk
1818
19from entertainerlib.frontend.gui.screen_history import ScreenHistory19from entertainerlib.gui.screen_history import ScreenHistory
20from entertainerlib.frontend.gui.screens.factory import ScreenFactory20from entertainerlib.gui.screens.factory import ScreenFactory
21from entertainerlib.frontend.gui.screens.screen import Screen21from entertainerlib.gui.screens.screen import Screen
22from entertainerlib.frontend.gui.transitions.factory import TransitionFactory22from entertainerlib.gui.transitions.factory import TransitionFactory
23from entertainerlib.frontend.gui.transitions.transition import Transition23from entertainerlib.gui.transitions.transition import Transition
24from entertainerlib.frontend.gui.user_event import UserEvent24from entertainerlib.gui.user_event import UserEvent
25from entertainerlib.frontend.gui.widgets.menu_overlay import MenuOverlay25from entertainerlib.gui.widgets.menu_overlay import MenuOverlay
26from entertainerlib.frontend.media_player import MediaPlayer26from entertainerlib.client.media_player import MediaPlayer
27from entertainerlib.utils.configuration import Configuration27from entertainerlib.configuration import Configuration
28from entertainerlib.utils.logger import Logger28from entertainerlib.logger import Logger
2929
30class UserInterface:30class UserInterface:
31 '''A main GUI window of the Entertainer client.'''31 '''A main GUI window of the Entertainer client.'''
3232
33 def __init__(self, feed_library, image_library, music_library,33 def __init__(self, feed_library, image_library, music_library,
34 video_library, quit_frontend_callback):34 video_library, quit_client_callback):
35 self.quit_frontend_callback = quit_frontend_callback35 self.quit_client_callback = quit_client_callback
36 self.config = Configuration()36 self.config = Configuration()
3737
38 # Store the dimensions in case users want to return to window mode38 # Store the dimensions in case users want to return to window mode
39 self.old_width = self.config.get_stage_width()39 self.old_width = self.config.get_stage_width()
40 self.old_height = self.config.get_stage_height()40 self.old_height = self.config.get_stage_height()
4141
42 self.logger = Logger().getLogger('frontend.gui.UserInterface')42 self.logger = Logger().getLogger('client.gui.UserInterface')
4343
44 self.window = gtk.Window()44 self.window = gtk.Window()
45 self.window.connect('destroy', self.destroy_callback)45 self.window.connect('destroy', self.destroy_callback)
@@ -53,7 +53,7 @@
53 except gobject.GError:53 except gobject.GError:
54 # Must not be installed from a package, get icon from the branch54 # Must not be installed from a package, get icon from the branch
55 file_dir = os.path.dirname(__file__)55 file_dir = os.path.dirname(__file__)
56 icon_path = os.path.join(file_dir, '..', '..', '..', 'icons',56 icon_path = os.path.join(file_dir, '..', '..', 'icons',
57 'hicolor', '48x48', 'apps', 'entertainer.png')57 'hicolor', '48x48', 'apps', 'entertainer.png')
58 icon = gtk.gdk.pixbuf_new_from_file(icon_path)58 icon = gtk.gdk.pixbuf_new_from_file(icon_path)
59 self.window.set_icon(icon)59 self.window.set_icon(icon)
@@ -170,7 +170,7 @@
170 UserEvent.PLAYER_SKIP_FORWARD : self._handle_player_skip_forward,170 UserEvent.PLAYER_SKIP_FORWARD : self._handle_player_skip_forward,
171 UserEvent.PLAYER_PREVIOUS : self._handle_player_previous,171 UserEvent.PLAYER_PREVIOUS : self._handle_player_previous,
172 UserEvent.PLAYER_NEXT : self._handle_player_next,172 UserEvent.PLAYER_NEXT : self._handle_player_next,
173 UserEvent.QUIT_FRONTEND : self._handle_quit_frontend173 UserEvent.QUIT_FRONTEND : self._handle_quit_client
174 }174 }
175175
176 self.logger.debug("Frontend GUI initialized succesfully")176 self.logger.debug("Frontend GUI initialized succesfully")
@@ -210,7 +210,7 @@
210210
211 def shutdown(self):211 def shutdown(self):
212 '''Shut down the user interface.'''212 '''Shut down the user interface.'''
213 self.quit_frontend_callback()213 self.quit_client_callback()
214214
215 def _toggle_fullscreen(self):215 def _toggle_fullscreen(self):
216 '''Set the User Interface to fullscreen mode or back to window mode.'''216 '''Set the User Interface to fullscreen mode or back to window mode.'''
@@ -384,7 +384,7 @@
384 '''Handle UserEvent.TOGGLE_FULLSCREEN.'''384 '''Handle UserEvent.TOGGLE_FULLSCREEN.'''
385 self._toggle_fullscreen()385 self._toggle_fullscreen()
386386
387 def _handle_quit_frontend(self, event):387 def _handle_quit_client(self, event):
388 '''Handle UserEvent.QUIT_FRONTEND.'''388 '''Handle UserEvent.QUIT_FRONTEND.'''
389 self.confirm_exit()389 self.confirm_exit()
390390
391391
=== modified file 'entertainerlib/gui/widgets/base.py'
--- entertainerlib/frontend/gui/widgets/base.py 2009-03-08 03:16:25 +0000
+++ entertainerlib/gui/widgets/base.py 2009-05-05 04:11:48 +0000
@@ -4,7 +4,7 @@
4__copyright__ = "2008, Matt Layman"4__copyright__ = "2008, Matt Layman"
5__author__ = "Matt Layman <laymansterm.dev@gmail.com>"5__author__ = "Matt Layman <laymansterm.dev@gmail.com>"
66
7from entertainerlib.utils.configuration import Configuration7from entertainerlib.configuration import Configuration
88
9class Base(object):9class Base(object):
10 """Inherited class that allows for transforms from relative to absolute for10 """Inherited class that allows for transforms from relative to absolute for
1111
=== modified file 'entertainerlib/gui/widgets/clock_label.py'
--- entertainerlib/frontend/gui/widgets/clock_label.py 2008-11-29 22:44:32 +0000
+++ entertainerlib/gui/widgets/clock_label.py 2009-05-05 03:31:34 +0000
@@ -8,7 +8,7 @@
8import time8import time
9import gobject9import gobject
1010
11from entertainerlib.frontend.gui.widgets.label import Label11from entertainerlib.gui.widgets.label import Label
1212
13class ClockLabel(Label):13class ClockLabel(Label):
14 """Label that displays current time."""14 """Label that displays current time."""
1515
=== modified file 'entertainerlib/gui/widgets/eyecandy_texture.py'
--- entertainerlib/frontend/gui/widgets/eyecandy_texture.py 2008-11-17 21:54:45 +0000
+++ entertainerlib/gui/widgets/eyecandy_texture.py 2009-05-05 03:31:34 +0000
@@ -5,8 +5,8 @@
5__author__ = "Lauri Taimila <lauri@taimila.com>"5__author__ = "Lauri Taimila <lauri@taimila.com>"
66
7import clutter7import clutter
8from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture8from entertainerlib.gui.widgets.rounded_texture import RoundedTexture
9from entertainerlib.frontend.gui.widgets.reflection_texture import (9from entertainerlib.gui.widgets.reflection_texture import (
10 ReflectionTexture)10 ReflectionTexture)
1111
12class EyeCandyTexture(clutter.Group):12class EyeCandyTexture(clutter.Group):
1313
=== modified file 'entertainerlib/gui/widgets/image_menu.py'
--- entertainerlib/frontend/gui/widgets/image_menu.py 2009-02-08 07:43:25 +0000
+++ entertainerlib/gui/widgets/image_menu.py 2009-05-05 04:11:48 +0000
@@ -6,8 +6,8 @@
66
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.widgets.grid_menu import GridMenu9from entertainerlib.gui.widgets.grid_menu import GridMenu
10from entertainerlib.utils.configuration import Configuration10from entertainerlib.configuration import Configuration
1111
12class ImageMenu(GridMenu):12class ImageMenu(GridMenu):
13 """13 """
1414
=== modified file 'entertainerlib/gui/widgets/image_menu_item.py'
--- entertainerlib/frontend/gui/widgets/image_menu_item.py 2009-01-11 00:25:55 +0000
+++ entertainerlib/gui/widgets/image_menu_item.py 2009-05-05 03:31:34 +0000
@@ -5,7 +5,7 @@
5__author__ = ("Lauri Taimila <lauri@taimila.com>",5__author__ = ("Lauri Taimila <lauri@taimila.com>",
6 "Matt Layman <laymansterms.dev@gmail.com>")6 "Matt Layman <laymansterms.dev@gmail.com>")
77
8from entertainerlib.frontend.gui.widgets.menu_item import MenuItem8from entertainerlib.gui.widgets.menu_item import MenuItem
99
10class ImageMenuItem(MenuItem):10class ImageMenuItem(MenuItem):
11 """11 """
1212
=== modified file 'entertainerlib/gui/widgets/label.py'
--- entertainerlib/frontend/gui/widgets/label.py 2009-03-08 03:16:25 +0000
+++ entertainerlib/gui/widgets/label.py 2009-05-05 03:31:34 +0000
@@ -6,7 +6,7 @@
66
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.widgets.base import Base9from entertainerlib.gui.widgets.base import Base
1010
11class Label(Base, clutter.Label):11class Label(Base, clutter.Label):
12 """Wrapper of a clutter label to encapsulate some label settings and have12 """Wrapper of a clutter label to encapsulate some label settings and have
1313
=== modified file 'entertainerlib/gui/widgets/list_indicator.py'
--- entertainerlib/frontend/gui/widgets/list_indicator.py 2008-11-17 21:53:05 +0000
+++ entertainerlib/gui/widgets/list_indicator.py 2009-05-05 04:11:48 +0000
@@ -6,9 +6,9 @@
66
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.widgets.arrow_texture import ArrowTexture9from entertainerlib.gui.widgets.arrow_texture import ArrowTexture
10from entertainerlib.frontend.gui.widgets.label import Label10from entertainerlib.gui.widgets.label import Label
11from entertainerlib.utils.configuration import Configuration11from entertainerlib.configuration import Configuration
1212
13class ListIndicator(clutter.Group):13class ListIndicator(clutter.Group):
14 """14 """
1515
=== modified file 'entertainerlib/gui/widgets/menu_item.py'
--- entertainerlib/frontend/gui/widgets/menu_item.py 2009-01-11 00:25:55 +0000
+++ entertainerlib/gui/widgets/menu_item.py 2009-05-05 03:31:34 +0000
@@ -6,7 +6,7 @@
66
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.widgets.base import Base9from entertainerlib.gui.widgets.base import Base
1010
11class MenuItem(Base, clutter.Group):11class MenuItem(Base, clutter.Group):
12 """12 """
1313
=== modified file 'entertainerlib/gui/widgets/menu_overlay.py'
--- entertainerlib/frontend/gui/widgets/menu_overlay.py 2008-11-17 11:20:47 +0000
+++ entertainerlib/gui/widgets/menu_overlay.py 2009-05-05 03:31:34 +0000
@@ -6,7 +6,7 @@
66
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.widgets.texture import Texture9from entertainerlib.gui.widgets.texture import Texture
1010
11class MenuOverlay(Texture):11class MenuOverlay(Texture):
12 """12 """
1313
=== modified file 'entertainerlib/gui/widgets/scroll_area.py'
--- entertainerlib/frontend/gui/widgets/scroll_area.py 2009-03-08 03:16:25 +0000
+++ entertainerlib/gui/widgets/scroll_area.py 2009-05-05 03:31:34 +0000
@@ -6,8 +6,8 @@
66
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.widgets.base import Base9from entertainerlib.gui.widgets.base import Base
10from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator10from entertainerlib.gui.widgets.list_indicator import ListIndicator
1111
12class ScrollArea(Base, clutter.Group):12class ScrollArea(Base, clutter.Group):
13 """Wrapper of a clutter Group that allows for scrolling. ScrollArea13 """Wrapper of a clutter Group that allows for scrolling. ScrollArea
1414
=== modified file 'entertainerlib/gui/widgets/selector.py'
--- entertainerlib/frontend/gui/widgets/selector.py 2008-11-26 11:20:54 +0000
+++ entertainerlib/gui/widgets/selector.py 2009-05-05 03:31:34 +0000
@@ -6,7 +6,7 @@
66
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.widgets.texture import Texture9from entertainerlib.gui.widgets.texture import Texture
1010
11class Selector(clutter.Group):11class Selector(clutter.Group):
12 """12 """
1313
=== modified file 'entertainerlib/gui/widgets/tab_group.py'
--- entertainerlib/frontend/gui/widgets/tab_group.py 2009-02-27 00:01:53 +0000
+++ entertainerlib/gui/widgets/tab_group.py 2009-05-05 04:11:48 +0000
@@ -7,9 +7,9 @@
7import clutter7import clutter
8import pango8import pango
99
10from entertainerlib.frontend.gui.widgets.label import Label10from entertainerlib.gui.widgets.label import Label
11from entertainerlib.frontend.gui.user_event import UserEvent11from entertainerlib.gui.user_event import UserEvent
12from entertainerlib.utils.configuration import Configuration12from entertainerlib.configuration import Configuration
1313
14class TabGroup(clutter.Group):14class TabGroup(clutter.Group):
15 """15 """
1616
=== modified file 'entertainerlib/gui/widgets/text_menu.py'
--- entertainerlib/frontend/gui/widgets/text_menu.py 2009-02-08 07:43:25 +0000
+++ entertainerlib/gui/widgets/text_menu.py 2009-05-05 03:31:34 +0000
@@ -4,8 +4,8 @@
4__copyright__ = "2007, Lauri Taimila"4__copyright__ = "2007, Lauri Taimila"
5__author__ = "Lauri Taimila <lauri@taimila.com>"5__author__ = "Lauri Taimila <lauri@taimila.com>"
66
7from entertainerlib.frontend.gui.widgets.selector import Selector7from entertainerlib.gui.widgets.selector import Selector
8from entertainerlib.frontend.gui.widgets.grid_menu import GridMenu8from entertainerlib.gui.widgets.grid_menu import GridMenu
99
10class TextMenu(GridMenu):10class TextMenu(GridMenu):
11 """11 """
1212
=== modified file 'entertainerlib/gui/widgets/text_menu_item.py'
--- entertainerlib/frontend/gui/widgets/text_menu_item.py 2008-11-25 23:20:28 +0000
+++ entertainerlib/gui/widgets/text_menu_item.py 2009-05-05 03:31:34 +0000
@@ -7,8 +7,8 @@
7import pango7import pango
8import clutter8import clutter
99
10from entertainerlib.frontend.gui.widgets.label import Label10from entertainerlib.gui.widgets.label import Label
11from entertainerlib.frontend.gui.widgets.menu_item import MenuItem11from entertainerlib.gui.widgets.menu_item import MenuItem
1212
13class TextMenuItem(MenuItem):13class TextMenuItem(MenuItem):
14 """14 """
1515
=== modified file 'entertainerlib/gui/widgets/texture.py'
--- entertainerlib/frontend/gui/widgets/texture.py 2009-03-08 03:16:25 +0000
+++ entertainerlib/gui/widgets/texture.py 2009-05-05 04:24:32 +0000
@@ -6,8 +6,8 @@
66
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.widgets.base import Base9from entertainerlib.gui.widgets.base import Base
10from entertainerlib.utils.logger import Logger10from entertainerlib.logger import Logger
1111
12class Texture(Base, clutter.Texture):12class Texture(Base, clutter.Texture):
13 """Wrapper of a clutter texture to encapsulate position settings and have13 """Wrapper of a clutter texture to encapsulate position settings and have
@@ -19,7 +19,7 @@
1919
20 Base.__init__(self)20 Base.__init__(self)
21 clutter.Texture.__init__(self, filename)21 clutter.Texture.__init__(self, filename)
22 self.logger = Logger().getLogger('frontend.gui.widgets.Texture')22 self.logger = Logger().getLogger('client.gui.widgets.Texture')
2323
24 self._position = None24 self._position = None
25 self._set_position((x_pos_percent, y_pos_percent))25 self._set_position((x_pos_percent, y_pos_percent))
2626
=== renamed file 'entertainerlib/utils/logger.py' => 'entertainerlib/logger.py'
--- entertainerlib/utils/logger.py 2009-01-11 02:26:45 +0000
+++ entertainerlib/logger.py 2009-05-05 04:24:32 +0000
@@ -6,7 +6,7 @@
66
7import logging7import logging
88
9from entertainerlib.utils.configuration import Configuration9from entertainerlib.configuration import Configuration
1010
11class Logger:11class Logger:
12 '''Logger to record behind the scenes information.12 '''Logger to record behind the scenes information.
1313
=== modified file 'entertainerlib/network/__init__.py'
--- entertainerlib/network/__init__.py 2009-04-04 05:30:04 +0000
+++ entertainerlib/network/__init__.py 2009-04-30 01:11:22 +0000
@@ -26,7 +26,7 @@
26def client_main():26def client_main():
27 '''Entertainer test client code.27 '''Entertainer test client code.
2828
29 This code will go away when what is now the frontend is made into a true29 This code will go away when what is now the client is made into a true
30 client.30 client.
31 '''31 '''
32 startLogging(sys.stdout)32 startLogging(sys.stdout)
3333
=== modified file 'entertainerlib/tests/__init__.py'
--- entertainerlib/tests/__init__.py 2009-02-06 07:23:29 +0000
+++ entertainerlib/tests/__init__.py 2009-05-05 04:11:48 +0000
@@ -5,9 +5,9 @@
5from storm.locals import Store5from storm.locals import Store
6import testtools6import testtools
77
8from entertainerlib.backend.core.db.connection import Database8from entertainerlib.db.connection import Database
9from entertainerlib.frontend.translation_setup import TranslationSetup9from entertainerlib.client.translation_setup import TranslationSetup
10from entertainerlib.utils.configuration import Configuration10from entertainerlib.configuration import Configuration
1111
12TranslationSetup()12TranslationSetup()
1313
1414
=== modified file 'entertainerlib/tests/mock.py'
--- entertainerlib/tests/mock.py 2009-02-27 02:16:34 +0000
+++ entertainerlib/tests/mock.py 2009-04-30 01:11:22 +0000
@@ -1,23 +1,23 @@
1'''Test mock objects'''1'''Test mock objects'''
2# pylint: disable-msg=W02312# pylint: disable-msg=W0231
33
4from entertainerlib.frontend.backend_connection import BackendConnection4from entertainerlib.client.backend_connection import BackendConnection
5from entertainerlib.frontend.media_player import MediaPlayer5from entertainerlib.client.media_player import MediaPlayer
6from entertainerlib.frontend.medialibrary.feeds import Entry6from entertainerlib.client.medialibrary.feeds import Entry
7from entertainerlib.frontend.medialibrary.feeds import Feed7from entertainerlib.client.medialibrary.feeds import Feed
8from entertainerlib.frontend.medialibrary.feeds import FeedLibrary8from entertainerlib.client.medialibrary.feeds import FeedLibrary
9from entertainerlib.frontend.medialibrary.images import Image9from entertainerlib.client.medialibrary.images import Image
10from entertainerlib.frontend.medialibrary.images import ImageLibrary10from entertainerlib.client.medialibrary.images import ImageLibrary
11from entertainerlib.frontend.medialibrary.music import Album11from entertainerlib.client.medialibrary.music import Album
12from entertainerlib.frontend.medialibrary.music import MusicLibrary12from entertainerlib.client.medialibrary.music import MusicLibrary
13from entertainerlib.frontend.medialibrary.music import Track13from entertainerlib.client.medialibrary.music import Track
14from entertainerlib.frontend.medialibrary.videos import Movie14from entertainerlib.client.medialibrary.videos import Movie
15from entertainerlib.frontend.medialibrary.videos import TVEpisode15from entertainerlib.client.medialibrary.videos import TVEpisode
16from entertainerlib.frontend.medialibrary.videos import TVSeries16from entertainerlib.client.medialibrary.videos import TVSeries
17from entertainerlib.frontend.medialibrary.videos import VideoLibrary17from entertainerlib.client.medialibrary.videos import VideoLibrary
1818
19class MockAlbum(Album):19class MockAlbum(Album):
20 '''Mock entertainerlib.frontend.medialibrary.music.Album'''20 '''Mock entertainerlib.client.medialibrary.music.Album'''
2121
22 def __init__(self, title=None, cursor=None, make_track=True):22 def __init__(self, title=None, cursor=None, make_track=True):
23 self.title = 'Ganging Up on the Sun'23 self.title = 'Ganging Up on the Sun'
@@ -43,7 +43,7 @@
4343
4444
45class MockBackendConnection(BackendConnection):45class MockBackendConnection(BackendConnection):
46 '''Mock entertainerlib.frontend.backend_connection.BackendConnection'''46 '''Mock entertainerlib.client.backend_connection.BackendConnection'''
4747
48 def __init__(self):48 def __init__(self):
49 '''Override init to prevent BackenConnection from attempting to connect49 '''Override init to prevent BackenConnection from attempting to connect
@@ -67,7 +67,7 @@
6767
6868
69class MockEntry(Entry):69class MockEntry(Entry):
70 '''Mock entertainerlib.frontend.medialibrary.feeds.Entry'''70 '''Mock entertainerlib.client.medialibrary.feeds.Entry'''
7171
72 def __init__(self, title=None, description=None, identifier=None,72 def __init__(self, title=None, description=None, identifier=None,
73 time=None, date=None, read=False):73 time=None, date=None, read=False):
@@ -81,7 +81,7 @@
8181
8282
83class MockFeed(Feed):83class MockFeed(Feed):
84 '''Mock entertainerlib.frontend.medialibrary.feeds.Feed'''84 '''Mock entertainerlib.client.medialibrary.feeds.Feed'''
8585
86 def __init__(self, url=None):86 def __init__(self, url=None):
87 self.date = 'January 31st'87 self.date = 'January 31st'
@@ -95,7 +95,7 @@
9595
9696
97class MockFeedLibrary(FeedLibrary):97class MockFeedLibrary(FeedLibrary):
98 '''Mock entertainerlib.frontend.medialibrary.feeds.FeedLibrary'''98 '''Mock entertainerlib.client.medialibrary.feeds.FeedLibrary'''
9999
100 def __init__(self, backend_connection=None):100 def __init__(self, backend_connection=None):
101 '''Override the intial behavior.'''101 '''Override the intial behavior.'''
@@ -106,7 +106,7 @@
106106
107107
108class MockImage(Image):108class MockImage(Image):
109 '''Mock entertainerlib.frontend.medialibrary.images.Image'''109 '''Mock entertainerlib.client.medialibrary.images.Image'''
110110
111 def __init__(self, filename=None, album_path=None, title=None,111 def __init__(self, filename=None, album_path=None, title=None,
112 description=None, date=None, time=None, width=None, height=None,112 description=None, date=None, time=None, width=None, height=None,
@@ -117,7 +117,7 @@
117117
118118
119class MockImageLibrary(ImageLibrary):119class MockImageLibrary(ImageLibrary):
120 '''Mock entertainerlib.frontend.medialibrary.images.ImageLibrary'''120 '''Mock entertainerlib.client.medialibrary.images.ImageLibrary'''
121121
122 def __init__(self, backend_connection=None):122 def __init__(self, backend_connection=None):
123 '''Override the intial behavior.'''123 '''Override the intial behavior.'''
@@ -128,7 +128,7 @@
128128
129129
130class MockMediaPlayer(MediaPlayer):130class MockMediaPlayer(MediaPlayer):
131 '''Mock entertainerlib.frontend.media_player.MediaPlayer'''131 '''Mock entertainerlib.client.media_player.MediaPlayer'''
132132
133 def __init__(self, stage=None, width=None, height=None):133 def __init__(self, stage=None, width=None, height=None):
134 '''Override any actual media player set up.'''134 '''Override any actual media player set up.'''
@@ -149,7 +149,7 @@
149149
150150
151class MockMovie(Movie):151class MockMovie(Movie):
152 '''Mock entertainerlib.frontend.medialibrary.videos.Movie'''152 '''Mock entertainerlib.client.medialibrary.videos.Movie'''
153153
154 def __init__(self, filename=None):154 def __init__(self, filename=None):
155 '''Override init to prevent a database connection'''155 '''Override init to prevent a database connection'''
@@ -195,7 +195,7 @@
195 return False195 return False
196196
197class MockMusicLibrary(MusicLibrary):197class MockMusicLibrary(MusicLibrary):
198 '''Mock entertainerlib.frontend.medialibrary.music.MusicLibrary'''198 '''Mock entertainerlib.client.medialibrary.music.MusicLibrary'''
199199
200 def __init__(self, backend_connection=None):200 def __init__(self, backend_connection=None):
201 '''Override the intial behavior.'''201 '''Override the intial behavior.'''
@@ -222,7 +222,7 @@
222222
223223
224class MockTVEpisode(TVEpisode):224class MockTVEpisode(TVEpisode):
225 '''Mock entertainerlib.frontend.medialibrary.videos.TVEpisode'''225 '''Mock entertainerlib.client.medialibrary.videos.TVEpisode'''
226226
227 def __init__(self, title=None):227 def __init__(self, title=None):
228 '''Override init to prevent a database connection'''228 '''Override init to prevent a database connection'''
@@ -245,7 +245,7 @@
245245
246246
247class MockTVSeries(TVSeries):247class MockTVSeries(TVSeries):
248 '''Mock entertainerlib.frontend.medialibrary.videos.TVSeries'''248 '''Mock entertainerlib.client.medialibrary.videos.TVSeries'''
249249
250 def __init__(self, title=None):250 def __init__(self, title=None):
251 '''Override init to prevent a database connection'''251 '''Override init to prevent a database connection'''
@@ -268,7 +268,7 @@
268 return False268 return False
269269
270class MockTrack(Track):270class MockTrack(Track):
271 '''Mock entertainerlib.frontend.medialibrary.music.Track'''271 '''Mock entertainerlib.client.medialibrary.music.Track'''
272272
273 def __init__(self, filename=None, title=None, tracknumber=None, artist=None,273 def __init__(self, filename=None, title=None, tracknumber=None, artist=None,
274 album=None, genre=None, bitrate=None, year=None, rating=None,274 album=None, genre=None, bitrate=None, year=None, rating=None,
@@ -287,7 +287,7 @@
287287
288288
289class MockVideoLibrary(VideoLibrary):289class MockVideoLibrary(VideoLibrary):
290 '''Mock entertainerlib.frontend.medialibrary.videos.VideoLibrary'''290 '''Mock entertainerlib.client.medialibrary.videos.VideoLibrary'''
291291
292 def __init__(self, backend_connection=None):292 def __init__(self, backend_connection=None):
293 '''Override the intial behavior.'''293 '''Override the intial behavior.'''
294294
=== modified file 'entertainerlib/tests/test_base.py'
--- entertainerlib/tests/test_base.py 2009-03-08 03:16:25 +0000
+++ entertainerlib/tests/test_base.py 2009-05-05 03:31:34 +0000
@@ -4,11 +4,11 @@
4__copyright__ = "2008, Matt Layman"4__copyright__ = "2008, Matt Layman"
5__author__ = "Matt Layman <laymansterms.dev@gmail.com>"5__author__ = "Matt Layman <laymansterms.dev@gmail.com>"
66
7from entertainerlib.frontend.gui.widgets.base import Base7from entertainerlib.gui.widgets.base import Base
8from entertainerlib.tests import EntertainerTest8from entertainerlib.tests import EntertainerTest
99
10class BaseTest(EntertainerTest):10class BaseTest(EntertainerTest):
11 """Test for entertainerlib.frontend.gui.widgets.base"""11 """Test for entertainerlib.gui.widgets.base"""
1212
13 def setUp(self):13 def setUp(self):
14 """Set up the test"""14 """Set up the test"""
1515
=== modified file 'entertainerlib/tests/test_configuration.py'
--- entertainerlib/tests/test_configuration.py 2009-03-08 03:16:25 +0000
+++ entertainerlib/tests/test_configuration.py 2009-05-05 04:11:48 +0000
@@ -8,7 +8,7 @@
8import os8import os
99
10from entertainerlib.tests import EntertainerTest10from entertainerlib.tests import EntertainerTest
11from entertainerlib.utils.configuration import Configuration11from entertainerlib.configuration import Configuration
1212
13class ConfigurationTest(EntertainerTest):13class ConfigurationTest(EntertainerTest):
14 '''Test for utils.configuration'''14 '''Test for utils.configuration'''
1515
=== modified file 'entertainerlib/tests/test_database.py'
--- entertainerlib/tests/test_database.py 2009-01-31 21:36:56 +0000
+++ entertainerlib/tests/test_database.py 2009-05-05 03:35:28 +0000
@@ -8,7 +8,7 @@
88
9from storm.locals import Store9from storm.locals import Store
1010
11from entertainerlib.backend.core.db.connection import Database, SCHEMA11from entertainerlib.db.connection import Database, SCHEMA
12from entertainerlib.tests import EntertainerTestWithDatabase12from entertainerlib.tests import EntertainerTestWithDatabase
1313
14class DatabaseTest(EntertainerTestWithDatabase):14class DatabaseTest(EntertainerTestWithDatabase):
1515
=== modified file 'entertainerlib/tests/test_feedconfigtools.py'
--- entertainerlib/tests/test_feedconfigtools.py 2009-02-08 07:01:25 +0000
+++ entertainerlib/tests/test_feedconfigtools.py 2009-05-06 04:00:53 +0000
@@ -9,14 +9,17 @@
9import gtk9import gtk
10import gtk.glade10import gtk.glade
1111
12from entertainerlib.utils.feed_utils import FeedConfigTools12from entertainerlib.backend.components.feeds.feed_utils import FeedConfigTools
13from entertainerlib.tests import EntertainerTest13from entertainerlib.tests import EntertainerTest
1414
15THIS_DIR = os.path.dirname(__file__)15THIS_DIR = os.path.dirname(__file__)
1616
1717
18class FeedConfigToolsTest(EntertainerTest):18class FeedConfigToolsTest(EntertainerTest):
19 '''Test for entertainerlib.utils.feed_utils FeedConfigTools'''19 # pylint: disable-msg=C0301
20 # This pylint warning can be removed when the backend stuff gets removed,
21 # and the pieces we keep get rearranged.
22 '''Test for entertainerlib.backend.components.feeds.feed_utils.FeedConfigTools'''
2023
21 def setUp(self):24 def setUp(self):
22 """Sets up everything for the test"""25 """Sets up everything for the test"""
2326
=== modified file 'entertainerlib/tests/test_feedentryparser.py'
--- entertainerlib/tests/test_feedentryparser.py 2009-02-08 07:01:25 +0000
+++ entertainerlib/tests/test_feedentryparser.py 2009-05-06 04:00:53 +0000
@@ -7,7 +7,7 @@
77
8import unittest8import unittest
99
10from entertainerlib.utils.feed_utils import FeedEntryParser10from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
1111
1212
13class FeedEntryParserTest(unittest.TestCase):13class FeedEntryParserTest(unittest.TestCase):
1414
=== modified file 'entertainerlib/tests/test_frontendfeed.py'
--- entertainerlib/tests/test_frontendfeed.py 2009-01-31 21:36:56 +0000
+++ entertainerlib/tests/test_frontendfeed.py 2009-04-30 01:11:22 +0000
@@ -1,4 +1,4 @@
1'''Tests feeds from the frontend'''1'''Tests feeds from the client'''
22
3__licence__ = "GPLv2"3__licence__ = "GPLv2"
4__copyright__ = "2008, Joshua Scotton"4__copyright__ = "2008, Joshua Scotton"
@@ -6,11 +6,11 @@
66
7from pysqlite2 import dbapi2 as sqlite7from pysqlite2 import dbapi2 as sqlite
88
9from entertainerlib.frontend.medialibrary.feeds import Feed9from entertainerlib.client.medialibrary.feeds import Feed
10from entertainerlib.tests import EntertainerTest10from entertainerlib.tests import EntertainerTest
1111
12class FrontendFeedTest(EntertainerTest):12class FrontendFeedTest(EntertainerTest):
13 '''Test reading feeds from the frontend'''13 '''Test reading feeds from the client'''
1414
15 def setUp(self):15 def setUp(self):
16 """Sets up everything for the test"""16 """Sets up everything for the test"""
1717
=== modified file 'entertainerlib/tests/test_frontendfeedentry.py'
--- entertainerlib/tests/test_frontendfeedentry.py 2009-02-06 08:33:50 +0000
+++ entertainerlib/tests/test_frontendfeedentry.py 2009-04-30 01:11:22 +0000
@@ -1,4 +1,4 @@
1'''Tests feed entries on the frontend'''1'''Tests feed entries on the client'''
2# pylint: disable-msg=W02122# pylint: disable-msg=W0212
33
4__licence__ = "GPLv2"4__licence__ = "GPLv2"
@@ -7,11 +7,11 @@
77
8from pysqlite2 import dbapi2 as sqlite8from pysqlite2 import dbapi2 as sqlite
99
10from entertainerlib.frontend.medialibrary.feeds import Feed, Entry10from entertainerlib.client.medialibrary.feeds import Feed, Entry
11from entertainerlib.tests import EntertainerTest11from entertainerlib.tests import EntertainerTest
1212
13class FrontendFeedEntryTest(EntertainerTest):13class FrontendFeedEntryTest(EntertainerTest):
14 '''Tests feed entries on the frontend'''14 '''Tests feed entries on the client'''
1515
16 entry_title = "Test Entry"16 entry_title = "Test Entry"
17 entry_desc = "This is a test description"17 entry_desc = "This is a test description"
1818
=== modified file 'entertainerlib/tests/test_frontendfeedlibrary.py'
--- entertainerlib/tests/test_frontendfeedlibrary.py 2009-02-04 04:17:37 +0000
+++ entertainerlib/tests/test_frontendfeedlibrary.py 2009-04-30 01:11:22 +0000
@@ -1,4 +1,4 @@
1'''Tests feed library from the frontend'''1'''Tests feed library from the client'''
22
3__licence__ = "GPLv2"3__licence__ = "GPLv2"
4__copyright__ = "2008, Joshua Scotton"4__copyright__ = "2008, Joshua Scotton"
@@ -6,13 +6,13 @@
66
7from pysqlite2 import dbapi2 as sqlite7from pysqlite2 import dbapi2 as sqlite
88
9from entertainerlib.frontend.medialibrary.feeds import FeedLibrary9from entertainerlib.client.medialibrary.feeds import FeedLibrary
10from entertainerlib.tests import EntertainerTest10from entertainerlib.tests import EntertainerTest
11from entertainerlib.tests.mock import MockBackendConnection11from entertainerlib.tests.mock import MockBackendConnection
1212
13class FrontendFeedLibraryTest(EntertainerTest):13class FrontendFeedLibraryTest(EntertainerTest):
14 """14 """
15 Tests the frontend.medialibrary.feeds.FeedLibrary object15 Tests the client.medialibrary.feeds.FeedLibrary object
16 @todo comment tests16 @todo comment tests
17 @todo the test database doesn't seem to be working17 @todo the test database doesn't seem to be working
18 """18 """
1919
=== modified file 'entertainerlib/tests/test_imagemenuitem.py'
--- entertainerlib/tests/test_imagemenuitem.py 2009-02-06 08:33:50 +0000
+++ entertainerlib/tests/test_imagemenuitem.py 2009-05-05 03:31:34 +0000
@@ -7,14 +7,14 @@
77
8import os8import os
99
10from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem10from entertainerlib.gui.widgets.image_menu_item import ImageMenuItem
11from entertainerlib.frontend.gui.widgets.texture import Texture11from entertainerlib.gui.widgets.texture import Texture
12from entertainerlib.tests import EntertainerTest12from entertainerlib.tests import EntertainerTest
1313
14THIS_DIR = os.path.dirname(__file__)14THIS_DIR = os.path.dirname(__file__)
1515
16class ImageMenuItemTest(EntertainerTest):16class ImageMenuItemTest(EntertainerTest):
17 """Test for entertainerlib.frontend.gui.widgets.image_menu_item"""17 """Test for entertainerlib.gui.widgets.image_menu_item"""
1818
19 def setUp(self):19 def setUp(self):
20 """Set up the test"""20 """Set up the test"""
2121
=== modified file 'entertainerlib/tests/test_label.py'
--- entertainerlib/tests/test_label.py 2009-02-06 08:33:50 +0000
+++ entertainerlib/tests/test_label.py 2009-05-05 03:31:34 +0000
@@ -7,12 +7,12 @@
77
8import clutter8import clutter
99
10from entertainerlib.frontend.gui.widgets.base import Base10from entertainerlib.gui.widgets.base import Base
11from entertainerlib.frontend.gui.widgets.label import Label11from entertainerlib.gui.widgets.label import Label
12from entertainerlib.tests import EntertainerTest12from entertainerlib.tests import EntertainerTest
1313
14class LabelTest(EntertainerTest):14class LabelTest(EntertainerTest):
15 """Test for entertainerlib.frontend.gui.widgets.label"""15 """Test for entertainerlib.gui.widgets.label"""
1616
17 def setUp(self):17 def setUp(self):
18 """Set up the test"""18 """Set up the test"""
1919
=== modified file 'entertainerlib/tests/test_logger.py'
--- entertainerlib/tests/test_logger.py 2009-01-31 21:36:56 +0000
+++ entertainerlib/tests/test_logger.py 2009-05-05 04:24:32 +0000
@@ -7,7 +7,7 @@
7import logging7import logging
88
9from entertainerlib.tests import EntertainerTest9from entertainerlib.tests import EntertainerTest
10from entertainerlib.utils.logger import Logger10from entertainerlib.logger import Logger
1111
12class TestLogger(EntertainerTest):12class TestLogger(EntertainerTest):
13 '''Logger test case'''13 '''Logger test case'''
1414
=== modified file 'entertainerlib/tests/test_lyricsdownloader.py'
--- entertainerlib/tests/test_lyricsdownloader.py 2009-02-06 08:33:50 +0000
+++ entertainerlib/tests/test_lyricsdownloader.py 2009-05-06 04:46:51 +0000
@@ -9,7 +9,7 @@
9import os9import os
10import urllib10import urllib
1111
12from entertainerlib.utils.lyrics_downloader import LyricsDownloader12from entertainerlib.download import LyricsDownloader
1313
14THIS_DIR = os.path.dirname(__file__)14THIS_DIR = os.path.dirname(__file__)
1515
1616
=== modified file 'entertainerlib/tests/test_models.py'
--- entertainerlib/tests/test_models.py 2009-01-31 21:36:56 +0000
+++ entertainerlib/tests/test_models.py 2009-05-05 03:35:28 +0000
@@ -6,7 +6,7 @@
66
7from storm.locals import Store7from storm.locals import Store
88
9from entertainerlib.backend.core.db import models9from entertainerlib.db import models
10from entertainerlib.tests import EntertainerTestWithDatabase10from entertainerlib.tests import EntertainerTestWithDatabase
1111
1212
1313
=== modified file 'entertainerlib/tests/test_music.py'
--- entertainerlib/tests/test_music.py 2009-02-14 15:26:32 +0000
+++ entertainerlib/tests/test_music.py 2009-04-30 01:11:22 +0000
@@ -8,11 +8,11 @@
88
9from pysqlite2 import dbapi2 as sqlite9from pysqlite2 import dbapi2 as sqlite
1010
11from entertainerlib.frontend.medialibrary.music import (11from entertainerlib.client.medialibrary.music import (
12 Album, AlbumHasNoTracks, MusicLibrary, Track, CompactDisc,12 Album, AlbumHasNoTracks, MusicLibrary, Track, CompactDisc,
13 TrackRatingOutOfRange, TrackTypeError)13 TrackRatingOutOfRange, TrackTypeError)
14from entertainerlib.tests.mock import MockBackendConnection14from entertainerlib.tests.mock import MockBackendConnection
15from entertainerlib.frontend.medialibrary.playable import Playable15from entertainerlib.client.medialibrary.playable import Playable
16from entertainerlib.tests import EntertainerTest16from entertainerlib.tests import EntertainerTest
1717
18class TestMusic(EntertainerTest):18class TestMusic(EntertainerTest):
1919
=== modified file 'entertainerlib/tests/test_opmlparser.py'
--- entertainerlib/tests/test_opmlparser.py 2009-02-08 07:01:25 +0000
+++ entertainerlib/tests/test_opmlparser.py 2009-05-06 04:00:53 +0000
@@ -9,7 +9,7 @@
9import os9import os
10import xml.parsers.expat10import xml.parsers.expat
1111
12from entertainerlib.utils.feed_utils import OPMLParser12from entertainerlib.backend.components.feeds.feed_utils import OPMLParser
1313
14THIS_DIR = os.path.dirname(__file__)14THIS_DIR = os.path.dirname(__file__)
1515
1616
=== modified file 'entertainerlib/tests/test_screen.py'
--- entertainerlib/tests/test_screen.py 2009-04-27 23:12:40 +0000
+++ entertainerlib/tests/test_screen.py 2009-05-05 03:31:34 +0000
@@ -7,15 +7,15 @@
7import clutter7import clutter
88
9from entertainerlib.exceptions import ScreenException9from entertainerlib.exceptions import ScreenException
10from entertainerlib.frontend.gui.screens.screen import Screen10from entertainerlib.gui.screens.screen import Screen
11from entertainerlib.frontend.gui.tabs.tab import Tab11from entertainerlib.gui.tabs.tab import Tab
12from entertainerlib.frontend.gui.user_event import UserEvent12from entertainerlib.gui.user_event import UserEvent
13from entertainerlib.frontend.gui.widgets.base import Base13from entertainerlib.gui.widgets.base import Base
14from entertainerlib.frontend.gui.widgets.tab_group import TabGroup14from entertainerlib.gui.widgets.tab_group import TabGroup
15from entertainerlib.tests import EntertainerTest15from entertainerlib.tests import EntertainerTest
1616
17class ScreenTest(EntertainerTest):17class ScreenTest(EntertainerTest):
18 '''Test for entertainerlib.frontend.gui.screens.screen'''18 """Test for entertainerlib.gui.screens.screen"""
1919
20 def setUp(self):20 def setUp(self):
21 '''Set up the test.'''21 '''Set up the test.'''
2222
=== modified file 'entertainerlib/tests/test_screenfactory.py'
--- entertainerlib/tests/test_screenfactory.py 2009-02-06 08:33:50 +0000
+++ entertainerlib/tests/test_screenfactory.py 2009-05-05 03:31:34 +0000
@@ -7,26 +7,26 @@
77
8import os8import os
99
10from entertainerlib.frontend.gui.screens.album import Album10from entertainerlib.gui.screens.album import Album
11from entertainerlib.frontend.gui.screens.artist import Artist11from entertainerlib.gui.screens.artist import Artist
12from entertainerlib.frontend.gui.screens.audio_play import AudioPlay12from entertainerlib.gui.screens.audio_play import AudioPlay
13from entertainerlib.frontend.gui.screens.disc import Disc13from entertainerlib.gui.screens.disc import Disc
14from entertainerlib.frontend.gui.screens.factory import ScreenFactory14from entertainerlib.gui.screens.factory import ScreenFactory
15from entertainerlib.frontend.gui.screens.feed import Feed15from entertainerlib.gui.screens.feed import Feed
16from entertainerlib.frontend.gui.screens.feed_entry import FeedEntry16from entertainerlib.gui.screens.feed_entry import FeedEntry
17from entertainerlib.frontend.gui.screens.main import Main17from entertainerlib.gui.screens.main import Main
18from entertainerlib.frontend.gui.screens.movie import Movie18from entertainerlib.gui.screens.movie import Movie
19from entertainerlib.frontend.gui.screens.music import Music19from entertainerlib.gui.screens.music import Music
20from entertainerlib.frontend.gui.screens.photo import Photo20from entertainerlib.gui.screens.photo import Photo
21from entertainerlib.frontend.gui.screens.photo_albums import PhotoAlbums21from entertainerlib.gui.screens.photo_albums import PhotoAlbums
22from entertainerlib.frontend.gui.screens.photographs import Photographs22from entertainerlib.gui.screens.photographs import Photographs
23from entertainerlib.frontend.gui.screens.question import Question23from entertainerlib.gui.screens.question import Question
24from entertainerlib.frontend.gui.screens.rss import Rss24from entertainerlib.gui.screens.rss import Rss
25from entertainerlib.frontend.gui.screens.tv_episodes import TvEpisodes25from entertainerlib.gui.screens.tv_episodes import TvEpisodes
26from entertainerlib.frontend.gui.screens.tv_series import TvSeries26from entertainerlib.gui.screens.tv_series import TvSeries
27from entertainerlib.frontend.gui.screens.video_osd import VideoOSD27from entertainerlib.gui.screens.video_osd import VideoOSD
28from entertainerlib.frontend.gui.screens.video import Video28from entertainerlib.gui.screens.video import Video
29from entertainerlib.frontend.gui.screens.weather import WeatherScreen29from entertainerlib.gui.screens.weather import WeatherScreen
30from entertainerlib.tests import EntertainerTest30from entertainerlib.tests import EntertainerTest
31from entertainerlib.tests.mock import MockAlbum31from entertainerlib.tests.mock import MockAlbum
32from entertainerlib.tests.mock import MockEntry32from entertainerlib.tests.mock import MockEntry
@@ -43,7 +43,7 @@
4343
4444
45class ScreenFactoryTest(EntertainerTest):45class ScreenFactoryTest(EntertainerTest):
46 '''Test for entertainerlib.frontend.gui.screens.factory'''46 '''Test for entertainerlib.gui.screens.factory'''
4747
48 def setUp(self):48 def setUp(self):
49 EntertainerTest.setUp(self)49 EntertainerTest.setUp(self)
5050
=== modified file 'entertainerlib/tests/test_scrollarea.py'
--- entertainerlib/tests/test_scrollarea.py 2009-02-06 08:13:48 +0000
+++ entertainerlib/tests/test_scrollarea.py 2009-05-05 03:31:34 +0000
@@ -6,13 +6,13 @@
66
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.widgets.base import Base9from entertainerlib.gui.widgets.base import Base
10from entertainerlib.frontend.gui.widgets.label import Label10from entertainerlib.gui.widgets.label import Label
11from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea11from entertainerlib.gui.widgets.scroll_area import ScrollArea
12from entertainerlib.tests import EntertainerTest12from entertainerlib.tests import EntertainerTest
1313
14class ScrollAreaTest(EntertainerTest):14class ScrollAreaTest(EntertainerTest):
15 """Test for entertainerlib.frontend.gui.widgets.scroll_area"""15 """Test for entertainerlib.gui.widgets.scroll_area"""
1616
17 def setUp(self):17 def setUp(self):
18 """Set up the test"""18 """Set up the test"""
1919
=== modified file 'entertainerlib/tests/test_texture.py'
--- entertainerlib/tests/test_texture.py 2009-02-06 08:33:50 +0000
+++ entertainerlib/tests/test_texture.py 2009-05-05 03:31:34 +0000
@@ -9,14 +9,14 @@
99
10import clutter10import clutter
1111
12from entertainerlib.frontend.gui.widgets.base import Base12from entertainerlib.gui.widgets.base import Base
13from entertainerlib.frontend.gui.widgets.texture import Texture13from entertainerlib.gui.widgets.texture import Texture
14from entertainerlib.tests import EntertainerTest14from entertainerlib.tests import EntertainerTest
1515
16THIS_DIR = os.path.dirname(__file__)16THIS_DIR = os.path.dirname(__file__)
1717
18class TextureTest(EntertainerTest):18class TextureTest(EntertainerTest):
19 """Test for entertainerlib.frontend.gui.widgets.texture"""19 """Test for entertainerlib.gui.widgets.texture"""
2020
21 def setUp(self):21 def setUp(self):
22 """Set up the test"""22 """Set up the test"""
2323
=== modified file 'entertainerlib/tests/test_theme.py'
--- entertainerlib/tests/test_theme.py 2009-01-31 21:36:56 +0000
+++ entertainerlib/tests/test_theme.py 2009-05-05 04:17:21 +0000
@@ -5,10 +5,10 @@
5__author__ = "Matt Layman <laymansterms.dev@gmail.com>"5__author__ = "Matt Layman <laymansterms.dev@gmail.com>"
66
7from entertainerlib.tests import EntertainerTest7from entertainerlib.tests import EntertainerTest
8from entertainerlib.utils.theme import Theme8from entertainerlib.gui.theme import Theme
99
10class ThemeTest(EntertainerTest):10class ThemeTest(EntertainerTest):
11 """Test for entertainerlib.utils.theme"""11 """Test for entertainerlib.gui.theme"""
1212
13 def setUp(self):13 def setUp(self):
14 """Set up the test"""14 """Set up the test"""
1515
=== modified file 'entertainerlib/tests/test_transitionfactory.py'
--- entertainerlib/tests/test_transitionfactory.py 2009-02-07 17:12:21 +0000
+++ entertainerlib/tests/test_transitionfactory.py 2009-05-05 03:31:34 +0000
@@ -5,15 +5,15 @@
5__copyright__ = '2009, Matt Layman'5__copyright__ = '2009, Matt Layman'
6__author__ = 'Matt Layman <laymansterms.dev@gmail.com>'6__author__ = 'Matt Layman <laymansterms.dev@gmail.com>'
77
8from entertainerlib.frontend.gui.transitions.factory import TransitionFactory8from entertainerlib.gui.transitions.factory import TransitionFactory
9from entertainerlib.frontend.gui.transitions.fade import Fade9from entertainerlib.gui.transitions.fade import Fade
10from entertainerlib.frontend.gui.transitions.no_effect import NoEffect10from entertainerlib.gui.transitions.no_effect import NoEffect
11from entertainerlib.frontend.gui.transitions.slide import Slide11from entertainerlib.gui.transitions.slide import Slide
12from entertainerlib.frontend.gui.transitions.zoom_and_fade import ZoomAndFade12from entertainerlib.gui.transitions.zoom_and_fade import ZoomAndFade
13from entertainerlib.tests import EntertainerTest13from entertainerlib.tests import EntertainerTest
1414
15class TransitionFactoryTest(EntertainerTest):15class TransitionFactoryTest(EntertainerTest):
16 '''Test for entertainerlib.frontend.gui.transitions.factory'''16 '''Test for entertainerlib.gui.transitions.factory'''
1717
18 def setUp(self):18 def setUp(self):
19 '''Set up the test'''19 '''Set up the test'''
2020
=== modified file 'entertainerlib/tests/test_userinterface.py'
--- entertainerlib/tests/test_userinterface.py 2009-04-27 23:12:40 +0000
+++ entertainerlib/tests/test_userinterface.py 2009-05-05 03:31:34 +0000
@@ -6,14 +6,14 @@
66
7import clutter7import clutter
88
9from entertainerlib.frontend.gui.screens.question import Question9from entertainerlib.gui.screens.question import Question
10from entertainerlib.frontend.gui.user_event import UserEvent10from entertainerlib.gui.user_event import UserEvent
11from entertainerlib.frontend.gui.user_interface import UserInterface11from entertainerlib.gui.user_interface import UserInterface
12from entertainerlib.tests import EntertainerTest12from entertainerlib.tests import EntertainerTest
13from entertainerlib.tests.mock import MockClutterKeyboardEvent13from entertainerlib.tests.mock import MockClutterKeyboardEvent
1414
15class UserInterfaceTest(EntertainerTest):15class UserInterfaceTest(EntertainerTest):
16 '''Test for entertainerlib.frontend.gui.user_interface'''16 '''Test for entertainerlib.gui.user_interface'''
1717
18 def setUp(self):18 def setUp(self):
19 EntertainerTest.setUp(self)19 EntertainerTest.setUp(self)
2020
=== modified file 'entertainerlib/tests/test_weather.py'
--- entertainerlib/tests/test_weather.py 2009-02-10 00:59:03 +0000
+++ entertainerlib/tests/test_weather.py 2009-05-06 05:10:14 +0000
@@ -7,7 +7,7 @@
7from datetime import datetime7from datetime import datetime
88
9from entertainerlib.tests import EntertainerTest9from entertainerlib.tests import EntertainerTest
10from entertainerlib.utils.weather import Weather10from entertainerlib.weather import Weather
1111
12class WeatherTest(EntertainerTest):12class WeatherTest(EntertainerTest):
13 """13 """
1414
=== modified file 'entertainerlib/thumbnailer.py'
--- entertainerlib/thumbnailer.py 2009-02-19 14:25:51 +0000
+++ entertainerlib/thumbnailer.py 2009-05-05 04:11:48 +0000
@@ -17,7 +17,7 @@
1717
18from entertainerlib.exceptions import (ImageThumbnailerException,18from entertainerlib.exceptions import (ImageThumbnailerException,
19 ThumbnailerException, VideoThumbnailerException)19 ThumbnailerException, VideoThumbnailerException)
20from entertainerlib.utils.configuration import Configuration20from entertainerlib.configuration import Configuration
2121
2222
23class Thumbnailer(object):23class Thumbnailer(object):
2424
=== removed directory 'entertainerlib/utils'
=== removed file 'entertainerlib/utils/__init__.py'
--- entertainerlib/utils/__init__.py 2008-08-07 01:17:29 +0000
+++ entertainerlib/utils/__init__.py 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
1'''Entertainer utils'''
20
=== removed file 'entertainerlib/utils/albumart_downloader.py'
--- entertainerlib/utils/albumart_downloader.py 2009-03-22 04:53:10 +0000
+++ entertainerlib/utils/albumart_downloader.py 1970-01-01 00:00:00 +0000
@@ -1,353 +0,0 @@
1'''AlbumArtDownloader - Search and download album art from the internet.'''
2
3__licence__ = "GPLv2"
4__copyright__ = "2007, Lauri Taimila, "
5__copyright__ += "2008, Matt Layman"
6__author__ = "Lauri Taimila <lauri@taimila.com>, "
7__author__ += "Matt Layman <laymansterms.dev@gmail.com"
8
9import os
10import re
11import locale
12import urllib
13import threading
14from xml.dom import minidom
15
16# Amazon licence for Entertainer
17LICENSE_KEY = "1YCWYZ0SPPAJ27YZZ482"
18DEFAULT_LOCALE = "en_US"
19ASSOCIATE = "webservices-20"
20
21# We are not allowed to batch more than 2 requests at once
22# http://docs.amazonwebservices.com/AWSEcommerceService/4-0/
23# PgCombiningOperations.html
24MAX_BATCH_JOBS = 2
25
26class Bag:
27 '''Just a helper'''
28
29
30class AlbumArtDownloader(threading.Thread):
31 """
32 Search and download album art from the internet.
33
34 This class is heavily based on Rhythmbox - AlbumArt plugin's class
35 'AmazonCoverArtSearch'. That plugin is released under GPLv2 (or higher)
36 and it's copyrights belong to Gareth Murphy and Martin Szulecki.
37
38 See more: http://www.gnome.org/projects/rhythmbox/
39
40 If you want better cover search, please contribute to Rhyhtmbox project.
41 """
42
43 def __init__(self, album, artist, art_file_path, callback = None):
44 """
45 Initialize album art downloader
46 @param album: Album title
47 @param artist: Artist name
48 @param art_file_path: Path where albumart is saved
49 @param callback: Callback function that is called after search if set
50 """
51 threading.Thread.__init__(self)
52 self.setName("AlbumArt Downloader")
53 self.callback_function = callback # Callback function
54 self.album = album # Album title
55 self.artist = artist # Artist name
56 # Album art files are in this directory
57 self.path = art_file_path
58 (self.tld, self.encoding) = self.__get_locale ()
59
60 def run(self):
61 """Start searching and downloading albumart."""
62 self.search()
63
64 def __get_locale (self):
65 '''Get locale information from user\'s machine'''
66 # "JP is the only locale that correctly takes UTF8 input.
67 # All other locales use LATIN1."
68 # http://developer.amazonwebservices.com/connect/
69 # entry.jspa?externalID=1295&categoryID=117
70 supported_locales = {
71 "en_US" : ("com", "latin1"),
72 "en_GB" : ("co.uk", "latin1"),
73 "de" : ("de", "latin1"),
74 "ja" : ("jp", "utf8")
75 }
76
77 lc_id = DEFAULT_LOCALE
78 default = locale.getdefaultlocale ()[0]
79 if default:
80 if supported_locales.has_key (default):
81 lc_id = default
82 else:
83 lang = default.split("_")[0]
84 if supported_locales.has_key (lang):
85 lc_id = lang
86
87 return supported_locales[lc_id]
88
89 def __valid_match (self, item):
90 '''Determine if item matches tag criteria'''
91 return (hasattr (item, "LargeImage") or hasattr (item, "MediumImage")) \
92 and hasattr (item, "ItemAttributes")
93
94 def __tidy_up_string (self, str_input):
95 """
96 Tidy up string. Remove spaces, convert to lowercase and replace chars.
97 """
98 # Lowercase
99 str_input = str_input.lower ()
100 # Strip
101 str_input = str_input.strip ()
102
103 # TODO: Convert accented to unaccented
104 str_input = str_input.replace (" - ", " ")
105 str_input = str_input.replace (": ", " ")
106 str_input = str_input.replace (" & ", " and ")
107
108 return str_input
109
110 def search(self):
111 """Search album art from Amazon"""
112 self.searching = True
113 self.cancel = False
114 self.keywords = []
115
116 st_artist = self.artist or u'Unknown'
117 st_album = self.album or u'Unknown'
118
119 if st_artist == st_album == u'Unknown':
120 self.on_search_completed (None)
121 return
122
123 # Tidy up
124
125 # Replace quote characters
126 # don't replace single quote: could be important punctuation
127 for char in ["\""]:
128 st_artist = st_artist.replace (char, '')
129 st_album = st_album.replace (char, '')
130
131
132 self.st_album = st_album
133 self.st_artist = st_artist
134
135 # Remove variants of Disc/CD [1-9] from album title before search
136 for exp in ["\([Dd]isc *[1-9]+\)", "\([Cc][Dd] *[1-9]+\)"]:
137 p = re.compile (exp)
138 st_album = p.sub ('', st_album)
139
140 st_album_no_vol = st_album
141 for exp in ["\(*[Vv]ol.*[1-9]+\)*"]:
142 p = re.compile (exp)
143 st_album_no_vol = p.sub ('', st_album_no_vol)
144
145 self.st_album_no_vol = st_album_no_vol
146
147 # Save current search's entry properties
148 self.search_album = st_album
149 self.search_artist = st_artist
150 self.search_album_no_vol = st_album_no_vol
151
152 # TODO: Improve to decrease wrong cover downloads, maybe add severity?
153 # Assemble list of search keywords (and thus search queries)
154 if st_album == u'Unknown':
155 self.keywords.append ("%s Best of" % (st_artist))
156 self.keywords.append ("%s Greatest Hits" % (st_artist))
157 self.keywords.append ("%s Essential" % (st_artist))
158 self.keywords.append ("%s Collection" % (st_artist))
159 self.keywords.append ("%s" % (st_artist))
160 elif st_artist == u'Unknown':
161 self.keywords.append ("%s" % (st_album))
162 if st_album_no_vol != st_artist:
163 self.keywords.append ("%s" % (st_album_no_vol))
164 self.keywords.append ("Various %s" % (st_album))
165 else:
166 if st_album != st_artist:
167 self.keywords.append ("%s %s" % (st_artist, st_album))
168 if st_album_no_vol != st_album:
169 self.keywords.append ("%s %s" %
170 (st_artist, st_album_no_vol))
171 self.keywords.append ("Various %s" % (st_album))
172 self.keywords.append ("%s" % (st_artist))
173
174 # Initiate asynchronous search
175 self.search_next ()
176
177 def search_next(self):
178 """Search again, because the last one didn't find any covers."""
179 if len (self.keywords) == 0:
180 # No keywords left to search -> no results
181 self.on_search_completed (None)
182 return False
183
184 self.searching = True
185
186 url = "http://ecs.amazonaws." + self.tld + "/onca/xml" \
187 "?Service=AWSECommerceService" \
188 "&AWSAccessKeyId=" + LICENSE_KEY + \
189 "&AssociateTag=" + ASSOCIATE + \
190 "&ResponseGroup=Images,ItemAttributes" \
191 "&Operation=ItemSearch" \
192 "&ItemSearch.Shared.SearchIndex=Music"
193
194 job = 1
195 while job <= MAX_BATCH_JOBS and len (self.keywords) > 0:
196 keyword = self.keywords.pop (0)
197 keyword = keyword.encode (self.encoding, "ignore")
198 keyword = keyword.strip ()
199 keyword = urllib.quote (keyword)
200 url += "&ItemSearch.%d.Keywords=%s" % (job, keyword)
201 job += 1
202
203 # Retrieve search for keyword
204 temp = urllib.urlopen(url)
205 search_results = temp.read()
206 self.on_search_response(search_results)
207 return True
208
209 def __unmarshal(self, element):
210 rc = Bag()
211 child_elements = [e for e in element.childNodes if isinstance (e,
212 minidom.Element)]
213 if child_elements:
214 for child in child_elements:
215 key = child.tagName
216 if hasattr (rc, key):
217 if not isinstance (getattr (rc, key), list):
218 setattr (rc, key, [getattr (rc, key)])
219 getattr (rc, key).append (self.__unmarshal (child))
220 # get_best_match_urls() wants a list, even if there is only
221 # one item/artist
222 elif child.tagName in ("Items", "Item", "Artist"):
223 setattr (rc, key, [self.__unmarshal(child)])
224 else:
225 setattr (rc, key, self.__unmarshal(child))
226 else:
227 rc = "".join ([e.data for e in element.childNodes if isinstance (e,
228 minidom.Text)])
229 return rc
230
231 def on_search_response (self, result_data):
232 '''Check search results
233
234 If results are not good, we search again with the next keyword.
235 '''
236 if result_data is None:
237 self.search_next()
238 return
239
240 try:
241 xmldoc = minidom.parseString(result_data)
242 except (TypeError, AttributeError):
243 self.search_next()
244 return
245
246 data = self.__unmarshal (xmldoc)
247 if not hasattr (data, "ItemSearchResponse") or \
248 not hasattr (data.ItemSearchResponse, "Items"):
249 # Something went wrong ...
250 self.search_next ()
251 else:
252 # We got some search results
253 self.on_search_results (data.ItemSearchResponse.Items)
254
255 def on_search_results (self, results):
256 '''Results were found, now we need to take action.'''
257 self.on_search_completed (results)
258
259 def on_search_completed (self, result):
260 """
261 Search completed and results found.
262
263 Download large album art image from the first result and save it to
264 the disk. This function diverges greatly from the rhythmbox
265 implementation in order to avoid their loader and CoverArtDatabase
266 """
267 self.searching = False
268 image_urls = self.get_best_match_urls(result)
269 if len(image_urls) == 0:
270 return
271 image_url = image_urls[0]
272 image_file = urllib.urlopen(image_url)
273 # base64 encode artist and album so there can be a '/' in the artist
274 # or album
275 artist_album = self.artist + " - " + self.album
276 artist_album = artist_album.encode("base64")
277
278 dest = open(os.path.join(self.path, artist_album + ".jpg"),'w')
279 dest.write(image_file.read())
280 dest.close()
281
282 if self.callback_function is not None:
283 self.callback_function(self.artist, self.album)
284
285 def get_best_match_urls (self, search_results):
286 """Return tuple of URL's to large and medium cover of the best match"""
287 # Default to "no match", our results must match our criteria
288
289 # This code comes from Rhythmbox so we can't control the use of 'filter'
290 # pylint: disable-msg=W0141
291
292 if not search_results:
293 return []
294
295 best_match = None
296
297 for result in search_results:
298 if not hasattr (result, "Item"):
299 # Search was unsuccessful, try next batch job
300 continue
301
302 items = filter(self.__valid_match, result.Item)
303 if self.search_album != u'Unknown':
304 album_check = self.__tidy_up_string (self.search_album)
305 for item in items:
306 if not hasattr (item.ItemAttributes, "Title"):
307 continue
308
309 album = self.__tidy_up_string (item.ItemAttributes.Title)
310 if album == album_check:
311 # Found exact album, can not get better than that
312 best_match = item
313 break
314 # If we already found a best_match, just keep checking for
315 # exact one. Check the results for both an album name that
316 # contains the name we're searching for, and an album name
317 # that's a substring of the name we're searching for
318 elif (best_match is None) and \
319 (album.find (album_check) != -1 or
320 album_check.find (album) != -1):
321 best_match = item
322
323 # If we still have no definite hit, use first result where artist
324 # matches
325 if (self.search_album == u'Unknown' and \
326 self.search_artist != u'Unknown'):
327 artist_check = self.__tidy_up_string (self.search_artist)
328 if best_match is None:
329 # Check if artist appears in the Artists list
330 hit = False
331 for item in items:
332 if not hasattr (item.ItemAttributes, "Artist"):
333 continue
334
335 for artist in item.ItemAttributes.Artist:
336 artist = self.__tidy_up_string (artist)
337 if artist.find (artist_check) != -1:
338 best_match = item
339 hit = True
340 break
341 if hit:
342 break
343
344 urls = [getattr (best_match, size).URL for size in ("LargeImage",
345 "MediumImage")
346 if hasattr (best_match, size)]
347 if urls:
348 return urls
349
350 # No search was successful
351 return []
352
353
3540
=== removed file 'entertainerlib/utils/cd_utils.py'
--- entertainerlib/utils/cd_utils.py 2008-06-16 04:10:47 +0000
+++ entertainerlib/utils/cd_utils.py 1970-01-01 00:00:00 +0000
@@ -1,11 +0,0 @@
1'''Utilities for handling the cd'''
2# XXX: rockstar - Why do we have module for a single function?
3
4__licence__ = "GPLv2"
5__copyright__ = "2008, Joshua Scotton"
6__author__ = "Joshua Scotton <josh@joshuascotton.com>"
7
8import os
9
10def eject_cd():
11 os.system("eject")
120
=== removed file 'entertainerlib/utils/content_management_dialog.py'
--- entertainerlib/utils/content_management_dialog.py 2009-04-17 21:35:32 +0000
+++ entertainerlib/utils/content_management_dialog.py 1970-01-01 00:00:00 +0000
@@ -1,774 +0,0 @@
1#!/usr/bin/env python
2'''Content management GUI for Entertainer'''
3
4__licence__ = "GPLv2"
5__copyright__ = "2008, Lauri Taimila and Joshua Scotton"
6__author__ = ("Lauri Taimila <lauri@taimila.com>",
7 "Joshua Scotton <josh@joshuascotton.com>")
8
9import os
10import socket
11
12import gtk
13import gtk.glade
14
15from entertainerlib.utils.weather import Weather
16from entertainerlib.utils.configuration import Configuration
17from entertainerlib.utils.open_feed_source_dialog import OpenFeedSourceDialog
18
19from entertainerlib.backend.core.message import Message
20from entertainerlib.backend.core.message_bus_proxy import MessageBusProxy
21from entertainerlib.backend.core.message_type_priority import MessageType
22
23class ContentManagementDialog:
24 """
25 This is a content management tool for Entertainer media center application.
26 """
27
28 # Temporary storage for entered URL
29 url = ""
30 GLADE_DIR = os.path.join(os.path.dirname(__file__), "glade")
31
32 def __init__(self, stand_alone):
33 """
34 Initialize content management dialog
35 @param stand_alone: Boolean, Is this dialog running as a stand alone
36 process
37 """
38 self.stand_alone = stand_alone
39 self.config = Configuration()
40 self.weather = Weather()
41
42 # Load glade UI
43 self.gladefile = os.path.join(self.GLADE_DIR,
44 "entertainer-content-management.glade")
45 self.widgets = gtk.glade.XML(self.gladefile)
46
47 # Get content management dialog and bind signal callbacks
48 self.dialog = self.widgets.get_widget("ContentManagementDialog")
49 if (self.dialog):
50 callback_dic = {
51 "on_button_open_list_clicked" :
52 self.on_button_open_list_clicked,
53 "on_close_button_clicked" : self.on_close_button_clicked,
54 "on_button_remove_videos_clicked" :
55 self.on_button_remove_videos_clicked,
56 "on_button_add_videos_clicked" :
57 self.on_button_add_videos_clicked,
58 "on_button_edit_videos_clicked" :
59 self.on_button_edit_videos_clicked,
60 "on_checkbutton_video_metadata_toggled" :
61 self.on_checkbutton_video_metadata_toggled,
62 "on_button_add_music_clicked" :
63 self.on_button_add_music_clicked,
64 "on_button_remove_music_clicked" :
65 self.on_button_remove_music_clicked,
66 "on_button_edit_music_clicked" :
67 self.on_button_edit_music_clicked,
68 "on_lyrics_checkbox_toggled" : self.on_lyrics_checkbox_toggled,
69 "on_art_checkbox_toggled" : self.on_art_checkbox_toggled,
70 "on_button_add_images_clicked" :
71 self.on_button_add_images_clicked,
72 "on_button_remove_images_clicked" :
73 self.on_button_remove_images_clicked,
74 "on_button_edit_images_clicked" :
75 self.on_button_edit_images_clicked,
76 "on_button_add_feed_clicked" :
77 self.on_button_add_feed_clicked,
78 "on_button_remove_feed_clicked" :
79 self.on_button_remove_feed_clicked,
80 "on_button_edit_feed_clicked" :
81 self.on_button_edit_feed_clicked,
82 "on_fetch_interval_spinbutton_value_changed" :
83 self.on_fetch_interval_spinbutton_value_changed,
84 "on_ContentManagementDialog_destroy" : self.on_dialog_closed,
85 "on_url_dialog_delete_event" : self.on_url_dialog_delete_event,
86 "on_url_dialog_ok_button_clicked" :
87 self.on_url_dialog_ok_button_clicked,
88 "on_url_dialog_cancel_button_clicked" :
89 self.on_url_dialog_cancel_button_clicked,
90 "on_button_video_rebuild_clicked" :
91 self.on_button_video_rebuild_clicked,
92 "on_button_music_rebuild_clicked" :
93 self.on_button_music_rebuild_clicked,
94 "on_button_image_rebuild_clicked" :
95 self.on_button_image_rebuild_clicked,
96 "on_button_feed_rebuild_clicked" :
97 self.on_button_feed_rebuild_clicked,
98 "on_button_add_weather_clicked" :
99 self.on_button_add_weather_clicked,
100 "on_button_remove_weather_clicked" :
101 self.on_button_remove_weather_clicked,
102 "on_weather_display_checkbox_toggled" :
103 self.on_weather_display_checkbox_toggled,
104 "on_location_find_button_clicked" :
105 self.on_location_find_button_clicked,
106 "on_location_cancel_button_clicked" :
107 self.on_location_cancel_button_clicked,
108 "on_location_add_button_clicked" :
109 self.on_location_add_button_clicked,
110 "on_location_entry_activate" : self.on_location_entry_activate}
111 self.widgets.signal_autoconnect(callback_dic)
112
113 # Initialize dialog widgets with correct values and show dialog
114 self.init_dialog_values_from_configure_file()
115 self.dialog.resize(500, 300)
116 self.dialog.show()
117
118 # Initialize location list in search dialog
119 result_list = self.widgets.get_widget("location_results_treeview")
120 store = gtk.ListStore(str)
121 result_list.set_model(store)
122 cell_renderer = gtk.CellRendererText()
123 column = gtk.TreeViewColumn(_("Location"), cell_renderer, text=0)
124 result_list.append_column(column)
125
126# Signal handlers
127
128 def on_dialog_closed(self, widget):
129 """Callback function for dialog's close button"""
130 try:
131 proxy = MessageBusProxy(client_name = "Content Management GUI")
132 proxy.connectToMessageBus()
133 proxy.sendMessage(Message(MessageType.CONTENT_CONF_UPDATED))
134 proxy.disconnectFromMessageBus()
135 except socket.error:
136 error = gtk.MessageDialog(
137 None, gtk.DIALOG_MODAL,
138 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
139 "Entertainer backend is not running. "
140 "Cache cannot be rebuilt."
141 ))
142 error.run()
143 error.destroy()
144
145
146 if(self.stand_alone):
147 self.dialog.hide()
148 self.dialog.destroy()
149 gtk.main_quit()
150 else:
151 self.dialog.hide()
152 self.dialog.destroy()
153
154 def on_close_button_clicked(self, widget):
155 """Callback function for dialog's close button"""
156 if(self.stand_alone):
157 self.dialog.hide()
158 self.dialog.destroy()
159 gtk.main_quit()
160 else:
161 self.dialog.hide()
162 self.dialog.destroy()
163
164 def on_button_add_videos_clicked(self, widget):
165 """Opens add URL dialog. """
166 widget = self.widgets.get_widget("treeview_videos")
167 model = widget.get_model()
168 # Open "Select folder" dialog
169 dialog = gtk.FileChooserDialog(_("Select video folder"), None,
170 gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
171 (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,
172 gtk.STOCK_OPEN,gtk.RESPONSE_OK),
173 None)
174 status = dialog.run()
175 # If folder was selected we add it to model and update config file
176 if(status == gtk.RESPONSE_OK):
177 self.add_to_model_and_config(dialog.get_current_folder(), model,
178 self.video_folders, "Videos")
179 dialog.destroy()
180
181 def on_button_remove_videos_clicked(self, widget):
182 """Remove currently selected folder from video folders"""
183 widget = self.widgets.get_widget("treeview_videos")
184 model = widget.get_model()
185 selection = widget.get_selection().get_selected()
186 if selection[1] == None:
187 return
188 rm_folder = model.get_value(selection[1], 0)
189 self.video_folders.remove(rm_folder)
190 str_folders = ";".join(self.video_folders)
191 self.config.write_content_value("Videos", "folders", str_folders)
192 model.remove(selection[1])
193
194 def on_button_edit_videos_clicked(self, widget):
195 """Edit currently selected folder"""
196 widget = self.widgets.get_widget("treeview_videos")
197 url_dialog = self.widgets.get_widget("url_dialog")
198 url_entry = self.widgets.get_widget("url_entry")
199 model = widget.get_model()
200 selection = widget.get_selection().get_selected()
201 if selection[1] == None:
202 return
203 folder = model.get_value(selection[1], 0)
204 url_entry.set_text(folder)
205 url_dialog.set_title(_("Edit URL"))
206 status = url_dialog.run()
207 if status == gtk.RESPONSE_OK and os.path.exists(self.url):
208 # Update list model
209 model.set_value(selection[1], 0, self.url)
210 # Update configure file
211 pos = self.video_folders.index(folder)
212 self.video_folders.remove(folder)
213 self.video_folders.insert(pos, self.url)
214 str_folders = ";".join(self.video_folders)
215 self.config.write_content_value("Videos", "folders",
216 str_folders)
217
218 def on_checkbutton_video_metadata_toggled(self, widget):
219 """
220 Download video file metadata from internet
221 @param widget: GTK-Widget
222 """
223 self.config.write_content_value("Videos", "download_metadata",
224 widget.get_active())
225
226 def on_button_add_music_clicked(self, widget):
227 """
228 Opens add URL dialog
229 @param widget: GTK-Widget
230 """
231 widget = self.widgets.get_widget("treeview_music")
232 model = widget.get_model()
233 # Open "Select folder" dialog
234 dialog = gtk.FileChooserDialog(_("Select music folder"), None,
235 gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
236 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
237 gtk.RESPONSE_OK),
238 None)
239 status = dialog.run()
240 # If folder was selected we add it to model and update config file
241 if(status == gtk.RESPONSE_OK):
242 self.add_to_model_and_config(dialog.get_current_folder(), model,
243 self.music_folders, "Music")
244 dialog.destroy()
245
246 def on_button_remove_music_clicked(self, widget):
247 """Remove currently selected folder from music folders"""
248 widget = self.widgets.get_widget("treeview_music")
249 model = widget.get_model()
250 selection = widget.get_selection().get_selected()
251 if selection[1] == None:
252 return
253 rm_folder = model.get_value(selection[1], 0)
254 self.music_folders.remove(rm_folder)
255 str_folders = ";".join(self.music_folders)
256 self.config.write_content_value("Music", "folders", str_folders)
257 model.remove(selection[1])
258
259 def on_button_edit_music_clicked(self, widget):
260 """Edit currently selected music folder"""
261 widget = self.widgets.get_widget("treeview_music")
262 url_dialog = self.widgets.get_widget("url_dialog")
263 url_entry = self.widgets.get_widget("url_entry")
264 model = widget.get_model()
265 selection = widget.get_selection().get_selected()
266 if selection[1] == None:
267 return
268 folder = model.get_value(selection[1], 0)
269 url_entry.set_text(folder)
270 url_dialog.set_title(_("Edit URL"))
271 status = url_dialog.run()
272 if status == gtk.RESPONSE_OK and os.path.exists(self.url):
273 # Update list model
274 model.set_value(selection[1], 0, self.url)
275 # Update configure file
276 pos = self.music_folders.index(folder)
277 self.music_folders.remove(folder)
278 self.music_folders.insert(pos, self.url)
279 str_folders = ";".join(self.music_folders)
280 self.config.write_content_value("Music", "folders", str_folders)
281
282 def on_button_add_images_clicked(self, widget):
283 """Opens add URL dialog. """
284 widget = self.widgets.get_widget("treeview_images")
285 model = widget.get_model()
286 # Open "Select folder" dialog
287 dialog = gtk.FileChooserDialog(_("Select image folder"), None,
288 gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
289 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
290 gtk.RESPONSE_OK),
291 None)
292 status = dialog.run()
293 # If folder was selected we add it to model and update config file
294 if(status == gtk.RESPONSE_OK):
295 self.add_to_model_and_config(dialog.get_current_folder(), model,
296 self.image_folders, "Images")
297 dialog.destroy()
298
299 def on_button_remove_images_clicked(self, widget):
300 """Remove currently selected folder from images folders"""
301 widget = self.widgets.get_widget("treeview_images")
302 model = widget.get_model()
303 selection = widget.get_selection().get_selected()
304 if selection[1] == None:
305 return
306 rm_folder = model.get_value(selection[1], 0)
307 self.image_folders.remove(rm_folder)
308 str_folders = ";".join(self.image_folders)
309 self.config.write_content_value("Images", "folders", str_folders)
310 model.remove(selection[1])
311
312 def on_button_edit_images_clicked(self, widget):
313 """Edit currently selected music folder"""
314 widget = self.widgets.get_widget("treeview_images")
315 url_dialog = self.widgets.get_widget("url_dialog")
316 url_entry = self.widgets.get_widget("url_entry")
317 model = widget.get_model()
318 selection = widget.get_selection().get_selected()
319 if selection[1] == None:
320 return
321 folder = model.get_value(selection[1], 0)
322 url_entry.set_text(folder)
323 url_dialog.set_title(_("Edit URL"))
324 status = url_dialog.run()
325 if status == gtk.RESPONSE_OK and os.path.exists(self.url):
326 # Update list model
327 model.set_value(selection[1], 0, self.url)
328 # Update configure file
329 pos = self.image_folders.index(folder)
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches