Merge lp:~mjthompson/openlp/media_plugin into lp:openlp

Proposed by Martin Thompson
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
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.

To post a comment you must log in.
Revision history for this message
Martin Thompson (mjthompson) wrote :

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

Revision history for this message
Tim Bentley (trb143) wrote :

Sorry but this will break the other plugins.

adding self.ServiceItemIconName will be required by all plugins now

Also is incorrect.
        - service_item.addIcon(u':/media/media_image.png')
92 + service_item.addIcon(self.ServiceItemIconName)

should be
          service_item.addIcon(u':/media/media_' + 'self.ShortPluginName + u'image.png')
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.

review: Needs Fixing
Revision history for this message
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 NotImplementedError(u'This function needs to be defined by the plugin')

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

review: Needs Fixing
Revision history for this message
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.ServiceItemIconName will be required by all plugins now
>

Should I add it to the other plugins then...?

> Also is incorrect.
> - service_item.addIcon(u':/media/media_image.png')
> 92 + service_item.addIcon(self.ServiceItemIconName)
>
> should be
> service_item.addIcon(u':/media/media_' + 'self.ShortPluginName + u'image.png')
> 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

lp:~mjthompson/openlp/media_plugin updated
480. By Martin Thompson

Changes from review

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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-