=== added file 'AmpacheBrowser.py' --- AmpacheBrowser.py 1970-01-01 00:00:00 +0000 +++ AmpacheBrowser.py 2012-04-18 10:35:48 +0000 @@ -0,0 +1,427 @@ +# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*- +# vim: expandtab shiftwidth=8 softtabstop=8 tabstop=8 + +# todo: +# - file append via write_async() results in garbage at the beginning of +# the data, temporary workaround: use python file I/O +# - refetch of song data will result in entry->refcount > 0 assertion +# and Segmentation Fault + +from gi.repository import RB +from gi.repository import GObject, Gtk, Gio, GLib + +import time +from time import mktime +from datetime import datetime +import re +import hashlib +import os +import os.path +import sys + +import xml.sax, xml.sax.handler + +class HandshakeHandler(xml.sax.handler.ContentHandler): + def __init__(self, handshake): + xml.sax.handler.ContentHandler.__init__(self) + self.__handshake = handshake + + def startElement(self, name, attrs): + self.__text = '' + + def endElement(self, name): + self.__handshake[name] = self.__text + + def characters(self, content): + self.__text = self.__text + content + +class SongsHandler(xml.sax.handler.ContentHandler): + def __init__(self, db, entry_type, albumart, auth): + xml.sax.handler.ContentHandler.__init__(self) + self.__db = db + self.__entry_type = entry_type + self.__albumart = albumart + self.__auth = auth + self.__clear() + self.__re_auth = re.compile('\\b(?:auth|ssid)=[a-fA-F0-9]*') + + def startElement(self, name, attrs): + if name == 'song': + self.__id = attrs['id'] + self.__text = '' + + def endElement(self, name): + if name == 'song': + try: + # add the track to the source if it doesn't exist + entry = self.__db.entry_lookup_by_location(str(self.__url)) + if entry == None: + entry = RB.RhythmDBEntry.new(self.__db, self.__entry_type, str(self.__url)) + + if self.__artist != '': + self.__db.entry_set(entry, RB.RhythmDBPropType.ARTIST, str(self.__artist)) + if self.__album != '': + self.__db.entry_set(entry, RB.RhythmDBPropType.ALBUM, str(self.__album)) + if self.__title != '': + self.__db.entry_set(entry, RB.RhythmDBPropType.TITLE, str(self.__title)) + if self.__tag != '': + self.__db.entry_set(entry, RB.RhythmDBPropType.GENRE, str(self.__tag)) + self.__db.entry_set(entry, RB.RhythmDBPropType.TRACK_NUMBER, self.__track) + self.__db.entry_set(entry, RB.RhythmDBPropType.DATE, self.__year) + self.__db.entry_set(entry, RB.RhythmDBPropType.DURATION, self.__time) + self.__db.entry_set(entry, RB.RhythmDBPropType.FILE_SIZE, self.__size) + self.__db.entry_set(entry, RB.RhythmDBPropType.RATING, self.__rating) + self.__db.commit() + + self.__albumart[str(self.__artist) + str(self.__album)] = str(self.__art) + + except Exception,e: # This happens on duplicate uris being added + sys.excepthook(*sys.exc_info()) + print("Couldn't add %s - %s" % (self.__artist, self.__title), e) + + self.__clear() + + elif name == 'url': + if self.__auth: # replace ssid string with new auth string + self.__text = re.sub(self.__re_auth, 'ssid='+self.__auth, self.__text); + self.__url = self.__text + elif name == 'artist': + self.__artist = self.__text.encode('utf-8') + elif name == 'album': + self.__album = self.__text.encode('utf-8') + elif name == 'title': + self.__title = self.__text.encode('utf-8') + elif name == 'tag': + self.__tag = self.__text + elif name == 'track': + self.__track = int(self.__text) + elif name == 'year': + if (GLib.Date.valid_year(int(self.__text))): + self.__year = GLib.Date.new_dmy(1, 1, int(self.__text)).get_julian() + elif name == 'time': + self.__time = int(self.__text) + elif name == 'size': + self.__size = int(self.__text) + elif name == 'rating': + self.__rating = int(self.__text) + elif name == 'art': + if self.__auth: # replace auth string with new auth string + self.__text = re.sub(self.__re_auth, 'auth='+self.__auth, self.__text); + self.__art = self.__text + else: + self.__null = self.__text + + def characters(self, content): + self.__text = self.__text + content + + def __clear(self): + self.__id = 0 + self.__url = '' + self.__artist = '' + self.__album = '' + self.__title = '' + self.__tag = '' + self.__track = '' + self.__year = 0 + self.__time = 0 + self.__size = 0 + self.__rating = 0 + self.__art = '' + +class AmpacheBrowser(RB.BrowserSource): + + def __init__(self): + RB.BrowserSource.__init__(self, name=_("Ampache")) + + self.__limit = 5000 + + self.__cache_filename = os.path.join(RB.user_cache_dir(), 'ampache', 'song_cache.xml') + self.settings = Gio.Settings('org.gnome.rhythmbox.plugins.ampache') + self.__albumart = {} + + self.__text = None + self.__progress_text = None + self.__progress = 1 + + self.__activate = False + + def do_show_popup(self): + if self.__activate: + self.__popup.popup(None, None, None, None, 3, Gtk.get_current_event_time()) + + def download_catalog(self): + +# def cache_saved_cb(stream, result, data): +# try: +# size = stream.write_finish(result) +# except Exception, e: +# print("error writing file: %s" % (self.__cache_filename)) +# sys.excepthook(*sys.exc_info()) +# +# # close stream +# stream.close(Gio.Cancellable()) +# +# # change modification time to update time +# update_time = int(mktime(self.__handshake_update.timetuple())) +# os.utime(self.__cache_filename, (update_time, update_time)) +# def open_append_cb(file, result, data): +# try: +# stream = file.append_to_finish(result) +# except Exception, e: +# print("error opening file for writing %s" % (self.__cache_filename)) +# sys.excepthook(*sys.exc_info()) +# +# stream.write_async( +# data.encode('utf-8'), +# GLib.PRIORITY_DEFAULT, +# Gio.Cancellable(), +# cache_saved_cb, +# None) +# print("write to cache file: %s" % (self.__cache_filename)) + + def songs_downloaded_cb(file, result, data): + try: + (ok, contents, etag) = file.load_contents_finish(result) + except Exception, e: + emsg = _('Catalog response: %s') % e + edlg = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, emsg) + edlg.run() + edlg.destroy() + self.__activate = False + return + + offset = data[0] + self.__limit + + self.__progress = float(offset) / float(self.__handshake_songs) + self.notify_status_changed() + + if offset < self.__handshake_songs: + # download subsequent chunk of songs + download_songs_chunk(offset, data[1], data[2]) + else: + self.__text = '' + self.__progress = 1 + self.notify_status_changed() + + try: + data[1].feed(contents) + data[1].reset() + except xml.sax.SAXParseException, e: + print("error parsing songs: %s" % e) + + # remove enveloping and tags + # as needed to regenerate one full .xml + lines = contents.decode('utf-8').splitlines(True) + if data[0] > 0: + del lines[:2] + if offset < self.__handshake_songs: + del lines[-2:] + + contents = ''.join(lines) + +# data[2].append_to_async( +# Gio.FileCreateFlags.NONE, +# GLib.PRIORITY_DEFAULT, +# Gio.Cancellable(), +# open_append_cb, +# contents) + data[2].writelines(contents.encode('utf-8')) + print("append to cache file: %s" % (self.__cache_filename)) + if offset >= self.__handshake_songs: + data[2].close() + + # change modification time to update time + update_time = int(mktime(self.__handshake_update.timetuple())) + os.utime(self.__cache_filename, (update_time, update_time)) +# + def download_songs_chunk(offset, parser, cache_file): + ampache_server_uri = '%s/server/xml.server.php?action=songs&auth=%s&offset=%s&limit=%s' % (self.settings['url'], self.__handshake_auth, offset, self.__limit) + ampache_server_file = Gio.file_new_for_uri(ampache_server_uri) + ampache_server_file.load_contents_async( + Gio.Cancellable(), + songs_downloaded_cb, + (offset, parser, cache_file)) + print("downloading songs: %s" % (ampache_server_uri)) + + self.__text = 'Download songs from Ampache server...' + self.__progress = 0 + self.notify_status_changed() + + # instantiate songs parser + parser = xml.sax.make_parser() + parser.setContentHandler(SongsHandler(self.__db, self.__entry_type, self.__albumart, None)) + +# cache_file = Gio.file_new_for_path(self.__cache_filename) + cache_file = open(self.__cache_filename, "w") + + # delete cache file if available +# try: +# cache_file.delete(Gio.Cancellable()) +# except Exception, e: +# pass + + # delete all ampache songs from database + self.__db.entry_delete_by_type(self.__entry_type) + + # download first chunk of songs + download_songs_chunk(0, parser, cache_file) + + def update_catalog(self): + + def handshake_cb(file, result, parser): + try: + (ok, contents, etag) = file.load_contents_finish(result) + except Exception, e: + emsg = _('Handshake response: %s') % e + edlg = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, emsg) + edlg.run() + edlg.destroy() + self.__activate = False + return + + try: + parser.feed(contents) + except xml.sax.SAXParseException, e: + print("error parsing handshake: %s" % e) + + # convert handshake update time into datetime + self.__handshake_update = datetime.strptime(handshake['update'][0:18], '%Y-%m-%dT%H:%M:%S') + self.__handshake_auth = handshake['auth'] + self.__handshake_songs = int(handshake['songs']) + + # cache file mtime >= handshake update time: load cached + if os.path.exists(self.__cache_filename) and datetime.fromtimestamp(os.path.getmtime(self.__cache_filename)) >= self.__handshake_update: + load_catalog() + else: + self.download_catalog() + + # check for errors + if not self.settings['url']: + edlg = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, _('URL missing')) + edlg.run() + edlg.destroy() + self.__activate = False + return + + if not self.settings['password']: + edlg = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, _('Password missing')) + edlg.run() + edlg.destroy() + self.__activate = False + return + + self.__text = 'Update songs...' + self.notify_status_changed() + + handshake = {} + + # instantiate handshake parser + parser = xml.sax.make_parser() + parser.setContentHandler(HandshakeHandler(handshake)) + + # build handshake url + timestamp = int(time.time()) + password = hashlib.sha256(self.settings['password']).hexdigest() + authkey = hashlib.sha256(str(timestamp) + password).hexdigest() + + # execute handshake + ampache_server_uri = '%s/server/xml.server.php?action=handshake&auth=%s×tamp=%s&user=%s&version=350001' % (self.settings['url'], authkey, timestamp, self.settings['username']) + ampache_server_file = Gio.file_new_for_uri(ampache_server_uri) + ampache_server_file.load_contents_async( + Gio.Cancellable(), + handshake_cb, + parser) + print("downloading handshake: %s" % (ampache_server_uri)) + + def load_catalog(): + def songs_loaded_cb(file, result, parser): + try: + (ok, contents, etag) = file.load_contents_finish(result) + except Exception, e: + RB.error_dialog( + title=_("Unable to load catalog"), + message=_("Rhythmbox could not load the Ampache catalog.")) + return + + try: + parser.feed(contents) + except xml.sax.SAXParseException, e: + print("error parsing songs: %s" % e) + + self.__text = '' + self.__progress = 1 + self.notify_status_changed() + + self.__text = 'Load songs from cache...' + self.__progress = 0 + self.notify_status_changed() + + # instantiate songs parser + parser = xml.sax.make_parser() + parser.setContentHandler(SongsHandler(self.__db, self.__entry_type, self.__albumart, self.__handshake_auth)) + + cache_file = Gio.file_new_for_path(self.__cache_filename) + cache_file.load_contents_async( + Gio.Cancellable(), + songs_loaded_cb, + parser) + + # Source is activated + def do_activate(self): + + # activate source if inactive + if not self.__activate: + self.__activate = True + + shell = self.props.shell + + # get db + self.__db = shell.props.db + self.__entry_type = self.props.entry_type + + # connect playing-song-changed signal + self.__art_store = RB.ExtDB(name="album-art") + self.__art_request = self.__art_store.connect("request", self.__album_art_requested) + + # get popup menu + self.__popup = shell.props.ui_manager.get_widget('/AmpacheSourceViewPopup') + + # create cache directory if it doesn't exist + cache_path = os.path.dirname(self.__cache_filename) + if not os.path.exists(cache_path): + os.mkdir(cache_path, 0700) + + self.update_catalog() + + # Source is deactivated + def do_deactivate(self): + + # deactivate source if active + if self.__activate: + self.__activate = False + + self.__art_store.disconnect(self.__art_request) + self.__art_store = None + + shell.props.ui_manager.remove_ui(self.__popup) + + self.object = None + + # Shortcut for single click + def do_selected(self): + self.do_activate() + + def __album_art_requested(self, store, key, last_time): + artist = key.get_field('artist') + album = key.get_field('album') + uri = self.__albumart[artist + album] + print('album art uri: %s' % uri) + if uri: + storekey = RB.ExtDBKey.create_storage('album', album) + storekey.add_field('artist', artist) + store.store_uri(storekey, RB.ExtDBSourceType.SEARCH, uri) + + def do_get_status(self, status, progress_text, progress): + return (self.__text, self.__progress_text, self.__progress) + +GObject.type_register(AmpacheBrowser) === added file 'AmpacheConfigDialog.py' --- AmpacheConfigDialog.py 1970-01-01 00:00:00 +0000 +++ AmpacheConfigDialog.py 2012-04-18 10:35:48 +0000 @@ -0,0 +1,40 @@ +# vim: expandtab shiftwidth=8 softtabstop=8 tabstop=8 + +import rb +from gi.repository import GObject, Gtk, Gio, PeasGtk + +class AmpacheConfigDialog(GObject.Object, PeasGtk.Configurable): + __gtype_name__ = 'AmpacheConfigDialog' + object = GObject.property(type=GObject.Object) + + def do_create_configure_widget(self): + + self.settings = Gio.Settings('org.gnome.rhythmbox.plugins.ampache') + self.ui = Gtk.Builder() + self.ui.add_from_file(rb.find_plugin_file(self, 'ampache-prefs.ui')) + self.config_dialog = self.ui.get_object('config') + + self.url = self.ui.get_object("url_entry") + self.url.set_text(self.settings['url']) + + self.username = self.ui.get_object("username_entry") + self.username.set_text(self.settings['username']) + + self.password = self.ui.get_object("password_entry") + self.password.set_visibility(False) + self.password.set_text(self.settings['password']) + + self.url.connect('changed', self.url_changed_cb) + self.username.connect('changed', self.username_changed_cb) + self.password.connect('changed', self.password_changed_cb) + + return self.config_dialog + + def url_changed_cb(self, widget): + self.settings['url'] = self.url.get_text() + + def username_changed_cb(self, widget): + self.settings['username'] = self.username.get_text() + + def password_changed_cb(self, widget): + self.settings['password'] = self.password.get_text() === added file 'LICENSE' --- LICENSE 1970-01-01 00:00:00 +0000 +++ LICENSE 2012-04-18 10:35:48 +0000 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 of the License, 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. === added file 'README' --- README 1970-01-01 00:00:00 +0000 +++ README 2012-04-18 10:35:48 +0000 @@ -0,0 +1,56 @@ +INFO + +Plase see http://code.google.com/p/rhythmbox-ampache/ for the latest version + +Ampache setup + +Read Ampache Access Control Lists documentation and create an ACL for RPC (XML API) + http://ampache.org/wiki/config:acl + +Rhythmbox setup + +Untar and put the ampache directory into $HOME/.gnome2/rhythmbox/plugins +or /usr/lib/rhythmbox/plugins + +Run rhythmbox (preferable with "-D ampache" from command line) + +Click Edit, select Plugins dialog box, enable "Ampache Library" + +Click Configure, enter your infromation, for example URL: + http://test.com/server/xml.server.php + +Click on Ampache under Library, songs should appear in the browser + + +COPYRIGHT + +Copyright (C) 2008-2010 Rhythmbox Ampache plugin team + +20-04-2010 - Philip Langdale + +I did some work over the weekend to fix the blocking nature of +the plugin. I switched to using async gio to make the http +transfers and dropped the per-request limit to 100. The net +result is a much more responsive UI that also shows progress +more often. + +I also fixed the reference leaks that prevent rhythmbox +exiting with the plugin today. + +19-04-2010 - Graham Grindlay + * Added a few small fixes to keep the plugin from choking on bad/empty tag data. + * Genre tags didn't seem to be coming through properly so I set them using the "tag" + field instead. This seems to work fine, although if multiple tags are associated + with a track, only the first one will be used. If you don't like this, you can + just comment out line 148 (self.db.set(e, rhythmdb.PROP_GENRE, e_genre)) + +Patched to work with Ampache 3.5.x by Massimo Mund + +Copyright (C) 2008 Seva + +Portions from Magnatune Rhythmbox plugin +Copyright (C) 2006 Adam Zimmerman +Copyright (C) 2006 James Livingston + +Portions from 'git clone http://quickplay.isfound.at' +Copyright (C) 2008 Kevin James Purdy irc://irc.freenode.org/purdyk,isnick === removed directory 'ampache' === added file 'ampache-prefs.ui' --- ampache-prefs.ui 1970-01-01 00:00:00 +0000 +++ ampache-prefs.ui 2012-04-18 10:35:48 +0000 @@ -0,0 +1,125 @@ + + + + + True + False + 5 + 10 + + + + True + True + 0 + + + + + True + False + 0 + <b>Ampache Instance:</b> + True + + + False + True + 1 + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 3 + 2 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + 1 + 2 + 1 + 2 + + + + + True + False + 0 + Server UR_L: + True + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + 1 + 2 + + + + + True + False + 0 + _Username (optional): + True + + + 1 + 2 + + + + + True + False + 0 + _Password (Key): + True + + + 2 + 3 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + 1 + 2 + 2 + 3 + + + + + True + True + 2 + + + + === added file 'ampache.ico' Binary files ampache.ico 1970-01-01 00:00:00 +0000 and ampache.ico 2012-04-18 10:35:48 +0000 differ === added file 'ampache.plugin' --- ampache.plugin 1970-01-01 00:00:00 +0000 +++ ampache.plugin 2012-04-18 10:35:48 +0000 @@ -0,0 +1,10 @@ +[Plugin] +Loader=python +Module=ampache +IAge=2 +Depends=rb +Name=Ampache Library +Description=Adds support for playing music from an Ampache instance +Authors=Rhythmbox Ampache plugin team +Copyright=Copyright ©2008-2010 Rhythmbox Ampache plugin team +Website=http://code.google.com/p/rhythmbox-ampache/ === added file 'ampache.png' Binary files ampache.png 1970-01-01 00:00:00 +0000 and ampache.png 2012-04-18 10:35:48 +0000 differ === added file 'ampache.py' --- ampache.py 1970-01-01 00:00:00 +0000 +++ ampache.py 2012-04-18 10:35:48 +0000 @@ -0,0 +1,97 @@ +# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*- +# vim: expandtab shiftwidth=8 softtabstop=8 tabstop=8 + +# todo: +# - do_deactivate: entry_delete_by_type() results in entry->refcount > 0 +# assertion and Segmentation Fault + +import rb +from gi.repository import RB +from gi.repository import GObject, Peas, Gtk, Gio, GdkPixbuf + +from AmpacheConfigDialog import AmpacheConfigDialog +from AmpacheBrowser import AmpacheBrowser + +popup_ui = """ + + + + + +""" + +class AmpacheEntryType(RB.RhythmDBEntryType): + def __init__(self): + RB.RhythmDBEntryType.__init__(self, name='AmpacheEntryType') + + def can_sync_metadata(self, entry): + return True + + def sync_metadata(self, entry, changes): + return + + +class Ampache(GObject.Object, Peas.Activatable): + __gtype_name__ = 'AmpachePlugin' + object = GObject.property(type=GObject.Object) + + def do_activate(self): + shell = self.object + self.db = shell.props.db + + self.entry_type = AmpacheEntryType() + self.db.register_entry_type(self.entry_type) + + theme = Gtk.IconTheme.get_default() + what, width, height = Gtk.icon_size_lookup(Gtk.IconSize.LARGE_TOOLBAR) + icon = GdkPixbuf.Pixbuf.new_from_file_at_size(rb.find_plugin_file(self, 'ampache.ico'), width, height) + + group = RB.DisplayPageGroup.get_by_id("shared") + settings = Gio.Settings("org.gnome.rhythmbox.plugins.ampache") + + self.source = GObject.new( + AmpacheBrowser, + shell=shell, + entry_type=self.entry_type, + pixbuf=icon, + plugin=self, + settings=settings.get_child("source"), + name=_("Ampache") + ) + + shell.register_entry_type_for_source(self.source, self.entry_type) + shell.append_display_page(self.source, group) + + manager = shell.props.ui_manager + self.action_group = Gtk.ActionGroup('AmpacheActions') + action = Gtk.Action('RefetchAmpache', + _('_Refetch Ampache Library'), + _('Update the local ampache library'), + '') + + action.connect('activate', self.refetch_ampache) + self.action_group.add_action(action) + manager.insert_action_group(self.action_group, -1) + self.ui_id = manager.add_ui_from_string(popup_ui) + manager.ensure_update() + + def do_deactivate(self): + shell = self.object + + manager = shell.props.ui_manager + manager.remove_ui(self.ui_id) + manager.remove_action_group(self.action_group) + self.action_group = None + +# self.db.entry_delete_by_type(self.entry_type) +# self.db.commit() + self.db = None + + self.entry_type = None + + self.source.delete_thyself() + self.source = None + + def refetch_ampache(self, widget): + shell = self.object + self.source.download_catalog() === removed file 'ampache/AmpacheBrowser.py' --- ampache/AmpacheBrowser.py 2011-01-12 17:12:50 +0000 +++ ampache/AmpacheBrowser.py 1970-01-01 00:00:00 +0000 @@ -1,387 +0,0 @@ -import rb, rhythmdb - -import gobject -import gtk -import gio -import datetime -import hashlib -import os - -class AmpacheCache(): - def __init__(self, cache_file, cache_enabled): - import xml.dom.minidom - self.file = cache_file # path to the cache file - - self.xml_document = xml.dom.minidom.Document() # xml root element - self.xml_root = self.xml_document.createElement('root') - self.xml_document.appendChild(self.xml_root) - - self.xml_songs = self.xml_document.createElement('songs') # xml songs element - self.xml_db_dates = self.xml_document.createElement('db_dates') # xml db_date element - - self.enabled = cache_enabled - - def set_db_dates(self, update_date, add_date, clean_date): - date = self.xml_document.createElement('update') - date.setAttribute('date', str(update_date)) - self.xml_db_dates.appendChild(date) - - date = self.xml_document.createElement('add') - date.setAttribute('date', str(add_date)) - self.xml_db_dates.appendChild(date) - - date = self.xml_document.createElement('clean') - date.setAttribute('date', str(clean_date)) - self.xml_db_dates.appendChild(date) - - def add_song(self, song_id, song_url, song_title, song_artist, song_album, song_genre, song_track_number, song_duration): - song = self.xml_document.createElement('song') - song.setAttribute('id', str(song_id)) - song.setAttribute('url', str(song_url)) - song.setAttribute('title', str(song_title)) - song.setAttribute('artist', str(song_artist)) - song.setAttribute('album', str(song_album)) - song.setAttribute('genre', str(song_genre)) - song.setAttribute('track_number', str(song_track_number)) - song.setAttribute('duration', str(song_duration)) - self.xml_songs.appendChild(song) - - def write(self): - self.xml_root.appendChild(self.xml_songs) - self.xml_root.appendChild(self.xml_db_dates) - - h = open(self.file, 'w') - h.writelines(self.xml_document.toprettyxml(indent=' ')) - h.close() - - -class AmpacheBrowser(rb.BrowserSource): - __gproperties__ = { - 'plugin': (rb.Plugin, 'plugin', 'plugin', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY), - } - - def __init__(self): - rb.BrowserSource.__init__(self) - - def activate(self, config): - # Plugin activated - self.limit = 100 - self.offset = 0 - self.url = '' - self.auth = None - - self.cache_stream = None - - self.cache_dir = os.path.expanduser("~/.cache/rhythmbox/ampache") - self.cache = AmpacheCache('%s/song-cache.xml' % self.cache_dir, True) - self.db_dates = None - self.update_date = None - self.add_date = None - self.clean_date = None - - self.config = config - - width, height = gtk.icon_size_lookup(gtk.ICON_SIZE_LARGE_TOOLBAR) - icon = gtk.gdk.pixbuf_new_from_file_at_size(self.config.get("icon_filename"), width, height) - self.set_property( "icon", icon) - - shell = self.get_property("shell") - self.db = shell.get_property("db") - self.entry_type = self.get_property("entry-type") - - self.__activate = False - - # need if we use find_file - def do_set_property(self, property, value): - if property.name == 'plugin': - self.__plugin = value - else: - raise AttributeError, 'unknown property %s' % property.name - - def do_impl_get_browser_key(self): - return "/apps/rhythmbox/plugins/ampache/show_browser" - - def do_impl_get_paned_key(self): - return "/apps/rhythmbox/plugins/ampache/paned_position" - - def db_add_entry(self, song_id, song_url, song_title, song_artist, song_album, song_genre, song_track_number, song_duration): - # check to see if entry already exists in DB before creating a new one - entry = self.db.entry_lookup_by_location(song_url) - if entry == None: - entry = self.db.entry_new(self.entry_type, song_url) - - if song_id != '': - self.db.set(entry, rhythmdb.PROP_TITLE, song_title) - if song_artist != '': - self.db.set(entry, rhythmdb.PROP_ARTIST, song_artist) - if song_album != '': - self.db.set(entry, rhythmdb.PROP_ALBUM, song_album) - if song_genre != '': - self.db.set(entry, rhythmdb.PROP_GENRE, song_genre) - - self.db.set(entry, rhythmdb.PROP_TRACK_NUMBER, song_track_number) - self.db.set(entry, rhythmdb.PROP_DURATION, song_duration) - - def load_db(self): - import urllib2 - import time - import xml.dom.minidom - - url = self.config.get("url") - username = self.config.get("username") - password = self.config.get("password") - - if not url: - emsg = _("Server URL is missing") - dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, emsg) - dlg.run() - dlg.destroy() - return - - if not password: - emsg = _("Server password is empty") - dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, emsg) - dlg.run() - dlg.destroy() - return - - timestamp = int(time.time()) - password = hashlib.sha256(password).hexdigest() - authkey = hashlib.sha256(str(timestamp) + password).hexdigest() - - self.url = url - - auth_xml = urllib2.urlopen("%s?action=handshake&auth=%s×tamp=%s&user=%s&version=350001" % (url, authkey, timestamp, username)).read() - - self.cache_stream = gio.File(self.cache.file) - self.auth_stream = None - - try: - dom = xml.dom.minidom.parseString(auth_xml) - - self.update_date = dom.getElementsByTagName("update")[0].childNodes[0].data - self.add_date = dom.getElementsByTagName("add")[0].childNodes[0].data - self.clean_date = dom.getElementsByTagName("clean")[0].childNodes[0].data - except: - emsg = _("Failed to authenticate with server") - dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, emsg) - dlg.run() - dlg.destroy() - return - - # print "self: update_date=%s. add_date=%s, clean_date=%s\n" % (self.update_date, self.add_date, self.clean_date) - self.cache_stream.read_async(self.load_db_cb) - - def load_db_cb(self, gdaemonfile, result): - import xml.dom.minidom - - xmldata = self.cache_stream.read_finish(result).read() - dom = xml.dom.minidom.parseString(xmldata) - self.cache_stream = None - - # check library modification dates to decide whether we can use the cache - db_dates = dom.getElementsByTagName('db_dates')[0] - update_date = db_dates.getElementsByTagName('update')[0].getAttribute('date') - add_date = db_dates.getElementsByTagName('add')[0].getAttribute('date') - clean_date = db_dates.getElementsByTagName('clean')[0].getAttribute('date') - - # print "update_date=%s. add_date=%s, clean_date=%s\n" % (update_date, add_date, clean_date) - # print "self: update_date=%s. add_date=%s, clean_date=%s\n" % (self.update_date, self.add_date, self.clean_date) - - if (update_date != self.update_date) or (add_date != self.add_date) or (clean_date != self.clean_date): - print "dates DID NOT match so downloading db" - self.download_db() - else: - print "dates DID match so loading cached db" - for song in dom.getElementsByTagName('song'): - song_id = song.getAttribute('id') - song_url = song.getAttribute('url') - song_title = song.getAttribute('title') - song_artist = song.getAttribute('artist') - song_album = song.getAttribute('album') - song_genre = song.getAttribute('genre') - song_track_number = int(song.getAttribute('track_number')) - song_duration = int(song.getAttribute('duration')) - self.db_add_entry(song_id, song_url, song_title, song_artist, song_album, song_genre, song_track_number, song_duration) - - self.db.commit() - - def download_db(self): - import time - - url = self.config.get("url") - - username = self.config.get("username") # necessary ? - password = self.config.get("password") - - if not url: - emsg = _("Server URL is missing") - dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, emsg) - dlg.run() - dlg.destroy() - return - - if not password: - emsg = _("Server password is empty") - dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, emsg) - dlg.run() - dlg.destroy() - return - - timestamp = int(time.time()) - password = hashlib.sha256(password).hexdigest() - authkey = hashlib.sha256(str(timestamp) + password).hexdigest() - - print "url=%s, authkey=%s, timestamp=%s, username=%s" % (url, authkey, timestamp, username) - - auth_url = "%s?action=handshake&auth=%s×tamp=%s&user=%s&version=350001" % (url, authkey, timestamp, username) - self.url = url - rb.Loader().get_url(auth_url, self.download_db_cb) - - def download_db_cb(self, result): - import xml.dom.minidom - - if result is None: - emsg = _("Error connecting to Ampache Server at %s") % (self.url) - dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, emsg) - dlg.run() - dlg.destroy() - return - - try: - dom = xml.dom.minidom.parseString(result) - self.auth = dom.getElementsByTagName("auth")[0].childNodes[0].data - self.update_date = dom.getElementsByTagName("update")[0].childNodes[0].data - self.add_date = dom.getElementsByTagName("add")[0].childNodes[0].data - self.clean_date = dom.getElementsByTagName("clean")[0].childNodes[0].data - except: - emsg = _("Could not authenticate username/password") - dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, emsg) - dlg.run() - dlg.destroy() - return - - self.populate() - - self.cache.set_db_dates(self.update_date, self.add_date, self.clean_date) - - def populate(self): - import xml.dom.minidom - - print "offset: %s, limit: %s" % (self.offset, self.limit) - request = "%s?offset=%s&limit=%s&action=songs&auth=%s" % (self.url, self.offset, self.limit, self.auth) - print "url: %s" % request - - rb.Loader().get_url(request, self.populate_cb, request) - - def populate_cb(self, result, url): - import xml.dom.minidom - - if result is None: - emsg = _("Error downloading song database from Ampache Server at %s") % (self.url) - dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, emsg) - dlg.run() - dlg.destroy() - return - - song_count = 0 - dom = xml.dom.minidom.parseString(result) - for node in dom.getElementsByTagName("song"): - song_count = song_count + 1 - song_id = node.getAttribute("id") - - tmp = node.getElementsByTagName("url") - if tmp == []: - song_url = '' #node.getElementsByTagName("genre")[0].childNodes[0].data - else: - if tmp[0].childNodes == []: - song_url = ''; - else: - song_url = tmp[0].childNodes[0].data - - tmp = node.getElementsByTagName("title") - if tmp == []: - song_title = '' #node.getElementsByTagName("genre")[0].childNodes[0].data - else: - if tmp[0].childNodes == []: - song_title = ''; - else: - song_title = tmp[0].childNodes[0].data - #print "title: %s" % e_title - - tmp = node.getElementsByTagName("artist") - if tmp == []: - song_artist = '' #node.getElementsByTagName("genre")[0].childNodes[0].data - else: - if tmp[0].childNodes == []: - song_artist = ''; - else: - song_artist = tmp[0].childNodes[0].data - #print "artist: %s" % e_artist - - tmp = node.getElementsByTagName("album") - if tmp == []: - song_album = '' #node.getElementsByTagName("genre")[0].childNodes[0].data - else: - if tmp[0].childNodes == []: - song_album = ''; - else: - song_album = tmp[0].childNodes[0].data - #print "album: %s" % e_album - - tmp = node.getElementsByTagName("tag") - if tmp == []: - song_genre = '' #node.getElementsByTagName("genre")[0].childNodes[0].data - else: - if tmp[0].childNodes == []: - song_genre = ''; - else: - song_genre = tmp[0].childNodes[0].data - #print "genre: %s" % e_genre - - song_track_number = int(node.getElementsByTagName("track")[0].childNodes[0].data) - song_duration = int(node.getElementsByTagName("time")[0].childNodes[0].data) - - #print "Processing %s - %s" % (artist, album) #DEBUG - - # adding the song to our database - self.db_add_entry(song_id, song_url, song_title, song_artist, song_album, song_genre, song_track_number, song_duration) - - if self.cache.enabled == True: - # create an entry for the cache file - self.cache.add_song(song_id, song_url, song_title, song_artist, song_album, song_genre, song_track_number, song_duration) - - self.db.commit() - if (song_count < self.limit): - if self.cache.enabled == True: - # write the xml cache file, so the next time we dont need to download this information - if not os.path.isdir(self.cache_dir): - os.makedirs(self.cache_dir) - self.cache.write() - - return False - else: - self.offset = self.offset + song_count - - self.populate() - return True - - # Source is first clicked on - def do_impl_activate (self): - # Connect to Ampache if not already - if not self.__activate: - self.__activate = True - if self.cache.enabled == True: - if not os.path.exists(self.cache.file): - print "no cache file exists so downloading db" - self.download_db() - else: - print "cache file does exist so attempting to load cached db" - self.load_db() - else: - print "caching disabled so downloading db" - self.download_db() - - rb.BrowserSource.do_impl_activate(self) - -gobject.type_register(AmpacheBrowser) === removed file 'ampache/AmpacheConfig.py' --- ampache/AmpacheConfig.py 2011-01-12 17:12:50 +0000 +++ ampache/AmpacheConfig.py 1970-01-01 00:00:00 +0000 @@ -1,40 +0,0 @@ -import gconf - -class AmpacheConfig(object): - def __init__(self): - self.gconf_keys = { - 'url' : '/apps/rhythmbox/plugins/ampache/url', - 'username' : '/apps/rhythmbox/plugins/ampache/username', - 'password' : '/apps/rhythmbox/plugins/ampache/password', - - 'name' : "/apps/rhythmbox/plugins/ampache/name", - 'group' : "/apps/rhythmbox/plugins/ampache/group", - - 'icon' : "/apps/rhythmbox/plugins/ampache/icon", - 'icon_filename' : "/apps/rhythmbox/plugins/ampache/icon_filename", - } - - self.gconf = gconf.client_get_default() - - # Defaults ("hidden" options) - self.set("name", "Ampache") - self.set("group", "Shared") - - self.set("icon", "ampache.ico") - - def get(self, key): - if self.gconf.get_string(self.gconf_keys[key]): - return self.gconf.get_string(self.gconf_keys[key]) - else: - return "" - - def set(self, key, value): - self.gconf.set_string(self.gconf_keys[key], value) - -#if __name__ == "__main__": - #config = AmpacheConfig() - #print config.get("url") - #config.set("password", "testing123") - #print config.get("username") - #print config.get("password") - === removed file 'ampache/AmpacheConfigDialog.py' --- ampache/AmpacheConfigDialog.py 2011-01-12 17:12:50 +0000 +++ ampache/AmpacheConfigDialog.py 1970-01-01 00:00:00 +0000 @@ -1,37 +0,0 @@ -import gtk, gtk.glade - -class AmpacheConfigDialog(object): - def __init__(self, glade_file, config): - self.gladexml = gtk.glade.XML(glade_file) - self.config = config - self.config_dialog = self.gladexml.get_widget("preferences_dialog") - - self.url = self.gladexml.get_widget("url_entry") - self.url.set_text(self.config.get("url")) - - self.username = self.gladexml.get_widget("username_entry") - self.username.set_text(self.config.get("username")) - - self.password = self.gladexml.get_widget("password_entry") - self.password.set_text(self.config.get("password")) - self.password.set_visibility(False) - - self.config_dialog.connect("response", self.dialog_response) - - def get_dialog(self): - return self.config_dialog - - def dialog_response(self, dialog, response): - if response == gtk.RESPONSE_OK: - self.config.set("url", self.url.get_text()) - self.config.set("username", self.username.get_text()) - self.config.set("password", self.password.get_text()) - - self.config_dialog.hide() - - elif response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT: - self.config_dialog.hide() - - else: - print "unexpected response type in dialog_response" - self.config_dialog.hide() === removed file 'ampache/LICENSE' --- ampache/LICENSE 2011-01-12 17:12:50 +0000 +++ ampache/LICENSE 1970-01-01 00:00:00 +0000 @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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 of the License, 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., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. === removed file 'ampache/README' --- ampache/README 2011-01-12 17:12:50 +0000 +++ ampache/README 1970-01-01 00:00:00 +0000 @@ -1,73 +0,0 @@ -INFO - -Plase see http://code.google.com/p/rhythmbox-ampache/ for the latest version - -Ampache setup - -Read Ampache Access Control Lists documentation and create an ACL for RPC (XML API) - http://ampache.org/wiki/config:acl - -Rhythmbox setup - -Untar and put the ampache directory into $HOME/.gnome2/rhythmbox/plugins -or /usr/lib/rhythmbox/plugins - -Run rhythmbox (preferable with "-D ampache" from command line) - -Click Edit, select Plugins dialog box, enable "Ampache Library" - -Click Configure, enter your infromation, for example URL: - http://test.com/server/xml.server.php - -Click on Ampache under Library, songs should appear in the browser - - -COPYRIGHT - -Copyright (C) 2008-2010 Rhythmbox Ampache plugin team - -20-04-2010 - Philip Langdale - -I did some work over the weekend to fix the blocking nature of -the plugin. I switched to using async gio to make the http -transfers and dropped the per-request limit to 100. The net -result is a much more responsive UI that also shows progress -more often. - -I also fixed the reference leaks that prevent rhythmbox -exiting with the plugin today. - -19-04-2010 - Graham Grindlay - * Added a few small fixes to keep the plugin from choking on bad/empty tag data. - * Genre tags didn't seem to be coming through properly so I set them using the "tag" - field instead. This seems to work fine, although if multiple tags are associated - with a track, only the first one will be used. If you don't like this, you can - just comment out line 148 (self.db.set(e, rhythmdb.PROP_GENRE, e_genre)) - -Patched to work with Ampache 3.5.x by Massimo Mund - -Copyright (C) 2008 Seva - -Portions from Magnatune Rhythmbox plugin -Copyright (C) 2006 Adam Zimmerman -Copyright (C) 2006 James Livingston - -Portions from 'git clone http://quickplay.isfound.at' -Copyright (C) 2008 Kevin James Purdy irc://irc.freenode.org/purdyk,isnick - - -LICENSE - -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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. === removed file 'ampache/__init__.py' --- ampache/__init__.py 2011-01-12 17:12:50 +0000 +++ ampache/__init__.py 1970-01-01 00:00:00 +0000 @@ -1,104 +0,0 @@ -# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*- - -import rhythmdb, rb -import gobject -import gtk - -from AmpacheConfig import AmpacheConfig -from AmpacheConfigDialog import AmpacheConfigDialog -from AmpacheBrowser import AmpacheBrowser - -ui_str = """ - - - - - - - - - -""" - -class AmpacheEntryType(rhythmdb.EntryType): - def __init__(self): - rhythmdb.EntryType.__init__(self, name='AmpacheEntryType') - -class Ampache(rb.Plugin): - def __init__(self): - self.config = AmpacheConfig() - - rb.Plugin.__init__(self) - - def activate(self, shell): - self.db = shell.props.db - - self.entry_type = AmpacheEntryType() - self.entry_type.can_sync_metadata = True - self.entry_type.sync_metadata = None - self.entry_type.category = rhythmdb.ENTRY_STREAM - - group = rb.rb_source_group_get_by_name(self.config.get("group")) - if not group: - group = rb.rb_source_group_register ( - "ampache", - self.config.get("group"), - rb.SOURCE_GROUP_CATEGORY_FIXED, - ) - - - self.source = gobject.new ( - AmpacheBrowser, - entry_type=self.entry_type, - source_group=group, - name=self.config.get("name"), - shell=shell, - ) - - self.config.set("icon_filename", self.find_file(self.config.get("icon"))) - self.source.activate(self.config) - - shell.register_entry_type_for_source(self.source, self.entry_type) - shell.append_source(self.source, None) - - ui_manager = shell.get_ui_manager() - action = gtk.Action('RefetchAmpache', - _('_Re-fetch Ampache Library'), - _('Update the local ampache library'), "") - action.connect ('activate', self.refetch_ampache, shell) - action_group = gtk.ActionGroup ('RefetchAmpacheGroup') - action_group.add_action(action) - ui_manager.insert_action_group(action_group, -1) - self.uid = ui_manager.add_ui_from_string(ui_str) - ui_manager.ensure_update() - - def deactivate(self, shell): - self.db.entry_delete_by_type(self.entry_type) - self.db.commit() - del self.db - self.db = None - - self.entry_type = None - - self.source.delete_thyself() - del self.source - self.source = None - - # clean up the UI - ui_manager = shell.get_ui_manager() - ui_manager.remove_ui(self.uid) - - def create_configure_dialog(self): - glade_file = self.find_file("ampache-prefs.glade") - - if glade_file: - dialog = AmpacheConfigDialog(glade_file, self.config).get_dialog() - - if dialog: - return dialog - else: - print "couldn't create configure dialog" - return None - - def refetch_ampache(self, widget, shell): - self.source.download_db() === removed file 'ampache/ampache-prefs.glade' --- ampache/ampache-prefs.glade 2011-01-12 17:12:50 +0000 +++ ampache/ampache-prefs.glade 1970-01-01 00:00:00 +0000 @@ -1,164 +0,0 @@ - - - - - - True - Ampache Plugin Preferences - 500 - GDK_WINDOW_TYPE_HINT_DIALOG - False - - - True - 2 - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - ampache.png - - - 1 - - - - - True - 5 - 10 - - - True - 0 - <b>Ampache Instance:</b> - True - - - False - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 3 - 2 - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - 1 - 2 - 1 - 2 - - - - - True - 0 - Server UR_L: - True - - - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - 1 - 2 - - - - - True - 0 - _Username (optional): - True - - - 1 - 2 - - - - - True - 0 - _Password (Key): - True - - - 2 - 3 - - - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - 1 - 2 - 2 - 3 - - - - - 1 - - - - - 2 - - - - - True - GTK_BUTTONBOX_EDGE - - - True - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-cancel - True - -6 - - - - - True - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-ok - True - -5 - - - 1 - - - - - False - GTK_PACK_END - - - - - - === removed file 'ampache/ampache.ico' Binary files ampache/ampache.ico 2011-01-12 17:12:50 +0000 and ampache/ampache.ico 1970-01-01 00:00:00 +0000 differ === removed file 'ampache/ampache.png' Binary files ampache/ampache.png 2011-01-12 17:12:50 +0000 and ampache/ampache.png 1970-01-01 00:00:00 +0000 differ === removed file 'ampache/ampache.rb-plugin' --- ampache/ampache.rb-plugin 2011-01-12 17:12:50 +0000 +++ ampache/ampache.rb-plugin 1970-01-01 00:00:00 +0000 @@ -1,9 +0,0 @@ -[RB Plugin] -Loader=python -Module=ampache -IAge=1 -Name=Ampache Library -Description=Adds support for playing music from an Ampache instance -Authors=Rhythmbox Ampache plugin team -Copyright=Copyright ©2008-2010 Rhythmbox Ampache plugin team -Website=http://code.google.com/p/rhythmbox-ampache/ === removed directory 'ampache/fedora' === removed directory 'ampache/fedora/.svn' === removed file 'ampache/fedora/.svn/all-wcprops' --- ampache/fedora/.svn/all-wcprops 2011-01-12 17:12:50 +0000 +++ ampache/fedora/.svn/all-wcprops 1970-01-01 00:00:00 +0000 @@ -1,11 +0,0 @@ -K 25 -svn:wc:ra_dav:version-url -V 37 -/svn/!svn/ver/24/trunk/ampache/fedora -END -rhythmbox-ampache.spec -K 25 -svn:wc:ra_dav:version-url -V 60 -/svn/!svn/ver/24/trunk/ampache/fedora/rhythmbox-ampache.spec -END === removed file 'ampache/fedora/.svn/entries' --- ampache/fedora/.svn/entries 2011-01-12 17:12:50 +0000 +++ ampache/fedora/.svn/entries 1970-01-01 00:00:00 +0000 @@ -1,62 +0,0 @@ -10 - -dir -24 -https://rhythmbox-ampache.googlecode.com/svn/trunk/ampache/fedora -https://rhythmbox-ampache.googlecode.com/svn - - - -2010-10-29T17:23:01.138391Z -24 -envyseapets - - - - - - - - - - - - - - -2f43e883-caf6-67b7-7e67-f8f7d4f62005 - -rhythmbox-ampache.spec -file - - - - -2010-12-06T00:56:36.475679Z -eea84a20cab9d14f1190819e70bf552f -2010-10-29T17:23:01.138391Z -24 -envyseapets - - - - - - - - - - - - - - - - - - - - - -960 - === removed directory 'ampache/fedora/.svn/prop-base' === removed directory 'ampache/fedora/.svn/props' === removed directory 'ampache/fedora/.svn/text-base' === removed file 'ampache/fedora/.svn/text-base/rhythmbox-ampache.spec.svn-base' --- ampache/fedora/.svn/text-base/rhythmbox-ampache.spec.svn-base 2011-01-12 17:12:50 +0000 +++ ampache/fedora/.svn/text-base/rhythmbox-ampache.spec.svn-base 1970-01-01 00:00:00 +0000 @@ -1,43 +0,0 @@ -Name: rhythmbox-ampache -Summary: Ampache plugin for Rhythmbox -Version: 0.11 -Release: 1%{?dist} -License: GPLv2 -Group: Applications/Multimedia -URL: http://code.google.com/p/rhythmbox-ampache/ - -Source: http://rhythmbox-ampache.googlecode.com/files/%{name}-%{version}.tar.gz - -BuildArch: noarch -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) - -Requires: rhythmbox - -%description -Ampache plugin for Rhythmbox - -%prep -%setup -q - -#%build -#make %{?_smp_mflags} - -%install -rm -rf %{buildroot} -mkdir -p %{buildroot}/%{_libdir}/rhythmbox/plugins/ampache -cp ampache/__init__.py ampache/[aA]mpache* %{buildroot}/%{_libdir}/rhythmbox/plugins/ampache - -%clean -rm -rf %{buildroot} - -%files -%defattr(-, root, root) -%doc ampache/README -%{_libdir}/rhythmbox/plugins/ampache/* - -%changelog -* Sat Oct 23 2010 Massimo Mund 0.11-1 -- Rhythmbox API changes - -* Mon Apr 26 2010 Seva Epsteyn 0.10-1 -- Initial build === removed directory 'ampache/fedora/.svn/tmp' === removed directory 'ampache/fedora/.svn/tmp/prop-base' === removed directory 'ampache/fedora/.svn/tmp/props' === removed directory 'ampache/fedora/.svn/tmp/text-base' === removed file 'ampache/fedora/rhythmbox-ampache.spec' --- ampache/fedora/rhythmbox-ampache.spec 2011-01-12 17:12:50 +0000 +++ ampache/fedora/rhythmbox-ampache.spec 1970-01-01 00:00:00 +0000 @@ -1,43 +0,0 @@ -Name: rhythmbox-ampache -Summary: Ampache plugin for Rhythmbox -Version: 0.11 -Release: 1%{?dist} -License: GPLv2 -Group: Applications/Multimedia -URL: http://code.google.com/p/rhythmbox-ampache/ - -Source: http://rhythmbox-ampache.googlecode.com/files/%{name}-%{version}.tar.gz - -BuildArch: noarch -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) - -Requires: rhythmbox - -%description -Ampache plugin for Rhythmbox - -%prep -%setup -q - -#%build -#make %{?_smp_mflags} - -%install -rm -rf %{buildroot} -mkdir -p %{buildroot}/%{_libdir}/rhythmbox/plugins/ampache -cp ampache/__init__.py ampache/[aA]mpache* %{buildroot}/%{_libdir}/rhythmbox/plugins/ampache - -%clean -rm -rf %{buildroot} - -%files -%defattr(-, root, root) -%doc ampache/README -%{_libdir}/rhythmbox/plugins/ampache/* - -%changelog -* Sat Oct 23 2010 Massimo Mund 0.11-1 -- Rhythmbox API changes - -* Mon Apr 26 2010 Seva Epsteyn 0.10-1 -- Initial build === modified file 'debian/changelog' --- debian/changelog 2011-01-12 17:12:50 +0000 +++ debian/changelog 2012-04-23 11:25:31 +0000 @@ -1,5 +1,17 @@ -rhythmbox-ampache (0.11.1-0ubuntu1) natty; urgency=low - - * Initial release. (LP: #683417) - - -- Charlie Smotherman Wed, 12 Jan 2011 17:12:50 -0500 +rhythmbox-ampache (0.11.1+svn34-0ubuntu1) precise; urgency=low + + * New upstream release which updates package for the new version of + rhythmbox and gtk3. (LP: #983300) Closes: #646642 + * debian/control + - update standards version to 3.9.3 no changes needed. + - updated debhelper and python depends. + * Removed debian/links not needed. + * Removed debian/install not needed. + + -- Charlie Smotherman Tue, 17 Apr 2012 09:17:47 -0500 + +rhythmbox-ampache (0.11.1-1) unstable; urgency=low + + * Initial release. Closes: #605556 (LP: #683417) + + -- Charlie Smotherman Sat, 04 Dec 2010 11:18:47 -0500 === modified file 'debian/control' --- debian/control 2011-01-12 17:12:50 +0000 +++ debian/control 2012-04-22 21:52:11 +0000 @@ -4,14 +4,13 @@ Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Charlie Smotherman Uploaders: Python Applications Packaging Team -Build-Depends: debhelper (>= 7.0.50~), python (>=2.6.5-2~) -X-Python-Version: >=2.6 -Standards-Version: 3.9.1 +Build-Depends: debhelper (>= 7.0.50~), python (>= 2.6.6-3~), libglib2.0-bin +X-Python-Version: >= 2.6 +Standards-Version: 3.9.3 Homepage: http://code.google.com/p/rhythmbox-ampache Package: rhythmbox-ampache Architecture: all -XB-Python-Version: ${python:Versions} Depends: ${misc:Depends}, ${python:Depends}, python-gtk2, python-gobject, rhythmbox-plugins, libglade2-0, python-gconf Description: play audio streams from an Ampache server === modified file 'debian/docs' --- debian/docs 2011-01-12 17:12:50 +0000 +++ debian/docs 2012-04-18 11:05:43 +0000 @@ -1,1 +1,1 @@ -ampache/README +README === added file 'org.gnome.rhythmbox.plugins.ampache.gschema.xml' --- org.gnome.rhythmbox.plugins.ampache.gschema.xml 1970-01-01 00:00:00 +0000 +++ org.gnome.rhythmbox.plugins.ampache.gschema.xml 2012-04-18 10:35:48 +0000 @@ -0,0 +1,20 @@ + + + + '' + URL for ampache server + URL for ampache server + + + '' + Username + Username + + + '' + Password + Password + + + + === modified file 'setup.py' --- setup.py 2011-01-12 17:12:50 +0000 +++ setup.py 2012-04-18 10:35:48 +0000 @@ -1,12 +1,28 @@ #!/usr/bin/env python from distutils.core import setup +from distutils.sysconfig import PREFIX +from distutils.command.install_data import install_data +import os + +class post_install(install_data): + def run(self): + # Call parent + install_data.run(self) + + # Execute commands after copying + os.system('glib-compile-schemas %s/share/glib-2.0/schemas' % self.install_dir) setup(name="rhythmbox-ampache", - version="0.11", - description="A Rhythmbox plugin to stream music from an Ampache server", - author="Seva Epsteyn", - author_email="seva@sevatech.com", - url="http://code.google.com/p/rhythmbox-ampache", - packages= ["ampache"], - ) + cmdclass={"install_data": post_install}, + version="0.11", + description="A Rhythmbox plugin to stream music from an Ampache server", + author="Rhythmbox Ampache plugin team", + author_email="rhythmbox-ampache@googlegroups.com", + url="http://code.google.com/p/rhythmbox-ampache", + data_files=[ + ("lib/rhythmbox/plugins/ampache", ["ampache.plugin", "ampache.py", "AmpacheBrowser.py", "AmpacheConfigDialog.py"]), + ("share/rhythmbox/plugins/ampache", ["ampache-prefs.ui", "ampache.ico", "ampache.png"]), + ("share/glib-2.0/schemas", ["org.gnome.rhythmbox.plugins.ampache.gschema.xml"]), + ], + )