Merge lp:~mjthompson/openlp/media_plugin into lp:openlp
- media_plugin
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~mjthompson/openlp/media_plugin |
Merge into: | lp:openlp |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~mjthompson/openlp/media_plugin |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Raoul Snyman | Needs Fixing | ||
Tim Bentley | Needs Fixing | ||
Review via email: mp+8066@code.launchpad.net |
This proposal has been superseded by a proposal from 2009-07-02.
Commit message
Description of the change
Martin Thompson (mjthompson) wrote : | # |
Tim Bentley (trb143) wrote : | # |
Sorry but this will break the other plugins.
adding self.ServiceIte
Also is incorrect.
- service_
92 + service_
should be
This does not work for bibles as the media_image for bibles is _verses instead of _bibles.
Use of assert causes problems and rejections on a number of boxes can we have throws error instead.
Raoul Snyman (raoul-snyman) wrote : | # |
Please don't use this, it throws Eric4 out (and is incorrect Python):
assert 0, 'This fn needs to be defined by the plugin'
Rather use:
raise NotImplementedE
Rather return None, and do a check on the other side... "image is not None":
264 + except:
265 + log.info("Can't generate video preview for some reason");
266 + import sys
267 + print sys.exc_info()
268 + return QtGui.QImage()
This should be in a docstring:
43 # self.ListViewWi
44 # each plugin needs to inherit a class from this and pass that *class* (not an instance) to here
45 # via the ListViewWithDnD
46 + # self.ServiceIte
47 + # self.PreviewFun
48 + # If this fn is not defined, a default will be used (treat the filename as an image)
49 +
50 # The assumption is that given that at least two plugins are of the form
51 # "text with an icon" then all this will help
52 # even for plugins of another sort, the setup of the right-click menu, common toolbar
Martin Thompson (mjthompson) wrote : | # |
On Wed, Jul 01, 2009 at 05:14:09AM -0000 or thereabouts, Tim Bentley wrote:
> Review: Needs Fixing
> Sorry but this will break the other plugins.
>
> adding self.ServiceIte
>
Should I add it to the other plugins then...?
> Also is incorrect.
> - service_
> 92 + service_
>
> should be
> service_
> This does not work for bibles as the media_image for bibles is _verses instead of _bibles.
which would also fix this problem.
>
> Use of assert causes problems and rejections on a number of boxes can we have throws error instead.
OK.
Cheers,
Martin
- 480. By Martin Thompson
-
Changes from review
Unmerged revisions
Preview Diff
1 | === modified file 'openlp/core/lib/listwithpreviews.py' |
2 | --- openlp/core/lib/listwithpreviews.py 2009-06-23 20:59:38 +0000 |
3 | +++ openlp/core/lib/listwithpreviews.py 2009-06-30 20:35:53 +0000 |
4 | @@ -35,14 +35,18 @@ |
5 | self.items = [] |
6 | self.rowheight = 50 |
7 | self.maximagewidth = self.rowheight * 16 / 9.0; |
8 | - if new_preview_function is not None: |
9 | - self.make_preview=new_preview_function |
10 | - else: |
11 | - self.make_preview=self.preview_function |
12 | + self.preview_function = new_preview_function |
13 | |
14 | - def preview_function(self, filename): |
15 | + def make_preview(self, filename): |
16 | if os.path.exists(filename): |
17 | - preview = QtGui.QImage(filename) |
18 | + if self.preview_function is not None: |
19 | + preview=self.preview_function(filename) |
20 | + else: |
21 | + preview = QtGui.QImage(filename) |
22 | + else: |
23 | + preview = None |
24 | + |
25 | + if preview is not None: |
26 | w = self.maximagewidth; |
27 | h = self.rowheight |
28 | preview = preview.scaled(w, h, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) |
29 | |
30 | === modified file 'openlp/core/lib/mediamanageritem.py' |
31 | --- openlp/core/lib/mediamanageritem.py 2009-06-25 20:03:41 +0000 |
32 | +++ openlp/core/lib/mediamanageritem.py 2009-06-30 20:35:53 +0000 |
33 | @@ -18,7 +18,7 @@ |
34 | Place, Suite 330, Boston, MA 02111-1307 USA |
35 | """ |
36 | import types |
37 | - |
38 | +import os |
39 | from PyQt4 import QtCore, QtGui |
40 | from openlp.core.lib.toolbar import * |
41 | from openlp.core.lib import translate |
42 | @@ -122,6 +122,10 @@ |
43 | # self.ListViewWithDnD_class - there is a base list class with DnD assigned to it (openlp.core.lib.BaseListWithDnD()) |
44 | # each plugin needs to inherit a class from this and pass that *class* (not an instance) to here |
45 | # via the ListViewWithDnD_class member |
46 | + # self.ServiceItemIconName - string referring to an icon file or a resource icon |
47 | + # self.PreviewFunction - a function which returns a QImage to represent the item (a preview usually) - no scaling required - that's done later |
48 | + # If this fn is not defined, a default will be used (treat the filename as an image) |
49 | + |
50 | # The assumption is that given that at least two plugins are of the form |
51 | # "text with an icon" then all this will help |
52 | # even for plugins of another sort, the setup of the right-click menu, common toolbar |
53 | @@ -166,7 +170,10 @@ |
54 | #Add the List widget |
55 | self.ListView = self.ListViewWithDnD_class() |
56 | self.ListView.uniformItemSizes = True |
57 | - self.ListData = ListWithPreviews() |
58 | + try: |
59 | + self.ListData = ListWithPreviews(self.PreviewFunction) |
60 | + except AttributeError: |
61 | + self.ListData = ListWithPreviews(None) |
62 | self.ListView.setModel(self.ListData) |
63 | self.ListView.setGeometry(QtCore.QRect(10, 100, 256, 591)) |
64 | self.ListView.setSpacing(1) |
65 | @@ -219,25 +226,25 @@ |
66 | self.parent.config.set_list(self.ConfigSection, self.ListData.getFileList()) |
67 | |
68 | def generateSlideData(self): |
69 | - assert (0, 'This fn needs to be defined by the plugin'); |
70 | + assert 0, 'This fn needs to be defined by the plugin' |
71 | |
72 | def onPreviewClick(self): |
73 | log.debug(self.PluginTextShort+u'Preview Requested') |
74 | service_item = ServiceItem(self.parent) |
75 | - service_item.addIcon(u':/media/media_image.png') |
76 | + service_item.addIcon(self.ServiceItemIconName) |
77 | self.generateSlideData(service_item) |
78 | self.parent.preview_controller.addServiceItem(service_item) |
79 | |
80 | def onLiveClick(self): |
81 | log.debug(self.PluginTextShort+u' Live Requested') |
82 | service_item = ServiceItem(self.parent) |
83 | - service_item.addIcon(u':/media/media_image.png') |
84 | + service_item.addIcon(self.ServiceItemIconName) |
85 | self.generateSlideData(service_item) |
86 | self.parent.live_controller.addServiceItem(service_item) |
87 | |
88 | def onAddClick(self): |
89 | log.debug(self.PluginTextShort+u' Add Requested') |
90 | service_item = ServiceItem(self.parent) |
91 | - service_item.addIcon(u':/media/media_image.png') |
92 | + service_item.addIcon(self.ServiceItemIconName) |
93 | self.generateSlideData(service_item) |
94 | self.parent.service_manager.addServiceItem(service_item) |
95 | |
96 | === modified file 'openlp/plugins/images/lib/mediaitem.py' |
97 | --- openlp/plugins/images/lib/mediaitem.py 2009-06-25 20:01:02 +0000 |
98 | +++ openlp/plugins/images/lib/mediaitem.py 2009-06-25 21:09:52 +0000 |
99 | @@ -49,7 +49,8 @@ |
100 | self.OnNewFileMasks = u'Images (*.jpg *jpeg *.gif *.png *.bmp)' |
101 | # this next is a class, not an instance of a class - it will |
102 | # be instanced by the base MediaManagerItem |
103 | - self.ListViewWithDnD_class = ImageListView |
104 | + self.ListViewWithDnD_class = ImageListView |
105 | + self.ServiceItemIconName = u':/media/media_image.png' |
106 | MediaManagerItem.__init__(self, parent, icon, title) |
107 | |
108 | |
109 | |
110 | === modified file 'openlp/plugins/media/lib/mediaitem.py' |
111 | --- openlp/plugins/media/lib/mediaitem.py 2009-06-16 18:21:24 +0000 |
112 | +++ openlp/plugins/media/lib/mediaitem.py 2009-06-30 20:35:53 +0000 |
113 | @@ -19,13 +19,25 @@ |
114 | """ |
115 | import logging |
116 | import os |
117 | - |
118 | +import tempfile |
119 | +try: |
120 | + import gst |
121 | +except: |
122 | + log = logging.getLogger(u'MediaMediaItemSetup') |
123 | + log.warning(u'Can\'t generate Vidoe previews - import gst failed'); |
124 | + |
125 | from PyQt4 import QtCore, QtGui |
126 | |
127 | from openlp.core.lib import MediaManagerItem, translate |
128 | |
129 | from openlp.plugins.media.lib import MediaTab |
130 | from openlp.plugins.media.lib import FileListData |
131 | +# from listwithpreviews import ListWithPreviews |
132 | +from openlp.core.lib import MediaManagerItem, ServiceItem, translate, BaseListWithDnD |
133 | +class MediaListView(BaseListWithDnD): |
134 | + def __init__(self, parent=None): |
135 | + self.PluginName = u'Media' |
136 | + BaseListWithDnD.__init__(self, parent) |
137 | |
138 | class MediaMediaItem(MediaManagerItem): |
139 | """ |
140 | @@ -36,100 +48,76 @@ |
141 | log.info(u'Media Media Item loaded') |
142 | |
143 | def __init__(self, parent, icon, title): |
144 | + self.TranslationContext = u'MediaPlugin' |
145 | + self.PluginTextShort = u'Media' |
146 | + self.ConfigSection = u'images' |
147 | + self.OnNewPrompt = u'Select Media(s)' |
148 | + self.OnNewFileMasks = u'Videos (*.avi *.mpeg *.mpg *.mp4);;Audio (*.ogg *.mp3 *.wma);;All files (*)' |
149 | + # this next is a class, not an instance of a class - it will |
150 | + # be instanced by the base MediaManagerItem |
151 | + self.ListViewWithDnD_class = MediaListView |
152 | + self.ServiceItemIconName = u':/media/media_image.png' |
153 | + self.PreviewFunction = self.video_get_preview |
154 | MediaManagerItem.__init__(self, parent, icon, title) |
155 | |
156 | - def setupUi(self): |
157 | - # Add a toolbar |
158 | - self.addToolbar() |
159 | - # Create buttons for the toolbar |
160 | - ## New Media Button ## |
161 | - self.addToolbarButton( |
162 | - translate(u'MediaMediaItem',u'New Media'), |
163 | - translate(u'MediaMediaItem',u'Load Media into openlp.org'), |
164 | - ':/videos/video_load.png', self.onMediaNewClick, 'MediaNewItem') |
165 | - ## Delete Media Button ## |
166 | - self.addToolbarButton( |
167 | - translate(u'MediaMediaItem',u'Delete Media'), |
168 | - translate(u'MediaMediaItem',u'Delete the selected Media item'), |
169 | - ':/videos/video_delete.png', self.onMediaDeleteClick, 'MediaDeleteItem') |
170 | - ## Separator Line ## |
171 | - self.addToolbarSeparator() |
172 | - ## Preview Media Button ## |
173 | - self.addToolbarButton( |
174 | - translate(u'MediaMediaItem',u'Preview Media'), |
175 | - translate(u'MediaMediaItem',u'Preview the selected Media item'), |
176 | - ':/system/system_preview.png', self.onMediaPreviewClick, 'MediaPreviewItem') |
177 | - ## Live Media Button ## |
178 | - self.addToolbarButton( |
179 | - translate(u'MediaMediaItem',u'Go Live'), |
180 | - translate(u'MediaMediaItem',u'Send the selected Media item live'), |
181 | - ':/system/system_live.png', self.onMediaLiveClick, 'MediaLiveItem') |
182 | - ## Add Media Button ## |
183 | - self.addToolbarButton( |
184 | - translate(u'MediaMediaItem',u'Add Media To Service'), |
185 | - translate(u'MediaMediaItem',u'Add the selected Media items(s) to the service'), |
186 | - ':/system/system_add.png',self.onMediaAddClick, 'MediaAddItem') |
187 | - ## Add the Medialist widget ## |
188 | - |
189 | - self.MediaListView = QtGui.QListView() |
190 | - self.MediaListView.setAlternatingRowColors(True) |
191 | - self.MediaListData = FileListData() |
192 | - self.MediaListView.setModel(self.MediaListData) |
193 | - |
194 | - self.PageLayout.addWidget(self.MediaListView) |
195 | - |
196 | - #define and add the context menu |
197 | - self.MediaListView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) |
198 | - |
199 | - self.MediaListView.addAction(self.contextMenuAction( |
200 | - self.MediaListView, ':/system/system_preview.png', |
201 | - translate(u'MediaMediaItem',u'&Preview Media'), self.onMediaPreviewClick)) |
202 | - self.MediaListView.addAction(self.contextMenuAction( |
203 | - self.MediaListView, ':/system/system_live.png', |
204 | - translate(u'MediaMediaItem',u'&Show Live'), self.onMediaLiveClick)) |
205 | - self.MediaListView.addAction(self.contextMenuAction( |
206 | - self.MediaListView, ':/system/system_add.png', |
207 | - translate(u'MediaMediaItem',u'&Add to Service'), self.onMediaAddClick)) |
208 | - |
209 | - def initialise(self): |
210 | - list = self.parent.config.load_list(u'Media') |
211 | - self.loadMediaList(list) |
212 | - |
213 | - def onMediaNewClick(self): |
214 | - files = QtGui.QFileDialog.getOpenFileNames(None, |
215 | - translate(u'MediaMediaItem', u'Select Media(s) items'), |
216 | - self.parent.config.get_last_dir(), |
217 | - u'Videos (*.avi *.mpeg);;Audio (*.mp3 *.ogg *.wma);;All files (*)') |
218 | - if len(files) > 0: |
219 | - self.loadMediaList(files) |
220 | - dir, filename = os.path.split(unicode(files[0])) |
221 | - self.parent.config.set_last_dir(dir) |
222 | - self.parent.config.set_list(u'media', self.MediaListData.getFileList()) |
223 | - |
224 | - def getFileList(self): |
225 | - filelist = [item[0] for item in self.MediaListView]; |
226 | - return filelist |
227 | - |
228 | - def loadMediaList(self, list): |
229 | - for files in list: |
230 | - self.MediaListData.addRow(files) |
231 | - |
232 | - def onMediaDeleteClick(self): |
233 | - indexes = self.MediaListView.selectedIndexes() |
234 | + def video_get_preview(self, filename): |
235 | + |
236 | + """Gets a preview of the first frame of a video file using |
237 | + GSTREAMER (non-portable??? - Can't figure out how to do with |
238 | + Phonon - returns a QImage""" |
239 | + |
240 | + try: |
241 | + # Define your pipeline, just as you would at the command prompt. |
242 | + # This is much easier than trying to create and link each gstreamer element in Python. |
243 | + # This is great for pipelines that end with a filesink (i.e. there is no audible or visual output) |
244 | + log.info ("Video preview %s"%( filename)) |
245 | + outfile=tempfile.NamedTemporaryFile(suffix='.png') |
246 | + cmd=u'filesrc location="%s" ! decodebin ! ffmpegcolorspace ! pngenc ! filesink location="%s"'% (filename, outfile.name) |
247 | + pipe = gst.parse_launch(cmd) |
248 | + # Get a reference to the pipeline's bus |
249 | + bus = pipe.get_bus() |
250 | + |
251 | + # Set the pipeline's state to PLAYING |
252 | + pipe.set_state(gst.STATE_PLAYING) |
253 | + |
254 | + # Listen to the pipeline's bus indefinitely until we receive a EOS (end of stream) message. |
255 | + # This is a super important step, or the pipeline might not work as expected. For example, |
256 | + # in my example pipeline above, the pngenc will not export an actual image unless you have |
257 | + # this line of code. It just exports a 0 byte png file. So... don't forget this step. |
258 | + bus.poll(gst.MESSAGE_EOS, -1) |
259 | + img=QtGui.QImage(outfile.name) |
260 | + outfile.close() |
261 | +# os.unlink(outfile.name) |
262 | + pipe.set_state(gst.STATE_NULL) |
263 | + return img |
264 | + except: |
265 | + log.info("Can't generate video preview for some reason"); |
266 | + import sys |
267 | + print sys.exc_info() |
268 | + return QtGui.QImage() |
269 | + |
270 | + |
271 | + def generateSlideData(self, service_item): |
272 | + indexes = self.ListView.selectedIndexes() |
273 | + service_item.title = u'Media' |
274 | for index in indexes: |
275 | - current_row = int(index.row()) |
276 | - self.MediaListData.removeRow(current_row) |
277 | - self.parent.config.set_list(u'media', self.MediaListData.getFileList()) |
278 | - |
279 | - def onMediaPreviewClick(self): |
280 | + filename = self.ListData.getFilename(index) |
281 | + frame = QtGui.QImage(unicode(filename)) |
282 | + (path, name) = os.path.split(filename) |
283 | + service_item.add_from_image(path, name, frame) |
284 | + |
285 | + |
286 | + def onPreviewClick(self): |
287 | log.debug(u'Media Preview Button pressed') |
288 | - items = self.MediaListView.selectedIndexes() |
289 | + items = self.ListView.selectedIndexes() |
290 | for item in items: |
291 | - text = self.MediaListData.getValue(item) |
292 | + text = self.ListData.getValue(item) |
293 | print text |
294 | |
295 | def onMediaLiveClick(self): |
296 | + log.debug(u'Media Live Button pressed') |
297 | pass |
298 | |
299 | - def onMediaAddClick(self): |
300 | - pass |
301 | \ No newline at end of file |
302 | +# def onMediaAddClick(self): |
303 | +# log.debug(u'Media Add Button pressed') |
304 | +# pass |
305 | |
306 | === modified file 'openlp/plugins/media/mediaplugin.py' |
307 | --- openlp/plugins/media/mediaplugin.py 2009-06-16 18:21:24 +0000 |
308 | +++ openlp/plugins/media/mediaplugin.py 2009-06-30 20:35:53 +0000 |
309 | @@ -22,7 +22,7 @@ |
310 | |
311 | from openlp.core.lib import Plugin, MediaManagerItem, SettingsTab |
312 | from openlp.plugins.media.lib import MediaTab,MediaMediaItem |
313 | - |
314 | +from video_preview import video_get_preview |
315 | class MediaPlugin(Plugin): |
316 | |
317 | def __init__(self, plugin_helpers): |
318 | |
319 | === removed file 'openlp/plugins/media/video_render.py' |
320 | --- openlp/plugins/media/video_render.py 2009-06-22 20:44:35 +0000 |
321 | +++ openlp/plugins/media/video_render.py 1970-01-01 00:00:00 +0000 |
322 | @@ -1,70 +0,0 @@ |
323 | -# -*- coding: utf-8 -*- |
324 | -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 |
325 | -""" |
326 | -OpenLP - Open Source Lyrics Projection |
327 | -Copyright (c) 2008 Raoul Snyman |
328 | -Portions copyright (c) 2008 Martin Thompson, Tim Bentley, |
329 | - |
330 | -This program is free software; you can redistribute it and/or modify it under |
331 | -the terms of the GNU General Public License as published by the Free Software |
332 | -Foundation; version 2 of the License. |
333 | - |
334 | -This program is distributed in the hope that it will be useful, but WITHOUT ANY |
335 | -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
336 | -PARTICULAR PURPOSE. See the GNU General Public License for more details. |
337 | - |
338 | -You should have received a copy of the GNU General Public License along with |
339 | -this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
340 | -Place, Suite 330, Boston, MA 02111-1307 USA |
341 | -""" |
342 | -import os |
343 | -from PyQt4 import QtCore, QtGui |
344 | - |
345 | -# xxx this needs a try, except once we've decided what to do if it fails |
346 | -from PyQt4.phonon import Phonon |
347 | - |
348 | -# from openlp.core.lib import Plugin, MediaManagerItem, SettingsTab |
349 | -# from openlp.plugins.media.lib import MediaTab,MediaMediaItem |
350 | - |
351 | -"""Renders a video to some surface or other """ |
352 | - |
353 | -class w(QtGui.QMainWindow): |
354 | - def __init__(self, parent=None): |
355 | - super(QtGui.QMainWindow, self).__init__(parent) |
356 | - self.resize(640,480) |
357 | - self.setWindowTitle(u'simple media player') |
358 | - self.show() |
359 | - |
360 | -if __name__==u'__main__': |
361 | - app = QtGui.QApplication([]) |
362 | -# widget = QtGui.QWidget() |
363 | -# widget.resize(320, 240) |
364 | -# widget.setWindowTitle(u'simple') |
365 | -# widget.show() |
366 | -# QCore.QCoreApplication.setApplicationName(u'OpenLP') |
367 | - mainwindow=w() |
368 | - widget=QtGui.QWidget(mainwindow) |
369 | - mainwindow.setCentralWidget(widget) |
370 | - widget.setLayout(QtGui.QVBoxLayout(widget)) |
371 | -# videofile=u'r-128.rm' |
372 | - videofile=u'/extra_space/Download/coa360download56Kbps240x160.mpg' |
373 | - source=Phonon.MediaSource(videofile) |
374 | - |
375 | - media=Phonon.MediaObject(widget) |
376 | - media.setCurrentSource(source) |
377 | - |
378 | - video=Phonon.VideoWidget(widget) |
379 | - audio=Phonon.AudioOutput(Phonon.MusicCategory) |
380 | -# controller=Phonon.MediaController(media) |
381 | - Phonon.createPath(media, video); |
382 | - Phonon.createPath(media, audio); |
383 | -# player=Phonon.VideoPlayer(Phonon.VideoCategory, widget) |
384 | - slider=Phonon.SeekSlider(media, mainwindow) |
385 | - widget.layout().addWidget(slider) |
386 | - widget.layout().addWidget(video) |
387 | - slider.show() |
388 | - |
389 | - video.show() |
390 | - media.play() |
391 | - app.exec_() |
392 | - |
Previews for video items - uses gstreamer as I can;t find a way to make Phonon do it. This may be unportable? But gets us through until Phonon gets the functionality.
No previews for audio files yet