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
=== modified file 'openlp/core/lib/listwithpreviews.py'
--- openlp/core/lib/listwithpreviews.py 2009-06-23 20:59:38 +0000
+++ openlp/core/lib/listwithpreviews.py 2009-06-30 20:35:53 +0000
@@ -35,14 +35,18 @@
35 self.items = []35 self.items = []
36 self.rowheight = 5036 self.rowheight = 50
37 self.maximagewidth = self.rowheight * 16 / 9.0;37 self.maximagewidth = self.rowheight * 16 / 9.0;
38 if new_preview_function is not None:38 self.preview_function = new_preview_function
39 self.make_preview=new_preview_function
40 else:
41 self.make_preview=self.preview_function
42 39
43 def preview_function(self, filename):40 def make_preview(self, filename):
44 if os.path.exists(filename):41 if os.path.exists(filename):
45 preview = QtGui.QImage(filename)42 if self.preview_function is not None:
43 preview=self.preview_function(filename)
44 else:
45 preview = QtGui.QImage(filename)
46 else:
47 preview = None
48
49 if preview is not None:
46 w = self.maximagewidth;50 w = self.maximagewidth;
47 h = self.rowheight51 h = self.rowheight
48 preview = preview.scaled(w, h, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)52 preview = preview.scaled(w, h, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
4953
=== modified file 'openlp/core/lib/mediamanageritem.py'
--- openlp/core/lib/mediamanageritem.py 2009-06-25 20:03:41 +0000
+++ openlp/core/lib/mediamanageritem.py 2009-06-30 20:35:53 +0000
@@ -18,7 +18,7 @@
18Place, Suite 330, Boston, MA 02111-1307 USA18Place, Suite 330, Boston, MA 02111-1307 USA
19"""19"""
20import types20import types
2121import os
22from PyQt4 import QtCore, QtGui22from PyQt4 import QtCore, QtGui
23from openlp.core.lib.toolbar import *23from openlp.core.lib.toolbar import *
24from openlp.core.lib import translate24from openlp.core.lib import translate
@@ -122,6 +122,10 @@
122 # self.ListViewWithDnD_class - there is a base list class with DnD assigned to it (openlp.core.lib.BaseListWithDnD())122 # self.ListViewWithDnD_class - there is a base list class with DnD assigned to it (openlp.core.lib.BaseListWithDnD())
123 # each plugin needs to inherit a class from this and pass that *class* (not an instance) to here123 # each plugin needs to inherit a class from this and pass that *class* (not an instance) to here
124 # via the ListViewWithDnD_class member124 # via the ListViewWithDnD_class member
125 # self.ServiceItemIconName - string referring to an icon file or a resource icon
126 # self.PreviewFunction - a function which returns a QImage to represent the item (a preview usually) - no scaling required - that's done later
127 # If this fn is not defined, a default will be used (treat the filename as an image)
128
125 # The assumption is that given that at least two plugins are of the form129 # The assumption is that given that at least two plugins are of the form
126 # "text with an icon" then all this will help130 # "text with an icon" then all this will help
127 # even for plugins of another sort, the setup of the right-click menu, common toolbar131 # even for plugins of another sort, the setup of the right-click menu, common toolbar
@@ -166,7 +170,10 @@
166 #Add the List widget170 #Add the List widget
167 self.ListView = self.ListViewWithDnD_class()171 self.ListView = self.ListViewWithDnD_class()
168 self.ListView.uniformItemSizes = True172 self.ListView.uniformItemSizes = True
169 self.ListData = ListWithPreviews()173 try:
174 self.ListData = ListWithPreviews(self.PreviewFunction)
175 except AttributeError:
176 self.ListData = ListWithPreviews(None)
170 self.ListView.setModel(self.ListData)177 self.ListView.setModel(self.ListData)
171 self.ListView.setGeometry(QtCore.QRect(10, 100, 256, 591))178 self.ListView.setGeometry(QtCore.QRect(10, 100, 256, 591))
172 self.ListView.setSpacing(1)179 self.ListView.setSpacing(1)
@@ -219,25 +226,25 @@
219 self.parent.config.set_list(self.ConfigSection, self.ListData.getFileList())226 self.parent.config.set_list(self.ConfigSection, self.ListData.getFileList())
220227
221 def generateSlideData(self):228 def generateSlideData(self):
222 assert (0, 'This fn needs to be defined by the plugin');229 assert 0, 'This fn needs to be defined by the plugin'
223230
224 def onPreviewClick(self):231 def onPreviewClick(self):
225 log.debug(self.PluginTextShort+u'Preview Requested')232 log.debug(self.PluginTextShort+u'Preview Requested')
226 service_item = ServiceItem(self.parent)233 service_item = ServiceItem(self.parent)
227 service_item.addIcon(u':/media/media_image.png')234 service_item.addIcon(self.ServiceItemIconName)
228 self.generateSlideData(service_item)235 self.generateSlideData(service_item)
229 self.parent.preview_controller.addServiceItem(service_item)236 self.parent.preview_controller.addServiceItem(service_item)
230237
231 def onLiveClick(self):238 def onLiveClick(self):
232 log.debug(self.PluginTextShort+u' Live Requested')239 log.debug(self.PluginTextShort+u' Live Requested')
233 service_item = ServiceItem(self.parent)240 service_item = ServiceItem(self.parent)
234 service_item.addIcon(u':/media/media_image.png')241 service_item.addIcon(self.ServiceItemIconName)
235 self.generateSlideData(service_item)242 self.generateSlideData(service_item)
236 self.parent.live_controller.addServiceItem(service_item)243 self.parent.live_controller.addServiceItem(service_item)
237244
238 def onAddClick(self):245 def onAddClick(self):
239 log.debug(self.PluginTextShort+u' Add Requested')246 log.debug(self.PluginTextShort+u' Add Requested')
240 service_item = ServiceItem(self.parent)247 service_item = ServiceItem(self.parent)
241 service_item.addIcon(u':/media/media_image.png')248 service_item.addIcon(self.ServiceItemIconName)
242 self.generateSlideData(service_item)249 self.generateSlideData(service_item)
243 self.parent.service_manager.addServiceItem(service_item)250 self.parent.service_manager.addServiceItem(service_item)
244251
=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py 2009-06-25 20:01:02 +0000
+++ openlp/plugins/images/lib/mediaitem.py 2009-06-25 21:09:52 +0000
@@ -49,7 +49,8 @@
49 self.OnNewFileMasks = u'Images (*.jpg *jpeg *.gif *.png *.bmp)'49 self.OnNewFileMasks = u'Images (*.jpg *jpeg *.gif *.png *.bmp)'
50 # this next is a class, not an instance of a class - it will50 # this next is a class, not an instance of a class - it will
51 # be instanced by the base MediaManagerItem51 # be instanced by the base MediaManagerItem
52 self.ListViewWithDnD_class = ImageListView 52 self.ListViewWithDnD_class = ImageListView
53 self.ServiceItemIconName = u':/media/media_image.png'
53 MediaManagerItem.__init__(self, parent, icon, title)54 MediaManagerItem.__init__(self, parent, icon, title)
5455
5556
5657
=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py 2009-06-16 18:21:24 +0000
+++ openlp/plugins/media/lib/mediaitem.py 2009-06-30 20:35:53 +0000
@@ -19,13 +19,25 @@
19"""19"""
20import logging20import logging
21import os21import os
2222import tempfile
23try:
24 import gst
25except:
26 log = logging.getLogger(u'MediaMediaItemSetup')
27 log.warning(u'Can\'t generate Vidoe previews - import gst failed');
28
23from PyQt4 import QtCore, QtGui29from PyQt4 import QtCore, QtGui
2430
25from openlp.core.lib import MediaManagerItem, translate31from openlp.core.lib import MediaManagerItem, translate
2632
27from openlp.plugins.media.lib import MediaTab33from openlp.plugins.media.lib import MediaTab
28from openlp.plugins.media.lib import FileListData34from openlp.plugins.media.lib import FileListData
35# from listwithpreviews import ListWithPreviews
36from openlp.core.lib import MediaManagerItem, ServiceItem, translate, BaseListWithDnD
37class MediaListView(BaseListWithDnD):
38 def __init__(self, parent=None):
39 self.PluginName = u'Media'
40 BaseListWithDnD.__init__(self, parent)
2941
30class MediaMediaItem(MediaManagerItem):42class MediaMediaItem(MediaManagerItem):
31 """43 """
@@ -36,100 +48,76 @@
36 log.info(u'Media Media Item loaded')48 log.info(u'Media Media Item loaded')
3749
38 def __init__(self, parent, icon, title):50 def __init__(self, parent, icon, title):
51 self.TranslationContext = u'MediaPlugin'
52 self.PluginTextShort = u'Media'
53 self.ConfigSection = u'images'
54 self.OnNewPrompt = u'Select Media(s)'
55 self.OnNewFileMasks = u'Videos (*.avi *.mpeg *.mpg *.mp4);;Audio (*.ogg *.mp3 *.wma);;All files (*)'
56 # this next is a class, not an instance of a class - it will
57 # be instanced by the base MediaManagerItem
58 self.ListViewWithDnD_class = MediaListView
59 self.ServiceItemIconName = u':/media/media_image.png'
60 self.PreviewFunction = self.video_get_preview
39 MediaManagerItem.__init__(self, parent, icon, title)61 MediaManagerItem.__init__(self, parent, icon, title)
4062
41 def setupUi(self):63 def video_get_preview(self, filename):
42 # Add a toolbar64
43 self.addToolbar()65 """Gets a preview of the first frame of a video file using
44 # Create buttons for the toolbar66 GSTREAMER (non-portable??? - Can't figure out how to do with
45 ## New Media Button ##67 Phonon - returns a QImage"""
46 self.addToolbarButton(68
47 translate(u'MediaMediaItem',u'New Media'),69 try:
48 translate(u'MediaMediaItem',u'Load Media into openlp.org'),70 # Define your pipeline, just as you would at the command prompt.
49 ':/videos/video_load.png', self.onMediaNewClick, 'MediaNewItem')71 # This is much easier than trying to create and link each gstreamer element in Python.
50 ## Delete Media Button ##72 # This is great for pipelines that end with a filesink (i.e. there is no audible or visual output)
51 self.addToolbarButton(73 log.info ("Video preview %s"%( filename))
52 translate(u'MediaMediaItem',u'Delete Media'),74 outfile=tempfile.NamedTemporaryFile(suffix='.png')
53 translate(u'MediaMediaItem',u'Delete the selected Media item'),75 cmd=u'filesrc location="%s" ! decodebin ! ffmpegcolorspace ! pngenc ! filesink location="%s"'% (filename, outfile.name)
54 ':/videos/video_delete.png', self.onMediaDeleteClick, 'MediaDeleteItem')76 pipe = gst.parse_launch(cmd)
55 ## Separator Line ##77 # Get a reference to the pipeline's bus
56 self.addToolbarSeparator()78 bus = pipe.get_bus()
57 ## Preview Media Button ##79
58 self.addToolbarButton(80 # Set the pipeline's state to PLAYING
59 translate(u'MediaMediaItem',u'Preview Media'),81 pipe.set_state(gst.STATE_PLAYING)
60 translate(u'MediaMediaItem',u'Preview the selected Media item'),82
61 ':/system/system_preview.png', self.onMediaPreviewClick, 'MediaPreviewItem')83 # Listen to the pipeline's bus indefinitely until we receive a EOS (end of stream) message.
62 ## Live Media Button ##84 # This is a super important step, or the pipeline might not work as expected. For example,
63 self.addToolbarButton(85 # in my example pipeline above, the pngenc will not export an actual image unless you have
64 translate(u'MediaMediaItem',u'Go Live'),86 # this line of code. It just exports a 0 byte png file. So... don't forget this step.
65 translate(u'MediaMediaItem',u'Send the selected Media item live'),87 bus.poll(gst.MESSAGE_EOS, -1)
66 ':/system/system_live.png', self.onMediaLiveClick, 'MediaLiveItem')88 img=QtGui.QImage(outfile.name)
67 ## Add Media Button ##89 outfile.close()
68 self.addToolbarButton(90# os.unlink(outfile.name)
69 translate(u'MediaMediaItem',u'Add Media To Service'),91 pipe.set_state(gst.STATE_NULL)
70 translate(u'MediaMediaItem',u'Add the selected Media items(s) to the service'),92 return img
71 ':/system/system_add.png',self.onMediaAddClick, 'MediaAddItem')93 except:
72 ## Add the Medialist widget ##94 log.info("Can't generate video preview for some reason");
7395 import sys
74 self.MediaListView = QtGui.QListView()96 print sys.exc_info()
75 self.MediaListView.setAlternatingRowColors(True)97 return QtGui.QImage()
76 self.MediaListData = FileListData()98
77 self.MediaListView.setModel(self.MediaListData)99
78100 def generateSlideData(self, service_item):
79 self.PageLayout.addWidget(self.MediaListView)101 indexes = self.ListView.selectedIndexes()
80102 service_item.title = u'Media'
81 #define and add the context menu
82 self.MediaListView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
83
84 self.MediaListView.addAction(self.contextMenuAction(
85 self.MediaListView, ':/system/system_preview.png',
86 translate(u'MediaMediaItem',u'&Preview Media'), self.onMediaPreviewClick))
87 self.MediaListView.addAction(self.contextMenuAction(
88 self.MediaListView, ':/system/system_live.png',
89 translate(u'MediaMediaItem',u'&Show Live'), self.onMediaLiveClick))
90 self.MediaListView.addAction(self.contextMenuAction(
91 self.MediaListView, ':/system/system_add.png',
92 translate(u'MediaMediaItem',u'&Add to Service'), self.onMediaAddClick))
93
94 def initialise(self):
95 list = self.parent.config.load_list(u'Media')
96 self.loadMediaList(list)
97
98 def onMediaNewClick(self):
99 files = QtGui.QFileDialog.getOpenFileNames(None,
100 translate(u'MediaMediaItem', u'Select Media(s) items'),
101 self.parent.config.get_last_dir(),
102 u'Videos (*.avi *.mpeg);;Audio (*.mp3 *.ogg *.wma);;All files (*)')
103 if len(files) > 0:
104 self.loadMediaList(files)
105 dir, filename = os.path.split(unicode(files[0]))
106 self.parent.config.set_last_dir(dir)
107 self.parent.config.set_list(u'media', self.MediaListData.getFileList())
108
109 def getFileList(self):
110 filelist = [item[0] for item in self.MediaListView];
111 return filelist
112
113 def loadMediaList(self, list):
114 for files in list:
115 self.MediaListData.addRow(files)
116
117 def onMediaDeleteClick(self):
118 indexes = self.MediaListView.selectedIndexes()
119 for index in indexes:103 for index in indexes:
120 current_row = int(index.row())104 filename = self.ListData.getFilename(index)
121 self.MediaListData.removeRow(current_row)105 frame = QtGui.QImage(unicode(filename))
122 self.parent.config.set_list(u'media', self.MediaListData.getFileList())106 (path, name) = os.path.split(filename)
123107 service_item.add_from_image(path, name, frame)
124 def onMediaPreviewClick(self):108
109
110 def onPreviewClick(self):
125 log.debug(u'Media Preview Button pressed')111 log.debug(u'Media Preview Button pressed')
126 items = self.MediaListView.selectedIndexes()112 items = self.ListView.selectedIndexes()
127 for item in items:113 for item in items:
128 text = self.MediaListData.getValue(item)114 text = self.ListData.getValue(item)
129 print text115 print text
130116
131 def onMediaLiveClick(self):117 def onMediaLiveClick(self):
118 log.debug(u'Media Live Button pressed')
132 pass119 pass
133120
134 def onMediaAddClick(self):
135 pass
136\ No newline at end of file121\ No newline at end of file
122# def onMediaAddClick(self):
123# log.debug(u'Media Add Button pressed')
124# pass
137125
=== modified file 'openlp/plugins/media/mediaplugin.py'
--- openlp/plugins/media/mediaplugin.py 2009-06-16 18:21:24 +0000
+++ openlp/plugins/media/mediaplugin.py 2009-06-30 20:35:53 +0000
@@ -22,7 +22,7 @@
2222
23from openlp.core.lib import Plugin, MediaManagerItem, SettingsTab23from openlp.core.lib import Plugin, MediaManagerItem, SettingsTab
24from openlp.plugins.media.lib import MediaTab,MediaMediaItem24from openlp.plugins.media.lib import MediaTab,MediaMediaItem
2525from video_preview import video_get_preview
26class MediaPlugin(Plugin):26class MediaPlugin(Plugin):
2727
28 def __init__(self, plugin_helpers):28 def __init__(self, plugin_helpers):
2929
=== removed file 'openlp/plugins/media/video_render.py'
--- openlp/plugins/media/video_render.py 2009-06-22 20:44:35 +0000
+++ openlp/plugins/media/video_render.py 1970-01-01 00:00:00 +0000
@@ -1,70 +0,0 @@
1# -*- coding: utf-8 -*-
2# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
3"""
4OpenLP - Open Source Lyrics Projection
5Copyright (c) 2008 Raoul Snyman
6Portions copyright (c) 2008 Martin Thompson, Tim Bentley,
7
8This program is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free Software
10Foundation; version 2 of the License.
11
12This program is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
14PARTICULAR PURPOSE. See the GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License along with
17this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18Place, Suite 330, Boston, MA 02111-1307 USA
19"""
20import os
21from PyQt4 import QtCore, QtGui
22
23# xxx this needs a try, except once we've decided what to do if it fails
24from PyQt4.phonon import Phonon
25
26# from openlp.core.lib import Plugin, MediaManagerItem, SettingsTab
27# from openlp.plugins.media.lib import MediaTab,MediaMediaItem
28
29"""Renders a video to some surface or other """
30
31class w(QtGui.QMainWindow):
32 def __init__(self, parent=None):
33 super(QtGui.QMainWindow, self).__init__(parent)
34 self.resize(640,480)
35 self.setWindowTitle(u'simple media player')
36 self.show()
37
38if __name__==u'__main__':
39 app = QtGui.QApplication([])
40# widget = QtGui.QWidget()
41# widget.resize(320, 240)
42# widget.setWindowTitle(u'simple')
43# widget.show()
44# QCore.QCoreApplication.setApplicationName(u'OpenLP')
45 mainwindow=w()
46 widget=QtGui.QWidget(mainwindow)
47 mainwindow.setCentralWidget(widget)
48 widget.setLayout(QtGui.QVBoxLayout(widget))
49# videofile=u'r-128.rm'
50 videofile=u'/extra_space/Download/coa360download56Kbps240x160.mpg'
51 source=Phonon.MediaSource(videofile)
52
53 media=Phonon.MediaObject(widget)
54 media.setCurrentSource(source)
55
56 video=Phonon.VideoWidget(widget)
57 audio=Phonon.AudioOutput(Phonon.MusicCategory)
58# controller=Phonon.MediaController(media)
59 Phonon.createPath(media, video);
60 Phonon.createPath(media, audio);
61# player=Phonon.VideoPlayer(Phonon.VideoCategory, widget)
62 slider=Phonon.SeekSlider(media, mainwindow)
63 widget.layout().addWidget(slider)
64 widget.layout().addWidget(video)
65 slider.show()
66
67 video.show()
68 media.play()
69 app.exec_()
70