Merge lp:~mvo/software-center/video-support into lp:software-center

Proposed by Michael Vogt
Status: Merged
Merged at revision: 2556
Proposed branch: lp:~mvo/software-center/video-support
Merge into: lp:software-center
Diff against target: 248 lines (+119/-2) (has conflicts)
8 files modified
softwarecenter/db/application.py (+11/-0)
softwarecenter/db/update.py (+5/-0)
softwarecenter/enums.py (+1/-0)
softwarecenter/testutils.py (+20/-0)
softwarecenter/ui/gtk3/views/appdetailsview_gtk.py (+13/-0)
softwarecenter/ui/gtk3/widgets/videoplayer.py (+22/-1)
test/gtk3/test_appdetailsview.py (+46/-0)
test/test_plugin.py (+1/-1)
Text conflict in softwarecenter/testutils.py
To merge this branch: bzr merge lp:~mvo/software-center/video-support
Reviewer Review Type Date Requested Status
Gary Lasker (community) Approve
Michael Vogt Pending
Review via email: mp+81826@code.launchpad.net

Description of the change

This branch adds basic support for displaying application releated videos inside software-center. If there is a X-AppInstall-Video-Url in the desktop file or the software-center-agent the video is displayed in a embedded webkit.

This means that something like:
<iframe width="640" height="390" src="http://www.youtube.com/embed/KBpdLe80gLE" frameborder="0" allowfullscreen></iframe>

on the server is enough to display a video. AFAICT (but please double check) this means we can embed videos from e.g. vimeo as they require only that their embedded player is used. In order to use this in production we will need a video hoster that can host WebM/ogg theora videos though.

To post a comment you must log in.
2553. By Michael Vogt

softwarecenter/ui/gtk3/widgets/videoplayer.py: ensure that clicking on links keeps working in the embedded video player

Revision history for this message
Michael Vogt (mvo) wrote :

I should also add that the current placement probably needs tweaking, here is what it currently looks like:
http://people.canonical.com/~mvo/tmp/Screenshot%20at%202011-11-10%2012:14:25.png

Note that without (much?) server support this is probably the best we can do for now, but simple server side
tweaks should help. The ToS needs some careful review, but I think it should be fine for free apps to embed the videos.

Revision history for this message
Gary Lasker (gary-lasker) wrote :

Michael, this is *seriously* cool!! It works really well and look just great. Awesome work!

I looked over the Terms of Service for YouTube (http://www.youtube.com/static?gl=US&template=terms) and I agree that from all appearances this implementation conforms to all of the terms; that seems to be true certainly for the free apps. The regular YouTube embedded player is used, and you implemented the links back to the YouTube site itself as required. From what I can tell that satisfies all of the conditions on that page.

It's not as clear to me that Vimeo is also an option, based on their Terms of Service at http://vimeo.com/terms. I don't see anything there that clearly describes the use of their embedded player. I guess we would need to explore this further if an app developer wanted consider using Vimeo.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'softwarecenter/db/application.py'
2--- softwarecenter/db/application.py 2011-11-07 08:26:13 +0000
3+++ softwarecenter/db/application.py 2011-11-10 11:11:58 +0000
4@@ -563,6 +563,17 @@
5 'version' : self.version or 0}
6
7 @property
8+ def video_url(self):
9+ # if there is a custom video url provided, use that
10+ if self._doc:
11+ if self._doc.get_value(XapianValues.VIDEO_URL):
12+ return self._doc.get_value(XapianValues.VIDEO_URL)
13+ # else use the video server
14+ #return self._distro.VIDEO_URL % { 'pkgname' : self.pkgname,
15+ # 'version' : self.version or 0}
16+ return None
17+
18+ @property
19 def version(self):
20 if self._pkg:
21 if self._pkg.installed:
22
23=== modified file 'softwarecenter/db/update.py'
24--- softwarecenter/db/update.py 2011-10-26 08:34:48 +0000
25+++ softwarecenter/db/update.py 2011-11-10 11:11:58 +0000
26@@ -151,6 +151,7 @@
27 'Icon' : 'icon',
28 'Screenshot-Url' : 'screenshot_url',
29 'Thumbnail-Url' : 'thumbnail_url',
30+ 'Video-Url' : 'video_url',
31 'Icon-Url' : 'icon_url',
32 }
33
34@@ -693,6 +694,10 @@
35 if parser.has_option_desktop("X-AppInstall-Thumbnail-Url"):
36 url = parser.get_desktop("X-AppInstall-Thumbnail-Url")
37 doc.add_value(XapianValues.THUMBNAIL_URL, url)
38+ # video support (for third party mostly)
39+ if parser.has_option_desktop("X-AppInstall-Video-Url"):
40+ url = parser.get_desktop("X-AppInstall-Video-Url")
41+ doc.add_value(XapianValues.VIDEO_URL, url)
42 # icon (for third party)
43 if parser.has_option_desktop("X-AppInstall-Icon-Url"):
44 url = parser.get_desktop("X-AppInstall-Icon-Url")
45
46=== modified file 'softwarecenter/enums.py'
47--- softwarecenter/enums.py 2011-10-22 04:24:42 +0000
48+++ softwarecenter/enums.py 2011-11-10 11:11:58 +0000
49@@ -128,6 +128,7 @@
50 LICENSE_KEY = 192
51 LICENSE_KEY_PATH = 193 # no longer used
52 LICENSE = 194
53+ VIDEO_URL = 194
54
55 # fake channels
56 PURCHASED_NEEDS_REINSTALL_MAGIC_CHANNEL_NAME = "for-pay-needs-reinstall"
57
58=== modified file 'softwarecenter/testutils.py'
59--- softwarecenter/testutils.py 2011-11-09 10:06:32 +0000
60+++ softwarecenter/testutils.py 2011-11-10 11:11:58 +0000
61@@ -109,9 +109,29 @@
62 limit=limit,
63 nonblocking_load=False)
64 return enquirer.matches
65+<<<<<<< TREE
66
67 def do_events():
68 from gi.repository import GObject
69 main_loop = GObject.main_context_default()
70 while main_loop.pending():
71 main_loop.iteration()
72+=======
73+
74+def get_mock_app_from_real_app(real_app):
75+ """ take a application and return a app where the details are a mock
76+ of the real details so they can easily be modified
77+ """
78+ from mock import Mock
79+ import copy
80+ app = copy.copy(real_app)
81+ db = get_test_db()
82+ details = app.get_details(db)
83+ details_mock = Mock()
84+ for a in dir(details):
85+ if a.startswith("_"): continue
86+ setattr(details_mock, a, getattr(details, a))
87+ app._details = details_mock
88+ app.get_details = lambda db: app._details
89+ return app
90+>>>>>>> MERGE-SOURCE
91
92=== modified file 'softwarecenter/ui/gtk3/views/appdetailsview_gtk.py'
93--- softwarecenter/ui/gtk3/views/appdetailsview_gtk.py 2011-11-09 10:10:52 +0000
94+++ softwarecenter/ui/gtk3/views/appdetailsview_gtk.py 2011-11-10 11:11:58 +0000
95@@ -994,6 +994,11 @@
96 self.homepage_btn.set_name("subtle-label")
97 self.homepage_btn.connect('activate-link', self._on_homepage_clicked)
98
99+ # video
100+ from softwarecenter.ui.gtk3.widgets.videoplayer import VideoPlayer
101+ self.videoplayer = VideoPlayer()
102+ vb.pack_start(self.videoplayer, False, False, 0)
103+
104 # add the links footer to the description widget
105 footer_hb = Gtk.HBox(spacing=6)
106 footer_hb.pack_start(self.homepage_btn, False, False, 0)
107@@ -1145,6 +1150,13 @@
108 self.homepage_btn.hide()
109 return
110
111+ def _update_app_video(self, app_details):
112+ if app_details.video_url:
113+ self.videoplayer.uri = app_details.video_url
114+ self.videoplayer.show()
115+ else:
116+ self.videoplayer.hide()
117+
118 def _update_app_screenshot(self, app_details):
119 # get screenshot urls and configure the ScreenshotView...
120 if app_details.thumbnail and app_details.screenshot:
121@@ -1232,6 +1244,7 @@
122 self._update_app_description(app_details, app_details.pkgname)
123 self._update_description_footer_links(app_details)
124 self._update_app_screenshot(app_details)
125+ self._update_app_video(app_details)
126 self._update_weblive(app_details)
127 self._update_pkg_info_table(app_details)
128 if not skip_update_addons:
129
130=== modified file 'softwarecenter/ui/gtk3/widgets/videoplayer.py'
131--- softwarecenter/ui/gtk3/widgets/videoplayer.py 2011-08-09 08:47:43 +0000
132+++ softwarecenter/ui/gtk3/widgets/videoplayer.py 2011-11-10 11:11:58 +0000
133@@ -31,15 +31,32 @@
134 def __init__(self):
135 super(VideoPlayer, self).__init__()
136 self.webkit = WebKit.WebView()
137+ self.webkit.connect("new-window-policy-decision-requested", self._on_new_window)
138 self.pack_start(self.webkit, True, True, 0)
139 self._uri = ""
140+
141+ # helper required to follow ToS about the "back" link
142+ def _on_new_window(self, view, frame, request, action, policy):
143+ import subprocess
144+ subprocess.Popen(['xdg-open', request.get_uri()])
145+ return True
146+
147+ # uri property
148 def _set_uri(self, v):
149 self._uri = v
150 self.webkit.load_uri(v)
151 def _get_uri(self):
152 return self._uri
153 uri = property(_get_uri, _set_uri, None, "uri property")
154-
155+
156+ def load_html_string(self, html):
157+ """ Instead of a video URI use a html embedded video like e.g.
158+ youtube or vimeo. Note that on a default install not all
159+ video codecs will play (no flash!), so be careful!
160+ """
161+ # FIXME: add something more useful here
162+ base_uri = "http://www.ubuntu.com"
163+ self.webkit.load_html_string(html, base_uri)
164
165 # AKA the-segfault-edition-with-no-documentation
166 class VideoPlayerGtk3(Gtk.VBox):
167@@ -108,6 +125,9 @@
168
169
170 def get_test_videoplayer_window():
171+ html = """
172+ <iframe width="640" height="390" src="http://www.youtube.com/embed/h3oBU0NZJuA" frameborder="0" allowfullscreen></iframe>
173+"""
174 win = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
175 win.set_default_size(500, 400)
176 win.connect("destroy", Gtk.main_quit)
177@@ -115,6 +135,7 @@
178 win.add(player)
179 if len(sys.argv) < 2:
180 player.uri = "http://upload.wikimedia.org/wikipedia/commons/9/9b/Pentagon_News_Sample.ogg"
181+ #player.load_html_string(html)
182 else:
183 player.uri = sys.argv[1]
184 win.show_all()
185
186=== added file 'test/gtk3/test_appdetailsview.py'
187--- test/gtk3/test_appdetailsview.py 1970-01-01 00:00:00 +0000
188+++ test/gtk3/test_appdetailsview.py 2011-11-10 11:11:58 +0000
189@@ -0,0 +1,46 @@
190+#!/usr/bin/python
191+
192+from gi.repository import Gtk, GObject
193+import sys
194+import unittest
195+
196+sys.path.insert(0,"../..")
197+sys.path.insert(0,"..")
198+
199+#from mock import Mock
200+
201+import softwarecenter.paths
202+softwarecenter.paths.datadir = "../data"
203+
204+from softwarecenter.db.application import Application
205+from softwarecenter.testutils import get_test_db, get_mock_app_from_real_app
206+from softwarecenter.ui.gtk3.views.appdetailsview_gtk import get_test_window_appdetails
207+class TestAppdetailsViews(unittest.TestCase):
208+
209+ def test_videoplayer(self):
210+ # get the widget
211+ win = get_test_window_appdetails()
212+ view = win.get_data("view")
213+
214+ # show app with no video
215+ app = Application("", "2vcard")
216+ view.show_app(app)
217+ while Gtk.events_pending():
218+ Gtk.main_iteration()
219+ self.assertFalse(view.videoplayer.get_property("visible"))
220+
221+ # create app with video and ensure its visible
222+ app = Application("", "synaptic")
223+ mock = get_mock_app_from_real_app(app)
224+ details = mock.get_details(None)
225+ # this is a example html - any html5 video will do
226+ details.video_url = "http://people.canonical.com/~mvo/totem.html"
227+ view.show_app(mock)
228+ while Gtk.events_pending():
229+ Gtk.main_iteration()
230+ self.assertTrue(view.videoplayer.get_property("visible"))
231+
232+if __name__ == "__main__":
233+ import logging
234+ logging.basicConfig(level=logging.DEBUG)
235+ unittest.main()
236
237=== modified file 'test/test_plugin.py'
238--- test/test_plugin.py 2011-05-18 08:26:04 +0000
239+++ test/test_plugin.py 2011-11-10 11:11:58 +0000
240@@ -11,7 +11,7 @@
241 class MockApp(object):
242 """ mock app """
243
244-class testPlugin(unittest.TestCase):
245+class TestPlugin(unittest.TestCase):
246
247 def setUp(self):
248 pass

Subscribers

People subscribed via source and target branches