Commit 5db81f7c authored by Jonathan Matthew's avatar Jonathan Matthew 🌯

remove jamendo plugin

Downloading the entire catalogue isn't really a viable approach
any more.
parent 4365dd90
......@@ -849,7 +849,6 @@ plugins/sample-vala/Makefile
plugins/pythonconsole/Makefile
plugins/artdisplay/Makefile
plugins/magnatune/Makefile
plugins/jamendo/Makefile
plugins/generic-player/Makefile
plugins/rb/Makefile
plugins/power-manager/Makefile
......
......@@ -322,19 +322,6 @@
<child name='source' schema='org.gnome.rhythmbox.plugins.iradio.source'/>
</schema>
<enum id="org.gnome.rhythmbox.plugins.jamendo.audio-format">
<value nick="ogg3" value="0"/>
<value nick="mp32" value="1"/>
</enum>
<schema id="org.gnome.rhythmbox.plugins.jamendo" path="/org/gnome/rhythmbox/plugins/jamendo/">
<key name="format" enum="org.gnome.rhythmbox.plugins.jamendo.audio-format">
<default>'ogg3'</default>
<summary>Audio format to use for Jamendo downloads</summary>
<description>Audio format to use for Jamendo downloads</description>
</key>
<child name="source" schema="org.gnome.rhythmbox.source"/>
</schema>
<schema id="org.gnome.rhythmbox.plugins.lyrics" path="/org/gnome/rhythmbox/plugins/lyrics/">
<key name="sites" type="as">
<default>['lyrc.com.ar']</default> <!-- do we have any that work? -->
......
......@@ -16,7 +16,6 @@ SUBDIRS += \
artdisplay \
context \
im-status \
jamendo \
lyrics \
magnatune \
pythonconsole \
......
# -*- coding: utf-8 -*-
# JamendoConfigureDialog.py
#
# Copyright (C) 2007 - Guillaume Desmottes
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import rb
from gi.repository import Gtk, Gio, GObject, PeasGtk
format_list = ['ogg3', 'mp32']
class JamendoConfigureDialog (GObject.Object, PeasGtk.Configurable):
__gtype_name__ = 'JamendoConfigureDialog'
object = GObject.property(type=GObject.Object)
def __init__(self):
GObject.Object.__init__(self)
self.settings = Gio.Settings("org.gnome.rhythmbox.plugins.jamendo")
self.hate = self
def do_create_configure_widget(self):
builder = Gtk.Builder()
builder.add_from_file(rb.find_plugin_file(self, "jamendo-prefs.ui"))
self.config = builder.get_object('config')
self.audio_combobox = builder.get_object("audio_combobox")
# probably should just bind this, but too lazy
format_text = self.settings['format']
if not format_text:
format_text = "ogg3"
try:
format = format_list.index(format_text)
except ValueError:
format = 0
self.audio_combobox.set_active(format)
self.audio_combobox.connect("changed", self.audio_combobox_changed)
return self.config
def audio_combobox_changed (self, combobox):
format = self.audio_combobox.get_active()
self.settings['format'] = format_list[format]
# -*- coding: utf-8 -*-
# JamendoSaxHandler.py
#
# Copyright (C) 2007 - Guillaume Desmottes
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import xml.sax, xml.sax.handler
import datetime
import codecs
import rb
from gi.repository import RB
data = {"artist" : ["name"],
"album" : ["name","id","releasedate","id3genre"],
"track" : ["name","id","numalbum","duration","id3genre"]}
stream_url = "http://api.jamendo.com/get2/stream/track/redirect/?id=%s&streamencoding=mp31"
def utf8ise(s):
return codecs.utf_8_encode(s)[0]
class JamendoSaxHandler(xml.sax.handler.ContentHandler):
def __init__(self,db,entry_type):
xml.sax.handler.ContentHandler.__init__(self)
self.__db = db
self.__entry_type = entry_type
self.__data = {}
for section in data:
self.__data[section]={}
self.__section = ""
self.__num_tracks = 0
def startElement(self, name, attrs):
self.__text = ""
self.__parse_content = False
if name in data:
self.__section = name
elif self.__section and name in data[self.__section]:
self.__parse_content = True
def endElement(self, name):
if self.__parse_content:
self.__data[self.__section][name] = self.__text
elif name == "track":
self.__num_tracks = self.__num_tracks + 1
track_url = stream_url % (self.__data["track"]["id"])
release_date = self.__data["album"]["releasedate"]
year = int(release_date[0:4])
date = datetime.date(year, 1, 1).toordinal()
try:
albumgenre = genre_id3[int(self.__data["album"]["id3genre"])]
except Exception:
albumgenre = _('Unknown')
try:
duration = int(float(self.__data["track"]["duration"]))
except Exception:
duration = 0
entry = self.__db.entry_lookup_by_location (track_url)
if entry == None:
entry = RB.RhythmDBEntry.new(self.__db, self.__entry_type, track_url)
self.__db.entry_set(entry, RB.RhythmDBPropType.ARTIST, utf8ise(self.__data["artist"]["name"]))
self.__db.entry_set(entry, RB.RhythmDBPropType.ALBUM, utf8ise(self.__data["album"]["name"]))
self.__db.entry_set(entry, RB.RhythmDBPropType.TITLE, utf8ise(self.__data["track"]["name"]))
self.__db.entry_set(entry, RB.RhythmDBPropType.TRACK_NUMBER, int(self.__data["track"]["numalbum"]))
self.__db.entry_set(entry, RB.RhythmDBPropType.DATE, date)
self.__db.entry_set(entry, RB.RhythmDBPropType.GENRE, albumgenre)
self.__db.entry_set(entry, RB.RhythmDBPropType.DURATION, duration)
# slight misuse, but this is far more efficient than having a python dict
# containing this data.
self.__db.entry_set(entry, RB.RhythmDBPropType.MB_ALBUMID, utf8ise(self.__data["album"]["id"]))
if self.__num_tracks % 1000 == 0:
self.__db.commit()
elif name == "JamendoData":
self.__db.commit()
#clean up data
if name in data:
self.__data[name].clear ()
def characters(self, content):
if self.__parse_content:
self.__text = self.__text + content
genre_id3 = ["Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge","Hip-Hop","Jazz","Metal","New Age","Oldies","Other","Pop","R&B","Rap","Reggae","Rock","Techno","Industrial","Alternative","Ska","Death Metal","Pranks","Soundtrack","Euro-Techno","Ambient","Trip-Hop","Vocal","Jazz+Funk","Fusion","Trance","Classical","Instrumental","Acid","House","Game","Sound Clip","Gospel","Noise","AlternRock","Bass","Soul","Punk","Space","Meditative","Instrumental Pop","Instrumental Rock","Ethnic","Gothic","Darkwave","Techno-Industrial","Electronic","Pop-Folk","Eurodance","Dream","Southern Rock","Comedy","Cult","Gangsta","Top 40","Christian Rap","Pop/Funk","Jungle","Native American","Cabaret","New Wave","Psychadelic","Rave","Showtunes","Trailer","Lo-Fi","Tribal","Acid Punk","Acid Jazz","Polka","Retro","Musical","Rock & Roll","Hard Rock","Folk","Folk-Rock","National Folk","Swing","Fast Fusion","Bebob","Latin","Revival","Celtic","Bluegrass","Avantgarde","Gothic Rock","Progressive Rock","Psychedelic Rock","Symphonic Rock","Slow Rock","Big Band","Chorus","Easy Listening","Acoustic","Humour","Speech","Chanson","Opera","Chamber Music","Sonata","Symphony","Booty Bass","Primus","Porn Groove","Satire","Slow Jam","Club","Tango","Samba","Folklore","Ballad","Power Ballad","Rhythmic Soul","Freestyle","Duet","Punk Rock","Drum Solo","Acapella","Euro-House","Dance Hall"]
# -*- coding: utf-8 -*-
# JamendoSource.py
#
# Copyright (C) 2007 - Guillaume Desmottes
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Parts from "Magnatune Rhythmbox plugin" (stolen from rhythmbox's MagnatuneSource.py)
# Copyright (C), 2006 Adam Zimmerman <adam_zimmerman@sfu.ca>
import os
import xml
import gzip
import datetime
import rb
from gi.repository import GObject, Gtk, Gdk, Gio
from gi.repository import RB
from JamendoSaxHandler import JamendoSaxHandler
import JamendoConfigureDialog
# URIs
jamendo_song_info_uri = "http://img.jamendo.com/data/dbdump_artistalbumtrack.xml.gz"
mp32_uri = "http://api.jamendo.com/get2/bittorrent/file/plain/?type=archive&class=mp32&album_id="
ogg3_uri = "http://api.jamendo.com/get2/bittorrent/file/plain/?type=archive&class=ogg3&album_id="
# MP3s for streaming : http://api.jamendo.com/get2/stream/track/redirect/?id={TRACKID}&streamencoding=mp31
# OGGs for streaming : http://api.jamendo.com/get2/stream/track/redirect/?id={TRACKID}&streamencoding=ogg2
# .torrent file for download (MP3 archive) : http://api.jamendo.com/get2/bittorrent/file/plain/?album_id={ALBUMID}&type=archive&class=mp32
# .torrent file for download (OGG archive) : http://api.jamendo.com/get2/bittorrent/file/plain/?album_id={ALBUMID}&type=archive&class=ogg3
# Album Covers are available here: http://api.jamendo.com/get2/image/album/redirect/?id={ALBUMID}&imagesize={100-600}
artwork_url = "http://api.jamendo.com/get2/image/album/redirect/?id=%s&imagesize=200"
artist_url = "http://www.jamendo.com/get/artist/id/album/page/plain/"
class JamendoSource(RB.BrowserSource):
def __init__(self):
RB.BrowserSource.__init__(self, name=_("Jamendo"))
# catalogue stuff
self.hate = self
self.__db = None
self.__saxHandler = None
self.__activated = False
self.__notify_id = 0
self.__update_id = 0
self.__info_screen = None
self.__updating = True
self.__load_current_size = 0
self.__load_total_size = 0
self.__db_load_finished = False
self.__catalogue_loader = None
self.__catalogue_check = None
self.__jamendo_dir = RB.find_user_cache_file("jamendo")
if os.path.exists(self.__jamendo_dir) is False:
os.makedirs(self.__jamendo_dir, 0700)
self.__local_catalogue_path = os.path.join(self.__jamendo_dir, "dbdump.xml")
self.__local_catalogue_temp = os.path.join(self.__jamendo_dir, "dbdump.xml.tmp")
self.settings = Gio.Settings("org.gnome.rhythmbox.plugins.jamendo")
def do_impl_can_delete (self):
return False
def do_impl_pack_paned (self, paned):
self.__paned_box = Gtk.VBox(homogeneous=False, spacing=5)
self.pack_start(self.__paned_box, True, True, 0)
self.__paned_box.pack_start(paned, True, True, 0)
#
# RBSource methods
#
def do_impl_show_entry_popup(self):
self.show_source_popup ("/JamendoSourceViewPopup")
def do_get_ui_actions(self):
return ["JamendoDownloadAlbum","JamendoDonateArtist"]
def do_get_status(self, status, progress_text, progress):
if self.__updating:
if self.__load_total_size > 0:
progress = min (float(self.__load_current_size) / self.__load_total_size, 1.0)
else:
progress = -1.0
return (_("Loading Jamendo catalog"), None, progress)
else:
qm = self.props.query_model
return (qm.compute_status_normal("%d song", "%d songs"), None, 2.0)
def do_selected(self):
if not self.__activated:
shell = self.props.shell
self.__db = shell.props.db
self.__entry_type = self.props.entry_type
self.__activated = True
self.__show_loading_screen (True)
# start our catalogue updates
self.__update_id = GObject.timeout_add_seconds(6 * 60 * 60, self.__update_catalogue)
self.__update_catalogue()
def do_delete_thyself(self):
if self.__update_id != 0:
GObject.source_remove (self.__update_id)
self.__update_id = 0
if self.__notify_id != 0:
GObject.source_remove (self.__notify_id)
self.__notify_id = 0
if self.__catalogue_loader:
self.__catalogue_loader.cancel()
self.__catalogue_loader = None
if self.__catalogue_check:
self.__catalogue_check.cancel()
self.__catalogue_check = None
#
# internal catalogue downloading and loading
#
def __catalogue_chunk_cb(self, result, total):
if not result or isinstance (result, Exception):
if result:
# report error somehow?
print "error loading catalogue: %s" % result
self.__parser.close()
self.__db_load_finished = True
self.__updating = False
self.__saxHandler = None
self.__show_loading_screen (False)
self.__catalogue_loader = None
return
self.__parser.feed(result)
self.__load_current_size += len(result)
self.__load_total_size = total
self.__notify_status_changed()
def __load_catalogue(self):
print "loading catalogue %s" % self.__local_catalogue_path
self.__notify_status_changed()
self.__db_load_finished = False
self.__saxHandler = JamendoSaxHandler(self.__db, self.__entry_type)
self.__parser = xml.sax.make_parser()
self.__parser.setContentHandler(self.__saxHandler)
self.__catalogue_loader = rb.ChunkLoader()
self.__catalogue_loader.get_url_chunks(self.__local_catalogue_path, 64*1024, True, self.__catalogue_chunk_cb)
def __download_catalogue_chunk_cb (self, result, total, out):
if not result:
# done downloading, unzip to real location
out.close()
catalog = gzip.open(self.__local_catalogue_temp)
out = open(self.__local_catalogue_path, 'w')
while True:
s = catalog.read(4096)
if s == "":
break
out.write(s)
out.close()
catalog.close()
os.unlink(self.__local_catalogue_temp)
self.__db_load_finished = True
self.__show_loading_screen (False)
self.__catalogue_loader = None
self.__load_catalogue ()
elif isinstance(result, Exception):
# complain
pass
else:
out.write(result)
self.__load_current_size += len(result)
self.__load_total_size = total
self.__notify_status_changed()
def __download_catalogue(self):
print "downloading catalogue"
self.__updating = True
out = open(self.__local_catalogue_temp, 'w')
self.__catalogue_loader = rb.ChunkLoader()
self.__catalogue_loader.get_url_chunks(jamendo_song_info_uri, 4*1024, True, self.__download_catalogue_chunk_cb, out)
def __update_catalogue(self):
def update_cb (result):
self.__catalogue_check = None
if result is True:
self.__download_catalogue()
elif self.__db_load_finished is False:
self.__load_catalogue()
self.__catalogue_check = rb.UpdateCheck()
self.__catalogue_check.check_for_update(self.__local_catalogue_path, jamendo_song_info_uri, update_cb)
def __show_loading_screen(self, show):
if self.__info_screen is None:
# load the builder stuff
builder = Gtk.Builder()
builder.add_from_file(rb.find_plugin_file(self.props.plugin, "jamendo-loading.ui"))
self.__info_screen = builder.get_object("jamendo_loading_scrolledwindow")
self.pack_start(self.__info_screen, True, True, 0)
self.get_entry_view().set_no_show_all (True)
self.__info_screen.set_no_show_all (True)
self.__info_screen.set_property("visible", show)
self.__paned_box.set_property("visible", not show)
def __notify_status_changed(self):
def change_idle_cb():
self.notify_status_changed()
self.__notify_id = 0
return False
if self.__notify_id == 0:
self.__notify_id = GObject.idle_add(change_idle_cb)
# Download album
def download_album (self):
tracks = self.get_entry_view().get_selected_entries()
format = self.settings['format']
if not format or format not in JamendoConfigureDialog.format_list:
format = 'ogg3'
#TODO: this should work if the album was selected in the browser
#without any track selected
if len(tracks) == 1:
track = tracks[0]
albumid = track.get_string(RB.RhythmDBPropType.MB_ALBUMID)
formats = {}
formats["mp32"] = mp32_uri + albumid
formats["ogg3"] = ogg3_uri + albumid
p2plink = formats[format]
l = rb.Loader()
l.get_url(p2plink, self.__download_p2plink, albumid)
def __download_p2plink (self, result, albumid):
if result is None:
emsg = _("Error looking up p2plink for album %s on jamendo.com") % (albumid)
Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, emsg).run()
return
Gtk.show_uri(self.props.shell.props.window.get_screen(), result, Gdk.CURRENT_TIME)
# Donate to Artist
def launch_donate (self):
tracks = self.get_entry_view().get_selected_entries()
#TODO: this should work if the artist was selected in the browser
#without any track selected
if len(tracks) == 1:
track = tracks[0]
# The Album ID can be used to lookup the artist, and issue a clean redirect.
albumid = track.get_string(RB.RhythmDBPropType.MB_ALBUMID)
artist = track.get_string(RB.RhythmDBPropType.ARTIST)
url = artist_url + albumid.__str__() + "/"
l = rb.Loader()
l.get_url(url, self.__open_donate, artist)
def __open_donate (self, result, artist):
if result is None:
emsg = _("Error looking up artist %s on jamendo.com") % (artist)
Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, emsg).run()
return
Gtk.show_uri(self.props.shell.props.window.get_screen(), result + "donate/", Gdk.CURRENT_TIME)
def playing_entry_changed (self, entry):
if not self.__db or not entry:
return
if entry.get_entry_type() != self.__db.entry_type_get_by_name("JamendoEntryType"):
return
GObject.idle_add(self.emit_cover_art_uri, entry)
def emit_cover_art_uri (self, entry):
stream = self.__db.entry_get_string (entry, RB.RhythmDBPropType.LOCATION)
albumid = self.__db.entry_get_string (entry, RB.RhythmDBPropType.MB_ALBUMID)
url = artwork_url % albumid
self.__db.emit_entry_extra_metadata_notify (entry, "rb:coverArt-uri", str(url))
return False
GObject.type_register(JamendoSource)
# Jamendo Python Plugin
plugindir = $(PLUGINDIR)/jamendo
plugindatadir = $(PLUGINDATADIR)/jamendo
plugin_PYTHON = \
JamendoSource.py \
JamendoSaxHandler.py \
JamendoConfigureDialog.py \
jamendo.py
%.plugin: %.plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
plugin_DATA = $(plugin_in_files:.plugin.in=.plugin)
plugin_in_files = jamendo.plugin.in
gtkbuilderdir = $(plugindatadir)
gtkbuilder_DATA = \
jamendo-loading.ui \
jamendo-prefs.ui \
jamendo_logo.png \
jamendo_logo_medium.png
themedir = $(pkgdatadir)/icons/hicolor
size = 24x24
context = places
icondir = $(themedir)/$(size)/$(context)
icon_DATA = icons/hicolor/$(size)/$(context)/jamendo.png
EXTRA_DIST = $(plugin_in_files) $(gtkbuilder_DATA) $(icon_DATA)
CLEANFILES = $(plugin_DATA)
DISTCLEANFILES = $(plugin_DATA)
<?xml version="1.0"?>
<interface>
<!-- interface-requires gtk+ 2.12 -->
<!-- interface-naming-policy toplevel-contextual -->
<object class="GtkScrolledWindow" id="jamendo_loading_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<object class="GtkViewport" id="viewport1">
<property name="visible">True</property>
<property name="resize_mode">queue</property>
<child>
<object class="GtkVBox" id="jamendo_loading_vbox">
<property name="visible">True</property>
<property name="border_width">12</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="pixbuf">jamendo_logo.png</property>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Jamendo&lt;/b&gt;</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Jamendo is a new model for artists to promote, publish, and be paid for their music.</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">On Jamendo, the artists distribute their music under Creative Commons licenses.
In a nutshell, they allow you to download, remix and share their music freely.
It's a "Some rights reserved" agreement, perfectly suited for the new century.</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">These new rules make Jamendo able to use the new powerful means of digital distribution like
Peer-to-Peer networks such as BitTorrent or eMule to legally distribute albums at near-zero cost.</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Jamendo users can discover and share albums, but also review them or start a discussion on the forums.
Albums are democratically rated based on the visitors’ reviews.
If they fancy an artist they can support him by making a donation.</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Jamendo is the only platform that joins together :</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">6</property>
</packing>
</child>
<child>
<object class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="n_rows">5</property>
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes"> * Free, simple and quick access to the music, for everyone.</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label9">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes" comments="This text comes from http://www.jamendo.com/en/static/concept/ and was already translated in many languages. You should take a look on this site and check if you can't reuse it."> * A legal framework protecting the artists (thanks to the Creative Commons licenses).</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label10">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes"> * The use of the latest Peer-to-Peer technologies</property>
</object>