Merge lp:~crichter/openlp/media_rewrite into lp:openlp
- media_rewrite
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 1810 | ||||
Proposed branch: | lp:~crichter/openlp/media_rewrite | ||||
Merge into: | lp:openlp | ||||
Diff against target: |
3216 lines (+1924/-669) 18 files modified
openlp/core/lib/htmlbuilder.py (+3/-107) openlp/core/lib/mediaplayer.py (+137/-0) openlp/core/lib/plugin.py (+1/-1) openlp/core/lib/renderer.py (+2/-2) openlp/core/ui/__init__.py (+2/-2) openlp/core/ui/maindisplay.py (+67/-208) openlp/core/ui/mainwindow.py (+3/-0) openlp/core/ui/media/__init__.py (+65/-0) openlp/core/ui/media/mediacontroller.py (+576/-0) openlp/core/ui/media/phononplayer.py (+201/-0) openlp/core/ui/media/webkitplayer.py (+426/-0) openlp/core/ui/slidecontroller.py (+70/-122) openlp/plugins/media/lib/mediaitem.py (+125/-55) openlp/plugins/media/lib/mediatab.py (+159/-29) openlp/plugins/media/mediaplugin.py (+43/-48) resources/forms/mediafilesdialog.ui (+0/-95) resources/pyinstaller/hook-openlp.core.ui.media.py (+30/-0) scripts/windows-builder.py (+14/-0) |
||||
To merge this branch: | bzr merge lp:~crichter/openlp/media_rewrite | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Raoul Snyman | Approve | ||
Tim Bentley | Approve | ||
Jonathan Corwin | Pending | ||
Andreas Preikschat | Pending | ||
Review via email:
|
This proposal supersedes a proposal from 2011-11-30.
Commit message
Description of the change
Rewrite of the multimedia stuff.
- add separated multimedia code in own subdirectory in openlp/ui
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Plugin code is working the wrong way.
Plugins should hook into core code not other way round.
The code in Mainwindows is incorrect and the plugin should look for preview and live controls.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jonathan Corwin (j-corwin) wrote : Posted in a previous version of this proposal | # |
Rather than have the MediaAPI's hardcoded (e.g. line 1095) it would be good (if possible) if you could dynamically load them and remove any direct reference to VLC/Phonon/Webkit from outside their individual classes.
This is how presentations work. It also allows someone to just drop a new py file inheriting MediaAPI in the folder and it would just work without needing to change any other code.
Presentations code:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jonathan Corwin (j-corwin) wrote : Posted in a previous version of this proposal | # |
Something else to think about, moving the webkit media out of htmlbuilder and pulling it in.
So in htmlbuilder.py, consider putting three more placeholders in the large HTML string for media CSS, media JS and media HTML.
Now instead of having the media css/html/js in there, move this to the webkit mediaapi item.
Then get htmlbuilder to request the above code to put in place.
So pseudo code with made up variable names...
HTMLSRC = "<style>
for plugin in self.parent.
css += plugin.
js += plugin.
html += plugin.
html = HTMLSRC % (css, js, html)
The media plugin can then do similar, looping around the MediaAPI's to get the relevant code.
We could then use this framework later to move the Alert HTML into the alert plugin, and should milleja46 decide to create his Countdown plugin, he will have a way of producing the relevant div's he'll need too.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
plugin.
plugin.
plugin.
1. These functions are poorly named. What does "display_css" mean? Does this function actually display CSS? Surely these functions should rather be called "get_css" or "get_display_css"
2. Plugin is a QObject-based class, therefore, like the rest of the functions in the Plugin class, your display_* functions should be getDisplayCss, getDisplayJavaS
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jonathan Corwin (j-corwin) wrote : Posted in a previous version of this proposal | # |
Lines 9 -> 17 need to be moved to the [renamed] webkit display_css() too.
(I'm aware you just copied my function names, but I was just doing some quick examples and not putting any thought into the naming :)
A few cosmetics:
Delete commented out code: 285,286,
Note, if they are commented out because you plan to implement properly in a future commit, just put a # TODO: comment above them, saying what the missing functionality is. We don't like to merge commented out code into trunk.
Lines too long: 353, 552
Space needed before the +=: 1269, 1278,1287
Lines 1688 -> 1698: You need a space on both sides of the + or -
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
The plugin to core code is wrong and I think it is because we have the placement wrong.
A lot of the plumbing is because the players are controlled by the plugin and core needs access to them.
For presentations this as fine as the players were external but for this it's more complicated.
I think Core should provide player services (extensible and flexible so more can be added). This should stop the needed to pass controllers around.
The media plugin asks what players can I use and what do I want to us or just does not care and uses what core has decided.
I am not sure what will happen if this code has media deactivated.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jonathan Corwin (j-corwin) wrote : Posted in a previous version of this proposal | # |
The core devs have had a brief chat about this and this has highlighted possible differences of opinion between us. So we plan to put aside some time to talk about this on IRC. However because of various commitments and holidays, this isn't likely to happen until after beta2 has released,
Once we come to a common agreement, we can then present you with a single direction, rather than four different people telling you potentially four conflicting things which doesn't really help anyone!
Some of the things we will discuss are likely to be:
1. Future enhancements such as linking songs to audio (backing tracks) and adding a video background to a theme, should be able to hook into the media displays without too much effort. Backing tracks would obviously need a way of pulling in the media controls cleanly. Some of this may determine how much code is in core and how much is in the media plugin.
2. If the media plugin is disabled, OpenLP should still function perfectly well, so what should happen about the items in #1
3. Plugins should not be accessing core classes/methods directly. I.e. we don't want a method in a plugin to just call something like mainwindow.
4. Whether plugins should be adding items to the controller toolbar, or whether these should be provided by core, and the best way of doing whichever option we choose.
And no doubt other things!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
Hello Tim, hello Jonathan!
Thanks for your comments and inspecially your explanations Jonathan.
Unfortunately at home it is rarely possible for me to talk in the chat. But maybe if you plan a fix date for speaking regarding this topic, I could organize some time for this? (not on a thursday or sunday please)?
In the meantime I will think about this and put some ideas in the wiki. (http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
only time for a quick run but initial comments.
I have a white rectangle on the live and preview displays which obscures the previews.
My test videos played OK and text over video worked.
I changed the video background and I got a segment fault.
The videos played with the global theme as background instead of black!.
Add any media files to servicemanage crashes with a unicode error (menu or DND). Note this code is changing in my B1 tree.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
> I have a white rectangle on the live and preview displays which obscures the
> previews.
-> removed
> I changed the video background and I got a segment fault.
-> may be fixed, but could you please retest and give me a little description which steps I have to do for this?
The videos played with the global theme as background instead of black!.
-> Hopefully fixed
> Add any media files to servicemanage crashes with a unicode error (menu or DND). Note this code is changing in my B1 tree.
-> Fixed, but currently the configurable Start/Stop Time doesnt work, because of missing implementation for getting the media length.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
- Start Time implementation added
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Preview image 1/4 of the size of the box.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal | # |
Btw, I assigned you to bug #813995 "opengl rendering".
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
@Tim: Preview image size corrected
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
162 - Plugins should push stuff in and core save it. Core should not go and grab it. If they are media plugins then we need to new name a plugins will be confussing. It has for me!
No check on inactive plugins. This should be added and removed when plugins are activated / deactivated. Unless covered by above
766 is a python object so you should have get_display_CSS also 864 and 871
862 868 duplicate blank lines
920 MediaManager is python so need _'s in method names
949 # Signals spaced needed. In many places.
977 this is for plugins not core so it necessary change name.
Toolbar you do not need to next / prev slides so these nned to be removed.
1815 should be indented.
1963 why do we need to add extra layer of object?
2275 Line length. No lines over 80 characters
2422 blank line
2582 why remove mediatab
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
Hi Tim, thanks for your review! Here some comments from my side of view.
> 162 - Plugins should push stuff in and core save it. Core should not go and
> grab it. If they are media plugins then we need to new name a plugins will be
> confussing. It has for me!
I found no better solution till now :-(, because of the media stuff is used also if the media plugin is deactivated.
This stuff could also exist in other plugins, e.g. Alarm or later on a countdown plugin, ...
>
> No check on inactive plugins. This should be added and removed when plugins
> are activated / deactivated. Unless covered by above
Because of the the same code is used for background video, there should be no check for activated/
> Toolbar you do not need to next / prev slides so these nned to be removed.
What do you mean here?
> 1963 why do we need to add extra layer of object?
In my opionion its easier to handle (hide/show) if we have more controls later on (eg. DVD controls)
> 2582 why remove mediatab
I thought, I have to remove it because the mediatab now is handled seperately with getSettingsTab, isnt it?
> 766 is a python object so you should have get_display_CSS also 864 and 871
> 862 868 duplicate blank lines
> 920 MediaManager is python so need _'s in method names
> 949 # Signals spaced needed. In many places.
> 977 this is for plugins not core so it necessary change name.
> 1815 should be indented.
> 2275 Line length. No lines over 80 characters
> 2422 blank line
I will change these issues.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
I think the term plugins is what has caused some of the confusion.
Could we use the term "players" for the media players, Htmlplayer PhononPlayer VLCPlayer.
This would then leave plugins to their existing meaning. This would then make things clearer as we would have player lists and plugin lists and their meaning would be clear.
You may think of a better term than player but not plugin! It needs to be new.
When you add the new toolbar you keep the existing toolbar. That has all the existing features even if they are not relevant. The Green arrows are not relevant.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Player is good. The presentations plugin calls its things Controllers, but I don't think that quite fits here.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
I'm a bit slow today :-(
What does your mean?
I thought the problem are the line 162ff
162 + plugin_css = u''
163 + plugin_js = u''
164 + plugin_html = u''
But I cant see any relation to the last two comments (Tims and Raouls)
Please could you explain it a little bit?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Yes that is correct.
Any thing which comes from core/ui/media is a player thing.
This means the above lines are player_css/js/html as they are managed by the players.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
The css/js/html stuff can delivered also from every plugin.
E.g. the alarm plugin should later this stuff deliver in this manner.
So this is the css/js/html part of any plugin which is webview related.
I have currently no other idea for a good title for this :-(
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
Please could you vote for a good variable name and an appropriate function title?
- plugins_webkit_css += plugin.
- css_from_plugins += plugin.
- css_additions += plugin.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Just a couple of small things as I browse through this:
215 + def getDisplayJavas
Technically, it is "JavaScript", not "Javascript"
329 + self.webView.
394 + self.webView.
What does the 7 stand for? Is there an enumeration you could use?
439 + or not self.isVisible():# or self.videoWidge
Please either remove the commented part, or put it on its own line.
754 === added file 'openlp/
(Optional) You can also name it "media_api.py" if you want. It's a little easier to read :-)
734 + Cd = 3
735 + Dvd = 4
CD and DVD can be all capital letters, it also makes it slightly easier to read.
789 + Specialiced Media API class
790 + to reflect Features of the related API
"A generic media API class to provide OpenLP with a pluggable media display framework."
938 +class MediaManager(
Can we rather name this the MediaController? We already have another class named "MediaManager" and I don't want developers to get confused by the two.
1414 + Specialiced MediaAPI class
1415 + to reflect Features of the Phonon API
"A specialised version of the MediaAPI class, which provides a Phonon display."
1633 + Specialiced MediaAPI class
1634 + to reflect Features of the QtWebkit API
"A specialised version of the MediaAPI class, which provides a QtWebKit display."
2028 + sender = self.sender(
This is the old-style ternary operator shortcut for Python. Rather use the new one, which is a little longer, but is much more accurate: sender = self.sender(
2386 + #(path, name) = os.path.
If it's commented out, and you're not going to use it, remove it.
Phew, that's all I have time for right now.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal | # |
Line 143 and 410: No \ needed.
Line 455/456: The operator should be before the \
if bla and \
foo:
instead of
if bla \
and foo:
When I hide the display (Hide Screen) and send a video live, the diplay is shown. But it should not be shown. (Single monitor set up)
Line 468: You can remove "shrinkItem" and use "self" instead.
Line 744/754: We have two blank lines between classes.
Line 958: Don't use a new line for each sentence, instead add full stops after each one and wrap the sentence if necessary. Also the syntax is not correct (the enumeration with "-" will not work). I suggest this: http://
Line 981 (and following): self.Time should be self.time
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Getting much better.
Need to roll with head.
Video works and plays.
Video preview does not work you just get the last theme used.
Video override for songs works but when the video finishes is drops back to theme. Video should loop?
Blank to screen blanks to theme. The rest work.
Preview bar needs to loose green arrows in the same way live did.
Need tool tips on the sliders.
Slides are not aligned.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
matysek (mzibricky) wrote : Posted in a previous version of this proposal | # |
This branch does not start when windows build is created:
The issue is that mediacontroller is looking for some python modules ending with 'api.py'. But these files are not available when a build is created.
We should find another way how to check available apis without looking directly on file system or we would need to include api.py files with the build.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal | # |
Please merge trunk and resubmit. When will you continue to work on this?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
> Please merge trunk and resubmit. When will you continue to work on this?
Hopefully I can do some progress this week!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
rimach this needs to be in by the end of November (in my view) if we release the next version at the end of the year.
I have taken some of your framework changes and are in the processing of merging them (with other fixes they were dependent on) so have a look at my merge request.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Missing and incomplete comments
; at the end of a number of lines.
Need to respin after my changes go in as they will be changes.
webkitapi.py has constants which should be in CAPITALS.
Check formatting (double blank lines) and field names there are naming standard errors.
Lots of commented out code some of which is your machine specific!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal | # |
Needs Fixing:
Please add more detailed documentation to the Display and MainDisplay classes. First I thought you were just splitting the MainDisplay class (withought seeing the use of it). Later I saw that you are actually creating Display objects. I had to look through the diff to confirm my guess (that it is used to as preview windows).
Do not do this:
The implementation of the Media Controller
The Media Controller adds an own class for every API
Currently these are QtWebkit, Phonon and planed Vlc.
Do this instead:
The implementation of the Media Controller. More text until the limit of 80
characters is reached. The Media Controller adds an own class for every API.
Currently these are QtWebkit, Phonon and planed Vlc.
It is much easier to read the docs in the code, when you do this.
(There is a option in Eric which may be helpful. It changes the background color of the relevant characters when you go beyond the allowed character length.)
Always end the sentence with a period. Look at this (periods missing): http://
The signals names need to be fixed (line 933ff): "Media Stop" vs "seekSlider" vs "media_hide".
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Initial comments
build_alert_css needs to be removed it is not called.
Why are the plugins added to the Renderer they are not used.
I will have a longer look tonight
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal | # |
http://
Traceback (most recent call last):
File "<string>", line 42, in <module>
File "D:\OpenLP_
File "D:\OpenLP_
File "D:\OpenLP_
File "D:\OpenLP_
File "D:\OpenLP_
WindowsError: [Error 3] Das System kann den angegebenen Pfad nicht finden: u'C:\\Program Files (x86)\\
None
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Tried running the code on linux and had a 10px border down the right and left side of the main display so I could see the slide below.
Need to update resources pyinstaller and create hook-openlp.
ui/__init__ why add Display and Controller they are internal.
Display and Maindisplay have duplicate set up code for QTWebKit objects.
Alerts no longer get add over the top of videos.
media/__init__ imports should be at the top.
766 MediaAPI should be lowercase and any over calls. This will stop case issues in the future.
922 self.APIs can we be more explicit with the name. What mediaPlayers for example.
925 currentDisplayM
977 does media_api.py need to be in that directory?
992 good example of why we need to rename.
999 is the comment correct.
1011 should there be a return here? Then we could loose the confusing isAnyonePlaying. If they are just leave. The stop would be unconditional.
1113 - 1115 ?????????????
1434 should be a constant at top of class as duplicated.
1643 etc why split out the flash CSS javascript etc.
1871,1878 double blank line
2025 why Display
2404 etc name verticalLayout_X with a name
2620 Are all these translates going to be used?
2722 Commented out code
2794 "Chose Player" ?? need to check with Raoul
2811 Commented code
General comment use of API in variable names should be resisted. Say what they are.
2992 how do we get here ?
MediaOpenForm Cannot show this!
Hook-open.media,py is wrong very wrong!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal | # |
It will be easier to see what you have changed when you merge and commit trunk first and then fix your code.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
> ui/__init__ why add Display and Controller they are internal.
the display classes will be also used for plugin.media and later possibly also for thememanager
> 1113 - 1115 ?????????????
some media types need special controls here this could be added depended at the related Media type and Player type
> 2025 why Display
Display will be used to create the preview Display
> 2404 etc name verticalLayout_X with a name
> 2620 Are all these translates going to be used?
> 2992 how do we get here ?
> MediaOpenForm Cannot show this!
> 2811 Commented code
This peace of code is currently not yet active
I did some implemention for opening a DVD or a complete folder, but decided to wait with further steps after integration of the current stuff,
because Im afraid this media implementation beast would grow and grow and never integrated
> 1643 etc why split out the flash CSS javascript etc.
The flash related stuff is slightly different to the video stuff and the split offers the possibility to switch of this partially.
(its not needed, but it helps in case of bugfixing issues related to Flash OR Video)
> 977 does media_api.py need to be in that directory?
may we could also move this file to core/lib directory?
> 2794 "Chose Player" ?? need to check with Raoul
Sure, could you help please Raoul?
> 925 currentDisplayM
No, as far as I understand phonon is not a special player. It an API for various installed Media Players. Thatswhy I used the term media api instead of media player all the time!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Responding to you comments but not reviewing the code.
Possible usage is no reason to put things in now. Put them in when they are needed.
If code cannot be run please remove it so we can see and test what is going to be implemented. The new dialog is case here. Same with the Flash stuff.
move media_api.py
I am not happy with api as a name for a variable. Phonon is an abstraction layer but so is vlc. API in the terms of a variable name is meaningless.
Please can you make sure changes and head merges are in separate commits as per Andrea's comment.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal | # |
Please set screen to {} and not None:
270 + self.screen = None
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal | # |
Does the preview (the little frame) works as good as in trunk without the changes you made to previewDisplay?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
> Does the preview (the little frame) works as good as in trunk without the
> changes you made to previewDisplay?
I'm not sure what you meant.
The preview is changed now for video. (in trunk there is only a widget for phonon, here there is a display widget which has webview, phonon widget, ...)
This widget should be disabled for other media types (images, songs, ...)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Modules should not have word separators:
openlp/
Should be:
mediaplayer.py
I have VLC installed, but the VLC media controller did not show up. Is there a way to make it appear?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
VLC can be tested with my media branch!
(it was a request from another core developer to split the stuff, to minimize the complexity. So I do all the changes at the media branch and merge it afterwards to the media_rewrite branch)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Slide controller seems to be broken.
Add a multi verse song and then add a video. The verse numbers are not reset but left as per the previous song.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
> Slide controller seems to be broken.
> Add a multi verse song and then add a video. The verse numbers are not reset
> but left as per the previous song.
Please could you add more information regarding this issue. I tried it at Windows XP and Ubuntu Oneiric. No problems here.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
It is difficult to test as I have a broken box.
I have no webkit playback so I cannot test the code so text over video / alerts are a problem.
855 needs to be a to of class
1313 needs a comment as to what [1] means. There are other places as well.
1508 Duplicate List
1844 Subset of Duplicate List
2177 Comment not needed
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Line 2407 - how do I get this to work?
Preview summary in slide controller is in the wrong place.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
Thanks for review!
> 855 needs to be a to of class
Im not understand this comment
> Line 2407 - how do I get this to work?
This is "only" a temporarily controller, which will used at loading of media file internally. (no really user interaction possible/needed, but needed to satisfy the interfaces)
> Preview summary in slide controller is in the wrong place.
What do you mean?
The other issues I will fix (hopefully this evening)
There is another issue I found by myself. Playing video with webkit in Preview is only visible after display setup :-(
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
855 is an import statement and should not be the last line of the file.
Need to look a 2407 but that should be over the weekend.
Preview display is in the top left corner of the previw area and not in the middle.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Line 862 is an import statement which should be a the top of the fine not the last line.
Line 716 isWebLoaded should be deleted as it is not referenced due to other moves.
1190 Indents
Slidecontroller you need to make a new group for next and previous buttons so you can use
self.toolbar.
2440 to 2661 cannot be tested so should not go in.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
John Cegalis (jseagull1) wrote : Posted in a previous version of this proposal | # |
I'm not sure if you are looking for input or errors but I was testing your branch and the text in front of video worked great. I unchecked webkit and left phonon enabled and it did not work. I then unchecked phonon and it came crashing down and now OpenLP will not start. Here is the debug link I get. I didn't want to paste the whole mess in here.
Great work.
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
John Cegalis (jseagull1) wrote : Posted in a previous version of this proposal | # |
As a followup, I cleaned up the files showing in the media config and it started up again.
I removed the media showing and kept this.
status=1
override%20player=0
All is well and thank you for working on this.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
John Cegalis (jseagull1) wrote : Posted in a previous version of this proposal | # |
Kubuntu Oneiric
Sorry for the trouble but I continued testing and found out what is happening.
I unchecked webkit and phonon as I said above and get this traceback below.
Looking at the media heading in the config file it adds "players=" as you see at the bottom. That addition to the config stops OpenLP from opening. If I remove "players=" from the config file it starts right up.
Traceback (most recent call last):
File "/home/
self.
KeyError: u''
[media]
status=1
override%20player=0
last%20director
media%20count=2
media%200=
media%201=
players=
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
See John's comment.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
Print statement left in
VLC definition added to build and not defined
Code which does not run still included
Horrible flash when a video goes live. Send live a video player starts on the control screen disappears and reappears on the display screen. This did not happen in the last version.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
> Line 716 isWebLoaded should be deleted as it is not referenced due to other moves.
is neede because of is used for Display and Maindisplay
> 1190 Indents
done
> Slidecontroller you need to make a new group for next and previous buttons so you can use
> self.toolbar.
done
> 2440 to 2661 cannot be tested so should not go in.
This code is active for loading a new media item. (see mediaitem.py line 199)
> VLC statement was wrong
> horrible flash I have to analysed
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
isWebLoaded is defined twice now in both display and maindisplay
previewDisplay is not used in mediaitem.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
> isWebLoaded is defined twice now in both display and maindisplay
Your right. I will remove the second one in maindisplay
> previewDisplay is not used in mediaitem.
However it will be used implicitely. (line 2576)
For using the video player mechanism it is needed to have a controller (which deliver the related control functions) and a related display class (which contain all available player widgets).
In this case while loading a new media item with the open dialog the choosen video file will be checked. Therefore the media plugin has an own mediacontroller (consist of a controller and an invisible preview Display).
Unfortunately I cant reproduce your "Horrible flash". So Im not sure what I can do!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
The horrible flash only started yesterday so what did you break /fix !
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
John Cegalis (jseagull1) wrote : Posted in a previous version of this proposal | # |
My crash problem is solved and flv files are playing fine on Kubuntu (if that is what you mean by horrible flash Tim).
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal | # |
No my horrible flash is a new window(may be the display) being created about 1/4 screen size. The video stating and the display moving to the final display and becoming large.
As this takes 0.5 of a sec all you see is a flash but enough to see something horrid.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
John Cegalis (jseagull1) wrote : Posted in a previous version of this proposal | # |
Maybe I need dual monitors. I'm not seeing it in the single display.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : Posted in a previous version of this proposal | # |
> No my horrible flash is a new window(may be the display) being created about
> 1/4 screen size. The video stating and the display moving to the final
> display and becoming large.
> As this takes 0.5 of a sec all you see is a flash but enough to see something
> horrid.
Hmm, please could you give me some more details?
- single or dual monitor mode
- starting video from mediamanager/
- with all types of player? (activate the override player option in media settings and choose an explicit mediaplayer)
- is this little new window a floating window and were it appears
- does this issue occour every time a video is started or only under some circumstances (after first start, after using a song, ...)
(What I found is that for used phonon player the theme background appears for a short time)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Tim Bentley (trb143) wrote : | # |
Rats looks like I can approve this ;-)
One small bug which should not stop it going in is when a video is started behind text the sound is not muted.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Raoul Snyman (raoul-snyman) wrote : | # |
Looks and works for me too. Approved then it is!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
rimach (crichter) wrote : | # |
jippi :-)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Meinert Jordan (m2j) wrote : | # |
Glückwunsch.
Preview Diff
1 | === modified file 'openlp/core/lib/htmlbuilder.py' |
2 | --- openlp/core/lib/htmlbuilder.py 2011-11-24 21:25:38 +0000 |
3 | +++ openlp/core/lib/htmlbuilder.py 2011-12-01 18:30:31 +0000 |
4 | @@ -53,8 +53,8 @@ |
5 | position: absolute; |
6 | left: 0px; |
7 | top: 0px; |
8 | - width: %spx; |
9 | - height: %spx; |
10 | + width: 100%%; |
11 | + height: 100%%; |
12 | } |
13 | #black { |
14 | z-index: 8; |
15 | @@ -67,12 +67,6 @@ |
16 | #image { |
17 | z-index: 2; |
18 | } |
19 | -#video1 { |
20 | - z-index: 3; |
21 | -} |
22 | -#video2 { |
23 | - z-index: 3; |
24 | -} |
25 | %s |
26 | #footer { |
27 | position: absolute; |
28 | @@ -90,90 +84,9 @@ |
29 | </style> |
30 | <script> |
31 | var timer = null; |
32 | - var video_timer = null; |
33 | - var current_video = '1'; |
34 | var transition = %s; |
35 | - |
36 | - function show_video(state, path, volume, loop){ |
37 | - // Note, the preferred method for looping would be to use the |
38 | - // video tag loop attribute. |
39 | - // But QtWebKit doesn't support this. Neither does it support the |
40 | - // onended event, hence the setInterval() |
41 | - // In addition, setting the currentTime attribute to zero to restart |
42 | - // the video raises an INDEX_SIZE_ERROR: DOM Exception 1 |
43 | - // To complicate it further, sometimes vid.currentTime stops |
44 | - // slightly short of vid.duration and vid.ended is intermittent! |
45 | - // |
46 | - // Note, currently the background may go black between loops. Not |
47 | - // desirable. Need to investigate using two <video>'s, and hiding/ |
48 | - // preloading one, and toggle between the two when looping. |
49 | - |
50 | - if(current_video=='1'){ |
51 | - var vid = document.getElementById('video1'); |
52 | - var vid2 = document.getElementById('video2'); |
53 | - } else { |
54 | - var vid = document.getElementById('video2'); |
55 | - var vid2 = document.getElementById('video1'); |
56 | - } |
57 | - if(volume != null){ |
58 | - vid.volume = volume; |
59 | - vid2.volume = volume; |
60 | - } |
61 | - switch(state){ |
62 | - case 'init': |
63 | - vid.src = 'file:///' + path; |
64 | - vid2.src = 'file:///' + path; |
65 | - if(loop == null) loop = false; |
66 | - vid.looping = loop; |
67 | - vid2.looping = loop; |
68 | - vid.load(); |
69 | - break; |
70 | - case 'load': |
71 | - vid2.style.visibility = 'hidden'; |
72 | - vid2.load(); |
73 | - break; |
74 | - case 'play': |
75 | - vid.play(); |
76 | - vid.style.visibility = 'visible'; |
77 | - if(vid.looping){ |
78 | - video_timer = setInterval( |
79 | - function() { |
80 | - show_video('poll'); |
81 | - }, 200); |
82 | - } |
83 | - break; |
84 | - case 'pause': |
85 | - if(video_timer!=null){ |
86 | - clearInterval(video_timer); |
87 | - video_timer = null; |
88 | - } |
89 | - vid.pause(); |
90 | - break; |
91 | - case 'stop': |
92 | - show_video('pause'); |
93 | - vid.style.visibility = 'hidden'; |
94 | - break; |
95 | - case 'poll': |
96 | - if(vid.ended||vid.currentTime+0.2>vid.duration) |
97 | - show_video('swap'); |
98 | - break; |
99 | - case 'swap': |
100 | - show_video('pause'); |
101 | - if(current_video=='1') |
102 | - current_video = '2'; |
103 | - else |
104 | - current_video = '1'; |
105 | - show_video('play'); |
106 | - show_video('load'); |
107 | - break; |
108 | - case 'close': |
109 | - show_video('stop'); |
110 | - vid.src = ''; |
111 | - vid2.src = ''; |
112 | - break; |
113 | - } |
114 | - } |
115 | %s |
116 | + |
117 | function show_image(src){ |
118 | var img = document.getElementById('image'); |
119 | img.src = src; |
120 | @@ -186,18 +99,14 @@ |
121 | function show_blank(state){ |
122 | var black = 'none'; |
123 | var lyrics = ''; |
124 | - var pause = false; |
125 | switch(state){ |
126 | case 'theme': |
127 | lyrics = 'hidden'; |
128 | - pause = true; |
129 | break; |
130 | case 'black': |
131 | black = 'block'; |
132 | - pause = true; |
133 | break; |
134 | case 'desktop': |
135 | - pause = true; |
136 | break; |
137 | } |
138 | document.getElementById('black').style.display = black; |
139 | @@ -210,13 +119,6 @@ |
140 | if(shadow!=null) |
141 | shadow.style.visibility = lyrics; |
142 | document.getElementById('footer').style.visibility = lyrics; |
143 | - var vid = document.getElementById('video'); |
144 | - if(vid.src != ''){ |
145 | - if(pause) |
146 | - vid.pause(); |
147 | - else |
148 | - vid.play(); |
149 | - } |
150 | } |
151 | |
152 | function show_footer(footertext){ |
153 | @@ -277,10 +179,6 @@ |
154 | <body> |
155 | <img id="bgimage" class="size" %s /> |
156 | <img id="image" class="size" %s /> |
157 | -<video id="video1" class="size" style="visibility:hidden" autobuffer preload> |
158 | -</video> |
159 | -<video id="video2" class="size" style="visibility:hidden" autobuffer preload> |
160 | -</video> |
161 | %s |
162 | %s |
163 | <div id="footer" class="footer"></div> |
164 | @@ -336,7 +234,6 @@ |
165 | js_additions += plugin.getDisplayJavaScript() |
166 | html_additions += plugin.getDisplayHtml() |
167 | html = HTMLSRC % (build_background_css(item, width, height), |
168 | - width, height, |
169 | css_additions, |
170 | build_footer_css(item, height), |
171 | build_lyrics_css(item, webkitvers), |
172 | @@ -609,4 +506,3 @@ |
173 | item.footer.width(), theme.font_footer_name, |
174 | theme.font_footer_size, theme.font_footer_color) |
175 | return lyrics_html |
176 | - |
177 | |
178 | === added file 'openlp/core/lib/mediaplayer.py' |
179 | --- openlp/core/lib/mediaplayer.py 1970-01-01 00:00:00 +0000 |
180 | +++ openlp/core/lib/mediaplayer.py 2011-12-01 18:30:31 +0000 |
181 | @@ -0,0 +1,137 @@ |
182 | +# -*- coding: utf-8 -*- |
183 | +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 |
184 | + |
185 | +############################################################################### |
186 | +# OpenLP - Open Source Lyrics Projection # |
187 | +# --------------------------------------------------------------------------- # |
188 | +# Copyright (c) 2008-2011 Raoul Snyman # |
189 | +# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan # |
190 | +# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, # |
191 | +# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias # |
192 | +# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # |
193 | +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund # |
194 | +# --------------------------------------------------------------------------- # |
195 | +# This program is free software; you can redistribute it and/or modify it # |
196 | +# under the terms of the GNU General Public License as published by the Free # |
197 | +# Software Foundation; version 2 of the License. # |
198 | +# # |
199 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
200 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
201 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
202 | +# more details. # |
203 | +# # |
204 | +# You should have received a copy of the GNU General Public License along # |
205 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
206 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
207 | +############################################################################### |
208 | + |
209 | +from openlp.core.ui.media import MediaState |
210 | + |
211 | +class MediaPlayer(object): |
212 | + """ |
213 | + This is the base class media Player class to provide OpenLP with a pluggable media display |
214 | + framework. |
215 | + """ |
216 | + |
217 | + def __init__(self, parent, name=u'media_player'): |
218 | + self.parent = parent |
219 | + self.name = name |
220 | + self.available = self.check_available() |
221 | + self.isActive = False |
222 | + self.canBackground = False |
223 | + self.canFolder = False |
224 | + self.state = MediaState.Off |
225 | + self.hasOwnWidget = False |
226 | + self.audio_extensions_list = [] |
227 | + self.video_extensions_list = [] |
228 | + |
229 | + def check_available(self): |
230 | + """ |
231 | + Player is available on this machine |
232 | + """ |
233 | + return False |
234 | + |
235 | + def setup(self, display): |
236 | + """ |
237 | + Create the related widgets for the current display |
238 | + """ |
239 | + pass |
240 | + |
241 | + def load(self, display): |
242 | + """ |
243 | + Load a new media file and check if it is valid |
244 | + """ |
245 | + return True |
246 | + |
247 | + def resize(self, display): |
248 | + """ |
249 | + If the main display size or position is changed, the media widgets |
250 | + should also resized |
251 | + """ |
252 | + pass |
253 | + |
254 | + def play(self, display): |
255 | + """ |
256 | + Starts playing of current Media File |
257 | + """ |
258 | + pass |
259 | + |
260 | + def pause(self, display): |
261 | + """ |
262 | + Pause of current Media File |
263 | + """ |
264 | + pass |
265 | + |
266 | + def stop(self, display): |
267 | + """ |
268 | + Stop playing of current Media File |
269 | + """ |
270 | + pass |
271 | + |
272 | + def volume(self, display, vol): |
273 | + """ |
274 | + Change volume of current Media File |
275 | + """ |
276 | + pass |
277 | + |
278 | + def seek(self, display, seekVal): |
279 | + """ |
280 | + Change playing position of current Media File |
281 | + """ |
282 | + pass |
283 | + |
284 | + def reset(self, display): |
285 | + """ |
286 | + Remove the current loaded video |
287 | + """ |
288 | + pass |
289 | + |
290 | + def set_visible(self, display, status): |
291 | + """ |
292 | + Show/Hide the media widgets |
293 | + """ |
294 | + pass |
295 | + |
296 | + def update_ui(self, display): |
297 | + """ |
298 | + Do some ui related stuff (e.g. update the seek slider) |
299 | + """ |
300 | + pass |
301 | + |
302 | + def get_media_display_css(self): |
303 | + """ |
304 | + Add css style sheets to htmlbuilder |
305 | + """ |
306 | + return u'' |
307 | + |
308 | + def get_media_display_javascript(self): |
309 | + """ |
310 | + Add javascript functions to htmlbuilder |
311 | + """ |
312 | + return u'' |
313 | + |
314 | + def get_media_display_html(self): |
315 | + """ |
316 | + Add html code to htmlbuilder |
317 | + """ |
318 | + return u'' |
319 | |
320 | === modified file 'openlp/core/lib/plugin.py' |
321 | --- openlp/core/lib/plugin.py 2011-10-17 18:01:07 +0000 |
322 | +++ openlp/core/lib/plugin.py 2011-12-01 18:30:31 +0000 |
323 | @@ -168,6 +168,7 @@ |
324 | self.mediadock = plugin_helpers[u'toolbox'] |
325 | self.pluginManager = plugin_helpers[u'pluginmanager'] |
326 | self.formparent = plugin_helpers[u'formparent'] |
327 | + self.mediaController = plugin_helpers[u'mediacontroller'] |
328 | QtCore.QObject.connect(Receiver.get_receiver(), |
329 | QtCore.SIGNAL(u'%s_add_service_item' % self.name), |
330 | self.processAddServiceEvent) |
331 | @@ -395,4 +396,3 @@ |
332 | Add html code to htmlbuilder. |
333 | """ |
334 | return u'' |
335 | - |
336 | |
337 | === modified file 'openlp/core/lib/renderer.py' |
338 | --- openlp/core/lib/renderer.py 2011-11-02 20:09:06 +0000 |
339 | +++ openlp/core/lib/renderer.py 2011-12-01 18:30:31 +0000 |
340 | @@ -76,7 +76,7 @@ |
341 | self.theme_data = None |
342 | self.bg_frame = None |
343 | self.force_page = False |
344 | - self.display = MainDisplay(None, self.imageManager, False) |
345 | + self.display = MainDisplay(None, self.imageManager, False, self) |
346 | self.display.setup() |
347 | |
348 | def update_display(self): |
349 | @@ -87,7 +87,7 @@ |
350 | self._calculate_default() |
351 | if self.display: |
352 | self.display.close() |
353 | - self.display = MainDisplay(None, self.imageManager, False) |
354 | + self.display = MainDisplay(None, self.imageManager, False, self) |
355 | self.display.setup() |
356 | self.bg_frame = None |
357 | self.theme_data = None |
358 | |
359 | === modified file 'openlp/core/ui/__init__.py' |
360 | --- openlp/core/ui/__init__.py 2011-10-29 19:13:11 +0000 |
361 | +++ openlp/core/ui/__init__.py 2011-12-01 18:30:31 +0000 |
362 | @@ -77,10 +77,10 @@ |
363 | from filerenameform import FileRenameForm |
364 | from starttimeform import StartTimeForm |
365 | from screen import ScreenList |
366 | -from maindisplay import MainDisplay |
367 | +from maindisplay import MainDisplay, Display |
368 | from servicenoteform import ServiceNoteForm |
369 | from serviceitemeditform import ServiceItemEditForm |
370 | -from slidecontroller import SlideController |
371 | +from slidecontroller import SlideController, Controller |
372 | from splashscreen import SplashScreen |
373 | from generaltab import GeneralTab |
374 | from themestab import ThemesTab |
375 | |
376 | === modified file 'openlp/core/ui/maindisplay.py' |
377 | --- openlp/core/ui/maindisplay.py 2011-11-01 06:09:21 +0000 |
378 | +++ openlp/core/ui/maindisplay.py 2011-12-01 18:30:31 +0000 |
379 | @@ -31,7 +31,7 @@ |
380 | import logging |
381 | import os |
382 | |
383 | -from PyQt4 import QtCore, QtGui, QtWebKit |
384 | +from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL |
385 | from PyQt4.phonon import Phonon |
386 | |
387 | from openlp.core.lib import Receiver, build_html, ServiceItem, image_to_byte, \ |
388 | @@ -44,11 +44,13 @@ |
389 | #http://www.steveheffernan.com/html5-video-player/demo-video-player.html |
390 | #http://html5demos.com/two-videos |
391 | |
392 | -class MainDisplay(QtGui.QGraphicsView): |
393 | - """ |
394 | - This is the display screen. |
395 | - """ |
396 | - def __init__(self, parent, imageManager, live): |
397 | +class Display(QtGui.QGraphicsView): |
398 | + """ |
399 | + This is a general display screen class. Here the general display settings |
400 | + will done. It will be used as specialized classes by Main Display and |
401 | + Preview display. |
402 | + """ |
403 | + def __init__(self, parent, live, controller): |
404 | if live: |
405 | QtGui.QGraphicsView.__init__(self) |
406 | # Overwrite the parent() method. |
407 | @@ -56,12 +58,60 @@ |
408 | else: |
409 | QtGui.QGraphicsView.__init__(self, parent) |
410 | self.isLive = live |
411 | + self.controller = controller |
412 | + self.screen = {} |
413 | + self.plugins = PluginManager.get_instance().plugins |
414 | + self.setViewport(QtOpenGL.QGLWidget()) |
415 | + |
416 | + def setup(self): |
417 | + """ |
418 | + Set up and build the screen base |
419 | + """ |
420 | + log.debug(u'Start Display base setup (live = %s)' % self.isLive) |
421 | + self.setGeometry(self.screen[u'size']) |
422 | + log.debug(u'Setup webView') |
423 | + self.webView = QtWebKit.QWebView(self) |
424 | + self.webView.setGeometry(0, 0, |
425 | + self.screen[u'size'].width(), self.screen[u'size'].height()) |
426 | + self.webView.settings().setAttribute( |
427 | + QtWebKit.QWebSettings.PluginsEnabled, True) |
428 | + self.page = self.webView.page() |
429 | + self.frame = self.page.mainFrame() |
430 | + if self.isLive and log.getEffectiveLevel() == logging.DEBUG: |
431 | + self.webView.settings().setAttribute( |
432 | + QtWebKit.QWebSettings.DeveloperExtrasEnabled, True) |
433 | + QtCore.QObject.connect(self.webView, |
434 | + QtCore.SIGNAL(u'loadFinished(bool)'), self.isWebLoaded) |
435 | + self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) |
436 | + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) |
437 | + self.frame.setScrollBarPolicy(QtCore.Qt.Vertical, |
438 | + QtCore.Qt.ScrollBarAlwaysOff) |
439 | + self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal, |
440 | + QtCore.Qt.ScrollBarAlwaysOff) |
441 | + |
442 | + def resizeEvent(self, ev): |
443 | + self.webView.setGeometry(0, 0, |
444 | + self.width(), self.height()) |
445 | + |
446 | + def isWebLoaded(self): |
447 | + """ |
448 | + Called by webView event to show display is fully loaded |
449 | + """ |
450 | + log.debug(u'Webloaded') |
451 | + self.webLoaded = True |
452 | + |
453 | + |
454 | +class MainDisplay(Display): |
455 | + """ |
456 | + This is the display screen as a specialized class from the Display class |
457 | + """ |
458 | + def __init__(self, parent, imageManager, live, controller): |
459 | + Display.__init__(self, parent, live, controller) |
460 | self.imageManager = imageManager |
461 | self.screens = ScreenList.get_instance() |
462 | self.plugins = PluginManager.get_instance().plugins |
463 | self.rebuildCSS = False |
464 | self.hideMode = None |
465 | - self.videoHide = False |
466 | self.override = {} |
467 | self.retranslateUi() |
468 | self.mediaObject = None |
469 | @@ -81,9 +131,6 @@ |
470 | QtCore.QObject.connect(Receiver.get_receiver(), |
471 | QtCore.SIGNAL(u'live_display_show'), self.showDisplay) |
472 | QtCore.QObject.connect(Receiver.get_receiver(), |
473 | - QtCore.SIGNAL(u'openlp_phonon_creation'), |
474 | - self.createMediaObject) |
475 | - QtCore.QObject.connect(Receiver.get_receiver(), |
476 | QtCore.SIGNAL(u'update_display_css'), self.cssChanged) |
477 | QtCore.QObject.connect(Receiver.get_receiver(), |
478 | QtCore.SIGNAL(u'config_updated'), self.configChanged) |
479 | @@ -115,36 +162,9 @@ |
480 | Set up and build the output screen |
481 | """ |
482 | log.debug(u'Start MainDisplay setup (live = %s)' % self.isLive) |
483 | - self.usePhonon = QtCore.QSettings().value( |
484 | - u'media/use phonon', QtCore.QVariant(True)).toBool() |
485 | - self.phononActive = False |
486 | self.screen = self.screens.current |
487 | self.setVisible(False) |
488 | - self.setGeometry(self.screen[u'size']) |
489 | - self.videoWidget = Phonon.VideoWidget(self) |
490 | - self.videoWidget.setVisible(False) |
491 | - self.videoWidget.setGeometry(QtCore.QRect(0, 0, |
492 | - self.screen[u'size'].width(), self.screen[u'size'].height())) |
493 | - if self.isLive: |
494 | - if not self.firstTime: |
495 | - self.createMediaObject() |
496 | - log.debug(u'Setup webView') |
497 | - self.webView = QtWebKit.QWebView(self) |
498 | - self.webView.setGeometry(0, 0, |
499 | - self.screen[u'size'].width(), self.screen[u'size'].height()) |
500 | - self.page = self.webView.page() |
501 | - self.frame = self.page.mainFrame() |
502 | - if self.isLive and log.getEffectiveLevel() == logging.DEBUG: |
503 | - self.webView.settings().setAttribute( |
504 | - QtWebKit.QWebSettings.DeveloperExtrasEnabled, True) |
505 | - QtCore.QObject.connect(self.webView, |
506 | - QtCore.SIGNAL(u'loadFinished(bool)'), self.isWebLoaded) |
507 | - self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) |
508 | - self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) |
509 | - self.frame.setScrollBarPolicy(QtCore.Qt.Vertical, |
510 | - QtCore.Qt.ScrollBarAlwaysOff) |
511 | - self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal, |
512 | - QtCore.Qt.ScrollBarAlwaysOff) |
513 | + Display.setup(self) |
514 | if self.isLive: |
515 | # Build the initial frame. |
516 | image_file = QtCore.QSettings().value(u'advanced/default image', |
517 | @@ -180,24 +200,6 @@ |
518 | self.primary = True |
519 | log.debug(u'Finished MainDisplay setup') |
520 | |
521 | - def createMediaObject(self): |
522 | - self.firstTime = False |
523 | - log.debug(u'Creating Phonon objects - Start for %s', self.isLive) |
524 | - self.mediaObject = Phonon.MediaObject(self) |
525 | - self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject) |
526 | - Phonon.createPath(self.mediaObject, self.videoWidget) |
527 | - Phonon.createPath(self.mediaObject, self.audio) |
528 | - QtCore.QObject.connect(self.mediaObject, |
529 | - QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'), |
530 | - self.videoState) |
531 | - QtCore.QObject.connect(self.mediaObject, |
532 | - QtCore.SIGNAL(u'finished()'), |
533 | - self.videoFinished) |
534 | - QtCore.QObject.connect(self.mediaObject, |
535 | - QtCore.SIGNAL(u'tick(qint64)'), |
536 | - self.videoTick) |
537 | - log.debug(u'Creating Phonon objects - Finished for %s', self.isLive) |
538 | - |
539 | def text(self, slide): |
540 | """ |
541 | Add the slide text from slideController |
542 | @@ -221,8 +223,8 @@ |
543 | The text to be displayed. |
544 | """ |
545 | log.debug(u'alert to display') |
546 | - if self.height() != self.screen[u'size'].height() or not \ |
547 | - self.isVisible() or self.videoWidget.isVisible(): |
548 | + if self.height() != self.screen[u'size'].height() or \ |
549 | + not self.isVisible(): |
550 | shrink = True |
551 | js = u'show_alert("%s", "%s")' % ( |
552 | text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'), |
553 | @@ -233,22 +235,18 @@ |
554 | text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"')) |
555 | height = self.frame.evaluateJavaScript(js) |
556 | if shrink: |
557 | - if self.phononActive: |
558 | - shrinkItem = self.webView |
559 | - else: |
560 | - shrinkItem = self |
561 | if text: |
562 | alert_height = int(height.toString()) |
563 | - shrinkItem.resize(self.width(), alert_height) |
564 | - shrinkItem.setVisible(True) |
565 | + self.resize(self.width(), alert_height) |
566 | + self.setVisible(True) |
567 | if location == AlertLocation.Middle: |
568 | - shrinkItem.move(self.screen[u'size'].left(), |
569 | + self.move(self.screen[u'size'].left(), |
570 | (self.screen[u'size'].height() - alert_height) / 2) |
571 | elif location == AlertLocation.Bottom: |
572 | - shrinkItem.move(self.screen[u'size'].left(), |
573 | + self.move(self.screen[u'size'].left(), |
574 | self.screen[u'size'].height() - alert_height) |
575 | else: |
576 | - shrinkItem.setVisible(False) |
577 | + self.setVisible(False) |
578 | self.setGeometry(self.screen[u'size']) |
579 | |
580 | def directImage(self, name, path, background): |
581 | @@ -276,7 +274,7 @@ |
582 | """ |
583 | log.debug(u'image to display') |
584 | image = self.imageManager.get_image_bytes(name) |
585 | - self.resetVideo() |
586 | + self.controller.mediaController.video_reset(self.controller) |
587 | self.displayImage(image) |
588 | |
589 | def displayImage(self, image): |
590 | @@ -303,135 +301,6 @@ |
591 | # clear the cache |
592 | self.override = {} |
593 | |
594 | - def resetVideo(self): |
595 | - """ |
596 | - Used after Video plugin has changed the background |
597 | - """ |
598 | - log.debug(u'resetVideo') |
599 | - if self.phononActive: |
600 | - self.mediaObject.stop() |
601 | - self.mediaObject.clearQueue() |
602 | - self.webView.setVisible(True) |
603 | - self.videoWidget.setVisible(False) |
604 | - self.phononActive = False |
605 | - else: |
606 | - self.frame.evaluateJavaScript(u'show_video("close");') |
607 | - self.override = {} |
608 | - |
609 | - def videoPlay(self): |
610 | - """ |
611 | - Responds to the request to play a loaded video |
612 | - """ |
613 | - log.debug(u'videoPlay') |
614 | - if self.phononActive: |
615 | - self.mediaObject.play() |
616 | - else: |
617 | - self.frame.evaluateJavaScript(u'show_video("play");') |
618 | - # show screen |
619 | - if self.isLive: |
620 | - self.setVisible(True) |
621 | - |
622 | - def videoPause(self): |
623 | - """ |
624 | - Responds to the request to pause a loaded video |
625 | - """ |
626 | - log.debug(u'videoPause') |
627 | - if self.phononActive: |
628 | - self.mediaObject.pause() |
629 | - else: |
630 | - self.frame.evaluateJavaScript(u'show_video("pause");') |
631 | - |
632 | - def videoStop(self): |
633 | - """ |
634 | - Responds to the request to stop a loaded video |
635 | - """ |
636 | - log.debug(u'videoStop') |
637 | - if self.phononActive: |
638 | - self.mediaObject.stop() |
639 | - else: |
640 | - self.frame.evaluateJavaScript(u'show_video("stop");') |
641 | - |
642 | - def videoVolume(self, volume): |
643 | - """ |
644 | - Changes the volume of a running video |
645 | - """ |
646 | - log.debug(u'videoVolume %d' % volume) |
647 | - vol = float(volume) / float(10) |
648 | - if self.phononActive: |
649 | - self.audio.setVolume(vol) |
650 | - else: |
651 | - self.frame.evaluateJavaScript(u'show_video(null, null, %s);' % |
652 | - str(vol)) |
653 | - |
654 | - def video(self, videoPath, volume, isBackground=False): |
655 | - """ |
656 | - Loads and starts a video to run with the option of sound |
657 | - """ |
658 | - # We request a background video but have no service Item |
659 | - if isBackground and not hasattr(self, u'serviceItem'): |
660 | - return False |
661 | - if not self.mediaObject: |
662 | - self.createMediaObject() |
663 | - log.debug(u'video') |
664 | - self.webLoaded = True |
665 | - self.setGeometry(self.screen[u'size']) |
666 | - # We are running a background theme |
667 | - self.override[u'theme'] = u'' |
668 | - self.override[u'video'] = True |
669 | - vol = float(volume) / float(10) |
670 | - if isBackground or not self.usePhonon: |
671 | - js = u'show_video("init", "%s", %s, true); show_video("play");' % \ |
672 | - (videoPath.replace(u'\\', u'\\\\'), str(vol)) |
673 | - self.frame.evaluateJavaScript(js) |
674 | - else: |
675 | - self.phononActive = True |
676 | - self.mediaObject.stop() |
677 | - self.mediaObject.clearQueue() |
678 | - self.mediaObject.setCurrentSource(Phonon.MediaSource(videoPath)) |
679 | - # Need the timer to trigger set the trigger to 200ms |
680 | - # Value taken from web documentation. |
681 | - if self.serviceItem.end_time != 0: |
682 | - self.mediaObject.setTickInterval(200) |
683 | - self.mediaObject.play() |
684 | - self.webView.setVisible(False) |
685 | - self.videoWidget.setVisible(True) |
686 | - self.audio.setVolume(vol) |
687 | - return True |
688 | - |
689 | - def videoState(self, newState, oldState): |
690 | - """ |
691 | - Start the video at a predetermined point. |
692 | - """ |
693 | - if newState == Phonon.PlayingState \ |
694 | - and oldState != Phonon.PausedState \ |
695 | - and self.serviceItem.start_time > 0: |
696 | - # set start time in milliseconds |
697 | - self.mediaObject.seek(self.serviceItem.start_time * 1000) |
698 | - |
699 | - def videoFinished(self): |
700 | - """ |
701 | - Blank the Video when it has finished so the final frame is not left |
702 | - hanging |
703 | - """ |
704 | - self.videoStop() |
705 | - self.hideDisplay(HideMode.Blank) |
706 | - self.phononActive = False |
707 | - self.videoHide = True |
708 | - |
709 | - def videoTick(self, tick): |
710 | - """ |
711 | - Triggered on video tick every 200 milli seconds |
712 | - """ |
713 | - if tick > self.serviceItem.end_time * 1000: |
714 | - self.videoFinished() |
715 | - |
716 | - def isWebLoaded(self): |
717 | - """ |
718 | - Called by webView event to show display is fully loaded |
719 | - """ |
720 | - log.debug(u'Webloaded') |
721 | - self.webLoaded = True |
722 | - |
723 | def preview(self): |
724 | """ |
725 | Generates a preview of the image displayed. |
726 | @@ -511,16 +380,12 @@ |
727 | if serviceItem.foot_text: |
728 | self.footer(serviceItem.foot_text) |
729 | # if was hidden keep it hidden |
730 | - if self.hideMode and self.isLive: |
731 | + if self.hideMode and self.isLive and not serviceItem.is_media(): |
732 | if QtCore.QSettings().value(u'general/auto unblank', |
733 | QtCore.QVariant(False)).toBool(): |
734 | Receiver.send_message(u'slidecontroller_live_unblank') |
735 | else: |
736 | self.hideDisplay(self.hideMode) |
737 | - # display hidden for video end we have a new item so must be shown |
738 | - if self.videoHide and self.isLive: |
739 | - self.videoHide = False |
740 | - self.showDisplay() |
741 | self.__hideMouse() |
742 | |
743 | def footer(self, text): |
744 | @@ -538,8 +403,6 @@ |
745 | Store the images so they can be replaced when required |
746 | """ |
747 | log.debug(u'hideDisplay mode = %d', mode) |
748 | - if self.phononActive: |
749 | - self.videoPause() |
750 | if mode == HideMode.Screen: |
751 | self.frame.evaluateJavaScript(u'show_blank("desktop");') |
752 | self.setVisible(False) |
753 | @@ -550,7 +413,6 @@ |
754 | if mode != HideMode.Screen: |
755 | if self.isHidden(): |
756 | self.setVisible(True) |
757 | - if self.phononActive: |
758 | self.webView.setVisible(True) |
759 | self.hideMode = mode |
760 | |
761 | @@ -564,9 +426,6 @@ |
762 | self.frame.evaluateJavaScript('show_blank("show");') |
763 | if self.isHidden(): |
764 | self.setVisible(True) |
765 | - if self.phononActive: |
766 | - self.webView.setVisible(False) |
767 | - self.videoPlay() |
768 | self.hideMode = None |
769 | # Trigger actions when display is active again |
770 | if self.isLive: |
771 | |
772 | === modified file 'openlp/core/ui/mainwindow.py' |
773 | --- openlp/core/ui/mainwindow.py 2011-10-22 11:09:01 +0000 |
774 | +++ openlp/core/ui/mainwindow.py 2011-12-01 18:30:31 +0000 |
775 | @@ -42,6 +42,7 @@ |
776 | from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ |
777 | ThemeManager, SlideController, PluginForm, MediaDockManager, \ |
778 | ShortcutListForm, FormattingTagForm |
779 | +from openlp.core.ui.media import MediaController |
780 | from openlp.core.utils import AppLocation, add_actions, LanguageManager, \ |
781 | get_application_version, delete_file |
782 | from openlp.core.utils.actions import ActionList, CategoryOrder |
783 | @@ -557,6 +558,7 @@ |
784 | self.pluginManager = PluginManager(pluginpath) |
785 | self.pluginHelpers = {} |
786 | self.imageManager = ImageManager() |
787 | + self.mediaController = MediaController(self) |
788 | # Set up the interface |
789 | self.setupUi(self) |
790 | # Load settings after setupUi so default UI sizes are overwritten |
791 | @@ -644,6 +646,7 @@ |
792 | self.pluginHelpers[u'toolbox'] = self.mediaDockManager |
793 | self.pluginHelpers[u'pluginmanager'] = self.pluginManager |
794 | self.pluginHelpers[u'formparent'] = self |
795 | + self.pluginHelpers[u'mediacontroller'] = self.mediaController |
796 | self.pluginManager.find_plugins(pluginpath, self.pluginHelpers) |
797 | # hook methods have to happen after find_plugins. Find plugins needs |
798 | # the controllers hence the hooks have moved from setupUI() to here |
799 | |
800 | === added directory 'openlp/core/ui/media' |
801 | === added file 'openlp/core/ui/media/__init__.py' |
802 | --- openlp/core/ui/media/__init__.py 1970-01-01 00:00:00 +0000 |
803 | +++ openlp/core/ui/media/__init__.py 2011-12-01 18:30:31 +0000 |
804 | @@ -0,0 +1,65 @@ |
805 | +# -*- coding: utf-8 -*- |
806 | +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 |
807 | + |
808 | +############################################################################### |
809 | +# OpenLP - Open Source Lyrics Projection # |
810 | +# --------------------------------------------------------------------------- # |
811 | +# Copyright (c) 2008-2011 Raoul Snyman # |
812 | +# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan # |
813 | +# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, # |
814 | +# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias # |
815 | +# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # |
816 | +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund # |
817 | +# --------------------------------------------------------------------------- # |
818 | +# This program is free software; you can redistribute it and/or modify it # |
819 | +# under the terms of the GNU General Public License as published by the Free # |
820 | +# Software Foundation; version 2 of the License. # |
821 | +# # |
822 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
823 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
824 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
825 | +# more details. # |
826 | +# # |
827 | +# You should have received a copy of the GNU General Public License along # |
828 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
829 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
830 | +############################################################################### |
831 | + |
832 | +class MediaState(object): |
833 | + """ |
834 | + An enumeration for possible States of the Media Player (copied partially |
835 | + from Phonon::State) |
836 | + """ |
837 | + Loading = 0 |
838 | + Stopped = 1 |
839 | + Playing = 2 |
840 | + Paused = 4 |
841 | + Off = 6 |
842 | + |
843 | + |
844 | +class MediaType(object): |
845 | + """ |
846 | + An enumeration of possibible Media Types |
847 | + """ |
848 | + Unused = 0 |
849 | + Audio = 1 |
850 | + Video = 2 |
851 | + CD = 3 |
852 | + DVD = 4 |
853 | + Folder = 5 |
854 | + |
855 | + |
856 | +class MediaInfo(object): |
857 | + """ |
858 | + This class hold the media related infos |
859 | + """ |
860 | + file_info = None |
861 | + volume = 100 |
862 | + is_flash = False |
863 | + is_background = False |
864 | + length = 0 |
865 | + start_time = 0 |
866 | + end_time = 0 |
867 | + media_type = MediaType() |
868 | + |
869 | +from mediacontroller import MediaController |
870 | |
871 | === added file 'openlp/core/ui/media/mediacontroller.py' |
872 | --- openlp/core/ui/media/mediacontroller.py 1970-01-01 00:00:00 +0000 |
873 | +++ openlp/core/ui/media/mediacontroller.py 2011-12-01 18:30:31 +0000 |
874 | @@ -0,0 +1,576 @@ |
875 | +# -*- coding: utf-8 -*- |
876 | +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 |
877 | + |
878 | +############################################################################### |
879 | +# OpenLP - Open Source Lyrics Projection # |
880 | +# --------------------------------------------------------------------------- # |
881 | +# Copyright (c) 2008-2011 Raoul Snyman # |
882 | +# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan # |
883 | +# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, # |
884 | +# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias # |
885 | +# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # |
886 | +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund # |
887 | +# --------------------------------------------------------------------------- # |
888 | +# This program is free software; you can redistribute it and/or modify it # |
889 | +# under the terms of the GNU General Public License as published by the Free # |
890 | +# Software Foundation; version 2 of the License. # |
891 | +# # |
892 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
893 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
894 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
895 | +# more details. # |
896 | +# # |
897 | +# You should have received a copy of the GNU General Public License along # |
898 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
899 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
900 | +############################################################################### |
901 | + |
902 | +import logging |
903 | + |
904 | +import sys, os,time |
905 | +from PyQt4 import QtCore, QtGui, QtWebKit |
906 | + |
907 | +from openlp.core.lib import OpenLPToolbar, Receiver, translate |
908 | +from openlp.core.lib.mediaplayer import MediaPlayer |
909 | +from openlp.core.lib.ui import UiStrings, critical_error_message_box |
910 | +from openlp.core.ui.media import MediaState, MediaInfo, MediaType |
911 | +from openlp.core.utils import AppLocation |
912 | + |
913 | +log = logging.getLogger(__name__) |
914 | + |
915 | +class MediaController(object): |
916 | + """ |
917 | + The implementation of the Media Controller. The Media Controller adds an own |
918 | + class for every Player. Currently these are QtWebkit, Phonon and planed Vlc. |
919 | + """ |
920 | + |
921 | + def __init__(self, parent): |
922 | + self.parent = parent |
923 | + self.mediaPlayers = {} |
924 | + self.controller = [] |
925 | + self.overridenPlayer = '' |
926 | + self.curDisplayMediaPlayer = {} |
927 | + # Timer for video state |
928 | + self.timer = QtCore.QTimer() |
929 | + self.timer.setInterval(200) |
930 | + self.withLivePreview = False |
931 | + self.check_available_media_players() |
932 | + # Signals |
933 | + QtCore.QObject.connect(self.timer, |
934 | + QtCore.SIGNAL("timeout()"), self.video_state) |
935 | + QtCore.QObject.connect(Receiver.get_receiver(), |
936 | + QtCore.SIGNAL(u'media_playback_play'), self.video_play) |
937 | + QtCore.QObject.connect(Receiver.get_receiver(), |
938 | + QtCore.SIGNAL(u'media_playback_pause'), self.video_pause) |
939 | + QtCore.QObject.connect(Receiver.get_receiver(), |
940 | + QtCore.SIGNAL(u'media_playback_stop'), self.video_stop) |
941 | + QtCore.QObject.connect(Receiver.get_receiver(), |
942 | + QtCore.SIGNAL(u'seek_slider'), self.video_seek) |
943 | + QtCore.QObject.connect(Receiver.get_receiver(), |
944 | + QtCore.SIGNAL(u'volume_slider'), self.video_volume) |
945 | + QtCore.QObject.connect(Receiver.get_receiver(), |
946 | + QtCore.SIGNAL(u'media_hide'), self.video_hide) |
947 | + QtCore.QObject.connect(Receiver.get_receiver(), |
948 | + QtCore.SIGNAL(u'media_blank'), self.video_blank) |
949 | + QtCore.QObject.connect(Receiver.get_receiver(), |
950 | + QtCore.SIGNAL(u'media_unblank'), self.video_unblank) |
951 | + QtCore.QObject.connect(Receiver.get_receiver(), |
952 | + QtCore.SIGNAL(u'media_override_player'), self.override_player) |
953 | + # Signals for background video |
954 | + QtCore.QObject.connect(Receiver.get_receiver(), |
955 | + QtCore.SIGNAL(u'songs_hide'), self.video_hide) |
956 | + QtCore.QObject.connect(Receiver.get_receiver(), |
957 | + QtCore.SIGNAL(u'songs_unblank'), self.video_unblank) |
958 | + QtCore.QObject.connect(Receiver.get_receiver(), |
959 | + QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.set_active_players) |
960 | + |
961 | + def set_active_players(self): |
962 | + playerSettings = str(QtCore.QSettings().value(u'media/players', |
963 | + QtCore.QVariant(u'webkit')).toString()) |
964 | + if len(playerSettings) == 0: |
965 | + playerSettings = u'webkit' |
966 | + savedPlayers = playerSettings.split(u',') |
967 | + for player in self.mediaPlayers.keys(): |
968 | + if player in savedPlayers: |
969 | + self.mediaPlayers[player].isActive = True |
970 | + else: |
971 | + self.mediaPlayers[player].isActive = False |
972 | + |
973 | + def register_controllers(self, controller): |
974 | + """ |
975 | + Register each media Player controller (Webkit, Phonon, etc) and store |
976 | + for later use |
977 | + """ |
978 | + if controller.check_available(): |
979 | + self.mediaPlayers[controller.name] = controller |
980 | + |
981 | + def check_available_media_players(self): |
982 | + """ |
983 | + Check to see if we have any media Player's available. If Not do not |
984 | + install the plugin. |
985 | + """ |
986 | + log.debug(u'check_available_media_players') |
987 | + controller_dir = os.path.join( |
988 | + AppLocation.get_directory(AppLocation.AppDir), |
989 | + u'core', u'ui', u'media') |
990 | + for filename in os.listdir(controller_dir): |
991 | + if filename.endswith(u'player.py') and \ |
992 | + not filename == 'media_player.py': |
993 | + path = os.path.join(controller_dir, filename) |
994 | + if os.path.isfile(path): |
995 | + modulename = u'openlp.core.ui.media.' + \ |
996 | + os.path.splitext(filename)[0] |
997 | + log.debug(u'Importing controller %s', modulename) |
998 | + try: |
999 | + __import__(modulename, globals(), locals(), []) |
1000 | + except ImportError: |
1001 | + log.warn(u'Failed to import %s on path %s', |
1002 | + modulename, path) |
1003 | + controller_classes = MediaPlayer.__subclasses__() |
1004 | + for controller_class in controller_classes: |
1005 | + controller = controller_class(self) |
1006 | + self.register_controllers(controller) |
1007 | + if self.mediaPlayers: |
1008 | + playerSettings = str(QtCore.QSettings().value(u'media/players', |
1009 | + QtCore.QVariant(u'webkit')).toString()) |
1010 | + savedPlayers = playerSettings.split(u',') |
1011 | + invalidMediaPlayers = [mediaPlayer for mediaPlayer in savedPlayers \ |
1012 | + if not mediaPlayer in self.mediaPlayers] |
1013 | + if len(invalidMediaPlayers)>0: |
1014 | + [savedPlayers.remove(invalidPlayer) for invalidPlayer in invalidMediaPlayers] |
1015 | + newPlayerSetting = u','.join(savedPlayers) |
1016 | + QtCore.QSettings().setValue(u'media/players', |
1017 | + QtCore.QVariant(newPlayerSetting)) |
1018 | + self.set_active_players() |
1019 | + return True |
1020 | + else: |
1021 | + return False |
1022 | + |
1023 | + def video_state(self): |
1024 | + """ |
1025 | + Check if there is a running media Player and do updating stuff (e.g. |
1026 | + update the UI) |
1027 | + """ |
1028 | + if len(self.curDisplayMediaPlayer.keys()) == 0: |
1029 | + self.timer.stop() |
1030 | + else: |
1031 | + for display in self.curDisplayMediaPlayer.keys(): |
1032 | + self.curDisplayMediaPlayer[display].resize(display) |
1033 | + self.curDisplayMediaPlayer[display].update_ui(display) |
1034 | + if self.curDisplayMediaPlayer[display] \ |
1035 | + .state == MediaState.Playing: |
1036 | + return |
1037 | + self.timer.stop() |
1038 | + |
1039 | + def get_media_display_css(self): |
1040 | + """ |
1041 | + Add css style sheets to htmlbuilder |
1042 | + """ |
1043 | + css = u'' |
1044 | + for player in self.mediaPlayers.values(): |
1045 | + if player.isActive: |
1046 | + css += player.get_media_display_css() |
1047 | + return css |
1048 | + |
1049 | + def get_media_display_javascript(self): |
1050 | + """ |
1051 | + Add javascript functions to htmlbuilder |
1052 | + """ |
1053 | + js = u'' |
1054 | + for player in self.mediaPlayers.values(): |
1055 | + if player.isActive: |
1056 | + js += player.get_media_display_javascript() |
1057 | + return js |
1058 | + |
1059 | + def get_media_display_html(self): |
1060 | + """ |
1061 | + Add html code to htmlbuilder |
1062 | + """ |
1063 | + html = u'' |
1064 | + for player in self.mediaPlayers.values(): |
1065 | + if player.isActive: |
1066 | + html += player.get_media_display_html() |
1067 | + return html |
1068 | + |
1069 | + def add_controller_items(self, controller, control_panel): |
1070 | + self.controller.append(controller) |
1071 | + self.setup_generic_controls(controller, control_panel) |
1072 | + self.setup_special_controls(controller, control_panel) |
1073 | + |
1074 | + def setup_generic_controls(self, controller, control_panel): |
1075 | + """ |
1076 | + Add generic media control items (valid for all types of medias) |
1077 | + """ |
1078 | + controller.media_info = MediaInfo() |
1079 | + # Build a Media ToolBar |
1080 | + controller.mediabar = OpenLPToolbar(controller) |
1081 | + controller.mediabar.addToolbarButton( |
1082 | + u'media_playback_play', u':/slides/media_playback_start.png', |
1083 | + translate('OpenLP.SlideController', 'Start playing media'), |
1084 | + controller.sendToPlugins) |
1085 | + controller.mediabar.addToolbarButton( |
1086 | + u'media_playback_pause', u':/slides/media_playback_pause.png', |
1087 | + translate('OpenLP.SlideController', 'Pause playing media'), |
1088 | + controller.sendToPlugins) |
1089 | + controller.mediabar.addToolbarButton( |
1090 | + u'media_playback_stop', u':/slides/media_playback_stop.png', |
1091 | + translate('OpenLP.SlideController', 'Stop playing media'), |
1092 | + controller.sendToPlugins) |
1093 | + # Build the seekSlider. |
1094 | + controller.seekSlider = QtGui.QSlider(QtCore.Qt.Horizontal) |
1095 | + controller.seekSlider.setMaximum(1000) |
1096 | + controller.seekSlider.setToolTip(translate( |
1097 | + 'OpenLP.SlideController', 'Video position.')) |
1098 | + controller.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24)) |
1099 | + controller.seekSlider.setObjectName(u'seek_slider') |
1100 | + controller.mediabar.addToolbarWidget(u'Seek Slider', |
1101 | + controller.seekSlider) |
1102 | + # Build the volumeSlider. |
1103 | + controller.volumeSlider = QtGui.QSlider(QtCore.Qt.Horizontal) |
1104 | + controller.volumeSlider.setTickInterval(10) |
1105 | + controller.volumeSlider.setTickPosition(QtGui.QSlider.TicksAbove) |
1106 | + controller.volumeSlider.setMinimum(0) |
1107 | + controller.volumeSlider.setMaximum(100) |
1108 | + controller.volumeSlider.setToolTip(translate( |
1109 | + 'OpenLP.SlideController', 'Audio Volume.')) |
1110 | + controller.volumeSlider.setValue(controller.media_info.volume) |
1111 | + controller.volumeSlider.setGeometry(QtCore.QRect(90, 160, 221, 24)) |
1112 | + controller.volumeSlider.setObjectName(u'volume_slider') |
1113 | + controller.mediabar.addToolbarWidget(u'Audio Volume', |
1114 | + controller.volumeSlider) |
1115 | + control_panel.addWidget(controller.mediabar) |
1116 | + controller.mediabar.setVisible(False) |
1117 | + # Signals |
1118 | + QtCore.QObject.connect(controller.seekSlider, |
1119 | + QtCore.SIGNAL(u'sliderMoved(int)'), controller.sendToPlugins) |
1120 | + QtCore.QObject.connect(controller.volumeSlider, |
1121 | + QtCore.SIGNAL(u'sliderMoved(int)'), controller.sendToPlugins) |
1122 | + |
1123 | + def setup_special_controls(self, controller, control_panel): |
1124 | + """ |
1125 | + Special media Toolbars will be created here (e.g. for DVD Playback) |
1126 | + """ |
1127 | + controller.media_info = MediaInfo() |
1128 | + # TODO: add Toolbar for DVD, ... |
1129 | + |
1130 | + def setup_display(self, display): |
1131 | + """ |
1132 | + After a new display is configured, all media related widget will be |
1133 | + created too |
1134 | + """ |
1135 | + # clean up possible running old media files |
1136 | + self.finalise() |
1137 | + # update player status |
1138 | + self.set_active_players() |
1139 | + display.hasAudio = True |
1140 | + if not self.withLivePreview and \ |
1141 | + display == self.parent.liveController.previewDisplay: |
1142 | + return |
1143 | + if display == self.parent.previewController.previewDisplay or \ |
1144 | + display == self.parent.liveController.previewDisplay: |
1145 | + display.hasAudio = False |
1146 | + for player in self.mediaPlayers.values(): |
1147 | + if player.isActive: |
1148 | + player.setup(display) |
1149 | + |
1150 | + def set_controls_visible(self, controller, value): |
1151 | + # Generic controls |
1152 | + controller.mediabar.setVisible(value) |
1153 | + # Special controls: Here media type specific Controls will be enabled |
1154 | + # (e.g. for DVD control, ...) |
1155 | + # TODO |
1156 | + |
1157 | + def resize(self, controller, display, player): |
1158 | + """ |
1159 | + After Mainwindow changes or Splitter moved all related media widgets |
1160 | + have to be resized |
1161 | + """ |
1162 | + player.resize(display) |
1163 | + |
1164 | + def video(self, controller, file, muted, isBackground): |
1165 | + """ |
1166 | + Loads and starts a video to run with the option of sound |
1167 | + """ |
1168 | + log.debug(u'video') |
1169 | + isValid = False |
1170 | + # stop running videos |
1171 | + self.video_reset(controller) |
1172 | + controller.media_info = MediaInfo() |
1173 | + if muted: |
1174 | + controller.media_info.volume = 0 |
1175 | + else: |
1176 | + controller.media_info.volume = controller.volumeSlider.value() |
1177 | + controller.media_info.file_info = QtCore.QFileInfo(file) |
1178 | + controller.media_info.is_background = isBackground |
1179 | + display = None |
1180 | + if controller.isLive: |
1181 | + if self.withLivePreview and controller.previewDisplay: |
1182 | + display = controller.previewDisplay |
1183 | + isValid = self.check_file_type(controller, display) |
1184 | + display = controller.display |
1185 | + isValid = self.check_file_type(controller, display) |
1186 | + display.override[u'theme'] = u'' |
1187 | + display.override[u'video'] = True |
1188 | + controller.media_info.start_time = display.serviceItem.start_time |
1189 | + controller.media_info.end_time = display.serviceItem.end_time |
1190 | + elif controller.previewDisplay: |
1191 | + display = controller.previewDisplay |
1192 | + isValid = self.check_file_type(controller, display) |
1193 | + if not isValid: |
1194 | + # Media could not be loaded correctly |
1195 | + critical_error_message_box( |
1196 | + translate('MediaPlugin.MediaItem', 'Unsupported File'), |
1197 | + unicode(translate('MediaPlugin.MediaItem', |
1198 | + 'Unsupported File'))) |
1199 | + return False |
1200 | + # dont care about actual theme, set a black background |
1201 | + if controller.isLive and ( \ |
1202 | + controller.media_info.is_background == False): |
1203 | + display.frame.evaluateJavaScript(u'show_video( \ |
1204 | + "setBackBoard", null, null, null,"visible");') |
1205 | + # now start playing |
1206 | + if controller.isLive and \ |
1207 | + (QtCore.QSettings().value(u'general/auto unblank', |
1208 | + QtCore.QVariant(False)).toBool() or \ |
1209 | + controller.media_info.is_background == True) or \ |
1210 | + controller.isLive == False: |
1211 | + if not self.video_play([controller]): |
1212 | + critical_error_message_box( |
1213 | + translate('MediaPlugin.MediaItem', 'Unsupported File'), |
1214 | + unicode(translate('MediaPlugin.MediaItem', |
1215 | + 'Unsupported File'))) |
1216 | + return False |
1217 | + self.set_controls_visible(controller, True) |
1218 | + log.debug(u'use %s controller' % self.curDisplayMediaPlayer[display]) |
1219 | + return True |
1220 | + |
1221 | + def check_file_type(self, controller, display): |
1222 | + """ |
1223 | + Used to choose the right media Player type from the prioritized Player list |
1224 | + """ |
1225 | + playerSettings = str(QtCore.QSettings().value(u'media/players', |
1226 | + QtCore.QVariant(u'webkit')).toString()) |
1227 | + usedPlayers = playerSettings.split(u',') |
1228 | + if QtCore.QSettings().value(u'media/override player', |
1229 | + QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked: |
1230 | + if self.overridenPlayer != '': |
1231 | + usedPlayers = [self.overridenPlayer] |
1232 | + if controller.media_info.file_info.isFile(): |
1233 | + suffix = u'*.%s' % controller.media_info.file_info.suffix().toLower() |
1234 | + for title in usedPlayers: |
1235 | + player = self.mediaPlayers[title] |
1236 | + if suffix in player.video_extensions_list: |
1237 | + if not controller.media_info.is_background or \ |
1238 | + controller.media_info.is_background and player.canBackground: |
1239 | + self.resize(controller, display, player) |
1240 | + if player.load(display): |
1241 | + self.curDisplayMediaPlayer[display] = player |
1242 | + controller.media_info.media_type = MediaType.Video |
1243 | + return True |
1244 | + if suffix in player.audio_extensions_list: |
1245 | + if player.load(display): |
1246 | + self.curDisplayMediaPlayer[display] = player |
1247 | + controller.media_info.media_type = MediaType.Audio |
1248 | + return True |
1249 | + else: |
1250 | + for title in usedPlayers: |
1251 | + player = self.mediaPlayers[title] |
1252 | + if player.canFolder: |
1253 | + self.resize(controller, display, player) |
1254 | + if player.load(display): |
1255 | + self.curDisplayMediaPlayer[display] = player |
1256 | + controller.media_info.media_type = MediaType.Video |
1257 | + return True |
1258 | + # no valid player found |
1259 | + return False |
1260 | + |
1261 | + def video_play(self, msg, status=True): |
1262 | + """ |
1263 | + Responds to the request to play a loaded video |
1264 | + |
1265 | + ``msg`` |
1266 | + First element is the controller which should be used |
1267 | + """ |
1268 | + log.debug(u'video_play') |
1269 | + controller = msg[0] |
1270 | + for display in self.curDisplayMediaPlayer.keys(): |
1271 | + if display.controller == controller: |
1272 | + if not self.curDisplayMediaPlayer[display].play(display): |
1273 | + return False |
1274 | + if status: |
1275 | + display.frame.evaluateJavaScript(u'show_blank("desktop");') |
1276 | + self.curDisplayMediaPlayer[display].set_visible(display, True) |
1277 | + if controller.isLive: |
1278 | + if controller.hideMenu.defaultAction().isChecked(): |
1279 | + controller.hideMenu.defaultAction().trigger() |
1280 | + # Start Timer for ui updates |
1281 | + if not self.timer.isActive(): |
1282 | + self.timer.start() |
1283 | + return True |
1284 | + |
1285 | + def video_pause(self, msg): |
1286 | + """ |
1287 | + Responds to the request to pause a loaded video |
1288 | + |
1289 | + ``msg`` |
1290 | + First element is the controller which should be used |
1291 | + """ |
1292 | + log.debug(u'video_pause') |
1293 | + controller = msg[0] |
1294 | + for display in self.curDisplayMediaPlayer.keys(): |
1295 | + if display.controller == controller: |
1296 | + self.curDisplayMediaPlayer[display].pause(display) |
1297 | + |
1298 | + def video_stop(self, msg): |
1299 | + """ |
1300 | + Responds to the request to stop a loaded video |
1301 | + |
1302 | + ``msg`` |
1303 | + First element is the controller which should be used |
1304 | + """ |
1305 | + log.debug(u'video_stop') |
1306 | + controller = msg[0] |
1307 | + for display in self.curDisplayMediaPlayer.keys(): |
1308 | + if display.controller == controller: |
1309 | + display.frame.evaluateJavaScript(u'show_blank("black");') |
1310 | + self.curDisplayMediaPlayer[display].stop(display) |
1311 | + self.curDisplayMediaPlayer[display].set_visible(display, False) |
1312 | + |
1313 | + def video_volume(self, msg): |
1314 | + """ |
1315 | + Changes the volume of a running video |
1316 | + |
1317 | + ``msg`` |
1318 | + First element is the controller which should be used |
1319 | + """ |
1320 | + controller = msg[0] |
1321 | + vol = msg[1][0] |
1322 | + log.debug(u'video_volume %d' % vol) |
1323 | + for display in self.curDisplayMediaPlayer.keys(): |
1324 | + if display.controller == controller: |
1325 | + self.curDisplayMediaPlayer[display].volume(display, vol) |
1326 | + |
1327 | + def video_seek(self, msg): |
1328 | + """ |
1329 | + Responds to the request to change the seek Slider of a loaded video |
1330 | + |
1331 | + ``msg`` |
1332 | + First element is the controller which should be used |
1333 | + Second element is a list with the seek Value as first element |
1334 | + """ |
1335 | + log.debug(u'video_seek') |
1336 | + controller = msg[0] |
1337 | + seekVal = msg[1][0] |
1338 | + for display in self.curDisplayMediaPlayer.keys(): |
1339 | + if display.controller == controller: |
1340 | + self.curDisplayMediaPlayer[display].seek(display, seekVal) |
1341 | + |
1342 | + def video_reset(self, controller): |
1343 | + """ |
1344 | + Responds to the request to reset a loaded video |
1345 | + """ |
1346 | + log.debug(u'video_reset') |
1347 | + for display in self.curDisplayMediaPlayer.keys(): |
1348 | + if display.controller == controller: |
1349 | + display.override = {} |
1350 | + self.curDisplayMediaPlayer[display].reset(display) |
1351 | + self.curDisplayMediaPlayer[display].set_visible(display, False) |
1352 | + display.frame.evaluateJavaScript(u'show_video( \ |
1353 | + "setBackBoard", null, null, null,"hidden");') |
1354 | + del self.curDisplayMediaPlayer[display] |
1355 | + self.set_controls_visible(controller, False) |
1356 | + |
1357 | + def video_hide(self, msg): |
1358 | + """ |
1359 | + Hide the related video Widget |
1360 | + |
1361 | + ``msg`` |
1362 | + First element is the boolean for Live indication |
1363 | + """ |
1364 | + isLive = msg[1] |
1365 | + if isLive: |
1366 | + controller = self.parent.liveController |
1367 | + for display in self.curDisplayMediaPlayer.keys(): |
1368 | + if display.controller == controller: |
1369 | + if self.curDisplayMediaPlayer[display] \ |
1370 | + .state == MediaState.Playing: |
1371 | + self.curDisplayMediaPlayer[display].pause(display) |
1372 | + self.curDisplayMediaPlayer[display] \ |
1373 | + .set_visible(display, False) |
1374 | + |
1375 | + def video_blank(self, msg): |
1376 | + """ |
1377 | + Blank the related video Widget |
1378 | + |
1379 | + ``msg`` |
1380 | + First element is the boolean for Live indication |
1381 | + Second element is the hide mode |
1382 | + """ |
1383 | + isLive = msg[1] |
1384 | + hide_mode = msg[2] |
1385 | + if isLive: |
1386 | + Receiver.send_message(u'live_display_hide', hide_mode) |
1387 | + controller = self.parent.liveController |
1388 | + for display in self.curDisplayMediaPlayer.keys(): |
1389 | + if display.controller == controller: |
1390 | + if self.curDisplayMediaPlayer[display] \ |
1391 | + .state == MediaState.Playing: |
1392 | + self.curDisplayMediaPlayer[display].pause(display) |
1393 | + self.curDisplayMediaPlayer[display] \ |
1394 | + .set_visible(display, False) |
1395 | + |
1396 | + def video_unblank(self, msg): |
1397 | + """ |
1398 | + Unblank the related video Widget |
1399 | + |
1400 | + ``msg`` |
1401 | + First element is not relevant in this context |
1402 | + Second element is the boolean for Live indication |
1403 | + """ |
1404 | + Receiver.send_message(u'live_display_show') |
1405 | + isLive = msg[1] |
1406 | + if isLive: |
1407 | + controller = self.parent.liveController |
1408 | + for display in self.curDisplayMediaPlayer.keys(): |
1409 | + if display.controller == controller: |
1410 | + if self.curDisplayMediaPlayer[display] \ |
1411 | + .state == MediaState.Paused: |
1412 | + if self.curDisplayMediaPlayer[display].play(display): |
1413 | + self.curDisplayMediaPlayer[display] \ |
1414 | + .set_visible(display, True) |
1415 | + # Start Timer for ui updates |
1416 | + if not self.timer.isActive(): |
1417 | + self.timer.start() |
1418 | + |
1419 | + |
1420 | + def get_audio_extensions_list(self): |
1421 | + audio_list = [] |
1422 | + for player in self.mediaPlayers.values(): |
1423 | + if player.isActive: |
1424 | + for item in player.audio_extensions_list: |
1425 | + if not item in audio_list: |
1426 | + audio_list.append(item) |
1427 | + return audio_list |
1428 | + |
1429 | + def get_video_extensions_list(self): |
1430 | + video_list = [] |
1431 | + for player in self.mediaPlayers.values(): |
1432 | + if player.isActive: |
1433 | + for item in player.video_extensions_list: |
1434 | + if not item in video_list: |
1435 | + video_list.append(item) |
1436 | + return video_list |
1437 | + |
1438 | + def override_player(self, override_player): |
1439 | + playerSettings = str(QtCore.QSettings().value(u'media/players', |
1440 | + QtCore.QVariant(u'webkit')).toString()) |
1441 | + usedPlayers = playerSettings.split(u',') |
1442 | + if override_player in usedPlayers: |
1443 | + self.overridenPlayer = override_player |
1444 | + else: |
1445 | + self.overridenPlayer = '' |
1446 | + |
1447 | + def finalise(self): |
1448 | + self.timer.stop() |
1449 | + for controller in self.controller: |
1450 | + self.video_reset(controller) |
1451 | |
1452 | === added file 'openlp/core/ui/media/phononplayer.py' |
1453 | --- openlp/core/ui/media/phononplayer.py 1970-01-01 00:00:00 +0000 |
1454 | +++ openlp/core/ui/media/phononplayer.py 2011-12-01 18:30:31 +0000 |
1455 | @@ -0,0 +1,201 @@ |
1456 | +# -*- coding: utf-8 -*- |
1457 | +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 |
1458 | + |
1459 | +############################################################################### |
1460 | +# OpenLP - Open Source Lyrics Projection # |
1461 | +# --------------------------------------------------------------------------- # |
1462 | +# Copyright (c) 2008-2011 Raoul Snyman # |
1463 | +# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan # |
1464 | +# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, # |
1465 | +# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias # |
1466 | +# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # |
1467 | +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund # |
1468 | +# --------------------------------------------------------------------------- # |
1469 | +# This program is free software; you can redistribute it and/or modify it # |
1470 | +# under the terms of the GNU General Public License as published by the Free # |
1471 | +# Software Foundation; version 2 of the License. # |
1472 | +# # |
1473 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
1474 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
1475 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
1476 | +# more details. # |
1477 | +# # |
1478 | +# You should have received a copy of the GNU General Public License along # |
1479 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
1480 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
1481 | +############################################################################### |
1482 | + |
1483 | +import logging |
1484 | +import mimetypes |
1485 | +from datetime import datetime |
1486 | + |
1487 | +from PyQt4 import QtCore, QtGui |
1488 | +from PyQt4.phonon import Phonon |
1489 | + |
1490 | +from openlp.core.lib import Receiver |
1491 | +from openlp.core.lib.mediaplayer import MediaPlayer |
1492 | +from openlp.core.ui.media import MediaState |
1493 | + |
1494 | +log = logging.getLogger(__name__) |
1495 | + |
1496 | +ADDITIONAL_EXT = { |
1497 | + u'audio/ac3': [u'.ac3'], |
1498 | + u'audio/flac': [u'.flac'], |
1499 | + u'audio/x-m4a': [u'.m4a'], |
1500 | + u'audio/midi': [u'.mid', u'.midi'], |
1501 | + u'audio/x-mp3': [u'.mp3'], |
1502 | + u'audio/mpeg': [u'.mp3', u'.mp2', u'.mpga', u'.mpega', u'.m4a'], |
1503 | + u'audio/qcelp': [u'.qcp'], |
1504 | + u'audio/x-wma': [u'.wma'], |
1505 | + u'audio/x-ms-wma': [u'.wma'], |
1506 | + u'video/x-flv': [u'.flv'], |
1507 | + u'video/x-matroska': [u'.mpv', u'.mkv'], |
1508 | + u'video/x-wmv': [u'.wmv'], |
1509 | + u'video/x-mpg': [u'.mpg'], |
1510 | + u'video/x-ms-wmv': [u'.wmv']} |
1511 | + |
1512 | + |
1513 | +class PhononPlayer(MediaPlayer): |
1514 | + """ |
1515 | + A specialised version of the MediaPlayer class, which provides a Phonon |
1516 | + display. |
1517 | + """ |
1518 | + |
1519 | + def __init__(self, parent): |
1520 | + MediaPlayer.__init__(self, parent, u'phonon') |
1521 | + self.parent = parent |
1522 | + self.additional_extensions = ADDITIONAL_EXT |
1523 | + mimetypes.init() |
1524 | + for mimetype in Phonon.BackendCapabilities.availableMimeTypes(): |
1525 | + mimetype = unicode(mimetype) |
1526 | + if mimetype.startswith(u'audio/'): |
1527 | + self._addToList(self.audio_extensions_list, mimetype) |
1528 | + elif mimetype.startswith(u'video/'): |
1529 | + self._addToList(self.video_extensions_list, mimetype) |
1530 | + |
1531 | + def _addToList(self, list, mimetype): |
1532 | + # Add all extensions which mimetypes provides us for supported types. |
1533 | + extensions = mimetypes.guess_all_extensions(unicode(mimetype)) |
1534 | + for extension in extensions: |
1535 | + ext = u'*%s' % extension |
1536 | + if ext not in list: |
1537 | + list.append(ext) |
1538 | + log.info(u'MediaPlugin: %s extensions: %s' % (mimetype, |
1539 | + u' '.join(extensions))) |
1540 | + # Add extensions for this mimetype from self.additional_extensions. |
1541 | + # This hack clears mimetypes' and operating system's shortcomings |
1542 | + # by providing possibly missing extensions. |
1543 | + if mimetype in self.additional_extensions.keys(): |
1544 | + for extension in self.additional_extensions[mimetype]: |
1545 | + ext = u'*%s' % extension |
1546 | + if ext not in list: |
1547 | + list.append(ext) |
1548 | + log.info(u'MediaPlugin: %s additional extensions: %s' % (mimetype, |
1549 | + u' '.join(self.additional_extensions[mimetype]))) |
1550 | + |
1551 | + def setup(self, display): |
1552 | + display.phononWidget = Phonon.VideoWidget(display) |
1553 | + display.phononWidget.resize(display.size()) |
1554 | + display.mediaObject = Phonon.MediaObject(display) |
1555 | + Phonon.createPath(display.mediaObject, display.phononWidget) |
1556 | + if display.hasAudio: |
1557 | + display.audio = Phonon.AudioOutput( \ |
1558 | + Phonon.VideoCategory, display.mediaObject) |
1559 | + Phonon.createPath(display.mediaObject, display.audio) |
1560 | + display.phononWidget.raise_() |
1561 | + display.phononWidget.hide() |
1562 | + self.hasOwnWidget = True |
1563 | + |
1564 | + def check_available(self): |
1565 | + return True |
1566 | + |
1567 | + def load(self, display): |
1568 | + log.debug(u'load vid in Phonon Controller') |
1569 | + controller = display.controller |
1570 | + volume = controller.media_info.volume |
1571 | + path = controller.media_info.file_info.absoluteFilePath() |
1572 | + display.mediaObject.setCurrentSource(Phonon.MediaSource(path)) |
1573 | + if not self.media_state_wait(display, Phonon.StoppedState): |
1574 | + return False |
1575 | + self.volume(display, volume) |
1576 | + return True |
1577 | + |
1578 | + def media_state_wait(self, display, mediaState): |
1579 | + """ |
1580 | + Wait for the video to change its state |
1581 | + Wait no longer than 5 seconds. |
1582 | + """ |
1583 | + start = datetime.now() |
1584 | + current_state = display.mediaObject.state() |
1585 | + while current_state != mediaState: |
1586 | + current_state = display.mediaObject.state() |
1587 | + if current_state == Phonon.ErrorState: |
1588 | + return False |
1589 | + Receiver.send_message(u'openlp_process_events') |
1590 | + if (datetime.now() - start).seconds > 5: |
1591 | + return False |
1592 | + return True |
1593 | + |
1594 | + def resize(self, display): |
1595 | + display.phononWidget.resize(display.size()) |
1596 | + |
1597 | + def play(self, display): |
1598 | + controller = display.controller |
1599 | + start_time = 0 |
1600 | + if display.mediaObject.state() != Phonon.PausedState and \ |
1601 | + controller.media_info.start_time > 0: |
1602 | + start_time = controller.media_info.start_time |
1603 | + display.mediaObject.play() |
1604 | + if self.media_state_wait(display, Phonon.PlayingState): |
1605 | + if start_time > 0: |
1606 | + self.seek(display, controller.media_info.start_time*1000) |
1607 | + self.volume(display, controller.media_info.volume) |
1608 | + controller.media_info.length = \ |
1609 | + int(display.mediaObject.totalTime()/1000) |
1610 | + controller.seekSlider.setMaximum(controller.media_info.length*1000) |
1611 | + self.state = MediaState.Playing |
1612 | + display.phononWidget.raise_() |
1613 | + return True |
1614 | + else: |
1615 | + return False |
1616 | + |
1617 | + def pause(self, display): |
1618 | + display.mediaObject.pause() |
1619 | + if self.media_state_wait(display, Phonon.PausedState): |
1620 | + self.state = MediaState.Paused |
1621 | + |
1622 | + def stop(self, display): |
1623 | + display.mediaObject.stop() |
1624 | + self.set_visible(display, False) |
1625 | + self.state = MediaState.Stopped |
1626 | + |
1627 | + def volume(self, display, vol): |
1628 | + # 1.0 is the highest value |
1629 | + if display.hasAudio: |
1630 | + vol = float(vol) / float(100) |
1631 | + display.audio.setVolume(vol) |
1632 | + |
1633 | + def seek(self, display, seekVal): |
1634 | + display.mediaObject.seek(seekVal) |
1635 | + |
1636 | + def reset(self, display): |
1637 | + display.mediaObject.stop() |
1638 | + display.mediaObject.clearQueue() |
1639 | + self.set_visible(display, False) |
1640 | + display.phononWidget.setVisible(False) |
1641 | + self.state = MediaState.Off |
1642 | + |
1643 | + def set_visible(self, display, status): |
1644 | + if self.hasOwnWidget: |
1645 | + display.phononWidget.setVisible(status) |
1646 | + |
1647 | + def update_ui(self, display): |
1648 | + controller = display.controller |
1649 | + if controller.media_info.end_time > 0: |
1650 | + if display.mediaObject.currentTime() > \ |
1651 | + controller.media_info.end_time*1000: |
1652 | + self.stop(display) |
1653 | + self.set_visible(display, False) |
1654 | + if not controller.seekSlider.isSliderDown(): |
1655 | + controller.seekSlider.setSliderPosition( \ |
1656 | + display.mediaObject.currentTime()) |
1657 | |
1658 | === added file 'openlp/core/ui/media/webkitplayer.py' |
1659 | --- openlp/core/ui/media/webkitplayer.py 1970-01-01 00:00:00 +0000 |
1660 | +++ openlp/core/ui/media/webkitplayer.py 2011-12-01 18:30:31 +0000 |
1661 | @@ -0,0 +1,426 @@ |
1662 | +# -*- coding: utf-8 -*- |
1663 | +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 |
1664 | + |
1665 | +############################################################################### |
1666 | +# OpenLP - Open Source Lyrics Projection # |
1667 | +# --------------------------------------------------------------------------- # |
1668 | +# Copyright (c) 2008-2011 Raoul Snyman # |
1669 | +# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan # |
1670 | +# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, # |
1671 | +# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias # |
1672 | +# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # |
1673 | +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund # |
1674 | +# --------------------------------------------------------------------------- # |
1675 | +# This program is free software; you can redistribute it and/or modify it # |
1676 | +# under the terms of the GNU General Public License as published by the Free # |
1677 | +# Software Foundation; version 2 of the License. # |
1678 | +# # |
1679 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
1680 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
1681 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
1682 | +# more details. # |
1683 | +# # |
1684 | +# You should have received a copy of the GNU General Public License along # |
1685 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
1686 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
1687 | +############################################################################### |
1688 | + |
1689 | +import logging |
1690 | + |
1691 | +from PyQt4 import QtCore, QtGui, QtWebKit |
1692 | + |
1693 | +from openlp.core.lib import OpenLPToolbar, translate |
1694 | +from openlp.core.lib.mediaplayer import MediaPlayer |
1695 | +from openlp.core.ui.media import MediaState |
1696 | + |
1697 | +log = logging.getLogger(__name__) |
1698 | + |
1699 | +VIDEO_CSS = u""" |
1700 | +#videobackboard { |
1701 | + z-index:3; |
1702 | + background-color: black; |
1703 | +} |
1704 | +#video1 { |
1705 | + z-index:4; |
1706 | +} |
1707 | +#video2 { |
1708 | + z-index:4; |
1709 | +} |
1710 | +""" |
1711 | + |
1712 | +VIDEO_JS = u""" |
1713 | + var video_timer = null; |
1714 | + var current_video = '1'; |
1715 | + |
1716 | + function show_video(state, path, volume, loop, varVal){ |
1717 | + // Note, the preferred method for looping would be to use the |
1718 | + // video tag loop attribute. |
1719 | + // But QtWebKit doesn't support this. Neither does it support the |
1720 | + // onended event, hence the setInterval() |
1721 | + // In addition, setting the currentTime attribute to zero to restart |
1722 | + // the video raises an INDEX_SIZE_ERROR: DOM Exception 1 |
1723 | + // To complicate it further, sometimes vid.currentTime stops |
1724 | + // slightly short of vid.duration and vid.ended is intermittent! |
1725 | + // |
1726 | + // Note, currently the background may go black between loops. Not |
1727 | + // desirable. Need to investigate using two <video>'s, and hiding/ |
1728 | + // preloading one, and toggle between the two when looping. |
1729 | + |
1730 | + if(current_video=='1'){ |
1731 | + var vid = document.getElementById('video1'); |
1732 | + var vid2 = document.getElementById('video2'); |
1733 | + } else { |
1734 | + var vid = document.getElementById('video2'); |
1735 | + var vid2 = document.getElementById('video1'); |
1736 | + } |
1737 | + if(volume != null){ |
1738 | + vid.volume = volume; |
1739 | + vid2.volume = volume; |
1740 | + } |
1741 | + switch(state){ |
1742 | + case 'init': |
1743 | + vid.src = 'file:///' + path; |
1744 | + vid2.src = 'file:///' + path; |
1745 | + if(loop == null) loop = false; |
1746 | + vid.looping = loop; |
1747 | + vid2.looping = loop; |
1748 | + vid.load(); |
1749 | + break; |
1750 | + case 'load': |
1751 | + vid2.style.visibility = 'hidden'; |
1752 | + vid2.load(); |
1753 | + break; |
1754 | + case 'play': |
1755 | + vid.play(); |
1756 | + if(vid.looping){ |
1757 | + video_timer = setInterval( |
1758 | + function() { |
1759 | + show_video('poll'); |
1760 | + }, 200); |
1761 | + } |
1762 | + break; |
1763 | + case 'pause': |
1764 | + if(video_timer!=null){ |
1765 | + clearInterval(video_timer); |
1766 | + video_timer = null; |
1767 | + } |
1768 | + vid.pause(); |
1769 | + break; |
1770 | + case 'stop': |
1771 | + show_video('pause'); |
1772 | + vid.currentTime = 0; |
1773 | + break; |
1774 | + case 'poll': |
1775 | + if(vid.ended||vid.currentTime+0.2>vid.duration) |
1776 | + show_video('swap'); |
1777 | + break; |
1778 | + case 'swap': |
1779 | + show_video('pause'); |
1780 | + if(current_video=='1') |
1781 | + current_video = '2'; |
1782 | + else |
1783 | + current_video = '1'; |
1784 | + show_video('load'); |
1785 | + show_video('play'); |
1786 | + show_video('setVisible',null,null,null,'visible'); |
1787 | + break; |
1788 | + case 'close': |
1789 | + show_video('stop'); |
1790 | + vid.src = ''; |
1791 | + vid2.src = ''; |
1792 | + break; |
1793 | + case 'length': |
1794 | + return vid.duration; |
1795 | + case 'currentTime': |
1796 | + return vid.currentTime; |
1797 | + case 'seek': |
1798 | + // doesnt work currently |
1799 | + vid.currentTime = varVal; |
1800 | + break; |
1801 | + case 'setVisible': |
1802 | + vid.style.visibility = varVal; |
1803 | + break; |
1804 | + case 'setBackBoard': |
1805 | + var back = document.getElementById('videobackboard'); |
1806 | + back.style.visibility = varVal; |
1807 | + break; |
1808 | + } |
1809 | + } |
1810 | +""" |
1811 | + |
1812 | +VIDEO_HTML = u""" |
1813 | +<div id="videobackboard" class="size" style="visibility:hidden"></div> |
1814 | +<video id="video1" class="size" style="visibility:hidden" autobuffer preload> |
1815 | +</video> |
1816 | +<video id="video2" class="size" style="visibility:hidden" autobuffer preload> |
1817 | +</video> |
1818 | +""" |
1819 | + |
1820 | +FLASH_CSS = u""" |
1821 | +#flash { |
1822 | + z-index:5; |
1823 | +} |
1824 | +""" |
1825 | + |
1826 | +FLASH_JS = u""" |
1827 | + function getFlashMovieObject(movieName) |
1828 | + { |
1829 | + if (window.document[movieName]) |
1830 | + { |
1831 | + return window.document[movieName]; |
1832 | + } |
1833 | + if (document.embeds && document.embeds[movieName]) |
1834 | + return document.embeds[movieName]; |
1835 | + } |
1836 | + |
1837 | + function show_flash(state, path, volume, varVal){ |
1838 | + var text = document.getElementById('flash'); |
1839 | + var flashMovie = getFlashMovieObject("OpenLPFlashMovie"); |
1840 | + var src = "src = 'file:///" + path + "'"; |
1841 | + var view_parm = " wmode='opaque'" + |
1842 | + " width='100%%'" + |
1843 | + " height='100%%'"; |
1844 | + var swf_parm = " name='OpenLPFlashMovie'" + |
1845 | + " autostart='true' loop='false' play='true'" + |
1846 | + " hidden='false' swliveconnect='true' allowscriptaccess='always'" + |
1847 | + " volume='" + volume + "'"; |
1848 | + |
1849 | + switch(state){ |
1850 | + case 'load': |
1851 | + text.innerHTML = "<embed " + src + view_parm + swf_parm + "/>"; |
1852 | + flashMovie = getFlashMovieObject("OpenLPFlashMovie"); |
1853 | + flashMovie.Play(); |
1854 | + break; |
1855 | + case 'play': |
1856 | + flashMovie.Play(); |
1857 | + break; |
1858 | + case 'pause': |
1859 | + flashMovie.StopPlay(); |
1860 | + break; |
1861 | + case 'stop': |
1862 | + flashMovie.StopPlay(); |
1863 | + tempHtml = text.innerHTML; |
1864 | + text.innerHTML = ''; |
1865 | + text.innerHTML = tempHtml; |
1866 | + break; |
1867 | + case 'close': |
1868 | + flashMovie.StopPlay(); |
1869 | + text.innerHTML = ''; |
1870 | + break; |
1871 | + case 'length': |
1872 | + return flashMovie.TotalFrames(); |
1873 | + case 'currentTime': |
1874 | + return flashMovie.CurrentFrame(); |
1875 | + case 'seek': |
1876 | +// flashMovie.GotoFrame(varVal); |
1877 | + break; |
1878 | + case 'setVisible': |
1879 | + text.style.visibility = varVal; |
1880 | + break; |
1881 | + } |
1882 | + } |
1883 | +""" |
1884 | + |
1885 | +FLASH_HTML = u""" |
1886 | +<div id="flash" class="size" style="visibility:hidden"></div> |
1887 | +""" |
1888 | + |
1889 | +VIDEO_EXT = [ |
1890 | + u'*.3gp' |
1891 | + , u'*.3gpp' |
1892 | + , u'*.3g2' |
1893 | + , u'*.3gpp2' |
1894 | + , u'*.aac' |
1895 | + , u'*.flv' |
1896 | + , u'*.f4a' |
1897 | + , u'*.f4b' |
1898 | + , u'*.f4p' |
1899 | + , u'*.f4v' |
1900 | + , u'*.mov' |
1901 | + , u'*.m4a' |
1902 | + , u'*.m4b' |
1903 | + , u'*.m4p' |
1904 | + , u'*.m4v' |
1905 | + , u'*.mkv' |
1906 | + , u'*.mp4' |
1907 | + , u'*.ogv' |
1908 | + , u'*.webm' |
1909 | + , u'*.mpg', u'*.wmv', u'*.mpeg', u'*.avi' |
1910 | + , u'*.swf' |
1911 | + ] |
1912 | + |
1913 | +AUDIO_EXT = [ |
1914 | + u'*.mp3' |
1915 | + , u'*.ogg' |
1916 | + ] |
1917 | + |
1918 | + |
1919 | +class WebkitPlayer(MediaPlayer): |
1920 | + """ |
1921 | + A specialised version of the MediaPlayer class, which provides a QtWebKit |
1922 | + display. |
1923 | + """ |
1924 | + |
1925 | + def __init__(self, parent): |
1926 | + MediaPlayer.__init__(self, parent, u'webkit') |
1927 | + self.parent = parent |
1928 | + self.canBackground = True |
1929 | + self.audio_extensions_list = AUDIO_EXT |
1930 | + self.video_extensions_list = VIDEO_EXT |
1931 | + |
1932 | + def get_media_display_css(self): |
1933 | + """ |
1934 | + Add css style sheets to htmlbuilder |
1935 | + """ |
1936 | + return VIDEO_CSS + FLASH_CSS |
1937 | + |
1938 | + def get_media_display_javascript(self): |
1939 | + """ |
1940 | + Add javascript functions to htmlbuilder |
1941 | + """ |
1942 | + return VIDEO_JS + FLASH_JS |
1943 | + |
1944 | + def get_media_display_html(self): |
1945 | + """ |
1946 | + Add html code to htmlbuilder |
1947 | + """ |
1948 | + return VIDEO_HTML + FLASH_HTML |
1949 | + |
1950 | + def setup(self, display): |
1951 | + display.webView.resize(display.size()) |
1952 | + display.webView.raise_() |
1953 | + self.hasOwnWidget = False |
1954 | + |
1955 | + def check_available(self): |
1956 | + return True |
1957 | + |
1958 | + def load(self, display): |
1959 | + log.debug(u'load vid in Webkit Controller') |
1960 | + controller = display.controller |
1961 | + if display.hasAudio: |
1962 | + volume = controller.media_info.volume |
1963 | + vol = float(volume) / float(100) |
1964 | + else: |
1965 | + vol = 0 |
1966 | + path = controller.media_info.file_info.absoluteFilePath() |
1967 | + if controller.media_info.is_background: |
1968 | + loop = u'true' |
1969 | + else: |
1970 | + loop = u'false' |
1971 | + display.webView.setVisible(True) |
1972 | + if controller.media_info.file_info.suffix() == u'swf': |
1973 | + controller.media_info.is_flash = True |
1974 | + js = u'show_flash("load","%s");' % \ |
1975 | + (path.replace(u'\\', u'\\\\')) |
1976 | + else: |
1977 | + js = u'show_video("init", "%s", %s, %s);' % \ |
1978 | + (path.replace(u'\\', u'\\\\'), str(vol), loop) |
1979 | + display.frame.evaluateJavaScript(js) |
1980 | + return True |
1981 | + |
1982 | + def resize(self, display): |
1983 | + controller = display.controller |
1984 | + display.webView.resize(display.size()) |
1985 | + |
1986 | + def play(self, display): |
1987 | + controller = display.controller |
1988 | + display.webLoaded = True |
1989 | + length = 0 |
1990 | + start_time = 0 |
1991 | + if self.state != MediaState.Paused and \ |
1992 | + controller.media_info.start_time > 0: |
1993 | + start_time = controller.media_info.start_time |
1994 | + self.set_visible(display, True) |
1995 | + if controller.media_info.is_flash: |
1996 | + display.frame.evaluateJavaScript(u'show_flash("play");') |
1997 | + else: |
1998 | + display.frame.evaluateJavaScript(u'show_video("play");') |
1999 | + if start_time > 0: |
2000 | + self.seek(display, controller.media_info.start_time*1000) |
2001 | + # TODO add playing check and get the correct media length |
2002 | + controller.media_info.length = length |
2003 | + self.state = MediaState.Playing |
2004 | + display.webView.raise_() |
2005 | + return True |
2006 | + |
2007 | + def pause(self, display): |
2008 | + controller = display.controller |
2009 | + if controller.media_info.is_flash: |
2010 | + display.frame.evaluateJavaScript(u'show_flash("pause");') |
2011 | + else: |
2012 | + display.frame.evaluateJavaScript(u'show_video("pause");') |
2013 | + self.state = MediaState.Paused |
2014 | + |
2015 | + def stop(self, display): |
2016 | + controller = display.controller |
2017 | + if controller.media_info.is_flash: |
2018 | + display.frame.evaluateJavaScript(u'show_flash("stop");') |
2019 | + else: |
2020 | + display.frame.evaluateJavaScript(u'show_video("stop");') |
2021 | + controller.seekSlider.setSliderPosition(0) |
2022 | + self.state = MediaState.Stopped |
2023 | + |
2024 | + def volume(self, display, vol): |
2025 | + controller = display.controller |
2026 | + # 1.0 is the highest value |
2027 | + if display.hasAudio: |
2028 | + vol = float(vol) / float(100) |
2029 | + if not controller.media_info.is_flash: |
2030 | + display.frame.evaluateJavaScript( |
2031 | + u'show_video(null, null, %s);' % str(vol)) |
2032 | + |
2033 | + def seek(self, display, seekVal): |
2034 | + controller = display.controller |
2035 | + if controller.media_info.is_flash: |
2036 | + seek = seekVal |
2037 | + display.frame.evaluateJavaScript( \ |
2038 | + u'show_flash("seek", null, null, "%s");' % (seek)) |
2039 | + else: |
2040 | + seek = float(seekVal)/1000 |
2041 | + display.frame.evaluateJavaScript( \ |
2042 | + u'show_video("seek", null, null, null, "%f");' % (seek)) |
2043 | + |
2044 | + def reset(self, display): |
2045 | + controller = display.controller |
2046 | + if controller.media_info.is_flash: |
2047 | + display.frame.evaluateJavaScript(u'show_flash("close");') |
2048 | + else: |
2049 | + display.frame.evaluateJavaScript(u'show_video("close");') |
2050 | + self.state = MediaState.Off |
2051 | + |
2052 | + def set_visible(self, display, status): |
2053 | + controller = display.controller |
2054 | + if status: |
2055 | + is_visible = "visible" |
2056 | + else: |
2057 | + is_visible = "hidden" |
2058 | + if controller.media_info.is_flash: |
2059 | + display.frame.evaluateJavaScript(u'show_flash( \ |
2060 | + "setVisible", null, null, "%s");' % (is_visible)) |
2061 | + else: |
2062 | + display.frame.evaluateJavaScript(u'show_video( \ |
2063 | + "setVisible", null, null, null, "%s");' % (is_visible)) |
2064 | + |
2065 | + def update_ui(self, display): |
2066 | + controller = display.controller |
2067 | + if controller.media_info.is_flash: |
2068 | + currentTime = display.frame.evaluateJavaScript( \ |
2069 | + u'show_flash("currentTime");').toInt()[0] |
2070 | + length = display.frame.evaluateJavaScript( \ |
2071 | + u'show_flash("length");').toInt()[0] |
2072 | + else: |
2073 | + (currentTime, ok) = display.frame.evaluateJavaScript( \ |
2074 | + u'show_video("currentTime");').toFloat() |
2075 | + # check if conversion was ok and value is not 'NaN' |
2076 | + if ok and currentTime != float('inf'): |
2077 | + currentTime = int(currentTime*1000) |
2078 | + (length, ok) = display.frame.evaluateJavaScript( \ |
2079 | + u'show_video("length");').toFloat() |
2080 | + # check if conversion was ok and value is not 'NaN' |
2081 | + if ok and length != float('inf'): |
2082 | + length = int(length*1000) |
2083 | + if currentTime > 0: |
2084 | + controller.media_info.length = length |
2085 | + controller.seekSlider.setMaximum(length) |
2086 | + if not controller.seekSlider.isSliderDown(): |
2087 | + controller.seekSlider.setSliderPosition(currentTime) |
2088 | |
2089 | === modified file 'openlp/core/ui/slidecontroller.py' |
2090 | --- openlp/core/ui/slidecontroller.py 2011-11-28 18:03:38 +0000 |
2091 | +++ openlp/core/ui/slidecontroller.py 2011-12-01 18:30:31 +0000 |
2092 | @@ -34,9 +34,9 @@ |
2093 | from PyQt4.phonon import Phonon |
2094 | |
2095 | from openlp.core.lib import OpenLPToolbar, Receiver, ItemCapabilities, \ |
2096 | - translate, build_icon |
2097 | + translate, build_icon, ServiceItem, build_html, PluginManager, ServiceItem |
2098 | from openlp.core.lib.ui import UiStrings, shortcut_action |
2099 | -from openlp.core.ui import HideMode, MainDisplay, ScreenList |
2100 | +from openlp.core.ui import HideMode, MainDisplay, Display, ScreenList |
2101 | from openlp.core.utils.actions import ActionList, CategoryOrder |
2102 | |
2103 | log = logging.getLogger(__name__) |
2104 | @@ -49,8 +49,29 @@ |
2105 | def __init__(self, parent=None, name=None): |
2106 | QtGui.QTableWidget.__init__(self, parent.controller) |
2107 | |
2108 | - |
2109 | -class SlideController(QtGui.QWidget): |
2110 | +class Controller(QtGui.QWidget): |
2111 | + """ |
2112 | + Controller is a general controller widget. |
2113 | + """ |
2114 | + def __init__(self, parent, isLive=False): |
2115 | + """ |
2116 | + Set up the general Controller. |
2117 | + """ |
2118 | + QtGui.QWidget.__init__(self, parent) |
2119 | + self.isLive = isLive |
2120 | + self.display = None |
2121 | + |
2122 | + def sendToPlugins(self, *args): |
2123 | + """ |
2124 | + This is the generic function to send signal for control widgets, |
2125 | + created from within other plugins |
2126 | + This function is needed to catch the current controller |
2127 | + """ |
2128 | + sender = self.sender().objectName() if self.sender().objectName() else self.sender().text() |
2129 | + controller = self |
2130 | + Receiver.send_message('%s' % sender, [controller, args]) |
2131 | + |
2132 | +class SlideController(Controller): |
2133 | """ |
2134 | SlideController is the slide controller widget. This widget is what the |
2135 | user uses to control the displaying of verses/slides/etc on the screen. |
2136 | @@ -59,13 +80,12 @@ |
2137 | """ |
2138 | Set up the Slide Controller. |
2139 | """ |
2140 | - QtGui.QWidget.__init__(self, parent) |
2141 | - self.isLive = isLive |
2142 | - self.display = None |
2143 | + Controller.__init__(self, parent, isLive) |
2144 | self.screens = ScreenList.get_instance() |
2145 | self.ratio = float(self.screens.current[u'size'].width()) / \ |
2146 | float(self.screens.current[u'size'].height()) |
2147 | self.imageManager = self.parent().imageManager |
2148 | + self.mediaController = self.parent().mediaController |
2149 | self.loopList = [ |
2150 | u'Play Slides Menu', |
2151 | u'Loop Separator', |
2152 | @@ -74,7 +94,10 @@ |
2153 | self.songEditList = [ |
2154 | u'Edit Song', |
2155 | ] |
2156 | - self.volume = 10 |
2157 | + self.nextPreviousList = [ |
2158 | + u'Previous Slide', |
2159 | + u'Next Slide' |
2160 | + ] |
2161 | self.timer_id = 0 |
2162 | self.songEdit = False |
2163 | self.selectedRow = 0 |
2164 | @@ -140,14 +163,14 @@ |
2165 | self.toolbar.sizePolicy().hasHeightForWidth()) |
2166 | self.toolbar.setSizePolicy(sizeToolbarPolicy) |
2167 | self.previousItem = self.toolbar.addToolbarButton( |
2168 | - translate('OpenLP.SlideController', 'Previous Slide'), |
2169 | + u'Previous Slide', |
2170 | u':/slides/slide_previous.png', |
2171 | translate('OpenLP.SlideController', 'Move to previous.'), |
2172 | self.onSlideSelectedPrevious, |
2173 | shortcuts=[QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp], |
2174 | context=QtCore.Qt.WidgetWithChildrenShortcut) |
2175 | self.nextItem = self.toolbar.addToolbarButton( |
2176 | - translate('OpenLP.SlideController', 'Next Slide'), |
2177 | + u'Next Slide', |
2178 | u':/slides/slide_next.png', |
2179 | translate('OpenLP.SlideController', 'Move to next.'), |
2180 | self.onSlideSelectedNext, |
2181 | @@ -234,20 +257,8 @@ |
2182 | 'Edit and reload song preview.'), |
2183 | self.onEditSong) |
2184 | self.controllerLayout.addWidget(self.toolbar) |
2185 | - # Build a Media ToolBar |
2186 | - self.mediabar = OpenLPToolbar(self) |
2187 | - self.mediabar.addToolbarButton( |
2188 | - u'Media Start', u':/slides/media_playback_start.png', |
2189 | - translate('OpenLP.SlideController', 'Start playing media.'), |
2190 | - self.onMediaPlay) |
2191 | - self.mediabar.addToolbarButton( |
2192 | - u'Media Pause', u':/slides/media_playback_pause.png', |
2193 | - translate('OpenLP.SlideController', 'Start playing media.'), |
2194 | - self.onMediaPause) |
2195 | - self.mediabar.addToolbarButton( |
2196 | - u'Media Stop', u':/slides/media_playback_stop.png', |
2197 | - translate('OpenLP.SlideController', 'Start playing media.'), |
2198 | - self.onMediaStop) |
2199 | + # Build the Media Toolbar |
2200 | + self.mediaController.add_controller_items(self, self.controllerLayout) |
2201 | if self.isLive: |
2202 | # Build the Song Toolbar |
2203 | self.songMenu = QtGui.QToolButton(self.toolbar) |
2204 | @@ -263,23 +274,6 @@ |
2205 | translate('OpenLP.SlideController', 'Pause audio.'), |
2206 | self.onAudioPauseClicked, True) |
2207 | self.audioPauseItem.setVisible(False) |
2208 | - # Build the volumeSlider. |
2209 | - self.volumeSlider = QtGui.QSlider(QtCore.Qt.Horizontal) |
2210 | - self.volumeSlider.setTickInterval(1) |
2211 | - self.volumeSlider.setTickPosition(QtGui.QSlider.TicksAbove) |
2212 | - self.volumeSlider.setMinimum(0) |
2213 | - self.volumeSlider.setMaximum(10) |
2214 | - else: |
2215 | - # Build the seekSlider. |
2216 | - self.seekSlider = Phonon.SeekSlider() |
2217 | - self.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24)) |
2218 | - self.seekSlider.setObjectName(u'seekSlider') |
2219 | - self.mediabar.addToolbarWidget(u'Seek Slider', self.seekSlider) |
2220 | - self.volumeSlider = Phonon.VolumeSlider() |
2221 | - self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24)) |
2222 | - self.volumeSlider.setObjectName(u'volumeSlider') |
2223 | - self.mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider) |
2224 | - self.controllerLayout.addWidget(self.mediabar) |
2225 | # Screen preview area |
2226 | self.previewFrame = QtGui.QFrame(self.splitter) |
2227 | self.previewFrame.setGeometry(QtCore.QRect(0, 0, 300, 300 * self.ratio)) |
2228 | @@ -296,17 +290,13 @@ |
2229 | self.slideLayout = QtGui.QVBoxLayout() |
2230 | self.slideLayout.setSpacing(0) |
2231 | self.slideLayout.setMargin(0) |
2232 | - self.slideLayout.setObjectName(u'slideLayout') |
2233 | - if not self.isLive: |
2234 | - self.mediaObject = Phonon.MediaObject(self) |
2235 | - self.video = Phonon.VideoWidget() |
2236 | - self.video.setVisible(False) |
2237 | - self.audio = Phonon.AudioOutput(Phonon.VideoCategory, |
2238 | - self.mediaObject) |
2239 | - Phonon.createPath(self.mediaObject, self.video) |
2240 | - Phonon.createPath(self.mediaObject, self.audio) |
2241 | - self.video.setGeometry(QtCore.QRect(0, 0, 300, 225)) |
2242 | - self.slideLayout.insertWidget(0, self.video) |
2243 | + self.slideLayout.setObjectName(u'SlideLayout') |
2244 | + self.previewDisplay = Display(self, self.isLive, self) |
2245 | + self.previewDisplay.setGeometry(QtCore.QRect(0, 0, 300, 300)) |
2246 | + self.previewDisplay.screen = {u'size':self.previewDisplay.geometry()} |
2247 | + self.previewDisplay.setup() |
2248 | + self.slideLayout.insertWidget(0, self.previewDisplay) |
2249 | + self.previewDisplay.hide() |
2250 | # Actual preview screen |
2251 | self.slidePreview = QtGui.QLabel(self) |
2252 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, |
2253 | @@ -415,8 +405,6 @@ |
2254 | QtCore.QObject.connect(self.previewListWidget, |
2255 | QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected) |
2256 | if self.isLive: |
2257 | - QtCore.QObject.connect(self.volumeSlider, |
2258 | - QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume) |
2259 | QtCore.QObject.connect(Receiver.get_receiver(), |
2260 | QtCore.SIGNAL(u'slidecontroller_live_spin_delay'), |
2261 | self.receiveSpinDelay) |
2262 | @@ -426,7 +414,6 @@ |
2263 | QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), |
2264 | self.onGoLiveClick) |
2265 | self.toolbar.makeWidgetsInvisible(self.songEditList) |
2266 | - self.mediabar.setVisible(False) |
2267 | if self.isLive: |
2268 | self.setLiveHotkeys(self) |
2269 | self.__addActionsToWidget(self.previewListWidget) |
2270 | @@ -577,7 +564,7 @@ |
2271 | |
2272 | def liveEscape(self): |
2273 | self.display.setVisible(False) |
2274 | - self.display.videoStop() |
2275 | + self.mediaController.video_stop([self]) |
2276 | |
2277 | def servicePrevious(self): |
2278 | """ |
2279 | @@ -617,14 +604,22 @@ |
2280 | # rebuild display as screen size changed |
2281 | if self.display: |
2282 | self.display.close() |
2283 | - self.display = MainDisplay(self, self.imageManager, self.isLive) |
2284 | + self.display = MainDisplay(self, self.imageManager, self.isLive, |
2285 | + self) |
2286 | self.display.setup() |
2287 | if self.isLive: |
2288 | self.__addActionsToWidget(self.display) |
2289 | # The SlidePreview's ratio. |
2290 | self.ratio = float(self.screens.current[u'size'].width()) / \ |
2291 | float(self.screens.current[u'size'].height()) |
2292 | + self.mediaController.setup_display(self.display) |
2293 | self.previewSizeChanged() |
2294 | + self.previewDisplay.setup() |
2295 | + serviceItem = ServiceItem() |
2296 | + self.previewDisplay.webView.setHtml(build_html(serviceItem, |
2297 | + self.previewDisplay.screen, None, self.isLive, None, |
2298 | + plugins=PluginManager.get_instance().plugins)) |
2299 | + self.mediaController.setup_display(self.previewDisplay) |
2300 | if self.serviceItem: |
2301 | self.refreshServiceItem() |
2302 | |
2303 | @@ -646,11 +641,17 @@ |
2304 | max_height = self.previewFrame.height() - self.grid.margin() * 2 |
2305 | self.slidePreview.setFixedSize(QtCore.QSize(max_height * self.ratio, |
2306 | max_height)) |
2307 | + self.previewDisplay.setFixedSize(QtCore.QSize(max_height * self.ratio, |
2308 | + max_height)) |
2309 | + self.previewDisplay.screen = {u'size':self.previewDisplay.geometry()} |
2310 | else: |
2311 | # We have to take the width as limit. |
2312 | max_width = self.previewFrame.width() - self.grid.margin() * 2 |
2313 | self.slidePreview.setFixedSize(QtCore.QSize(max_width, |
2314 | max_width / self.ratio)) |
2315 | + self.previewDisplay.setFixedSize(QtCore.QSize(max_width, |
2316 | + max_width / self.ratio)) |
2317 | + self.previewDisplay.screen = {u'size':self.previewDisplay.geometry()} |
2318 | # Make sure that the frames have the correct size. |
2319 | self.previewListWidget.setColumnWidth(0, |
2320 | self.previewListWidget.viewport().size().width()) |
2321 | @@ -711,12 +712,13 @@ |
2322 | len(item.get_frames()) > 1: |
2323 | self.toolbar.makeWidgetsVisible(self.loopList) |
2324 | if item.is_media(): |
2325 | - self.toolbar.setVisible(False) |
2326 | self.mediabar.setVisible(True) |
2327 | + self.toolbar.makeWidgetsInvisible(self.nextPreviousList) |
2328 | else: |
2329 | # Work-around for OS X, hide and then show the toolbar |
2330 | # See bug #791050 |
2331 | - self.toolbar.show() |
2332 | + self.toolbar.makeWidgetsVisible(self.nextPreviousList) |
2333 | + self.toolbar.show() |
2334 | |
2335 | def enablePreviewToolBar(self, item): |
2336 | """ |
2337 | @@ -730,13 +732,13 @@ |
2338 | if item.is_capable(ItemCapabilities.CanEdit) and item.from_plugin: |
2339 | self.toolbar.makeWidgetsVisible(self.songEditList) |
2340 | elif item.is_media(): |
2341 | - self.toolbar.setVisible(False) |
2342 | self.mediabar.setVisible(True) |
2343 | - self.volumeSlider.setAudioOutput(self.audio) |
2344 | + self.toolbar.makeWidgetsInvisible(self.nextPreviousList) |
2345 | if not item.is_media(): |
2346 | # Work-around for OS X, hide and then show the toolbar |
2347 | # See bug #791050 |
2348 | - self.toolbar.show() |
2349 | + self.toolbar.makeWidgetsVisible(self.nextPreviousList) |
2350 | + self.toolbar.show() |
2351 | |
2352 | def refreshServiceItem(self): |
2353 | """ |
2354 | @@ -1325,72 +1327,18 @@ |
2355 | """ |
2356 | log.debug(u'SlideController onMediaStart') |
2357 | file = os.path.join(item.get_frame_path(), item.get_frame_title()) |
2358 | - if self.isLive: |
2359 | - self.display.video(file, self.volume) |
2360 | - self.volumeSlider.setValue(self.volume) |
2361 | - else: |
2362 | - self.mediaObject.stop() |
2363 | - self.mediaObject.clearQueue() |
2364 | - self.mediaObject.setCurrentSource(Phonon.MediaSource(file)) |
2365 | - self.seekSlider.setMediaObject(self.mediaObject) |
2366 | - self.seekSlider.show() |
2367 | - self.onMediaPlay() |
2368 | - |
2369 | - def mediaVolume(self): |
2370 | - """ |
2371 | - Respond to the release of Volume Slider |
2372 | - """ |
2373 | - log.debug(u'SlideController mediaVolume') |
2374 | - self.volume = self.volumeSlider.value() |
2375 | - self.display.videoVolume(self.volume) |
2376 | - |
2377 | - def onMediaPause(self): |
2378 | - """ |
2379 | - Respond to the Pause from the media Toolbar |
2380 | - """ |
2381 | - log.debug(u'SlideController onMediaPause') |
2382 | - if self.isLive: |
2383 | - self.display.videoPause() |
2384 | - else: |
2385 | - self.mediaObject.pause() |
2386 | - |
2387 | - def onMediaPlay(self): |
2388 | - """ |
2389 | - Respond to the Play from the media Toolbar |
2390 | - """ |
2391 | - log.debug(u'SlideController onMediaPlay') |
2392 | - if self.isLive: |
2393 | - self.display.videoPlay() |
2394 | - else: |
2395 | + self.mediaController.video(self, file, False, False) |
2396 | + if not self.isLive or self.mediaController.withLivePreview: |
2397 | + self.previewDisplay.show() |
2398 | self.slidePreview.hide() |
2399 | - self.video.show() |
2400 | - self.mediaObject.play() |
2401 | - |
2402 | - def onMediaStop(self): |
2403 | - """ |
2404 | - Respond to the Stop from the media Toolbar |
2405 | - """ |
2406 | - log.debug(u'SlideController onMediaStop') |
2407 | - if self.isLive: |
2408 | - self.display.videoStop() |
2409 | - else: |
2410 | - self.mediaObject.stop() |
2411 | - self.video.hide() |
2412 | - self.slidePreview.clear() |
2413 | - self.slidePreview.show() |
2414 | |
2415 | def onMediaClose(self): |
2416 | """ |
2417 | Respond to a request to close the Video |
2418 | """ |
2419 | - log.debug(u'SlideController onMediaStop') |
2420 | - if self.isLive: |
2421 | - self.display.resetVideo() |
2422 | - else: |
2423 | - self.mediaObject.stop() |
2424 | - self.mediaObject.clearQueue() |
2425 | - self.video.hide() |
2426 | - self.slidePreview.clear() |
2427 | + log.debug(u'SlideController onMediaClose') |
2428 | + self.mediaController.video_reset(self) |
2429 | + self.previewDisplay.hide() |
2430 | self.slidePreview.show() |
2431 | |
2432 | def _resetBlank(self): |
2433 | |
2434 | === modified file 'openlp/plugins/media/lib/mediaitem.py' |
2435 | --- openlp/plugins/media/lib/mediaitem.py 2011-10-15 11:30:39 +0000 |
2436 | +++ openlp/plugins/media/lib/mediaitem.py 2011-12-01 18:30:31 +0000 |
2437 | @@ -25,7 +25,6 @@ |
2438 | # Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
2439 | ############################################################################### |
2440 | |
2441 | -from datetime import datetime |
2442 | import logging |
2443 | import os |
2444 | import locale |
2445 | @@ -34,12 +33,17 @@ |
2446 | from PyQt4.phonon import Phonon |
2447 | |
2448 | from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \ |
2449 | - SettingsManager, translate, check_item_selected, Receiver, MediaType |
2450 | -from openlp.core.lib.ui import UiStrings, critical_error_message_box |
2451 | + SettingsManager, translate, check_item_selected, Receiver, MediaType, \ |
2452 | + ServiceItem, build_html |
2453 | +from openlp.core.lib.ui import UiStrings, critical_error_message_box, \ |
2454 | + media_item_combo_box |
2455 | +from openlp.core.ui import Controller, Display |
2456 | |
2457 | log = logging.getLogger(__name__) |
2458 | |
2459 | CLAPPERBOARD = QtGui.QImage(u':/media/media_video.png') |
2460 | +#TODO: Add an appropriate Icon for DVDs, CDs, ... |
2461 | +DVD_ICON = QtGui.QImage(u':/media/media_video.png') |
2462 | |
2463 | class MediaMediaItem(MediaManagerItem): |
2464 | """ |
2465 | @@ -51,16 +55,40 @@ |
2466 | self.iconPath = u'images/image' |
2467 | self.background = False |
2468 | self.previewFunction = CLAPPERBOARD |
2469 | + self.Automatic = u'' |
2470 | MediaManagerItem.__init__(self, parent, plugin, icon) |
2471 | self.singleServiceItem = False |
2472 | self.hasSearch = True |
2473 | self.mediaObject = None |
2474 | + self.mediaController = Controller(parent) |
2475 | + self.mediaController.controllerLayout = QtGui.QVBoxLayout() |
2476 | + self.plugin.mediaController.add_controller_items(self.mediaController, \ |
2477 | + self.mediaController.controllerLayout) |
2478 | + self.plugin.mediaController.set_controls_visible(self.mediaController, \ |
2479 | + False) |
2480 | + self.mediaController.previewDisplay = Display(self.mediaController, \ |
2481 | + False, self.mediaController) |
2482 | + self.mediaController.previewDisplay.setGeometry( |
2483 | + QtCore.QRect(0, 0, 300, 300)) |
2484 | + self.mediaController.previewDisplay.screen = \ |
2485 | + {u'size':self.mediaController.previewDisplay.geometry()} |
2486 | + self.mediaController.previewDisplay.setup() |
2487 | + serviceItem = ServiceItem() |
2488 | + self.mediaController.previewDisplay.webView.setHtml(build_html( \ |
2489 | + serviceItem, self.mediaController.previewDisplay.screen, None, \ |
2490 | + False, None)) |
2491 | + self.mediaController.previewDisplay.setup() |
2492 | + self.plugin.mediaController.setup_display( \ |
2493 | + self.mediaController.previewDisplay) |
2494 | + self.mediaController.previewDisplay.hide() |
2495 | + |
2496 | QtCore.QObject.connect(Receiver.get_receiver(), |
2497 | QtCore.SIGNAL(u'video_background_replaced'), |
2498 | self.videobackgroundReplaced) |
2499 | QtCore.QObject.connect(Receiver.get_receiver(), |
2500 | - QtCore.SIGNAL(u'openlp_phonon_creation'), |
2501 | - self.createPhonon) |
2502 | + QtCore.SIGNAL(u'mediaitem_media_rebuild'), self.rebuild) |
2503 | + QtCore.QObject.connect(Receiver.get_receiver(), |
2504 | + QtCore.SIGNAL(u'config_screen_changed'), self.displaySetup) |
2505 | # Allow DnD from the desktop |
2506 | self.listView.activateDnD() |
2507 | |
2508 | @@ -74,6 +102,10 @@ |
2509 | self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG) |
2510 | self.resetAction.setText(UiStrings().ResetBG) |
2511 | self.resetAction.setToolTip(UiStrings().ResetLiveBG) |
2512 | + self.Automatic = translate('MediaPlugin.MediaItem', |
2513 | + 'Automatic') |
2514 | + self.displayTypeLabel.setText( |
2515 | + translate('MediaPlugin.MediaItem', 'Use Player:')) |
2516 | |
2517 | def requiredIcons(self): |
2518 | MediaManagerItem.requiredIcons(self) |
2519 | @@ -92,13 +124,34 @@ |
2520 | self.resetAction = self.addToolbarButton(u'', u'', |
2521 | u':/system/system_close.png', self.onResetClick, False) |
2522 | self.resetAction.setVisible(False) |
2523 | + self.mediaWidget = QtGui.QWidget(self) |
2524 | + self.mediaWidget.setObjectName(u'mediaWidget') |
2525 | + self.displayLayout = QtGui.QFormLayout(self.mediaWidget) |
2526 | + self.displayLayout.setMargin(self.displayLayout.spacing()) |
2527 | + self.displayLayout.setObjectName(u'displayLayout') |
2528 | + self.displayTypeLabel = QtGui.QLabel(self.mediaWidget) |
2529 | + self.displayTypeLabel.setObjectName(u'displayTypeLabel') |
2530 | + self.displayTypeComboBox = media_item_combo_box( |
2531 | + self.mediaWidget, u'displayTypeComboBox') |
2532 | + self.displayTypeLabel.setBuddy(self.displayTypeComboBox) |
2533 | + self.displayLayout.addRow(self.displayTypeLabel, |
2534 | + self.displayTypeComboBox) |
2535 | + # Add the Media widget to the page layout |
2536 | + self.pageLayout.addWidget(self.mediaWidget) |
2537 | + QtCore.QObject.connect(self.displayTypeComboBox, |
2538 | + QtCore.SIGNAL(u'currentIndexChanged (int)'), self.overridePlayerChanged) |
2539 | + |
2540 | + def overridePlayerChanged(self, index): |
2541 | + Receiver.send_message(u'media_override_player', \ |
2542 | + u'%s' % self.displayTypeComboBox.currentText()) |
2543 | |
2544 | def onResetClick(self): |
2545 | """ |
2546 | - Called to reset the Live backgound with the media selected. |
2547 | + Called to reset the Live background with the media selected, |
2548 | """ |
2549 | + self.plugin.liveController.mediaController.video_reset( \ |
2550 | + self.plugin.liveController) |
2551 | self.resetAction.setVisible(False) |
2552 | - self.plugin.liveController.display.resetVideo() |
2553 | |
2554 | def videobackgroundReplaced(self): |
2555 | """ |
2556 | @@ -108,7 +161,7 @@ |
2557 | |
2558 | def onReplaceClick(self): |
2559 | """ |
2560 | - Called to replace Live backgound with the media selected. |
2561 | + Called to replace Live background with the media selected. |
2562 | """ |
2563 | if check_item_selected(self.listView, |
2564 | translate('MediaPlugin.MediaItem', |
2565 | @@ -116,8 +169,8 @@ |
2566 | item = self.listView.currentItem() |
2567 | filename = unicode(item.data(QtCore.Qt.UserRole).toString()) |
2568 | if os.path.exists(filename): |
2569 | - (path, name) = os.path.split(filename) |
2570 | - if self.plugin.liveController.display.video(filename, 0, True): |
2571 | + if self.plugin.liveController.mediaController.video( \ |
2572 | + self.plugin.liveController, filename, True, True): |
2573 | self.resetAction.setVisible(True) |
2574 | else: |
2575 | critical_error_message_box(UiStrings().LiveBGError, |
2576 | @@ -144,30 +197,18 @@ |
2577 | unicode(translate('MediaPlugin.MediaItem', |
2578 | 'The file %s no longer exists.')) % filename) |
2579 | return False |
2580 | - self.mediaObject.stop() |
2581 | - self.mediaObject.clearQueue() |
2582 | - self.mediaObject.setCurrentSource(Phonon.MediaSource(filename)) |
2583 | - if not self.mediaStateWait(Phonon.StoppedState): |
2584 | - critical_error_message_box(UiStrings().UnsupportedFile, |
2585 | - UiStrings().UnsupportedFile) |
2586 | + self.mediaLength = 0 |
2587 | + if self.plugin.mediaController.video( \ |
2588 | + self.mediaController, filename, False, False): |
2589 | + self.mediaLength = self.mediaController.media_info.length |
2590 | + service_item.media_length = self.mediaLength |
2591 | + self.plugin.mediaController.video_reset(self.mediaController) |
2592 | + if self.mediaLength > 0: |
2593 | + service_item.add_capability( |
2594 | + ItemCapabilities.HasVariableStartTime) |
2595 | + else: |
2596 | return False |
2597 | - # File too big for processing |
2598 | - if os.path.getsize(filename) <= 52428800: # 50MiB |
2599 | - self.mediaObject.play() |
2600 | - if not self.mediaStateWait(Phonon.PlayingState) \ |
2601 | - or self.mediaObject.currentSource().type() \ |
2602 | - == Phonon.MediaSource.Invalid: |
2603 | - self.mediaObject.stop() |
2604 | - critical_error_message_box( |
2605 | - translate('MediaPlugin.MediaItem', 'File Too Big'), |
2606 | - translate('MediaPlugin.MediaItem', 'The file you are ' |
2607 | - 'trying to load is too big. Please reduce it to less ' |
2608 | - 'than 50MiB.')) |
2609 | - return False |
2610 | - self.mediaObject.stop() |
2611 | - service_item.media_length = self.mediaObject.totalTime() / 1000 |
2612 | - service_item.add_capability( |
2613 | - ItemCapabilities.HasVariableStartTime) |
2614 | + service_item.media_length = self.mediaLength |
2615 | service_item.title = unicode(self.plugin.nameStrings[u'singular']) |
2616 | service_item.add_capability(ItemCapabilities.RequiresMedia) |
2617 | # force a non-existent theme |
2618 | @@ -177,23 +218,49 @@ |
2619 | service_item.add_from_command(path, name, frame) |
2620 | return True |
2621 | |
2622 | - def mediaStateWait(self, mediaState): |
2623 | - """ |
2624 | - Wait for the video to change its state. Wait no longer than 5 seconds. |
2625 | - """ |
2626 | - start = datetime.now() |
2627 | - while self.mediaObject.state() != mediaState: |
2628 | - if self.mediaObject.state() == Phonon.ErrorState: |
2629 | - return False |
2630 | - Receiver.send_message(u'openlp_process_events') |
2631 | - if (datetime.now() - start).seconds > 5: |
2632 | - return False |
2633 | - return True |
2634 | - |
2635 | def initialise(self): |
2636 | self.listView.clear() |
2637 | self.listView.setIconSize(QtCore.QSize(88, 50)) |
2638 | self.loadList(SettingsManager.load_list(self.settingsSection, u'media')) |
2639 | + self.populateDisplayTypes() |
2640 | + |
2641 | + def rebuild(self): |
2642 | + """ |
2643 | + Rebuild the tab in the media manager when changes are made in |
2644 | + the settings |
2645 | + """ |
2646 | + self.populateDisplayTypes() |
2647 | + self.onNewFileMasks = unicode(translate('MediaPlugin.MediaItem', |
2648 | + 'Videos (%s);;Audio (%s);;%s (*)')) % ( |
2649 | + u' '.join(self.plugin.video_extensions_list), |
2650 | + u' '.join(self.plugin.audio_extensions_list), UiStrings().AllFiles) |
2651 | + |
2652 | + def displaySetup(self): |
2653 | + self.plugin.mediaController.setup_display( \ |
2654 | + self.mediaController.previewDisplay) |
2655 | + |
2656 | + |
2657 | + def populateDisplayTypes(self): |
2658 | + """ |
2659 | + Load the combobox with the enabled media players, |
2660 | + allowing user to select a specific player if settings allow |
2661 | + """ |
2662 | + self.displayTypeComboBox.clear() |
2663 | + playerSettings = str(QtCore.QSettings().value(u'media/players', |
2664 | + QtCore.QVariant(u'webkit')).toString()) |
2665 | + usedPlayers = playerSettings.split(u',') |
2666 | + for title in usedPlayers: |
2667 | + # load the drop down selection |
2668 | + self.displayTypeComboBox.addItem(title) |
2669 | + if self.displayTypeComboBox.count() > 1: |
2670 | + self.displayTypeComboBox.insertItem(0, self.Automatic) |
2671 | + self.displayTypeComboBox.setCurrentIndex(0) |
2672 | + if QtCore.QSettings().value(self.settingsSection + u'/override player', |
2673 | + QtCore.QVariant(QtCore.Qt.Unchecked)) == QtCore.Qt.Checked: |
2674 | + self.mediaWidget.show() |
2675 | + else: |
2676 | + self.mediaWidget.hide() |
2677 | + |
2678 | |
2679 | def onDeleteClick(self): |
2680 | """ |
2681 | @@ -214,10 +281,18 @@ |
2682 | media.sort(cmp=locale.strcoll, |
2683 | key=lambda filename: os.path.split(unicode(filename))[1].lower()) |
2684 | for track in media: |
2685 | - filename = os.path.split(unicode(track))[1] |
2686 | - item_name = QtGui.QListWidgetItem(filename) |
2687 | - item_name.setIcon(build_icon(CLAPPERBOARD)) |
2688 | - item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track)) |
2689 | + track_info = QtCore.QFileInfo(track) |
2690 | + if not track_info.isFile(): |
2691 | + filename = os.path.split(unicode(track))[1] |
2692 | + item_name = QtGui.QListWidgetItem(filename) |
2693 | + item_name.setIcon(build_icon(CLAPPERBOARD)) |
2694 | + item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track)) |
2695 | + else: |
2696 | + filename = os.path.split(unicode(track))[1] |
2697 | + item_name = QtGui.QListWidgetItem(filename) |
2698 | + #TODO: add the appropriate Icon |
2699 | + #item_name.setIcon(build_icon(DVD_ICON)) |
2700 | + item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track)) |
2701 | item_name.setToolTip(track) |
2702 | self.listView.addItem(item_name) |
2703 | |
2704 | @@ -234,11 +309,6 @@ |
2705 | media = filter(lambda x: os.path.splitext(x)[1] in ext, media) |
2706 | return media |
2707 | |
2708 | - def createPhonon(self): |
2709 | - log.debug(u'CreatePhonon') |
2710 | - if not self.mediaObject: |
2711 | - self.mediaObject = Phonon.MediaObject(self) |
2712 | - |
2713 | def search(self, string): |
2714 | files = SettingsManager.load_list(self.settingsSection, u'media') |
2715 | results = [] |
2716 | |
2717 | === modified file 'openlp/plugins/media/lib/mediatab.py' |
2718 | --- openlp/plugins/media/lib/mediatab.py 2011-06-12 16:02:52 +0000 |
2719 | +++ openlp/plugins/media/lib/mediatab.py 2011-12-01 18:30:31 +0000 |
2720 | @@ -28,51 +28,181 @@ |
2721 | from PyQt4 import QtCore, QtGui |
2722 | |
2723 | from openlp.core.lib import SettingsTab, translate, Receiver |
2724 | +from openlp.core.lib.ui import UiStrings, critical_error_message_box |
2725 | |
2726 | class MediaTab(SettingsTab): |
2727 | """ |
2728 | MediaTab is the Media settings tab in the settings dialog. |
2729 | """ |
2730 | - def __init__(self, parent, title, visible_title, icon_path): |
2731 | + def __init__(self, parent, title, visible_title, media_players, icon_path): |
2732 | + self.media_players = media_players |
2733 | SettingsTab.__init__(self, parent, title, visible_title, icon_path) |
2734 | |
2735 | def setupUi(self): |
2736 | self.setObjectName(u'MediaTab') |
2737 | SettingsTab.setupUi(self) |
2738 | - self.mediaModeGroupBox = QtGui.QGroupBox(self.leftColumn) |
2739 | - self.mediaModeGroupBox.setObjectName(u'mediaModeGroupBox') |
2740 | - self.mediaModeLayout = QtGui.QFormLayout(self.mediaModeGroupBox) |
2741 | - self.mediaModeLayout.setObjectName(u'mediaModeLayout') |
2742 | - self.usePhononCheckBox = QtGui.QCheckBox(self.mediaModeGroupBox) |
2743 | - self.usePhononCheckBox.setObjectName(u'usePhononCheckBox') |
2744 | - self.mediaModeLayout.addRow(self.usePhononCheckBox) |
2745 | - self.leftLayout.addWidget(self.mediaModeGroupBox) |
2746 | + self.mediaPlayerGroupBox = QtGui.QGroupBox(self.leftColumn) |
2747 | + self.mediaPlayerGroupBox.setObjectName(u'mediaPlayerGroupBox') |
2748 | + self.mediaPlayerLayout = QtGui.QVBoxLayout(self.mediaPlayerGroupBox) |
2749 | + self.mediaPlayerLayout.setObjectName(u'mediaPlayerLayout') |
2750 | + self.PlayerCheckBoxes = {} |
2751 | + for key in self.media_players: |
2752 | + player = self.media_players[key] |
2753 | + checkbox = QtGui.QCheckBox(self.mediaPlayerGroupBox) |
2754 | + checkbox.setEnabled(player.available) |
2755 | + checkbox.setObjectName(player.name + u'CheckBox') |
2756 | + self.PlayerCheckBoxes[player.name] = checkbox |
2757 | + self.mediaPlayerLayout.addWidget(checkbox) |
2758 | + self.leftLayout.addWidget(self.mediaPlayerGroupBox) |
2759 | + self.playerOrderGroupBox = QtGui.QGroupBox(self.leftColumn) |
2760 | + self.playerOrderGroupBox.setObjectName(u'playerOrderGroupBox') |
2761 | + self.playerOrderLayout = QtGui.QVBoxLayout(self.playerOrderGroupBox) |
2762 | + self.playerOrderLayout.setObjectName(u'playerOrderLayout') |
2763 | + self.playerOrderlistWidget = QtGui.QListWidget( \ |
2764 | + self.playerOrderGroupBox) |
2765 | + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, |
2766 | + QtGui.QSizePolicy.Expanding) |
2767 | + sizePolicy.setHorizontalStretch(0) |
2768 | + sizePolicy.setVerticalStretch(0) |
2769 | + sizePolicy.setHeightForWidth(self.playerOrderlistWidget. \ |
2770 | + sizePolicy().hasHeightForWidth()) |
2771 | + self.playerOrderlistWidget.setSizePolicy(sizePolicy) |
2772 | + self.playerOrderlistWidget.setVerticalScrollBarPolicy( \ |
2773 | + QtCore.Qt.ScrollBarAsNeeded) |
2774 | + self.playerOrderlistWidget.setHorizontalScrollBarPolicy( \ |
2775 | + QtCore.Qt.ScrollBarAlwaysOff) |
2776 | + self.playerOrderlistWidget.setEditTriggers( \ |
2777 | + QtGui.QAbstractItemView.NoEditTriggers) |
2778 | + self.playerOrderlistWidget.setObjectName(u'playerOrderlistWidget') |
2779 | + self.playerOrderLayout.addWidget(self.playerOrderlistWidget) |
2780 | + self.orderingButtonsWidget = QtGui.QWidget(self.playerOrderGroupBox) |
2781 | + self.orderingButtonsWidget.setObjectName(u'orderingButtonsWidget') |
2782 | + self.orderingButtonLayout = QtGui.QHBoxLayout( \ |
2783 | + self.orderingButtonsWidget) |
2784 | + self.orderingButtonLayout.setObjectName(u'orderingButtonLayout') |
2785 | + self.orderingDownButton = QtGui.QPushButton(self.orderingButtonsWidget) |
2786 | + self.orderingDownButton.setObjectName(u'orderingDownButton') |
2787 | + self.orderingButtonLayout.addWidget(self.orderingDownButton) |
2788 | + self.orderingUpButton = QtGui.QPushButton(self.playerOrderGroupBox) |
2789 | + self.orderingUpButton.setObjectName(u'orderingUpButton') |
2790 | + self.orderingButtonLayout.addWidget(self.orderingUpButton) |
2791 | + self.playerOrderLayout.addWidget(self.orderingButtonsWidget) |
2792 | + self.leftLayout.addWidget(self.playerOrderGroupBox) |
2793 | + self.AdvancedGroupBox = QtGui.QGroupBox(self.leftColumn) |
2794 | + self.AdvancedGroupBox.setObjectName(u'AdvancedGroupBox') |
2795 | + self.AdvancedLayout = QtGui.QVBoxLayout(self.AdvancedGroupBox) |
2796 | + self.AdvancedLayout.setObjectName(u'AdvancedLayout') |
2797 | + self.OverridePlayerCheckBox = QtGui.QCheckBox(self.AdvancedGroupBox) |
2798 | + self.OverridePlayerCheckBox.setObjectName(u'OverridePlayerCheckBox') |
2799 | + self.AdvancedLayout.addWidget(self.OverridePlayerCheckBox) |
2800 | + self.leftLayout.addWidget(self.AdvancedGroupBox) |
2801 | self.leftLayout.addStretch() |
2802 | self.rightLayout.addStretch() |
2803 | - QtCore.QObject.connect(self.usePhononCheckBox, |
2804 | - QtCore.SIGNAL(u'stateChanged(int)'), |
2805 | - self.onUsePhononCheckBoxChanged) |
2806 | + for key in self.media_players: |
2807 | + player = self.media_players[key] |
2808 | + checkbox = self.PlayerCheckBoxes[player.name] |
2809 | + QtCore.QObject.connect(checkbox, |
2810 | + QtCore.SIGNAL(u'stateChanged(int)'), |
2811 | + self.onPlayerCheckBoxChanged) |
2812 | + QtCore.QObject.connect(self.orderingUpButton, |
2813 | + QtCore.SIGNAL(u'pressed()'), self.onOrderingUpButtonPressed) |
2814 | + QtCore.QObject.connect(self.orderingDownButton, |
2815 | + QtCore.SIGNAL(u'pressed()'), self.onOrderingDownButtonPressed) |
2816 | |
2817 | def retranslateUi(self): |
2818 | - self.mediaModeGroupBox.setTitle( |
2819 | - translate('MediaPlugin.MediaTab', 'Media Display')) |
2820 | - self.usePhononCheckBox.setText( |
2821 | - translate('MediaPlugin.MediaTab', 'Use Phonon for video playback')) |
2822 | - |
2823 | - def onUsePhononCheckBoxChanged(self, check_state): |
2824 | - self.usePhonon = (check_state == QtCore.Qt.Checked) |
2825 | - self.usePhononChanged = True |
2826 | + self.mediaPlayerGroupBox.setTitle( |
2827 | + translate('MediaPlugin.MediaTab', 'Available Media Players')) |
2828 | + for key in self.media_players: |
2829 | + player = self.media_players[key] |
2830 | + checkbox = self.PlayerCheckBoxes[player.name] |
2831 | + if player.available: |
2832 | + checkbox.setText(player.name) |
2833 | + else: |
2834 | + checkbox.setText( |
2835 | + unicode(translate('MediaPlugin.MediaTab', |
2836 | + '%s (unavailable)')) % player.name) |
2837 | + self.playerOrderGroupBox.setTitle( |
2838 | + translate('MediaPlugin.MediaTab', 'Player Order')) |
2839 | + self.orderingDownButton.setText( |
2840 | + translate('MediaPlugin.MediaTab', 'Down')) |
2841 | + self.orderingUpButton.setText( |
2842 | + translate('MediaPlugin.MediaTab', 'Up')) |
2843 | + self.AdvancedGroupBox.setTitle(UiStrings().Advanced) |
2844 | + self.OverridePlayerCheckBox.setText( |
2845 | + translate('MediaPlugin.MediaTab', |
2846 | + 'Allow media player to be overriden')) |
2847 | + |
2848 | + def onPlayerCheckBoxChanged(self, check_state): |
2849 | + player = self.sender().text() |
2850 | + if check_state == QtCore.Qt.Checked: |
2851 | + if player not in self.usedPlayers: |
2852 | + self.usedPlayers.append(player) |
2853 | + else: |
2854 | + self.usedPlayers.takeAt(self.usedPlayers.indexOf(player)) |
2855 | + self.updatePlayerList() |
2856 | + |
2857 | + def updatePlayerList(self): |
2858 | + self.playerOrderlistWidget.clear() |
2859 | + for player in self.usedPlayers: |
2860 | + if player in self.PlayerCheckBoxes.keys(): |
2861 | + if len(self.usedPlayers) == 1: |
2862 | + # at least one media player have to stay active |
2863 | + self.PlayerCheckBoxes[u'%s' % player].setEnabled(False) |
2864 | + else: |
2865 | + self.PlayerCheckBoxes[u'%s' % player].setEnabled(True) |
2866 | + self.playerOrderlistWidget.addItem(player) |
2867 | + |
2868 | + def onOrderingUpButtonPressed(self): |
2869 | + currentRow = self.playerOrderlistWidget.currentRow() |
2870 | + if currentRow > 0: |
2871 | + item = self.playerOrderlistWidget.takeItem(currentRow) |
2872 | + self.playerOrderlistWidget.insertItem(currentRow - 1, item) |
2873 | + self.playerOrderlistWidget.setCurrentRow(currentRow - 1) |
2874 | + self.usedPlayers.move(currentRow, currentRow - 1) |
2875 | + |
2876 | + def onOrderingDownButtonPressed(self): |
2877 | + currentRow = self.playerOrderlistWidget.currentRow() |
2878 | + if currentRow < self.playerOrderlistWidget.count() - 1: |
2879 | + item = self.playerOrderlistWidget.takeItem(currentRow) |
2880 | + self.playerOrderlistWidget.insertItem(currentRow + 1, item) |
2881 | + self.playerOrderlistWidget.setCurrentRow(currentRow + 1) |
2882 | + self.usedPlayers.move(currentRow, currentRow + 1) |
2883 | |
2884 | def load(self): |
2885 | - self.usePhonon = QtCore.QSettings().value( |
2886 | - self.settingsSection + u'/use phonon', |
2887 | - QtCore.QVariant(True)).toBool() |
2888 | - self.usePhononCheckBox.setChecked(self.usePhonon) |
2889 | + self.usedPlayers = QtCore.QSettings().value( |
2890 | + self.settingsSection + u'/players', |
2891 | + QtCore.QVariant(u'webkit')).toString().split(u',') |
2892 | + for key in self.media_players: |
2893 | + player = self.media_players[key] |
2894 | + checkbox = self.PlayerCheckBoxes[player.name] |
2895 | + if player.available and player.name in self.usedPlayers: |
2896 | + checkbox.setChecked(True) |
2897 | + self.updatePlayerList() |
2898 | + self.OverridePlayerCheckBox.setChecked(QtCore.QSettings().value( |
2899 | + self.settingsSection + u'/override player', |
2900 | + QtCore.QVariant(QtCore.Qt.Unchecked)).toInt()[0]) |
2901 | |
2902 | def save(self): |
2903 | - oldUsePhonon = QtCore.QSettings().value( |
2904 | - u'media/use phonon', QtCore.QVariant(True)).toBool() |
2905 | - if oldUsePhonon != self.usePhonon: |
2906 | - QtCore.QSettings().setValue(self.settingsSection + u'/use phonon', |
2907 | - QtCore.QVariant(self.usePhonon)) |
2908 | + override_changed = False |
2909 | + player_string_changed = False |
2910 | + oldPlayerString = QtCore.QSettings().value( |
2911 | + self.settingsSection + u'/players', |
2912 | + QtCore.QVariant(u'webkit')).toString() |
2913 | + newPlayerString = self.usedPlayers.join(u',') |
2914 | + if oldPlayerString != newPlayerString: |
2915 | + # clean old Media stuff |
2916 | + QtCore.QSettings().setValue(self.settingsSection + u'/players', |
2917 | + QtCore.QVariant(newPlayerString)) |
2918 | + player_string_changed = True |
2919 | + override_changed = True |
2920 | + setting_key = self.settingsSection + u'/override player' |
2921 | + if QtCore.QSettings().value(setting_key) != \ |
2922 | + self.OverridePlayerCheckBox.checkState(): |
2923 | + QtCore.QSettings().setValue(setting_key, |
2924 | + QtCore.QVariant(self.OverridePlayerCheckBox.checkState())) |
2925 | + override_changed = True |
2926 | + if override_changed: |
2927 | + Receiver.send_message(u'mediaitem_media_rebuild') |
2928 | + if player_string_changed: |
2929 | + Receiver.send_message(u'mediaitem_media_rebuild') |
2930 | Receiver.send_message(u'config_screen_changed') |
2931 | |
2932 | === modified file 'openlp/plugins/media/mediaplugin.py' |
2933 | --- openlp/plugins/media/mediaplugin.py 2011-07-23 21:29:24 +0000 |
2934 | +++ openlp/plugins/media/mediaplugin.py 2011-12-01 18:30:31 +0000 |
2935 | @@ -26,9 +26,7 @@ |
2936 | ############################################################################### |
2937 | |
2938 | import logging |
2939 | -import mimetypes |
2940 | - |
2941 | -from PyQt4.phonon import Phonon |
2942 | +import os |
2943 | |
2944 | from openlp.core.lib import Plugin, StringContent, build_icon, translate |
2945 | from openlp.plugins.media.lib import MediaMediaItem, MediaTab |
2946 | @@ -40,57 +38,28 @@ |
2947 | |
2948 | def __init__(self, plugin_helpers): |
2949 | Plugin.__init__(self, u'media', plugin_helpers, |
2950 | - MediaMediaItem, MediaTab) |
2951 | + MediaMediaItem) |
2952 | self.weight = -6 |
2953 | self.icon_path = u':/plugins/plugin_media.png' |
2954 | self.icon = build_icon(self.icon_path) |
2955 | # passed with drag and drop messages |
2956 | self.dnd_id = u'Media' |
2957 | - self.additional_extensions = { |
2958 | - u'audio/ac3': [u'.ac3'], |
2959 | - u'audio/flac': [u'.flac'], |
2960 | - u'audio/x-m4a': [u'.m4a'], |
2961 | - u'audio/midi': [u'.mid', u'.midi'], |
2962 | - u'audio/x-mp3': [u'.mp3'], |
2963 | - u'audio/mpeg': [u'.mp3', u'.mp2', u'.mpga', u'.mpega', u'.m4a'], |
2964 | - u'audio/qcelp': [u'.qcp'], |
2965 | - u'audio/x-wma': [u'.wma'], |
2966 | - u'audio/x-ms-wma': [u'.wma'], |
2967 | - u'video/x-flv': [u'.flv'], |
2968 | - u'video/x-matroska': [u'.mpv', u'.mkv'], |
2969 | - u'video/x-wmv': [u'.wmv'], |
2970 | - u'video/x-ms-wmv': [u'.wmv']} |
2971 | - self.audio_extensions_list = [] |
2972 | - self.video_extensions_list = [] |
2973 | - mimetypes.init() |
2974 | - for mimetype in Phonon.BackendCapabilities.availableMimeTypes(): |
2975 | - mimetype = unicode(mimetype) |
2976 | - if mimetype.startswith(u'audio/'): |
2977 | - self._addToList(self.audio_extensions_list, mimetype) |
2978 | - elif mimetype.startswith(u'video/'): |
2979 | - self._addToList(self.video_extensions_list, mimetype) |
2980 | + self.audio_extensions_list = \ |
2981 | + self.mediaController.get_audio_extensions_list() |
2982 | + for ext in self.audio_extensions_list: |
2983 | + self.serviceManager.supportedSuffixes(ext[2:]) |
2984 | + self.video_extensions_list = \ |
2985 | + self.mediaController.get_video_extensions_list() |
2986 | + for ext in self.video_extensions_list: |
2987 | + self.serviceManager.supportedSuffixes(ext[2:]) |
2988 | |
2989 | - def _addToList(self, list, mimetype): |
2990 | - # Add all extensions which mimetypes provides us for supported types. |
2991 | - extensions = mimetypes.guess_all_extensions(unicode(mimetype)) |
2992 | - for extension in extensions: |
2993 | - ext = u'*%s' % extension |
2994 | - if ext not in list: |
2995 | - list.append(ext) |
2996 | - self.serviceManager.supportedSuffixes(extension[1:]) |
2997 | - log.info(u'MediaPlugin: %s extensions: %s' % (mimetype, |
2998 | - u' '.join(extensions))) |
2999 | - # Add extensions for this mimetype from self.additional_extensions. |
3000 | - # This hack clears mimetypes' and operating system's shortcomings |
3001 | - # by providing possibly missing extensions. |
3002 | - if mimetype in self.additional_extensions.keys(): |
3003 | - for extension in self.additional_extensions[mimetype]: |
3004 | - ext = u'*%s' % extension |
3005 | - if ext not in list: |
3006 | - list.append(ext) |
3007 | - self.serviceManager.supportedSuffixes(extension[1:]) |
3008 | - log.info(u'MediaPlugin: %s additional extensions: %s' % (mimetype, |
3009 | - u' '.join(self.additional_extensions[mimetype]))) |
3010 | + def getSettingsTab(self, parent): |
3011 | + """ |
3012 | + Create the settings Tab |
3013 | + """ |
3014 | + visible_name = self.getString(StringContent.VisibleName) |
3015 | + return MediaTab(parent, self.name, visible_name[u'title'], |
3016 | + self.mediaController.mediaPlayers, self.icon_path) |
3017 | |
3018 | def about(self): |
3019 | about_text = translate('MediaPlugin', '<strong>Media Plugin</strong>' |
3020 | @@ -123,3 +92,29 @@ |
3021 | 'Add the selected media to the service.') |
3022 | } |
3023 | self.setPluginUiTextStrings(tooltips) |
3024 | + |
3025 | + def finalise(self): |
3026 | + """ |
3027 | + Time to tidy up on exit |
3028 | + """ |
3029 | + log.info(u'Media Finalising') |
3030 | + self.mediaController.finalise() |
3031 | + Plugin.finalise(self) |
3032 | + |
3033 | + def getDisplayCss(self): |
3034 | + """ |
3035 | + Add css style sheets to htmlbuilder |
3036 | + """ |
3037 | + return self.mediaController.get_media_display_css() |
3038 | + |
3039 | + def getDisplayJavaScript(self): |
3040 | + """ |
3041 | + Add javascript functions to htmlbuilder |
3042 | + """ |
3043 | + return self.mediaController.get_media_display_javascript() |
3044 | + |
3045 | + def getDisplayHtml(self): |
3046 | + """ |
3047 | + Add html code to htmlbuilder |
3048 | + """ |
3049 | + return self.mediaController.get_media_display_html() |
3050 | |
3051 | === removed file 'resources/forms/mediafilesdialog.ui' |
3052 | --- resources/forms/mediafilesdialog.ui 2011-08-23 21:48:46 +0000 |
3053 | +++ resources/forms/mediafilesdialog.ui 1970-01-01 00:00:00 +0000 |
3054 | @@ -1,95 +0,0 @@ |
3055 | -<?xml version="1.0" encoding="UTF-8"?> |
3056 | -<ui version="4.0"> |
3057 | - <class>MediaFilesDialog</class> |
3058 | - <widget class="QDialog" name="MediaFilesDialog"> |
3059 | - <property name="windowModality"> |
3060 | - <enum>Qt::ApplicationModal</enum> |
3061 | - </property> |
3062 | - <property name="geometry"> |
3063 | - <rect> |
3064 | - <x>0</x> |
3065 | - <y>0</y> |
3066 | - <width>400</width> |
3067 | - <height>300</height> |
3068 | - </rect> |
3069 | - </property> |
3070 | - <property name="windowTitle"> |
3071 | - <string>Select Media File(s)</string> |
3072 | - </property> |
3073 | - <property name="modal"> |
3074 | - <bool>true</bool> |
3075 | - </property> |
3076 | - <layout class="QVBoxLayout" name="filesVerticalLayout"> |
3077 | - <property name="spacing"> |
3078 | - <number>8</number> |
3079 | - </property> |
3080 | - <property name="margin"> |
3081 | - <number>8</number> |
3082 | - </property> |
3083 | - <item> |
3084 | - <widget class="QLabel" name="selectLabel"> |
3085 | - <property name="text"> |
3086 | - <string>Select one or more audio files from the list below, and click OK to import them into this song.</string> |
3087 | - </property> |
3088 | - <property name="wordWrap"> |
3089 | - <bool>true</bool> |
3090 | - </property> |
3091 | - </widget> |
3092 | - </item> |
3093 | - <item> |
3094 | - <widget class="QListView" name="fileListView"> |
3095 | - <property name="alternatingRowColors"> |
3096 | - <bool>true</bool> |
3097 | - </property> |
3098 | - </widget> |
3099 | - </item> |
3100 | - <item> |
3101 | - <widget class="QDialogButtonBox" name="buttonBox"> |
3102 | - <property name="orientation"> |
3103 | - <enum>Qt::Horizontal</enum> |
3104 | - </property> |
3105 | - <property name="standardButtons"> |
3106 | - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> |
3107 | - </property> |
3108 | - </widget> |
3109 | - </item> |
3110 | - </layout> |
3111 | - </widget> |
3112 | - <resources> |
3113 | - <include location="../images/openlp-2.qrc"/> |
3114 | - </resources> |
3115 | - <connections> |
3116 | - <connection> |
3117 | - <sender>buttonBox</sender> |
3118 | - <signal>accepted()</signal> |
3119 | - <receiver>MediaFilesDialog</receiver> |
3120 | - <slot>accept()</slot> |
3121 | - <hints> |
3122 | - <hint type="sourcelabel"> |
3123 | - <x>248</x> |
3124 | - <y>254</y> |
3125 | - </hint> |
3126 | - <hint type="destinationlabel"> |
3127 | - <x>157</x> |
3128 | - <y>274</y> |
3129 | - </hint> |
3130 | - </hints> |
3131 | - </connection> |
3132 | - <connection> |
3133 | - <sender>buttonBox</sender> |
3134 | - <signal>rejected()</signal> |
3135 | - <receiver>MediaFilesDialog</receiver> |
3136 | - <slot>reject()</slot> |
3137 | - <hints> |
3138 | - <hint type="sourcelabel"> |
3139 | - <x>316</x> |
3140 | - <y>260</y> |
3141 | - </hint> |
3142 | - <hint type="destinationlabel"> |
3143 | - <x>286</x> |
3144 | - <y>274</y> |
3145 | - </hint> |
3146 | - </hints> |
3147 | - </connection> |
3148 | - </connections> |
3149 | -</ui> |
3150 | |
3151 | === added file 'resources/pyinstaller/hook-openlp.core.ui.media.py' |
3152 | --- resources/pyinstaller/hook-openlp.core.ui.media.py 1970-01-01 00:00:00 +0000 |
3153 | +++ resources/pyinstaller/hook-openlp.core.ui.media.py 2011-12-01 18:30:31 +0000 |
3154 | @@ -0,0 +1,30 @@ |
3155 | +# -*- coding: utf-8 -*- |
3156 | +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 |
3157 | + |
3158 | +############################################################################### |
3159 | +# OpenLP - Open Source Lyrics Projection # |
3160 | +# --------------------------------------------------------------------------- # |
3161 | +# Copyright (c) 2008-2011 Raoul Snyman # |
3162 | +# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael # |
3163 | +# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, # |
3164 | +# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, # |
3165 | +# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode # |
3166 | +# Woldsund # |
3167 | +# --------------------------------------------------------------------------- # |
3168 | +# This program is free software; you can redistribute it and/or modify it # |
3169 | +# under the terms of the GNU General Public License as published by the Free # |
3170 | +# Software Foundation; version 2 of the License. # |
3171 | +# # |
3172 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
3173 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
3174 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
3175 | +# more details. # |
3176 | +# # |
3177 | +# You should have received a copy of the GNU General Public License along # |
3178 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
3179 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
3180 | +############################################################################### |
3181 | + |
3182 | +hiddenimports = ['openlp.core.ui.media.phononplayer', |
3183 | + 'openlp.core.ui.media.vlcplayer', |
3184 | + 'openlp.core.ui.media.webkitplayer'] |
3185 | |
3186 | === modified file 'scripts/windows-builder.py' |
3187 | --- scripts/windows-builder.py 2011-10-28 13:28:57 +0000 |
3188 | +++ scripts/windows-builder.py 2011-12-01 18:30:31 +0000 |
3189 | @@ -233,6 +233,19 @@ |
3190 | copy(os.path.join(root, filename), |
3191 | os.path.join(dest_path, filename)) |
3192 | |
3193 | +def copy_media_player(): |
3194 | + print u'Copying media player...' |
3195 | + source = os.path.join(source_path, u'core', u'ui', u'media') |
3196 | + dest = os.path.join(dist_path, u'core', u'ui', u'media') |
3197 | + for root, dirs, files in os.walk(source): |
3198 | + for filename in files: |
3199 | + if not filename.endswith(u'.pyc'): |
3200 | + dest_path = os.path.join(dest, root[len(source)+1:]) |
3201 | + if not os.path.exists(dest_path): |
3202 | + os.makedirs(dest_path) |
3203 | + copy(os.path.join(root, filename), |
3204 | + os.path.join(dest_path, filename)) |
3205 | + |
3206 | def copy_windows_files(): |
3207 | print u'Copying extra files for Windows...' |
3208 | copy(os.path.join(winres_path, u'OpenLP.ico'), |
3209 | @@ -355,6 +368,7 @@ |
3210 | run_pyinstaller() |
3211 | write_version_file() |
3212 | copy_plugins() |
3213 | + copy_media_player() |
3214 | if os.path.exists(manual_path): |
3215 | run_sphinx() |
3216 | run_htmlhelp() |
51 + if (navigator. appName. indexOf( "Microsoft Internet")==-1) embeds[ movieName] ) embeds[ movieName] ;
52 + {
53 + if (document.embeds && document.
54 + return document.
55 + }
Please remove all traces of stuff that references Internet Explorer. We are using WebKit, this code doesn't need to be here and only serves to add bloat where it is not needed.
616 + def sendToPlugins(self, v1=None, v2=None, v3=None, v4=None, v5=None):
What is "v1", "v2", etc? They don't make sense. If you need to add multiple arguments and you don't know how many, either use *args or **kwargs.
767 +class MediaAPIs(object):
Enumeration objects should be singular ("MediaAPI"). You can also combine the enumeration and the actual MediaAPI object (Qt does this).
This is all I have to say so far, without further testing. I'll have a look again at home.