Merge lp:~dobey/ubuntu/natty/rhythmbox-ubuntuone-music-store/use-defer into lp:ubuntu/natty/rhythmbox-ubuntuone-music-store
- Natty (11.04)
- use-defer
- Merge into natty
Proposed by
dobey
on 2010-12-21
| Status: | Merged |
|---|---|
| Merged at revision: | 56 |
| Proposed branch: | lp:~dobey/ubuntu/natty/rhythmbox-ubuntuone-music-store/use-defer |
| Merge into: | lp:ubuntu/natty/rhythmbox-ubuntuone-music-store |
| Diff against target: |
1180 lines (+377/-305) 15 files modified
MANIFEST (+0/-36) MANIFEST.in (+5/-10) PKG-INFO (+6/-4) debian/changelog (+7/-0) gst/__init__.py (+14/-0) gst/pbutils.py (+6/-0) po/rhythmbox-ubuntuone-music-store.pot (+0/-60) rb.py (+36/-0) rhythmdb.py (+12/-0) run-tests (+24/-0) setup.py (+12/-8) umusicstore/MusicStoreWidget.py (+137/-100) umusicstore/U1MSLinks.py (+107/-68) umusicstore/__init__.py (+11/-9) umusicstore/umusicstore.rb-plugin (+0/-10) |
| To merge this branch: | bzr merge lp:~dobey/ubuntu/natty/rhythmbox-ubuntuone-music-store/use-defer |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Ubuntu branches | 2010-12-21 | Pending | |
|
Review via email:
|
|||
Commit Message
Description of the Change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | === removed file 'MANIFEST' |
| 2 | --- MANIFEST 2010-08-26 01:47:45 +0000 |
| 3 | +++ MANIFEST 1970-01-01 00:00:00 +0000 |
| 4 | @@ -1,36 +0,0 @@ |
| 5 | -COPYING |
| 6 | -MANIFEST |
| 7 | -MANIFEST.in |
| 8 | -README |
| 9 | -setup.cfg |
| 10 | -setup.py |
| 11 | -po/POTFILES.in |
| 12 | -po/ca.po |
| 13 | -po/cs.po |
| 14 | -po/da.po |
| 15 | -po/de.po |
| 16 | -po/el.po |
| 17 | -po/en_GB.po |
| 18 | -po/es.po |
| 19 | -po/eu.po |
| 20 | -po/fi.po |
| 21 | -po/fr.po |
| 22 | -po/fy.po |
| 23 | -po/gl.po |
| 24 | -po/he.po |
| 25 | -po/hr.po |
| 26 | -po/ka.po |
| 27 | -po/nl.po |
| 28 | -po/pl.po |
| 29 | -po/rhythmbox-ubuntuone-music-store.pot |
| 30 | -po/ro.po |
| 31 | -po/ru.po |
| 32 | -po/sv.po |
| 33 | -po/uk.po |
| 34 | -umusicstore/MusicStoreWidget.py |
| 35 | -umusicstore/U1MSLinks.py |
| 36 | -umusicstore/__init__.py |
| 37 | -umusicstore/empty.mp3 |
| 38 | -umusicstore/u1msurl.glade |
| 39 | -umusicstore/umusicstore.rb-plugin |
| 40 | -umusicstore/umusicstore.rb-plugin.in |
| 41 | |
| 42 | === modified file 'MANIFEST.in' |
| 43 | --- MANIFEST.in 2010-07-21 16:03:11 +0000 |
| 44 | +++ MANIFEST.in 2010-12-21 15:44:57 +0000 |
| 45 | @@ -1,10 +1,5 @@ |
| 46 | -include COPYING README |
| 47 | -include MANIFEST.in MANIFEST |
| 48 | -include po/* |
| 49 | -include umusicstore/umusicstore.rb-plugin* |
| 50 | -include umusicstore/empty.mp3 |
| 51 | -include umusicstore/musicstore_icon.png |
| 52 | -include umusicstore/__init__.py |
| 53 | -include umusicstore/MusicStoreWidget.py |
| 54 | -include umusicstore/U1MSLinks.py |
| 55 | -include umusicstore/u1msurl.glade |
| 56 | +include COPYING MANIFEST.in README |
| 57 | +include run-tests rb.py rhythmdb.py |
| 58 | +recursive-include gst *.py |
| 59 | +recursive-include po *.po POTFILES.in |
| 60 | +recursive-include umusicstore *.py *.glade *.mp3 *.rb-plugin.in |
| 61 | |
| 62 | === modified file 'PKG-INFO' |
| 63 | --- PKG-INFO 2010-10-07 13:09:46 +0000 |
| 64 | +++ PKG-INFO 2010-12-21 15:44:57 +0000 |
| 65 | @@ -1,11 +1,13 @@ |
| 66 | Metadata-Version: 1.1 |
| 67 | Name: rhythmbox-ubuntuone-music-store |
| 68 | -Version: 0.1.9 |
| 69 | -Summary: Ubuntu One Music Store Rhythmbox plugin |
| 70 | +Version: 0.1.10 |
| 71 | +Summary: Ubuntu One Music Store Rhythmbox plug-in |
| 72 | Home-page: https://launchpad.net/rhythmbox-ubuntuone-music-store |
| 73 | Author: Stuart Langridge |
| 74 | Author-email: stuart.langridge@canonical.com |
| 75 | License: LGPL v3 |
| 76 | -Description: A Rhythmbox plugin that makes the Ubuntu Music Store available in Rhythmbox. |
| 77 | +Description: A plug-in to access the Ubuntu One Music Store in Rhythmbox. |
| 78 | Platform: UNKNOWN |
| 79 | -Requires: DistUtilsExtra.auto |
| 80 | +Provides: gst |
| 81 | +Provides: rb |
| 82 | +Provides: rhythmdb |
| 83 | |
| 84 | === modified file 'debian/changelog' |
| 85 | --- debian/changelog 2010-12-21 10:32:15 +0000 |
| 86 | +++ debian/changelog 2010-12-21 15:44:57 +0000 |
| 87 | @@ -1,3 +1,10 @@ |
| 88 | +rhythmbox-ubuntuone-music-store (0.1.10-0ubuntu1) natty; urgency=low |
| 89 | + |
| 90 | + * New upstream release. |
| 91 | + - Handle the defer API being split out from aptdaemon. (LP: #691647) |
| 92 | + |
| 93 | + -- Rodney Dawes <rodney.dawes@ubuntu.com> Tue, 21 Dec 2010 10:37:55 -0500 |
| 94 | + |
| 95 | rhythmbox-ubuntuone-music-store (0.1.9-0ubuntu2) natty; urgency=low |
| 96 | |
| 97 | * debian/control: add Depends on python-defer (LP: #691647) |
| 98 | |
| 99 | === added directory 'gst' |
| 100 | === added file 'gst/__init__.py' |
| 101 | --- gst/__init__.py 1970-01-01 00:00:00 +0000 |
| 102 | +++ gst/__init__.py 2010-12-21 15:44:57 +0000 |
| 103 | @@ -0,0 +1,14 @@ |
| 104 | +"""Fake gst.py module for testing.""" |
| 105 | + |
| 106 | +STATE_NULL = 0 |
| 107 | +STATE_PLAYING = 1 |
| 108 | + |
| 109 | + |
| 110 | +def parse_launch(*args, **kwargs): |
| 111 | + """Fake parse_launch.""" |
| 112 | + return |
| 113 | + |
| 114 | + |
| 115 | +def update_registry(*args, **kwargs): |
| 116 | + """Fake update_registry.""" |
| 117 | + return |
| 118 | |
| 119 | === added file 'gst/pbutils.py' |
| 120 | --- gst/pbutils.py 1970-01-01 00:00:00 +0000 |
| 121 | +++ gst/pbutils.py 2010-12-21 15:44:57 +0000 |
| 122 | @@ -0,0 +1,6 @@ |
| 123 | +"""Fake pbutils module for testing.""" |
| 124 | + |
| 125 | + |
| 126 | +def is_missing_plugin_message(*args, **kwargs): |
| 127 | + """Fake is_missing_plugin_message.""" |
| 128 | + return |
| 129 | |
| 130 | === removed file 'po/rhythmbox-ubuntuone-music-store.pot' |
| 131 | --- po/rhythmbox-ubuntuone-music-store.pot 2010-04-01 17:30:54 +0000 |
| 132 | +++ po/rhythmbox-ubuntuone-music-store.pot 1970-01-01 00:00:00 +0000 |
| 133 | @@ -1,60 +0,0 @@ |
| 134 | -# SOME DESCRIPTIVE TITLE. |
| 135 | -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER |
| 136 | -# This file is distributed under the same license as the PACKAGE package. |
| 137 | -# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. |
| 138 | -# |
| 139 | -#, fuzzy |
| 140 | -msgid "" |
| 141 | -msgstr "" |
| 142 | -"Project-Id-Version: PACKAGE VERSION\n" |
| 143 | -"Report-Msgid-Bugs-To: \n" |
| 144 | -"POT-Creation-Date: 2010-03-25 10:30+0100\n" |
| 145 | -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
| 146 | -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
| 147 | -"Language-Team: LANGUAGE <LL@li.org>\n" |
| 148 | -"MIME-Version: 1.0\n" |
| 149 | -"Content-Type: text/plain; charset=CHARSET\n" |
| 150 | -"Content-Transfer-Encoding: 8bit\n" |
| 151 | - |
| 152 | -#. Translators: this is the name under Music for U1 music in Rhythmbox |
| 153 | -#: ../umusicstore/__init__.py:172 |
| 154 | -msgid "Purchased from Ubuntu One" |
| 155 | -msgstr "" |
| 156 | - |
| 157 | -#: ../umusicstore/__init__.py:243 |
| 158 | -msgid "Ubuntu One" |
| 159 | -msgstr "" |
| 160 | - |
| 161 | -#: ../umusicstore/__init__.py:288 |
| 162 | -msgid "MP3 plugins are not installed" |
| 163 | -msgstr "" |
| 164 | - |
| 165 | -#: ../umusicstore/__init__.py:293 |
| 166 | -msgid "" |
| 167 | -"To listen to your purchased songs, you need to install MP3 plugins. Click " |
| 168 | -"below to install them." |
| 169 | -msgstr "" |
| 170 | - |
| 171 | -#: ../umusicstore/__init__.py:298 |
| 172 | -msgid "Install MP3 plugins" |
| 173 | -msgstr "" |
| 174 | - |
| 175 | -#: ../umusicstore/__init__.py:320 |
| 176 | -msgid "Installing MP3 plugins" |
| 177 | -msgstr "" |
| 178 | - |
| 179 | -#: ../umusicstore/__init__.py:340 |
| 180 | -msgid "There was a problem installing, sorry" |
| 181 | -msgstr "" |
| 182 | - |
| 183 | -#: ../umusicstore/__init__.py:343 |
| 184 | -msgid "Check your internet connection and try again." |
| 185 | -msgstr "" |
| 186 | - |
| 187 | -#: ../umusicstore/umusicstore.rb-plugin.in.h:1 |
| 188 | -msgid "The Ubuntu One Music Store" |
| 189 | -msgstr "" |
| 190 | - |
| 191 | -#: ../umusicstore/umusicstore.rb-plugin.in.h:2 |
| 192 | -msgid "Ubuntu One Music Store" |
| 193 | -msgstr "" |
| 194 | |
| 195 | === added file 'rb.py' |
| 196 | --- rb.py 1970-01-01 00:00:00 +0000 |
| 197 | +++ rb.py 2010-12-21 15:44:57 +0000 |
| 198 | @@ -0,0 +1,36 @@ |
| 199 | +"""Fake rb.py for use in testing.""" |
| 200 | + |
| 201 | +SOURCE_GROUP_CATEGORY_FIXED = 0 |
| 202 | + |
| 203 | + |
| 204 | +class Plugin(object): |
| 205 | + """Fake Plugin class for testing.""" |
| 206 | + |
| 207 | + def __init__(self, *args, **kwargs): |
| 208 | + super(Plugin, self).__init__() |
| 209 | + |
| 210 | + def find_file(self, *args, **kwargs): |
| 211 | + """Fake find_file method.""" |
| 212 | + pass |
| 213 | + |
| 214 | + |
| 215 | +class Source(object): |
| 216 | + """Fake Source class for testing.""" |
| 217 | + |
| 218 | + add = __fake_it |
| 219 | + remove = __fake_it |
| 220 | + show_all = __fake_it |
| 221 | + do_impl_activate = __fake_it |
| 222 | + |
| 223 | + def __fake_it(self, *args, **kwargs): |
| 224 | + """Fake method.""" |
| 225 | + |
| 226 | + |
| 227 | +def rb_source_group_get_by_name(*args, **kwargs): |
| 228 | + """Fake method.""" |
| 229 | + return |
| 230 | + |
| 231 | + |
| 232 | +def rb_source_group_register(*args, **kwargs): |
| 233 | + """Fake method.""" |
| 234 | + return |
| 235 | |
| 236 | === added file 'rhythmdb.py' |
| 237 | --- rhythmdb.py 1970-01-01 00:00:00 +0000 |
| 238 | +++ rhythmdb.py 2010-12-21 15:44:57 +0000 |
| 239 | @@ -0,0 +1,12 @@ |
| 240 | +"""Fake rhythmdb.py for use in testing.""" |
| 241 | + |
| 242 | +PROP_ARTIST = 0 |
| 243 | +PROP_TITLE = 1 |
| 244 | +PROP_ALBUM = 2 |
| 245 | + |
| 246 | + |
| 247 | +class EntryType(object): |
| 248 | + """Fake EntryType object.""" |
| 249 | + |
| 250 | + def __init__(self, *args, **kwargs): |
| 251 | + super(EntryType, self).__init__() |
| 252 | |
| 253 | === added file 'run-tests' |
| 254 | --- run-tests 1970-01-01 00:00:00 +0000 |
| 255 | +++ run-tests 2010-12-21 15:44:57 +0000 |
| 256 | @@ -0,0 +1,24 @@ |
| 257 | +#!/bin/bash |
| 258 | +# Author: Natalia Bidart <natalia.bidart@canonical.com> |
| 259 | +# |
| 260 | +# Copyright 2010 Canonical Ltd. |
| 261 | +# |
| 262 | +# This program is free software: you can redistribute it and/or modify it |
| 263 | +# under the terms of the GNU General Public License version 3, as published |
| 264 | +# by the Free Software Foundation. |
| 265 | +# |
| 266 | +# This program is distributed in the hope that it will be useful, but |
| 267 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
| 268 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 269 | +# PURPOSE. See the GNU General Public License for more details. |
| 270 | +# |
| 271 | +# You should have received a copy of the GNU General Public License along |
| 272 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
| 273 | +export PYTHONPATH=umusicstore |
| 274 | +u1lint . |
| 275 | +if [ -x `which pep8` ]; then |
| 276 | + pep8 --repeat . |
| 277 | +else |
| 278 | + echo "Please install the 'pep8' package." |
| 279 | +fi |
| 280 | +rm -rf _trial_temp |
| 281 | |
| 282 | === modified file 'setup.py' (properties changed: -x to +x) |
| 283 | --- setup.py 2010-10-07 13:09:46 +0000 |
| 284 | +++ setup.py 2010-12-21 15:44:57 +0000 |
| 285 | @@ -1,18 +1,22 @@ |
| 286 | +#!/usr/bin/python |
| 287 | +"""Setup script for Ubuntu One Music Store plug-in for Rhythmbox.""" |
| 288 | |
| 289 | import DistUtilsExtra.auto |
| 290 | |
| 291 | DistUtilsExtra.auto.setup( |
| 292 | name='rhythmbox-ubuntuone-music-store', |
| 293 | - version='0.1.9', |
| 294 | + version='0.1.10', |
| 295 | license='LGPL v3', |
| 296 | author='Stuart Langridge', |
| 297 | author_email='stuart.langridge@canonical.com', |
| 298 | - description='Ubuntu One Music Store Rhythmbox plugin', |
| 299 | - long_description='A Rhythmbox plugin that makes the Ubuntu Music Store available in Rhythmbox.', |
| 300 | + description='Ubuntu One Music Store Rhythmbox plug-in', |
| 301 | + long_description=('A plug-in to access the Ubuntu One Music Store ' |
| 302 | + 'in Rhythmbox.'), |
| 303 | url='https://launchpad.net/rhythmbox-ubuntuone-music-store', |
| 304 | - data_files = [('/usr/lib/rhythmbox/plugins/umusicstore', ['umusicstore/__init__.py', |
| 305 | - 'umusicstore/empty.mp3', |
| 306 | - 'umusicstore/MusicStoreWidget.py', |
| 307 | - 'umusicstore/U1MSLinks.py', |
| 308 | - 'umusicstore/u1msurl.glade'])], |
| 309 | + data_files=[('/usr/lib/rhythmbox/plugins/umusicstore', |
| 310 | + ['umusicstore/__init__.py', |
| 311 | + 'umusicstore/empty.mp3', |
| 312 | + 'umusicstore/MusicStoreWidget.py', |
| 313 | + 'umusicstore/U1MSLinks.py', |
| 314 | + 'umusicstore/u1msurl.glade'])], |
| 315 | ) |
| 316 | |
| 317 | === modified file 'umusicstore/MusicStoreWidget.py' |
| 318 | --- umusicstore/MusicStoreWidget.py 2010-10-07 13:09:46 +0000 |
| 319 | +++ umusicstore/MusicStoreWidget.py 2010-12-21 15:44:57 +0000 |
| 320 | @@ -16,39 +16,59 @@ |
| 321 | # |
| 322 | # Authored by Stuart Langridge <stuart.langridge@canonical.com> |
| 323 | |
| 324 | - |
| 325 | -import gtk, gobject, os, urllib, gconf, stat, urlparse, gio |
| 326 | -import gst, gst.pbutils |
| 327 | +# pylint: disable=W0201 |
| 328 | + |
| 329 | +# Import these here as gtk/gio import ordering seems to matter |
| 330 | +import gobject |
| 331 | +import gtk |
| 332 | +import gio |
| 333 | + |
| 334 | import aptdaemon.client |
| 335 | -from aptdaemon import policykit1 |
| 336 | -from aptdaemon.defer import inline_callbacks |
| 337 | -from aptdaemon.enums import * |
| 338 | -from aptdaemon.gtkwidgets import AptErrorDialog, \ |
| 339 | - AptProgressBar |
| 340 | -from commands import getstatusoutput |
| 341 | -import rb, rhythmdb |
| 342 | -from ubuntuone.gtkwidgets import MusicStore as U1MusicStore |
| 343 | -import xdg.BaseDirectory |
| 344 | import dbus |
| 345 | import dbus.exceptions |
| 346 | - |
| 347 | +import gconf |
| 348 | import gettext |
| 349 | +import gst |
| 350 | +import gst.pbutils |
| 351 | +import os |
| 352 | +# pylint: disable=F0401 |
| 353 | +import rb |
| 354 | +import rhythmdb |
| 355 | +# pylint: enable=F0401 |
| 356 | +import stat |
| 357 | +import urllib |
| 358 | +import urlparse |
| 359 | +import xdg.BaseDirectory |
| 360 | + |
| 361 | +from aptdaemon import policykit1 |
| 362 | +try: |
| 363 | + from aptdaemon.defer import inline_callbacks |
| 364 | +except ImportError: |
| 365 | + from defer import inline_callbacks |
| 366 | +from aptdaemon.enums import EXIT_SUCCESS |
| 367 | +from aptdaemon.gtkwidgets import AptProgressBar |
| 368 | from gettext import lgettext as _ |
| 369 | +from ubuntuone.gtkwidgets import MusicStore as U1MusicStore |
| 370 | + |
| 371 | gettext.bindtextdomain("rhythmbox-ubuntuone-music-store", "/usr/share/locale") |
| 372 | gettext.textdomain("rhythmbox-ubuntuone-music-store") |
| 373 | |
| 374 | -MUSIC_STORE_WIDGET = U1MusicStore() # keep this around for later |
| 375 | +MUSIC_STORE_WIDGET = U1MusicStore() # keep this around for later |
| 376 | U1LIBRARYPATH = MUSIC_STORE_WIDGET.get_library_location() |
| 377 | RB_LIBRARY_LOCATIONS = "/apps/rhythmbox/library_locations" |
| 378 | PARTNER_LIST = "canonical-partner-maverick.list" |
| 379 | SOURCES_DIR = "/etc/apt/sources.list.d/" |
| 380 | PLUGIN_PACKAGENAME = "gstreamer0.10-fluendo-plugins-mp3-partner" |
| 381 | |
| 382 | + |
| 383 | class U1EntryType(rhythmdb.EntryType): |
| 384 | + """Entry type for the Ubuntu One Music Store source.""" |
| 385 | + |
| 386 | def __init__(self): |
| 387 | rhythmdb.EntryType.__init__(self, name='ubuntuone') |
| 388 | |
| 389 | def can_sync_metadata(self, entry): |
| 390 | + """Not a real source, so we can't sync metadata.""" |
| 391 | return False |
| 392 | |
| 393 | |
| 394 | @@ -57,37 +77,37 @@ |
| 395 | def __init__(self, plugin, find_file): |
| 396 | self.plugin = plugin |
| 397 | self.find_file = find_file |
| 398 | - |
| 399 | + |
| 400 | def activate(self, shell): |
| 401 | """Plugin startup.""" |
| 402 | self.db = shell.get_property("db") |
| 403 | - group = rb.rb_source_group_get_by_name ("stores") |
| 404 | + group = rb.rb_source_group_get_by_name("stores") |
| 405 | if not group: |
| 406 | - group = rb.rb_source_group_register ("stores", |
| 407 | - "Stores", |
| 408 | - rb.SOURCE_GROUP_CATEGORY_FIXED) |
| 409 | - |
| 410 | + group = rb.rb_source_group_register("stores", |
| 411 | + "Stores", |
| 412 | + rb.SOURCE_GROUP_CATEGORY_FIXED) |
| 413 | + |
| 414 | icon = gtk.IconTheme().load_icon( |
| 415 | "ubuntuone", gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)[0], 0) |
| 416 | |
| 417 | self.entry_type = U1EntryType() |
| 418 | self.db.register_entry_type(self.entry_type) |
| 419 | |
| 420 | - self.source = gobject.new (U1Source, |
| 421 | - shell=shell, |
| 422 | - entry_type=self.entry_type, |
| 423 | - source_group=group, |
| 424 | - icon=icon, |
| 425 | - plugin=self.plugin) |
| 426 | + self.source = gobject.new(U1Source, |
| 427 | + shell=shell, |
| 428 | + entry_type=self.entry_type, |
| 429 | + source_group=group, |
| 430 | + icon=icon, |
| 431 | + plugin=self.plugin) |
| 432 | shell.register_entry_type_for_source(self.source, self.entry_type) |
| 433 | self.shell = shell |
| 434 | self.source.connect("preview-mp3", self.play_preview_mp3) |
| 435 | self.source.connect("play-library", self.play_library) |
| 436 | self.source.connect("download-finished", self.download_finished) |
| 437 | self.source.connect("url-loaded", self.url_loaded) |
| 438 | - |
| 439 | + |
| 440 | # Do these every time |
| 441 | - shell.append_source(self.source, None) # Add the source to the list |
| 442 | + shell.append_source(self.source, None) # Add the source to the list |
| 443 | self.add_u1_library() |
| 444 | |
| 445 | def deactivate(self, shell): |
| 446 | @@ -96,18 +116,20 @@ |
| 447 | self.source.delete_thyself() |
| 448 | # remove the library, if it's empty |
| 449 | try: |
| 450 | - filecount = len(os.listdir(self.U1_LIBRARY_SYMLINK)) |
| 451 | + filecount = len(os.listdir(self.u1_library_symlink)) |
| 452 | except OSError: |
| 453 | # symlink is dangling |
| 454 | # so they never downloaded anything |
| 455 | filecount = 0 |
| 456 | if filecount == 0: |
| 457 | client = gconf.client_get_default() |
| 458 | - libraries = client.get_list(RB_LIBRARY_LOCATIONS, gconf.VALUE_STRING) |
| 459 | + libraries = client.get_list(RB_LIBRARY_LOCATIONS, |
| 460 | + gconf.VALUE_STRING) |
| 461 | if self.u1_library_path_url in libraries: |
| 462 | client.notify_remove(self.library_adder) |
| 463 | libraries.remove(self.u1_library_path_url) |
| 464 | - client.set_list(RB_LIBRARY_LOCATIONS, gconf.VALUE_STRING, libraries) |
| 465 | + client.set_list(RB_LIBRARY_LOCATIONS, |
| 466 | + gconf.VALUE_STRING, libraries) |
| 467 | # delete held references |
| 468 | del self.db |
| 469 | del self.source |
| 470 | @@ -116,24 +138,24 @@ |
| 471 | def url_loaded(self, source, url): |
| 472 | """A URL is loaded in the plugin""" |
| 473 | print "URL loaded:", url |
| 474 | - if urlparse.urlparse(url).scheme == "https": |
| 475 | + if urlparse.urlparse(url)[2] == "https": |
| 476 | pass |
| 477 | else: |
| 478 | pass |
| 479 | - |
| 480 | + |
| 481 | def _udf_path_to_library_uri(self, path): |
| 482 | - """Calculate the path in the library. |
| 483 | - Since the library is accessed via the created symlink, but the path |
| 484 | - passed to us is to a file in the actual music store UDF, we need to |
| 485 | + """Calculate the path in the library. |
| 486 | + Since the library is accessed via the created symlink, but the path |
| 487 | + passed to us is to a file in the actual music store UDF, we need to |
| 488 | work out the path inside the library, i.e., what the path to that file |
| 489 | is via the symlink.""" |
| 490 | - |
| 491 | if path.startswith(U1LIBRARYPATH): |
| 492 | subpath = path[len(U1LIBRARYPATH):] |
| 493 | else: |
| 494 | subpath = path |
| 495 | - if subpath.startswith("/"): subpath = subpath[1:] |
| 496 | - library_path = os.path.join(self.U1_LIBRARY_SYMLINK, subpath) |
| 497 | + if subpath.startswith("/"): |
| 498 | + subpath = subpath[1:] |
| 499 | + library_path = os.path.join(self.u1_library_symlink, subpath) |
| 500 | # convert path to URI. Don't use urllib for this; Python and |
| 501 | # glib escape URLs differently. gio does it the glib way. |
| 502 | library_uri = gio.File(library_path).get_uri() |
| 503 | @@ -150,11 +172,11 @@ |
| 504 | """Switch to and start playing a song from the library""" |
| 505 | uri = self._udf_path_to_library_uri(path) |
| 506 | entry = self.shell.props.db.entry_lookup_by_location(uri) |
| 507 | - if not entry: |
| 508 | + if not entry: |
| 509 | print "couldn't find entry", uri |
| 510 | return |
| 511 | libsrc = self.shell.props.library_source |
| 512 | - genre_view, artist_view, album_view = libsrc.get_property_views() |
| 513 | + artist_view, album_view = libsrc.get_property_views()[0:] |
| 514 | song_view = libsrc.get_entry_view() |
| 515 | artist = self.shell.props.db.entry_get(entry, rhythmdb.PROP_ARTIST) |
| 516 | album = self.shell.props.db.entry_get(entry, rhythmdb.PROP_ALBUM) |
| 517 | @@ -177,7 +199,7 @@ |
| 518 | player = self.shell.get_player() |
| 519 | player.stop() |
| 520 | player.play_entry(entry, self.source) |
| 521 | - # FIXME delete this entry when it finishes playing. Don't know how yet. |
| 522 | + # delete this entry when it finishes playing. Don't know how yet. |
| 523 | |
| 524 | def add_u1_library(self): |
| 525 | """Add the U1 library if not listed in RB and re-add if changed.""" |
| 526 | @@ -187,11 +209,12 @@ |
| 527 | os.chmod(u1_library_path_folder, |
| 528 | os.stat(u1_library_path_folder)[stat.ST_MODE] | stat.S_IWUSR) |
| 529 | # Translators: this is the name under Music for U1 music in Rhythmbox |
| 530 | - u1_library_path = os.path.join(u1_library_path_folder, _("Purchased from Ubuntu One")) |
| 531 | + u1_library_path = os.path.join(u1_library_path_folder, |
| 532 | + _("Purchased from Ubuntu One")) |
| 533 | if not os.path.islink(u1_library_path): |
| 534 | if not os.path.exists(u1_library_path): |
| 535 | - print "Attempting to symlink %s to %s" % (U1LIBRARYPATH, |
| 536 | - u1_library_path) |
| 537 | + print "Attempting to symlink %s to %s" % (U1LIBRARYPATH, |
| 538 | + u1_library_path) |
| 539 | os.symlink(U1LIBRARYPATH, u1_library_path) |
| 540 | else: |
| 541 | # something that isn't a symlink already exists. That's not |
| 542 | @@ -204,12 +227,13 @@ |
| 543 | "delete or rename %s.") % (u1_library_path, U1LIBRARYPATH, |
| 544 | u1_library_path) |
| 545 | self.u1_library_path_url = "file://%s" % urllib.quote(u1_library_path) |
| 546 | - self.U1_LIBRARY_SYMLINK = u1_library_path |
| 547 | + self.u1_library_symlink = u1_library_path |
| 548 | client = gconf.client_get_default() |
| 549 | self._add_u1_library_if_not_present(client) |
| 550 | self._remove_old_u1_library_if_present(client) |
| 551 | # Watch for changes to the gconf key and re-add the library |
| 552 | - self.library_adder = client.notify_add(RB_LIBRARY_LOCATIONS, |
| 553 | + self.library_adder = client.notify_add( |
| 554 | + RB_LIBRARY_LOCATIONS, |
| 555 | self._add_u1_library_if_not_present) |
| 556 | |
| 557 | def _add_u1_library_if_not_present(self, client, *args, **kwargs): |
| 558 | @@ -217,7 +241,8 @@ |
| 559 | libraries = client.get_list(RB_LIBRARY_LOCATIONS, gconf.VALUE_STRING) |
| 560 | if self.u1_library_path_url not in libraries: |
| 561 | libraries.append(self.u1_library_path_url) |
| 562 | - client.set_list(RB_LIBRARY_LOCATIONS, gconf.VALUE_STRING, libraries) |
| 563 | + client.set_list(RB_LIBRARY_LOCATIONS, |
| 564 | + gconf.VALUE_STRING, libraries) |
| 565 | |
| 566 | def _remove_old_u1_library_if_present(self, client, *args, **kwargs): |
| 567 | """Check for the old U1 library and remove it from libraries list.""" |
| 568 | @@ -243,46 +268,57 @@ |
| 569 | # created under one name (say, English) and then had it created |
| 570 | # under another (say, after a translation to Dutch) |
| 571 | u1_library_path_folder = xdg.BaseDirectory.save_data_path("ubuntuone") |
| 572 | - u1_library_path_folder_url = "file://%s" % urllib.quote(u1_library_path_folder) |
| 573 | - symlink_url = "file://" + urllib.quote(self.U1_LIBRARY_SYMLINK) |
| 574 | - for l in libraries: |
| 575 | - if l.startswith(u1_library_path_folder_url) and l != symlink_url: |
| 576 | - libraries.remove(l) |
| 577 | + u1_library_path_folder_url = "file://%s" % urllib.quote( |
| 578 | + u1_library_path_folder) |
| 579 | + symlink_url = "file://" + urllib.quote(self.u1_library_symlink) |
| 580 | + for lib in libraries: |
| 581 | + if lib.startswith(u1_library_path_folder_url) and \ |
| 582 | + lib != symlink_url: |
| 583 | + libraries.remove(lib) |
| 584 | # and delete the symlink itself |
| 585 | - symlink_to_remove = urllib.unquote(l[7:]) |
| 586 | + symlink_to_remove = urllib.unquote(lib[7:]) |
| 587 | os.unlink(symlink_to_remove) |
| 588 | removed = True |
| 589 | if removed: |
| 590 | - client.set_list(RB_LIBRARY_LOCATIONS, gconf.VALUE_STRING, libraries) |
| 591 | - |
| 592 | -class U1Source(rb.Source): |
| 593 | + client.set_list(RB_LIBRARY_LOCATIONS, |
| 594 | + gconf.VALUE_STRING, libraries) |
| 595 | + |
| 596 | + |
| 597 | +class U1Source(rb.Source, gobject.GObject): |
| 598 | """A Rhythmbox source widget for the U1 Music Store.""" |
| 599 | # gproperties required so that rb.Source is instantiable |
| 600 | __gproperties__ = { |
| 601 | - 'plugin': (rb.Plugin, 'plugin', 'plugin', |
| 602 | - gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY), |
| 603 | - } |
| 604 | - # we have the preview-mp3 signal; we receive it from the widget, and re-emit |
| 605 | - # it so that the plugin gets it, because the plugin actually plays the mp3 |
| 606 | + 'plugin': (rb.Plugin, 'plugin', 'plugin', |
| 607 | + gobject.PARAM_WRITABLE | gobject.PARAM_CONSTRUCT_ONLY), |
| 608 | + } |
| 609 | + # we have the preview-mp3 signal; we receive it from the widget, and |
| 610 | + # re-emit it so that the plugin gets it, because the plugin actually |
| 611 | + # plays the mp3 |
| 612 | __gsignals__ = { |
| 613 | - "preview-mp3": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (str, str)), |
| 614 | - "play-library": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (str,)), |
| 615 | - "download-finished": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (str,)), |
| 616 | - "url-loaded": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (str,)), |
| 617 | + "preview-mp3": (gobject.SIGNAL_RUN_FIRST, |
| 618 | + gobject.TYPE_NONE, (str, str)), |
| 619 | + "play-library": (gobject.SIGNAL_RUN_FIRST, |
| 620 | + gobject.TYPE_NONE, (str,)), |
| 621 | + "download-finished": (gobject.SIGNAL_RUN_FIRST, |
| 622 | + gobject.TYPE_NONE, (str,)), |
| 623 | + "url-loaded": (gobject.SIGNAL_RUN_FIRST, |
| 624 | + gobject.TYPE_NONE, (str,)), |
| 625 | } |
| 626 | - |
| 627 | + |
| 628 | def __init__(self): |
| 629 | rb.Source.__init__(self, name=_("Ubuntu One")) |
| 630 | + gobject.GObject.__init__(self) |
| 631 | self.browser = MUSIC_STORE_WIDGET |
| 632 | self.__activated = False |
| 633 | |
| 634 | def do_impl_activate(self): |
| 635 | """Source startup.""" |
| 636 | - if self.__activated: return |
| 637 | + if self.__activated: |
| 638 | + return |
| 639 | self.__activated = True |
| 640 | self.test_can_play_mp3() |
| 641 | - rb.Source.do_impl_activate (self) |
| 642 | - |
| 643 | + rb.Source.do_impl_activate(self) |
| 644 | + |
| 645 | def do_impl_want_uri(self, uri): |
| 646 | """I want to handle u1ms URLs""" |
| 647 | if uri.startswith("u1ms://"): |
| 648 | @@ -326,7 +362,7 @@ |
| 649 | """Handler for end of stream from the check-mp3 pipeline. |
| 650 | If we reach the end of the stream, mp3 playback is enabled.""" |
| 651 | self.install_pipeline.set_state(gst.STATE_NULL) |
| 652 | - if os.environ.has_key("U1INSTALLMP3ANYWAY"): |
| 653 | + if os.environ.get("U1INSTALLMP3ANYWAY", None) is not None: |
| 654 | # override the decision not to install the package |
| 655 | self.install_mp3_playback() |
| 656 | else: |
| 657 | @@ -349,7 +385,7 @@ |
| 658 | self.install_label_body.set_alignment(0.0, 0.5) |
| 659 | self.install_label_eula = gtk.Label() |
| 660 | # EULA text copied from |
| 661 | - # /var/lib/dpkg/info/gstreamer0.10-fluendo-plugins-mp3-partner.templates |
| 662 | + # gstreamer0.10-fluendo-plugins-mp3-partner.templates |
| 663 | # The partner package shows the EULA itself on installations, but |
| 664 | # aptdaemon installations work like DEBIAN_FRONTEND=noninteractive |
| 665 | # so we show the EULA here. (This also avoids a popup window.) |
| 666 | @@ -365,7 +401,7 @@ |
| 667 | "unless the seller has received a license by Fraunhofer IIS" |
| 668 | "and Thomson and pay the relevant royalties to them.</small>") |
| 669 | self.install_label_eula.set_alignment(0.0, 0.5) |
| 670 | - self.install_label_eula.set_size_request(400,200) |
| 671 | + self.install_label_eula.set_size_request(400, 200) |
| 672 | self.install_label_eula.set_line_wrap(True) |
| 673 | self.install_hbtn = gtk.HButtonBox() |
| 674 | self.install_hbtn.set_layout(gtk.BUTTONBOX_END) |
| 675 | @@ -403,11 +439,10 @@ |
| 676 | dist="maverick", |
| 677 | comps=["partner"], |
| 678 | comment="added by U1MusicStoreWidget", |
| 679 | - sourcesfile=PARTNER_LIST, defer=True) |
| 680 | + sourcesfile=PARTNER_LIST) |
| 681 | yield trans.run(defer=True) |
| 682 | trans = yield self.ac.update_cache(sources_list=SOURCES_DIR |
| 683 | - + PARTNER_LIST, |
| 684 | - defer=True) |
| 685 | + + PARTNER_LIST) |
| 686 | self.update_progress = AptProgressBar(trans) |
| 687 | self.update_progress.show() |
| 688 | self.install_label_head.set_text("") |
| 689 | @@ -417,7 +452,7 @@ |
| 690 | self.install_vbox.pack_start(self.update_progress, expand=False) |
| 691 | trans.connect("finished", self._package_update_finished) |
| 692 | yield trans.run(defer=True) |
| 693 | - except Exception, e: |
| 694 | + except Exception, e: # pylint: disable=W0703 |
| 695 | self._on_error(e) |
| 696 | |
| 697 | @inline_callbacks |
| 698 | @@ -428,8 +463,7 @@ |
| 699 | self._on_error(transaction) |
| 700 | return |
| 701 | try: |
| 702 | - trans = yield self.ac.install_packages([PLUGIN_PACKAGENAME], |
| 703 | - defer=True) |
| 704 | + trans = yield self.ac.install_packages([PLUGIN_PACKAGENAME]) |
| 705 | self.install_progress = AptProgressBar(transaction) |
| 706 | self.install_progress.show() |
| 707 | self.install_label_head.set_text("") |
| 708 | @@ -439,7 +473,7 @@ |
| 709 | self.install_vbox.pack_start(self.install_progress, expand=False) |
| 710 | trans.connect("finished", self._package_install_finished) |
| 711 | yield trans.run(defer=True) |
| 712 | - except Exception, e: |
| 713 | + except Exception, e: # pylint: disable=W0703 |
| 714 | self._on_error(e) |
| 715 | |
| 716 | def _package_install_finished(self, trans, exit_code): |
| 717 | @@ -449,7 +483,8 @@ |
| 718 | gst.update_registry() |
| 719 | self.add_music_store_widget() |
| 720 | else: |
| 721 | - self._on_error("Could not find the %r package" % PLUGIN_PACKAGENAME) |
| 722 | + self._on_error( |
| 723 | + "Could not find the %r package" % PLUGIN_PACKAGENAME) |
| 724 | |
| 725 | def _on_error(self, error): |
| 726 | """Error handler for aptdaemon.""" |
| 727 | @@ -457,10 +492,10 @@ |
| 728 | problem_installing = _("There was a problem installing, sorry") |
| 729 | self.install_label_head.set_markup('<span weight="bold" size="larger">' |
| 730 | '%s</span>' % problem_installing) |
| 731 | - self.install_label_body.set_text(_('Check your internet connection and ' |
| 732 | - 'try again.')) |
| 733 | - for widget in ("install_progress", "update_progress"): |
| 734 | - widget = getattr(self, widget, None) |
| 735 | + self.install_label_body.set_text(_('Check your internet connection ' |
| 736 | + 'and try again.')) |
| 737 | + for widget_name in ("install_progress", "update_progress"): |
| 738 | + widget = getattr(self, widget_name, None) |
| 739 | if widget is not None: |
| 740 | widget.hide() |
| 741 | self.install_hbtn.show() |
| 742 | @@ -471,37 +506,39 @@ |
| 743 | self.show_all() |
| 744 | self.browser.set_no_show_all(True) |
| 745 | self.browser.set_property("visible", True) |
| 746 | - self.browser.connect("preview-mp3", self.re_emit_preview) |
| 747 | - self.browser.connect("play-library", self.re_emit_playlibrary) |
| 748 | - self.browser.connect("download-finished", self.re_emit_downloadfinished) |
| 749 | - self.browser.connect("url-loaded", self.re_emit_urlloaded) |
| 750 | + self.browser.connect("preview-mp3", |
| 751 | + self.re_emit_preview) |
| 752 | + self.browser.connect("play-library", |
| 753 | + self.re_emit_playlibrary) |
| 754 | + self.browser.connect("download-finished", |
| 755 | + self.re_emit_downloadfinished) |
| 756 | + self.browser.connect("url-loaded", |
| 757 | + self.re_emit_urlloaded) |
| 758 | |
| 759 | - def do_impl_can_pause(self): |
| 760 | + def do_impl_can_pause(self): |
| 761 | """Implementation can pause. |
| 762 | If we don't handle this, Rhythmbox segfaults.""" |
| 763 | - return True # so we can pause, else we segfault |
| 764 | + return True # so we can pause, else we segfault |
| 765 | |
| 766 | def re_emit_preview(self, widget, url, title): |
| 767 | - """Handle the preview-mp3 signal and re-emit it to the rb.Plugin.""" |
| 768 | + """Handle the preview-mp3 signal and re-emit it to rb.Plugin.""" |
| 769 | self.emit("preview-mp3", url, title) |
| 770 | |
| 771 | def re_emit_playlibrary(self, widget, path): |
| 772 | - """Handle the play-library signal and re-emit it to the rb.Plugin.""" |
| 773 | + """Handle the play-library signal and re-emit it to rb.Plugin.""" |
| 774 | self.emit("play-library", path) |
| 775 | |
| 776 | def re_emit_downloadfinished(self, widget, path): |
| 777 | - """Handle the download-finished signal and re-emit it to the rb.Plugin.""" |
| 778 | + """Handle the download-finished signal and re-emit it to rb.Plugin.""" |
| 779 | self.emit("download-finished", path) |
| 780 | |
| 781 | def re_emit_urlloaded(self, widget, url): |
| 782 | - """Handle the url-loaded signal and re-emit it to the rb.Plugin.""" |
| 783 | + """Handle the url-loaded signal and re-emit it to rb.Plugin.""" |
| 784 | self.emit("url-loaded", url) |
| 785 | |
| 786 | - def do_set_property(self, property, value): |
| 787 | + def do_set_property(self, prop, value): |
| 788 | """Allow property settings to handle the plugin call.""" |
| 789 | - if property.name == 'plugin': |
| 790 | + if prop.name == 'plugin': |
| 791 | self.__plugin = value |
| 792 | else: |
| 793 | - raise AttributeError, 'unknown property %s' % property.name |
| 794 | - |
| 795 | - |
| 796 | + raise AttributeError('unknown property %s' % prop.name) |
| 797 | |
| 798 | === modified file 'umusicstore/U1MSLinks.py' |
| 799 | --- umusicstore/U1MSLinks.py 2010-09-20 18:02:02 +0000 |
| 800 | +++ umusicstore/U1MSLinks.py 2010-12-21 15:44:57 +0000 |
| 801 | @@ -16,22 +16,35 @@ |
| 802 | # |
| 803 | # Authored by Stuart Langridge <stuart.langridge@canonical.com> |
| 804 | |
| 805 | -import urllib, xml.parsers, cgi, os, locale, operator, re, unicodedata |
| 806 | -import gtk, gio, glib |
| 807 | -import rb, rhythmdb |
| 808 | +# Import gtk+/gio bits here because order matters for some unknown reason |
| 809 | +import glib |
| 810 | +import gtk |
| 811 | +import gio |
| 812 | + |
| 813 | +import cgi |
| 814 | +import gettext |
| 815 | +import locale |
| 816 | +import operator |
| 817 | +import os |
| 818 | +import re |
| 819 | +import rhythmdb |
| 820 | +import unicodedata |
| 821 | +import urllib |
| 822 | +import xml.parsers |
| 823 | + |
| 824 | +from gettext import lgettext as _ |
| 825 | from xml.dom import minidom |
| 826 | + |
| 827 | try: |
| 828 | import gwibber.lib.gtk.widgets |
| 829 | - gwibber_available = True |
| 830 | + GWIBBER_AVAILABLE = True |
| 831 | except ImportError: |
| 832 | - gwibber_available = False |
| 833 | + GWIBBER_AVAILABLE = False |
| 834 | |
| 835 | -import gettext |
| 836 | -from gettext import lgettext as _ |
| 837 | gettext.bindtextdomain("U1MSURL", "/usr/share/locale") |
| 838 | gettext.textdomain("U1MSURL") |
| 839 | |
| 840 | -ui_str = \ |
| 841 | +UI_STR = \ |
| 842 | """<ui> |
| 843 | <menubar name="MenuBar"> |
| 844 | <menu name="ViewMenu" action="View"> |
| 845 | @@ -53,12 +66,10 @@ |
| 846 | "the song in the Ubuntu One Music Store."), |
| 847 | "could-not-find": _("Unable to automatically find the song that is " |
| 848 | "currently playing in the Ubuntu One Music Store. " |
| 849 | - "Please try searching manually." |
| 850 | - ), |
| 851 | + "Please try searching manually."), |
| 852 | "could-not-connect": _("Unable to look up the song (couldn't connect " |
| 853 | "to Ubuntu One). Please check your internet " |
| 854 | - "connection, or try again shortly.") |
| 855 | -} |
| 856 | + "connection, or try again shortly.")} |
| 857 | |
| 858 | BASE_URL = os.environ.get("U1MSLINK_BASE_URL", "https://one.ubuntu.com") |
| 859 | # getpreferredencoding isn't threadsafe, but we should be OK |
| 860 | @@ -66,10 +77,11 @@ |
| 861 | SCRUBBING_RE = re.compile(r"[\W\s]+", re.UNICODE) |
| 862 | CLEANING_RE = re.compile(r"\s+", re.UNICODE) |
| 863 | |
| 864 | -def normalize(a): |
| 865 | + |
| 866 | +def normalize(string): |
| 867 | """Normalize the given string, to increase the chances of a match""" |
| 868 | # rule #0 of unicode mangling: first, normalize |
| 869 | - na = unicodedata.normalize('NFKD', a) |
| 870 | + na = unicodedata.normalize('NFKD', string) |
| 871 | # then, strip everything that's not alphanumeric |
| 872 | na = re.sub(SCRUBBING_RE, "", na) |
| 873 | # \w leaves _s |
| 874 | @@ -79,39 +91,52 @@ |
| 875 | # finally, lowercase |
| 876 | na = na.lower() |
| 877 | # if there's anything left, go with that, otherwise rollback |
| 878 | - return na if na else a |
| 879 | - |
| 880 | -def modified_levenshtein(a,b): |
| 881 | - """Calculates the Levenshtein distance between a and b, but counts added |
| 882 | - characters as worth 0 (thus meaning that 'Foo' will match |
| 883 | - 'Foo (Radio Edit)' with a high degree of accuracy.)""" |
| 884 | - a = normalize(a) |
| 885 | - b = normalize(b) |
| 886 | - n, m = len(a), len(b) |
| 887 | - if n > m: |
| 888 | - # Make sure n <= m, to use O(min(n,m)) space |
| 889 | - a,b = b,a |
| 890 | - n,m = m,n |
| 891 | - |
| 892 | - current = range(n+1) |
| 893 | - for i in range(1,m+1): |
| 894 | - previous, current = current, [i]+[0]*n |
| 895 | - for j in range(1,n+1): |
| 896 | - add, delete = previous[j]+1, current[j-1]+1 |
| 897 | - change = previous[j-1] |
| 898 | - if a[j-1] != b[i-1]: |
| 899 | + return na if na else string |
| 900 | + |
| 901 | + |
| 902 | +def modified_levenshtein(start, end): |
| 903 | + """Calculates the Levenshtein distance between start and end, |
| 904 | + but counts added characters as worth 0 (thus meaning that 'Foo' will match |
| 905 | + 'Foo (Radio Edit)' with a high degree of accuracy.)""" |
| 906 | + start = normalize(start) |
| 907 | + end = normalize(end) |
| 908 | + ls, le = len(start), len(end) |
| 909 | + if ls > le: |
| 910 | + # Make sure ls <= le, to use O(min(ls, le)) space |
| 911 | + start, end = end, start |
| 912 | + ls, le = le, ls |
| 913 | + |
| 914 | + current = range(ls + 1) |
| 915 | + for pose in range(1, le + 1): |
| 916 | + previous, current = current, [pose] + [0] * ls |
| 917 | + for poss in range(1, ls + 1): |
| 918 | + delete = current[poss - 1] + 1 |
| 919 | + change = previous[poss - 1] |
| 920 | + if start[poss - 1] != end[pose - 1]: |
| 921 | change = change + 1 |
| 922 | # replace add with 10000 to not count adds |
| 923 | - current[j] = min(10000, delete, change) |
| 924 | - |
| 925 | - return current[n] |
| 926 | + current[poss] = min(10000, delete, change) |
| 927 | + |
| 928 | + return current[ls] |
| 929 | + |
| 930 | |
| 931 | class U1MSLinkProvider(object): |
| 932 | - """Get a public link for any playing song to buy it in |
| 933 | + """Get a public link for any playing song to buy it in |
| 934 | the Ubuntu One Music Store.""" |
| 935 | |
| 936 | def __init__(self, find_file): |
| 937 | self.find_file = find_file |
| 938 | + self.track_fetch = None |
| 939 | + self.artist_fetch = None |
| 940 | + self.win = None |
| 941 | + self.button = None |
| 942 | + self.action_group = None |
| 943 | + self.builder = None |
| 944 | + self.pvb = None |
| 945 | + self.tweet_button = None |
| 946 | + self.message = None |
| 947 | + self.ui_id = None |
| 948 | + self.image = None |
| 949 | |
| 950 | def activate(self, shell): |
| 951 | """Plugin startup.""" |
| 952 | @@ -125,7 +150,7 @@ |
| 953 | self.action_group = gtk.ActionGroup('U1MSLinkPluginActions') |
| 954 | self.action_group.add_action(action) |
| 955 | manager.insert_action_group(self.action_group, 0) |
| 956 | - self.ui_id = manager.add_ui_from_string(ui_str) |
| 957 | + self.ui_id = manager.add_ui_from_string(UI_STR) |
| 958 | manager.ensure_update() |
| 959 | |
| 960 | def deactivate(self, shell): |
| 961 | @@ -136,6 +161,7 @@ |
| 962 | manager.ensure_update() |
| 963 | |
| 964 | def dialog_response_cb(self, dialog, response_id): |
| 965 | + """Destroy the window.""" |
| 966 | self.win.destroy() |
| 967 | self.win = None |
| 968 | |
| 969 | @@ -173,8 +199,9 @@ |
| 970 | _("Looking up <i>%s</i> by <i>%s</i> from <i>%s</i>") % ( |
| 971 | cgi.escape(title), cgi.escape(artist), cgi.escape(album))) |
| 972 | self.lookup_artist(title, artist, album) |
| 973 | - |
| 974 | + |
| 975 | def display_error(self, messagename): |
| 976 | + """Display error text and a warning icon.""" |
| 977 | self.builder.get_object("lblProgress").set_markup(ERRORS[messagename]) |
| 978 | self.image.set_from_icon_name("dialog-warning", |
| 979 | gtk.ICON_SIZE_DIALOG) |
| 980 | @@ -184,12 +211,13 @@ |
| 981 | base_url = "http://api.7digital.com/1.2/artist/search" |
| 982 | params = {"q": artist.encode("utf-8", "replace"), |
| 983 | "oauth_consumer_key": "canonical", |
| 984 | - "country":"GB"} |
| 985 | + "country": "GB"} |
| 986 | url = "%s?%s" % (base_url, urllib.urlencode(params)) |
| 987 | self.artist_fetch = gio.File(url) |
| 988 | - self.artist_fetch.read_async(self.artist_fetched, |
| 989 | + self.artist_fetch.read_async( |
| 990 | + self.artist_fetched, |
| 991 | user_data=(title, artist, album)) |
| 992 | - |
| 993 | + |
| 994 | def artist_fetched(self, gdaemonfile, result, user_data): |
| 995 | """Artist query is complete""" |
| 996 | title, artist, album = user_data |
| 997 | @@ -217,8 +245,7 @@ |
| 998 | possibles.append(( |
| 999 | anode.getAttribute("id"), |
| 1000 | modified_levenshtein(name, artist), |
| 1001 | - name |
| 1002 | - )) |
| 1003 | + name)) |
| 1004 | possibles.sort(key=operator.itemgetter(1)) |
| 1005 | if not possibles: |
| 1006 | self.display_error('could-not-find') |
| 1007 | @@ -227,7 +254,7 @@ |
| 1008 | artist_id = possibles[0][0] |
| 1009 | print "got artist %s" % possibles[0][2] |
| 1010 | self.lookup_track(title, artist, album, artist_id) |
| 1011 | - |
| 1012 | + |
| 1013 | def lookup_track(self, title, artist, album, artist_id): |
| 1014 | """Use the 7digital public API to look up the song.""" |
| 1015 | # There is no way of searching the 7digital API and saying |
| 1016 | @@ -238,7 +265,7 @@ |
| 1017 | params = {"q": "%s %s" % (artist.encode("utf-8", "replace"), |
| 1018 | title.encode("utf-8", "replace")), |
| 1019 | "oauth_consumer_key": "canonical", |
| 1020 | - "country":"GB", "pagesize": 50} |
| 1021 | + "country": "GB", "pagesize": 50} |
| 1022 | url = "%s?%s" % (base_url, urllib.urlencode(params)) |
| 1023 | self.track_fetch = gio.File(url) |
| 1024 | self.track_fetch.read_async(self.track_fetched, |
| 1025 | @@ -263,27 +290,35 @@ |
| 1026 | possibles = [] |
| 1027 | for tnode in tracknodes: |
| 1028 | anodes = tnode.getElementsByTagName("artist") |
| 1029 | - if not anodes: continue |
| 1030 | + if not anodes: |
| 1031 | + continue |
| 1032 | aid = anodes[0].getAttribute("id") |
| 1033 | if aid == artist_id: |
| 1034 | artist_score = 0 |
| 1035 | artist_name = artist |
| 1036 | else: |
| 1037 | atnodes = anodes[0].getElementsByTagName("name") |
| 1038 | - if not atnodes: continue |
| 1039 | - if not atnodes[0].hasChildNodes(): continue |
| 1040 | + if not atnodes: |
| 1041 | + continue |
| 1042 | + if not atnodes[0].hasChildNodes(): |
| 1043 | + continue |
| 1044 | # a +100 penalty for not being the right artist |
| 1045 | artist_score = 100 + modified_levenshtein( |
| 1046 | atnodes[0].firstChild.nodeValue, artist) |
| 1047 | artist_name = atnodes[0].firstChild.nodeValue |
| 1048 | nnodes = tnode.getElementsByTagName("title") |
| 1049 | - if not nnodes: continue |
| 1050 | - if not nnodes[0].hasChildNodes(): continue |
| 1051 | + if not nnodes: |
| 1052 | + continue |
| 1053 | + if not nnodes[0].hasChildNodes(): |
| 1054 | + continue |
| 1055 | rnodes = tnode.getElementsByTagName("release") |
| 1056 | - if not rnodes: continue |
| 1057 | + if not rnodes: |
| 1058 | + continue |
| 1059 | rtnodes = rnodes[0].getElementsByTagName("title") |
| 1060 | - if not rtnodes: continue |
| 1061 | - if not rtnodes[0].hasChildNodes(): continue |
| 1062 | + if not rtnodes: |
| 1063 | + continue |
| 1064 | + if not rtnodes[0].hasChildNodes(): |
| 1065 | + continue |
| 1066 | track_score = modified_levenshtein( |
| 1067 | nnodes[0].firstChild.nodeValue, title) |
| 1068 | album_score = modified_levenshtein( |
| 1069 | @@ -310,32 +345,36 @@ |
| 1070 | match = possibles[0] |
| 1071 | print "got a match", match |
| 1072 | dest_url = "%s/music/l/%s/%s" % ( |
| 1073 | - BASE_URL, match["trackid"], "0") # this 0 should be userid |
| 1074 | - self.builder.get_object("lblProgress").set_markup(_("Found <i>%s</i> by <i>%s</i> from <i>%s</i>") % ( |
| 1075 | - cgi.escape(match["track"]), cgi.escape(match["artist"]), |
| 1076 | - cgi.escape(match["album"]))) |
| 1077 | - self.builder.get_object("lblURL").set_markup("<big><b>%s</b></big>" % dest_url) |
| 1078 | + BASE_URL, match["trackid"], "0") # this 0 should be userid |
| 1079 | + self.builder.get_object("lblProgress").set_markup( |
| 1080 | + _("Found <i>%s</i> by <i>%s</i> from <i>%s</i>") % ( |
| 1081 | + cgi.escape(match["track"]), cgi.escape(match["artist"]), |
| 1082 | + cgi.escape(match["album"]))) |
| 1083 | + self.builder.get_object("lblURL").set_markup( |
| 1084 | + "<big><b>%s</b></big>" % dest_url) |
| 1085 | clipboard = gtk.Clipboard( |
| 1086 | gtk.gdk.display_manager_get().get_default_display(), "CLIPBOARD") |
| 1087 | clipboard.set_text(dest_url) |
| 1088 | |
| 1089 | - if gwibber_available: |
| 1090 | + if GWIBBER_AVAILABLE: |
| 1091 | self.tweet_button = gtk.Button("Tweet this link") |
| 1092 | - self.tweet_button.connect("clicked", self.gwibber, dest_url, |
| 1093 | + self.tweet_button.connect("clicked", self.gwibber, dest_url, |
| 1094 | artist, album, title) |
| 1095 | self.win.get_content_area().pack_end(self.tweet_button) |
| 1096 | self.tweet_button.show() |
| 1097 | - |
| 1098 | + |
| 1099 | def gwibber(self, button, url, artist, album, title): |
| 1100 | + """Show the Gwibber posting widget.""" |
| 1101 | self.pvb = gwibber.lib.gtk.widgets.GwibberPosterVBox() |
| 1102 | - self.message = "Currently listening to %s by %s %s" % (title, artist, url) |
| 1103 | - self.pvb.input.connect("expose-event", self.gwibber_input_text) |
| 1104 | - self.pvb.button_send.connect("clicked", |
| 1105 | + self.message = "Currently listening to %s by %s %s" % ( |
| 1106 | + title, artist, url) |
| 1107 | + self.pvb.input.connect("expose-event", self.gwibber_input_text) |
| 1108 | + self.pvb.button_send.connect("clicked", |
| 1109 | lambda x: self.builder.get_object("winMain").destroy()) |
| 1110 | self.pvb.show_all() |
| 1111 | self.builder.get_object("vbMain").pack_end(self.pvb) |
| 1112 | self.tweet_button.hide() |
| 1113 | |
| 1114 | def gwibber_input_text(self, *args): |
| 1115 | + """Set the Gwibber text input.""" |
| 1116 | self.pvb.input.set_text(self.message) |
| 1117 | - |
| 1118 | |
| 1119 | === modified file 'umusicstore/__init__.py' |
| 1120 | --- umusicstore/__init__.py 2010-09-23 16:36:08 +0000 |
| 1121 | +++ umusicstore/__init__.py 2010-12-21 15:44:57 +0000 |
| 1122 | @@ -17,23 +17,25 @@ |
| 1123 | # Authored by Stuart Langridge <stuart.langridge@canonical.com> |
| 1124 | |
| 1125 | import gconf |
| 1126 | -from MusicStoreWidget import U1MusicStoreWidget |
| 1127 | -from U1MSLinks import U1MSLinkProvider |
| 1128 | - |
| 1129 | -U1_FIRST_TIME_FLAG_ENTRY = "/apps/rhythmbox/plugins/umusicstore/first_time_flag" |
| 1130 | - |
| 1131 | import rb |
| 1132 | |
| 1133 | +from .MusicStoreWidget import U1MusicStoreWidget |
| 1134 | +from .U1MSLinks import U1MSLinkProvider |
| 1135 | + |
| 1136 | +U1_FIRST_TIME_FLAG_ENTRY = \ |
| 1137 | + "/apps/rhythmbox/plugins/umusicstore/first_time_flag" |
| 1138 | + |
| 1139 | + |
| 1140 | class U1MusicStorePlugin (rb.Plugin): |
| 1141 | """The Ubuntu One Music Store.""" |
| 1142 | |
| 1143 | def __init__(self): |
| 1144 | rb.Plugin.__init__(self) |
| 1145 | - |
| 1146 | + |
| 1147 | # The Music Store itself |
| 1148 | self.music_store_widget = U1MusicStoreWidget( |
| 1149 | plugin=self, find_file=self.find_file) |
| 1150 | - |
| 1151 | + |
| 1152 | # The Links button |
| 1153 | self.music_store_links_provider = U1MSLinkProvider( |
| 1154 | find_file=self.find_file) |
| 1155 | @@ -43,8 +45,8 @@ |
| 1156 | self.music_store_widget.activate(shell) |
| 1157 | self.music_store_links_provider.activate(shell) |
| 1158 | |
| 1159 | - # Select the source if it's the first time |
| 1160 | - conf_client = gconf.client_get_default () |
| 1161 | + # Select the source if it's the first time |
| 1162 | + conf_client = gconf.client_get_default() |
| 1163 | if not conf_client.get_bool(U1_FIRST_TIME_FLAG_ENTRY): |
| 1164 | shell.props.sourcelist.select(self.music_store_widget.source) |
| 1165 | conf_client.set_bool(U1_FIRST_TIME_FLAG_ENTRY, True) |
| 1166 | |
| 1167 | === removed file 'umusicstore/umusicstore.rb-plugin' |
| 1168 | --- umusicstore/umusicstore.rb-plugin 2010-07-21 16:03:11 +0000 |
| 1169 | +++ umusicstore/umusicstore.rb-plugin 1970-01-01 00:00:00 +0000 |
| 1170 | @@ -1,10 +0,0 @@ |
| 1171 | -[RB Plugin] |
| 1172 | -Loader=python |
| 1173 | -Module=umusicstore |
| 1174 | -IAge=1 |
| 1175 | -Name=Ubuntu One Music Store |
| 1176 | -Description=The Ubuntu One Music Store |
| 1177 | -Authors=Stuart Langridge <stuart.langridge@canonical.com>, Rodrigo Moya <rodrigo.moya@canonical.com> |
| 1178 | -Copyright=Copyright © 2009 Canonical |
| 1179 | -Website=http://one.ubuntu.com/ |
| 1180 | - |

